Compare commits

..

47 Commits

Author SHA1 Message Date
Eelco Dolstra
0bd62229cd Bump version 2024-03-07 13:10:53 +01:00
Eelco Dolstra
17a598e644 Merge pull request from GHSA-2ffj-w4mj-pg37
Sandbox escape 2.18
2024-03-07 11:56:24 +01:00
Tom Bereknyei
9e7065bef5 Add release notes
Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io>
2024-03-07 09:33:44 +01:00
Tom Bereknyei
4bc5a3510f Copy the output of fixed-output derivations before registering them
It is possible to exfiltrate a file descriptor out of the build sandbox
of FODs, and use it to modify the store path after it has been
registered.
To avoid that issue, don't register the output of the build, but a copy
of it (that will be free of any leaked file descriptor).

Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2024-03-07 09:33:44 +01:00
Tom Bereknyei
f8d20e91a4 Add a NixOS test for the sandbox escape
Test that we can't leverage abstract unix domain sockets to leak file
descriptors out of the sandbox and modify the path after it has been
registered.

Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io>
2024-03-07 09:33:40 +01:00
tomberek
ec177b98f3 Merge pull request #10170 from johnrichardrinehart/jrinehart/2_18-faster-flake-lock-parsing
2.18 faster flake lock parsing
2024-03-07 00:11:54 -05:00
Graham Dennis
7b237ebe5c Faster flake.lock parsing
This PR reduces the creation of short-lived basic_json objects while
parsing flake.lock files. For large flake.lock files (~1.5MB) I was
observing ~60s being spent for trivial nix build operations while
after this change it is now taking ~1.6s.

