mirror of
https://github.com/NixOS/nix.git
synced 2024-11-25 08:12:29 +00:00
Merge branch '2.19-maintenance' into ifd-buildStore-2.19
This commit is contained in:
commit
2e4239f9e3
@ -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}]
|
||||
[*.{hpp,cc,hh,sh,pl,xs}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
|
2
.github/labeler.yml
vendored
2
.github/labeler.yml
vendored
@ -20,4 +20,4 @@
|
||||
# Unit tests
|
||||
- src/*/tests/**/*
|
||||
# Functional and integration tests
|
||||
- tests/**/*
|
||||
- tests/functional/**/*
|
||||
|
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Create backport PRs
|
||||
# should be kept in sync with `version`
|
||||
uses: zeebe-io/backport-action@v1.4.0
|
||||
uses: zeebe-io/backport-action@v2.1.1
|
||||
with:
|
||||
# Config README: https://github.com/zeebe-io/backport-action#backport-action
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@ -101,6 +101,9 @@ jobs:
|
||||
|
||||
docker_push_image:
|
||||
needs: [check_secrets, tests]
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
if: >-
|
||||
github.event_name == 'push' &&
|
||||
github.ref_name == 'master' &&
|
||||
@ -126,6 +129,9 @@ 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:
|
||||
@ -133,3 +139,20 @@ 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
|
||||
|
42
.gitignore
vendored
42
.gitignore
vendored
@ -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
|
||||
|
||||
@ -79,24 +79,24 @@ perl/Makefile.config
|
||||
|
||||
/src/build-remote/build-remote
|
||||
|
||||
# /tests/
|
||||
/tests/test-tmp
|
||||
/tests/common/vars-and-functions.sh
|
||||
/tests/result*
|
||||
/tests/restricted-innocent
|
||||
/tests/shell
|
||||
/tests/shell.drv
|
||||
/tests/config.nix
|
||||
/tests/ca/config.nix
|
||||
/tests/dyn-drv/config.nix
|
||||
/tests/repl-result-out
|
||||
/tests/test-libstoreconsumer/test-libstoreconsumer
|
||||
# /tests/functional/
|
||||
/tests/functional/test-tmp
|
||||
/tests/functional/common/vars-and-functions.sh
|
||||
/tests/functional/result*
|
||||
/tests/functional/restricted-innocent
|
||||
/tests/functional/shell
|
||||
/tests/functional/shell.drv
|
||||
/tests/functional/config.nix
|
||||
/tests/functional/ca/config.nix
|
||||
/tests/functional/dyn-drv/config.nix
|
||||
/tests/functional/repl-result-out
|
||||
/tests/functional/test-libstoreconsumer/test-libstoreconsumer
|
||||
|
||||
# /tests/lang/
|
||||
/tests/lang/*.out
|
||||
/tests/lang/*.out.xml
|
||||
/tests/lang/*.err
|
||||
/tests/lang/*.ast
|
||||
# /tests/functional/lang/
|
||||
/tests/functional/lang/*.out
|
||||
/tests/functional/lang/*.out.xml
|
||||
/tests/functional/lang/*.err
|
||||
/tests/functional/lang/*.ast
|
||||
|
||||
/perl/lib/Nix/Config.pm
|
||||
/perl/lib/Nix/Store.cc
|
||||
@ -138,7 +138,9 @@ nix-rust/target
|
||||
|
||||
result
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# clangd and possibly more
|
||||
.cache/
|
||||
|
@ -24,25 +24,33 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy).
|
||||
|
||||
## Making changes to Nix
|
||||
|
||||
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.
|
||||
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.
|
||||
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 changes!
|
||||
4. Make your change!
|
||||
|
||||
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.
|
||||
* Link related issues in your pull request to inform interested parties and future contributors about your change.
|
||||
* 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.
|
||||
* 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.
|
||||
@ -52,7 +60,7 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy).
|
||||
|
||||
- [ ] Fixes an [idea approved](https://github.com/NixOS/nix/labels/idea%20approved) issue
|
||||
- [ ] Tests, as appropriate:
|
||||
- Functional tests – [`tests/**.sh`](./tests)
|
||||
- Functional tests – [`tests/functional/**.sh`](./tests/functional)
|
||||
- Unit tests – [`src/*/tests`](./src/)
|
||||
- Integration tests – [`tests/nixos/*`](./tests/nixos)
|
||||
- [ ] User documentation in the [manual](..doc/manual/src)
|
||||
|
32
Makefile
32
Makefile
@ -1,3 +1,7 @@
|
||||
-include Makefile.config
|
||||
clean-files += Makefile.config
|
||||
|
||||
ifeq ($(ENABLE_BUILD), yes)
|
||||
makefiles = \
|
||||
mk/precompiled-headers.mk \
|
||||
local.mk \
|
||||
@ -18,19 +22,25 @@ makefiles = \
|
||||
misc/upstart/local.mk \
|
||||
doc/manual/local.mk \
|
||||
doc/internal-api/local.mk
|
||||
endif
|
||||
|
||||
-include Makefile.config
|
||||
|
||||
ifeq ($(tests), yes)
|
||||
ifeq ($(ENABLE_BUILD)_$(ENABLE_TESTS), yes_yes)
|
||||
makefiles += \
|
||||
src/libutil/tests/local.mk \
|
||||
src/libstore/tests/local.mk \
|
||||
src/libexpr/tests/local.mk \
|
||||
tests/local.mk \
|
||||
tests/ca/local.mk \
|
||||
tests/dyn-drv/local.mk \
|
||||
tests/test-libstoreconsumer/local.mk \
|
||||
tests/plugins/local.mk
|
||||
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
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_TESTS), yes)
|
||||
makefiles += \
|
||||
tests/functional/local.mk \
|
||||
tests/functional/ca/local.mk \
|
||||
tests/functional/dyn-drv/local.mk \
|
||||
tests/functional/test-libstoreconsumer/local.mk \
|
||||
tests/functional/plugins/local.mk
|
||||
else
|
||||
makefiles += \
|
||||
mk/disable-tests.mk
|
||||
|
@ -28,6 +28,8 @@ SODIUM_LIBS = @SODIUM_LIBS@
|
||||
SQLITE3_LIBS = @SQLITE3_LIBS@
|
||||
bash = @bash@
|
||||
bindir = @bindir@
|
||||
checkbindir = @checkbindir@
|
||||
checklibdir = @checklibdir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
doc_generate = @doc_generate@
|
||||
@ -46,5 +48,7 @@ sandbox_shell = @sandbox_shell@
|
||||
storedir = @storedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
system = @system@
|
||||
tests = @tests@
|
||||
ENABLE_BUILD = @ENABLE_BUILD@
|
||||
ENABLE_TESTS = @ENABLE_TESTS@
|
||||
INSTALL_UNIT_TESTS = @INSTALL_UNIT_TESTS@
|
||||
internal_api_docs = @internal_api_docs@
|
||||
|
18
README.md
18
README.md
@ -7,21 +7,20 @@ 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
|
||||
## Installation and 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):
|
||||
Visit [nix.dev](https://nix.dev) for [installation instructions](https://nix.dev/tutorials/install-nix) and [beginner tutorials](https://nix.dev/tutorials/first-steps).
|
||||
|
||||
```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).
|
||||
Full reference documentation can be found in the [Nix manual](https://nixos.org/nix/manual).
|
||||
|
||||
## Building And Developing
|
||||
|
||||
See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacking.html) in our manual for instruction on how to
|
||||
to set up a development environment and build Nix from source.
|
||||
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.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
@ -29,7 +28,6 @@ to set up a development environment and build Nix from source.
|
||||
- [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
|
||||
|
||||
|
@ -59,12 +59,18 @@ index b5d71e62..aed7b0bf 100644
|
||||
GC_bool found_me = FALSE;
|
||||
size_t nthreads = 0;
|
||||
int i;
|
||||
@@ -851,6 +853,31 @@ GC_INNER void GC_push_all_stacks(void)
|
||||
@@ -851,6 +853,37 @@ GC_INNER void GC_push_all_stacks(void)
|
||||
hi = p->altstack + p->altstack_size;
|
||||
/* FIXME: Need to scan the normal stack too, but how ? */
|
||||
/* FIXME: Assume stack grows down */
|
||||
+ } else {
|
||||
+ if (pthread_getattr_np(p->id, &pattr)) {
|
||||
+#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
|
||||
+ {
|
||||
+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
|
||||
+ }
|
||||
+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) {
|
||||
|
@ -1,4 +0,0 @@
|
||||
#! /bin/sh -e
|
||||
rm -f aclocal.m4
|
||||
mkdir -p config
|
||||
exec autoreconf -vfi
|
26
configure.ac
26
configure.ac
@ -68,6 +68,9 @@ case "$host_os" in
|
||||
esac
|
||||
|
||||
|
||||
ENSURE_NO_GCC_BUG_80431
|
||||
|
||||
|
||||
# Check for pubsetbuf.
|
||||
AC_MSG_CHECKING([for pubsetbuf])
|
||||
AC_LANG_PUSH(C++)
|
||||
@ -152,12 +155,29 @@ 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]),
|
||||
tests=$enableval, tests=yes)
|
||||
AC_SUBST(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)
|
||||
|
||||
# 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]),
|
||||
@ -289,7 +309,7 @@ if test "$gc" = yes; then
|
||||
fi
|
||||
|
||||
|
||||
if test "$tests" = yes; then
|
||||
if test "$ENABLE_TESTS" = yes; then
|
||||
|
||||
# Look for gtest.
|
||||
PKG_CHECK_MODULES([GTEST], [gtest_main])
|
||||
|
@ -39,21 +39,42 @@ 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
|
||||
@ -61,3 +82,16 @@ INPUT = \
|
||||
# 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
|
||||
|
30
doc/manual/_redirects
Normal file
30
doc/manual/_redirects
Normal 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!
|
||||
|
@ -1,11 +1,12 @@
|
||||
let
|
||||
inherit (builtins)
|
||||
attrNames attrValues fromJSON listToAttrs mapAttrs
|
||||
attrNames attrValues fromJSON listToAttrs mapAttrs groupBy
|
||||
concatStringsSep concatMap length lessThan replaceStrings sort;
|
||||
inherit (import ./utils.nix) concatStrings optionalString filterAttrs trim squash unique showSettings;
|
||||
inherit (import <nix/utils.nix>) attrsToList concatStrings optionalString filterAttrs trim squash unique;
|
||||
showStoreDocs = import ./generate-store-info.nix;
|
||||
in
|
||||
|
||||
commandDump:
|
||||
inlineHTML: commandDump:
|
||||
|
||||
let
|
||||
|
||||
@ -30,7 +31,7 @@ let
|
||||
|
||||
${maybeSubcommands}
|
||||
|
||||
${maybeDocumentation}
|
||||
${maybeStoreDocs}
|
||||
|
||||
${maybeOptions}
|
||||
'';
|
||||
@ -40,15 +41,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
|
||||
@ -70,40 +71,57 @@ let
|
||||
* [`${command} ${name}`](./${appendName filename name}.md) - ${subcmd.description}
|
||||
'';
|
||||
|
||||
maybeDocumentation = optionalString
|
||||
(details ? doc)
|
||||
(replaceStrings ["@stores@"] [storeDocs] details.doc);
|
||||
# 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);
|
||||
|
||||
maybeOptions = optionalString (details.flags != {}) ''
|
||||
maybeOptions = let
|
||||
allVisibleOptions = filterAttrs
|
||||
(_: o: ! o.hiddenCategory)
|
||||
(details.flags // toplevel.flags);
|
||||
in optionalString (allVisibleOptions != {}) ''
|
||||
# Options
|
||||
|
||||
${showOptions details.flags toplevel.flags}
|
||||
${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 = options: commonOptions:
|
||||
showOptions = inlineHTML: allOptions:
|
||||
let
|
||||
allOptions = options // commonOptions;
|
||||
showCategory = cat: ''
|
||||
${optionalString (cat != "") "**${cat}:**"}
|
||||
showCategory = cat: opts: ''
|
||||
${optionalString (cat != "") "## ${cat}"}
|
||||
|
||||
${listOptions (filterAttrs (n: v: v.category == cat) allOptions)}
|
||||
${concatStringsSep "\n" (attrValues (mapAttrs showOption opts))}
|
||||
'';
|
||||
listOptions = opts: concatStringsSep "\n" (attrValues (mapAttrs showOption opts));
|
||||
showOption = name: option:
|
||||
let
|
||||
result = trim ''
|
||||
- ${item}
|
||||
|
||||
${option.description}
|
||||
'';
|
||||
item = if inlineHTML
|
||||
then ''<span id="opt-${name}">[`--${name}`](#opt-${name})</span> ${shortName} ${labels}''
|
||||
else "`--${name}` ${shortName} ${labels}";
|
||||
shortName = optionalString
|
||||
(option ? shortName)
|
||||
("/ `-${option.shortName}`");
|
||||
labels = optionalString
|
||||
(option ? labels)
|
||||
(concatStringsSep " " (map (s: "*${s}*") option.labels));
|
||||
in trim ''
|
||||
- <span id="opt-${name}">[`--${name}`](#opt-${name})</span> ${shortName} ${labels}
|
||||
|
||||
${option.description}
|
||||
'';
|
||||
categories = sort lessThan (unique (map (cmd: cmd.category) (attrValues allOptions)));
|
||||
in concatStrings (map showCategory categories);
|
||||
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));
|
||||
in squash result;
|
||||
|
||||
appendName = filename: name: (if filename == "nix" then "nix3" else filename) + "-" + name;
|
||||
@ -135,35 +153,4 @@ 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 { useAnchors = false; } settings}
|
||||
'';
|
||||
in concatStrings (attrValues (mapAttrs showStore commandInfo.stores));
|
||||
|
||||
in (listToAttrs manpages) // { "SUMMARY.md" = tableOfContents; }
|
||||
|
66
doc/manual/generate-settings.nix
Normal file
66
doc/manual/generate-settings.nix
Normal file
@ -0,0 +1,66 @@
|
||||
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))
|
45
doc/manual/generate-store-info.nix
Normal file
45
doc/manual/generate-store-info.nix
Normal file
@ -0,0 +1,45 @@
|
||||
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))
|
@ -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/corepkgs=corepkgs --store dummy:// --impure --raw
|
||||
nix-eval = $(dummy-env) $(bindir)/nix eval --experimental-features nix-command -I nix=doc/manual --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 $(bindir)/nix
|
||||
$(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
|
||||
@rm -rf $@ $@.tmp
|
||||
$(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-manpage.nix (builtins.readFile $<)'
|
||||
$(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)/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)/generate-settings.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/utils.nix).showSettings { useAnchors = true; } (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp;
|
||||
$(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-settings.nix { prefix = "conf"; } (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) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-xp-features > $@.tmp
|
||||
$(trace-gen) $(dummy-env) $(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) NIX_PATH=nix/corepkgs=corepkgs $(bindir)/nix __dump-language > $@.tmp
|
||||
$(trace-gen) $(dummy-env) $(bindir)/nix __dump-language > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
# Generate the HTML manual.
|
||||
@ -173,6 +173,10 @@ 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)"; \
|
||||
@ -180,7 +184,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' | while read -r file; do \
|
||||
find "$$tmp" -name '*.md' ! -name 'documentation.md' | while read -r file; do \
|
||||
docroot="$$(realpath --relative-to="$$(dirname "$$file")" $$tmp/manual/src)"; \
|
||||
sed -i "s,@docroot@,$$docroot,g" "$$file"; \
|
||||
done; \
|
||||
|
@ -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,14 +339,13 @@ 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",
|
||||
@ -355,6 +357,7 @@ 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",
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
synopsis: Option `allowed-uris` can now match whole schemes in URIs without slashes
|
||||
prs: 9547
|
||||
---
|
||||
|
||||
If a scheme, such as `github:` is specified in the `allowed-uris` option, all URIs starting with `github:` are allowed.
|
||||
Previously this only worked for schemes whose URIs used the `://` syntax.
|
@ -16,26 +16,31 @@
|
||||
- [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)
|
||||
@ -97,7 +102,6 @@
|
||||
- [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)
|
||||
@ -105,11 +109,12 @@
|
||||
- [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 X.Y (202?-??-??)](release-notes/rl-next.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)
|
||||
|
@ -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 ping --store ssh://mac
|
||||
$ nix store info --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 ping --store ssh://mac?ssh-key=/home/alice/my-key
|
||||
$ nix store info --store ssh://mac?ssh-key=/home/alice/my-key
|
||||
```
|
||||
|
||||
Since builds should be non-interactive, the key should not have a
|
||||
|
@ -17,9 +17,8 @@ the build loop.
|
||||
|
||||
# Prerequisites
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
# Set up a Signing Key
|
||||
|
||||
@ -69,6 +68,8 @@ 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:
|
||||
|
||||
|
@ -59,10 +59,11 @@ 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](../glossary.md#gloss-store), 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](../store/index.md), 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.
|
||||
|
@ -2,109 +2,124 @@
|
||||
|
||||
Most Nix commands interpret the following environment variables:
|
||||
|
||||
- <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`.
|
||||
- <span id="env-IN_NIX_SHELL">[`IN_NIX_SHELL`](#env-IN_NIX_SHELL)</span>
|
||||
|
||||
- <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).
|
||||
Indicator that tells if the current environment was set up by
|
||||
`nix-shell`. It can have the values `pure` or `impure`.
|
||||
|
||||
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:
|
||||
- <span id="env-NIX_PATH">[`NIX_PATH`](#env-NIX_PATH)</span>
|
||||
|
||||
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`
|
||||
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).
|
||||
|
||||
If `NIX_PATH` is set to an empty string, resolving search paths will always fail.
|
||||
For example, attempting to use `<nixpkgs>` will produce:
|
||||
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:
|
||||
|
||||
error: file 'nixpkgs' was not found in the Nix search path
|
||||
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`
|
||||
|
||||
- <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 you’re not going to do that, you
|
||||
can set `NIX_IGNORE_SYMLINK_STORE` to `1`.
|
||||
If `NIX_PATH` is set to an empty string, resolving search paths will always fail.
|
||||
For example, attempting to use `<nixpkgs>` will produce:
|
||||
|
||||
Note that if you’re symlinking the Nix store so that you can put it
|
||||
on another file system than the root file system, on Linux you’re
|
||||
better off using `bind` mount points, e.g.,
|
||||
error: file 'nixpkgs' was not found in the Nix search path
|
||||
|
||||
```console
|
||||
$ mkdir /nix
|
||||
$ mount -o bind /mnt/otherdisk/nix /nix
|
||||
```
|
||||
- <span id="env-NIX_IGNORE_SYMLINK_STORE">[`NIX_IGNORE_SYMLINK_STORE`](#env-NIX_IGNORE_SYMLINK_STORE)</span>
|
||||
|
||||
Consult the mount 8 manual page for details.
|
||||
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 you’re not going to do that, you
|
||||
can set `NIX_IGNORE_SYMLINK_STORE` to `1`.
|
||||
|
||||
- <span id="env-NIX_STORE_DIR">[`NIX_STORE_DIR`](#env-NIX_STORE_DIR)</span>\
|
||||
Overrides the location of the Nix store (default `prefix/store`).
|
||||
Note that if you’re symlinking the Nix store so that you can put it
|
||||
on another file system than the root file system, on Linux you’re
|
||||
better off using `bind` mount points, e.g.,
|
||||
|
||||
- <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`).
|
||||
```console
|
||||
$ mkdir /nix
|
||||
$ mount -o bind /mnt/otherdisk/nix /nix
|
||||
```
|
||||
|
||||
- <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`).
|
||||
Consult the mount 8 manual page for details.
|
||||
|
||||
- <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`).
|
||||
- <span id="env-NIX_STORE_DIR">[`NIX_STORE_DIR`](#env-NIX_STORE_DIR)</span>
|
||||
|
||||
- <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`).
|
||||
Overrides the location of the Nix store (default `prefix/store`).
|
||||
|
||||
- <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_DATA_DIR">[`NIX_DATA_DIR`](#env-NIX_DATA_DIR)</span>
|
||||
|
||||
- <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.
|
||||
Overrides the location of the Nix static data directory (default
|
||||
`prefix/share`).
|
||||
|
||||
The default are the locations according to the [XDG Base Directory Specification].
|
||||
See the [XDG Base Directories](#xdg-base-directories) sub-section for details.
|
||||
- <span id="env-NIX_LOG_DIR">[`NIX_LOG_DIR`](#env-NIX_LOG_DIR)</span>
|
||||
|
||||
The variable is treated as a list separated by the `:` token.
|
||||
Overrides the location of the Nix log directory (default
|
||||
`prefix/var/log/nix`).
|
||||
|
||||
- <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_STATE_DIR">[`NIX_STATE_DIR`](#env-NIX_STATE_DIR)</span>
|
||||
|
||||
- <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.
|
||||
Overrides the location of the Nix state directory (default
|
||||
`prefix/var/nix`).
|
||||
|
||||
- <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_CONF_DIR">[`NIX_CONF_DIR`](#env-NIX_CONF_DIR)</span>
|
||||
|
||||
- <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.
|
||||
Overrides the location of the system Nix configuration directory
|
||||
(default `prefix/etc/nix`).
|
||||
|
||||
- <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-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.
|
||||
|
||||
## XDG Base Directories
|
||||
|
||||
|
@ -14,16 +14,21 @@
|
||||
|
||||
# Description
|
||||
|
||||
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:
|
||||
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.
|
||||
|
||||
- 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.
|
||||
[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
|
||||
|
||||
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
|
||||
@ -40,44 +45,90 @@ 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` (`-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 [`--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 `--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
|
||||
[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 `--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 *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 derivations](@docroot@/glossary.md#gloss-store-derivation), then these are [realised], and the resulting output paths are installed.
|
||||
|
||||
- If *args* are store paths that are not store derivations, then these
|
||||
are [realised](@docroot@/command-ref/nix-store/realise.md) and installed.
|
||||
- If *args* are [store paths] that are not store derivations, then these are [realised] and installed.
|
||||
|
||||
- By default all outputs are installed for each derivation. That can
|
||||
be reduced by setting `meta.outputsToInstall`.
|
||||
- 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.
|
||||
|
||||
# Flags
|
||||
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`
|
||||
|
||||
- `--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
|
||||
@ -85,7 +136,8 @@ 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.
|
||||
|
@ -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 double quotes (`"`) when passing a simple Nix expression
|
||||
> You must use single or 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
|
||||
|
@ -15,8 +15,12 @@ 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. 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 -->
|
||||
- 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 -->
|
||||
- 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.
|
||||
|
@ -1,57 +0,0 @@
|
||||
\--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
|
@ -203,3 +203,7 @@ 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.
|
||||
|
@ -1,15 +0,0 @@
|
||||
\--prebuilt-only
|
||||
|
||||
\-b
|
||||
|
||||
\--attr
|
||||
|
||||
\-A
|
||||
|
||||
\--from-expression
|
||||
|
||||
\-E
|
||||
|
||||
\--from-profile
|
||||
|
||||
path
|
@ -1 +1,8 @@
|
||||
# Contributing
|
||||
# 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).
|
||||
|
210
doc/manual/src/contributing/documentation.md
Normal file
210
doc/manual/src/contributing/documentation.md
Normal file
@ -0,0 +1,210 @@
|
||||
# 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
|
||||
```
|
@ -42,8 +42,8 @@ $ nix develop .#native-clang11StdenvPackages
|
||||
To build Nix itself in this shell:
|
||||
|
||||
```console
|
||||
[nix-shell]$ ./bootstrap.sh
|
||||
[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out
|
||||
[nix-shell]$ autoreconfPhase
|
||||
[nix-shell]$ configurePhase
|
||||
[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]$ ./bootstrap.sh
|
||||
[nix-shell]$ autoreconfPhase
|
||||
[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 install
|
||||
make clean && bear -- make -j$NIX_BUILD_CORES default check 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,68 +220,3 @@ 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).
|
||||
|
@ -1,18 +1,117 @@
|
||||
# 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 for each Nix library (`libexpr`, `libstore`, etc..) are defined
|
||||
under `src/{library_name}/tests` using the
|
||||
[googletest](https://google.github.io/googletest/) and
|
||||
[rapidcheck](https://github.com/emil-e/rapidcheck) frameworks.
|
||||
The unit tests are defined using the [googletest] and [rapidcheck] frameworks.
|
||||
|
||||
[googletest]: https://google.github.io/googletest/
|
||||
[rapidcheck]: https://github.com/emil-e/rapidcheck
|
||||
[property testing]: https://en.wikipedia.org/wiki/Property_testing
|
||||
|
||||
### Source and header layout
|
||||
|
||||
> An example of some files, demonstrating much of what is described below
|
||||
>
|
||||
> ```
|
||||
> src
|
||||
> ├── libexpr
|
||||
> │ ├── local.mk
|
||||
> │ ├── value/context.hh
|
||||
> │ ├── value/context.cc
|
||||
> │ …
|
||||
> │
|
||||
> ├── tests
|
||||
> │ │
|
||||
> │ …
|
||||
> │ └── unit
|
||||
> │ ├── libutil
|
||||
> │ │ ├── local.mk
|
||||
> │ │ …
|
||||
> │ │ └── data
|
||||
> │ │ ├── git/tree.txt
|
||||
> │ │ …
|
||||
> │ │
|
||||
> │ ├── libexpr-support
|
||||
> │ │ ├── local.mk
|
||||
> │ │ └── tests
|
||||
> │ │ ├── value/context.hh
|
||||
> │ │ ├── value/context.cc
|
||||
> │ │ …
|
||||
> │ │
|
||||
> │ ├── libexpr
|
||||
> │ … ├── local.mk
|
||||
> │ ├── value/context.cc
|
||||
> │ …
|
||||
> …
|
||||
> ```
|
||||
|
||||
The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `tests/unit/${library_name_without-nix}`.
|
||||
Given a interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `tests/unit/libexpr/tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `tests/unit/libexpr-support/tests/value/context.{hh,cc}`.
|
||||
|
||||
Data for unit tests is stored in a `data` subdir of the directory for each unit test executable.
|
||||
For example, `libnixstore` code is in `src/libstore`, and its test data is in `tests/unit/libstore/data`.
|
||||
The path to the `tests/unit/data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`.
|
||||
Note that each executable only gets the data for its tests.
|
||||
|
||||
The unit test libraries are in `tests/unit/${library_name_without-nix}-lib`.
|
||||
All headers are in a `tests` subdirectory so they are included with `#include "tests/"`.
|
||||
|
||||
The use of all these separate directories for the unit tests might seem inconvenient, as for example the tests are not "right next to" the part of the code they are testing.
|
||||
But organizing the tests this way has one big benefit:
|
||||
there is no risk of any build-system wildcards for the library accidentally picking up test code that should not built and installed as part of the library.
|
||||
|
||||
### Running tests
|
||||
|
||||
You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`.
|
||||
Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option, or the `GTEST_FILTER` environment variable.
|
||||
|
||||
### Characterisation testing { #characaterisation-testing-unit }
|
||||
|
||||
See [functional characterisation testing](#characterisation-testing-functional) for a broader discussion of characterisation testing.
|
||||
|
||||
Like with the functional characterisation, `_NIX_TEST_ACCEPT=1` is also used.
|
||||
For example:
|
||||
```shell-session
|
||||
$ _NIX_TEST_ACCEPT=1 make libstore-tests_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.
|
||||
|
||||
### Unit test support libraries
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
The functional tests reside under the `tests` directory and are listed in `tests/local.mk`.
|
||||
The functional tests reside under the `tests/functional` directory and are listed in `tests/functional/local.mk`.
|
||||
Each test is a bash script.
|
||||
|
||||
### Running the whole test suite
|
||||
@ -21,8 +120,8 @@ The whole test suite can be run with:
|
||||
|
||||
```shell-session
|
||||
$ make install && make installcheck
|
||||
ran test tests/foo.sh... [PASS]
|
||||
ran test tests/bar.sh... [PASS]
|
||||
ran test tests/functional/foo.sh... [PASS]
|
||||
ran test tests/functional/bar.sh... [PASS]
|
||||
...
|
||||
```
|
||||
|
||||
@ -30,14 +129,14 @@ ran test tests/bar.sh... [PASS]
|
||||
|
||||
Sometimes it is useful to group related tests so they can be easily run together without running the entire test suite.
|
||||
Each test group is in a subdirectory of `tests`.
|
||||
For example, `tests/ca/local.mk` defines a `ca` test group for content-addressed derivation outputs.
|
||||
For example, `tests/functional/ca/local.mk` defines a `ca` test group for content-addressed derivation outputs.
|
||||
|
||||
That test group can be run like this:
|
||||
|
||||
```shell-session
|
||||
$ make ca.test-group -j50
|
||||
ran test tests/ca/nix-run.sh... [PASS]
|
||||
ran test tests/ca/import-derivation.sh... [PASS]
|
||||
ran test tests/functional/ca/nix-run.sh... [PASS]
|
||||
ran test tests/functional/ca/import-derivation.sh... [PASS]
|
||||
...
|
||||
```
|
||||
|
||||
@ -56,24 +155,24 @@ install-tests-groups += $(test-group-name)
|
||||
Individual tests can be run with `make`:
|
||||
|
||||
```shell-session
|
||||
$ make tests/${testName}.sh.test
|
||||
ran test tests/${testName}.sh... [PASS]
|
||||
$ make tests/functional/${testName}.sh.test
|
||||
ran test tests/functional/${testName}.sh... [PASS]
|
||||
```
|
||||
|
||||
or without `make`:
|
||||
|
||||
```shell-session
|
||||
$ ./mk/run-test.sh tests/${testName}.sh
|
||||
ran test tests/${testName}.sh... [PASS]
|
||||
$ ./mk/run-test.sh tests/functional/${testName}.sh tests/functional/init.sh
|
||||
ran test tests/functional/${testName}.sh... [PASS]
|
||||
```
|
||||
|
||||
To see the complete output, one can also run:
|
||||
|
||||
```shell-session
|
||||
$ ./mk/debug-test.sh tests/${testName}.sh
|
||||
+ foo
|
||||
$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh
|
||||
+(${testName}.sh:1) foo
|
||||
output from foo
|
||||
+ bar
|
||||
+(${testName}.sh:2) bar
|
||||
output from bar
|
||||
...
|
||||
```
|
||||
@ -105,7 +204,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/${testName}.sh
|
||||
$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh
|
||||
...
|
||||
+ gdb blash blub
|
||||
GNU gdb (GDB) 12.1
|
||||
@ -116,17 +215,29 @@ 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.
|
||||
|
||||
### Characterization testing
|
||||
### Troubleshooting
|
||||
|
||||
Occasionally, Nix utilizes a technique called [Characterization Testing](https://en.wikipedia.org/wiki/Characterization_test) as part of the functional tests.
|
||||
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.
|
||||
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.
|
||||
|
||||
It is frequently useful to regenerate the expected output.
|
||||
To do that, rerun the failed test with `_NIX_TEST_ACCEPT=1`.
|
||||
(At least, this is the convention we've used for `tests/lang.sh`.
|
||||
If we add more characterization testing we should always strive to be consistent.)
|
||||
To do that, rerun the failed test(s) with `_NIX_TEST_ACCEPT=1`.
|
||||
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.
|
||||
@ -139,7 +250,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 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`.
|
||||
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`.
|
||||
|
||||
## Integration tests
|
||||
|
||||
@ -153,7 +264,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 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):
|
||||
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):
|
||||
|
||||
- The `installer` job generates installers for the platforms below and uploads them to your Cachix cache:
|
||||
- `x86_64-linux`
|
||||
|
@ -33,11 +33,15 @@
|
||||
|
||||
Ensure a [store path] is [valid][validity].
|
||||
|
||||
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 -->
|
||||
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 -->
|
||||
|
||||
See [`nix-build`](./command-ref/nix-build.md) and [`nix-store --realise`](@docroot@/command-ref/nix-store/realise.md).
|
||||
See [`nix-store --realise`](@docroot@/command-ref/nix-store/realise.md) for a detailed description of the algorithm.
|
||||
|
||||
See [`nix build`](./command-ref/new-cli/nix3-build.md) (experimental).
|
||||
See also [`nix-build`](./command-ref/nix-build.md) and [`nix build`](./command-ref/new-cli/nix3-build.md) (experimental).
|
||||
|
||||
[realise]: #gloss-realise
|
||||
|
||||
@ -54,22 +58,16 @@
|
||||
|
||||
- [store]{#gloss-store}
|
||||
|
||||
The location in the file system where store objects live. Typically
|
||||
`/nix/store`.
|
||||
A collection of store objects, with operations to manipulate that collection.
|
||||
See [Nix store](./store/index.md) for details.
|
||||
|
||||
From the perspective of the location where Nix is
|
||||
invoked, the Nix store can be referred to
|
||||
as a "_local_" or a "_remote_" one:
|
||||
There are many types of stores.
|
||||
See [`nix help-stores`](@docroot@/command-ref/new-cli/nix3-help-stores.md) for a complete list.
|
||||
|
||||
+ 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.
|
||||
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.
|
||||
|
||||
[store]: #gloss-store
|
||||
[local store]: #gloss-local-store
|
||||
@ -88,10 +86,13 @@
|
||||
|
||||
- [store path]{#gloss-store-path}
|
||||
|
||||
The location of a [store object] in the file system, i.e., an
|
||||
immediate child of the Nix store directory.
|
||||
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.
|
||||
|
||||
Example: `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1`
|
||||
> **Example**
|
||||
>
|
||||
> `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1`
|
||||
|
||||
See [Store Path](@docroot@/store/store-path.md) for details.
|
||||
|
||||
[store path]: #gloss-store-path
|
||||
|
||||
@ -99,18 +100,25 @@
|
||||
|
||||
The Nix data model for representing simplified file system data.
|
||||
|
||||
See [File System Object](@docroot@/architecture/file-system-object.md) for details.
|
||||
See [File System Object](@docroot@/store/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], [reference]s to other store objects, and other metadata.
|
||||
A store object consists of a [file system object], [references][reference] 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
|
||||
@ -200,6 +208,7 @@
|
||||
- [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
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
After cloning Nix's Git repository, issue the following commands:
|
||||
|
||||
```console
|
||||
$ ./bootstrap.sh
|
||||
$ autoreconf -vfi
|
||||
$ ./configure options...
|
||||
$ make
|
||||
$ make install
|
||||
|
@ -3,14 +3,14 @@
|
||||
To run the latest stable release of Nix with Docker run the following command:
|
||||
|
||||
```console
|
||||
$ docker run -ti nixos/nix
|
||||
Unable to find image 'nixos/nix:latest' locally
|
||||
latest: Pulling from nixos/nix
|
||||
$ docker run -ti ghcr.io/nixos/nix
|
||||
Unable to find image 'ghcr.io/nixos/nix:latest' locally
|
||||
latest: Pulling from ghcr.io/nixos/nix
|
||||
5843afab3874: Pull complete
|
||||
b52bf13f109c: Pull complete
|
||||
1e2415612aa3: Pull complete
|
||||
Digest: sha256:27f6e7f60227e959ee7ece361f75d4844a40e1cc6878b6868fe30140420031ff
|
||||
Status: Downloaded newer image for nixos/nix:latest
|
||||
Status: Downloaded newer image for ghcr.io/nixos/nix:latest
|
||||
35ca4ada6e96:/# nix --version
|
||||
nix (Nix) 2.3.12
|
||||
35ca4ada6e96:/# exit
|
||||
|
@ -112,6 +112,13 @@ 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
|
||||
@ -229,6 +236,8 @@ 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
|
||||
@ -261,6 +270,9 @@ 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
|
||||
@ -271,18 +283,21 @@ 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 in JSON format and made available to the
|
||||
builder via the file `.attrs.json` in the builder’s temporary
|
||||
directory. This obviates the need for [`passAsFile`](#adv-attr-passAsFile) since JSON files
|
||||
have no size restrictions, unlike process environments.
|
||||
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.
|
||||
|
||||
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 named `.attrs.sh` to the builder’s directory
|
||||
that initialises shell variables corresponding to all attributes
|
||||
that are representable in Bash. This includes non-nested
|
||||
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
|
||||
(associative) arrays. For example, the attribute `hardening.format = true`
|
||||
ends up as the Bash associative array element `${hardening[format]}`.
|
||||
|
||||
@ -335,3 +350,15 @@ 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.
|
||||
|
@ -132,6 +132,32 @@ 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:
|
||||
@ -146,65 +172,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
|
||||
```
|
||||
|
27
doc/manual/src/language/constructs/lookup-path.md
Normal file
27
doc/manual/src/language/constructs/lookup-path.md
Normal file
@ -0,0 +1,27 @@
|
||||
# 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
|
@ -1,161 +1,315 @@
|
||||
# Derivations
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
- 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.)
|
||||
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.
|
||||
|
||||
- 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.
|
||||
[store derivation]: @docroot@/glossary.md#gloss-store-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`).
|
||||
## Input attributes
|
||||
|
||||
- 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.
|
||||
### Required
|
||||
|
||||
- The optional attribute `args` specifies command-line arguments to be
|
||||
passed to the builder. It should be a list.
|
||||
- [`name`]{#attr-name} ([String](@docroot@/language/values.md#type-string))
|
||||
|
||||
- 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 doesn’t need the header files and documentation at runtime,
|
||||
and it doesn’t 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`.
|
||||
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).
|
||||
|
||||
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.
|
||||
[store path]: @docroot@/glossary.md#gloss-store-path
|
||||
|
||||
The builder is executed as follows:
|
||||
> **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>]`
|
||||
|
||||
- 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.
|
||||
- [`system`]{#attr-system} ([String](@docroot@/language/values.md#type-string))
|
||||
|
||||
- The environment is cleared and set to the derivation attributes, as
|
||||
specified above.
|
||||
The system type on which the [`builder`](#attr-builder) executable is meant to be run.
|
||||
|
||||
- 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
|
||||
it’s `out`.)
|
||||
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.
|
||||
|
||||
- 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.
|
||||
[`system` configuration option]: @docroot@/command-ref/conf-file.md#conf-system
|
||||
|
||||
- A log of the combined standard output and error is written to
|
||||
`/nix/var/log/nix`.
|
||||
> **Example**
|
||||
>
|
||||
> Declare a derivation to be built on a specific system type:
|
||||
>
|
||||
> ```nix
|
||||
> derivation {
|
||||
> # ...
|
||||
> system = "x86_64-linux";
|
||||
> # ...
|
||||
> }
|
||||
> ```
|
||||
|
||||
- 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.
|
||||
> **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.
|
||||
|
||||
- The temporary directory is removed (unless the `-K` option was
|
||||
specified).
|
||||
- [`builder`]{#attr-builder} ([Path](@docroot@/language/values.md#type-path) | [String](@docroot@/language/values.md#type-string))
|
||||
|
||||
- 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.
|
||||
Path to an executable that will perform the build.
|
||||
|
||||
- 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.
|
||||
> **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 doesn’t need the header files and documentation at runtime, and it doesn’t 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
|
||||
it’s `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.
|
||||
|
139
doc/manual/src/language/import-from-derivation.md
Normal file
139
doc/manual/src/language/import-from-derivation.md
Normal file
@ -0,0 +1,139 @@
|
||||
# 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|
|
||||
'-------' '-------------' '---------'
|
||||
```
|
@ -83,7 +83,8 @@ This is an incomplete overview of language features, by example.
|
||||
</td>
|
||||
<td>
|
||||
|
||||
A multi-line string. Strips common prefixed whitespace. Evaluates to `"multi\n line\n string"`.
|
||||
<!-- 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"`.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -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* | none | 14 |
|
||||
| [Logical implication] | *bool* `->` *bool* | right | 14 |
|
||||
|
||||
[string]: ./values.md#type-string
|
||||
[path]: ./values.md#type-path
|
||||
@ -35,6 +35,8 @@
|
||||
|
||||
## Attribute selection
|
||||
|
||||
> **Syntax**
|
||||
>
|
||||
> *attrset* `.` *attrpath* \[ `or` *expr* \]
|
||||
|
||||
Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*.
|
||||
@ -42,21 +44,29 @@ If the attribute doesn’t 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:
|
||||
@ -70,6 +80,8 @@ 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.
|
||||
@ -78,6 +90,8 @@ Concatenate two [string]s and merge their string contexts.
|
||||
|
||||
## Path concatenation
|
||||
|
||||
> **Syntax**
|
||||
>
|
||||
> *path* `+` *path*
|
||||
|
||||
Concatenate two [path]s.
|
||||
@ -87,6 +101,8 @@ The result is a path.
|
||||
|
||||
## Path and string concatenation
|
||||
|
||||
> **Syntax**
|
||||
>
|
||||
> *path* + *string*
|
||||
|
||||
Concatenate *[path]* with *[string]*.
|
||||
@ -100,6 +116,8 @@ The result is a path.
|
||||
|
||||
## String and path concatenation
|
||||
|
||||
> **Syntax**
|
||||
>
|
||||
> *string* + *path*
|
||||
|
||||
Concatenate *[string]* with *[path]*.
|
||||
@ -117,6 +135,8 @@ The result is a string.
|
||||
|
||||
## Update
|
||||
|
||||
> **Syntax**
|
||||
>
|
||||
> *attrset1* // *attrset2*
|
||||
|
||||
Update [attribute set] *attrset1* with names and values from *attrset2*.
|
||||
|
@ -1,19 +1,12 @@
|
||||
# String interpolation
|
||||
|
||||
String interpolation is a language feature where a [string], [path], or [attribute name] can contain expressions enclosed in `${ }` (dollar-sign with curly brackets).
|
||||
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).
|
||||
|
||||
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]
|
||||
Such a construct is called *interpolated string*, and the expression inside is an [interpolated expression](#interpolated-expression).
|
||||
|
||||
[string]: ./values.md#type-string
|
||||
[path]: ./values.md#type-path
|
||||
[attribute name]: ./values.md#attribute-set
|
||||
[derivation]: ../glossary.md#gloss-derivation
|
||||
[attribute set]: ./values.md#attribute-set
|
||||
|
||||
## Examples
|
||||
|
||||
@ -70,13 +63,136 @@ you can instead write
|
||||
|
||||
### Attribute name
|
||||
|
||||
Attribute names can be created dynamically with string interpolation:
|
||||
<!--
|
||||
FIXME: these examples are redundant with the main page on attribute sets.
|
||||
figure out what to do about that
|
||||
-->
|
||||
|
||||
```nix
|
||||
let name = "foo"; in
|
||||
{
|
||||
${name} = "bar";
|
||||
}
|
||||
```
|
||||
Attribute names can be interpolated strings.
|
||||
|
||||
{ foo = "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}"
|
||||
> | ^
|
||||
|
@ -107,29 +107,24 @@
|
||||
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.
|
||||
|
||||
Paths themselves, except those in angle brackets (`< >`), support [string interpolation].
|
||||
[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
|
||||
|
||||
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`.
|
||||
@ -167,13 +162,17 @@ 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 (`;`).
|
||||
|
||||
> *attrset* = `{` [ *name* `=` *expr* `;` `]`... `}`
|
||||
> **Syntax**
|
||||
>
|
||||
> *attrset* = `{` [ *name* `=` *expr* `;` ]... `}`
|
||||
|
||||
Attributes can appear in any order.
|
||||
An attribute name may only occur once.
|
||||
|
@ -1,179 +0,0 @@
|
||||
# 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 user’s `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, it’s 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, you’re 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 you’ve 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 Nix’s 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 you’d
|
||||
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 it’s 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'
|
||||
```
|
@ -1,115 +0,0 @@
|
||||
# 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®ion=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'
|
||||
```
|
@ -1 +1,12 @@
|
||||
# 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.
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
* 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].
|
||||
|
||||
|
77
doc/manual/src/release-notes/rl-2.19.md
Normal file
77
doc/manual/src/release-notes/rl-2.19.md
Normal file
@ -0,0 +1,77 @@
|
||||
# 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).
|
||||
This allows stabilising it independently of the rest of what is encompassed by [`flakes`](@docroot@/contributing/experimental-features.md#xp-fetch-tree).
|
||||
|
||||
- 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.
|
@ -1 +1,2 @@
|
||||
# Release X.Y (202?-??-??)
|
||||
|
||||
|
5
doc/manual/src/store/index.md
Normal file
5
doc/manual/src/store/index.md
Normal file
@ -0,0 +1,5 @@
|
||||
# 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.
|
10
doc/manual/src/store/store-object.md
Normal file
10
doc/manual/src/store/store-object.md
Normal file
@ -0,0 +1,10 @@
|
||||
## 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.
|
69
doc/manual/src/store/store-path.md
Normal file
69
doc/manual/src/store/store-path.md
Normal file
@ -0,0 +1,69 @@
|
||||
# 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 store’s [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.
|
@ -44,63 +44,6 @@ rec {
|
||||
|
||||
optionalString = cond: string: if cond then string else "";
|
||||
|
||||
showSetting = { useAnchors }: name: { description, documentDefault, defaultValue, aliases, value, experimentalFeature }:
|
||||
let
|
||||
result = squash ''
|
||||
- ${if useAnchors
|
||||
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));
|
||||
}
|
||||
|
12
flake.lock
12
flake.lock
@ -34,16 +34,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1695124524,
|
||||
"narHash": "sha256-trXDytVCqf3KryQQQrHOZKUabu1/lB8/ndOAuZKQrOE=",
|
||||
"owner": "edolstra",
|
||||
"lastModified": 1700748986,
|
||||
"narHash": "sha256-/nqLrNU297h3PCw4QyDpZKZEUHmialJdZW2ceYFobds=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a3d30b525535e3158221abc1a957ce798ab159fe",
|
||||
"rev": "9ba29e2346bc542e9909d1021e8fd7d4b3f64db0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"ref": "fix-aws-sdk-cpp",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.05-small",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
236
flake.nix
236
flake.nix
@ -1,8 +1,7 @@
|
||||
{
|
||||
description = "The purely functional package manager";
|
||||
|
||||
#inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small";
|
||||
inputs.nixpkgs.url = "github:edolstra/nixpkgs/fix-aws-sdk-cpp";
|
||||
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; };
|
||||
@ -12,7 +11,7 @@
|
||||
let
|
||||
inherit (nixpkgs) lib;
|
||||
|
||||
officialRelease = false;
|
||||
officialRelease = true;
|
||||
|
||||
version = lib.fileContents ./.version + versionSuffix;
|
||||
versionSuffix =
|
||||
@ -25,8 +24,11 @@
|
||||
linuxSystems = linux32BitSystems ++ linux64BitSystems;
|
||||
darwinSystems = [ "x86_64-darwin" "aarch64-darwin" ];
|
||||
systems = linuxSystems ++ darwinSystems;
|
||||
|
||||
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
||||
|
||||
crossSystems = [
|
||||
"armv6l-linux" "armv7l-linux"
|
||||
"x86_64-freebsd13" "x86_64-netbsd"
|
||||
];
|
||||
|
||||
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" "ccacheStdenv" ];
|
||||
|
||||
@ -57,44 +59,55 @@
|
||||
# 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.difference
|
||||
(fileset.unions [
|
||||
./.version
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
./bootstrap.sh
|
||||
./configure.ac
|
||||
./doc
|
||||
./local.mk
|
||||
./m4
|
||||
./Makefile
|
||||
./Makefile.config.in
|
||||
./misc
|
||||
./mk
|
||||
./precompiled-headers.h
|
||||
./src
|
||||
./tests
|
||||
./COPYING
|
||||
./scripts/local.mk
|
||||
(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
|
||||
])
|
||||
(fileset.unions [
|
||||
# Removed file sets
|
||||
./tests/nixos
|
||||
./tests/installer
|
||||
])
|
||||
);
|
||||
fileset = fileset.intersect baseFiles (fileset.unions [
|
||||
configureFiles
|
||||
topLevelBuildFiles
|
||||
./boehmgc-coroutine-sp-fallback.diff
|
||||
./doc
|
||||
./misc
|
||||
./precompiled-headers.h
|
||||
./src
|
||||
./tests/unit
|
||||
./COPYING
|
||||
./scripts/local.mk
|
||||
functionalTestFiles
|
||||
]);
|
||||
};
|
||||
|
||||
# Memoize nixpkgs for different platforms for efficiency.
|
||||
nixpkgsFor = forAllSystems
|
||||
(system: let
|
||||
make-pkgs = crossSystem: stdenv: import nixpkgs {
|
||||
inherit system crossSystem;
|
||||
localSystem = {
|
||||
inherit system;
|
||||
};
|
||||
crossSystem = if crossSystem == null then null else {
|
||||
system = crossSystem;
|
||||
} // lib.optionalAttrs (crossSystem == "x86_64-freebsd13") {
|
||||
useLLVM = true;
|
||||
};
|
||||
overlays = [
|
||||
(overlayFor (p: p.${stdenv}))
|
||||
];
|
||||
@ -149,6 +162,10 @@
|
||||
|
||||
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 = [
|
||||
@ -170,6 +187,7 @@
|
||||
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)];
|
||||
|
||||
@ -180,9 +198,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 = [
|
||||
@ -258,7 +276,14 @@
|
||||
"-${client.version}-against-${daemon.version}";
|
||||
inherit version;
|
||||
|
||||
src = nixSrc;
|
||||
src = fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = fileset.intersect baseFiles (fileset.unions [
|
||||
configureFiles
|
||||
topLevelBuildFiles
|
||||
functionalTestFiles
|
||||
]);
|
||||
};
|
||||
|
||||
VERSION_SUFFIX = versionSuffix;
|
||||
|
||||
@ -268,7 +293,9 @@
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
configureFlags = testConfigureFlags; # otherwise configure fails
|
||||
configureFlags =
|
||||
testConfigureFlags # otherwise configure fails
|
||||
++ [ "--disable-build" ];
|
||||
dontBuild = true;
|
||||
doInstallCheck = true;
|
||||
|
||||
@ -276,7 +303,10 @@
|
||||
mkdir -p $out
|
||||
'';
|
||||
|
||||
installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES";
|
||||
installCheckPhase = ''
|
||||
mkdir -p src/nix-channel
|
||||
make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES
|
||||
'';
|
||||
};
|
||||
|
||||
binaryTarball = nix: pkgs:
|
||||
@ -376,7 +406,8 @@
|
||||
src = nixSrc;
|
||||
VERSION_SUFFIX = versionSuffix;
|
||||
|
||||
outputs = [ "out" "dev" "doc" ];
|
||||
outputs = [ "out" "dev" "doc" ]
|
||||
++ lib.optional (currentStdenv.hostPlatform != currentStdenv.buildPlatform) "check";
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps;
|
||||
buildInputs = buildDeps
|
||||
@ -449,39 +480,13 @@
|
||||
|
||||
hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie";
|
||||
|
||||
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";
|
||||
});
|
||||
passthru.perl-bindings = final.callPackage ./perl {
|
||||
inherit fileset;
|
||||
stdenv = currentStdenv;
|
||||
};
|
||||
|
||||
meta.platforms = lib.platforms.unix;
|
||||
meta.mainProgram = "nix";
|
||||
});
|
||||
|
||||
lowdown-nix = with final; currentStdenv.mkDerivation rec {
|
||||
@ -502,18 +507,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
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.
|
||||
@ -620,49 +613,29 @@
|
||||
};
|
||||
|
||||
# System tests.
|
||||
tests.authorization = runNixOSTestFor "x86_64-linux" ./tests/nixos/authorization.nix;
|
||||
tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // {
|
||||
|
||||
tests.remoteBuilds = runNixOSTestFor "x86_64-linux" ./tests/nixos/remote-builds.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.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);
|
||||
|
||||
|
||||
# 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 ];
|
||||
}
|
||||
);
|
||||
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;
|
||||
@ -733,20 +706,29 @@
|
||||
|
||||
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" ];
|
||||
outputs = [ "out" "dev" "doc" ]
|
||||
++ lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) "check";
|
||||
|
||||
nativeBuildInputs = nativeBuildDeps
|
||||
++ (lib.optionals stdenv.cc.isClang [ pkgs.bear pkgs.clang-tools ]);
|
||||
++ lib.optional stdenv.cc.isClang pkgs.buildPackages.bear
|
||||
++ lib.optional
|
||||
(stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform)
|
||||
pkgs.buildPackages.clang-tools
|
||||
;
|
||||
|
||||
buildInputs = buildDeps ++ propagatedDeps
|
||||
++ awsDeps ++ checkDeps ++ internalApiDocsDeps;
|
||||
|
||||
configureFlags = configureFlags
|
||||
++ testConfigureFlags ++ internalApiDocsConfigureFlags;
|
||||
++ testConfigureFlags ++ internalApiDocsConfigureFlags
|
||||
++ lib.optional (!canRunInstalled) "--disable-doc-gen";
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
|
2
local.mk
2
local.mk
@ -1,5 +1,3 @@
|
||||
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
|
||||
|
64
m4/gcc_bug_80431.m4
Normal file
64
m4/gcc_bug_80431.m4
Normal file
@ -0,0 +1,64 @@
|
||||
# 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)
|
||||
])
|
||||
])
|
@ -2,7 +2,7 @@
|
||||
|
||||
## Motivation
|
||||
|
||||
The team's main responsibility is to set a direction for the development of Nix and ensure that the code is in good shape.
|
||||
The team's main responsibility is to guide and direct 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,7 +50,9 @@ 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), and 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).
|
||||
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).
|
||||
|
||||
## Project board protocol
|
||||
|
||||
@ -96,8 +98,10 @@ 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 is determined as follows:
|
||||
As a general guideline, the order of items to discuss is determined as follows:
|
||||
|
||||
- Prioritise pull requests over issues
|
||||
|
||||
|
@ -1,11 +1,27 @@
|
||||
TESTS_ENVIRONMENT=("TEST_NAME=${test%.*}" 'NIX_REMOTE=')
|
||||
# Remove overall test dir (at most one of the two should match) and
|
||||
# remove file extension.
|
||||
test_name=$(echo -n "$test" | sed \
|
||||
-e "s|^tests/unit/[^/]*/data/||" \
|
||||
-e "s|^tests/functional/||" \
|
||||
-e "s|\.sh$||" \
|
||||
)
|
||||
|
||||
TESTS_ENVIRONMENT=(
|
||||
"TEST_NAME=$test_name"
|
||||
'NIX_REMOTE='
|
||||
'PS4=+(${BASH_SOURCE[0]-$0}:$LINENO) '
|
||||
)
|
||||
|
||||
: ${BASH:=/usr/bin/env bash}
|
||||
|
||||
run () {
|
||||
cd "$(dirname $1)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -x -e -u -o pipefail $(basename $1)
|
||||
}
|
||||
|
||||
init_test () {
|
||||
cd tests && env "${TESTS_ENVIRONMENT[@]}" $BASH -e init.sh 2>/dev/null > /dev/null
|
||||
run "$init" 2>/dev/null > /dev/null
|
||||
}
|
||||
|
||||
run_test_proper () {
|
||||
cd $(dirname $test) && env "${TESTS_ENVIRONMENT[@]}" $BASH -e $(basename $test)
|
||||
run "$test"
|
||||
}
|
||||
|
@ -3,9 +3,12 @@
|
||||
set -eu -o pipefail
|
||||
|
||||
test=$1
|
||||
init=${2-}
|
||||
|
||||
dir="$(dirname "${BASH_SOURCE[0]}")"
|
||||
source "$dir/common-test.sh"
|
||||
|
||||
(init_test)
|
||||
if [ -n "$init" ]; then
|
||||
(init_test)
|
||||
fi
|
||||
run_test_proper
|
||||
|
@ -122,14 +122,15 @@ $(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-install-test,$(test))) \
|
||||
$(eval $(call run-test,$(test),$(install_test_init))) \
|
||||
$(eval installcheck: $(test).test))
|
||||
$(foreach test-group, $(install-tests-groups), \
|
||||
$(eval $(call run-install-test-group,$(test-group))) \
|
||||
$(eval $(call run-test-group,$(test-group),$(install_test_init))) \
|
||||
$(eval installcheck: $(test-group).test-group) \
|
||||
$(foreach test, $($(test-group)-tests), \
|
||||
$(eval $(call run-install-test,$(test))) \
|
||||
$(eval $(call run-test,$(test),$(install_test_init))) \
|
||||
$(eval $(test-group).test-group: $(test).test)))
|
||||
|
||||
$(foreach file, $(man-pages), $(eval $(call install-data-in, $(file), $(mandir)/man$(patsubst .%,%,$(suffix $(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) $$($(1)_PATH)
|
||||
$(trace-test) $$($(1)_ENV) $$($(1)_PATH)
|
||||
|
||||
endef
|
||||
|
@ -8,6 +8,7 @@ yellow=""
|
||||
normal=""
|
||||
|
||||
test=$1
|
||||
init=${2-}
|
||||
|
||||
dir="$(dirname "${BASH_SOURCE[0]}")"
|
||||
source "$dir/common-test.sh"
|
||||
@ -21,7 +22,9 @@ if [ -t 1 ]; then
|
||||
fi
|
||||
|
||||
run_test () {
|
||||
(init_test 2>/dev/null > /dev/null)
|
||||
if [ -n "$init" ]; then
|
||||
(init_test 2>/dev/null > /dev/null)
|
||||
fi
|
||||
log="$(run_test_proper 2>&1)" && status=0 || status=$?
|
||||
}
|
||||
|
||||
|
21
mk/tests.mk
21
mk/tests.mk
@ -2,19 +2,22 @@
|
||||
|
||||
test-deps =
|
||||
|
||||
define run-install-test
|
||||
define run-bash
|
||||
|
||||
.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
|
||||
.PHONY: $1
|
||||
$1: $2
|
||||
@env BASH=$(bash) $(bash) $3 < /dev/null
|
||||
|
||||
endef
|
||||
|
||||
define run-install-test-group
|
||||
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
|
||||
|
||||
.PHONY: $1.test-group
|
||||
|
||||
|
@ -1,6 +1,12 @@
|
||||
makefiles = local.mk
|
||||
|
||||
GLOBAL_CXXFLAGS += -g -Wall -std=c++2a -I ../src
|
||||
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
|
||||
|
||||
-include Makefile.config
|
||||
|
||||
|
51
perl/default.nix
Normal file
51
perl/default.nix
Normal file
@ -0,0 +1,51 @@
|
||||
{ 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";
|
||||
})
|
@ -11,7 +11,6 @@
|
||||
#include "derivations.hh"
|
||||
#include "globals.hh"
|
||||
#include "store-api.hh"
|
||||
#include "util.hh"
|
||||
#include "crypto.hh"
|
||||
|
||||
#include <sodium.h>
|
||||
@ -78,7 +77,7 @@ SV * queryReferences(char * path)
|
||||
SV * queryPathHash(char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash.to_string(Base32, true);
|
||||
auto s = store()->queryPathInfo(store()->parseStorePath(path))->narHash.to_string(HashFormat::Base32, true);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@ -104,7 +103,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 ? Base32 : Base16, true);
|
||||
auto s = info->narHash.to_string(base32 ? HashFormat::Base32 : HashFormat::Base16, true);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
mXPUSHi(info->registrationTime);
|
||||
mXPUSHi(info->narSize);
|
||||
@ -206,7 +205,7 @@ SV * hashPath(char * algo, int base32, char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h = hashPath(parseHashType(algo), path).first;
|
||||
auto s = h.to_string(base32 ? Base32 : Base16, false);
|
||||
auto s = h.to_string(base32 ? HashFormat::Base32 : HashFormat::Base16, false);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@ -217,7 +216,7 @@ SV * hashFile(char * algo, int base32, char * path)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h = hashFile(parseHashType(algo), path);
|
||||
auto s = h.to_string(base32 ? Base32 : Base16, false);
|
||||
auto s = h.to_string(base32 ? HashFormat::Base32 : HashFormat::Base16, false);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@ -228,7 +227,7 @@ SV * hashString(char * algo, int base32, char * s)
|
||||
PPCODE:
|
||||
try {
|
||||
Hash h = hashString(parseHashType(algo), s);
|
||||
auto s = h.to_string(base32 ? Base32 : Base16, false);
|
||||
auto s = h.to_string(base32 ? HashFormat::Base32 : HashFormat::Base16, false);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
@ -239,7 +238,7 @@ SV * convertHash(char * algo, char * s, int toBase32)
|
||||
PPCODE:
|
||||
try {
|
||||
auto h = Hash::parseAny(s, parseHashType(algo));
|
||||
auto s = h.to_string(toBase32 ? Base32 : Base16, false);
|
||||
auto s = h.to_string(toBase32 ? HashFormat::Base32 : HashFormat::Base16, false);
|
||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||
} catch (Error & e) {
|
||||
croak("%s", e.what());
|
||||
|
@ -452,6 +452,14 @@ 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,
|
||||
|
@ -19,6 +19,14 @@ 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
|
||||
|
@ -30,6 +30,14 @@ 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
|
||||
|
@ -20,6 +20,14 @@ 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
|
||||
|
@ -32,6 +32,14 @@ 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
|
||||
|
@ -1,3 +1,6 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "derived-path.hh"
|
||||
#include "realisation.hh"
|
||||
|
||||
|
@ -98,7 +98,7 @@ EvalCommand::EvalCommand()
|
||||
EvalCommand::~EvalCommand()
|
||||
{
|
||||
if (evalState)
|
||||
evalState->printStats();
|
||||
evalState->maybePrintStats();
|
||||
}
|
||||
|
||||
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.push_back(BuiltPath::Opaque{p});
|
||||
paths.emplace_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.push_back(BuiltPath::Opaque{path});
|
||||
paths.emplace_back(BuiltPath::Opaque{path});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,21 +34,28 @@ struct NixMultiCommand : virtual MultiCommand, virtual Command
|
||||
// For the overloaded run methods
|
||||
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
|
||||
|
||||
/* A command that requires a Nix store. */
|
||||
/**
|
||||
* A command that requires a \ref Store "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`
|
||||
stores. */
|
||||
/**
|
||||
* A command that copies something between `--from` and `--to` \ref
|
||||
* Store stores.
|
||||
*/
|
||||
struct CopyCommand : virtual StoreCommand
|
||||
{
|
||||
std::string srcUri, dstUri;
|
||||
@ -60,6 +67,9 @@ struct CopyCommand : virtual StoreCommand
|
||||
ref<Store> getDstStore();
|
||||
};
|
||||
|
||||
/**
|
||||
* A command that needs to evaluate Nix language expressions.
|
||||
*/
|
||||
struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||
{
|
||||
bool startReplOnEvalErrors = false;
|
||||
@ -79,20 +89,26 @@ 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();
|
||||
|
||||
virtual std::vector<std::string> getFlakesForCompletion()
|
||||
/**
|
||||
* 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()
|
||||
{ return {}; }
|
||||
|
||||
void completeFlakeInput(std::string_view prefix);
|
||||
|
||||
void completionHook() override;
|
||||
};
|
||||
|
||||
struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||
@ -112,15 +128,35 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||
|
||||
virtual Strings getDefaultFlakeAttrPathPrefixes();
|
||||
|
||||
void completeInstallable(std::string_view prefix);
|
||||
/**
|
||||
* 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();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 */
|
||||
/**
|
||||
* Like InstallablesCommand but the installables are not loaded.
|
||||
*
|
||||
* This is needed by `CmdRepl` which wants to load (and reload) the
|
||||
* installables itself.
|
||||
*/
|
||||
struct RawInstallablesCommand : virtual Args, SourceExprCommand
|
||||
{
|
||||
RawInstallablesCommand();
|
||||
@ -129,19 +165,22 @@ 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<std::string> getFlakesForCompletion() override;
|
||||
std::vector<FlakeRef> getFlakeRefsForCompletion() 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;
|
||||
@ -149,7 +188,9 @@ 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();
|
||||
@ -158,10 +199,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand
|
||||
|
||||
void run(ref<Store> store) override;
|
||||
|
||||
std::vector<std::string> getFlakesForCompletion() override
|
||||
{
|
||||
return {_installable};
|
||||
}
|
||||
std::vector<FlakeRef> getFlakeRefsForCompletion() override;
|
||||
|
||||
private:
|
||||
|
||||
@ -175,7 +213,12 @@ struct MixOperateOnOptions : virtual Args
|
||||
MixOperateOnOptions();
|
||||
};
|
||||
|
||||
/* A command that operates on zero or more store paths. */
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
struct BuiltPathsCommand : InstallablesCommand, virtual MixOperateOnOptions
|
||||
{
|
||||
private:
|
||||
@ -207,7 +250,9 @@ 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;
|
||||
@ -215,7 +260,9 @@ struct StorePathCommand : public StorePathsCommand
|
||||
void run(ref<Store> store, StorePaths && storePaths) override;
|
||||
};
|
||||
|
||||
/* A helper class for registering commands globally. */
|
||||
/**
|
||||
* A helper class for registering \ref Command commands globally.
|
||||
*/
|
||||
struct RegisterCommand
|
||||
{
|
||||
typedef std::map<std::vector<std::string>, std::function<ref<Command>()>> Commands;
|
||||
@ -271,13 +318,24 @@ 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 completeFlakeRef(ref<Store> store, std::string_view prefix);
|
||||
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 completeFlakeRefWithFragment(
|
||||
AddCompletions & completions,
|
||||
ref<EvalState> evalState,
|
||||
flake::LockFlags lockFlags,
|
||||
Strings attrPathPrefixes,
|
||||
|
@ -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 = {[&](size_t, std::string_view prefix) {
|
||||
completeFlakeRef(openStore(), prefix);
|
||||
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
completeFlakeRef(completions, openStore(), prefix);
|
||||
}}
|
||||
});
|
||||
|
||||
@ -164,18 +164,18 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
||||
return res.finish();
|
||||
}
|
||||
|
||||
SourcePath lookupFileArg(EvalState & state, std::string_view s)
|
||||
SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDir)
|
||||
{
|
||||
if (EvalSettings::isPseudoUrl(s)) {
|
||||
auto storePath = fetchers::downloadTarball(
|
||||
state.store, EvalSettings::resolvePseudoUrl(s), "source", false).tree.storePath;
|
||||
state.store, EvalSettings::resolvePseudoUrl(s), "source", false).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.storePath;
|
||||
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first;
|
||||
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s)
|
||||
}
|
||||
|
||||
else
|
||||
return state.rootPath(CanonPath::fromCwd(s));
|
||||
return state.rootPath(CanonPath(s, baseDir));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
///@file
|
||||
|
||||
#include "args.hh"
|
||||
#include "canon-path.hh"
|
||||
#include "common-args.hh"
|
||||
#include "search-path.hh"
|
||||
|
||||
@ -28,6 +29,6 @@ private:
|
||||
std::map<std::string, std::string> autoArgs;
|
||||
};
|
||||
|
||||
SourcePath lookupFileArg(EvalState & state, std::string_view s);
|
||||
SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDir = CanonPath::fromCwd());
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "util.hh"
|
||||
#include "editor-for.hh"
|
||||
#include "environment-variables.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
#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"
|
||||
|
@ -28,6 +28,11 @@ 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());
|
||||
|
@ -4,6 +4,7 @@
|
||||
#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"
|
||||
@ -28,15 +29,38 @@
|
||||
|
||||
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.",
|
||||
.description = R"(
|
||||
Recreate the flake's lock file from scratch.
|
||||
|
||||
> **DEPRECATED**
|
||||
>
|
||||
> Use [`nix flake update`](@docroot@/command-ref/new-cli/nix3-flake-update.md) instead.
|
||||
)",
|
||||
.category = category,
|
||||
.handler = {&lockFlags.recreateLockFile, true}
|
||||
.handler = {[&]() {
|
||||
lockFlags.recreateLockFile = true;
|
||||
warn("'--recreate-lock-file' is deprecated and will be removed in a future version; use 'nix flake update' instead.");
|
||||
}}
|
||||
});
|
||||
|
||||
addFlag({
|
||||
@ -55,8 +79,13 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
|
||||
addFlag({
|
||||
.longName = "no-registries",
|
||||
.description =
|
||||
"Don't allow lookups in the flake registries. This option is deprecated; use `--no-use-registries`.",
|
||||
.description = R"(
|
||||
Don't allow lookups in the flake registries.
|
||||
|
||||
> **DEPRECATED**
|
||||
>
|
||||
> Use [`--no-use-registries`](#opt-no-use-registries) instead.
|
||||
)",
|
||||
.category = category,
|
||||
.handler = {[&]() {
|
||||
lockFlags.useRegistries = false;
|
||||
@ -73,14 +102,21 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
|
||||
addFlag({
|
||||
.longName = "update-input",
|
||||
.description = "Update a specific flake input (ignoring its previous entry in the lock file).",
|
||||
.description = R"(
|
||||
Update a specific flake input (ignoring its previous entry in the lock file).
|
||||
|
||||
> **DEPRECATED**
|
||||
>
|
||||
> Use [`nix flake update`](@docroot@/command-ref/new-cli/nix3-flake-update.md) instead.
|
||||
)",
|
||||
.category = category,
|
||||
.labels = {"input-path"},
|
||||
.handler = {[&](std::string s) {
|
||||
warn("'--update-input' is a deprecated alias for 'flake update' and will be removed in a future version.");
|
||||
lockFlags.inputUpdates.insert(flake::parseInputPath(s));
|
||||
}},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
needsFlakeInputCompletion = {std::string(prefix)};
|
||||
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix);
|
||||
}}
|
||||
});
|
||||
|
||||
@ -93,13 +129,14 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
lockFlags.writeLockFile = false;
|
||||
lockFlags.inputOverrides.insert_or_assign(
|
||||
flake::parseInputPath(inputPath),
|
||||
parseFlakeRef(flakeRef, absPath("."), true));
|
||||
parseFlakeRef(flakeRef, absPath(getCommandBaseDir()), true));
|
||||
}},
|
||||
.completer = {[&](size_t n, std::string_view prefix) {
|
||||
if (n == 0)
|
||||
needsFlakeInputCompletion = {std::string(prefix)};
|
||||
else if (n == 1)
|
||||
completeFlakeRef(getEvalState()->store, prefix);
|
||||
.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);
|
||||
}
|
||||
}}
|
||||
});
|
||||
|
||||
@ -134,7 +171,7 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
auto evalState = getEvalState();
|
||||
auto flake = flake::lockFlake(
|
||||
*evalState,
|
||||
parseFlakeRef(flakeRef, absPath(".")),
|
||||
parseFlakeRef(flakeRef, absPath(getCommandBaseDir())),
|
||||
{ .writeLockFile = false });
|
||||
for (auto & [inputName, input] : flake.lockFile.root->inputs) {
|
||||
auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes
|
||||
@ -146,30 +183,12 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
}
|
||||
}
|
||||
}},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
completeFlakeRef(getEvalState()->store, prefix);
|
||||
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
|
||||
completeFlakeRef(completions, 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({
|
||||
@ -226,11 +245,18 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
|
||||
};
|
||||
}
|
||||
|
||||
void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
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)
|
||||
{
|
||||
try {
|
||||
if (file) {
|
||||
completionType = ctAttrs;
|
||||
completions.setType(AddCompletions::Type::Attrs);
|
||||
|
||||
evalSettings.pureEval = false;
|
||||
auto state = getEvalState();
|
||||
@ -265,14 +291,15 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
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(),
|
||||
@ -285,6 +312,7 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
}
|
||||
|
||||
void completeFlakeRefWithFragment(
|
||||
AddCompletions & completions,
|
||||
ref<EvalState> evalState,
|
||||
flake::LockFlags lockFlags,
|
||||
Strings attrPathPrefixes,
|
||||
@ -296,12 +324,19 @@ void completeFlakeRefWithFragment(
|
||||
try {
|
||||
auto hash = prefix.find('#');
|
||||
if (hash == std::string::npos) {
|
||||
completeFlakeRef(evalState->store, prefix);
|
||||
completeFlakeRef(completions, evalState->store, prefix);
|
||||
} else {
|
||||
completionType = ctAttrs;
|
||||
completions.setType(AddCompletions::Type::Attrs);
|
||||
|
||||
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,
|
||||
@ -309,6 +344,9 @@ 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. */
|
||||
@ -333,7 +371,7 @@ void completeFlakeRefWithFragment(
|
||||
auto attrPath2 = (*attr)->getAttrPath(attr2);
|
||||
/* Strip the attrpath prefix. */
|
||||
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
|
||||
completions->add(flakeRefS + "#" + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
|
||||
completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -344,7 +382,7 @@ void completeFlakeRefWithFragment(
|
||||
for (auto & attrPath : defaultFlakeAttrPaths) {
|
||||
auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath));
|
||||
if (!attr) continue;
|
||||
completions->add(flakeRefS + "#");
|
||||
completions.add(flakeRefS + "#" + prefixRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -353,15 +391,15 @@ void completeFlakeRefWithFragment(
|
||||
}
|
||||
}
|
||||
|
||||
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
||||
void completeFlakeRef(AddCompletions & completions, ref<Store> store, std::string_view prefix)
|
||||
{
|
||||
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
|
||||
return;
|
||||
|
||||
if (prefix == "")
|
||||
completions->add(".");
|
||||
completions.add(".");
|
||||
|
||||
completeDir(0, prefix);
|
||||
Args::completeDir(completions, 0, prefix);
|
||||
|
||||
/* Look for registry entries that match the prefix. */
|
||||
for (auto & registry : fetchers::getRegistries(store)) {
|
||||
@ -370,10 +408,10 @@ void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -447,10 +485,12 @@ Installables SourceExprCommand::parseInstallables(
|
||||
auto e = state->parseStdin();
|
||||
state->eval(e, *vFile);
|
||||
}
|
||||
else if (file)
|
||||
state->evalFile(lookupFileArg(*state, *file), *vFile);
|
||||
else if (file) {
|
||||
state->evalFile(lookupFileArg(*state, *file, CanonPath::fromCwd(getCommandBaseDir())), *vFile);
|
||||
}
|
||||
else {
|
||||
auto e = state->parseExprFromString(*expr, state->rootPath(CanonPath::fromCwd()));
|
||||
CanonPath dir(CanonPath::fromCwd(getCommandBaseDir()));
|
||||
auto e = state->parseExprFromString(*expr, state->rootPath(dir));
|
||||
state->eval(e, *vFile);
|
||||
}
|
||||
|
||||
@ -485,7 +525,7 @@ Installables SourceExprCommand::parseInstallables(
|
||||
}
|
||||
|
||||
try {
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath("."));
|
||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath(getCommandBaseDir()));
|
||||
result.push_back(make_ref<InstallableFlake>(
|
||||
this,
|
||||
getEvalState(),
|
||||
@ -669,7 +709,7 @@ BuiltPaths Installable::toBuiltPaths(
|
||||
|
||||
BuiltPaths res;
|
||||
for (auto & drvPath : Installable::toDerivations(store, installables, true))
|
||||
res.push_back(BuiltPath::Opaque{drvPath});
|
||||
res.emplace_back(BuiltPath::Opaque{drvPath});
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@ -739,9 +779,7 @@ RawInstallablesCommand::RawInstallablesCommand()
|
||||
expectArgs({
|
||||
.label = "installables",
|
||||
.handler = {&rawInstallables},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
completeInstallable(prefix);
|
||||
}}
|
||||
.completer = getCompleteInstallable(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -754,6 +792,17 @@ 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)) {
|
||||
@ -767,10 +816,13 @@ void RawInstallablesCommand::run(ref<Store> store)
|
||||
run(store, std::move(rawInstallables));
|
||||
}
|
||||
|
||||
std::vector<std::string> RawInstallablesCommand::getFlakesForCompletion()
|
||||
std::vector<FlakeRef> InstallableCommand::getFlakeRefsForCompletion()
|
||||
{
|
||||
applyDefaultInstallables(rawInstallables);
|
||||
return rawInstallables;
|
||||
return {
|
||||
parseFlakeRefWithFragment(
|
||||
expandTilde(_installable),
|
||||
absPath(getCommandBaseDir())).first
|
||||
};
|
||||
}
|
||||
|
||||
void InstallablesCommand::run(ref<Store> store, std::vector<std::string> && rawInstallables)
|
||||
@ -786,9 +838,7 @@ InstallableCommand::InstallableCommand()
|
||||
.label = "installable",
|
||||
.optional = true,
|
||||
.handler = {&_installable},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
completeInstallable(prefix);
|
||||
}}
|
||||
.completer = getCompleteInstallable(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "util.hh"
|
||||
#include "path.hh"
|
||||
#include "outputs-spec.hh"
|
||||
#include "derived-path.hh"
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "markdown.hh"
|
||||
#include "util.hh"
|
||||
#include "finally.hh"
|
||||
#include "terminal.hh"
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <lowdown.h>
|
||||
|
@ -22,6 +22,7 @@ extern "C" {
|
||||
#include "repl.hh"
|
||||
|
||||
#include "ansicolor.hh"
|
||||
#include "signals.hh"
|
||||
#include "shared.hh"
|
||||
#include "eval.hh"
|
||||
#include "eval-cache.hh"
|
||||
@ -36,6 +37,8 @@ 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"
|
||||
@ -922,7 +925,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||
|
||||
case nString:
|
||||
str << ANSI_WARNING;
|
||||
printLiteralString(str, v.string.s);
|
||||
printLiteralString(str, v.string_view());
|
||||
str << ANSI_NORMAL;
|
||||
break;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include "attr-path.hh"
|
||||
#include "eval-inline.hh"
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
namespace nix {
|
||||
@ -132,7 +131,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 {CanonPath(fn.substr(0, colon)), lineno};
|
||||
return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno};
|
||||
} catch (std::invalid_argument & e) {
|
||||
fail();
|
||||
abort();
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "users.hh"
|
||||
#include "eval-cache.hh"
|
||||
#include "sqlite.hh"
|
||||
#include "eval.hh"
|
||||
@ -50,7 +51,7 @@ struct AttrDb
|
||||
Path cacheDir = getCacheDir() + "/nix/eval-cache-v5";
|
||||
createDirs(cacheDir);
|
||||
|
||||
Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite";
|
||||
Path dbPath = cacheDir + "/" + fingerprint.to_string(HashFormat::Base16, false) + ".sqlite";
|
||||
|
||||
state->db = SQLite(dbPath);
|
||||
state->db.isCache();
|
||||
@ -440,8 +441,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.string.s, v.string.context),
|
||||
string_t{v.string.s, {}}};
|
||||
cachedValue = {root->db->setString(getKey(), v.c_str(), v.context()),
|
||||
string_t{v.c_str(), {}}};
|
||||
else if (v.type() == nPath) {
|
||||
auto path = v.path().path;
|
||||
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
|
||||
@ -582,7 +583,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.string.s : v.path().to_string();
|
||||
return v.type() == nString ? v.c_str() : v.path().to_string();
|
||||
}
|
||||
|
||||
string_t AttrCursor::getStringWithContext()
|
||||
@ -624,7 +625,7 @@ string_t AttrCursor::getStringWithContext()
|
||||
if (v.type() == nString) {
|
||||
NixStringContext context;
|
||||
copyContext(v, context);
|
||||
return {v.string.s, std::move(context)};
|
||||
return {v.c_str(), std::move(context)};
|
||||
}
|
||||
else if (v.type() == nPath)
|
||||
return {v.path().to_string(), {}};
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "users.hh"
|
||||
#include "globals.hh"
|
||||
#include "profiles.hh"
|
||||
#include "eval.hh"
|
||||
|
@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "config.hh"
|
||||
|
||||
namespace nix {
|
||||
@ -29,10 +31,12 @@ struct EvalSettings : Config
|
||||
this, false, "restrict-eval",
|
||||
R"(
|
||||
If set to `true`, the Nix evaluator will not allow access to any
|
||||
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`.
|
||||
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.
|
||||
)"};
|
||||
|
||||
Setting<bool> pureEval{this, false, "pure-eval",
|
||||
@ -40,18 +44,22 @@ 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 [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
|
||||
- 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)
|
||||
)"
|
||||
};
|
||||
|
||||
Setting<bool> enableImportFromDerivation{
|
||||
this, true, "allow-import-from-derivation",
|
||||
R"(
|
||||
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.
|
||||
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.
|
||||
)"};
|
||||
|
||||
Setting<Strings> allowedUris{this, {}, "allowed-uris",
|
||||
@ -60,6 +68,11 @@ struct EvalSettings : Config
|
||||
evaluation mode. For example, when set to
|
||||
`https://github.com/NixOS`, builtin functions such as `fetchGit` are
|
||||
allowed to access `https://github.com/NixOS/patchelf.git`.
|
||||
|
||||
Access is granted when
|
||||
- the URI is equal to the prefix,
|
||||
- or the URI is a subpath of the prefix,
|
||||
- or the prefix is a URI scheme ended by a colon `:` and the URI has the same scheme.
|
||||
)"};
|
||||
|
||||
Setting<bool> traceFunctionCalls{this, false, "trace-function-calls",
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "eval.hh"
|
||||
#include "eval-settings.hh"
|
||||
#include "hash.hh"
|
||||
#include "primops.hh"
|
||||
#include "types.hh"
|
||||
#include "util.hh"
|
||||
#include "store-api.hh"
|
||||
@ -12,6 +13,10 @@
|
||||
#include "function-trace.hh"
|
||||
#include "profiles.hh"
|
||||
#include "print.hh"
|
||||
#include "fs-input-accessor.hh"
|
||||
#include "memory-input-accessor.hh"
|
||||
#include "signals.hh"
|
||||
#include "url.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
@ -114,7 +119,7 @@ void Value::print(const SymbolTable &symbols, std::ostream &str,
|
||||
printLiteralBool(str, boolean);
|
||||
break;
|
||||
case tString:
|
||||
printLiteralString(str, string.s);
|
||||
printLiteralString(str, string_view());
|
||||
break;
|
||||
case tPath:
|
||||
str << path().to_string(); // !!! escaping?
|
||||
@ -339,7 +344,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.s);
|
||||
return state.symbols.create(nameValue.string_view());
|
||||
}
|
||||
}
|
||||
|
||||
@ -503,7 +508,17 @@ EvalState::EvalState(
|
||||
, sOutputSpecified(symbols.create("outputSpecified"))
|
||||
, repair(NoRepair)
|
||||
, emptyBindings(0)
|
||||
, derivationInternal(rootPath(CanonPath("/builtin/derivation.nix")))
|
||||
, 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"
|
||||
)}
|
||||
, store(store)
|
||||
, buildStore(buildStore ? buildStore : store)
|
||||
, debugRepl(nullptr)
|
||||
@ -539,7 +554,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 {
|
||||
@ -555,6 +570,11 @@ EvalState::EvalState(
|
||||
}
|
||||
}
|
||||
|
||||
corepkgsFS->addFile(
|
||||
CanonPath("fetchurl.nix"),
|
||||
#include "fetchurl.nix.gen.hh"
|
||||
);
|
||||
|
||||
createBaseEnv();
|
||||
}
|
||||
|
||||
@ -583,8 +603,20 @@ void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value &
|
||||
mkStorePathString(storePath, v);
|
||||
}
|
||||
|
||||
inline static bool isJustSchemePrefix(std::string_view prefix)
|
||||
{
|
||||
return
|
||||
!prefix.empty()
|
||||
&& prefix[prefix.size() - 1] == ':'
|
||||
&& isValidSchemeName(prefix.substr(0, prefix.size() - 1));
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
@ -599,8 +631,6 @@ 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;
|
||||
@ -617,7 +647,7 @@ SourcePath EvalState::checkSourcePath(const SourcePath & path_)
|
||||
|
||||
/* Resolve symlinks. */
|
||||
debug("checking access to '%s'", abspath);
|
||||
SourcePath path = CanonPath(canonPath(abspath, true));
|
||||
SourcePath path = rootPath(CanonPath(canonPath(abspath, true)));
|
||||
|
||||
for (auto & i : *allowedPaths) {
|
||||
if (isDirOrInDir(path.path.abs(), i)) {
|
||||
@ -630,31 +660,47 @@ SourcePath EvalState::checkSourcePath(const SourcePath & path_)
|
||||
}
|
||||
|
||||
|
||||
bool isAllowedURI(std::string_view uri, const Strings & allowedUris)
|
||||
{
|
||||
/* 'uri' should be equal to a prefix, or in a subdirectory of a
|
||||
prefix. Thus, the prefix https://github.co does not permit
|
||||
access to https://github.com. */
|
||||
for (auto & prefix : allowedUris) {
|
||||
if (uri == prefix
|
||||
// Allow access to subdirectories of the prefix.
|
||||
|| (uri.size() > prefix.size()
|
||||
&& prefix.size() > 0
|
||||
&& hasPrefix(uri, prefix)
|
||||
&& (
|
||||
// Allow access to subdirectories of the prefix.
|
||||
prefix[prefix.size() - 1] == '/'
|
||||
|| uri[prefix.size()] == '/'
|
||||
|
||||
// Allow access to whole schemes
|
||||
|| isJustSchemePrefix(prefix)
|
||||
)
|
||||
))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EvalState::checkURI(const std::string & uri)
|
||||
{
|
||||
if (!evalSettings.restrictEval) return;
|
||||
|
||||
/* 'uri' should be equal to a prefix, or in a subdirectory of a
|
||||
prefix. Thus, the prefix https://github.co does not permit
|
||||
access to https://github.com. Note: this allows 'http://' and
|
||||
'https://' as prefixes for any http/https URI. */
|
||||
for (auto & prefix : evalSettings.allowedUris.get())
|
||||
if (uri == prefix ||
|
||||
(uri.size() > prefix.size()
|
||||
&& prefix.size() > 0
|
||||
&& hasPrefix(uri, prefix)
|
||||
&& (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/')))
|
||||
return;
|
||||
if (isAllowedURI(uri, evalSettings.allowedUris.get())) return;
|
||||
|
||||
/* If the URI is a path, then check it against allowedPaths as
|
||||
well. */
|
||||
if (hasPrefix(uri, "/")) {
|
||||
checkSourcePath(CanonPath(uri));
|
||||
checkSourcePath(rootPath(CanonPath(uri)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasPrefix(uri, "file://")) {
|
||||
checkSourcePath(CanonPath(std::string(uri, 7)));
|
||||
checkSourcePath(rootPath(CanonPath(std::string(uri, 7))));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -703,6 +749,23 @@ 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
|
||||
@ -950,7 +1013,7 @@ void Value::mkStringMove(const char * s, const NixStringContext & context)
|
||||
|
||||
void Value::mkPath(const SourcePath & path)
|
||||
{
|
||||
mkPath(makeImmutableString(path.path.abs()));
|
||||
mkPath(&*path.accessor, makeImmutableString(path.path.abs()));
|
||||
}
|
||||
|
||||
|
||||
@ -1035,7 +1098,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();
|
||||
@ -1165,24 +1228,6 @@ 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 {
|
||||
@ -1211,6 +1256,13 @@ void EvalState::cacheFile(
|
||||
}
|
||||
|
||||
|
||||
void EvalState::resetFileCache()
|
||||
{
|
||||
fileEvalCache.clear();
|
||||
fileParseCache.clear();
|
||||
}
|
||||
|
||||
|
||||
void EvalState::eval(Expr * e, Value & v)
|
||||
{
|
||||
e->eval(*this, baseEnv, v);
|
||||
@ -1343,7 +1395,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.s);
|
||||
auto nameSym = state.symbols.create(nameVal.string_view());
|
||||
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>();
|
||||
@ -1740,6 +1792,12 @@ 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);
|
||||
@ -2037,7 +2095,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(CanonPath(canonPath(str())));
|
||||
v.mkPath(state.rootPath(CanonPath(canonPath(str()))));
|
||||
} else
|
||||
v.mkStringMove(c_str(), context);
|
||||
}
|
||||
@ -2155,7 +2213,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.s;
|
||||
return v.string_view();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
@ -2182,8 +2240,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.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>();
|
||||
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>();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@ -2196,7 +2254,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 strcmp(i->value->string.s, "derivation") == 0;
|
||||
return i->value->string_view().compare("derivation") == 0;
|
||||
}
|
||||
|
||||
|
||||
@ -2228,7 +2286,7 @@ BackedStringView EvalState::coerceToString(
|
||||
|
||||
if (v.type() == nString) {
|
||||
copyContext(v, context);
|
||||
return std::string_view(v.string.s);
|
||||
return v.string_view();
|
||||
}
|
||||
|
||||
if (v.type() == nPath) {
|
||||
@ -2236,7 +2294,7 @@ BackedStringView EvalState::coerceToString(
|
||||
!canonicalizePath && !copyToStore
|
||||
? // FIXME: hack to preserve path literals that end in a
|
||||
// slash, as in /foo/${x}.
|
||||
v._path
|
||||
v._path.path
|
||||
: copyToStore
|
||||
? store->printStorePath(copyPathToStore(context, v.path()))
|
||||
: std::string(v.path().path.abs());
|
||||
@ -2290,7 +2348,7 @@ BackedStringView EvalState::coerceToString(
|
||||
&& (!v2->isList() || v2->listSize() != 0))
|
||||
result += " ";
|
||||
}
|
||||
return std::move(result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2310,7 +2368,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
|
||||
auto dstPath = i != srcToStore.end()
|
||||
? i->second
|
||||
: [&]() {
|
||||
auto dstPath = path.fetchToStore(store, path.baseName(), nullptr, repair);
|
||||
auto dstPath = path.fetchToStore(store, path.baseName(), FileIngestionMethod::Recursive, nullptr, repair);
|
||||
allowPath(dstPath);
|
||||
srcToStore.insert_or_assign(path, dstPath);
|
||||
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath));
|
||||
@ -2326,10 +2384,34 @@ 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 CanonPath(path);
|
||||
return rootPath(CanonPath(path));
|
||||
}
|
||||
|
||||
|
||||
@ -2426,10 +2508,13 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
||||
return v1.boolean == v2.boolean;
|
||||
|
||||
case nString:
|
||||
return strcmp(v1.string.s, v2.string.s) == 0;
|
||||
return v1.string_view().compare(v2.string_view()) == 0;
|
||||
|
||||
case nPath:
|
||||
return strcmp(v1._path, v2._path) == 0;
|
||||
return
|
||||
// FIXME: compare accessors by their fingerprint.
|
||||
v1._path.accessor == v2._path.accessor
|
||||
&& strcmp(v1._path.path, v2._path.path) == 0;
|
||||
|
||||
case nNull:
|
||||
return true;
|
||||
@ -2477,10 +2562,37 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
||||
}
|
||||
}
|
||||
|
||||
void EvalState::printStats()
|
||||
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()
|
||||
{
|
||||
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);
|
||||
@ -2494,105 +2606,105 @@ void EvalState::printStats()
|
||||
GC_word heapSize, totalBytes;
|
||||
GC_get_heap_usage_safe(&heapSize, 0, 0, 0, &totalBytes);
|
||||
#endif
|
||||
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;
|
||||
|
||||
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 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;
|
||||
}
|
||||
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 (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,12 +18,20 @@
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
@ -69,6 +77,12 @@ 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();
|
||||
};
|
||||
|
||||
/**
|
||||
@ -211,8 +225,26 @@ 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.
|
||||
*/
|
||||
@ -223,7 +255,6 @@ public:
|
||||
*/
|
||||
const ref<Store> buildStore;
|
||||
|
||||
RootValue vCallFlake = nullptr;
|
||||
RootValue vImportedDrvToDerivation = nullptr;
|
||||
|
||||
/**
|
||||
@ -405,16 +436,6 @@ 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();
|
||||
|
||||
/**
|
||||
@ -424,7 +445,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 optinal key part)
|
||||
* Try to resolve a search path value (not the optional key part)
|
||||
*
|
||||
* If the specified search path element is a URI, download it.
|
||||
*
|
||||
@ -709,9 +730,25 @@ public:
|
||||
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx);
|
||||
|
||||
/**
|
||||
* Print statistics.
|
||||
* Print statistics, if enabled.
|
||||
*
|
||||
* Performs a full memory GC before printing the statistics, so that the
|
||||
* GC statistics are more accurate.
|
||||
*/
|
||||
void printStats();
|
||||
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();
|
||||
|
||||
/**
|
||||
* Realise the given context, and return a mapping from the placeholders
|
||||
@ -802,7 +839,12 @@ std::string showType(const Value & v);
|
||||
/**
|
||||
* If `path` refers to a directory, then append "/default.nix".
|
||||
*/
|
||||
SourcePath resolveExprPath(const SourcePath & path);
|
||||
SourcePath resolveExprPath(SourcePath path);
|
||||
|
||||
/**
|
||||
* Whether a URI is allowed, assuming restrictEval is enabled
|
||||
*/
|
||||
bool isAllowedURI(std::string_view uri, const Strings & allowedPaths);
|
||||
|
||||
struct InvalidPathError : EvalError
|
||||
{
|
||||
@ -813,8 +855,6 @@ struct InvalidPathError : EvalError
|
||||
#endif
|
||||
};
|
||||
|
||||
static const std::string corepkgsPrefix{"/__corepkgs__/"};
|
||||
|
||||
template<class ErrorType>
|
||||
void ErrorBuilder::debugThrow()
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "flake.hh"
|
||||
#include "users.hh"
|
||||
#include "globals.hh"
|
||||
#include "fetch-settings.hh"
|
||||
#include "flake.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "terminal.hh"
|
||||
#include "flake.hh"
|
||||
#include "eval.hh"
|
||||
#include "eval-settings.hh"
|
||||
@ -8,6 +9,7 @@
|
||||
#include "fetchers.hh"
|
||||
#include "finally.hh"
|
||||
#include "fetch-settings.hh"
|
||||
#include "value-to-json.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@ -15,7 +17,7 @@ using namespace flake;
|
||||
|
||||
namespace flake {
|
||||
|
||||
typedef std::pair<fetchers::Tree, FlakeRef> FetchedFlake;
|
||||
typedef std::pair<StorePath, FlakeRef> FetchedFlake;
|
||||
typedef std::vector<std::pair<FlakeRef, FetchedFlake>> FlakeCache;
|
||||
|
||||
static std::optional<FetchedFlake> lookupInFlakeCache(
|
||||
@ -34,7 +36,7 @@ static std::optional<FetchedFlake> lookupInFlakeCache(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||
static std::tuple<StorePath, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||
EvalState & state,
|
||||
const FlakeRef & originalRef,
|
||||
bool allowLookup,
|
||||
@ -61,16 +63,16 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||
flakeCache.push_back({originalRef, *fetched});
|
||||
}
|
||||
|
||||
auto [tree, lockedRef] = *fetched;
|
||||
auto [storePath, lockedRef] = *fetched;
|
||||
|
||||
debug("got tree '%s' from '%s'",
|
||||
state.store->printStorePath(tree.storePath), lockedRef);
|
||||
state.store->printStorePath(storePath), lockedRef);
|
||||
|
||||
state.allowPath(tree.storePath);
|
||||
state.allowPath(storePath);
|
||||
|
||||
assert(!originalRef.input.getNarHash() || tree.storePath == originalRef.input.computeStorePath(*state.store));
|
||||
assert(!originalRef.input.getNarHash() || storePath == originalRef.input.computeStorePath(*state.store));
|
||||
|
||||
return {std::move(tree), resolvedRef, lockedRef};
|
||||
return {std::move(storePath), resolvedRef, lockedRef};
|
||||
}
|
||||
|
||||
static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
|
||||
@ -113,7 +115,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
try {
|
||||
if (attr.name == sUrl) {
|
||||
expectType(state, nString, *attr.value, attr.pos);
|
||||
url = attr.value->string.s;
|
||||
url = attr.value->string_view();
|
||||
attrs.emplace("url", *url);
|
||||
} else if (attr.name == sFlake) {
|
||||
expectType(state, nBool, *attr.value, attr.pos);
|
||||
@ -122,7 +124,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->string.s));
|
||||
auto follows(parseInputPath(attr.value->c_str()));
|
||||
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
||||
input.follows = follows;
|
||||
} else {
|
||||
@ -131,7 +133,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->string.s);
|
||||
attrs.emplace(state.symbols[attr.name], attr.value->c_str());
|
||||
break;
|
||||
case nBool:
|
||||
attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean });
|
||||
@ -140,8 +142,13 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||
attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
|
||||
break;
|
||||
default:
|
||||
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||
state.symbols[attr.name], showType(*attr.value));
|
||||
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));
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
@ -202,34 +209,34 @@ static Flake getFlake(
|
||||
FlakeCache & flakeCache,
|
||||
InputPath lockRootPath)
|
||||
{
|
||||
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||
state, originalRef, allowLookup, flakeCache);
|
||||
|
||||
// Guard against symlink attacks.
|
||||
auto flakeDir = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir, true);
|
||||
auto flakeDir = canonPath(state.store->toRealPath(storePath) + "/" + lockedRef.subdir, true);
|
||||
auto flakeFile = canonPath(flakeDir + "/flake.nix", true);
|
||||
if (!isInDir(flakeFile, sourceInfo.actualPath))
|
||||
if (!isInDir(flakeFile, state.store->toRealPath(storePath)))
|
||||
throw Error("'flake.nix' file of flake '%s' escapes from '%s'",
|
||||
lockedRef, state.store->printStorePath(sourceInfo.storePath));
|
||||
lockedRef, state.store->printStorePath(storePath));
|
||||
|
||||
Flake flake {
|
||||
.originalRef = originalRef,
|
||||
.resolvedRef = resolvedRef,
|
||||
.lockedRef = lockedRef,
|
||||
.sourceInfo = std::make_shared<fetchers::Tree>(std::move(sourceInfo))
|
||||
.storePath = storePath,
|
||||
};
|
||||
|
||||
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(CanonPath(flakeFile), vInfo, true); // FIXME: symlink attack
|
||||
state.evalFile(state.rootPath(CanonPath(flakeFile)), vInfo, true); // FIXME: symlink attack
|
||||
|
||||
expectType(state, nAttrs, vInfo, state.positions.add({CanonPath(flakeFile)}, 1, 1));
|
||||
expectType(state, nAttrs, vInfo, state.positions.add({state.rootPath(CanonPath(flakeFile))}, 1, 1));
|
||||
|
||||
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||
expectType(state, nString, *description->value, description->pos);
|
||||
flake.description = description->value->string.s;
|
||||
flake.description = description->value->c_str();
|
||||
}
|
||||
|
||||
auto sInputs = state.symbols.create("inputs");
|
||||
@ -346,7 +353,7 @@ LockedFlake lockFlake(
|
||||
// FIXME: symlink attack
|
||||
auto oldLockFile = LockFile::read(
|
||||
lockFlags.referenceLockFilePath.value_or(
|
||||
flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir + "/flake.lock"));
|
||||
state.store->toRealPath(flake.storePath) + "/" + flake.lockedRef.subdir + "/flake.lock"));
|
||||
|
||||
debug("old lock file: %s", oldLockFile);
|
||||
|
||||
@ -447,8 +454,8 @@ LockedFlake lockFlake(
|
||||
|
||||
assert(input.ref);
|
||||
|
||||
/* Do we have an entry in the existing lock file? And we
|
||||
don't have a --update-input flag for this input? */
|
||||
/* Do we have an entry in the existing lock file?
|
||||
And the input is not in updateInputs? */
|
||||
std::shared_ptr<LockedNode> oldLock;
|
||||
|
||||
updatesUsed.insert(inputPath);
|
||||
@ -472,9 +479,8 @@ LockedFlake lockFlake(
|
||||
|
||||
node->inputs.insert_or_assign(id, childNode);
|
||||
|
||||
/* If we have an --update-input flag for an input
|
||||
of this input, then we must fetch the flake to
|
||||
update it. */
|
||||
/* If we have this input in updateInputs, then we
|
||||
must fetch the flake to update it. */
|
||||
auto lb = lockFlags.inputUpdates.lower_bound(inputPath);
|
||||
|
||||
auto mustRefetch =
|
||||
@ -574,7 +580,7 @@ LockedFlake lockFlake(
|
||||
oldLock
|
||||
? std::dynamic_pointer_cast<const Node>(oldLock)
|
||||
: LockFile::read(
|
||||
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root.get_ptr(),
|
||||
state.store->toRealPath(inputFlake.storePath) + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root.get_ptr(),
|
||||
oldLock ? lockRootPath : inputPath,
|
||||
localPath,
|
||||
false);
|
||||
@ -598,7 +604,7 @@ LockedFlake lockFlake(
|
||||
};
|
||||
|
||||
// Bring in the current ref for relative path resolution if we have it
|
||||
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
|
||||
auto parentPath = canonPath(state.store->toRealPath(flake.storePath) + "/" + flake.lockedRef.subdir, true);
|
||||
|
||||
computeLocks(
|
||||
flake.inputs,
|
||||
@ -616,19 +622,14 @@ LockedFlake lockFlake(
|
||||
|
||||
for (auto & i : lockFlags.inputUpdates)
|
||||
if (!updatesUsed.count(i))
|
||||
warn("the flag '--update-input %s' does not match any input", printInputPath(i));
|
||||
warn("'%s' does not match any input of this flake", 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) {
|
||||
@ -636,7 +637,7 @@ LockedFlake lockFlake(
|
||||
auto diff = LockFile::diff(oldLockFile, newLockFile);
|
||||
|
||||
if (lockFlags.writeLockFile) {
|
||||
if (outputLockFilePath) {
|
||||
if (sourcePath || lockFlags.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);
|
||||
@ -644,41 +645,48 @@ 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);
|
||||
|
||||
bool lockFileExists = pathExists(*outputLockFilePath);
|
||||
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);
|
||||
|
||||
if (lockFileExists) {
|
||||
auto s = chomp(diff);
|
||||
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);
|
||||
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);
|
||||
|
||||
newLockFile.write(*outputLockFilePath);
|
||||
std::optional<std::string> commitMessage = std::nullopt;
|
||||
|
||||
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;
|
||||
if (lockFlags.commitLockFile) {
|
||||
std::string cm;
|
||||
|
||||
cm = fetchSettings.commitLockFileSummary.get();
|
||||
cm = fetchSettings.commitLockFileSummary.get();
|
||||
|
||||
if (cm == "") {
|
||||
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
|
||||
if (cm == "") {
|
||||
cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add");
|
||||
}
|
||||
|
||||
cm += "\n\nFlake lock file updates:\n\n";
|
||||
cm += filterANSIEscapes(diff, true);
|
||||
commitMessage = cm;
|
||||
}
|
||||
|
||||
cm += "\n\nFlake lock file updates:\n\n";
|
||||
cm += filterANSIEscapes(diff, true);
|
||||
commitMessage = cm;
|
||||
topRef.input.putFile(
|
||||
CanonPath((topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock"),
|
||||
newLockFileS, commitMessage);
|
||||
}
|
||||
|
||||
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... */
|
||||
@ -729,7 +737,7 @@ void callFlake(EvalState & state,
|
||||
|
||||
emitTreeAttrs(
|
||||
state,
|
||||
*lockedFlake.flake.sourceInfo,
|
||||
lockedFlake.flake.storePath,
|
||||
lockedFlake.flake.lockedRef.input,
|
||||
*vRootSrc,
|
||||
false,
|
||||
@ -737,14 +745,10 @@ void callFlake(EvalState & state,
|
||||
|
||||
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
|
||||
|
||||
if (!state.vCallFlake) {
|
||||
state.vCallFlake = allocRootValue(state.allocValue());
|
||||
state.eval(state.parseExprFromString(
|
||||
#include "call-flake.nix.gen.hh"
|
||||
, CanonPath::root), **state.vCallFlake);
|
||||
}
|
||||
auto vCallFlake = state.allocValue();
|
||||
state.evalFile(state.callFlakeInternal, *vCallFlake);
|
||||
|
||||
state.callFunction(**state.vCallFlake, *vLocks, *vTmp1, noPos);
|
||||
state.callFunction(*vCallFlake, *vLocks, *vTmp1, noPos);
|
||||
state.callFunction(*vTmp1, *vRootSrc, *vTmp2, noPos);
|
||||
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
|
||||
}
|
||||
@ -850,7 +854,7 @@ static void prim_flakeRefToString(
|
||||
Explicit<bool> { attr.value->boolean });
|
||||
} else if (t == nString) {
|
||||
attrs.emplace(state.symbols[attr.name],
|
||||
std::string(attr.value->str()));
|
||||
std::string(attr.value->string_view()));
|
||||
} else {
|
||||
state.error(
|
||||
"flake reference attribute sets may only contain integers, Booleans, "
|
||||
@ -893,7 +897,7 @@ Fingerprint LockedFlake::getFingerprint() const
|
||||
// flake.sourceInfo.storePath for the fingerprint.
|
||||
return hashString(htSHA256,
|
||||
fmt("%s;%s;%d;%d;%s",
|
||||
flake.sourceInfo->storePath.to_string(),
|
||||
flake.storePath.to_string(),
|
||||
flake.lockedRef.subdir,
|
||||
flake.lockedRef.input.getRevCount().value_or(0),
|
||||
flake.lockedRef.input.getLastModified().value_or(0),
|
||||
|
@ -10,8 +10,6 @@ namespace nix {
|
||||
|
||||
class EvalState;
|
||||
|
||||
namespace fetchers { struct Tree; }
|
||||
|
||||
namespace flake {
|
||||
|
||||
struct FlakeInput;
|
||||
@ -84,7 +82,7 @@ struct Flake
|
||||
*/
|
||||
bool forceDirty = false;
|
||||
std::optional<std::string> description;
|
||||
std::shared_ptr<const fetchers::Tree> sourceInfo;
|
||||
StorePath storePath;
|
||||
FlakeInputs inputs;
|
||||
/**
|
||||
* 'nixConfig' attribute
|
||||
@ -193,7 +191,7 @@ void callFlake(
|
||||
|
||||
void emitTreeAttrs(
|
||||
EvalState & state,
|
||||
const fetchers::Tree & tree,
|
||||
const StorePath & storePath,
|
||||
const fetchers::Input & input,
|
||||
Value & v,
|
||||
bool emptyRevFallback = false,
|
||||
|
@ -69,32 +69,130 @@ std::optional<FlakeRef> maybeParseFlakeRef(
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
std::pair<FlakeRef, std::string> parsePathFlakeRefWithFragment(
|
||||
const std::string & url,
|
||||
const std::optional<Path> & baseDir,
|
||||
bool allowMissing,
|
||||
bool isFlake)
|
||||
{
|
||||
using namespace fetchers;
|
||||
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-pathEnd-1));
|
||||
}
|
||||
|
||||
static std::string fnRegex = "[0-9a-zA-Z-._~!$&'\"()*+,;=]+";
|
||||
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::regex pathUrlRegex(
|
||||
"(/?" + fnRegex + "(?:/" + fnRegex + ")*/?)"
|
||||
+ "(?:\\?(" + queryRegex + "))?"
|
||||
+ "(?:#(" + queryRegex + "))?",
|
||||
std::regex::ECMAScript);
|
||||
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 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,
|
||||
@ -105,111 +203,53 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||
};
|
||||
|
||||
return std::make_pair(
|
||||
FlakeRef(Input::fromURL(parsedURL, isFlake), ""),
|
||||
FlakeRef(fetchers::Input::fromURL(parsedURL, isFlake), ""),
|
||||
percentDecode(match.str(6)));
|
||||
}
|
||||
|
||||
else if (std::regex_match(url, match, pathUrlRegex)) {
|
||||
std::string path = match[1];
|
||||
std::string fragment = percentDecode(match.str(3));
|
||||
return {};
|
||||
}
|
||||
|
||||
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::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;
|
||||
}
|
||||
|
||||
else {
|
||||
auto parsedURL = parseURL(url);
|
||||
std::string fragment;
|
||||
std::swap(fragment, parsedURL.fragment);
|
||||
std::string fragment;
|
||||
std::swap(fragment, parsedURL.fragment);
|
||||
|
||||
auto input = Input::fromURL(parsedURL, isFlake);
|
||||
input.parent = baseDir;
|
||||
auto input = fetchers::Input::fromURL(parsedURL, isFlake);
|
||||
input.parent = baseDir;
|
||||
|
||||
return std::make_pair(
|
||||
FlakeRef(std::move(input), getOr(parsedURL.query, "dir", "")),
|
||||
fragment);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,10 +272,10 @@ FlakeRef FlakeRef::fromAttrs(const fetchers::Attrs & attrs)
|
||||
fetchers::maybeGetStrAttr(attrs, "dir").value_or(""));
|
||||
}
|
||||
|
||||
std::pair<fetchers::Tree, FlakeRef> FlakeRef::fetchTree(ref<Store> store) const
|
||||
std::pair<StorePath, FlakeRef> FlakeRef::fetchTree(ref<Store> store) const
|
||||
{
|
||||
auto [tree, lockedInput] = input.fetch(store);
|
||||
return {std::move(tree), FlakeRef(std::move(lockedInput), subdir)};
|
||||
auto [storePath, lockedInput] = input.fetch(store);
|
||||
return {std::move(storePath), FlakeRef(std::move(lockedInput), subdir)};
|
||||
}
|
||||
|
||||
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(
|
||||
@ -249,4 +289,6 @@ std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragment
|
||||
return {std::move(flakeRef), fragment, std::move(extendedOutputsSpec)};
|
||||
}
|
||||
|
||||
std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript);
|
||||
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user