(cherry picked from commit 7fd0de38c6)
2024-03-06 17:57:15 -08:00
github-actions[bot]
afb55f36df Fix bad_format_string error when builder stdout contains % (#10063)
(cherry picked from commit e2b6821ca0)

Co-authored-by: roblabla <unfiltered@roblab.la>
2024-02-22 23:12:29 +01:00
Eelco Dolstra
2a6a7aad3f Merge pull request #10018 from NixOS/fix-18-build
[2.18] Fix the build
2024-02-15 11:00:13 +01:00
Théophane Hufschmitt
f36d4aefd3 Fix the build
bef68e53b9 (backport of
31ebc6028b) accidentally broke the build
because of a change in the constructor of `SourcePath` between 2.18 and
master. Fix that.
2024-02-15 09:50:12 +01:00
github-actions[bot]
2db5c5326b fix location of _redirects file (#9956) 2024-02-07 11:44:06 +01:00
Eelco Dolstra
5f7f68e0a8 Merge pull request #9827 from NixOS/backport-9363-to-2.18-maintenance
[Backport 2.18-maintenance] Fix symlink handling
2024-01-22 14:30:17 +01:00
Eelco Dolstra
bef68e53b9 Fix symlink handling
This restores the symlink handling behaviour prior to
94812cca98.

Fixes #9298.

(cherry picked from commit 31ebc6028b)
2024-01-21 20:53:30 +00:00
John Ericson
60eb80593f Merge pull request #9738 from shlevy/ifd-buildStore-2.18
(Backport #9661) Build IFD in the build store when using eval-store.
2024-01-11 10:06:20 -05:00
Shea Levy
ba48ab4b95 Merge branch '2.18-maintenance' into ifd-buildStore-2.18 2024-01-11 07:21:16 -05:00
Shea Levy
e7c2b35827 Build IFD in the build store when using eval-store.
Previously, IFDs would be built within the eval store, even though one
is typically using `--eval-store` precisely to *avoid* local builds.

Because the resulting Nix expression must be copied back to the eval
store in order to be imported, this requires the eval store to trust
the build store's signatures.

(cherry picked from commit c3942ef85f)
2024-01-11 06:34:27 -05:00
Shea Levy
be208d8e78 remote-store test: Break out IFD expression into a separate file
(cherry picked from commit 9cb287657b)
2024-01-11 06:30:02 -05:00
github-actions[bot]
4dd5171652 fix an old lost direct (#9717)
this part must have been moved quite a while ago, but apparently so far
no one noticed

(cherry picked from commit 6db805b3d1)

Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2024-01-08 20:57:39 +01:00
John Ericson
4a1c3762df Merge pull request #9543 from NixOS/backport-9542-to-2.18-maintenance
[Backport 2.18-maintenance] Add missing `-pthread` for test support libraries
2023-12-05 19:26:18 -05:00
John Ericson
615bd655e5 Add missing -pthread for test support libraries
This is good in general (see how the other libraries also have long had
it, since 49fe9592a4) but in particular
needed to fix the NetBSD build.

(cherry picked from commit b23273f6a2)
2023-12-05 23:44:41 +00:00
John Ericson
211b6e1855 Merge pull request #9517 from NixOS/2.18-flatten-tests
[Backport 2.18-maintanence] Backport test source layout reorgs
2023-12-01 13:53:13 -05:00
John Ericson
f7f37035c8 Move tests to separate directories, and document
Today, with the tests inside a `tests` intermingled with the
corresponding library's source code, we have a few problems:

- We have to be careful that wildcards don't end up with tests being
  built as part of Nix proper, or test headers being installed as part
  of Nix proper.

- Tests in libraries but not executables is not right:

  - It means each executable runs the previous unit tests again, because
    it needs the libraries.

  - It doesn't work right on Windows, which doesn't want you to load a
    DLL just for the side global variable . It could be made to work
    with the dlopen equivalent, but that's gross!

This reorg solves these problems.

There is a remaining problem which is that sibbling headers (like
`hash.hh` the test header vs `hash.hh` the main `libnixutil` header) end
up shadowing each other. This PR doesn't solve that. That is left as
future work for a future PR.

Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>

(cherry picked from commit 91b6833686)
(cherry picked from commit a61e42adb5)
2023-12-01 13:05:03 -05:00
John Ericson
30dcc19d1f Put functional tests in tests/functional
I think it is bad for these reasons when `tests/` contains a mix of
functional and integration tests

 - Concepts is harder to understand, the documentation makes a good
   unit vs functional vs integration distinction, but when the
   integration tests are just two subdirs within `tests/` this is not
   clear.

 - Source filtering in the `flake.nix` is more complex. We need to
   filter out some of the dirs from `tests/`, rather than simply pick
   the dirs we want and take all of them. This is a good sign the
   structure of what we are trying to do is not matching the structure
   of the files.

With this change we have a clean:
```shell-session
$ git show 'HEAD:tests'
tree HEAD:tests

functional/
installer/
nixos/
```

(cherry picked from commit 68c81c7375)
2023-12-01 12:06:43 -05:00
John Ericson
7242521265 Merge pull request #9514 from NixOS/2.18-nixpkgs-bump
[Backport 2.18-maintainence] nixpkgs bump
2023-12-01 11:30:05 -05:00
Robert Hensing
016f936df6 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/31ed632c692e6a36cfc18083b88ece892f863ed4' (2023-09-21)
  → 'github:NixOS/nixpkgs/9ba29e2346bc542e9909d1021e8fd7d4b3f64db0' (2023-11-23)

(cherry picked from commit fe4f573d49)
(cherry picked from commit f01baf5f06
2023-12-01 11:08:15 -05:00
Robert Hensing
083b198bb7 Merge pull request #9471 from NixOS/backport-9459-to-2.18-maintenance
[Backport 2.18-maintenance] add path based redirects
2023-11-27 15:15:52 +01:00
Valentin Gagarin
8fb477a6e7 add path based redirects
up to now, those were managed outside of this repo, which as
unsurprisingly a real hassle to deal with if one wanted to prevent URLs
from breaking when moving pages around. this change removes a large part
of the friction involved in moving content in the Nix manual.

possible next steps for further automation:
- check for content that moved and warn if it's not reachable from
  links that were valid prior to a change
- create redirect rules automatically based on this information

(cherry picked from commit 2b7016cc56)
2023-11-27 13:29:00 +00:00
John Ericson
05b7e2dc57 Merge pull request #9436 from NixOS/backport-9431-to-2.18-maintenance
[Backport 2.18-maintenance] Add missing `-lrapidcheck` fixing build with shared lib
2023-11-22 09:35:24 -05:00
John Ericson
89a66633b7 Add missing -lrapidcheck fixing build with shared lib
https://github.com/NixOS/nixpkgs/pull/269064 makes rapidcheck be build
as a shared lib, but that broke Nix because the `-lrapidcheck` was
missing. This fixes that (and doesn't break Nix what the library is a
static archive as today).

(cherry picked from commit 46131567da)
2023-11-22 04:48:11 +00:00
Théophane Hufschmitt
fadad86276 Merge pull request #9406 from NixOS/backport-9390-to-2.18-maintenance
[Backport 2.18-maintenance] fetchTree: clarify docs for shallow flag
2023-11-20 15:38:25 +01:00
DavHau
23775b7364 fetchTree: clarify docs for shallow flag
(cherry picked from commit 796a7eb92d)
2023-11-20 14:16:03 +00:00
Robert Hensing
8e72a529da Merge pull request #9366 from NixOS/backport-9187-to-2.18-maintenance
[2.18-maintenance] Backport #9187 apple-virt
2023-11-16 19:44:32 +01:00
Robert Hensing
af21431140 libstore: Add apple-virt to system features when available
I'm sure that we'll adjust the implementation over time, but this
at least discerns between an apple silicon bare metal machine and
a tart VM.

(cherry picked from commit 9277eb276b)
2023-11-16 18:59:06 +01:00
Eelco Dolstra
184a20ec04 Merge pull request #9334 from fricklerhandwerk/backport-2.18-fix-anchors
backport fix for the `--help` output to 2.18
2023-11-16 15:02:18 +01:00
Valentin Gagarin
a68bf15fe6 backport fix for the --help output 2023-11-10 19:22:42 +01:00
John Ericson
78fd621397 Merge pull request #9122 from NixOS/backport-9095-to-2.18-maintenance
[Backport 2.18-maintenance] StorePath: reject names starting with '.'
2023-10-09 10:27:51 -04:00
edef
82040664e4 StorePath: reject names starting with '.'
This has been the behaviour before Nix 2.4. It was dropped in a rewrite
in 759947bf72, allowing the creation of
store paths that aren't considered valid by older Nix versions or other
Nix tooling.

Nix 2.4 didn't ship in NixOS until 22.05, and stdenv.mkDerivation in
nixpkgs drops leading periods since April 2022, so it's unlikely anyone
is relying on the current lax behaviour.

Closes #9091.

Change-Id: I4a57bd9899e1b0dba56870ae5a1b680918a18ce9
(cherry picked from commit 24bda0c7b3)
2023-10-08 12:21:18 +00:00
Eelco Dolstra
f5f4de6a55 Merge pull request #9086 from NixOS/backport-9081-to-2.18-maintenance
[Backport 2.18-maintenance] Revert "Adapt scheduler to work with dynamic derivations"
2023-10-02 17:05:54 +02:00
John Ericson
72b65981f9 Revert "Adapt scheduler to work with dynamic derivations"
This reverts commit 5e3986f59c. This
un-implements RFC 92 but fixes the critical bug #9052 which many people
are hitting. This is a decent stop-gap until a minimal reproduction of
that bug is found and a proper fix can be made.

Mostly fixed #9052, but I would like to leave that issue open until we
have a regression test, so I can then properly fix the bug (unbreaking
RFC 92) later.

(cherry picked from commit 8440afbed7)
2023-10-02 15:05:23 +00:00
Eelco Dolstra
7e2399b123 Merge pull request #9079 from hercules-ci/backport-9022-to-2.18-maintenance
[Backport 2.18-maintenance] pathExists: isDir when endswith /.
2023-10-02 14:08:25 +02:00
Robert Hensing
860f64c345 pathExists: isDir when endswith /.
(cherry picked from commit f8a3893e8d)
2023-10-01 13:58:20 +01:00
Eelco Dolstra
9e212344f9 Merge pull request #9036 from NixOS/backport-9026-to-2.18-maintenance
[Backport 2.18-maintenance] flake: complete update to 23.05
2023-09-25 15:05:25 +02:00
Gerg-L
277ba90779 flake: complete update to 23.05
(cherry picked from commit f264d9ff08)
2023-09-25 08:43:54 +00:00
Robert Hensing
a4445859ab Merge pull request #9009 from NixOS/backport-9008-to-2.18-maintenance
[Backport 2.18-maintenance] Re-enable systemd-nspawn test
2023-09-21 13:13:16 +02:00
Cole Helbling
f3005632c4 Re-enable systemd-nspawn test
It was disabled in c6953d1ff6 because
a recent Nixpkgs bump brought in a new systemd which changed how
systemd-nspawn worked.

As far as I can tell, the issue was caused by this upstream systemd
commit:
b71a0192c0

Bind-mounting the host's `/sys` and `/proc` into the container's
`/run/host/{sys,proc}` fixes the issue and allows the test to succeed.

(cherry picked from commit 883092e3f7)
2023-09-20 17:03:47 +00:00
Eelco Dolstra
40a014416b Bump version 2023-09-20 15:20:52 +02:00
Eelco Dolstra
44fb119218 Mark official release 2023-09-20 12:49:01 +02:00
531 changed files with 8126 additions and 15471 deletions

View File

@@ -17,7 +17,7 @@ indent_style = space
indent_size = 2
# Match c++/shell/perl, set indent to spaces with width of four
[*.{hpp,cc,hh,sh,pl,xs}]
[*.{hpp,cc,hh,sh,pl}]
indent_style = space
indent_size = 4

View File

@@ -21,7 +21,7 @@ jobs:
fetch-depth: 0
- name: Create backport PRs
# should be kept in sync with `version`
uses: zeebe-io/backport-action@v2.1.1
uses: zeebe-io/backport-action@v1.4.0
with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -101,9 +101,6 @@ jobs:
docker_push_image:
needs: [check_secrets, tests]
permissions:
contents: read
packages: write
if: >-
github.event_name == 'push' &&
github.ref_name == 'master' &&
@@ -129,9 +126,6 @@ jobs:
- run: docker load -i ./result/image.tar.gz
- run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION
- run: docker tag nix:$NIX_VERSION nixos/nix:master
# We'll deploy the newly built image to both Docker Hub and Github Container Registry.
#
# Push to Docker Hub first
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
@@ -139,20 +133,3 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
- run: docker push nixos/nix:$NIX_VERSION
- run: docker push nixos/nix:master
# Push to GitHub Container Registry as well
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push image
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/nix
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
docker tag nix:$NIX_VERSION $IMAGE_ID:$NIX_VERSION
docker tag nix:$NIX_VERSION $IMAGE_ID:master
docker push $IMAGE_ID:$NIX_VERSION
docker push $IMAGE_ID:master

8
.gitignore vendored
View File

@@ -41,14 +41,14 @@ perl/Makefile.config
/src/libexpr/parser-tab.hh
/src/libexpr/parser-tab.output
/src/libexpr/nix.tbl
/src/libexpr/tests/libnixexpr-tests
/tests/unit/libexpr/libnixexpr-tests
# /src/libstore/
*.gen.*
/src/libstore/tests/libnixstore-tests
/tests/unit/libstore/libnixstore-tests
# /src/libutil/
/src/libutil/tests/libnixutil-tests
/tests/unit/libutil/libnixutil-tests
/src/nix/nix
@@ -138,9 +138,7 @@ nix-rust/target
result
# IDE
.vscode/
.idea/
# clangd and possibly more
.cache/

View File

@@ -1 +1 @@
2.19.0
2.18.2

View File

@@ -24,33 +24,25 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy).
## Making changes to Nix
1. Search for related issues that cover what you're going to work on.
It could help to mention there that you will work on the issue.
Issues labeled [good first issue](https://github.com/NixOS/nix/labels/good%20first%20issue) should be relatively easy to fix and are likely to get merged quickly.
Pull requests addressing issues labeled [idea approved](https://github.com/NixOS/nix/labels/idea%20approved) or [RFC](https://github.com/NixOS/nix/labels/RFC) are especially welcomed by maintainers and will receive prioritised review.
If you are proficient with C++, addressing one of the [popular issues](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) will be highly appreciated by maintainers and Nix users all over the world.
For far-reaching changes, please investigate possible blockers and design implications, and coordinate with maintainers before investing too much time in writing code that may not end up getting merged.
If there is no relevant issue yet and you're not sure whether your change is likely to be accepted, [open an issue](https://github.com/NixOS/nix/issues/new/choose) yourself.
2. Check for [pull requests](https://github.com/NixOS/nix/pulls) that might already cover the contribution you are about to make.
There are many open pull requests that might already do what you intend to work on.
1. Check for [pull requests](https://github.com/NixOS/nix/pulls) that might already cover the contribution you are about to make.
There are many open pull requests that might already do what you intent to work on.
You can use [labels](https://github.com/NixOS/nix/labels) to filter for relevant topics.
2. Search for related issues that cover what you're going to work on. It could help to mention there that you will work on the issue.
Issues labeled [good first issue](https://github.com/NixOS/nix/labels/good-first-issue) should be relatively easy to fix and are likely to get merged quickly.
Pull requests addressing issues labeled [idea approved](https://github.com/NixOS/nix/labels/idea%20approved) are especially welcomed by maintainers and will receive prioritised review.
3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests.
For contributions to the command line interface, please check the [CLI guidelines](https://nixos.org/manual/nix/unstable/contributing/cli-guideline.html).
4. Make your change!
4. Make your changes!
5. [Create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) for your changes.
* Clearly explain the problem that you're solving.
Link related issues to inform interested parties and future contributors about your change.
If your pull request closes one or multiple issues, mention that in the description using `Closes: #<number>`, as it will then happen automatically when your change is merged.
* Link related issues in your pull request to inform interested parties and future contributors about your change.
* Make sure to have [a clean history of commits on your branch by using rebase](https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request).
If your pull request closes one or multiple issues, note that in the description using `Closes: #<number>`, as it will then happen automatically when your change is merged.
* [Mark the pull request as draft](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request) if you're not done with the changes.
6. Do not expect your pull request to be reviewed immediately.

View File

@@ -1,7 +1,3 @@
-include Makefile.config
clean-files += Makefile.config
ifeq ($(ENABLE_BUILD), yes)
makefiles = \
mk/precompiled-headers.mk \
local.mk \
@@ -22,18 +18,17 @@ makefiles = \
misc/upstart/local.mk \
doc/manual/local.mk \
doc/internal-api/local.mk
endif
ifeq ($(ENABLE_BUILD)_$(ENABLE_TESTS), yes_yes)
UNIT_TEST_ENV = _NIX_TEST_UNIT_DATA=unit-test-data
makefiles += \
src/libutil/tests/local.mk \
src/libstore/tests/local.mk \
src/libexpr/tests/local.mk
endif
-include Makefile.config
ifeq ($(ENABLE_TESTS), yes)
ifeq ($(tests), yes)
makefiles += \
tests/unit/libutil/local.mk \
tests/unit/libutil-support/local.mk \
tests/unit/libstore/local.mk \
tests/unit/libstore-support/local.mk \
tests/unit/libexpr/local.mk \
tests/unit/libexpr-support/local.mk \
tests/functional/local.mk \
tests/functional/ca/local.mk \
tests/functional/dyn-drv/local.mk \

View File

@@ -28,8 +28,6 @@ SODIUM_LIBS = @SODIUM_LIBS@
SQLITE3_LIBS = @SQLITE3_LIBS@
bash = @bash@
bindir = @bindir@
checkbindir = @checkbindir@
checklibdir = @checklibdir@
datadir = @datadir@
datarootdir = @datarootdir@
doc_generate = @doc_generate@
@@ -48,7 +46,5 @@ sandbox_shell = @sandbox_shell@
storedir = @storedir@
sysconfdir = @sysconfdir@
system = @system@
ENABLE_BUILD = @ENABLE_BUILD@
ENABLE_TESTS = @ENABLE_TESTS@
INSTALL_UNIT_TESTS = @INSTALL_UNIT_TESTS@
tests = @tests@
internal_api_docs = @internal_api_docs@

View File

@@ -7,20 +7,21 @@ Nix is a powerful package manager for Linux and other Unix systems that makes pa
management reliable and reproducible. Please refer to the [Nix manual](https://nixos.org/nix/manual)
for more details.
## Installation and first steps
## Installation
Visit [nix.dev](https://nix.dev) for [installation instructions](https://nix.dev/tutorials/install-nix) and [beginner tutorials](https://nix.dev/tutorials/first-steps).
On Linux and macOS the easiest way to install Nix is to run the following shell command
(as a user other than root):
Full reference documentation can be found in the [Nix manual](https://nixos.org/nix/manual).
```console
$ curl -L https://nixos.org/nix/install | sh
```
Information on additional installation methods is available on the [Nix download page](https://nixos.org/download.html).
## Building And Developing
See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacking.html) in our manual for instruction on how to
set up a development environment and build Nix from source.
## Contributing
Check the [contributing guide](./CONTRIBUTING.md) if you want to get involved with developing Nix.
to set up a development environment and build Nix from source.
## Additional Resources
@@ -28,6 +29,7 @@ Check the [contributing guide](./CONTRIBUTING.md) if you want to get involved wi
- [Nix jobsets on hydra.nixos.org](https://hydra.nixos.org/project/nix)
- [NixOS Discourse](https://discourse.nixos.org/)
- [Matrix - #nix:nixos.org](https://matrix.to/#/#nix:nixos.org)
- [IRC - #nixos on libera.chat](irc://irc.libera.chat/#nixos)
## License

View File

@@ -59,18 +59,12 @@ index b5d71e62..aed7b0bf 100644
GC_bool found_me = FALSE;
size_t nthreads = 0;
int i;
@@ -851,6 +853,37 @@ GC_INNER void GC_push_all_stacks(void)
@@ -851,6 +853,31 @@ GC_INNER void GC_push_all_stacks(void)
hi = p->altstack + p->altstack_size;
/* FIXME: Need to scan the normal stack too, but how ? */
/* FIXME: Assume stack grows down */
+ } else {
+#ifdef HAVE_PTHREAD_ATTR_GET_NP
+ if (!pthread_attr_init(&pattr)
+ || !pthread_attr_get_np(p->id, &pattr))
+#else /* HAVE_PTHREAD_GETATTR_NP */
+ if (pthread_getattr_np(p->id, &pattr))
+#endif
+ {
+ if (pthread_getattr_np(p->id, &pattr)) {
+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
+ }
+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) {

4
bootstrap.sh Executable file
View File

@@ -0,0 +1,4 @@
#! /bin/sh -e
rm -f aclocal.m4
mkdir -p config
exec autoreconf -vfi

View File

@@ -68,9 +68,6 @@ case "$host_os" in
esac
ENSURE_NO_GCC_BUG_80431
# Check for pubsetbuf.
AC_MSG_CHECKING([for pubsetbuf])
AC_LANG_PUSH(C++)
@@ -155,29 +152,12 @@ if test "x$GCC_ATOMIC_BUILTINS_NEED_LIBATOMIC" = xyes; then
LDFLAGS="-latomic $LDFLAGS"
fi
# Running the functional tests without building Nix is useful for testing
# different pre-built versions of Nix against each other.
AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]),
ENABLE_BUILD=$enableval, ENABLE_BUILD=yes)
AC_SUBST(ENABLE_BUILD)
# Building without tests is useful for bootstrapping with a smaller footprint
# or running the tests in a separate derivation. Otherwise, we do compile and
# run them.
AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[Do not build the tests]),
ENABLE_TESTS=$enableval, ENABLE_TESTS=yes)
AC_SUBST(ENABLE_TESTS)
AC_ARG_ENABLE(install-unit-tests, AS_HELP_STRING([--enable-install-unit-tests],[Install the unit tests for running later (default no)]),
INSTALL_UNIT_TESTS=$enableval, INSTALL_UNIT_TESTS=no)
AC_SUBST(INSTALL_UNIT_TESTS)
AC_ARG_WITH(check-bin-dir, AS_HELP_STRING([--with-check-bin-dir=PATH],[path to install unit tests for running later (defaults to $libexecdir/nix)]),
checkbindir=$withval, checkbindir=$libexecdir/nix)
AC_SUBST(checkbindir)
AC_ARG_WITH(check-lib-dir, AS_HELP_STRING([--with-check-lib-dir=PATH],[path to install unit tests for running later (defaults to $libdir)]),
checklibdir=$withval, checklibdir=$libdir)
AC_SUBST(checklibdir)
tests=$enableval, tests=yes)
AC_SUBST(tests)
# Building without API docs is the default as Nix' C++ interfaces are internal and unstable.
AC_ARG_ENABLE(internal_api_docs, AS_HELP_STRING([--enable-internal-api-docs],[Build API docs for Nix's internal unstable C++ interfaces]),
@@ -309,7 +289,7 @@ if test "$gc" = yes; then
fi
if test "$ENABLE_TESTS" = yes; then
if test "$tests" = yes; then
# Look for gtest.
PKG_CHECK_MODULES([GTEST], [gtest_main])

View File

@@ -39,38 +39,25 @@ INPUT = \
src/libcmd \
src/libexpr \
src/libexpr/flake \
src/libexpr/tests \
src/libexpr/tests/value \
tests/unit/libexpr \
tests/unit/libexpr/value \
tests/unit/libexpr/test \
tests/unit/libexpr/test/value \
src/libexpr/value \
src/libfetchers \
src/libmain \
src/libstore \
src/libstore/build \
src/libstore/builtins \
src/libstore/tests \
tests/unit/libstore \
tests/unit/libstore/test \
src/libutil \
src/libutil/tests \
tests/unit/libutil \
tests/unit/libutil/test \
src/nix \
src/nix-env \
src/nix-store
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
# in the source code. If set to NO, only conditional compilation will be
# performed. Macro expansion can be done in a controlled way by setting
# EXPAND_ONLY_PREDEF to YES.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
# EXPAND_AS_DEFINED tags.
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by the
# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of
@@ -78,16 +65,3 @@ EXPAND_ONLY_PREDEF = YES
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH = @RAPIDCHECK_HEADERS@
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
# macro definition that is found in the sources will be used. Use the PREDEFINED
# tag if you want to use a different macro definition that overrules the
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED = \
DECLARE_COMMON_SERIALISER \
DECLARE_WORKER_SERIALISER \
DECLARE_SERVE_SERIALISER \
LENGTH_PREFIXED_PROTO_HELPER

View File

@@ -1,9 +1,8 @@
let
inherit (builtins)
attrNames attrValues fromJSON listToAttrs mapAttrs groupBy
attrNames attrValues fromJSON listToAttrs mapAttrs
concatStringsSep concatMap length lessThan replaceStrings sort;
inherit (import <nix/utils.nix>) attrsToList concatStrings optionalString filterAttrs trim squash unique;
showStoreDocs = import ./generate-store-info.nix;
inherit (import ./utils.nix) concatStrings optionalString filterAttrs trim squash unique showSettings;
in
inlineHTML: commandDump:
@@ -41,15 +40,15 @@ let
showArgument = arg: "*${arg.label}*" + optionalString (! arg ? arity) "...";
arguments = concatStringsSep " " (map showArgument args);
in ''
`${command}` [*option*...] ${arguments}
`${command}` [*option*...] ${arguments}
'';
maybeSubcommands = optionalString (details ? commands && details.commands != {})
''
where *subcommand* is one of the following:
''
where *subcommand* is one of the following:
${subcommands}
'';
${subcommands}
'';
subcommands = if length categories > 1
then listCategories
@@ -71,38 +70,29 @@ let
* [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
'';
# FIXME: this is a hack.
# store parameters should not be part of command documentation to begin
# with, but instead be rendered on separate pages.
maybeStoreDocs = optionalString (details ? doc)
(replaceStrings [ "@stores@" ] [ (showStoreDocs inlineHTML commandInfo.stores) ] details.doc);
maybeStoreDocs = optionalString
(details ? doc)
(replaceStrings ["@stores@"] [storeDocs] details.doc);
maybeOptions = let
allVisibleOptions = filterAttrs
(_: o: ! o.hiddenCategory)
(details.flags // toplevel.flags);
in optionalString (allVisibleOptions != {}) ''
maybeOptions = optionalString (details.flags != {}) ''
# Options
${showOptions inlineHTML allVisibleOptions}
> **Note**
>
> See [`man nix.conf`](@docroot@/command-ref/conf-file.md#command-line-flags) for overriding configuration settings with command line flags.
${showOptions details.flags toplevel.flags}
'';
showOptions = inlineHTML: allOptions:
showOptions = options: commonOptions:
let
showCategory = cat: opts: ''
${optionalString (cat != "") "## ${cat}"}
allOptions = options // commonOptions;
showCategory = cat: ''
${optionalString (cat != "") "**${cat}:**"}
${concatStringsSep "\n" (attrValues (mapAttrs showOption opts))}
${listOptions (filterAttrs (n: v: v.category == cat) allOptions)}
'';
listOptions = opts: concatStringsSep "\n" (attrValues (mapAttrs showOption opts));
showOption = name: option:
let
result = trim ''
- ${item}
${option.description}
'';
item = if inlineHTML
@@ -115,13 +105,8 @@ let
(option ? labels)
(concatStringsSep " " (map (s: "*${s}*") option.labels));
in result;
categories = mapAttrs
# Convert each group from a list of key-value pairs back to an attrset
(_: listToAttrs)
(groupBy
(cmd: cmd.value.category)
(attrsToList allOptions));
in concatStrings (attrValues (mapAttrs showCategory categories));
categories = sort lessThan (unique (map (cmd: cmd.category) (attrValues allOptions)));
in concatStrings (map showCategory categories);
in squash result;
appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name;
@@ -153,4 +138,35 @@ let
" - [${page.command}](command-ref/new-cli/${page.name})";
in concatStringsSep "\n" (map showEntry manpages) + "\n";
storeDocs =
let
showStore = name: { settings, doc, experimentalFeature }:
let
experimentalFeatureNote = optionalString (experimentalFeature != null) ''
> **Warning**
> This store is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
To use this store, you need to make sure the corresponding experimental feature,
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
is enabled.
For example, include the following in [`nix.conf`](#):
```
extra-experimental-features = ${experimentalFeature}
```
'';
in ''
## ${name}
${doc}
${experimentalFeatureNote}
**Settings**:
${showSettings { inherit inlineHTML; } settings}
'';
in concatStrings (attrValues (mapAttrs showStore commandInfo.stores));
in (listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; }

View File

@@ -1,66 +0,0 @@
let
inherit (builtins) attrValues concatStringsSep isAttrs isBool mapAttrs;
inherit (import ./utils.nix) concatStrings indent optionalString squash;
in
# `inlineHTML` is a hack to accommodate inconsistent output from `lowdown`
{ prefix, inlineHTML ? true }: settingsInfo:
let
showSetting = prefix: setting: { description, documentDefault, defaultValue, aliases, value, experimentalFeature }:
let
result = squash ''
- ${item}
${indent " " body}
'';
item = if inlineHTML
then ''<span id="${prefix}-${setting}">[`${setting}`](#${prefix}-${setting})</span>''
else "`${setting}`";
# separate body to cleanly handle indentation
body = ''
${description}
${experimentalFeatureNote}
**Default:** ${showDefault documentDefault defaultValue}
${showAliases aliases}
'';
experimentalFeatureNote = optionalString (experimentalFeature != null) ''
> **Warning**
> This setting is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
To change this setting, you need to make sure the corresponding experimental feature,
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
is enabled.
For example, include the following in [`nix.conf`](#):
```
extra-experimental-features = ${experimentalFeature}
${setting} = ...
```
'';
showDefault = documentDefault: defaultValue:
if documentDefault then
# a StringMap value type is specified as a string, but
# this shows the value type. The empty stringmap is `null` in
# JSON, but that converts to `{ }` here.
if defaultValue == "" || defaultValue == [] || isAttrs defaultValue
then "*empty*"
else if isBool defaultValue then
if defaultValue then "`true`" else "`false`"
else "`${toString defaultValue}`"
else "*machine-specific*";
showAliases = aliases:
optionalString (aliases != [])
"**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}";
in result;
in concatStrings (attrValues (mapAttrs (showSetting prefix) settingsInfo))

View File

@@ -1,45 +0,0 @@
let
inherit (builtins) attrValues mapAttrs;
inherit (import ./utils.nix) concatStrings optionalString;
showSettings = import ./generate-settings.nix;
in
inlineHTML: storesInfo:
let
showStore = name: { settings, doc, experimentalFeature }:
let
result = ''
## ${name}
${doc}
${experimentalFeatureNote}
### Settings
${showSettings { prefix = "store-${slug}"; inherit inlineHTML; } settings}
'';
# markdown doesn't like spaces in URLs
slug = builtins.replaceStrings [ " " ] [ "-" ] name;
experimentalFeatureNote = optionalString (experimentalFeature != null) ''
> **Warning**
> This store is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
To use this store, you need to make sure the corresponding experimental feature,
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
is enabled.
For example, include the following in [`nix.conf`](#):
```
extra-experimental-features = ${experimentalFeature}
```
'';
in result;
in concatStrings (attrValues (mapAttrs showStore storesInfo))

View File

@@ -32,7 +32,7 @@ dummy-env = env -i \
NIX_STATE_DIR=/dummy \
NIX_CONFIG='cores = 0'
nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -I nix=doc/manual --store dummy:// --impure --raw
nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -I nix/corepkgs=corepkgs --store dummy:// --impure --raw
# re-implement mdBook's include directive to make it usable for terminal output and for proper @docroot@ substitution
define process-includes
@@ -96,14 +96,14 @@ $(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/command-ref/new-cli $(d)/sr
@cp $< $@
@$(call process-includes,$@,$@)
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage.nix $(d)/generate-settings.nix $(d)/generate-store-info.nix $(bindir)/nix
$(d)/src/command-ref/new-cli: $(d)/nix.json $(d)/utils.nix $(d)/generate-manpage.nix $(bindir)/nix
@rm -rf $@ $@.tmp
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix true (builtins.readFile $<)'
@mv $@.tmp $@
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/generate-settings.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features-shortlist.md $(bindir)/nix
$(d)/src/command-ref/conf-file.md: $(d)/conf-file.json $(d)/utils.nix $(d)/src/command-ref/conf-file-prefix.md $(d)/src/command-ref/experimental-features-shortlist.md $(bindir)/nix
@cat doc/manual/src/command-ref/conf-file-prefix.md > $@.tmp
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-settings.nix { prefix = "conf"; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
$(trace-gen) $(nix-eval) --expr '(import doc/manual/utils.nix).showSettings { inlineHTML = true; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
@mv $@.tmp $@
$(d)/nix.json: $(bindir)/nix
@@ -125,7 +125,7 @@ $(d)/src/command-ref/experimental-features-shortlist.md: $(d)/xp-features.json $
@mv $@.tmp $@
$(d)/xp-features.json: $(bindir)/nix
$(trace-gen) $(dummy-env) $(bindir)/nix __dump-xp-features > $@.tmp
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp
@mv $@.tmp $@
$(d)/src/language/builtins.md: $(d)/language.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(bindir)/nix
@@ -141,7 +141,7 @@ $(d)/src/language/builtin-constants.md: $(d)/language.json $(d)/generate-builtin
@mv $@.tmp $@
$(d)/language.json: $(bindir)/nix
$(trace-gen) $(dummy-env) $(bindir)/nix __dump-language > $@.tmp
$(trace-gen) $(dummy-env) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-language > $@.tmp
@mv $@.tmp $@
# Generate the HTML manual.
@@ -173,10 +173,6 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli
done
@touch $@
# the `! -name 'contributing.md'` filter excludes the one place where
# `@docroot@` is to be preserved for documenting the mechanism
# FIXME: maybe contributing guides should live right next to the code
# instead of in the manual
$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md
$(trace-gen) \
tmp="$$(mktemp -d)"; \
@@ -184,7 +180,7 @@ $(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/
find "$$tmp" -name '*.md' | while read -r file; do \
$(call process-includes,$$file,$$file); \
done; \
find "$$tmp" -name '*.md' ! -name 'documentation.md' | while read -r file; do \
find "$$tmp" -name '*.md' | while read -r file; do \
docroot="$$(realpath --relative-to="$$(dirname "$$file")" $$tmp/manual/src)"; \
sed -i "s,@docroot@,$$docroot,g" "$$file"; \
done; \

View File

@@ -1,7 +1,9 @@
// redirect rules for anchors ensure backwards compatibility of URLs.
// this must be done on the client side, as web servers do not see the anchor part of the URL.
// redirect rules for URL fragments (client-side) to prevent link rot.
// this must be done on the client side, as web servers do not see the fragment part of the URL.
// it will only work with JavaScript enabled in the browser, but this is the best we can do here.
// see ./_redirects for path redirects (client-side)
// redirections are declared as follows:
// redirects are declared as follows:
// each entry has as its key a path matching the requested URL path, relative to the mdBook document root.
//
// IMPORTANT: it must specify the full path with file name and suffix
@@ -19,6 +21,7 @@ const redirects = {
"chap-distributed-builds": "advanced-topics/distributed-builds.html",
"chap-post-build-hook": "advanced-topics/post-build-hook.html",
"chap-post-build-hook-caveats": "advanced-topics/post-build-hook.html#implementation-caveats",
"chap-writing-nix-expressions": "language/index.html",
"part-command-ref": "command-ref/command-ref.html",
"conf-allow-import-from-derivation": "command-ref/conf-file.html#conf-allow-import-from-derivation",
"conf-allow-new-privileges": "command-ref/conf-file.html#conf-allow-new-privileges",
@@ -336,13 +339,14 @@ const redirects = {
"simple-values": "#primitives",
"lists": "#list",
"strings": "#string",
"lists": "#list",
"attribute-sets": "#attribute-set",
},
"installation/installing-binary.html": {
"linux": "uninstall.html#linux",
"macos": "uninstall.html#macos",
"uninstalling": "uninstall.html",
},
}
"contributing/hacking.html": {
"nix-with-flakes": "#building-nix-with-flakes",
"classic-nix": "#building-nix",
@@ -354,7 +358,6 @@ const redirects = {
"installer-tests": "testing.html#installer-tests",
"one-time-setup": "testing.html#one-time-setup",
"using-the-ci-generated-installer-for-manual-testing": "testing.html#using-the-ci-generated-installer-for-manual-testing",
"characterization-testing": "#characterisation-testing-unit",
}
};

View File

@@ -16,31 +16,26 @@
- [Environment Variables](installation/env-variables.md)
- [Upgrading Nix](installation/upgrading.md)
- [Uninstalling Nix](installation/uninstall.md)
- [Nix Store](store/index.md)
- [File System Object](store/file-system-object.md)
- [Store Object](store/store-object.md)
- [Store Path](store/store-path.md)
- [Nix Language](language/index.md)
- [Data Types](language/values.md)
- [Language Constructs](language/constructs.md)
- [String interpolation](language/string-interpolation.md)
- [Lookup path](language/constructs/lookup-path.md)
- [Operators](language/operators.md)
- [Derivations](language/derivations.md)
- [Advanced Attributes](language/advanced-attributes.md)
- [Import From Derivation](language/import-from-derivation.md)
- [Built-in Constants](language/builtin-constants.md)
- [Built-in Functions](language/builtins.md)
- [Package Management](package-management/package-management.md)
- [Basic Package Management](package-management/basic-package-mgmt.md)
- [Profiles](package-management/profiles.md)
- [Garbage Collection](package-management/garbage-collection.md)
- [Garbage Collector Roots](package-management/garbage-collector-roots.md)
- [Advanced Topics](advanced-topics/advanced-topics.md)
- [Sharing Packages Between Machines](package-management/sharing-packages.md)
- [Serving a Nix store via HTTP](package-management/binary-cache-substituter.md)
- [Copying Closures via SSH](package-management/copy-closure.md)
- [Serving a Nix store via SSH](package-management/ssh-substituter.md)
- [Serving a Nix store via S3](package-management/s3-substituter.md)
- [Nix Language](language/index.md)
- [Data Types](language/values.md)
- [Language Constructs](language/constructs.md)
- [String interpolation](language/string-interpolation.md)
- [Operators](language/operators.md)
- [Derivations](language/derivations.md)
- [Advanced Attributes](language/advanced-attributes.md)
- [Built-in Constants](language/builtin-constants.md)
- [Built-in Functions](language/builtins.md)
- [Advanced Topics](advanced-topics/advanced-topics.md)
- [Remote Builds](advanced-topics/distributed-builds.md)
- [Tuning Cores and Jobs](advanced-topics/cores-vs-jobs.md)
- [Verifying Build Reproducibility](advanced-topics/diff-hook.md)
@@ -102,6 +97,7 @@
- [Channels](command-ref/files/channels.md)
- [Default Nix expression](command-ref/files/default-nix-expression.md)
- [Architecture and Design](architecture/architecture.md)
- [File System Object](architecture/file-system-object.md)
- [Protocols](protocols/protocols.md)
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
- [Derivation "ATerm" file format](protocols/derivation-aterm.md)
@@ -109,12 +105,10 @@
- [Contributing](contributing/contributing.md)
- [Hacking](contributing/hacking.md)
- [Testing](contributing/testing.md)
- [Documentation](contributing/documentation.md)
- [Experimental Features](contributing/experimental-features.md)
- [CLI guideline](contributing/cli-guideline.md)
- [C++ style guide](contributing/cxx.md)
- [Release Notes](release-notes/release-notes.md)
- [Release 2.19 (2023-11-17)](release-notes/rl-2.19.md)
- [Release 2.18 (2023-09-20)](release-notes/rl-2.18.md)
- [Release 2.17 (2023-07-24)](release-notes/rl-2.17.md)
- [Release 2.16 (2023-05-31)](release-notes/rl-2.16.md)

30
doc/manual/src/_redirects Normal file
View File

@@ -0,0 +1,30 @@
# redirect rules for paths (server-side) to prevent link rot.
# see ./redirects.js for redirects based on URL fragments (client-side)
#
# concrete user story this supports:
# - user finds URL to the manual for Nix x.y
# - Nix x.z (z > y) is the most recent release
# - updating the version in the URL will show the right thing
#
# format documentation:
# - https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file
# - https://docs.netlify.com/routing/redirects/redirect-options/
#
# conventions:
# - always force (<CODE>!) since this allows re-using file names
# - group related paths to ease readability
# - always append new redirects to the end of the file
# - redirects that should have been there but are missing can be inserted where they belong
/expressions/expression-language /language/ 301!
/expressions/language-values /language/values 301!
/expressions/language-constructs /language/constructs 301!
/expressions/language-operators /language/operators 301!
/expressions/* /language/:splat 301!
/package-management/basic-package-mgmt /command-ref/nix-env 301!
/package-management/channels* /command-ref/nix-channel 301!
/package-management/s3-substituter* /command-ref/new-cli/nix3-help-stores#s3-binary-cache-store 301!

View File

@@ -12,14 +12,14 @@ machine is accessible via SSH and that it has Nix installed. You can
test whether connecting to the remote Nix instance works, e.g.
```console
$ nix store info --store ssh://mac
$ nix store ping --store ssh://mac
```
will try to connect to the machine named `mac`. It is possible to
specify an SSH identity file as part of the remote store URI, e.g.
```console
$ nix store info --store ssh://mac?ssh-key=/home/alice/my-key
$ nix store ping --store ssh://mac?ssh-key=/home/alice/my-key
```
Since builds should be non-interactive, the key should not have a

View File

@@ -17,8 +17,9 @@ the build loop.
# Prerequisites
This tutorial assumes you have configured an [S3-compatible binary cache](@docroot@/command-ref/new-cli/nix3-help-stores.md#s3-binary-cache-store) as a [substituter](../command-ref/conf-file.md#conf-substituters),
and that the `root` user's default AWS profile can upload to the bucket.
This tutorial assumes you have [configured an S3-compatible binary
cache](../package-management/s3-substituter.md), and that the `root`
user's default AWS profile can upload to the bucket.
# Set up a Signing Key
@@ -68,8 +69,6 @@ exec nix copy --to "s3://example-nix-cache" $OUT_PATHS
> store sign`. Nix guarantees the paths will not contain any spaces,
> however a store path might contain glob characters. The `set -f`
> disables globbing in the shell.
> If you want to upload the `.drv` file too, the `$DRV_PATH` variable
> is also defined for the script and works just like `$OUT_PATHS`.
Then make sure the hook program is executable by the `root` user:

View File

@@ -59,11 +59,10 @@ The [Nix language](../language/index.md) evaluator transforms Nix expressions in
The command line interface and Nix expressions are what users deal with most.
> **Note**
>
> The Nix language itself does not have a notion of *packages* or *configurations*.
> As far as we are concerned here, the inputs and results of a build plan are just data.
Underlying the command line interface and the Nix language evaluator is the [Nix store](../store/index.md), a mechanism to keep track of build plans, data, and references between them.
Underlying the command line interface and the Nix language evaluator is the [Nix store](../glossary.md#gloss-store), a mechanism to keep track of build plans, data, and references between them.
It can also execute build plans to produce new data, which are made available to the operating system as files.
A build plan itself is a series of *build tasks*, together with their build inputs.

View File

@@ -2,124 +2,109 @@
Most Nix commands interpret the following environment variables:
- <span id="env-IN_NIX_SHELL">[`IN_NIX_SHELL`](#env-IN_NIX_SHELL)</span>
- <span id="env-IN_NIX_SHELL">[`IN_NIX_SHELL`](#env-IN_NIX_SHELL)</span>\
Indicator that tells if the current environment was set up by
`nix-shell`. It can have the values `pure` or `impure`.
Indicator that tells if the current environment was set up by
`nix-shell`. It can have the values `pure` or `impure`.
- <span id="env-NIX_PATH">[`NIX_PATH`](#env-NIX_PATH)</span>\
A colon-separated list of directories used to look up the location of Nix
expressions using [paths](@docroot@/language/values.md#type-path)
enclosed in angle brackets (i.e., `<path>`),
e.g. `/home/eelco/Dev:/etc/nixos`. It can be extended using the
[`-I` option](@docroot@/command-ref/opt-common.md#opt-I).
- <span id="env-NIX_PATH">[`NIX_PATH`](#env-NIX_PATH)</span>
If `NIX_PATH` is not set at all, Nix will fall back to the following list in [impure](@docroot@/command-ref/conf-file.md#conf-pure-eval) and [unrestricted](@docroot@/command-ref/conf-file.md#conf-restrict-eval) evaluation mode:
A colon-separated list of directories used to look up the location of Nix
expressions using [paths](@docroot@/language/values.md#type-path)
enclosed in angle brackets (i.e., `<path>`),
e.g. `/home/eelco/Dev:/etc/nixos`. It can be extended using the
[`-I` option](@docroot@/command-ref/opt-common.md#opt-I).
1. `$HOME/.nix-defexpr/channels`
2. `nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixpkgs`
3. `/nix/var/nix/profiles/per-user/root/channels`
If `NIX_PATH` is not set at all, Nix will fall back to the following list in [impure](@docroot@/command-ref/conf-file.md#conf-pure-eval) and [unrestricted](@docroot@/command-ref/conf-file.md#conf-restrict-eval) evaluation mode:
If `NIX_PATH` is set to an empty string, resolving search paths will always fail.
For example, attempting to use `<nixpkgs>` will produce:
1. `$HOME/.nix-defexpr/channels`
2. `nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixpkgs`
3. `/nix/var/nix/profiles/per-user/root/channels`
error: file 'nixpkgs' was not found in the Nix search path
If `NIX_PATH` is set to an empty string, resolving search paths will always fail.
For example, attempting to use `<nixpkgs>` will produce:
- <span id="env-NIX_IGNORE_SYMLINK_STORE">[`NIX_IGNORE_SYMLINK_STORE`](#env-NIX_IGNORE_SYMLINK_STORE)</span>\
Normally, the Nix store directory (typically `/nix/store`) is not
allowed to contain any symlink components. This is to prevent
“impure” builds. Builders sometimes “canonicalise” paths by
resolving all symlink components. Thus, builds on different machines
(with `/nix/store` resolving to different locations) could yield
different results. This is generally not a problem, except when
builds are deployed to machines where `/nix/store` resolves
differently. If you are sure that youre not going to do that, you
can set `NIX_IGNORE_SYMLINK_STORE` to `1`.
error: file 'nixpkgs' was not found in the Nix search path
Note that if youre symlinking the Nix store so that you can put it
on another file system than the root file system, on Linux youre
better off using `bind` mount points, e.g.,
- <span id="env-NIX_IGNORE_SYMLINK_STORE">[`NIX_IGNORE_SYMLINK_STORE`](#env-NIX_IGNORE_SYMLINK_STORE)</span>
```console
$ mkdir /nix
$ mount -o bind /mnt/otherdisk/nix /nix
```
Normally, the Nix store directory (typically `/nix/store`) is not
allowed to contain any symlink components. This is to prevent
“impure” builds. Builders sometimes “canonicalise” paths by
resolving all symlink components. Thus, builds on different machines
(with `/nix/store` resolving to different locations) could yield
different results. This is generally not a problem, except when
builds are deployed to machines where `/nix/store` resolves
differently. If you are sure that youre not going to do that, you
can set `NIX_IGNORE_SYMLINK_STORE` to `1`.
Consult the mount 8 manual page for details.
Note that if youre symlinking the Nix store so that you can put it
on another file system than the root file system, on Linux youre
better off using `bind` mount points, e.g.,
- <span id="env-NIX_STORE_DIR">[`NIX_STORE_DIR`](#env-NIX_STORE_DIR)</span>\
Overrides the location of the Nix store (default `prefix/store`).
```console
$ mkdir /nix
$ mount -o bind /mnt/otherdisk/nix /nix
```
- <span id="env-NIX_DATA_DIR">[`NIX_DATA_DIR`](#env-NIX_DATA_DIR)</span>\
Overrides the location of the Nix static data directory (default
`prefix/share`).
Consult the mount 8 manual page for details.
- <span id="env-NIX_LOG_DIR">[`NIX_LOG_DIR`](#env-NIX_LOG_DIR)</span>\
Overrides the location of the Nix log directory (default
`prefix/var/log/nix`).
- <span id="env-NIX_STORE_DIR">[`NIX_STORE_DIR`](#env-NIX_STORE_DIR)</span>
- <span id="env-NIX_STATE_DIR">[`NIX_STATE_DIR`](#env-NIX_STATE_DIR)</span>\
Overrides the location of the Nix state directory (default
`prefix/var/nix`).
Overrides the location of the Nix store (default `prefix/store`).
- <span id="env-NIX_CONF_DIR">[`NIX_CONF_DIR`](#env-NIX_CONF_DIR)</span>\
Overrides the location of the system Nix configuration directory
(default `prefix/etc/nix`).
- <span id="env-NIX_DATA_DIR">[`NIX_DATA_DIR`](#env-NIX_DATA_DIR)</span>
- <span id="env-NIX_CONFIG">[`NIX_CONFIG`](#env-NIX_CONFIG)</span>\
Applies settings from Nix configuration from the environment.
The content is treated as if it was read from a Nix configuration file.
Settings are separated by the newline character.
Overrides the location of the Nix static data directory (default
`prefix/share`).
- <span id="env-NIX_USER_CONF_FILES">[`NIX_USER_CONF_FILES`](#env-NIX_USER_CONF_FILES)</span>\
Overrides the location of the Nix user configuration files to load from.
- <span id="env-NIX_LOG_DIR">[`NIX_LOG_DIR`](#env-NIX_LOG_DIR)</span>
The default are the locations according to the [XDG Base Directory Specification].
See the [XDG Base Directories](#xdg-base-directories) sub-section for details.
Overrides the location of the Nix log directory (default
`prefix/var/log/nix`).
The variable is treated as a list separated by the `:` token.
- <span id="env-NIX_STATE_DIR">[`NIX_STATE_DIR`](#env-NIX_STATE_DIR)</span>
- <span id="env-TMPDIR">[`TMPDIR`](#env-TMPDIR)</span>\
Use the specified directory to store temporary files. In particular,
this includes temporary build directories; these can take up
substantial amounts of disk space. The default is `/tmp`.
Overrides the location of the Nix state directory (default
`prefix/var/nix`).
- <span id="env-NIX_REMOTE">[`NIX_REMOTE`](#env-NIX_REMOTE)</span>\
This variable should be set to `daemon` if you want to use the Nix
daemon to execute Nix operations. This is necessary in [multi-user
Nix installations](@docroot@/installation/multi-user.md). If the Nix
daemon's Unix socket is at some non-standard path, this variable
should be set to `unix://path/to/socket`. Otherwise, it should be
left unset.
- <span id="env-NIX_CONF_DIR">[`NIX_CONF_DIR`](#env-NIX_CONF_DIR)</span>
- <span id="env-NIX_SHOW_STATS">[`NIX_SHOW_STATS`](#env-NIX_SHOW_STATS)</span>\
If set to `1`, Nix will print some evaluation statistics, such as
the number of values allocated.
Overrides the location of the system Nix configuration directory
(default `prefix/etc/nix`).
- <span id="env-NIX_COUNT_CALLS">[`NIX_COUNT_CALLS`](#env-NIX_COUNT_CALLS)</span>\
If set to `1`, Nix will print how often functions were called during
Nix expression evaluation. This is useful for profiling your Nix
expressions.
- <span id="env-NIX_CONFIG">[`NIX_CONFIG`](#env-NIX_CONFIG)</span>
Applies settings from Nix configuration from the environment.
The content is treated as if it was read from a Nix configuration file.
Settings are separated by the newline character.
- <span id="env-NIX_USER_CONF_FILES">[`NIX_USER_CONF_FILES`](#env-NIX_USER_CONF_FILES)</span>
Overrides the location of the Nix user configuration files to load from.
The default are the locations according to the [XDG Base Directory Specification].
See the [XDG Base Directories](#xdg-base-directories) sub-section for details.
The variable is treated as a list separated by the `:` token.
- <span id="env-TMPDIR">[`TMPDIR`](#env-TMPDIR)</span>
Use the specified directory to store temporary files. In particular,
this includes temporary build directories; these can take up
substantial amounts of disk space. The default is `/tmp`.
- <span id="env-NIX_REMOTE">[`NIX_REMOTE`](#env-NIX_REMOTE)</span>
This variable should be set to `daemon` if you want to use the Nix
daemon to execute Nix operations. This is necessary in [multi-user
Nix installations](@docroot@/installation/multi-user.md). If the Nix
daemon's Unix socket is at some non-standard path, this variable
should be set to `unix://path/to/socket`. Otherwise, it should be
left unset.
- <span id="env-NIX_SHOW_STATS">[`NIX_SHOW_STATS`](#env-NIX_SHOW_STATS)</span>
If set to `1`, Nix will print some evaluation statistics, such as
the number of values allocated.
- <span id="env-NIX_COUNT_CALLS">[`NIX_COUNT_CALLS`](#env-NIX_COUNT_CALLS)</span>
If set to `1`, Nix will print how often functions were called during
Nix expression evaluation. This is useful for profiling your Nix
expressions.
- <span id="env-GC_INITIAL_HEAP_SIZE">[`GC_INITIAL_HEAP_SIZE`](#env-GC_INITIAL_HEAP_SIZE)</span>
If Nix has been configured to use the Boehm garbage collector, this
variable sets the initial size of the heap in bytes. It defaults to
384 MiB. Setting it to a low value reduces memory consumption, but
will increase runtime due to the overhead of garbage collection.
- <span id="env-GC_INITIAL_HEAP_SIZE">[`GC_INITIAL_HEAP_SIZE`](#env-GC_INITIAL_HEAP_SIZE)</span>\
If Nix has been configured to use the Boehm garbage collector, this
variable sets the initial size of the heap in bytes. It defaults to
384 MiB. Setting it to a low value reduces memory consumption, but
will increase runtime due to the overhead of garbage collection.
## XDG Base Directories

View File

@@ -14,21 +14,16 @@
# Description
The install operation creates a new user environment.
It is based on the current generation of the active [profile](@docroot@/command-ref/files/profiles.md), to which a set of [store paths] described by *args* is added.
The install operation creates a new user environment, based on the
current generation of the active profile, to which a set of store paths
described by *args* is added. The arguments *args* map to store paths in
a number of possible ways:
[store paths]: @docroot@/glossary.md#gloss-store-path
The arguments *args* map to store paths in a number of possible ways:
- By default, *args* is a set of [derivation] names denoting derivations in the [default Nix expression].
These are [realised], and the resulting output paths are installed.
Currently installed derivations with a name equal to the name of a derivation being added are removed unless the option `--preserve-installed` is specified.
[derivation]: @docroot@/glossary.md#gloss-derivation
[default Nix expression]: @docroot@/command-ref/files/default-nix-expression.md
[realised]: @docroot@/glossary.md#gloss-realise
- By default, *args* is a set of derivation names denoting derivations
in the active Nix expression. These are realised, and the resulting
output paths are installed. Currently installed derivations with a
name equal to the name of a derivation being added are removed
unless the option `--preserve-installed` is specified.
If there are multiple derivations matching a name in *args* that
have the same name (e.g., `gcc-3.3.6` and `gcc-4.1.1`), then the
@@ -45,90 +40,44 @@ The arguments *args* map to store paths in a number of possible ways:
gcc-3.3.6 gcc-4.1.1` will install both version of GCC (and will
probably cause a user environment conflict\!).
- If [`--attr`](#opt-attr) / `-A` is specified, the arguments are *attribute paths* that select attributes from the [default Nix expression].
This is faster than using derivation names and unambiguous.
Show the attribute paths of available packages with [`nix-env --query`](./query.md):
```console
nix-env --query --available --attr-path`
```
- If `--attr` (`-A`) is specified, the arguments are *attribute
paths* that select attributes from the top-level Nix
expression. This is faster than using derivation names and
unambiguous. To find out the attribute paths of available
packages, use `nix-env --query --available --attr-path `.
- If `--from-profile` *path* is given, *args* is a set of names
denoting installed [store paths] in the profile *path*. This is an
denoting installed store paths in the profile *path*. This is an
easy way to copy user environment elements from one profile to
another.
- If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/constructs.md#functions) that are called with the [default Nix expression] as their single argument.
The derivations returned by those function calls are installed.
This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name.
- If `--from-expression` is given, *args* are Nix
[functions](@docroot@/language/constructs.md#functions)
that are called with the active Nix expression as their single
argument. The derivations returned by those function calls are
installed. This allows derivations to be specified in an
unambiguous way, which is necessary if there are multiple
derivations with the same name.
- If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are [realised], and the resulting output paths are installed.
- If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are
[realised](@docroot@/command-ref/nix-store/realise.md), and the resulting output paths
are installed.
- If *args* are [store paths] that are not store derivations, then these are [realised] and installed.
- If *args* are store paths that are not store derivations, then these
are [realised](@docroot@/command-ref/nix-store/realise.md) and installed.
- By default all [outputs](@docroot@/language/derivations.md#attr-outputs) are installed for each [derivation].
This can be overridden by adding a `meta.outputsToInstall` attribute on the derivation listing a subset of the output names.
- By default all outputs are installed for each derivation. That can
be reduced by setting `meta.outputsToInstall`.
Example:
The file `example.nix` defines a derivation with two outputs `foo` and `bar`, each containing a file.
```nix
# example.nix
let
pkgs = import <nixpkgs> {};
command = ''
${pkgs.coreutils}/bin/mkdir -p $foo $bar
echo foo > $foo/foo-file
echo bar > $bar/bar-file
'';
in
derivation {
name = "example";
builder = "${pkgs.bash}/bin/bash";
args = [ "-c" command ];
outputs = [ "foo" "bar" ];
system = builtins.currentSystem;
}
```
Installing from this Nix expression will make files from both outputs appear in the current profile.
```console
$ nix-env --install --file example.nix
installing 'example'
$ ls ~/.nix-profile
foo-file
bar-file
manifest.nix
```
Adding `meta.outputsToInstall` to that derivation will make `nix-env` only install files from the specified outputs.
```nix
# example-outputs.nix
import ./example.nix // { meta.outputsToInstall = [ "bar" ]; }
```
```console
$ nix-env --install --file example-outputs.nix
installing 'example'
$ ls ~/.nix-profile
bar-file
manifest.nix
```
# Options
- `--prebuilt-only` / `-b`
# Flags
- `--prebuilt-only` / `-b`\
Use only derivations for which a substitute is registered, i.e.,
there is a pre-built binary available that can be downloaded in lieu
of building the derivation. Thus, no packages will be built from
source.
- `--preserve-installed` / `-P`
- `--preserve-installed` / `-P`\
Do not remove derivations with a name matching one of the
derivations being installed. Usually, trying to have two versions of
the same package installed in the same generation of a profile will
@@ -136,8 +85,7 @@ The arguments *args* map to store paths in a number of possible ways:
clashes between the two versions. However, this is not the case for
all packages.
- `--remove-all` / `-r`
- `--remove-all` / `-r`\
Remove all previously installed packages first. This is equivalent
to running `nix-env --uninstall '.*'` first, except that everything happens
in a single transaction.

View File

@@ -235,14 +235,14 @@ package like Terraform:
```bash
#! /usr/bin/env nix-shell
#! nix-shell -i bash --packages 'terraform.withPlugins (plugins: [ plugins.openstack ])'
#! nix-shell -i bash --packages "terraform.withPlugins (plugins: [ plugins.openstack ])"
terraform apply
```
> **Note**
>
> You must use single or double quotes (`'`, `"`) when passing a simple Nix expression
> You must use double quotes (`"`) when passing a simple Nix expression
> in a nix-shell shebang.
Finally, using the merging of multiple nix-shell shebangs the following
@@ -251,7 +251,7 @@ branch):
```haskell
#! /usr/bin/env nix-shell
#! nix-shell -i runghc --packages 'haskellPackages.ghcWithPackages (ps: [ps.download-curl ps.tagsoup])'
#! nix-shell -i runghc --packages "haskellPackages.ghcWithPackages (ps: [ps.download-curl ps.tagsoup])"
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-20.03.tar.gz
import Network.Curl.Download

View File

@@ -15,12 +15,8 @@ Each of *paths* is processed as follows:
1. If it is not [valid], substitute the store derivation file itself.
2. Realise its [output paths]:
- Try to fetch from [substituters] the [store objects] associated with the output paths in the store derivation's [closure].
- With [content-addressed derivations] (experimental):
Determine the output paths to realise by querying content-addressed realisation entries in the [Nix database].
- For any store paths that cannot be substituted, produce the required store objects:
1. Realise all outputs of the derivation's dependencies
2. Run the derivation's [`builder`](@docroot@/language/derivations.md#attr-builder) executable
<!-- TODO: Link to build process page #8888 -->
- With [content-addressed derivations] (experimental): Determine the output paths to realise by querying content-addressed realisation entries in the [Nix database].
- For any store paths that cannot be substituted, produce the required store objects. This involves first realising all outputs of the derivation's dependencies and then running the derivation's [`builder`](@docroot@/language/derivations.md#attr-builder) executable. <!-- TODO: Link to build process page #8888 -->
- Otherwise, and if the path is not already valid: Try to fetch the associated [store objects] in the path's [closure] from [substituters].
If no substitutes are available and no store derivation is given, realisation fails.

View File

@@ -0,0 +1,57 @@
\--help
\--version
\--verbose
\-v
\--quiet
\--log-format
format
\--no-build-output
\-Q
\--max-jobs
\-j
number
\--cores
number
\--max-silent-time
number
\--timeout
number
\--keep-going
\-k
\--keep-failed
\-K
\--fallback
\--readonly-mode
\-I
path
\--option
name
value

View File

@@ -203,7 +203,3 @@ Most Nix commands accept the following command-line options:
Fix corrupted or missing store paths by redownloading or rebuilding them.
Note that this is slow because it requires computing a cryptographic hash of the contents of every path in the closure of the build.
Also note the warning under `nix-store --repair-path`.
> **Note**
>
> See [`man nix.conf`](@docroot@/command-ref/conf-file.md#command-line-flags) for overriding configuration settings with command line flags.

View File

@@ -0,0 +1,15 @@
\--prebuilt-only
\-b
\--attr
\-A
\--from-expression
\-E
\--from-profile
path

View File

@@ -1,8 +1 @@
# Development
Nix is developed on GitHub.
Check the [contributing guide](https://github.com/NixOS/nix/blob/master/CONTRIBUTING.md) if you want to get involved.
This chapter is a collection of guides for making changes to the code and documentation.
If you're not sure where to start, try to [compile Nix from source](./hacking.md) and consider [making improvements to documentation](./documentation.md).
# Contributing

View File

@@ -1,210 +0,0 @@
# Contributing documentation
Improvements to documentation are very much appreciated, and a good way to start out with contributing to Nix.
This is how you can help:
- Address [open issues with documentation](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+label%3Adocumentation)
- Review [pull requests concerning documentation](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+label%3Adocumentation)
Incremental refactorings of the documentation build setup to make it faster or easier to understand and maintain are also welcome.
## Building the manual
Build the manual from scratch:
```console
nix-build $(nix-instantiate)'!doc'
```
or
```console
nix build .#^doc
```
and open `./result-doc/share/doc/nix/manual/index.html`.
To build the manual incrementally, [enter the development shell](./hacking.md) and run:
```console
make manual-html -j $NIX_BUILD_CORES
```
and open `./outputs/out/share/doc/nix/manual/language/index.html`.
In order to reflect changes to the [Makefile for the manual], clear all generated files before re-building:
[Makefile for the manual]: https://github.com/NixOS/nix/blob/master/doc/manual/local.mk
```console
rm $(git ls-files doc/manual/ -o | grep -F '.md') && rmdir doc/manual/src/command-ref/new-cli && make manual-html -j $NIX_BUILD_CORES
```
## Style guide
The goal of this style guide is to make it such that
- The manual is easy to search and skim for relevant information
- Documentation sources are easy to edit
- Changes to documentation are easy to review
You will notice that this is not implemented consistently yet.
Please follow the guide when making additions or changes to existing documentation.
Do not make sweeping changes, unless they are programmatic and can be validated easily.
### Language
This manual is [reference documentation](https://diataxis.fr/reference/).
The typical usage pattern is to look up isolated pieces of information.
It should therefore aim to be correct, consistent, complete, and easy to navigate at a glance.
- Aim for clarity and brevity.
Please take the time to read the [plain language guidelines](https://www.plainlanguage.gov/guidelines/) for details.
- Describe the subject factually.
In particular, do not make value judgements or recommendations.
Check the code or add tests if in doubt.
- Provide complete, minimal examples, and explain them.
Readers should be able to try examples verbatim and get the same results as shown in the manual.
Always describe in words what a given example does.
Non-trivial examples may need additional explanation, especially if they use concepts from outside the given context.
- Always explain code examples in the text.
Use comments in code samples very sparingly, for instance to highlight a particular aspect.
Readers tend to glance over large amounts of code when scanning for information.
Especially beginners will likely find reading more complex-looking code strenuous and may therefore avoid it altogether.
If a code sample appears to require a lot of inline explanation, consider replacing it with a simpler one.
If that's not possible, break the example down into multiple parts, explain them separately, and then show the combined result at the end.
This should be a last resort, as that would amount to writing a [tutorial](https://diataxis.fr/tutorials/) on the given subject.
- Use British English.
This is a somewhat arbitrary choice to force consistency, and accounts for the fact that a majority of Nix users and developers are from Europe.
### Links and anchors
Reference documentation must be readable in arbitrary order.
Readers cannot be expected to have any particular prerequisite knowledge about Nix.
While the table of contents can provide guidance and full-text search can help, they are most likely to find what they need by following sensible cross-references.
- Link to technical terms
When mentioning Nix-specific concepts, commands, options, settings, etc., link to appropriate documentation.
Also link to external tools or concepts, especially if their meaning may be ambiguous.
You may also want to link to definitions of less common technical terms.
Then readers won't have to actively search for definitions and are more likely to discover relevant information on their own.
> **Note**
>
> `man` and `--help` pages don't display links.
> Use appropriate link texts such that readers of terminal output can infer search terms.
- Do not break existing URLs between releases.
There are countless links in the wild pointing to old versions of the manual.
We want people to find up-to-date documentation when following popular advice.
- When moving files, update [redirects on nixos.org](https://github.com/NixOS/nixos-homepage/blob/master/netlify.toml).
This is especially important when moving information out of the Nix manual to other resources.
- When changing anchors, update [client-side redirects](https://github.com/NixOS/nix/blob/master/doc/manual/redirects.js)
The current setup is cumbersome, and help making better automation is appreciated.
The build checks for broken internal links with.
This happens late in the process, so [building the whole manual](#building-the-manual) is not suitable for iterating quickly.
[`mdbook-linkcheck`] does not implement checking [URI fragments] yet.
[`mdbook-linkcheck`]: https://github.com/Michael-F-Bryan/mdbook-linkcheck
[URI fragments]: https://en.wikipedia.org/wiki/URI_fragment
### Markdown conventions
The manual is written in markdown, and rendered with [mdBook](https://github.com/rust-lang/mdBook) for the web and with [lowdown](https://github.com/kristapsdz/lowdown) for `man` pages and `--help` output.
For supported markdown features, refer to:
- [mdBook documentation](https://rust-lang.github.io/mdBook/format/markdown.html)
- [lowdown documentation](https://kristaps.bsd.lv/lowdown/)
Please observe these guidelines to ease reviews:
- Write one sentence per line.
This makes long sentences immediately visible, and makes it easier to review changes and make direct suggestions.
- Use reference links sparingly to ease source readability.
Put definitions close to their first use.
Example:
```
A [store object] contains a [file system object] and [references] to other store objects.
[store object]: @docroot@/glossary.md#gloss-store-object
[file system object]: @docroot@/architecture/file-system-object.md
[references]: @docroot@/glossary.md#gloss-reference
```
- Use admonitions of the following form:
```
> **Note**
>
> This is a note.
```
Highlight examples as such:
````
> **Example**
>
> ```console
> $ nix --version
> ```
````
Highlight syntax definiions as such, using [EBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form) notation:
````
> **Syntax**
>
> *attribute-set* = `{` [ *attribute-name* `=` *expression* `;` ... ] `}`
````
### The `@docroot@` variable
`@docroot@` provides a base path for links that occur in reusable snippets or other documentation that doesn't have a base path of its own.
If a broken link occurs in a snippet that was inserted into multiple generated files in different directories, use `@docroot@` to reference the `doc/manual/src` directory.
If the `@docroot@` literal appears in an error message from the [`mdbook-linkcheck`] tool, the `@docroot@` replacement needs to be applied to the generated source file that mentions it.
See existing `@docroot@` logic in the [Makefile for the manual].
Regular markdown files used for the manual have a base path of their own and they can use relative paths instead of `@docroot@`.
## API documentation
[Doxygen API documentation] is available online.
You can also build and view it yourself:
[Doxygen API documentation]: https://hydra.nixos.org/job/nix/master/internal-api-docs/latest/download-by-type/doc/internal-api-docs
```console
# nix build .#hydraJobs.internal-api-docs
# xdg-open ./result/share/doc/nix/internal-api/html/index.html
```
or inside `nix-shell` or `nix develop`:
```
# make internal-api-html
# xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html
```

View File

@@ -42,8 +42,8 @@ $ nix develop .#native-clang11StdenvPackages
To build Nix itself in this shell:
```console
[nix-shell]$ autoreconfPhase
[nix-shell]$ configurePhase
[nix-shell]$ ./bootstrap.sh
[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
[nix-shell]$ make -j $NIX_BUILD_CORES
```
@@ -86,7 +86,7 @@ $ nix-shell --attr devShells.x86_64-linux.native-clang11StdenvPackages
To build Nix itself in this shell:
```console
[nix-shell]$ autoreconfPhase
[nix-shell]$ ./bootstrap.sh
[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
[nix-shell]$ make -j $NIX_BUILD_CORES
```
@@ -210,7 +210,7 @@ See [supported compilation environments](#compilation-environments) and instruct
To use the LSP with your editor, you first need to [set up `clangd`](https://clangd.llvm.org/installation#project-setup) by running:
```console
make clean && bear -- make -j$NIX_BUILD_CORES default check install
make clean && bear -- make -j$NIX_BUILD_CORES install
```
Configure your editor to use the `clangd` from the shell, either by running it inside the development shell, or by using [nix-direnv](https://github.com/nix-community/nix-direnv) and [the appropriate editor plugin](https://github.com/direnv/direnv/wiki#editor-integration).
@@ -220,3 +220,68 @@ Configure your editor to use the `clangd` from the shell, either by running it i
> For some editors (e.g. Visual Studio Code), you may need to install a [special extension](https://open-vsx.org/extension/llvm-vs-code-extensions/vscode-clangd) for the editor to interact with `clangd`.
> Some other editors (e.g. Emacs, Vim) need a plugin to support LSP servers in general (e.g. [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs and [vim-lsp](https://github.com/prabirshrestha/vim-lsp) for vim).
> Editor-specific setup is typically opinionated, so we will not cover it here in more detail.
### Checking links in the manual
The build checks for broken internal links.
This happens late in the process, so `nix build` is not suitable for iterating.
To build the manual incrementally, run:
```console
make html -j $NIX_BUILD_CORES
```
In order to reflect changes to the [Makefile], clear all generated files before re-building:
[Makefile]: https://github.com/NixOS/nix/blob/master/doc/manual/local.mk
```console
rm $(git ls-files doc/manual/ -o | grep -F '.md') && rmdir doc/manual/src/command-ref/new-cli && make html -j $NIX_BUILD_CORES
```
[`mdbook-linkcheck`] does not implement checking [URI fragments] yet.
[`mdbook-linkcheck`]: https://github.com/Michael-F-Bryan/mdbook-linkcheck
[URI fragments]: https://en.wikipedia.org/wiki/URI_fragment
#### `@docroot@` variable
`@docroot@` provides a base path for links that occur in reusable snippets or other documentation that doesn't have a base path of its own.
If a broken link occurs in a snippet that was inserted into multiple generated files in different directories, use `@docroot@` to reference the `doc/manual/src` directory.
If the `@docroot@` literal appears in an error message from the `mdbook-linkcheck` tool, the `@docroot@` replacement needs to be applied to the generated source file that mentions it.
See existing `@docroot@` logic in the [Makefile].
Regular markdown files used for the manual have a base path of their own and they can use relative paths instead of `@docroot@`.
## API documentation
Doxygen API documentation is [available
online](https://hydra.nixos.org/job/nix/master/internal-api-docs/latest/download-by-type/doc/internal-api-docs). You
can also build and view it yourself:
```console
# nix build .#hydraJobs.internal-api-docs
# xdg-open ./result/share/doc/nix/internal-api/html/index.html
```
or inside a `nix develop` shell by running:
```
# make internal-api-html
# xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html
```
## Coverage analysis
A coverage analysis report is [available
online](https://hydra.nixos.org/job/nix/master/coverage/latest/download-by-type/report/coverage). You
can build it yourself:
```
# nix build .#hydraJobs.coverage
# xdg-open ./result/coverage/index.html
```
Metrics about the change in line/function coverage over time are also
[available](https://hydra.nixos.org/job/nix/master/coverage#tabs-charts).

View File

@@ -1,84 +1,26 @@
# Running tests
## Coverage analysis
A [coverage analysis report] is available online
You can build it yourself:
[coverage analysis report]: https://hydra.nixos.org/job/nix/master/coverage/latest/download-by-type/report/coverage
```
# nix build .#hydraJobs.coverage
# xdg-open ./result/coverage/index.html
```
[Extensive records of build metrics](https://hydra.nixos.org/job/nix/master/coverage#tabs-charts), such as test coverage over time, are also available online.
## Unit-tests
The unit tests are defined using the [googletest] and [rapidcheck] frameworks.
[googletest]: https://google.github.io/googletest/
[rapidcheck]: https://github.com/emil-e/rapidcheck
### Source and header layout
> An example of some files, demonstrating much of what is described below
>
> ```
> src
> ├── libexpr
> │ ├── value/context.hh
> │ ├── value/context.cc
> │ │
> │ …
> └── tests
> │ ├── value/context.hh
> │ ├── value/context.cc
> │ │
> │ …
>
> ├── unit-test-data
> │ ├── libstore
> │ │ ├── worker-protocol/content-address.bin
> │ │ …
> │ …
>
> ```
The unit tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `src/${library_shortname}/tests` within the directory for the library (`src/${library_shortname}`).
The data is in `unit-test-data`, with one subdir per library, with the same name as where the code goes.
For example, `libnixstore` code is in `src/libstore`, and its test data is in `unit-test-data/libstore`.
The path to the `unit-test-data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`.
> **Note**
> Due to the way googletest works, downstream unit test executables will actually include and re-run upstream library tests.
> Therefore it is important that the same value for `_NIX_TEST_UNIT_DATA` be used with the tests for each library.
> That is why we have the test data nested within a single `unit-test-data` directory.
### Running tests
The unit-tests for each Nix library (`libexpr`, `libstore`, etc..) are defined
under `tests/unit/{library_name}/tests` using the
[googletest](https://google.github.io/googletest/) and
[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks.
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`.
Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option, or the `GTEST_FILTER` environment variable.
### Characterisation testing { #characaterisation-testing-unit }
### Unit test support libraries
See [functional characterisation testing](#characterisation-testing-functional) for a broader discussion of characterisation testing.
There are headers and code which are not just used to test the library in question, but also downstream libraries.
For example, we do [property testing] with the [rapidcheck] library.
This requires writing `Arbitrary` "instances", which are used to describe how to generate values of a given type for the sake of running property tests.
Because types contain other types, `Arbitrary` "instances" for some type are not just useful for testing that type, but also any other type that contains it.
Downstream types frequently contain upstream types, so it is very important that we share arbitrary instances so that downstream libraries' property tests can also use them.
Like with the functional characterisation, `_NIX_TEST_ACCEPT=1` is also used.
For example:
```shell-session
$ _NIX_TEST_ACCEPT=1 make libstore-tests-exe_RUN
...
[ SKIPPED ] WorkerProtoTest.string_read
[ SKIPPED ] WorkerProtoTest.string_write
[ SKIPPED ] WorkerProtoTest.storePath_read
[ SKIPPED ] WorkerProtoTest.storePath_write
...
```
will regenerate the "golden master" expected result for the `libnixstore` characterisation tests.
The characterisation tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything.
It is important that these testing libraries don't contain any actual tests themselves.
On some platforms they would be run as part of every test executable that uses them, which is redundant.
On other platforms they wouldn't be run at all.
## Functional tests
@@ -133,17 +75,17 @@ ran test tests/functional/${testName}.sh... [PASS]
or without `make`:
```shell-session
$ ./mk/run-test.sh tests/functional/${testName}.sh tests/functional/init.sh
$ ./mk/run-test.sh tests/functional/${testName}.sh
ran test tests/functional/${testName}.sh... [PASS]
```
To see the complete output, one can also run:
```shell-session
$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh
+(${testName}.sh:1) foo
$ ./mk/debug-test.sh tests/functional/${testName}.sh
+ foo
output from foo
+(${testName}.sh:2) bar
+ bar
output from bar
...
```
@@ -175,7 +117,7 @@ edit it like so:
Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point:
```shell-session
$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh
$ ./mk/debug-test.sh tests/functional/${testName}.sh
...
+ gdb blash blub
GNU gdb (GDB) 12.1
@@ -186,18 +128,9 @@ GNU gdb (GDB) 12.1
One can debug the Nix invocation in all the usual ways.
For example, enter `run` to start the Nix invocation.
### Troubleshooting
### Characterization testing
Sometimes running tests in the development shell may leave artefacts in the local repository.
To remove any traces of that:
```console
git clean -x --force tests
```
### Characterisation testing { #characterisation-testing-functional }
Occasionally, Nix utilizes a technique called [Characterisation Testing](https://en.wikipedia.org/wiki/Characterization_test) as part of the functional tests.
Occasionally, Nix utilizes a technique called [Characterization Testing](https://en.wikipedia.org/wiki/Characterization_test) as part of the functional tests.
This technique is to include the exact output/behavior of a former version of Nix in a test in order to check that Nix continues to produce the same behavior going forward.
For example, this technique is used for the language tests, to check both the printed final value if evaluation was successful, and any errors and warnings encountered.
@@ -208,7 +141,6 @@ For example:
```bash
_NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test
```
This convention is shared with the [characterisation unit tests](#characterisation-testing-unit) too.
An interesting situation to document is the case when these tests are "overfitted".
The language tests are, again, an example of this.
@@ -221,7 +153,7 @@ Diagnostic outputs are indeed not a stable interface, but they still are importa
By recording the expected output, the test suite guards against accidental changes, and ensure the *result* (not just the code that implements it) of the diagnostic code paths are under code review.
Regressions are caught, and improvements always show up in code review.
To ensure that characterisation testing doesn't make it harder to intentionally change these interfaces, there always must be an easy way to regenerate the expected output, as we do with `_NIX_TEST_ACCEPT=1`.
To ensure that characterization testing doesn't make it harder to intentionally change these interfaces, there always must be an easy way to regenerate the expected output, as we do with `_NIX_TEST_ACCEPT=1`.
## Integration tests
@@ -235,7 +167,7 @@ You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix-
After a one-time setup, the Nix repository's GitHub Actions continuous integration (CI) workflow can test the installer each time you push to a branch.
Creating a Cachix cache for your installer tests and adding its authorisation token to GitHub enables [two installer-specific jobs in the CI workflow](https://github.com/NixOS/nix/blob/88a45d6149c0e304f6eb2efcc2d7a4d0d569f8af/.github/workflows/ci.yml#L50-L91):
Creating a Cachix cache for your installer tests and adding its authorization token to GitHub enables [two installer-specific jobs in the CI workflow](https://github.com/NixOS/nix/blob/88a45d6149c0e304f6eb2efcc2d7a4d0d569f8af/.github/workflows/ci.yml#L50-L91):
- The `installer` job generates installers for the platforms below and uploads them to your Cachix cache:
- `x86_64-linux`

View File

@@ -33,15 +33,11 @@
Ensure a [store path] is [valid][validity].
This can be achieved by:
- Fetching a pre-built [store object] from a [substituter]
- Running the [`builder`](@docroot@/language/derivations.md#attr-builder) executable as specified in the corresponding [derivation]
- Delegating to a [remote builder](@docroot@/advanced-topics/distributed-builds.html) and retrieving the outputs
<!-- TODO: link [running] to build process page, #8888 -->
This means either running the [`builder`](@docroot@/language/derivations.md#attr-builder) executable as specified in the corresponding [derivation], or fetching a pre-built [store object] from a [substituter], or delegating to a [remote builder](@docroot@/advanced-topics/distributed-builds.html) and retrieving the outputs. <!-- TODO: link [running] to build process page, #8888 -->
See [`nix-store --realise`](@docroot@/command-ref/nix-store/realise.md) for a detailed description of the algorithm.
See [`nix-build`](./command-ref/nix-build.md) and [`nix-store --realise`](@docroot@/command-ref/nix-store/realise.md).
See also [`nix-build`](./command-ref/nix-build.md) and [`nix build`](./command-ref/new-cli/nix3-build.md) (experimental).
See [`nix build`](./command-ref/new-cli/nix3-build.md) (experimental).
[realise]: #gloss-realise
@@ -58,16 +54,22 @@
- [store]{#gloss-store}
A collection of store objects, with operations to manipulate that collection.
See [Nix store](./store/index.md) for details.
The location in the file system where store objects live. Typically
`/nix/store`.
There are many types of stores.
See [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md) for a complete list.
From the perspective of the location where Nix is
invoked, the Nix store can be referred to
as a "_local_" or a "_remote_" one:
From the perspective of the location where Nix is invoked, the Nix store can be referred to _local_ or _remote_.
Only a [local store]{#gloss-local-store} exposes a location in the file system of the machine where Nix is invoked that allows access to store objects, typically `/nix/store`.
Local stores can be used for building [derivations](#derivation).
See [Local Store](@docroot@/command-ref/new-cli/nix3-help-stores.md#local-store) for details.
+ A [local store]{#gloss-local-store} exists on the filesystem of
the machine where Nix is invoked. You can use other
local stores by passing the `--store` flag to the
`nix` command. Local stores can be used for building derivations.
+ A *remote store* exists anywhere other than the
local filesystem. One example is the `/nix/store`
directory on another machine, accessed via `ssh` or
served by the `nix-serve` Perl script.
[store]: #gloss-store
[local store]: #gloss-local-store
@@ -86,13 +88,10 @@
- [store path]{#gloss-store-path}
The location of a [store object](@docroot@/store/index.md#store-object) in the file system, i.e., an immediate child of the Nix store directory.
The location of a [store object] in the file system, i.e., an
immediate child of the Nix store directory.
> **Example**
>
> `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1`
See [Store Path](@docroot@/store/store-path.md) for details.
Example: `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1`
[store path]: #gloss-store-path
@@ -100,25 +99,18 @@
The Nix data model for representing simplified file system data.
See [File System Object](@docroot@/store/file-system-object.md) for details.
See [File System Object](@docroot@/architecture/file-system-object.md) for details.
[file system object]: #gloss-file-system-object
- [store object]{#gloss-store-object}
Part of the contents of a [store].
A store object consists of a [file system object], [references][reference] to other store objects, and other metadata.
A store object consists of a [file system object], [reference]s to other store objects, and other metadata.
It can be referred to by a [store path].
See [Store Object](@docroot@/store/index.md#store-object) for details.
[store object]: #gloss-store-object
- [IFD]{#gloss-ifd}
[Import From Derivation](./language/import-from-derivation.md)
- [input-addressed store object]{#gloss-input-addressed-store-object}
A store object produced by building a
@@ -208,7 +200,6 @@
- [output]{#gloss-output}
A [store object] produced by a [derivation].
See [the `outputs` argument to the `derivation` function](@docroot@/language/derivations.md#attr-outputs) for details.
[output]: #gloss-output

View File

@@ -3,7 +3,7 @@
After cloning Nix's Git repository, issue the following commands:
```console
$ autoreconf -vfi
$ ./bootstrap.sh
$ ./configure options...
$ make
$ make install

View File

@@ -3,14 +3,14 @@
To run the latest stable release of Nix with Docker run the following command:
```console
$ docker run -ti ghcr.io/nixos/nix
Unable to find image 'ghcr.io/nixos/nix:latest' locally
latest: Pulling from ghcr.io/nixos/nix
$ docker run -ti nixos/nix
Unable to find image 'nixos/nix:latest' locally
latest: Pulling from nixos/nix
5843afab3874: Pull complete
b52bf13f109c: Pull complete
1e2415612aa3: Pull complete
Digest: sha256:27f6e7f60227e959ee7ece361f75d4844a40e1cc6878b6868fe30140420031ff
Status: Downloaded newer image for ghcr.io/nixos/nix:latest
Status: Downloaded newer image for nixos/nix:latest
35ca4ada6e96:/# nix --version
nix (Nix) 2.3.12
35ca4ada6e96:/# exit

View File

@@ -112,13 +112,6 @@ Derivations can declare some infrequently used optional attributes.
> environmental variables come from the environment of the
> `nix-build`.
If the [`configurable-impure-env` experimental
feature](@docroot@/contributing/experimental-features.md#xp-feature-configurable-impure-env)
is enabled, these environment variables can also be controlled
through the
[`impure-env`](@docroot@/command-ref/conf-file.md#conf-impure-env)
configuration setting.
- [`outputHash`]{#adv-attr-outputHash}; [`outputHashAlgo`]{#adv-attr-outputHashAlgo}; [`outputHashMode`]{#adv-attr-outputHashMode}\
These attributes declare that the derivation is a so-called
*fixed-output derivation*, which means that a cryptographic hash of
@@ -236,8 +229,6 @@ Derivations can declare some infrequently used optional attributes.
[`outputHashAlgo`](#adv-attr-outputHashAlgo)
like for *fixed-output derivations* (see above).
It also implicitly requires that the machine to build the derivation must have the `ca-derivations` [system feature](@docroot@/command-ref/conf-file.md#conf-system-features).
- [`passAsFile`]{#adv-attr-passAsFile}\
A list of names of attributes that should be passed via files rather
than environment variables. For example, if you have
@@ -270,9 +261,6 @@ Derivations can declare some infrequently used optional attributes.
useful for very trivial derivations (such as `writeText` in Nixpkgs)
that are cheaper to build than to substitute from a binary cache.
You may disable the effects of this attibute by enabling the
`always-allow-substitutes` configuration option in Nix.
> **Note**
>
> You need to have a builder configured which satisfies the
@@ -283,21 +271,18 @@ Derivations can declare some infrequently used optional attributes.
- [`__structuredAttrs`]{#adv-attr-structuredAttrs}\
If the special attribute `__structuredAttrs` is set to `true`, the other derivation
attributes are serialised into a file in JSON format. The environment variable
`NIX_ATTRS_JSON_FILE` points to the exact location of that file both in a build
and a [`nix-shell`](../command-ref/nix-shell.md). This obviates the need for
[`passAsFile`](#adv-attr-passAsFile) since JSON files have no size restrictions,
unlike process environments.
attributes are serialised in JSON format and made available to the
builder via the file `.attrs.json` in the builders temporary
directory. This obviates the need for [`passAsFile`](#adv-attr-passAsFile) since JSON files
have no size restrictions, unlike process environments.
It also makes it possible to tweak derivation settings in a structured way; see
[`outputChecks`](#adv-attr-outputChecks) for example.
As a convenience to Bash builders,
Nix writes a script that initialises shell variables
corresponding to all attributes that are representable in Bash. The
environment variable `NIX_ATTRS_SH_FILE` points to the exact
location of the script, both in a build and a
[`nix-shell`](../command-ref/nix-shell.md). This includes non-nested
Nix writes a script named `.attrs.sh` to the builders directory
that initialises shell variables corresponding to all attributes
that are representable in Bash. This includes non-nested
(associative) arrays. For example, the attribute `hardening.format = true`
ends up as the Bash associative array element `${hardening[format]}`.
@@ -350,15 +335,3 @@ Derivations can declare some infrequently used optional attributes.
This is useful, for example, when generating self-contained filesystem images with
their own embedded Nix store: hashes found inside such an image refer
to the embedded store and not to the host's Nix store.
- [`requiredSystemFeatures`]{#adv-attr-requiredSystemFeatures}\
If a derivation has the `requiredSystemFeatures` attribute, then Nix will only build it on a machine that has the corresponding features set in its [`system-features` configuration](@docroot@/command-ref/conf-file.md#conf-system-features).
For example, setting
```nix
requiredSystemFeatures = [ "kvm" ];
```
ensures that the derivation can only be built on a machine with the `kvm` feature.

View File

@@ -132,32 +132,6 @@ a = src-set.a; b = src-set.b; c = src-set.c;
when used while defining local variables in a let-expression or while
defining a set.
In a `let` expression, `inherit` can be used to selectively bring specific attributes of a set into scope. For example
```nix
let
x = { a = 1; b = 2; };
inherit (builtins) attrNames;
in
{
names = attrNames x;
}
```
is equivalent to
```nix
let
x = { a = 1; b = 2; };
in
{
names = builtins.attrNames x;
}
```
both evaluate to `{ names = [ "a" "b" ]; }`.
## Functions
Functions have the following form:
@@ -172,65 +146,65 @@ three kinds of patterns:
- If a pattern is a single identifier, then the function matches any
argument. Example:
```nix
let negate = x: !x;
concat = x: y: x + y;
in if negate true then concat "foo" "bar" else ""
```
Note that `concat` is a function that takes one argument and returns
a function that takes another argument. This allows partial
parameterisation (i.e., only filling some of the arguments of a
function); e.g.,
```nix
map (concat "foo") [ "bar" "bla" "abc" ]
```
evaluates to `[ "foobar" "foobla" "fooabc" ]`.
- A *set pattern* of the form `{ name1, name2, …, nameN }` matches a
set containing the listed attributes, and binds the values of those
attributes to variables in the function body. For example, the
function
```nix
{ x, y, z }: z + y + x
```
can only be called with a set containing exactly the attributes `x`,
`y` and `z`. No other attributes are allowed. If you want to allow
additional arguments, you can use an ellipsis (`...`):
```nix
{ x, y, z, ... }: z + y + x
```
This works on any set that contains at least the three named
attributes.
It is possible to provide *default values* for attributes, in
which case they are allowed to be missing. A default value is
specified by writing `name ? e`, where *e* is an arbitrary
expression. For example,
```nix
{ x, y ? "foo", z ? "bar" }: z + y + x
```
specifies a function that only requires an attribute named `x`, but
optionally accepts `y` and `z`.
- An `@`-pattern provides a means of referring to the whole value
being matched:
```nix
args@{ x, y, z, ... }: z + y + x + args.a
```
but can also be written as:
```nix
{ x, y, z, ... } @ args: z + y + x + args.a
```

View File

@@ -1,27 +0,0 @@
# Lookup path
> **Syntax**
>
> *lookup-path* = `<` *identifier* [ `/` *identifier* ]... `>`
A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/values.md#type-path) if the identifier matches a search path entry.
The value of a lookup path is determined by [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath).
See [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile) for details on lookup path resolution.
> **Example**
>
> ```nix
> <nixpkgs>
>```
>
> /nix/var/nix/profiles/per-user/root/channels/nixpkgs
> **Example**
>
> ```nix
> <nixpkgs/nixos>
>```
>
> /nix/var/nix/profiles/per-user/root/channels/nixpkgs/nixos

View File

@@ -1,315 +1,161 @@
# Derivations
The most important built-in function is `derivation`, which is used to describe a single derivation:
a specification for running an executable on precisely defined input files to repeatably produce output files at uniquely determined file system paths.
It takes as input an attribute set, the attributes of which specify the inputs to the process.
It outputs an attribute set, and produces a [store derivation] as a side effect of evaluation.
[store derivation]: @docroot@/glossary.md#gloss-store-derivation
## Input attributes
### Required
- [`name`]{#attr-name} ([String](@docroot@/language/values.md#type-string))
A symbolic name for the derivation.
It is added to the [store path] of the corresponding [store derivation] as well as to its [output paths](@docroot@/glossary.md#gloss-output-path).
[store path]: @docroot@/glossary.md#gloss-store-path
> **Example**
>
> ```nix
> derivation {
> name = "hello";
> # ...
> }
> ```
>
> The store derivation's path will be `/nix/store/<hash>-hello.drv`.
> The [output](#attr-outputs) paths will be of the form `/nix/store/<hash>-hello[-<output>]`
- [`system`]{#attr-system} ([String](@docroot@/language/values.md#type-string))
The system type on which the [`builder`](#attr-builder) executable is meant to be run.
A necessary condition for Nix to build derivations locally is that the `system` attribute matches the current [`system` configuration option].
It can automatically [build on other platforms](../advanced-topics/distributed-builds.md) by forwarding build requests to other machines.
[`system` configuration option]: @docroot@/command-ref/conf-file.md#conf-system
> **Example**
>
> Declare a derivation to be built on a specific system type:
>
> ```nix
> derivation {
> # ...
> system = "x86_64-linux";
> # ...
> }
> ```
> **Example**
>
> Declare a derivation to be built on the system type that evaluates the expression:
>
> ```nix
> derivation {
> # ...
> system = builtins.currentSystem;
> # ...
> }
> ```
>
> [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) has the value of the [`system` configuration option], and defaults to the system type of the current Nix installation.
- [`builder`]{#attr-builder} ([Path](@docroot@/language/values.md#type-path) | [String](@docroot@/language/values.md#type-string))
Path to an executable that will perform the build.
> **Example**
>
> Use the file located at `/bin/bash` as the builder executable:
>
> ```nix
> derivation {
> # ...
> builder = "/bin/bash";
> # ...
> };
> ```
<!-- -->
> **Example**
>
> Copy a local file to the Nix store for use as the builder executable:
>
> ```nix
> derivation {
> # ...
> builder = ./builder.sh;
> # ...
> };
> ```
<!-- -->
> **Example**
>
> Use a file from another derivation as the builder executable:
>
> ```nix
> let pkgs = import <nixpkgs> {}; in
> derivation {
> # ...
> builder = "${pkgs.python}/bin/python";
> # ...
> };
> ```
### Optional
- [`args`]{#attr-args} ([List](@docroot@/language/values.md#list) of [String](@docroot@/language/values.md#type-string))
Default: `[ ]`
Command-line arguments to be passed to the [`builder`](#attr-builder) executable.
> **Example**
>
> Pass arguments to Bash to interpret a shell command:
>
> ```nix
> derivation {
> # ...
> builder = "/bin/bash";
> args = [ "-c" "echo hello world > $out" ];
> # ...
> };
> ```
- [`outputs`]{#attr-outputs} ([List](@docroot@/language/values.md#list) of [String](@docroot@/language/values.md#type-string))
Default: `[ "out" ]`
Symbolic outputs of the derivation.
Each output name is passed to the [`builder`](#attr-builder) executable as an environment variable with its value set to the corresponding [store path].
By default, a derivation produces a single output called `out`.
However, derivations can produce multiple outputs.
This allows the associated [store objects](@docroot@/glossary.md#gloss-store-object) and their [closures](@docroot@/glossary.md#gloss-closure) to be copied or garbage-collected separately.
> **Example**
>
> Imagine a library package that provides a dynamic library, header files, and documentation.
> A program that links against such a library doesnt need the header files and documentation at runtime, and it doesnt need the documentation at build time.
> Thus, the library package could specify:
>
> ```nix
> derivation {
> # ...
> outputs = [ "lib" "dev" "doc" ];
> # ...
> }
> ```
>
> This will cause Nix to pass environment variables `lib`, `dev`, and `doc` to the builder containing the intended store paths of each output.
> The builder would typically do something like
>
> ```bash
> ./configure \
> --libdir=$lib/lib \
> --includedir=$dev/include \
> --docdir=$doc/share/doc
> ```
>
> for an Autoconf-style package.
The name of an output is combined with the name of the derivation to create the name part of the output's store path, unless it is `out`, in which case just the name of the derivation is used.
> **Example**
>
>
> ```nix
> derivation {
> name = "example";
> outputs = [ "lib" "dev" "doc" "out" ];
> # ...
> }
> ```
>
> The store derivation path will be `/nix/store/<hash>-example.drv`.
> The output paths will be
> - `/nix/store/<hash>-example-lib`
> - `/nix/store/<hash>-example-dev`
> - `/nix/store/<hash>-example-doc`
> - `/nix/store/<hash>-example`
You can refer to each output of a derivation by selecting it as an attribute.
The first element of `outputs` determines the *default output* and ends up at the top-level.
> **Example**
>
> Select an output by attribute name:
>
> ```nix
> let
> myPackage = derivation {
> name = "example";
> outputs = [ "lib" "dev" "doc" "out" ];
> # ...
> };
> in myPackage.dev
> ```
>
> Since `lib` is the first output, `myPackage` is equivalent to `myPackage.lib`.
<!-- FIXME: refer to the output attributes when we have one -->
- See [Advanced Attributes](./advanced-attributes.md) for more, infrequently used, optional attributes.
<!-- FIXME: This should be moved here -->
- Every other attribute is passed as an environment variable to the builder.
Attribute values are translated to environment variables as follows:
- Strings are passed unchanged.
- Integral numbers are converted to decimal notation.
- Floating point numbers are converted to simple decimal or scientific notation with a preset precision.
- A *path* (e.g., `../foo/sources.tar`) causes the referenced file
to be copied to the store; its location in the store is put in
the environment variable. The idea is that all sources should
reside in the Nix store, since all inputs to a derivation should
reside in the Nix store.
- A *derivation* causes that derivation to be built prior to the
present derivation. The environment variable is set to the [store path] of the derivation's default [output](#attr-outputs).
- Lists of the previous types are also allowed. They are simply
concatenated, separated by spaces.
- `true` is passed as the string `1`, `false` and `null` are
passed as an empty string.
<!-- FIXME: add a section on output attributes -->
## Builder execution
The [`builder`](#attr-builder) is executed as follows:
- A temporary directory is created under the directory specified by
`TMPDIR` (default `/tmp`) where the build will take place. The
current directory is changed to this directory.
- The environment is cleared and set to the derivation attributes, as
specified above.
- In addition, the following variables are set:
- `NIX_BUILD_TOP` contains the path of the temporary directory for
this build.
- Also, `TMPDIR`, `TEMPDIR`, `TMP`, `TEMP` are set to point to the
temporary directory. This is to prevent the builder from
accidentally writing temporary files anywhere else. Doing so
might cause interference by other processes.
- `PATH` is set to `/path-not-set` to prevent shells from
initialising it to their built-in default value.
- `HOME` is set to `/homeless-shelter` to prevent programs from
using `/etc/passwd` or the like to find the user's home
directory, which could cause impurity. Usually, when `HOME` is
set, it is used as the location of the home directory, even if
it points to a non-existent path.
- `NIX_STORE` is set to the path of the top-level Nix store
directory (typically, `/nix/store`).
- `NIX_ATTRS_JSON_FILE` & `NIX_ATTRS_SH_FILE` if `__structuredAttrs`
is set to `true` for the dervation. A detailed explanation of this
behavior can be found in the
[section about structured attrs](./advanced-attributes.md#adv-attr-structuredAttrs).
- For each output declared in `outputs`, the corresponding
environment variable is set to point to the intended path in the
Nix store for that output. Each output path is a concatenation
of the cryptographic hash of all build inputs, the `name`
attribute and the output name. (The output name is omitted if
its `out`.)
- If an output path already exists, it is removed. Also, locks are
acquired to prevent multiple Nix instances from performing the same
build at the same time.
- A log of the combined standard output and error is written to
`/nix/var/log/nix`.
- The builder is executed with the arguments specified by the
attribute `args`. If it exits with exit code 0, it is considered to
have succeeded.
- The temporary directory is removed (unless the `-K` option was
specified).
- If the build was successful, Nix scans each output path for
references to input paths by looking for the hash parts of the input
paths. Since these are potential runtime dependencies, Nix registers
them as dependencies of the output paths.
- After the build, Nix sets the last-modified timestamp on all files
in the build result to 1 (00:00:01 1/1/1970 UTC), sets the group to
the default group, and sets the mode of the file to 0444 or 0555
(i.e., read-only, with execute permission enabled if the file was
originally executable). Note that possible `setuid` and `setgid`
bits are cleared. Setuid and setgid programs are not currently
supported by Nix. This is because the Nix archives used in
deployment have no concept of ownership information, and because it
makes the build result dependent on the user performing the build.
The most important built-in function is `derivation`, which is used to
describe a single derivation (a build task). It takes as input a set,
the attributes of which specify the inputs of the build.
- There must be an attribute named [`system`]{#attr-system} whose value must be a
string specifying a Nix system type, such as `"i686-linux"` or
`"x86_64-darwin"`. (To figure out your system type, run `nix -vv
--version`.) The build can only be performed on a machine and
operating system matching the system type. (Nix can automatically
[forward builds for other
platforms](../advanced-topics/distributed-builds.md) by forwarding
them to other machines.)
- There must be an attribute named `name` whose value must be a
string. This is used as a symbolic name for the package by
`nix-env`, and it is appended to the output paths of the derivation.
- There must be an attribute named [`builder`]{#attr-builder} that identifies the
program that is executed to perform the build. It can be either a
derivation or a source (a local file reference, e.g.,
`./builder.sh`).
- Every attribute is passed as an environment variable to the builder.
Attribute values are translated to environment variables as follows:
- Strings and numbers are just passed verbatim.
- A *path* (e.g., `../foo/sources.tar`) causes the referenced file
to be copied to the store; its location in the store is put in
the environment variable. The idea is that all sources should
reside in the Nix store, since all inputs to a derivation should
reside in the Nix store.
- A *derivation* causes that derivation to be built prior to the
present derivation; its default output path is put in the
environment variable.
- Lists of the previous types are also allowed. They are simply
concatenated, separated by spaces.
- `true` is passed as the string `1`, `false` and `null` are
passed as an empty string.
- The optional attribute `args` specifies command-line arguments to be
passed to the builder. It should be a list.
- The optional attribute `outputs` specifies a list of symbolic
outputs of the derivation. By default, a derivation produces a
single output path, denoted as `out`. However, derivations can
produce multiple output paths. This is useful because it allows
outputs to be downloaded or garbage-collected separately. For
instance, imagine a library package that provides a dynamic library,
header files, and documentation. A program that links against the
library doesnt need the header files and documentation at runtime,
and it doesnt need the documentation at build time. Thus, the
library package could specify:
```nix
outputs = [ "lib" "headers" "doc" ];
```
This will cause Nix to pass environment variables `lib`, `headers`
and `doc` to the builder containing the intended store paths of each
output. The builder would typically do something like
```bash
./configure \
--libdir=$lib/lib \
--includedir=$headers/include \
--docdir=$doc/share/doc
```
for an Autoconf-style package. You can refer to each output of a
derivation by selecting it as an attribute, e.g.
```nix
buildInputs = [ pkg.lib pkg.headers ];
```
The first element of `outputs` determines the *default output*.
Thus, you could also write
```nix
buildInputs = [ pkg pkg.headers ];
```
since `pkg` is equivalent to `pkg.lib`.
The function `mkDerivation` in the Nixpkgs standard environment is a
wrapper around `derivation` that adds a default value for `system` and
always uses Bash as the builder, to which the supplied builder is passed
as a command-line argument. See the Nixpkgs manual for details.
The builder is executed as follows:
- A temporary directory is created under the directory specified by
`TMPDIR` (default `/tmp`) where the build will take place. The
current directory is changed to this directory.
- The environment is cleared and set to the derivation attributes, as
specified above.
- In addition, the following variables are set:
- `NIX_BUILD_TOP` contains the path of the temporary directory for
this build.
- Also, `TMPDIR`, `TEMPDIR`, `TMP`, `TEMP` are set to point to the
temporary directory. This is to prevent the builder from
accidentally writing temporary files anywhere else. Doing so
might cause interference by other processes.
- `PATH` is set to `/path-not-set` to prevent shells from
initialising it to their built-in default value.
- `HOME` is set to `/homeless-shelter` to prevent programs from
using `/etc/passwd` or the like to find the user's home
directory, which could cause impurity. Usually, when `HOME` is
set, it is used as the location of the home directory, even if
it points to a non-existent path.
- `NIX_STORE` is set to the path of the top-level Nix store
directory (typically, `/nix/store`).
- For each output declared in `outputs`, the corresponding
environment variable is set to point to the intended path in the
Nix store for that output. Each output path is a concatenation
of the cryptographic hash of all build inputs, the `name`
attribute and the output name. (The output name is omitted if
its `out`.)
- If an output path already exists, it is removed. Also, locks are
acquired to prevent multiple Nix instances from performing the same
build at the same time.
- A log of the combined standard output and error is written to
`/nix/var/log/nix`.
- The builder is executed with the arguments specified by the
attribute `args`. If it exits with exit code 0, it is considered to
have succeeded.
- The temporary directory is removed (unless the `-K` option was
specified).
- If the build was successful, Nix scans each output path for
references to input paths by looking for the hash parts of the input
paths. Since these are potential runtime dependencies, Nix registers
them as dependencies of the output paths.
- After the build, Nix sets the last-modified timestamp on all files
in the build result to 1 (00:00:01 1/1/1970 UTC), sets the group to
the default group, and sets the mode of the file to 0444 or 0555
(i.e., read-only, with execute permission enabled if the file was
originally executable). Note that possible `setuid` and `setgid`
bits are cleared. Setuid and setgid programs are not currently
supported by Nix. This is because the Nix archives used in
deployment have no concept of ownership information, and because it
makes the build result dependent on the user performing the build.

View File

@@ -1,139 +0,0 @@
# Import From Derivation
The value of a Nix expression can depend on the contents of a [store object](@docroot@/glossary.md#gloss-store-object).
Passing an expression `expr` that evaluates to a [store path](@docroot@/glossary.md#gloss-store-path) to any built-in function which reads from the filesystem constitutes Import From Derivation (IFD):
- [`import`](./builtins.md#builtins-import)` expr`
- [`builtins.readFile`](./builtins.md#builtins-readFile)` expr`
- [`builtins.readFileType`](./builtins.md#builtins-readFileType)` expr`
- [`builtins.readDir`](./builtins.md#builtins-readDir)` expr`
- [`builtins.pathExists`](./builtins.md#builtins-pathExists)` expr`
- [`builtins.filterSource`](./builtins.md#builtins-filterSource)` f expr`
- [`builtins.path`](./builtins.md#builtins-path)` { path = expr; }`
- [`builtins.hashFile`](./builtins.md#builtins-hashFile)` t expr`
- `builtins.scopedImport x drv`
When the store path needs to be accessed, evaluation will be paused, the corresponding store object [realised], and then evaluation resumed.
[realised]: @docroot@/glossary.md#gloss-realise
This has performance implications:
Evaluation can only finish when all required store objects are realised.
Since the Nix language evaluator is sequential, it only finds store paths to read from one at a time.
While realisation is always parallel, in this case it cannot be done for all required store paths at once, and is therefore much slower than otherwise.
Realising store objects during evaluation can be disabled by setting [`allow-import-from-derivation`](../command-ref/conf-file.md#conf-allow-import-from-derivation) to `false`.
Without IFD it is ensured that evaluation is complete and Nix can produce a build plan before starting any realisation.
## Example
In the following Nix expression, the inner derivation `drv` produces a file with contents `hello`.
```nix
# IFD.nix
let
drv = derivation {
name = "hello";
builder = "/bin/sh";
args = [ "-c" "echo -n hello > $out" ];
system = builtins.currentSystem;
};
in "${builtins.readFile drv} world"
```
```shellSession
nix-instantiate IFD.nix --eval --read-write-mode
```
```
building '/nix/store/348q1cal6sdgfxs8zqi9v8llrsn4kqkq-hello.drv'...
"hello world"
```
The contents of the derivation's output have to be [realised] before they can be read with [`readFile`](./builtins.md#builtins-readFile).
Only then evaluation can continue to produce the final result.
## Illustration
As a first approximation, the following data flow graph shows how evaluation and building are interleaved, if the value of a Nix expression depends on realising a [store object].
Boxes are data structures, arrow labels are transformations.
```
+----------------------+ +------------------------+
| Nix evaluator | | Nix store |
| .----------------. | | |
| | Nix expression | | | |
| '----------------' | | |
| | | | |
| evaluate | | |
| | | | |
| V | | |
| .------------. | | .------------------. |
| | derivation |----|-instantiate-|->| store derivation | |
| '------------' | | '------------------' |
| | | | |
| | | realise |
| | | | |
| | | V |
| .----------------. | | .--------------. |
| | Nix expression |<-|----read-----|----| store object | |
| '----------------' | | '--------------' |
| | | | |
| evaluate | | |
| | | | |
| V | | |
| .------------. | | |
| | value | | | |
| '------------' | | |
+----------------------+ +------------------------+
```
In more detail, the following sequence diagram shows how the expression is evaluated step by step, and where evaluation is blocked to wait for the build output to appear.
```
.-------. .-------------. .---------.
|Nix CLI| |Nix evaluator| |Nix store|
'-------' '-------------' '---------'
| | |
|evaluate IFD.nix| |
|--------------->| |
| | |
| evaluate `"${readFile drv} world"` |
| | |
| evaluate `readFile drv` |
| | |
| evaluate `drv` as string |
| | |
| |instantiate /nix/store/...-hello.drv|
| |----------------------------------->|
| : |
| : realise /nix/store/...-hello.drv |
| :----------------------------------->|
| : |
| |--------.
| : | |
| (evaluation blocked) | echo hello > $out
| : | |
| |<-------'
| : /nix/store/...-hello |
| |<-----------------------------------|
| | |
| resume `readFile /nix/store/...-hello` |
| | |
| | readFile /nix/store/...-hello |
| |----------------------------------->|
| | |
| | hello |
| |<-----------------------------------|
| | |
| resume `"${"hello"} world"` |
| | |
| resume `"hello world"` |
| | |
| "hello world" | |
|<---------------| |
.-------. .-------------. .---------.
|Nix CLI| |Nix evaluator| |Nix store|
'-------' '-------------' '---------'
```

View File

@@ -83,8 +83,7 @@ This is an incomplete overview of language features, by example.
</td>
<td>
<!-- FIXME: using two no-break spaces, because apparently mdBook swallows the second regular space! -->
A multi-line string. Strips common prefixed whitespace. Evaluates to `"multi\n line\n  string"`.
A multi-line string. Strips common prefixed whitespace. Evaluates to `"multi\n line\n string"`.
</td>
</tr>

View File

@@ -25,7 +25,7 @@
| Inequality | *expr* `!=` *expr* | none | 11 |
| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
| Logical disjunction (`OR`) | *bool* <code>\|\|</code> *bool* | left | 13 |
| [Logical implication] | *bool* `->` *bool* | right | 14 |
| [Logical implication] | *bool* `->` *bool* | none | 14 |
[string]: ./values.md#type-string
[path]: ./values.md#type-path
@@ -35,8 +35,6 @@
## Attribute selection
> **Syntax**
>
> *attrset* `.` *attrpath* \[ `or` *expr* \]
Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*.
@@ -44,29 +42,21 @@ If the attribute doesnt exist, return the *expr* after `or` if provided, othe
An attribute path is a dot-separated list of [attribute names](./values.md#attribute-set).
> **Syntax**
>
> *attrpath* = *name* [ `.` *name* ]...
[Attribute selection]: #attribute-selection
## Has attribute
> **Syntax**
>
> *attrset* `?` *attrpath*
Test whether [attribute set] *attrset* contains the attribute denoted by *attrpath*.
The result is a [Boolean] value.
See also: [`builtins.hasAttr`](@docroot@/language/builtins.md#builtins-hasAttr)
[Boolean]: ./values.md#type-boolean
[Has attribute]: #has-attribute
After evaluating *attrset* and *attrpath*, the computational complexity is O(log(*n*)) for *n* attributes in the *attrset*
## Arithmetic
Numbers are type-compatible:
@@ -80,8 +70,6 @@ The `+` operator is overloaded to also work on strings and paths.
## String concatenation
> **Syntax**
>
> *string* `+` *string*
Concatenate two [string]s and merge their string contexts.
@@ -90,8 +78,6 @@ Concatenate two [string]s and merge their string contexts.
## Path concatenation
> **Syntax**
>
> *path* `+` *path*
Concatenate two [path]s.
@@ -101,8 +87,6 @@ The result is a path.
## Path and string concatenation
> **Syntax**
>
> *path* + *string*
Concatenate *[path]* with *[string]*.
@@ -116,8 +100,6 @@ The result is a path.
## String and path concatenation
> **Syntax**
>
> *string* + *path*
Concatenate *[string]* with *[path]*.
@@ -135,8 +117,6 @@ The result is a string.
## Update
> **Syntax**
>
> *attrset1* // *attrset2*
Update [attribute set] *attrset1* with names and values from *attrset2*.

View File

@@ -1,12 +1,19 @@
# String interpolation
String interpolation is a language feature where a [string], [path], or [attribute name][attribute set] can contain expressions enclosed in `${ }` (dollar-sign with curly brackets).
String interpolation is a language feature where a [string], [path], or [attribute name] can contain expressions enclosed in `${ }` (dollar-sign with curly brackets).
Such a construct is called *interpolated string*, and the expression inside is an [interpolated expression](#interpolated-expression).
Such a string is an *interpolated string*, and an expression inside is an *interpolated expression*.
Interpolated expressions must evaluate to one of the following:
- a [string]
- a [path]
- a [derivation]
[string]: ./values.md#type-string
[path]: ./values.md#type-path
[attribute set]: ./values.md#attribute-set
[attribute name]: ./values.md#attribute-set
[derivation]: ../glossary.md#gloss-derivation
## Examples
@@ -63,136 +70,13 @@ you can instead write
### Attribute name
<!--
FIXME: these examples are redundant with the main page on attribute sets.
figure out what to do about that
-->
Attribute names can be created dynamically with string interpolation:
Attribute names can be interpolated strings.
```nix
let name = "foo"; in
{
${name} = "bar";
}
```
> **Example**
>
> ```nix
> let name = "foo"; in
> { ${name} = 123; }
> ```
>
> { foo = 123; }
Attributes can be selected with interpolated strings.
> **Example**
>
> ```nix
> let name = "foo"; in
> { foo = 123; }.${name}
> ```
>
> 123
# Interpolated expression
An expression that is interpolated must evaluate to one of the following:
- a [string]
- a [path]
- an [attribute set] that has a `__toString` attribute or an `outPath` attribute
- `__toString` must be a function that takes the attribute set itself and returns a string
- `outPath` must be a string
This includes [derivations](./derivations.md) or [flake inputs](@docroot@/command-ref/new-cli/nix3-flake.md#flake-inputs) (experimental).
A string interpolates to itself.
A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](../glossary.md#gloss-store-object).
[store path]: ../glossary.md#gloss-store-path
> **Example**
>
> ```console
> $ mkdir foo
> ```
>
> Reference the empty directory in an interpolated expression:
>
> ```nix
> "${./foo}"
> ```
>
> "/nix/store/2hhl2nz5v0khbn06ys82nrk99aa1xxdw-foo"
A derivation interpolates to the [store path] of its first [output](./derivations.md#attr-outputs).
> **Example**
>
> ```nix
> let
> pkgs = import <nixpkgs> {};
> in
> "${pkgs.hello}"
> ```
>
> "/nix/store/4xpfqf29z4m8vbhrqcz064wfmb46w5r7-hello-2.12.1"
An attribute set interpolates to the return value of the function in the `__toString` applied to the attribute set itself.
> **Example**
>
> ```nix
> let
> a = {
> value = 1;
> __toString = self: toString (self.value + 1);
> };
> in
> "${a}"
> ```
>
> "2"
An attribute set also interpolates to the value of its `outPath` attribute.
> **Example**
>
> ```nix
> let
> a = { outPath = "foo"; };
> in
> "${a}"
> ```
>
> "foo"
If both `__toString` and `outPath` are present in an attribute set, `__toString` takes precedence.
> **Example**
>
> ```nix
> let
> a = { __toString = _: "yes"; outPath = throw "no"; };
> in
> "${a}"
> ```
>
> "yes"
If neither is present, an error is thrown.
> **Example**
>
> ```nix
> let
> a = {};
> in
> "${a}"
> ```
>
> error: cannot coerce a set to a string
>
> at «string»:4:2:
>
> 3| in
> 4| "${a}"
> | ^
{ foo = "bar"; }

View File

@@ -107,24 +107,29 @@
e.g. `~/foo` would be equivalent to `/home/edolstra/foo` for a user
whose home directory is `/home/edolstra`.
Paths can also be specified between angle brackets, e.g.
`<nixpkgs>`. This means that the directories listed in the
environment variable `NIX_PATH` will be searched for the given file
or directory name.
When an [interpolated string][string interpolation] evaluates to a path, the path is first copied into the Nix store and the resulting string is the [store path] of the newly created [store object].
[store path]: ../glossary.md#gloss-store-path
[store object]: ../glossary.md#gloss-store-object
For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` in the current directory to be copied into the Nix store and result in the string `"/nix/store/<hash>-foo.txt"`.
Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression.
For example, assume you used a file path in an interpolated string during a `nix repl` session.
Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents.
Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new store path, since Nix might not re-read the file contents.
[store path]: ../glossary.md#gloss-store-path
Paths can include [string interpolation] and can themselves be [interpolated in other expressions].
[interpolated in other expressions]: ./string-interpolation.md#interpolated-expressions
Paths themselves, except those in angle brackets (`< >`), support [string interpolation].
At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path.
`a.${foo}/b.${bar}` is a syntactically valid division operation.
`./a.${foo}/b.${bar}` is a path.
[Lookup paths](./constructs/lookup-path.md) such as `<nixpkgs>` resolve to path values.
- <a id="type-boolean" href="#type-boolean">Boolean</a>
*Booleans* with values `true` and `false`.
@@ -162,17 +167,13 @@ An attribute set is a collection of name-value-pairs (called *attributes*) enclo
An attribute name can be an identifier or a [string](#string).
An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`).
> **Syntax**
>
> *name* = *identifier* | *string* \
> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*`
Names and values are separated by an equal sign (`=`).
Each value is an arbitrary expression terminated by a semicolon (`;`).
> **Syntax**
>
> *attrset* = `{` [ *name* `=` *expr* `;` ]... `}`
> *attrset* = `{` [ *name* `=` *expr* `;` `]`... `}`
Attributes can appear in any order.
An attribute name may only occur once.

View File

@@ -0,0 +1,179 @@
# Basic Package Management
The main command for package management is
[`nix-env`](../command-ref/nix-env.md). You can use it to install,
upgrade, and erase packages, and to query what packages are installed
or are available for installation.
In Nix, different users can have different “views” on the set of
installed applications. That is, there might be lots of applications
present on the system (possibly in many different versions), but users
can have a specific selection of those active — where “active” just
means that it appears in a directory in the users `PATH`. Such a view
on the set of installed applications is called a *user environment*,
which is just a directory tree consisting of symlinks to the files of
the active applications.
Components are installed from a set of *Nix expressions* that tell Nix
how to build those packages, including, if necessary, their
dependencies. There is a collection of Nix expressions called the
Nixpkgs package collection that contains packages ranging from basic
development stuff such as GCC and Glibc, to end-user applications like
Mozilla Firefox. (Nix is however not tied to the Nixpkgs package
collection; you could write your own Nix expressions based on Nixpkgs,
or completely new ones.)
You can manually download the latest version of Nixpkgs from
<https://github.com/NixOS/nixpkgs>. However, its much more
convenient to use the Nixpkgs [*channel*](../command-ref/nix-channel.md), since it makes
it easy to stay up to date with new versions of Nixpkgs. Nixpkgs is
automatically added to your list of “subscribed” channels when you
install Nix. If this is not the case for some reason, you can add it
as follows:
```console
$ nix-channel --add https://nixos.org/channels/nixpkgs-unstable
$ nix-channel --update
```
> **Note**
>
> On NixOS, youre automatically subscribed to a NixOS channel
> corresponding to your NixOS major release (e.g.
> <http://nixos.org/channels/nixos-21.11>). A NixOS channel is identical
> to the Nixpkgs channel, except that it contains only Linux binaries
> and is updated only if a set of regression tests succeed.
You can view the set of available packages in Nixpkgs:
```console
$ nix-env --query --available --attr-path
nixpkgs.aterm aterm-2.2
nixpkgs.bash bash-3.0
nixpkgs.binutils binutils-2.15
nixpkgs.bison bison-1.875d
nixpkgs.blackdown blackdown-1.4.2
nixpkgs.bzip2 bzip2-1.0.2
```
The flag `-q` specifies a query operation, `-a` means that you want
to show the “available” (i.e., installable) packages, as opposed to the
installed packages, and `-P` prints the attribute paths that can be used
to unambiguously select a package for installation (listed in the first column).
If you downloaded Nixpkgs yourself, or if you checked it out from GitHub,
then you need to pass the path to your Nixpkgs tree using the `-f` flag:
```console
$ nix-env --query --available --attr-path --file /path/to/nixpkgs
aterm aterm-2.2
bash bash-3.0
```
where */path/to/nixpkgs* is where youve unpacked or checked out
Nixpkgs.
You can filter the packages by name:
```console
$ nix-env --query --available --attr-path firefox
nixpkgs.firefox-esr firefox-91.3.0esr
nixpkgs.firefox firefox-94.0.1
```
and using regular expressions:
```console
$ nix-env --query --available --attr-path 'firefox.*'
```
It is also possible to see the *status* of available packages, i.e.,
whether they are installed into the user environment and/or present in
the system:
```console
$ nix-env --query --available --attr-path --status
-PS nixpkgs.bash bash-3.0
--S nixpkgs.binutils binutils-2.15
IPS nixpkgs.bison bison-1.875d
```
The first character (`I`) indicates whether the package is installed in
your current user environment. The second (`P`) indicates whether it is
present on your system (in which case installing it into your user
environment would be a very quick operation). The last one (`S`)
indicates whether there is a so-called *substitute* for the package,
which is Nixs mechanism for doing binary deployment. It just means that
Nix knows that it can fetch a pre-built package from somewhere
(typically a network server) instead of building it locally.
You can install a package using `nix-env --install --attr `. For instance,
```console
$ nix-env --install --attr nixpkgs.subversion
```
will install the package called `subversion` from `nixpkgs` channel (which is, of course, the
[Subversion version management system](http://subversion.tigris.org/)).
> **Note**
>
> When you ask Nix to install a package, it will first try to get it in
> pre-compiled form from a *binary cache*. By default, Nix will use the
> binary cache <https://cache.nixos.org>; it contains binaries for most
> packages in Nixpkgs. Only if no binary is available in the binary
> cache, Nix will build the package from source. So if `nix-env
> -iA nixpkgs.subversion` results in Nix building stuff from source, then either
> the package is not built for your platform by the Nixpkgs build
> servers, or your version of Nixpkgs is too old or too new. For
> instance, if you have a very recent checkout of Nixpkgs, then the
> Nixpkgs build servers may not have had a chance to build everything
> and upload the resulting binaries to <https://cache.nixos.org>. The
> Nixpkgs channel is only updated after all binaries have been uploaded
> to the cache, so if you stick to the Nixpkgs channel (rather than
> using a Git checkout of the Nixpkgs tree), you will get binaries for
> most packages.
Naturally, packages can also be uninstalled. Unlike when installing, you will
need to use the derivation name (though the version part can be omitted),
instead of the attribute path, as `nix-env` does not record which attribute
was used for installing:
```console
$ nix-env --uninstall subversion
```
Upgrading to a new version is just as easy. If you have a new release of
Nix Packages, you can do:
```console
$ nix-env --upgrade --attr nixpkgs.subversion
```
This will *only* upgrade Subversion if there is a “newer” version in the
new set of Nix expressions, as defined by some pretty arbitrary rules
regarding ordering of version numbers (which generally do what youd
expect of them). To just unconditionally replace Subversion with
whatever version is in the Nix expressions, use `-i` instead of `-u`;
`-i` will remove whatever version is already installed.
You can also upgrade all packages for which there are newer versions:
```console
$ nix-env --upgrade
```
Sometimes its useful to be able to ask what `nix-env` would do, without
actually doing it. For instance, to find out what packages would be
upgraded by `nix-env --upgrade `, you can do
```console
$ nix-env --upgrade --dry-run
(dry run; not doing anything)
upgrading `libxslt-1.1.0' to `libxslt-1.1.10'
upgrading `graphviz-1.10' to `graphviz-1.12'
upgrading `coreutils-5.0' to `coreutils-5.2.1'
```

View File

@@ -0,0 +1,115 @@
# Serving a Nix store via S3
Nix has [built-in support](@docroot@/command-ref/new-cli/nix3-help-stores.md#s3-binary-cache-store)
for storing and fetching store paths from
Amazon S3 and S3-compatible services. This uses the same *binary*
cache mechanism that Nix usually uses to fetch prebuilt binaries from
[cache.nixos.org](https://cache.nixos.org/).
In this example we will use the bucket named `example-nix-cache`.
## Anonymous Reads to your S3-compatible binary cache
If your binary cache is publicly accessible and does not require
authentication, the simplest and easiest way to use Nix with your S3
compatible binary cache is to use the HTTP URL for that cache.
For AWS S3 the binary cache URL for example bucket will be exactly
<https://example-nix-cache.s3.amazonaws.com> or
<s3://example-nix-cache>. For S3 compatible binary caches, consult that
cache's documentation.
Your bucket will need the following bucket policy:
```json
{
"Id": "DirectReads",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowDirectReads",
"Action": [
"s3:GetObject",
"s3:GetBucketLocation"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::example-nix-cache",
"arn:aws:s3:::example-nix-cache/*"
],
"Principal": "*"
}
]
}
```
## Authenticated Reads to your S3 binary cache
For AWS S3 the binary cache URL for example bucket will be exactly
<s3://example-nix-cache>.
Nix will use the [default credential provider
chain](https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/credentials.html)
for authenticating requests to Amazon S3.
Nix supports authenticated reads from Amazon S3 and S3 compatible binary
caches.
Your bucket will need a bucket policy allowing the desired users to
perform the `s3:GetObject` and `s3:GetBucketLocation` action on all
objects in the bucket. The [anonymous policy given
above](#anonymous-reads-to-your-s3-compatible-binary-cache) can be
updated to have a restricted `Principal` to support this.
## Authenticated Writes to your S3-compatible binary cache
Nix support fully supports writing to Amazon S3 and S3 compatible
buckets. The binary cache URL for our example bucket will be
<s3://example-nix-cache>.
Nix will use the [default credential provider
chain](https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/credentials.html)
for authenticating requests to Amazon S3.
Your account will need the following IAM policy to upload to the cache:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "UploadToCache",
"Effect": "Allow",
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:ListMultipartUploadParts",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::example-nix-cache",
"arn:aws:s3:::example-nix-cache/*"
]
}
]
}
```
## Examples
To upload with a specific credential profile for Amazon S3:
```console
$ nix copy nixpkgs.hello \
--to 's3://example-nix-cache?profile=cache-upload&region=eu-west-2'
```
To upload to an S3-compatible binary cache:
```console
$ nix copy nixpkgs.hello --to \
's3://example-nix-cache?profile=cache-upload&scheme=https&endpoint=minio.example.com'
```

View File

@@ -1,12 +1 @@
# Nix Release Notes
Nix has a release cycle of roughly 6 weeks.
Notable changes and additions are announced in the release notes for each version.
Bugfixes can be backported on request to previous Nix releases.
We typically backport only as far back as the Nix version used in the latest NixOS release, which is announced in the [NixOS release notes](https://nixos.org/manual/nixos/stable/release-notes.html#ch-release-notes).
Backports never skip releases.
If a feature is backported to version `x.y`, it must also be available in version `x.(y+1)`.
This ensures that upgrading from an older version with backports is still safe and no backported functionality will go missing.

View File

@@ -2,6 +2,7 @@
* On Linux, Nix can now run builds in a user namespace where they run
as root (UID 0) and have 65,536 UIDs available.
<!-- FIXME: move this to its own section about system features -->
This is primarily useful for running containers such as `systemd-nspawn`
inside a Nix build. For an example, see [`tests/systemd-nspawn/nix`][nspawn].

View File

@@ -1,77 +0,0 @@
# Release 2.19 (2023-11-17)
- The experimental `nix` command can now act as a [shebang interpreter](@docroot@/command-ref/new-cli/nix.md#shebang-interpreter)
by appending the contents of any `#! nix` lines and the script's location into a single call.
- [URL flake references](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references) now support [percent-encoded](https://datatracker.ietf.org/doc/html/rfc3986#section-2.1) characters.
- [Path-like flake references](@docroot@/command-ref/new-cli/nix3-flake.md#path-like-syntax) now accept arbitrary unicode characters (except `#` and `?`).
- The experimental feature `repl-flake` is no longer needed, as its functionality is now part of the `flakes` experimental feature. To get the previous behavior, use the `--file/--expr` flags accordingly.
- There is a new flake installable syntax `flakeref#.attrPath` where the "." prefix specifies that `attrPath` is interpreted from the root of the flake outputs, with no searching of default attribute prefixes like `packages.<SYSTEM>` or `legacyPackages.<SYSTEM>`.
- Nix adds `apple-virt` to the default system features on macOS systems that support virtualization. This is similar to what's done for the `kvm` system feature on Linux hosts.
- Add a new built-in function [`builtins.convertHash`](@docroot@/language/builtins.md#builtins-convertHash).
- `nix-shell` shebang lines now support single-quoted arguments.
- `builtins.fetchTree` is now its own experimental feature, [`fetch-tree`](@docroot@/contributing/experimental-features.md#xp-fetch-tree).
As described in the documentation for that feature, this is because we anticipate polishing it and then stabilizing it before the rest of flakes.
- The interface for creating and updating lock files has been overhauled:
- [`nix flake lock`](@docroot@/command-ref/new-cli/nix3-flake-lock.md) only creates lock files and adds missing inputs now.
It will *never* update existing inputs.
- [`nix flake update`](@docroot@/command-ref/new-cli/nix3-flake-update.md) does the same, but *will* update inputs.
- Passing no arguments will update all inputs of the current flake, just like it already did.
- Passing input names as arguments will ensure only those are updated. This replaces the functionality of `nix flake lock --update-input`
- To operate on a flake outside the current directory, you must now pass `--flake path/to/flake`.
- The flake-specific flags `--recreate-lock-file` and `--update-input` have been removed from all commands operating on installables.
They are superceded by `nix flake update`.
- Commit signature verification for the [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit) is added as the new [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches).
- [`nix path-info --json`](@docroot@/command-ref/new-cli/nix3-path-info.md)
(experimental) now returns a JSON map rather than JSON list.
The `path` field of each object has instead become the key in the outer map, since it is unique.
The `valid` field also goes away because we just use `null` instead.
- Old way:
```json5
[
{
"path": "/nix/store/8fv91097mbh5049i9rglc73dx6kjg3qk-bash-5.2-p15",
"valid": true,
// ...
},
{
"path": "/nix/store/wffw7l0alvs3iw94cbgi1gmmbmw99sqb-home-manager-path",
"valid": false
}
]
```
- New way
```json5
{
"/nix/store/8fv91097mbh5049i9rglc73dx6kjg3qk-bash-5.2-p15": {
// ...
},
"/nix/store/wffw7l0alvs3iw94cbgi1gmmbmw99sqb-home-manager-path": null,
}
```
This makes it match `nix derivation show`, which also maps store paths to information.
- When Nix is installed using the [binary installer](@docroot@/installation/installing-binary.md), in supported shells (Bash, Zsh, Fish)
[`XDG_DATA_DIRS`](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables) is now populated with the path to the `/share` subdirectory of the current profile.
This means that command completion scripts, `.desktop` files, and similar artifacts installed via [`nix-env`](@docroot@/command-ref/nix-env.md) or [`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md)
(experimental) can be found by any program that follows the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html).
- A new command `nix store add` has been added. It replaces `nix store add-file` and `nix store add-path` which are now deprecated.

View File

@@ -1,2 +1,9 @@
# Release X.Y (202?-??-??)
- Fix a FOD sandbox escape:
Cooperating Nix derivations could send file descriptors to files in the Nix
store to each other via Unix domain sockets in the abstract namespace. This
allowed one derivation to modify the output of the other derivation, after Nix
has registered the path as "valid" and immutable in the Nix database.
In particular, this allowed the output of fixed-output derivations to be
modified from their expected content. This isn't the case any more.

View File

@@ -1,5 +0,0 @@
# Nix Store
The *Nix store* is an abstraction to store immutable file system data (such as software packages) that can have dependencies on other such data.
There are multiple implementations of Nix stores with different capabilities, such as the actual filesystem (`/nix/store`) or binary caches.

View File

@@ -1,10 +0,0 @@
## Store Object
A Nix store is a collection of *store objects* with *references* between them.
A store object consists of
- A [file system object](./file-system-object.md) as data
- A set of [store paths](./store-path.md) as references to other store objects
Store objects are [immutable](https://en.wikipedia.org/wiki/Immutable_object):
Once created, they do not change until they are deleted.

View File

@@ -1,69 +0,0 @@
# Store Path
Nix implements references to [store objects](./index.md#store-object) as *store paths*.
Think of a store path as an [opaque], [unique identifier]:
The only way to obtain store path is by adding or building store objects.
A store path will always reference exactly one store object.
[opaque]: https://en.m.wikipedia.org/wiki/Opaque_data_type
[unique identifier]: https://en.m.wikipedia.org/wiki/Unique_identifier
Store paths are pairs of
- A 20-byte digest for identification
- A symbolic name for people to read
> **Example**
>
> - Digest: `b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z`
> - Name: `firefox-33.1`
To make store objects accessible to operating system processes, stores have to expose store objects through the file system.
A store path is rendered to a file system path as the concatenation of
- [Store directory](#store-directory) (typically `/nix/store`)
- Path separator (`/`)
- Digest rendered in a custom variant of [Base32](https://en.wikipedia.org/wiki/Base32) (20 arbitrary bytes become 32 ASCII characters)
- Hyphen (`-`)
- Name
> **Example**
>
> ```
> /nix/store/b6gvzjyb2pg0kjfwrjmg1vfhh54ad73z-firefox-33.1
> |--------| |------------------------------| |----------|
> store directory digest name
> ```
## Store Directory
Every [Nix store](./index.md) has a store directory.
Not every store can be accessed through the file system.
But if the store has a file system representation, the store directory contains the stores [file system objects], which can be addressed by [store paths](#store-path).
[file system objects]: ./file-system-object.md
This means a store path is not just derived from the referenced store object itself, but depends on the store the store object is in.
> **Note**
>
> The store directory defaults to `/nix/store`, but is in principle arbitrary.
It is important which store a given store object belongs to:
Files in the store object can contain store paths, and processes may read these paths.
Nix can only guarantee referential integrity if store paths do not cross store boundaries.
Therefore one can only copy store objects to a different store if
- The source and target stores' directories match
or
- The store object in question has no references, that is, contains no store paths
One cannot copy a store object to a store with a different store directory.
Instead, it has to be rebuilt, together with all its dependencies.
It is in general not enough to replace the store directory string in file contents, as this may render executables unusable by invalidating their internal offsets or checksums.

View File

@@ -44,6 +44,63 @@ rec {
optionalString = cond: string: if cond then string else "";
showSetting = { inlineHTML }: name: { description, documentDefault, defaultValue, aliases, value, experimentalFeature }:
let
result = squash ''
- ${if inlineHTML
then ''<span id="conf-${name}">[`${name}`](#conf-${name})</span>''
else ''`${name}`''}
${indent " " body}
'';
experimentalFeatureNote = optionalString (experimentalFeature != null) ''
> **Warning**
> This setting is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
To change this setting, you need to make sure the corresponding experimental feature,
[`${experimentalFeature}`](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}),
is enabled.
For example, include the following in [`nix.conf`](#):
```
extra-experimental-features = ${experimentalFeature}
${name} = ...
```
'';
# separate body to cleanly handle indentation
body = ''
${description}
${experimentalFeatureNote}
**Default:** ${showDefault documentDefault defaultValue}
${showAliases aliases}
'';
showDefault = documentDefault: defaultValue:
if documentDefault then
# a StringMap value type is specified as a string, but
# this shows the value type. The empty stringmap is `null` in
# JSON, but that converts to `{ }` here.
if defaultValue == "" || defaultValue == [] || isAttrs defaultValue
then "*empty*"
else if isBool defaultValue then
if defaultValue then "`true`" else "`false`"
else "`${toString defaultValue}`"
else "*machine-specific*";
showAliases = aliases:
optionalString (aliases != [])
"**Deprecated alias:** ${(concatStringsSep ", " (map (s: "`${s}`") aliases))}";
in result;
indent = prefix: s:
concatStringsSep "\n" (map (x: if x == "" then x else "${prefix}${x}") (splitLines s));
showSettings = args: settingsInfo: concatStrings (attrValues (mapAttrs (showSetting args) settingsInfo));
}

8
flake.lock generated
View File

@@ -34,16 +34,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1698876495,
"narHash": "sha256-nsQo2/mkDUFeAjuu92p0dEqhRvHHiENhkKVIV1y0/Oo=",
"lastModified": 1700748986,
"narHash": "sha256-/nqLrNU297h3PCw4QyDpZKZEUHmialJdZW2ceYFobds=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9eb24edd6a0027fed010ccfe300a9734d029983c",
"rev": "9ba29e2346bc542e9909d1021e8fd7d4b3f64db0",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.05",
"ref": "nixos-23.05-small",
"repo": "nixpkgs",
"type": "github"
}

210
flake.nix
View File

@@ -1,9 +1,7 @@
{
description = "The purely functional package manager";
# FIXME go back to nixos-23.05-small once
# https://github.com/NixOS/nixpkgs/pull/264875 is included.
inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-23.05";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small";
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
@@ -27,10 +25,7 @@
darwinSystems = [ "x86_64-darwin" "aarch64-darwin" ];
systems = linuxSystems ++ darwinSystems;
crossSystems = [
"armv6l-linux" "armv7l-linux"
"x86_64-freebsd13" "x86_64-netbsd"
];
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" "ccacheStdenv" ];
@@ -61,40 +56,29 @@
# that would interfere with repo semantics.
fileset.fileFilter (f: f.name != ".gitignore") ./.;
configureFiles = fileset.unions [
./.version
./configure.ac
./m4
# TODO: do we really need README.md? It doesn't seem used in the build.
./README.md
];
topLevelBuildFiles = fileset.unions [
./local.mk
./Makefile
./Makefile.config.in
./mk
];
functionalTestFiles = fileset.unions [
./tests/functional
(fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
];
nixSrc = fileset.toSource {
root = ./.;
fileset = fileset.intersect baseFiles (fileset.unions [
configureFiles
topLevelBuildFiles
./.version
./boehmgc-coroutine-sp-fallback.diff
./bootstrap.sh
./configure.ac
./doc
./local.mk
./m4
./Makefile
./Makefile.config.in
./misc
./mk
./precompiled-headers.h
./src
./unit-test-data
./tests/functional
./tests/unit
./COPYING
./scripts/local.mk
functionalTestFiles
(fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
# TODO: do we really need README.md? It doesn't seem used in the build.
./README.md
]);
};
@@ -102,14 +86,7 @@
nixpkgsFor = forAllSystems
(system: let
make-pkgs = crossSystem: stdenv: import nixpkgs {
localSystem = {
inherit system;
};
crossSystem = if crossSystem == null then null else {
system = crossSystem;
} // lib.optionalAttrs (crossSystem == "x86_64-freebsd13") {
useLLVM = true;
};
inherit system crossSystem;
overlays = [
(overlayFor (p: p.${stdenv}))
];
@@ -164,10 +141,6 @@
testConfigureFlags = [
"RAPIDCHECK_HEADERS=${lib.getDev rapidcheck}/extras/gtest/include"
] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
"--enable-install-unit-tests"
"--with-check-bin-dir=${builtins.placeholder "check"}/bin"
"--with-check-lib-dir=${builtins.placeholder "check"}/lib"
];
internalApiDocsConfigureFlags = [
@@ -189,7 +162,6 @@
buildPackages.git
buildPackages.mercurial # FIXME: remove? only needed for tests
buildPackages.jq # Also for custom mdBook preprocessor.
buildPackages.openssh # only needed for tests (ssh-keygen)
]
++ lib.optionals stdenv.hostPlatform.isLinux [(buildPackages.util-linuxMinimal or buildPackages.utillinuxMinimal)];
@@ -200,9 +172,9 @@
libarchive
boost
lowdown-nix
libsodium
]
++ lib.optionals stdenv.isLinux [libseccomp]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
checkDeps = [
@@ -278,14 +250,7 @@
"-${client.version}-against-${daemon.version}";
inherit version;
src = fileset.toSource {
root = ./.;
fileset = fileset.intersect baseFiles (fileset.unions [
configureFiles
topLevelBuildFiles
functionalTestFiles
]);
};
src = nixSrc;
VERSION_SUFFIX = versionSuffix;
@@ -295,9 +260,7 @@
enableParallelBuilding = true;
configureFlags =
testConfigureFlags # otherwise configure fails
++ [ "--disable-build" ];
configureFlags = testConfigureFlags; # otherwise configure fails
dontBuild = true;
doInstallCheck = true;
@@ -305,10 +268,7 @@
mkdir -p $out
'';
installCheckPhase = ''
mkdir -p src/nix-channel
make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES
'';
installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES";
};
binaryTarball = nix: pkgs:
@@ -408,8 +368,7 @@
src = nixSrc;
VERSION_SUFFIX = versionSuffix;
outputs = [ "out" "dev" "doc" ]
++ lib.optional (currentStdenv.hostPlatform != currentStdenv.buildPlatform) "check";
outputs = [ "out" "dev" "doc" ];
nativeBuildInputs = nativeBuildDeps;
buildInputs = buildDeps
@@ -482,13 +441,39 @@
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
passthru.perl-bindings = final.callPackage ./perl {
inherit fileset;
stdenv = currentStdenv;
};
passthru.perl-bindings = with final; perl.pkgs.toPerlModule (currentStdenv.mkDerivation {
name = "nix-perl-${version}";
src = self;
nativeBuildInputs =
[ buildPackages.autoconf-archive
buildPackages.autoreconfHook
buildPackages.pkg-config
];
buildInputs =
[ nix
curl
bzip2
xz
pkgs.perl
boost
]
++ lib.optional (currentStdenv.isLinux || currentStdenv.isDarwin) libsodium
++ lib.optional currentStdenv.isDarwin darwin.apple_sdk.frameworks.Security;
configureFlags = [
"--with-dbi=${perlPackages.DBI}/${pkgs.perl.libPrefix}"
"--with-dbd-sqlite=${perlPackages.DBDSQLite}/${pkgs.perl.libPrefix}"
];
enableParallelBuilding = true;
postUnpack = "sourceRoot=$sourceRoot/perl";
});
meta.platforms = lib.platforms.unix;
meta.mainProgram = "nix";
});
lowdown-nix = with final; currentStdenv.mkDerivation rec {
@@ -509,6 +494,18 @@
};
};
nixos-lib = import (nixpkgs + "/nixos/lib") { };
# https://nixos.org/manual/nixos/unstable/index.html#sec-calling-nixos-tests
runNixOSTestFor = system: test: nixos-lib.runTest {
imports = [ test ];
hostPkgs = nixpkgsFor.${system}.native;
defaults = {
nixpkgs.pkgs = nixpkgsFor.${system}.native;
};
_module.args.nixpkgs = nixpkgs;
};
in {
# A Nixpkgs overlay that overrides the 'nix' and
# 'nix.perl-bindings' packages.
@@ -615,29 +612,51 @@
};
# System tests.
tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // {
tests.authorization = runNixOSTestFor "x86_64-linux" ./tests/nixos/authorization.nix;
# Make sure that nix-env still produces the exact same result
# on a particular version of Nixpkgs.
evalNixpkgs =
with nixpkgsFor.x86_64-linux.native;
runCommand "eval-nixos" { buildInputs = [ nix ]; }
''
type -p nix-env
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
[[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]]
mkdir $out
'';
tests.remoteBuilds = runNixOSTestFor "x86_64-linux" ./tests/nixos/remote-builds.nix;
nixpkgsLibTests =
forAllSystems (system:
import (nixpkgs + "/lib/tests/release.nix")
{ pkgs = nixpkgsFor.${system}.native;
nixVersions = [ self.packages.${system}.nix ];
}
);
};
tests.nix-copy-closure = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy-closure.nix;
tests.nix-copy = runNixOSTestFor "x86_64-linux" ./tests/nixos/nix-copy.nix;
tests.nssPreload = runNixOSTestFor "x86_64-linux" ./tests/nixos/nss-preload.nix;
tests.githubFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/github-flakes.nix;
tests.sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/sourcehut-flakes.nix;
tests.tarballFlakes = runNixOSTestFor "x86_64-linux" ./tests/nixos/tarball-flakes.nix;
tests.containers = runNixOSTestFor "x86_64-linux" ./tests/nixos/containers/containers.nix;
tests.setuid = lib.genAttrs
["i686-linux" "x86_64-linux"]
(system: runNixOSTestFor system ./tests/nixos/setuid.nix);
tests.ca-fd-leak = runNixOSTestFor "x86_64-linux" ./tests/nixos/ca-fd-leak;
# Make sure that nix-env still produces the exact same result
# on a particular version of Nixpkgs.
tests.evalNixpkgs =
with nixpkgsFor.x86_64-linux.native;
runCommand "eval-nixos" { buildInputs = [ nix ]; }
''
type -p nix-env
# Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593.
time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages
[[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]]
mkdir $out
'';
tests.nixpkgsLibTests =
forAllSystems (system:
import (nixpkgs + "/lib/tests/release.nix")
{ pkgs = nixpkgsFor.${system}.native;
nixVersions = [ self.packages.${system}.nix ];
}
);
metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" {
pkgs = nixpkgsFor.x86_64-linux.native;
@@ -708,29 +727,20 @@
devShells = let
makeShell = pkgs: stdenv:
let
canRunInstalled = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
in
with commonDeps { inherit pkgs; };
stdenv.mkDerivation {
name = "nix";
outputs = [ "out" "dev" "doc" ]
++ lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) "check";
outputs = [ "out" "dev" "doc" ];
nativeBuildInputs = nativeBuildDeps
++ lib.optional stdenv.cc.isClang pkgs.buildPackages.bear
++ lib.optional
(stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
pkgs.buildPackages.clang-tools
;
++ (lib.optionals stdenv.cc.isClang [ pkgs.bear pkgs.clang-tools ]);
buildInputs = buildDeps ++ propagatedDeps
++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
configureFlags = configureFlags
++ testConfigureFlags ++ internalApiDocsConfigureFlags
++ lib.optional (!canRunInstalled) "--disable-doc-gen";
++ testConfigureFlags ++ internalApiDocsConfigureFlags;
enableParallelBuilding = true;

View File

@@ -1,3 +1,5 @@
clean-files += Makefile.config
GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch
# Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers.
ERROR_SWITCH_ENUM = -Werror=switch-enum

View File

@@ -1,64 +0,0 @@
# Ensure that this bug is not present in the C++ toolchain we are using.
#
# URL for bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80431
#
# The test program is from that issue, with only a slight modification
# to set an exit status instead of printing strings.
AC_DEFUN([ENSURE_NO_GCC_BUG_80431],
[
AC_MSG_CHECKING([that GCC bug 80431 is fixed])
AC_LANG_PUSH(C++)
AC_RUN_IFELSE(
[AC_LANG_PROGRAM(
[[
#include <cstdio>
static bool a = true;
static bool b = true;
struct Options { };
struct Option
{
Option(Options * options)
{
a = false;
}
~Option()
{
b = false;
}
};
struct MyOptions : Options { };
struct MyOptions2 : virtual MyOptions
{
Option foo{this};
};
]],
[[
{
MyOptions2 opts;
}
return (a << 1) | b;
]])],
[status_80431=0],
[status_80431=$?],
[
# Assume we're bug-free when cross-compiling
])
AC_LANG_POP(C++)
AS_CASE([$status_80431],
[0],[
AC_MSG_RESULT(yes)
],
[2],[
AC_MSG_RESULT(no)
AC_MSG_ERROR(Cannot build Nix with C++ compiler with this bug)
],
[
AC_MSG_RESULT(unexpected result $status_80431: not expected failure with bug, ignoring)
])
])

View File

@@ -2,7 +2,7 @@
## Motivation
The team's main responsibility is to guide and direct the development of Nix and ensure that the code is in good shape.
The team's main responsibility is to set a direction for the development of Nix and ensure that the code is in good shape.
We aim to achieve this by improving the contributor experience and attracting more maintainers that is, by helping other people contributing to Nix and eventually taking responsibility in order to scale the development process to match users' needs.
@@ -50,9 +50,7 @@ The team meets twice a week:
1. Code review on pull requests from [In review](#in-review).
2. Other chores and tasks.
Meeting notes are collected on a [collaborative scratchpad](https://pad.lassul.us/Cv7FpYx-Ri-4VjUykQOLAw).
Notes on issues and pull requests are posted as comments and linked from the meeting notes, so they are easy to find from both places.
[All meeting notes](https://discourse.nixos.org/search?expanded=true&q=Nix%20team%20meeting%20minutes%20%23%20%23dev%3Anix%20in%3Atitle%20order%3Alatest_topic) are published on Discourse under the [Nix category](https://discourse.nixos.org/c/dev/nix/50).
Meeting notes are collected on a [collaborative scratchpad](https://pad.lassul.us/Cv7FpYx-Ri-4VjUykQOLAw), and published on Discourse under the [Nix category](https://discourse.nixos.org/c/dev/nix/50).
## Project board protocol
@@ -98,10 +96,8 @@ What constitutes a trivial pull request is up to maintainers' judgement.
Pull requests and issues that are deemed important and controversial are discussed by the team during discussion meetings.
This may be where the merit of the change itself or the implementation strategy is contested by a team member.
Whenever the discussion opens up questions about the process or this team's goals, this may indicate that the change is too large in scope.
In that case it is taken off the board to be reconsidered by the author or broken down into smaller pieces that are less far-reaching and can be reviewed independently.
As a general guideline, the order of items to discuss is determined as follows:
As a general guideline, the order of items is determined as follows:
- Prioritise pull requests over issues

View File

@@ -1,27 +1,15 @@
# Remove overall test dir (at most one of the two should match) and
# remove file extension.
test_name=$(echo -n "$test" | sed \
-e "s|^unit-test-data/||" \
-e "s|^tests/functional/||" \
-e "s|\.sh$||" \
)
test_dir=tests/functional
TESTS_ENVIRONMENT=(
"TEST_NAME=$test_name"
'NIX_REMOTE='
'PS4=+(${BASH_SOURCE[0]-$0}:$LINENO) '
)
test=$(echo -n "$test" | sed -e "s|^$test_dir/||")
TESTS_ENVIRONMENT=("TEST_NAME=${test%.*}" 'NIX_REMOTE=')
: ${BASH:=/usr/bin/env bash}
run () {
cd "$(dirname $1)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -x -e -u -o pipefail $(basename $1)
}
init_test () {
run "$init" 2>/dev/null > /dev/null
cd "$test_dir" && env "${TESTS_ENVIRONMENT[@]}" $BASH -e init.sh 2>/dev/null > /dev/null
}
run_test_proper () {
run "$test"
cd "$test_dir/$(dirname $test)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -e $(basename $test)
}

View File

@@ -3,12 +3,9 @@
set -eu -o pipefail
test=$1
init=${2-}
dir="$(dirname "${BASH_SOURCE[0]}")"
source "$dir/common-test.sh"
if [ -n "$init" ]; then
(init_test)
fi
(init_test)
run_test_proper

View File

@@ -122,15 +122,14 @@ $(foreach script, $(bin-scripts), $(eval $(call install-program-in,$(script),$(b
$(foreach script, $(bin-scripts), $(eval programs-list += $(script)))
$(foreach script, $(noinst-scripts), $(eval programs-list += $(script)))
$(foreach template, $(template-files), $(eval $(call instantiate-template,$(template))))
install_test_init=tests/functional/init.sh
$(foreach test, $(install-tests), \
$(eval $(call run-test,$(test),$(install_test_init))) \
$(eval $(call run-install-test,$(test))) \
$(eval installcheck: $(test).test))
$(foreach test-group, $(install-tests-groups), \
$(eval $(call run-test-group,$(test-group),$(install_test_init))) \
$(eval $(call run-install-test-group,$(test-group))) \
$(eval installcheck: $(test-group).test-group) \
$(foreach test, $($(test-group)-tests), \
$(eval $(call run-test,$(test),$(install_test_init))) \
$(eval $(call run-install-test,$(test))) \
$(eval $(test-group).test-group: $(test).test)))
$(foreach file, $(man-pages), $(eval $(call install-data-in, $(file), $(mandir)/man$(patsubst .%,%,$(suffix $(file))))))

View File

@@ -87,6 +87,6 @@ define build-program
# Phony target to run this program (typically as a dependency of 'check').
.PHONY: $(1)_RUN
$(1)_RUN: $$($(1)_PATH)
$(trace-test) $$(UNIT_TEST_ENV) $$($(1)_PATH)
$(trace-test) $$($(1)_PATH)
endef

View File

@@ -8,7 +8,6 @@ yellow=""
normal=""
test=$1
init=${2-}
dir="$(dirname "${BASH_SOURCE[0]}")"
source "$dir/common-test.sh"
@@ -22,9 +21,7 @@ if [ -t 1 ]; then
fi
run_test () {
if [ -n "$init" ]; then
(init_test 2>/dev/null > /dev/null)
fi
(init_test 2>/dev/null > /dev/null)
log="$(run_test_proper 2>&1)" && status=0 || status=$?
}

View File

@@ -2,22 +2,19 @@
test-deps =
define run-bash
define run-install-test
.PHONY: $1
$1: $2
@env BASH=$(bash) $(bash) $3 < /dev/null
.PHONY: $1.test
$1.test: $1 $(test-deps)
@env BASH=$(bash) $(bash) mk/run-test.sh $1 < /dev/null
.PHONY: $1.test-debug
$1.test-debug: $1 $(test-deps)
@env BASH=$(bash) $(bash) mk/debug-test.sh $1 < /dev/null
endef
define run-test
$(eval $(call run-bash,$1.test,$1 $(test-deps),mk/run-test.sh $1 $2))
$(eval $(call run-bash,$1.test-debug,$1 $(test-deps),mk/debug-test.sh $1 $2))
endef
define run-test-group
define run-install-test-group
.PHONY: $1.test-group

View File

@@ -1,12 +1,6 @@
makefiles = local.mk
GLOBAL_CXXFLAGS += -g -Wall -std=c++2a
# A convenience for concurrent development of Nix and its Perl bindings.
# Not needed in a standalone build of the Perl bindings.
ifneq ("$(wildcard ../src)", "")
GLOBAL_CXXFLAGS += -I ../src
endif
GLOBAL_CXXFLAGS += -g -Wall -std=c++2a -I ../src
-include Makefile.config

View File

@@ -1,51 +0,0 @@
{ lib, fileset
, stdenv
, perl, perlPackages
, autoconf-archive, autoreconfHook, pkg-config
, nix, curl, bzip2, xz, boost, libsodium, darwin
}:
perl.pkgs.toPerlModule (stdenv.mkDerivation {
name = "nix-perl-${nix.version}";
src = fileset.toSource {
root = ../.;
fileset = fileset.unions [
../.version
../m4
../mk
./MANIFEST
./Makefile
./Makefile.config.in
./configure.ac
./lib
./local.mk
];
};
nativeBuildInputs =
[ autoconf-archive
autoreconfHook
pkg-config
];
buildInputs =
[ nix
curl
bzip2
xz
perl
boost
]
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security;
configureFlags = [
"--with-dbi=${perlPackages.DBI}/${perl.libPrefix}"
"--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}"
];
enableParallelBuilding = true;
postUnpack = "sourceRoot=$sourceRoot/perl";
})

View File

@@ -11,6 +11,7 @@
#include "derivations.hh"
#include "globals.hh"
#include "store-api.hh"
#include "util.hh"
#include "crypto.hh"
#include <sodium.h>
@@ -77,7 +78,7 @@ SV * queryReferences(char * path)
SV * queryPathHash(char * path)
PPCODE:
try {
auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash.to_string(HashFormat::Base32, true);
auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash.to_string(Base32, true);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -103,7 +104,7 @@ SV * queryPathInfo(char * path, int base32)
XPUSHs(&PL_sv_undef);
else
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(*info->deriver).c_str(), 0)));
auto s = info->narHash.to_string(base32 ? HashFormat::Base32 : HashFormat::Base16, true);
auto s = info->narHash.to_string(base32 ? Base32 : Base16, true);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
mXPUSHi(info->registrationTime);
mXPUSHi(info->narSize);
@@ -205,7 +206,7 @@ SV * hashPath(char * algo, int base32, char * path)
PPCODE:
try {
Hash h = hashPath(parseHashType(algo), path).first;
auto s = h.to_string(base32 ? HashFormat::Base32 : HashFormat::Base16, false);
auto s = h.to_string(base32 ? Base32 : Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -216,7 +217,7 @@ SV * hashFile(char * algo, int base32, char * path)
PPCODE:
try {
Hash h = hashFile(parseHashType(algo), path);
auto s = h.to_string(base32 ? HashFormat::Base32 : HashFormat::Base16, false);
auto s = h.to_string(base32 ? Base32 : Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -227,7 +228,7 @@ SV * hashString(char * algo, int base32, char * s)
PPCODE:
try {
Hash h = hashString(parseHashType(algo), s);
auto s = h.to_string(base32 ? HashFormat::Base32 : HashFormat::Base16, false);
auto s = h.to_string(base32 ? Base32 : Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());
@@ -238,7 +239,7 @@ SV * convertHash(char * algo, char * s, int toBase32)
PPCODE:
try {
auto h = Hash::parseAny(s, parseHashType(algo));
auto s = h.to_string(toBase32 ? HashFormat::Base32 : HashFormat::Base16, false);
auto s = h.to_string(toBase32 ? Base32 : Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
} catch (Error & e) {
croak("%s", e.what());

View File

@@ -452,14 +452,6 @@ EOF
# a row for different files.
if [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then
# this backup process first released in Nix 2.1
if diff -q "$profile_target$PROFILE_BACKUP_SUFFIX" "$profile_target" > /dev/null; then
# a backup file for the rc-file exist, but they are identical,
# so we can safely ignore it and overwrite it with the same
# content later
continue
fi
failure <<EOF
I back up shell profile/rc scripts before I add Nix to them.
I need to back up $profile_target to $profile_target$PROFILE_BACKUP_SUFFIX,

View File

@@ -19,14 +19,6 @@ set __ETC_PROFILE_NIX_SOURCED 1
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
# Populate bash completions, .desktop files, etc
if test -z "$XDG_DATA_DIRS"
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
set --export XDG_DATA_DIRS "/usr/local/share:/usr/share:/nix/var/nix/profiles/default/share"
else
set --export XDG_DATA_DIRS "$XDG_DATA_DIRS:/nix/var/nix/profiles/default/share"
end
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if test -n "$NIX_SSH_CERT_FILE"
: # Allow users to override the NIX_SSL_CERT_FILE

View File

@@ -30,14 +30,6 @@ fi
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
# Populate bash completions, .desktop files, etc
if [ -z "$XDG_DATA_DIRS" ]; then
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
export XDG_DATA_DIRS="/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
else
export XDG_DATA_DIRS="$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
fi
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if [ -n "${NIX_SSL_CERT_FILE:-}" ]; then
: # Allow users to override the NIX_SSL_CERT_FILE

View File

@@ -20,14 +20,6 @@ if test -n "$HOME" && test -n "$USER"
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
set --export NIX_PROFILES "@localstatedir@/nix/profiles/default $HOME/.nix-profile"
# Populate bash completions, .desktop files, etc
if test -z "$XDG_DATA_DIRS"
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
set --export XDG_DATA_DIRS "/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
else
set --export XDG_DATA_DIRS "$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
end
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if test -n "$NIX_SSH_CERT_FILE"
: # Allow users to override the NIX_SSL_CERT_FILE

View File

@@ -32,14 +32,6 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then
# This part should be kept in sync with nixpkgs:nixos/modules/programs/environment.nix
export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK"
# Populate bash completions, .desktop files, etc
if [ -z "$XDG_DATA_DIRS" ]; then
# According to XDG spec the default is /usr/local/share:/usr/share, don't set something that prevents that default
export XDG_DATA_DIRS="/usr/local/share:/usr/share:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
else
export XDG_DATA_DIRS="$XDG_DATA_DIRS:$NIX_LINK/share:/nix/var/nix/profiles/default/share"
fi
# Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work.
if [ -e /etc/ssl/certs/ca-certificates.crt ]; then # NixOS, Ubuntu, Debian, Gentoo, Arch
export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt

View File

@@ -1,6 +1,3 @@
#pragma once
///@file
#include "derived-path.hh"
#include "realisation.hh"

View File

@@ -98,7 +98,7 @@ EvalCommand::EvalCommand()
EvalCommand::~EvalCommand()
{
if (evalState)
evalState->maybePrintStats();
evalState->printStats();
}
ref<Store> EvalCommand::getEvalStore()
@@ -175,7 +175,7 @@ void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
throw UsageError("'--all' does not expect arguments");
// XXX: Only uses opaque paths, ignores all the realisations
for (auto & p : store->queryAllValidPaths())
paths.emplace_back(BuiltPath::Opaque{p});
paths.push_back(BuiltPath::Opaque{p});
} else {
paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
if (recursive) {
@@ -188,7 +188,7 @@ void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
}
store->computeFSClosure(pathsRoots, pathsClosure);
for (auto & path : pathsClosure)
paths.emplace_back(BuiltPath::Opaque{path});
paths.push_back(BuiltPath::Opaque{path});
}
}

View File

@@ -34,28 +34,21 @@ struct NixMultiCommand : virtual MultiCommand, virtual Command
// For the overloaded run methods
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
/**
* A command that requires a \ref Store "Nix store".
*/
/* A command that requires a Nix store. */
struct StoreCommand : virtual Command
{
StoreCommand();
void run() override;
ref<Store> getStore();
virtual ref<Store> createStore();
/**
* Main entry point, with a `Store` provided
*/
virtual void run(ref<Store>) = 0;
private:
std::shared_ptr<Store> _store;
};
/**
* A command that copies something between `--from` and `--to` \ref
* Store stores.
*/
/* A command that copies something between `--from` and `--to`
stores. */
struct CopyCommand : virtual StoreCommand
{
std::string srcUri, dstUri;
@@ -67,9 +60,6 @@ struct CopyCommand : virtual StoreCommand
ref<Store> getDstStore();
};
/**
* A command that needs to evaluate Nix language expressions.
*/
struct EvalCommand : virtual StoreCommand, MixEvalArgs
{
bool startReplOnEvalErrors = false;
@@ -89,26 +79,20 @@ private:
std::shared_ptr<EvalState> evalState;
};
/**
* A mixin class for commands that process flakes, adding a few standard
* flake-related options/flags.
*/
struct MixFlakeOptions : virtual Args, EvalCommand
{
flake::LockFlags lockFlags;
std::optional<std::string> needsFlakeInputCompletion = {};
MixFlakeOptions();
/**
* The completion for some of these flags depends on the flake(s) in
* question.
*
* This method should be implemented to gather all flakerefs the
* command is operating with (presumably specified via some other
* arguments) so that the completions for these flags can use them.
*/
virtual std::vector<FlakeRef> getFlakeRefsForCompletion()
virtual std::vector<std::string> getFlakesForCompletion()
{ return {}; }
void completeFlakeInput(std::string_view prefix);
void completionHook() override;
};
struct SourceExprCommand : virtual Args, MixFlakeOptions
@@ -128,35 +112,15 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
virtual Strings getDefaultFlakeAttrPathPrefixes();
/**
* Complete an installable from the given prefix.
*/
void completeInstallable(AddCompletions & completions, std::string_view prefix);
/**
* Convenience wrapper around the underlying function to make setting the
* callback easier.
*/
CompleterClosure getCompleteInstallable();
void completeInstallable(std::string_view prefix);
};
/**
* A mixin class for commands that need a read-only flag.
*
* What exactly is "read-only" is unspecified, but it will usually be
* the \ref Store "Nix store".
*/
struct MixReadOnlyOption : virtual Args
{
MixReadOnlyOption();
};
/**
* Like InstallablesCommand but the installables are not loaded.
*
* This is needed by `CmdRepl` which wants to load (and reload) the
* installables itself.
*/
/* Like InstallablesCommand but the installables are not loaded */
struct RawInstallablesCommand : virtual Args, SourceExprCommand
{
RawInstallablesCommand();
@@ -165,22 +129,19 @@ struct RawInstallablesCommand : virtual Args, SourceExprCommand
void run(ref<Store> store) override;
// FIXME make const after `CmdRepl`'s override is fixed up
// FIXME make const after CmdRepl's override is fixed up
virtual void applyDefaultInstallables(std::vector<std::string> & rawInstallables);
bool readFromStdIn = false;
std::vector<FlakeRef> getFlakeRefsForCompletion() override;
std::vector<std::string> getFlakesForCompletion() override;
private:
std::vector<std::string> rawInstallables;
};
/**
* A command that operates on a list of "installables", which can be
* store paths, attribute paths, Nix expressions, etc.
*/
/* A command that operates on a list of "installables", which can be
store paths, attribute paths, Nix expressions, etc. */
struct InstallablesCommand : RawInstallablesCommand
{
virtual void run(ref<Store> store, Installables && installables) = 0;
@@ -188,9 +149,7 @@ struct InstallablesCommand : RawInstallablesCommand
void run(ref<Store> store, std::vector<std::string> && rawInstallables) override;
};
/**
* A command that operates on exactly one "installable".
*/
/* A command that operates on exactly one "installable" */
struct InstallableCommand : virtual Args, SourceExprCommand
{
InstallableCommand();
@@ -199,7 +158,10 @@ struct InstallableCommand : virtual Args, SourceExprCommand
void run(ref<Store> store) override;
std::vector<FlakeRef> getFlakeRefsForCompletion() override;
std::vector<std::string> getFlakesForCompletion() override
{
return {_installable};
}
private:
@@ -213,12 +175,7 @@ struct MixOperateOnOptions : virtual Args
MixOperateOnOptions();
};
/**
* A command that operates on zero or more extant store paths.
*
* If the argument the user passes is a some sort of recipe for a path
* not yet built, it must be built first.
*/
/* A command that operates on zero or more store paths. */
struct BuiltPathsCommand : InstallablesCommand, virtual MixOperateOnOptions
{
private:
@@ -250,9 +207,7 @@ struct StorePathsCommand : public BuiltPathsCommand
void run(ref<Store> store, BuiltPaths && paths) override;
};
/**
* A command that operates on exactly one store path.
*/
/* A command that operates on exactly one store path. */
struct StorePathCommand : public StorePathsCommand
{
virtual void run(ref<Store> store, const StorePath & storePath) = 0;
@@ -260,9 +215,7 @@ struct StorePathCommand : public StorePathsCommand
void run(ref<Store> store, StorePaths && storePaths) override;
};
/**
* A helper class for registering \ref Command commands globally.
*/
/* A helper class for registering commands globally. */
struct RegisterCommand
{
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
@@ -318,24 +271,13 @@ struct MixEnvironment : virtual Args {
MixEnvironment();
/***
* Modify global environ based on `ignoreEnvironment`, `keep`, and
* `unset`. It's expected that exec will be called before this class
* goes out of scope, otherwise `environ` will become invalid.
*/
/* Modify global environ based on ignoreEnvironment, keep, and unset. It's expected that exec will be called before this class goes out of scope, otherwise environ will become invalid. */
void setEnviron();
};
void completeFlakeInputPath(
AddCompletions & completions,
ref<EvalState> evalState,
const std::vector<FlakeRef> & flakeRefs,
std::string_view prefix);
void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::string_view prefix);
void completeFlakeRef(ref<Store> store, std::string_view prefix);
void completeFlakeRefWithFragment(
AddCompletions & completions,
ref<EvalState> evalState,
flake::LockFlags lockFlags,
Strings attrPathPrefixes,

View File

@@ -2,13 +2,13 @@
#include "common-eval-args.hh"
#include "shared.hh"
#include "filetransfer.hh"
#include "util.hh"
#include "eval.hh"
#include "fetchers.hh"
#include "registry.hh"
#include "flake/flakeref.hh"
#include "store-api.hh"
#include "command.hh"
#include "tarball.hh"
namespace nix {
@@ -132,8 +132,8 @@ MixEvalArgs::MixEvalArgs()
if (to.subdir != "") extraAttrs["dir"] = to.subdir;
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
}},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, openStore(), prefix);
.completer = {[&](size_t, std::string_view prefix) {
completeFlakeRef(openStore(), prefix);
}}
});
@@ -164,18 +164,18 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
return res.finish();
}
SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDir)
SourcePath lookupFileArg(EvalState & state, std::string_view s)
{
if (EvalSettings::isPseudoUrl(s)) {
auto storePath = fetchers::downloadTarball(
state.store, EvalSettings::resolvePseudoUrl(s), "source", false).storePath;
state.store, EvalSettings::resolvePseudoUrl(s), "source", false).tree.storePath;
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
}
else if (hasPrefix(s, "flake:")) {
experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false);
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first;
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
}
@@ -185,7 +185,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDi
}
else
return state.rootPath(CanonPath(s, baseDir));
return state.rootPath(CanonPath::fromCwd(s));
}
}

View File

@@ -2,7 +2,6 @@
///@file
#include "args.hh"
#include "canon-path.hh"
#include "common-args.hh"
#include "search-path.hh"
@@ -29,6 +28,6 @@ private:
std::map<std::string, std::string> autoArgs;
};
SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDir = CanonPath::fromCwd());
SourcePath lookupFileArg(EvalState & state, std::string_view s);
}

View File

@@ -1,5 +1,5 @@
#include "util.hh"
#include "editor-for.hh"
#include "environment-variables.hh"
namespace nix {

View File

@@ -4,6 +4,7 @@
#include "globals.hh"
#include "installable-value.hh"
#include "outputs-spec.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
#include "common-eval-args.hh"

View File

@@ -28,11 +28,6 @@ namespace nix {
std::vector<std::string> InstallableFlake::getActualAttrPaths()
{
std::vector<std::string> res;
if (attrPaths.size() == 1 && attrPaths.front().starts_with(".")){
attrPaths.front().erase(0,1);
res.push_back(attrPaths.front());
return res;
}
for (auto & prefix : prefixes)
res.push_back(prefix + *attrPaths.begin());

View File

@@ -4,7 +4,6 @@
#include "installable-attr-path.hh"
#include "installable-flake.hh"
#include "outputs-spec.hh"
#include "users.hh"
#include "util.hh"
#include "command.hh"
#include "attr-path.hh"
@@ -29,24 +28,17 @@
namespace nix {
void completeFlakeInputPath(
AddCompletions & completions,
ref<EvalState> evalState,
const std::vector<FlakeRef> & flakeRefs,
std::string_view prefix)
{
for (auto & flakeRef : flakeRefs) {
auto flake = flake::getFlake(*evalState, flakeRef, true);
for (auto & input : flake.inputs)
if (hasPrefix(input.first, prefix))
completions.add(input.first);
}
}
MixFlakeOptions::MixFlakeOptions()
{
auto category = "Common flake-related options";
addFlag({
.longName = "recreate-lock-file",
.description = "Recreate the flake's lock file from scratch.",
.category = category,
.handler = {&lockFlags.recreateLockFile, true}
});
addFlag({
.longName = "no-update-lock-file",
.description = "Do not allow any updates to the flake's lock file.",
@@ -79,6 +71,19 @@ MixFlakeOptions::MixFlakeOptions()
.handler = {&lockFlags.commitLockFile, true}
});
addFlag({
.longName = "update-input",
.description = "Update a specific flake input (ignoring its previous entry in the lock file).",
.category = category,
.labels = {"input-path"},
.handler = {[&](std::string s) {
lockFlags.inputUpdates.insert(flake::parseInputPath(s));
}},
.completer = {[&](size_t, std::string_view prefix) {
needsFlakeInputCompletion = {std::string(prefix)};
}}
});
addFlag({
.longName = "override-input",
.description = "Override a specific flake input (e.g. `dwarffs/nixpkgs`). This implies `--no-write-lock-file`.",
@@ -88,14 +93,13 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.writeLockFile = false;
lockFlags.inputOverrides.insert_or_assign(
flake::parseInputPath(inputPath),
parseFlakeRef(flakeRef, absPath(getCommandBaseDir()), true));
parseFlakeRef(flakeRef, absPath("."), true));
}},
.completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) {
if (n == 0) {
completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix);
} else if (n == 1) {
completeFlakeRef(completions, getEvalState()->store, prefix);
}
.completer = {[&](size_t n, std::string_view prefix) {
if (n == 0)
needsFlakeInputCompletion = {std::string(prefix)};
else if (n == 1)
completeFlakeRef(getEvalState()->store, prefix);
}}
});
@@ -130,7 +134,7 @@ MixFlakeOptions::MixFlakeOptions()
auto evalState = getEvalState();
auto flake = flake::lockFlake(
*evalState,
parseFlakeRef(flakeRef, absPath(getCommandBaseDir())),
parseFlakeRef(flakeRef, absPath(".")),
{ .writeLockFile = false });
for (auto & [inputName, input] : flake.lockFile.root->inputs) {
auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes
@@ -142,12 +146,30 @@ MixFlakeOptions::MixFlakeOptions()
}
}
}},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, getEvalState()->store, prefix);
.completer = {[&](size_t, std::string_view prefix) {
completeFlakeRef(getEvalState()->store, prefix);
}}
});
}
void MixFlakeOptions::completeFlakeInput(std::string_view prefix)
{
auto evalState = getEvalState();
for (auto & flakeRefS : getFlakesForCompletion()) {
auto flakeRef = parseFlakeRefWithFragment(expandTilde(flakeRefS), absPath(".")).first;
auto flake = flake::getFlake(*evalState, flakeRef, true);
for (auto & input : flake.inputs)
if (hasPrefix(input.first, prefix))
completions->add(input.first);
}
}
void MixFlakeOptions::completionHook()
{
if (auto & prefix = needsFlakeInputCompletion)
completeFlakeInput(*prefix);
}
SourceExprCommand::SourceExprCommand()
{
addFlag({
@@ -204,18 +226,11 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
};
}
Args::CompleterClosure SourceExprCommand::getCompleteInstallable()
{
return [this](AddCompletions & completions, size_t, std::string_view prefix) {
completeInstallable(completions, prefix);
};
}
void SourceExprCommand::completeInstallable(AddCompletions & completions, std::string_view prefix)
void SourceExprCommand::completeInstallable(std::string_view prefix)
{
try {
if (file) {
completions.setType(AddCompletions::Type::Attrs);
completionType = ctAttrs;
evalSettings.pureEval = false;
auto state = getEvalState();
@@ -250,15 +265,14 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
std::string name = state->symbols[i.name];
if (name.find(searchWord) == 0) {
if (prefix_ == "")
completions.add(name);
completions->add(name);
else
completions.add(prefix_ + "." + name);
completions->add(prefix_ + "." + name);
}
}
}
} else {
completeFlakeRefWithFragment(
completions,
getEvalState(),
lockFlags,
getDefaultFlakeAttrPathPrefixes(),
@@ -271,7 +285,6 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s
}
void completeFlakeRefWithFragment(
AddCompletions & completions,
ref<EvalState> evalState,
flake::LockFlags lockFlags,
Strings attrPathPrefixes,
@@ -283,19 +296,12 @@ void completeFlakeRefWithFragment(
try {
auto hash = prefix.find('#');
if (hash == std::string::npos) {
completeFlakeRef(completions, evalState->store, prefix);
completeFlakeRef(evalState->store, prefix);
} else {
completions.setType(AddCompletions::Type::Attrs);
completionType = ctAttrs;
auto fragment = prefix.substr(hash + 1);
std::string prefixRoot = "";
if (fragment.starts_with(".")){
fragment = fragment.substr(1);
prefixRoot = ".";
}
auto flakeRefS = std::string(prefix.substr(0, hash));
// TODO: ideally this would use the command base directory instead of assuming ".".
auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
auto evalCache = openEvalCache(*evalState,
@@ -303,9 +309,6 @@ void completeFlakeRefWithFragment(
auto root = evalCache->getRoot();
if (prefixRoot == "."){
attrPathPrefixes.clear();
}
/* Complete 'fragment' relative to all the
attrpath prefixes as well as the root of the
flake. */
@@ -330,7 +333,7 @@ void completeFlakeRefWithFragment(
auto attrPath2 = (*attr)->getAttrPath(attr2);
/* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
completions->add(flakeRefS + "#" + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
}
}
}
@@ -341,7 +344,7 @@ void completeFlakeRefWithFragment(
for (auto & attrPath : defaultFlakeAttrPaths) {
auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath));
if (!attr) continue;
completions.add(flakeRefS + "#" + prefixRoot);
completions->add(flakeRefS + "#");
}
}
}
@@ -350,15 +353,15 @@ void completeFlakeRefWithFragment(
}
}
void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::string_view prefix)
void completeFlakeRef(ref<Store> store, std::string_view prefix)
{
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
return;
if (prefix == "")
completions.add(".");
completions->add(".");
Args::completeDir(completions, 0, prefix);
completeDir(0, prefix);
/* Look for registry entries that match the prefix. */
for (auto & registry : fetchers::getRegistries(store)) {
@@ -367,10 +370,10 @@ void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::strin
if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) {
std::string from2(from, 6);
if (hasPrefix(from2, prefix))
completions.add(from2);
completions->add(from2);
} else {
if (hasPrefix(from, prefix))
completions.add(from);
completions->add(from);
}
}
}
@@ -444,12 +447,10 @@ Installables SourceExprCommand::parseInstallables(
auto e = state->parseStdin();
state->eval(e, *vFile);
}
else if (file) {
state->evalFile(lookupFileArg(*state, *file, CanonPath::fromCwd(getCommandBaseDir())), *vFile);
}
else if (file)
state->evalFile(lookupFileArg(*state, *file), *vFile);
else {
CanonPath dir(CanonPath::fromCwd(getCommandBaseDir()));
auto e = state->parseExprFromString(*expr, state->rootPath(dir));
auto e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
state->eval(e, *vFile);
}
@@ -484,7 +485,7 @@ Installables SourceExprCommand::parseInstallables(
}
try {
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath(getCommandBaseDir()));
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath("."));
result.push_back(make_ref<InstallableFlake>(
this,
getEvalState(),
@@ -668,7 +669,7 @@ BuiltPaths Installable::toBuiltPaths(
BuiltPaths res;
for (auto & drvPath : Installable::toDerivations(store, installables, true))
res.emplace_back(BuiltPath::Opaque{drvPath});
res.push_back(BuiltPath::Opaque{drvPath});
return res;
}
}
@@ -738,7 +739,9 @@ RawInstallablesCommand::RawInstallablesCommand()
expectArgs({
.label = "installables",
.handler = {&rawInstallables},
.completer = getCompleteInstallable(),
.completer = {[&](size_t, std::string_view prefix) {
completeInstallable(prefix);
}}
});
}
@@ -751,17 +754,6 @@ void RawInstallablesCommand::applyDefaultInstallables(std::vector<std::string> &
}
}
std::vector<FlakeRef> RawInstallablesCommand::getFlakeRefsForCompletion()
{
applyDefaultInstallables(rawInstallables);
std::vector<FlakeRef> res;
for (auto i : rawInstallables)
res.push_back(parseFlakeRefWithFragment(
expandTilde(i),
absPath(getCommandBaseDir())).first);
return res;
}
void RawInstallablesCommand::run(ref<Store> store)
{
if (readFromStdIn && !isatty(STDIN_FILENO)) {
@@ -775,13 +767,10 @@ void RawInstallablesCommand::run(ref<Store> store)
run(store, std::move(rawInstallables));
}
std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
std::vector<std::string> RawInstallablesCommand::getFlakesForCompletion()
{
return {
parseFlakeRefWithFragment(
expandTilde(_installable),
absPath(getCommandBaseDir())).first
};
applyDefaultInstallables(rawInstallables);
return rawInstallables;
}
void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables)
@@ -797,7 +786,9 @@ InstallableCommand::InstallableCommand()
.label = "installable",
.optional = true,
.handler = {&_installable},
.completer = getCompleteInstallable(),
.completer = {[&](size_t, std::string_view prefix) {
completeInstallable(prefix);
}}
});
}

View File

@@ -1,6 +1,7 @@
#pragma once
///@file
#include "util.hh"
#include "path.hh"
#include "outputs-spec.hh"
#include "derived-path.hh"

View File

@@ -1,7 +1,6 @@
#include "markdown.hh"
#include "util.hh"
#include "finally.hh"
#include "terminal.hh"
#include <sys/queue.h>
#include <lowdown.h>

View File

@@ -22,7 +22,6 @@ extern "C" {
#include "repl.hh"
#include "ansicolor.hh"
#include "signals.hh"
#include "shared.hh"
#include "eval.hh"
#include "eval-cache.hh"
@@ -37,8 +36,6 @@ extern "C" {
#include "globals.hh"
#include "flake/flake.hh"
#include "flake/lockfile.hh"
#include "users.hh"
#include "terminal.hh"
#include "editor-for.hh"
#include "finally.hh"
#include "markdown.hh"
@@ -925,7 +922,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
case nString:
str << ANSI_WARNING;
printLiteralString(str, v.string_view());
printLiteralString(str, v.string.s);
str << ANSI_NORMAL;
break;

View File

@@ -1,5 +1,6 @@
#include "attr-path.hh"
#include "eval-inline.hh"
#include "util.hh"
namespace nix {
@@ -131,7 +132,7 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
if (colon == std::string::npos) fail();
std::string filename(fn, 0, colon);
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno};
return {CanonPath(fn.substr(0, colon)), lineno};
} catch (std::invalid_argument & e) {
fail();
abort();

View File

@@ -1,4 +1,3 @@
#include "users.hh"
#include "eval-cache.hh"
#include "sqlite.hh"
#include "eval.hh"
@@ -51,7 +50,7 @@ struct AttrDb
Path cacheDir = getCacheDir() + "/nix/eval-cache-v5";
createDirs(cacheDir);
Path dbPath = cacheDir + "/" + fingerprint.to_string(HashFormat::Base16, false) + ".sqlite";
Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite";
state->db = SQLite(dbPath);
state->db.isCache();
@@ -441,8 +440,8 @@ Value & AttrCursor::forceValue()
if (root->db && (!cachedValue || std::get_if<placeholder_t>(&cachedValue->second))) {
if (v.type() == nString)
cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()),
string_t{v.c_str(), {}}};
cachedValue = {root->db->setString(getKey(), v.string.s, v.string.context),
string_t{v.string.s, {}}};
else if (v.type() == nPath) {
auto path = v.path().path;
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
@@ -583,7 +582,7 @@ std::string AttrCursor::getString()
if (v.type() != nString && v.type() != nPath)
root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow<TypeError>();
return v.type() == nString ? v.c_str() : v.path().to_string();
return v.type() == nString ? v.string.s : v.path().to_string();
}
string_t AttrCursor::getStringWithContext()
@@ -625,7 +624,7 @@ string_t AttrCursor::getStringWithContext()
if (v.type() == nString) {
NixStringContext context;
copyContext(v, context);
return {v.c_str(), std::move(context)};
return {v.string.s, std::move(context)};
}
else if (v.type() == nPath)
return {v.path().to_string(), {}};

View File

@@ -1,4 +1,3 @@
#include "users.hh"
#include "globals.hh"
#include "profiles.hh"
#include "eval.hh"

View File

@@ -1,6 +1,4 @@
#pragma once
///@file
#include "config.hh"
namespace nix {
@@ -31,12 +29,10 @@ struct EvalSettings : Config
this, false, "restrict-eval",
R"(
If set to `true`, the Nix evaluator will not allow access to any
files outside of
[`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath),
or to URIs outside of
[`allowed-uris`](@docroot@/command-ref/conf-file.md#conf-allowed-uris).
Also the default value for [`nix-path`](#conf-nix-path) is ignored, such that only explicitly set search path entries are taken into account.
files outside of the Nix search path (as set via the `NIX_PATH`
environment variable or the `-I` option), or to URIs outside of
[`allowed-uris`](../command-ref/conf-file.md#conf-allowed-uris).
The default is `false`.
)"};
Setting<bool> pureEval{this, false, "pure-eval",
@@ -44,22 +40,18 @@ struct EvalSettings : Config
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
- Restrict file system and network access to files specified by cryptographic hash
- Disable impure constants:
- [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem)
- [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
- [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath)
- Disable [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
)"
};
Setting<bool> enableImportFromDerivation{
this, true, "allow-import-from-derivation",
R"(
By default, Nix allows [Import from Derivation](@docroot@/language/import-from-derivation.md).
With this option set to `false`, Nix will throw an error when evaluating an expression that uses this feature,
even when the required store object is readily available.
This ensures that evaluation will not require any builds to take place,
regardless of the state of the store.
By default, Nix allows you to `import` from a derivation, allowing
building at evaluation time. With this option set to false, Nix will
throw an error when evaluating an expression that uses this feature,
allowing users to ensure their evaluation will not require any
builds to take place.
)"};
Setting<Strings> allowedUris{this, {}, "allowed-uris",

View File

@@ -1,7 +1,6 @@
#include "eval.hh"
#include "eval-settings.hh"
#include "hash.hh"
#include "primops.hh"
#include "types.hh"
#include "util.hh"
#include "store-api.hh"
@@ -13,9 +12,6 @@
#include "function-trace.hh"
#include "profiles.hh"
#include "print.hh"
#include "fs-input-accessor.hh"
#include "memory-input-accessor.hh"
#include "signals.hh"
#include <algorithm>
#include <chrono>
@@ -118,7 +114,7 @@ void Value::print(const SymbolTable &symbols, std::ostream &str,
printLiteralBool(str, boolean);
break;
case tString:
printLiteralString(str, string_view());
printLiteralString(str, string.s);
break;
case tPath:
str << path().to_string(); // !!! escaping?
@@ -343,7 +339,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
Value nameValue;
name.expr->eval(state, env, nameValue);
state.forceStringNoCtx(nameValue, noPos, "while evaluating an attribute name");
return state.symbols.create(nameValue.string_view());
return state.symbols.create(nameValue.string.s);
}
}
@@ -507,17 +503,7 @@ EvalState::EvalState(
, sOutputSpecified(symbols.create("outputSpecified"))
, repair(NoRepair)
, emptyBindings(0)
, rootFS(makeFSInputAccessor(CanonPath::root))
, corepkgsFS(makeMemoryInputAccessor())
, internalFS(makeMemoryInputAccessor())
, derivationInternal{corepkgsFS->addFile(
CanonPath("derivation-internal.nix"),
#include "primops/derivation.nix.gen.hh"
)}
, callFlakeInternal{internalFS->addFile(
CanonPath("call-flake.nix"),
#include "flake/call-flake.nix.gen.hh"
)}
, derivationInternal(rootPath(CanonPath("/builtin/derivation.nix")))
, store(store)
, buildStore(buildStore ? buildStore : store)
, debugRepl(nullptr)
@@ -553,7 +539,7 @@ EvalState::EvalState(
auto r = resolveSearchPathPath(i.path);
if (!r) continue;
auto path = std::move(*r);
auto path = *std::move(r);
if (store->isInStore(path)) {
try {
@@ -569,11 +555,6 @@ EvalState::EvalState(
}
}
corepkgsFS->addFile(
CanonPath("fetchurl.nix"),
#include "fetchurl.nix.gen.hh"
);
createBaseEnv();
}
@@ -604,9 +585,6 @@ void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value &
SourcePath EvalState::checkSourcePath(const SourcePath & path_)
{
// Don't check non-rootFS accessors, they're in a different namespace.
if (path_.accessor != ref<InputAccessor>(rootFS)) return path_;
if (!allowedPaths) return path_;
auto i = resolvedPaths.find(path_.path.abs());
@@ -621,6 +599,8 @@ SourcePath EvalState::checkSourcePath(const SourcePath & path_)
*/
Path abspath = canonPath(path_.path.abs());
if (hasPrefix(abspath, corepkgsPrefix)) return CanonPath(abspath);
for (auto & i : *allowedPaths) {
if (isDirOrInDir(abspath, i)) {
found = true;
@@ -637,7 +617,7 @@ SourcePath EvalState::checkSourcePath(const SourcePath & path_)
/* Resolve symlinks. */
debug("checking access to '%s'", abspath);
SourcePath path = rootPath(CanonPath(canonPath(abspath, true)));
SourcePath path = CanonPath(canonPath(abspath, true));
for (auto & i : *allowedPaths) {
if (isDirOrInDir(path.path.abs(), i)) {
@@ -669,12 +649,12 @@ void EvalState::checkURI(const std::string & uri)
/* If the URI is a path, then check it against allowedPaths as
well. */
if (hasPrefix(uri, "/")) {
checkSourcePath(rootPath(CanonPath(uri)));
checkSourcePath(CanonPath(uri));
return;
}
if (hasPrefix(uri, "file://")) {
checkSourcePath(rootPath(CanonPath(std::string(uri, 7))));
checkSourcePath(CanonPath(std::string(uri, 7)));
return;
}
@@ -723,23 +703,6 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
}
void PrimOp::check()
{
if (arity > maxPrimOpArity) {
throw Error("primop arity must not exceed %1%", maxPrimOpArity);
}
}
void Value::mkPrimOp(PrimOp * p)
{
p->check();
clearValue();
internalType = tPrimOp;
primOp = p;
}
Value * EvalState::addPrimOp(PrimOp && primOp)
{
/* Hack to make constants lazy: turn them into a application of
@@ -987,7 +950,7 @@ void Value::mkStringMove(const char * s, const NixStringContext & context)
void Value::mkPath(const SourcePath & path)
{
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
mkPath(makeImmutableString(path.path.abs()));
}
@@ -1072,7 +1035,7 @@ std::string EvalState::mkOutputStringRaw(
/* In practice, this is testing for the case of CA derivations, or
dynamic derivations. */
return optStaticOutputPath
? store->printStorePath(std::move(*optStaticOutputPath))
? store->printStorePath(*std::move(optStaticOutputPath))
/* Downstream we would substitute this for an actual path once
we build the floating CA derivation */
: DownstreamPlaceholder::fromSingleDerivedPathBuilt(b, xpSettings).render();
@@ -1202,6 +1165,24 @@ void EvalState::evalFile(const SourcePath & path_, Value & v, bool mustBeTrivial
if (!e)
e = parseExprFromFile(checkSourcePath(resolvedPath));
cacheFile(path, resolvedPath, e, v, mustBeTrivial);
}
void EvalState::resetFileCache()
{
fileEvalCache.clear();
fileParseCache.clear();
}
void EvalState::cacheFile(
const SourcePath & path,
const SourcePath & resolvedPath,
Expr * e,
Value & v,
bool mustBeTrivial)
{
fileParseCache[resolvedPath] = e;
try {
@@ -1230,13 +1211,6 @@ void EvalState::evalFile(const SourcePath & path_, Value & v, bool mustBeTrivial
}
void EvalState::resetFileCache()
{
fileEvalCache.clear();
fileParseCache.clear();
}
void EvalState::eval(Expr * e, Value & v)
{
e->eval(*this, baseEnv, v);
@@ -1369,7 +1343,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
if (nameVal.type() == nNull)
continue;
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
auto nameSym = state.symbols.create(nameVal.string_view());
auto nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end())
state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow<EvalError>();
@@ -1766,12 +1740,6 @@ void ExprCall::eval(EvalState & state, Env & env, Value & v)
Value vFun;
fun->eval(state, env, vFun);
// Empirical arity of Nixpkgs lambdas by regex e.g. ([a-zA-Z]+:(\s|(/\*.*\/)|(#.*\n))*){5}
// 2: over 4000
// 3: about 300
// 4: about 60
// 5: under 10
// This excluded attrset lambdas (`{...}:`). Contributions of mixed lambdas appears insignificant at ~150 total.
Value * vArgs[args.size()];
for (size_t i = 0; i < args.size(); ++i)
vArgs[i] = args[i]->maybeThunk(state, env);
@@ -2069,7 +2037,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
else if (firstType == nPath) {
if (!context.empty())
state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow<EvalError>();
v.mkPath(state.rootPath(CanonPath(canonPath(str()))));
v.mkPath(CanonPath(canonPath(str())));
} else
v.mkStringMove(c_str(), context);
}
@@ -2187,7 +2155,7 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
forceValue(v, pos);
if (v.type() != nString)
error("value is %1% while a string was expected", showType(v)).debugThrow<TypeError>();
return v.string_view();
return v.string.s;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
@@ -2214,8 +2182,8 @@ std::string_view EvalState::forceString(Value & v, NixStringContext & context, c
std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx)
{
auto s = forceString(v, pos, errorCtx);
if (v.context()) {
error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string_view(), v.context()[0]).withTrace(pos, errorCtx).debugThrow<EvalError>();
if (v.string.context) {
error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow<EvalError>();
}
return s;
}
@@ -2228,7 +2196,7 @@ bool EvalState::isDerivation(Value & v)
if (i == v.attrs->end()) return false;
forceValue(*i->value, i->pos);
if (i->value->type() != nString) return false;
return i->value->string_view().compare("derivation") == 0;
return strcmp(i->value->string.s, "derivation") == 0;
}
@@ -2260,7 +2228,7 @@ BackedStringView EvalState::coerceToString(
if (v.type() == nString) {
copyContext(v, context);
return v.string_view();
return std::string_view(v.string.s);
}
if (v.type() == nPath) {
@@ -2268,7 +2236,7 @@ BackedStringView EvalState::coerceToString(
!canonicalizePath && !copyToStore
? // FIXME: hack to preserve path literals that end in a
// slash, as in /foo/${x}.
v._path.path
v._path
: copyToStore
? store->printStorePath(copyPathToStore(context, v.path()))
: std::string(v.path().path.abs());
@@ -2322,7 +2290,7 @@ BackedStringView EvalState::coerceToString(
&& (!v2->isList() || v2->listSize() != 0))
result += " ";
}
return result;
return std::move(result);
}
}
@@ -2342,7 +2310,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
auto dstPath = i != srcToStore.end()
? i->second
: [&]() {
auto dstPath = path.fetchToStore(store, path.baseName(), FileIngestionMethod::Recursive, nullptr, repair);
auto dstPath = path.fetchToStore(store, path.baseName(), nullptr, repair);
allowPath(dstPath);
srcToStore.insert_or_assign(path, dstPath);
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath));
@@ -2358,34 +2326,10 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext & context, std::string_view errorCtx)
{
try {
forceValue(v, pos);
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
}
/* Handle path values directly, without coercing to a string. */
if (v.type() == nPath)
return v.path();
/* Similarly, handle __toString where the result may be a path
value. */
if (v.type() == nAttrs) {
auto i = v.attrs->find(sToString);
if (i != v.attrs->end()) {
Value v1;
callFunction(*i->value, v, v1, pos);
return coerceToPath(pos, v1, context, errorCtx);
}
}
/* Any other value should be coercable to a string, interpreted
relative to the root filesystem. */
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
if (path == "" || path[0] != '/')
error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow<EvalError>();
return rootPath(CanonPath(path));
return CanonPath(path);
}
@@ -2482,13 +2426,10 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
return v1.boolean == v2.boolean;
case nString:
return v1.string_view().compare(v2.string_view()) == 0;
return strcmp(v1.string.s, v2.string.s) == 0;
case nPath:
return
// FIXME: compare accessors by their fingerprint.
v1._path.accessor == v2._path.accessor
&& strcmp(v1._path.path, v2._path.path) == 0;
return strcmp(v1._path, v2._path) == 0;
case nNull:
return true;
@@ -2536,37 +2477,10 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
}
}
bool EvalState::fullGC() {
#if HAVE_BOEHMGC
GC_gcollect();
// Check that it ran. We might replace this with a version that uses more
// of the boehm API to get this reliably, at a maintenance cost.
// We use a 1K margin because technically this has a race condtion, but we
// probably won't encounter it in practice, because the CLI isn't concurrent
// like that.
return GC_get_bytes_since_gc() < 1024;
#else
return false;
#endif
}
void EvalState::maybePrintStats()
void EvalState::printStats()
{
bool showStats = getEnv("NIX_SHOW_STATS").value_or("0") != "0";
if (showStats) {
// Make the final heap size more deterministic.
#if HAVE_BOEHMGC
if (!fullGC()) {
warn("failed to perform a full GC before reporting stats");
}
#endif
printStatistics();
}
}
void EvalState::printStatistics()
{
struct rusage buf;
getrusage(RUSAGE_SELF, &buf);
float cpuTime = buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000);
@@ -2580,105 +2494,105 @@ void EvalState::printStatistics()
GC_word heapSize, totalBytes;
GC_get_heap_usage_safe(&heapSize, 0, 0, 0, &totalBytes);
#endif
auto outPath = getEnv("NIX_SHOW_STATS_PATH").value_or("-");
std::fstream fs;
if (outPath != "-")
fs.open(outPath, std::fstream::out);
json topObj = json::object();
topObj["cpuTime"] = cpuTime;
topObj["envs"] = {
{"number", nrEnvs},
{"elements", nrValuesInEnvs},
{"bytes", bEnvs},
};
topObj["nrExprs"] = Expr::nrExprs;
topObj["list"] = {
{"elements", nrListElems},
{"bytes", bLists},
{"concats", nrListConcats},
};
topObj["values"] = {
{"number", nrValues},
{"bytes", bValues},
};
topObj["symbols"] = {
{"number", symbols.size()},
{"bytes", symbols.totalSize()},
};
topObj["sets"] = {
{"number", nrAttrsets},
{"bytes", bAttrsets},
{"elements", nrAttrsInAttrsets},
};
topObj["sizes"] = {
{"Env", sizeof(Env)},
{"Value", sizeof(Value)},
{"Bindings", sizeof(Bindings)},
{"Attr", sizeof(Attr)},
};
topObj["nrOpUpdates"] = nrOpUpdates;
topObj["nrOpUpdateValuesCopied"] = nrOpUpdateValuesCopied;
topObj["nrThunks"] = nrThunks;
topObj["nrAvoided"] = nrAvoided;
topObj["nrLookups"] = nrLookups;
topObj["nrPrimOpCalls"] = nrPrimOpCalls;
topObj["nrFunctionCalls"] = nrFunctionCalls;
if (showStats) {
auto outPath = getEnv("NIX_SHOW_STATS_PATH").value_or("-");
std::fstream fs;
if (outPath != "-")
fs.open(outPath, std::fstream::out);
json topObj = json::object();
topObj["cpuTime"] = cpuTime;
topObj["envs"] = {
{"number", nrEnvs},
{"elements", nrValuesInEnvs},
{"bytes", bEnvs},
};
topObj["list"] = {
{"elements", nrListElems},
{"bytes", bLists},
{"concats", nrListConcats},
};
topObj["values"] = {
{"number", nrValues},
{"bytes", bValues},
};
topObj["symbols"] = {
{"number", symbols.size()},
{"bytes", symbols.totalSize()},
};
topObj["sets"] = {
{"number", nrAttrsets},
{"bytes", bAttrsets},
{"elements", nrAttrsInAttrsets},
};
topObj["sizes"] = {
{"Env", sizeof(Env)},
{"Value", sizeof(Value)},
{"Bindings", sizeof(Bindings)},
{"Attr", sizeof(Attr)},
};
topObj["nrOpUpdates"] = nrOpUpdates;
topObj["nrOpUpdateValuesCopied"] = nrOpUpdateValuesCopied;
topObj["nrThunks"] = nrThunks;
topObj["nrAvoided"] = nrAvoided;
topObj["nrLookups"] = nrLookups;
topObj["nrPrimOpCalls"] = nrPrimOpCalls;
topObj["nrFunctionCalls"] = nrFunctionCalls;
#if HAVE_BOEHMGC
topObj["gc"] = {
{"heapSize", heapSize},
{"totalBytes", totalBytes},
};
topObj["gc"] = {
{"heapSize", heapSize},
{"totalBytes", totalBytes},
};
#endif
if (countCalls) {
topObj["primops"] = primOpCalls;
{
auto& list = topObj["functions"];
list = json::array();
for (auto & [fun, count] : functionCalls) {
json obj = json::object();
if (fun->name)
obj["name"] = (std::string_view) symbols[fun->name];
else
obj["name"] = nullptr;
if (auto pos = positions[fun->pos]) {
if (auto path = std::get_if<SourcePath>(&pos.origin))
obj["file"] = path->to_string();
obj["line"] = pos.line;
obj["column"] = pos.column;
if (countCalls) {
topObj["primops"] = primOpCalls;
{
auto& list = topObj["functions"];
list = json::array();
for (auto & [fun, count] : functionCalls) {
json obj = json::object();
if (fun->name)
obj["name"] = (std::string_view) symbols[fun->name];
else
obj["name"] = nullptr;
if (auto pos = positions[fun->pos]) {
if (auto path = std::get_if<SourcePath>(&pos.origin))
obj["file"] = path->to_string();
obj["line"] = pos.line;
obj["column"] = pos.column;
}
obj["count"] = count;
list.push_back(obj);
}
}
{
auto list = topObj["attributes"];
list = json::array();
for (auto & i : attrSelects) {
json obj = json::object();
if (auto pos = positions[i.first]) {
if (auto path = std::get_if<SourcePath>(&pos.origin))
obj["file"] = path->to_string();
obj["line"] = pos.line;
obj["column"] = pos.column;
}
obj["count"] = i.second;
list.push_back(obj);
}
obj["count"] = count;
list.push_back(obj);
}
}
{
auto list = topObj["attributes"];
list = json::array();
for (auto & i : attrSelects) {
json obj = json::object();
if (auto pos = positions[i.first]) {
if (auto path = std::get_if<SourcePath>(&pos.origin))
obj["file"] = path->to_string();
obj["line"] = pos.line;
obj["column"] = pos.column;
}
obj["count"] = i.second;
list.push_back(obj);
}
}
}
if (getEnv("NIX_SHOW_SYMBOLS").value_or("0") != "0") {
// XXX: overrides earlier assignment
topObj["symbols"] = json::array();
auto &list = topObj["symbols"];
symbols.dump([&](const std::string & s) { list.emplace_back(s); });
}
if (outPath == "-") {
std::cerr << topObj.dump(2) << std::endl;
} else {
fs << topObj.dump(2) << std::endl;
if (getEnv("NIX_SHOW_SYMBOLS").value_or("0") != "0") {
// XXX: overrides earlier assignment
topObj["symbols"] = json::array();
auto &list = topObj["symbols"];
symbols.dump([&](const std::string & s) { list.emplace_back(s); });
}
if (outPath == "-") {
std::cerr << topObj.dump(2) << std::endl;
} else {
fs << topObj.dump(2) << std::endl;
}
}
}

View File

@@ -18,20 +18,12 @@
namespace nix {
/**
* We put a limit on primop arity because it lets us use a fixed size array on
* the stack. 8 is already an impractical number of arguments. Use an attrset
* argument for such overly complicated functions.
*/
constexpr size_t maxPrimOpArity = 8;
class Store;
class EvalState;
class StorePath;
struct SingleDerivedPath;
enum RepairFlag : bool;
struct FSInputAccessor;
struct MemoryInputAccessor;
/**
@@ -77,12 +69,6 @@ struct PrimOp
* Optional experimental for this to be gated on.
*/
std::optional<ExperimentalFeature> experimentalFeature;
/**
* Validity check to be performed by functions that introduce primops,
* such as RegisterPrimOp() and Value::mkPrimOp().
*/
void check();
};
/**
@@ -225,26 +211,8 @@ public:
Bindings emptyBindings;
/**
* The accessor for the root filesystem.
*/
const ref<FSInputAccessor> rootFS;
/**
* The in-memory filesystem for <nix/...> paths.
*/
const ref<MemoryInputAccessor> corepkgsFS;
/**
* In-memory filesystem for internal, non-user-callable Nix
* expressions like call-flake.nix.
*/
const ref<MemoryInputAccessor> internalFS;
const SourcePath derivationInternal;
const SourcePath callFlakeInternal;
/**
* Store used to materialise .drv files.
*/
@@ -255,6 +223,7 @@ public:
*/
const ref<Store> buildStore;
RootValue vCallFlake = nullptr;
RootValue vImportedDrvToDerivation = nullptr;
/**
@@ -436,6 +405,16 @@ public:
*/
void evalFile(const SourcePath & path, Value & v, bool mustBeTrivial = false);
/**
* Like `evalFile`, but with an already parsed expression.
*/
void cacheFile(
const SourcePath & path,
const SourcePath & resolvedPath,
Expr * e,
Value & v,
bool mustBeTrivial = false);
void resetFileCache();
/**
@@ -445,7 +424,7 @@ public:
SourcePath findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos);
/**
* Try to resolve a search path value (not the optional key part)
* Try to resolve a search path value (not the optinal key part)
*
* If the specified search path element is a URI, download it.
*
@@ -730,25 +709,9 @@ public:
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
/**
* Print statistics, if enabled.
*
* Performs a full memory GC before printing the statistics, so that the
* GC statistics are more accurate.
* Print statistics.
*/
void maybePrintStats();
/**
* Print statistics, unconditionally, cheaply, without performing a GC first.
*/
void printStatistics();
/**
* Perform a full memory garbage collection - not incremental.
*
* @return true if Nix was built with GC and a GC was performed, false if not.
* The return value is currently not thread safe - just the return value.
*/
bool fullGC();
void printStats();
/**
* Realise the given context, and return a mapping from the placeholders
@@ -850,6 +813,8 @@ struct InvalidPathError : EvalError
#endif
};
static const std::string corepkgsPrefix{"/__corepkgs__/"};
template<class ErrorType>
void ErrorBuilder::debugThrow()
{

View File

@@ -1,7 +1,6 @@
#include "users.hh"
#include "flake.hh"
#include "globals.hh"
#include "fetch-settings.hh"
#include "flake.hh"
#include <nlohmann/json.hpp>

View File

@@ -1,4 +1,3 @@
#include "terminal.hh"
#include "flake.hh"
#include "eval.hh"
#include "eval-settings.hh"
@@ -9,7 +8,6 @@
#include "fetchers.hh"
#include "finally.hh"
#include "fetch-settings.hh"
#include "value-to-json.hh"
namespace nix {
@@ -17,7 +15,7 @@ using namespace flake;
namespace flake {
typedef std::pair<StorePath, FlakeRef> FetchedFlake;
typedef std::pair<fetchers::Tree, FlakeRef> FetchedFlake;
typedef std::vector<std::pair<FlakeRef, FetchedFlake>> FlakeCache;
static std::optional<FetchedFlake> lookupInFlakeCache(
@@ -36,7 +34,7 @@ static std::optional<FetchedFlake> lookupInFlakeCache(
return std::nullopt;
}
static std::tuple<StorePath, FlakeRef, FlakeRef> fetchOrSubstituteTree(
static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
EvalState & state,
const FlakeRef & originalRef,
bool allowLookup,
@@ -63,16 +61,16 @@ static std::tuple<StorePath, FlakeRef, FlakeRef> fetchOrSubstituteTree(
flakeCache.push_back({originalRef, *fetched});
}
auto [storePath, lockedRef] = *fetched;
auto [tree, lockedRef] = *fetched;
debug("got tree '%s' from '%s'",
state.store->printStorePath(storePath), lockedRef);
state.store->printStorePath(tree.storePath), lockedRef);
state.allowPath(storePath);
state.allowPath(tree.storePath);
assert(!originalRef.input.getNarHash() || storePath == originalRef.input.computeStorePath(*state.store));
assert(!originalRef.input.getNarHash() || tree.storePath == originalRef.input.computeStorePath(*state.store));
return {std::move(storePath), resolvedRef, lockedRef};
return {std::move(tree), resolvedRef, lockedRef};
}
static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
@@ -115,7 +113,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
try {
if (attr.name == sUrl) {
expectType(state, nString, *attr.value, attr.pos);
url = attr.value->string_view();
url = attr.value->string.s;
attrs.emplace("url", *url);
} else if (attr.name == sFlake) {
expectType(state, nBool, *attr.value, attr.pos);
@@ -124,7 +122,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath);
} else if (attr.name == sFollows) {
expectType(state, nString, *attr.value, attr.pos);
auto follows(parseInputPath(attr.value->c_str()));
auto follows(parseInputPath(attr.value->string.s));
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
input.follows = follows;
} else {
@@ -133,7 +131,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch (attr.value->type()) {
case nString:
attrs.emplace(state.symbols[attr.name], attr.value->c_str());
attrs.emplace(state.symbols[attr.name], attr.value->string.s);
break;
case nBool:
attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean });
@@ -142,13 +140,8 @@ static FlakeInput parseFlakeInput(EvalState & state,
attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
break;
default:
if (attr.name == state.symbols.create("publicKeys")) {
experimentalFeatureSettings.require(Xp::VerifiedFetches);
NixStringContext emptyContext = {};
attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, emptyContext).dump());
} else
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
state.symbols[attr.name], showType(*attr.value));
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
state.symbols[attr.name], showType(*attr.value));
}
#pragma GCC diagnostic pop
}
@@ -209,34 +202,34 @@ static Flake getFlake(
FlakeCache & flakeCache,
InputPath lockRootPath)
{
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree(
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
state, originalRef, allowLookup, flakeCache);
// Guard against symlink attacks.
auto flakeDir = canonPath(state.store->toRealPath(storePath) + "/" + lockedRef.subdir, true);
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
if (!isInDir(flakeFile, state.store->toRealPath(storePath)))
if (!isInDir(flakeFile, sourceInfo.actualPath))
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
lockedRef, state.store->printStorePath(storePath));
lockedRef, state.store->printStorePath(sourceInfo.storePath));
Flake flake {
.originalRef = originalRef,
.resolvedRef = resolvedRef,
.lockedRef = lockedRef,
.storePath = storePath,
.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
};
if (!pathExists(flakeFile))
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir);
Value vInfo;
state.evalFile(state.rootPath(CanonPath(flakeFile)), vInfo, true); // FIXME: symlink attack
state.evalFile(CanonPath(flakeFile), vInfo, true); // FIXME: symlink attack
expectType(state, nAttrs, vInfo, state.positions.add({state.rootPath(CanonPath(flakeFile))}, 1, 1));
expectType(state, nAttrs, vInfo, state.positions.add({CanonPath(flakeFile)}, 1, 1));
if (auto description = vInfo.attrs->get(state.sDescription)) {
expectType(state, nString, *description->value, description->pos);
flake.description = description->value->c_str();
flake.description = description->value->string.s;
}
auto sInputs = state.symbols.create("inputs");
@@ -353,7 +346,7 @@ LockedFlake lockFlake(
// FIXME: symlink attack
auto oldLockFile = LockFile::read(
lockFlags.referenceLockFilePath.value_or(
state.store->toRealPath(flake.storePath) + "/" + flake.lockedRef.subdir + "/flake.lock"));
flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock"));
debug("old lock file: %s", oldLockFile);
@@ -454,8 +447,8 @@ LockedFlake lockFlake(
assert(input.ref);
/* Do we have an entry in the existing lock file?
And the input is not in updateInputs? */
/* Do we have an entry in the existing lock file? And we
don't have a --update-input flag for this input? */
std::shared_ptr<LockedNode> oldLock;
updatesUsed.insert(inputPath);
@@ -479,8 +472,9 @@ LockedFlake lockFlake(
node->inputs.insert_or_assign(id, childNode);
/* If we have this input in updateInputs, then we
must fetch the flake to update it. */
/* If we have an --update-input flag for an input
of this input, then we must fetch the flake to
update it. */
auto lb = lockFlags.inputUpdates.lower_bound(inputPath);
auto mustRefetch =
@@ -580,7 +574,7 @@ LockedFlake lockFlake(
oldLock
? std::dynamic_pointer_cast<const Node>(oldLock)
: LockFile::read(
state.store->toRealPath(inputFlake.storePath) + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root.get_ptr(),
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root.get_ptr(),
oldLock ? lockRootPath : inputPath,
localPath,
false);
@@ -604,7 +598,7 @@ LockedFlake lockFlake(
};
// Bring in the current ref for relative path resolution if we have it
auto parentPath = canonPath(state.store->toRealPath(flake.storePath) + "/" + flake.lockedRef.subdir, true);
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
computeLocks(
flake.inputs,
@@ -622,14 +616,19 @@ LockedFlake lockFlake(
for (auto & i : lockFlags.inputUpdates)
if (!updatesUsed.count(i))
warn("'%s' does not match any input of this flake", printInputPath(i));
warn("the flag '--update-input %s' does not match any input", printInputPath(i));
/* Check 'follows' inputs. */
newLockFile.check();
debug("new lock file: %s", newLockFile);
auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
auto sourcePath = topRef.input.getSourcePath();
auto outputLockFilePath = sourcePath ? std::optional{*sourcePath + "/" + relPath} : std::nullopt;
if (lockFlags.outputLockFilePath) {
outputLockFilePath = lockFlags.outputLockFilePath;
}
/* Check whether we need to / can write the new lock file. */
if (newLockFile != oldLockFile || lockFlags.outputLockFilePath) {
@@ -637,7 +636,7 @@ LockedFlake lockFlake(
auto diff = LockFile::diff(oldLockFile, newLockFile);
if (lockFlags.writeLockFile) {
if (sourcePath || lockFlags.outputLockFilePath) {
if (outputLockFilePath) {
if (auto unlockedInput = newLockFile.isUnlocked()) {
if (fetchSettings.warnDirty)
warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput);
@@ -645,48 +644,41 @@ LockedFlake lockFlake(
if (!lockFlags.updateLockFile)
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);
auto newLockFileS = fmt("%s\n", newLockFile);
if (lockFlags.outputLockFilePath) {
if (lockFlags.commitLockFile)
throw Error("'--commit-lock-file' and '--output-lock-file' are incompatible");
writeFile(*lockFlags.outputLockFilePath, newLockFileS);
} else {
auto relPath = (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock";
auto outputLockFilePath = *sourcePath + "/" + relPath;
bool lockFileExists = pathExists(outputLockFilePath);
bool lockFileExists = pathExists(*outputLockFilePath);
if (lockFileExists) {
auto s = chomp(diff);
if (lockFileExists) {
if (s.empty())
warn("updating lock file '%s'", outputLockFilePath);
else
warn("updating lock file '%s':\n%s", outputLockFilePath, s);
} else
warn("creating lock file '%s': \n%s", outputLockFilePath, s);
if (s.empty())
warn("updating lock file '%s'", *outputLockFilePath);
else
warn("updating lock file '%s':\n%s", *outputLockFilePath, s);
} else
warn("creating lock file '%s'", *outputLockFilePath);
std::optional<std::string> commitMessage = std::nullopt;
newLockFile.write(*outputLockFilePath);
if (lockFlags.commitLockFile) {
std::string cm;
std::optional<std::string> commitMessage = std::nullopt;
if (lockFlags.commitLockFile) {
if (lockFlags.outputLockFilePath) {
throw Error("--commit-lock-file and --output-lock-file are currently incompatible");
}
std::string cm;
cm = fetchSettings.commitLockFileSummary.get();
cm = fetchSettings.commitLockFileSummary.get();
if (cm == "") {
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
}
cm += "\n\nFlake lock file updates:\n\n";
cm += filterANSIEscapes(diff, true);
commitMessage = cm;
if (cm == "") {
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
}
topRef.input.putFile(
CanonPath((topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock"),
newLockFileS, commitMessage);
cm += "\n\nFlake lock file updates:\n\n";
cm += filterANSIEscapes(diff, true);
commitMessage = cm;
}
topRef.input.markChangedFile(
(topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock",
commitMessage);
/* Rewriting the lockfile changed the top-level
repo, so we should re-read it. FIXME: we could
also just clear the 'rev' field... */
@@ -737,7 +729,7 @@ void callFlake(EvalState & state,
emitTreeAttrs(
state,
lockedFlake.flake.storePath,
*lockedFlake.flake.sourceInfo,
lockedFlake.flake.lockedRef.input,
*vRootSrc,
false,
@@ -745,10 +737,14 @@ void callFlake(EvalState & state,
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
auto vCallFlake = state.allocValue();
state.evalFile(state.callFlakeInternal, *vCallFlake);
if (!state.vCallFlake) {
state.vCallFlake = allocRootValue(state.allocValue());
state.eval(state.parseExprFromString(
#include "call-flake.nix.gen.hh"
, CanonPath::root), **state.vCallFlake);
}
state.callFunction(*vCallFlake, *vLocks, *vTmp1, noPos);
state.callFunction(**state.vCallFlake, *vLocks, *vTmp1, noPos);
state.callFunction(*vTmp1, *vRootSrc, *vTmp2, noPos);
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
}
@@ -854,7 +850,7 @@ static void prim_flakeRefToString(
Explicit<bool> { attr.value->boolean });
} else if (t == nString) {
attrs.emplace(state.symbols[attr.name],
std::string(attr.value->string_view()));
std::string(attr.value->str()));
} else {
state.error(
"flake reference attribute sets may only contain integers, Booleans, "
@@ -897,7 +893,7 @@ Fingerprint LockedFlake::getFingerprint() const
// flake.sourceInfo.storePath for the fingerprint.
return hashString(htSHA256,
fmt("%s;%s;%d;%d;%s",
flake.storePath.to_string(),
flake.sourceInfo->storePath.to_string(),
flake.lockedRef.subdir,
flake.lockedRef.input.getRevCount().value_or(0),
flake.lockedRef.input.getLastModified().value_or(0),

View File

@@ -10,6 +10,8 @@ namespace nix {
class EvalState;
namespace fetchers { struct Tree; }
namespace flake {
struct FlakeInput;
@@ -82,7 +84,7 @@ struct Flake
*/
bool forceDirty = false;
std::optional<std::string> description;
StorePath storePath;
std::shared_ptr<const fetchers::Tree> sourceInfo;
FlakeInputs inputs;
/**
* 'nixConfig' attribute
@@ -191,7 +193,7 @@ void callFlake(
void emitTreeAttrs(
EvalState & state,
const StorePath & storePath,
const fetchers::Tree & tree,
const fetchers::Input & input,
Value & v,
bool emptyRevFallback = false,

View File

@@ -69,130 +69,32 @@ std::optional<FlakeRef> maybeParseFlakeRef(
}
}
std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const std::string & url,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake)
{
std::string path = url;
std::string fragment = "";
std::map<std::string, std::string> query;
auto pathEnd = url.find_first_of("#?");
auto fragmentStart = pathEnd;
if (pathEnd != std::string::npos && url[pathEnd] == '?') {
fragmentStart = url.find("#");
}
if (pathEnd != std::string::npos) {
path = url.substr(0, pathEnd);
}
if (fragmentStart != std::string::npos) {
fragment = percentDecode(url.substr(fragmentStart+1));
}
if (pathEnd != std::string::npos && fragmentStart != std::string::npos) {
query = decodeQuery(url.substr(pathEnd+1, fragmentStart));
}
using namespace fetchers;
if (baseDir) {
/* Check if 'url' is a path (either absolute or relative
to 'baseDir'). If so, search upward to the root of the
repo (i.e. the directory containing .git). */
static std::string fnRegex = "[0-9a-zA-Z-._~!$&'\"()*+,;=]+";
path = absPath(path, baseDir);
if (isFlake) {
if (!allowMissing && !pathExists(path + "/flake.nix")){
notice("path '%s' does not contain a 'flake.nix', searching up",path);
// Save device to detect filesystem boundary
dev_t device = lstat(path).st_dev;
bool found = false;
while (path != "/") {
if (pathExists(path + "/flake.nix")) {
found = true;
break;
} else if (pathExists(path + "/.git"))
throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path);
else {
if (lstat(path).st_dev != device)
throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
}
path = dirOf(path);
}
if (!found)
throw BadURL("could not find a flake.nix file");
}
if (!S_ISDIR(lstat(path).st_mode))
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
if (!allowMissing && !pathExists(path + "/flake.nix"))
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
auto flakeRoot = path;
std::string subdir;
while (flakeRoot != "/") {
if (pathExists(flakeRoot + "/.git")) {
auto base = std::string("git+file://") + flakeRoot;
auto parsedURL = ParsedURL{
.url = base, // FIXME
.base = base,
.scheme = "git+file",
.authority = "",
.path = flakeRoot,
.query = query,
};
if (subdir != "") {
if (parsedURL.query.count("dir"))
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
parsedURL.query.insert_or_assign("dir", subdir);
}
if (pathExists(flakeRoot + "/.git/shallow"))
parsedURL.query.insert_or_assign("shallow", "1");
return std::make_pair(
FlakeRef(fetchers::Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")),
fragment);
}
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
flakeRoot = dirOf(flakeRoot);
}
}
} else {
if (!hasPrefix(path, "/"))
throw BadURL("flake reference '%s' is not an absolute path", url);
path = canonPath(path + "/" + getOr(query, "dir", ""));
}
fetchers::Attrs attrs;
attrs.insert_or_assign("type", "path");
attrs.insert_or_assign("path", path);
return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(std::move(attrs)), ""), fragment);
};
/* Check if 'url' is a flake ID. This is an abbreviated syntax for
'flake:<flake-id>?ref=<ref>&rev=<rev>'. */
std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
const std::string & url,
bool isFlake
)
{
std::smatch match;
static std::regex pathUrlRegex(
"(/?" + fnRegex + "(?:/" + fnRegex + ")*/?)"
+ "(?:\\?(" + queryRegex + "))?"
+ "(?:#(" + queryRegex + "))?",
std::regex::ECMAScript);
static std::regex flakeRegex(
"((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)"
+ "(?:#(" + queryRegex + "))?",
std::regex::ECMAScript);
std::smatch match;
/* Check if 'url' is a flake ID. This is an abbreviated syntax for
'flake:<flake-id>?ref=<ref>&rev=<rev>'. */
if (std::regex_match(url, match, flakeRegex)) {
auto parsedURL = ParsedURL{
.url = url,
@@ -203,53 +105,111 @@ std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
};
return std::make_pair(
FlakeRef(fetchers::Input::fromURL(parsedURL, isFlake), ""),
FlakeRef(Input::fromURL(parsedURL, isFlake), ""),
percentDecode(match.str(6)));
}
return {};
}
else if (std::regex_match(url, match, pathUrlRegex)) {
std::string path = match[1];
std::string fragment = percentDecode(match.str(3));
std::optional<std::pair<FlakeRef, std::string>> parseURLFlakeRef(
const std::string & url,
const std::optional<Path> & baseDir,
bool isFlake
)
{
ParsedURL parsedURL;
try {
parsedURL = parseURL(url);
} catch (BadURL &) {
return std::nullopt;
if (baseDir) {
/* Check if 'url' is a path (either absolute or relative
to 'baseDir'). If so, search upward to the root of the
repo (i.e. the directory containing .git). */
path = absPath(path, baseDir);
if (isFlake) {
if (!allowMissing && !pathExists(path + "/flake.nix")){
notice("path '%s' does not contain a 'flake.nix', searching up",path);
// Save device to detect filesystem boundary
dev_t device = lstat(path).st_dev;
bool found = false;
while (path != "/") {
if (pathExists(path + "/flake.nix")) {
found = true;
break;
} else if (pathExists(path + "/.git"))
throw Error("path '%s' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)", path);
else {
if (lstat(path).st_dev != device)
throw Error("unable to find a flake before encountering filesystem boundary at '%s'", path);
}
path = dirOf(path);
}
if (!found)
throw BadURL("could not find a flake.nix file");
}
if (!S_ISDIR(lstat(path).st_mode))
throw BadURL("path '%s' is not a flake (because it's not a directory)", path);
if (!allowMissing && !pathExists(path + "/flake.nix"))
throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path);
auto flakeRoot = path;
std::string subdir;
while (flakeRoot != "/") {
if (pathExists(flakeRoot + "/.git")) {
auto base = std::string("git+file://") + flakeRoot;
auto parsedURL = ParsedURL{
.url = base, // FIXME
.base = base,
.scheme = "git+file",
.authority = "",
.path = flakeRoot,
.query = decodeQuery(match[2]),
};
if (subdir != "") {
if (parsedURL.query.count("dir"))
throw Error("flake URL '%s' has an inconsistent 'dir' parameter", url);
parsedURL.query.insert_or_assign("dir", subdir);
}
if (pathExists(flakeRoot + "/.git/shallow"))
parsedURL.query.insert_or_assign("shallow", "1");
return std::make_pair(
FlakeRef(Input::fromURL(parsedURL, isFlake), getOr(parsedURL.query, "dir", "")),
fragment);
}
subdir = std::string(baseNameOf(flakeRoot)) + (subdir.empty() ? "" : "/" + subdir);
flakeRoot = dirOf(flakeRoot);
}
}
} else {
if (!hasPrefix(path, "/"))
throw BadURL("flake reference '%s' is not an absolute path", url);
auto query = decodeQuery(match[2]);
path = canonPath(path + "/" + getOr(query, "dir", ""));
}
fetchers::Attrs attrs;
attrs.insert_or_assign("type", "path");
attrs.insert_or_assign("path", path);
return std::make_pair(FlakeRef(Input::fromAttrs(std::move(attrs)), ""), fragment);
}
std::string fragment;
std::swap(fragment, parsedURL.fragment);
else {
auto parsedURL = parseURL(url);
std::string fragment;
std::swap(fragment, parsedURL.fragment);
auto input = fetchers::Input::fromURL(parsedURL, isFlake);
input.parent = baseDir;
auto input = Input::fromURL(parsedURL, isFlake);
input.parent = baseDir;
return std::make_pair(
FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")),
fragment);
}
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
const std::string & url,
const std::optional<Path> & baseDir,
bool allowMissing,
bool isFlake)
{
using namespace fetchers;
std::smatch match;
if (auto res = parseFlakeIdRef(url, isFlake)) {
return *res;
} else if (auto res = parseURLFlakeRef(url, baseDir, isFlake)) {
return *res;
} else {
return parsePathFlakeRefWithFragment(url, baseDir, allowMissing, isFlake);
return std::make_pair(
FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")),
fragment);
}
}
@@ -272,10 +232,10 @@ FlakeRef FlakeRef::fromAttrs(const fetchers::Attrs & attrs)
fetchers::maybeGetStrAttr(attrs, "dir").value_or(""));
}
std::pair<StorePath, FlakeRef> FlakeRef::fetchTree(ref<Store> store) const
std::pair<fetchers::Tree, FlakeRef> FlakeRef::fetchTree(ref<Store> store) const
{
auto [storePath, lockedInput] = input.fetch(store);
return {std::move(storePath), FlakeRef(std::move(lockedInput), subdir)};
auto [tree, lockedInput] = input.fetch(store);
return {std::move(tree), FlakeRef(std::move(lockedInput), subdir)};
}
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(
@@ -289,6 +249,4 @@ std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragment
return {std::move(flakeRef), fragment, std::move(extendedOutputsSpec)};
}
std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript);
}

View File

@@ -6,7 +6,6 @@
#include "fetchers.hh"
#include "outputs-spec.hh"
#include <regex>
#include <variant>
namespace nix {
@@ -63,7 +62,7 @@ struct FlakeRef
static FlakeRef fromAttrs(const fetchers::Attrs & attrs);
std::pair<StorePath, FlakeRef> fetchTree(ref<Store> store) const;
std::pair<fetchers::Tree, FlakeRef> fetchTree(ref<Store> store) const;
};
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
@@ -92,7 +91,5 @@ std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragment
bool allowMissing = false,
bool isFlake = true);
const static std::string flakeIdRegexS = "[a-zA-Z][a-zA-Z0-9_-]*";
extern std::regex flakeIdRegex;
}

View File

@@ -2,10 +2,8 @@
#include "store-api.hh"
#include "url-parts.hh"
#include <algorithm>
#include <iomanip>
#include <iterator>
#include <nlohmann/json.hpp>
namespace nix::flake {
@@ -47,26 +45,16 @@ StorePath LockedNode::computeStorePath(Store & store) const
return lockedRef.input.computeStorePath(store);
}
static std::shared_ptr<Node> doFind(const ref<Node>& root, const InputPath & path, std::vector<InputPath>& visited) {
std::shared_ptr<Node> LockFile::findInput(const InputPath & path)
{
auto pos = root;
auto found = std::find(visited.cbegin(), visited.cend(), path);
if(found != visited.end()) {
std::vector<std::string> cycle;
std::transform(found, visited.cend(), std::back_inserter(cycle), printInputPath);
cycle.push_back(printInputPath(path));
throw Error("follow cycle detected: [%s]", concatStringsSep(" -> ", cycle));
}
visited.push_back(path);
for (auto & elem : path) {
if (auto i = get(pos->inputs, elem)) {
if (auto node = std::get_if<0>(&*i))
pos = *node;
else if (auto follows = std::get_if<1>(&*i)) {
if (auto p = doFind(root, *follows, visited))
if (auto p = findInput(*follows))
pos = ref(p);
else
return {};
@@ -78,12 +66,6 @@ static std::shared_ptr<Node> doFind(const ref<Node>& root, const InputPath & pat
return pos;
}
std::shared_ptr<Node> LockFile::findInput(const InputPath & path)
{
std::vector<InputPath> visited;
return doFind(root, path, visited);
}
LockFile::LockFile(const nlohmann::json & json, const Path & path)
{
auto version = json.value("version", 0);
@@ -107,7 +89,7 @@ LockFile::LockFile(const nlohmann::json & json, const Path & path)
std::string inputKey = i.value();
auto k = nodeMap.find(inputKey);
if (k == nodeMap.end()) {
auto nodes = json["nodes"];
auto & nodes = json["nodes"];
auto jsonNode2 = nodes.find(inputKey);
if (jsonNode2 == nodes.end())
throw Error("lock file references missing node '%s'", inputKey);
@@ -214,6 +196,12 @@ std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile)
return stream;
}
void LockFile::write(const Path & path) const
{
createDirs(dirOf(path));
writeFile(path, fmt("%s\n", *this));
}
std::optional<FlakeRef> LockFile::isUnlocked() const
{
std::set<ref<const Node>> nodes;

Some files were not shown because too many files have changed in this diff Show More