mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-23 22:46:16 +02:00
Merge remote-tracking branch 'origin/master' into tarball-cache
This commit is contained in:
commit
cf02b3335c
125 changed files with 1045 additions and 937 deletions
10
.github/CODEOWNERS
vendored
10
.github/CODEOWNERS
vendored
|
@ -10,16 +10,8 @@
|
||||||
# This file
|
# This file
|
||||||
.github/CODEOWNERS @edolstra
|
.github/CODEOWNERS @edolstra
|
||||||
|
|
||||||
# Public documentation
|
|
||||||
/doc @fricklerhandwerk
|
|
||||||
*.md @fricklerhandwerk
|
|
||||||
|
|
||||||
# Documentation of built-in functions
|
# Documentation of built-in functions
|
||||||
src/libexpr/primops.cc @fricklerhandwerk @roberth
|
src/libexpr/primops.cc @roberth
|
||||||
# Documentation on experimental features
|
|
||||||
src/libutil/experimental-features.cc @fricklerhandwerk
|
|
||||||
# Documentation on configuration settings
|
|
||||||
src/libstore/globals.hh @fricklerhandwerk
|
|
||||||
|
|
||||||
# Libstore layer
|
# Libstore layer
|
||||||
/src/libstore @thufschmitt
|
/src/libstore @thufschmitt
|
||||||
|
|
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
<!-- Large change: Provide instructions to reviewers how to read the diff. -->
|
<!-- Large change: Provide instructions to reviewers how to read the diff. -->
|
||||||
|
|
||||||
# Priorities
|
# Priorities and Process
|
||||||
|
|
||||||
Add :+1: to [pull requests you find important](https://github.com/NixOS/nix/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc).
|
Add :+1: to [pull requests you find important](https://github.com/NixOS/nix/pulls?q=is%3Aopen+sort%3Areactions-%2B1-desc).
|
||||||
|
|
||||||
|
The Nix maintainer team uses a [GitHub project board](https://github.com/orgs/NixOS/projects/19) to [schedule and track reviews](https://github.com/NixOS/nix/tree/master/maintainers#project-board-protocol).
|
||||||
|
|
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Create backport PRs
|
- name: Create backport PRs
|
||||||
# should be kept in sync with `version`
|
# should be kept in sync with `version`
|
||||||
uses: zeebe-io/backport-action@v2.2.0
|
uses: zeebe-io/backport-action@v2.3.0
|
||||||
with:
|
with:
|
||||||
# Config README: https://github.com/zeebe-io/backport-action#backport-action
|
# Config README: https://github.com/zeebe-io/backport-action#backport-action
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
38
Makefile
38
Makefile
|
@ -24,7 +24,7 @@ makefiles = \
|
||||||
misc/upstart/local.mk
|
misc/upstart/local.mk
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_BUILD)_$(ENABLE_TESTS), yes_yes)
|
ifeq ($(ENABLE_UNIT_TESTS), yes)
|
||||||
makefiles += \
|
makefiles += \
|
||||||
tests/unit/libutil/local.mk \
|
tests/unit/libutil/local.mk \
|
||||||
tests/unit/libutil-support/local.mk \
|
tests/unit/libutil-support/local.mk \
|
||||||
|
@ -34,16 +34,13 @@ makefiles += \
|
||||||
tests/unit/libexpr-support/local.mk
|
tests/unit/libexpr-support/local.mk
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_TESTS), yes)
|
ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes)
|
||||||
makefiles += \
|
makefiles += \
|
||||||
tests/functional/local.mk \
|
tests/functional/local.mk \
|
||||||
tests/functional/ca/local.mk \
|
tests/functional/ca/local.mk \
|
||||||
tests/functional/dyn-drv/local.mk \
|
tests/functional/dyn-drv/local.mk \
|
||||||
tests/functional/test-libstoreconsumer/local.mk \
|
tests/functional/test-libstoreconsumer/local.mk \
|
||||||
tests/functional/plugins/local.mk
|
tests/functional/plugins/local.mk
|
||||||
else
|
|
||||||
makefiles += \
|
|
||||||
mk/disable-tests.mk
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OPTIMIZE = 1
|
OPTIMIZE = 1
|
||||||
|
@ -57,11 +54,40 @@ endif
|
||||||
|
|
||||||
include mk/lib.mk
|
include mk/lib.mk
|
||||||
|
|
||||||
|
# Must be included after `mk/lib.mk` so isn't the default target.
|
||||||
|
ifneq ($(ENABLE_UNIT_TESTS), yes)
|
||||||
|
.PHONY: check
|
||||||
|
check:
|
||||||
|
@echo "Unit tests are disabled. Configure without '--disable-unit-tests', or avoid calling 'make check'."
|
||||||
|
@exit 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(ENABLE_FUNCTIONAL_TESTS), yes)
|
||||||
|
.PHONY: installcheck
|
||||||
|
installcheck:
|
||||||
|
@echo "Functional tests are disabled. Configure without '--disable-functional-tests', or avoid calling 'make installcheck'."
|
||||||
|
@exit 1
|
||||||
|
endif
|
||||||
|
|
||||||
# Must be included after `mk/lib.mk` so rules refer to variables defined
|
# Must be included after `mk/lib.mk` so rules refer to variables defined
|
||||||
# by the library. Rules are not "lazy" like variables, unfortunately.
|
# by the library. Rules are not "lazy" like variables, unfortunately.
|
||||||
ifeq ($(ENABLE_BUILD), yes)
|
|
||||||
|
ifeq ($(ENABLE_DOC_GEN), yes)
|
||||||
$(eval $(call include-sub-makefile, doc/manual/local.mk))
|
$(eval $(call include-sub-makefile, doc/manual/local.mk))
|
||||||
|
else
|
||||||
|
.PHONY: manual-html manpages
|
||||||
|
manual-html manpages:
|
||||||
|
@echo "Generated docs are disabled. Configure without '--disable-doc-gen', or avoid calling 'make manpages' and 'make manual-html'."
|
||||||
|
@exit 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_INTERNAL_API_DOCS), yes)
|
||||||
$(eval $(call include-sub-makefile, doc/internal-api/local.mk))
|
$(eval $(call include-sub-makefile, doc/internal-api/local.mk))
|
||||||
|
else
|
||||||
|
.PHONY: internal-api-html
|
||||||
|
internal-api-html:
|
||||||
|
@echo "Internal API docs are disabled. Configure with '--enable-internal-api-docs', or avoid calling 'make internal-api-html'."
|
||||||
|
@exit 1
|
||||||
|
endif
|
||||||
|
|
||||||
GLOBAL_CXXFLAGS += -g -Wall -include $(buildprefix)config.h -std=c++2a -I src
|
GLOBAL_CXXFLAGS += -g -Wall -include $(buildprefix)config.h -std=c++2a -I src
|
||||||
|
|
|
@ -9,8 +9,11 @@ CXXFLAGS = @CXXFLAGS@
|
||||||
CXXLTO = @CXXLTO@
|
CXXLTO = @CXXLTO@
|
||||||
EDITLINE_LIBS = @EDITLINE_LIBS@
|
EDITLINE_LIBS = @EDITLINE_LIBS@
|
||||||
ENABLE_BUILD = @ENABLE_BUILD@
|
ENABLE_BUILD = @ENABLE_BUILD@
|
||||||
|
ENABLE_DOC_GEN = @ENABLE_DOC_GEN@
|
||||||
|
ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@
|
||||||
|
ENABLE_INTERNAL_API_DOCS = @ENABLE_INTERNAL_API_DOCS@
|
||||||
ENABLE_S3 = @ENABLE_S3@
|
ENABLE_S3 = @ENABLE_S3@
|
||||||
ENABLE_TESTS = @ENABLE_TESTS@
|
ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@
|
||||||
GTEST_LIBS = @GTEST_LIBS@
|
GTEST_LIBS = @GTEST_LIBS@
|
||||||
HAVE_LIBCPUID = @HAVE_LIBCPUID@
|
HAVE_LIBCPUID = @HAVE_LIBCPUID@
|
||||||
HAVE_SECCOMP = @HAVE_SECCOMP@
|
HAVE_SECCOMP = @HAVE_SECCOMP@
|
||||||
|
@ -36,12 +39,10 @@ checkbindir = @checkbindir@
|
||||||
checklibdir = @checklibdir@
|
checklibdir = @checklibdir@
|
||||||
datadir = @datadir@
|
datadir = @datadir@
|
||||||
datarootdir = @datarootdir@
|
datarootdir = @datarootdir@
|
||||||
doc_generate = @doc_generate@
|
|
||||||
docdir = @docdir@
|
docdir = @docdir@
|
||||||
embedded_sandbox_shell = @embedded_sandbox_shell@
|
embedded_sandbox_shell = @embedded_sandbox_shell@
|
||||||
exec_prefix = @exec_prefix@
|
exec_prefix = @exec_prefix@
|
||||||
includedir = @includedir@
|
includedir = @includedir@
|
||||||
internal_api_docs = @internal_api_docs@
|
|
||||||
libdir = @libdir@
|
libdir = @libdir@
|
||||||
libexecdir = @libexecdir@
|
libexecdir = @libexecdir@
|
||||||
localstatedir = @localstatedir@
|
localstatedir = @localstatedir@
|
||||||
|
|
43
configure.ac
43
configure.ac
|
@ -138,20 +138,38 @@ AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]),
|
||||||
ENABLE_BUILD=$enableval, ENABLE_BUILD=yes)
|
ENABLE_BUILD=$enableval, ENABLE_BUILD=yes)
|
||||||
AC_SUBST(ENABLE_BUILD)
|
AC_SUBST(ENABLE_BUILD)
|
||||||
|
|
||||||
# Building without tests is useful for bootstrapping with a smaller footprint
|
# Building without unit tests is useful for bootstrapping with a smaller footprint
|
||||||
# or running the tests in a separate derivation. Otherwise, we do compile and
|
# or running the tests in a separate derivation. Otherwise, we do compile and
|
||||||
# run them.
|
# run them.
|
||||||
AC_ARG_ENABLE(tests, AS_HELP_STRING([--disable-tests],[Do not build the tests]),
|
|
||||||
ENABLE_TESTS=$enableval, ENABLE_TESTS=yes)
|
|
||||||
AC_SUBST(ENABLE_TESTS)
|
|
||||||
|
|
||||||
# Building without API docs is the default as Nix' C++ interfaces are internal and unstable.
|
AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build the tests]),
|
||||||
AC_ARG_ENABLE(internal_api_docs, AS_HELP_STRING([--enable-internal-api-docs],[Build API docs for Nix's internal unstable C++ interfaces]),
|
ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD)
|
||||||
internal_api_docs=$enableval, internal_api_docs=no)
|
AC_SUBST(ENABLE_UNIT_TESTS)
|
||||||
AC_SUBST(internal_api_docs)
|
|
||||||
|
|
||||||
AS_IF(
|
AS_IF(
|
||||||
[test "$ENABLE_BUILD" == "yes" || test "$ENABLE_TEST" == "yes"],
|
[test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"],
|
||||||
|
[AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(functional-tests, AS_HELP_STRING([--disable-functional-tests],[Do not build the tests]),
|
||||||
|
ENABLE_FUNCTIONAL_TESTS=$enableval, ENABLE_FUNCTIONAL_TESTS=yes)
|
||||||
|
AC_SUBST(ENABLE_FUNCTIONAL_TESTS)
|
||||||
|
|
||||||
|
# documentation generation switch
|
||||||
|
AC_ARG_ENABLE(doc-gen, AS_HELP_STRING([--disable-doc-gen],[disable documentation generation]),
|
||||||
|
ENABLE_DOC_GEN=$enableval, ENABLE_DOC_GEN=$ENABLE_BUILD)
|
||||||
|
AC_SUBST(ENABLE_DOC_GEN)
|
||||||
|
|
||||||
|
AS_IF(
|
||||||
|
[test "$ENABLE_BUILD" == "no" && test "$ENABLE_GENERATED_DOCS" == "yes"],
|
||||||
|
[AC_MSG_ERROR([Cannot enable generated docs when building overall is disabled. Please do not pass '--enable-doc-gen' or do not pass '--disable-build'.])])
|
||||||
|
|
||||||
|
# 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]),
|
||||||
|
ENABLE_INTERNAL_API_DOCS=$enableval, ENABLE_INTERNAL_API_DOCS=no)
|
||||||
|
AC_SUBST(ENABLE_INTERNAL_API_DOCS)
|
||||||
|
|
||||||
|
AS_IF(
|
||||||
|
[test "$ENABLE_FUNCTIONAL_TESTS" == "yes" || test "$ENABLE_DOC_GEN" == "yes"],
|
||||||
[NEED_PROG(jq, jq)])
|
[NEED_PROG(jq, jq)])
|
||||||
|
|
||||||
AS_IF([test "$ENABLE_BUILD" == "yes"],[
|
AS_IF([test "$ENABLE_BUILD" == "yes"],[
|
||||||
|
@ -317,7 +335,7 @@ if test "$gc" = yes; then
|
||||||
AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.])
|
AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AS_IF([test "$ENABLE_TESTS" == "yes"],[
|
AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[
|
||||||
|
|
||||||
# Look for gtest.
|
# Look for gtest.
|
||||||
PKG_CHECK_MODULES([GTEST], [gtest_main])
|
PKG_CHECK_MODULES([GTEST], [gtest_main])
|
||||||
|
@ -349,11 +367,6 @@ AC_LANG_POP(C++)
|
||||||
# Look for nlohmann/json.
|
# Look for nlohmann/json.
|
||||||
PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9])
|
PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9])
|
||||||
|
|
||||||
# documentation generation switch
|
|
||||||
AC_ARG_ENABLE(doc-gen, AS_HELP_STRING([--disable-doc-gen],[disable documentation generation]),
|
|
||||||
doc_generate=$enableval, doc_generate=yes)
|
|
||||||
AC_SUBST(doc_generate)
|
|
||||||
|
|
||||||
|
|
||||||
# Look for lowdown library.
|
# Look for lowdown library.
|
||||||
PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.9.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
|
PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.9.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
.PHONY: internal-api-html
|
|
||||||
|
|
||||||
ifeq ($(internal_api_docs), yes)
|
|
||||||
|
|
||||||
$(docdir)/internal-api/html/index.html $(docdir)/internal-api/latex: $(d)/doxygen.cfg
|
$(docdir)/internal-api/html/index.html $(docdir)/internal-api/latex: $(d)/doxygen.cfg
|
||||||
mkdir -p $(docdir)/internal-api
|
mkdir -p $(docdir)/internal-api
|
||||||
{ cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/internal-api" ; } | doxygen -
|
{ cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/internal-api" ; } | doxygen -
|
||||||
|
|
||||||
# Generate the HTML API docs for Nix's unstable internal interfaces.
|
# Generate the HTML API docs for Nix's unstable internal interfaces.
|
||||||
|
.PHONY: internal-api-html
|
||||||
internal-api-html: $(docdir)/internal-api/html/index.html
|
internal-api-html: $(docdir)/internal-api/html/index.html
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
# Make a nicer error message
|
|
||||||
internal-api-html:
|
|
||||||
@echo "Internal API docs are disabled. Configure with '--enable-internal-api-docs', or avoid calling 'make internal-api-html'."
|
|
||||||
@exit 1
|
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
ifeq ($(doc_generate),yes)
|
|
||||||
|
|
||||||
# The version of Nix used to generate the doc. Can also be
|
# The version of Nix used to generate the doc. Can also be
|
||||||
# `$(nix_INSTALL_PATH)` or just `nix` (to grap ambient from the `PATH`),
|
# `$(nix_INSTALL_PATH)` or just `nix` (to grap ambient from the `PATH`),
|
||||||
# if one prefers.
|
# if one prefers.
|
||||||
|
@ -180,6 +178,8 @@ manual-html: $(docdir)/manual/index.html
|
||||||
install: $(docdir)/manual/index.html
|
install: $(docdir)/manual/index.html
|
||||||
|
|
||||||
# Generate 'nix' manpages.
|
# Generate 'nix' manpages.
|
||||||
|
.PHONY: manpages
|
||||||
|
manpages: $(mandir)/man1/nix3-manpages
|
||||||
install: $(mandir)/man1/nix3-manpages
|
install: $(mandir)/man1/nix3-manpages
|
||||||
man: doc/manual/generated/man1/nix3-manpages
|
man: doc/manual/generated/man1/nix3-manpages
|
||||||
all: doc/manual/generated/man1/nix3-manpages
|
all: doc/manual/generated/man1/nix3-manpages
|
||||||
|
@ -225,5 +225,3 @@ $(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/
|
||||||
@rm -rf $(DESTDIR)$(docdir)/manual
|
@rm -rf $(DESTDIR)$(docdir)/manual
|
||||||
@mv $(DESTDIR)$(docdir)/manual.tmp/html $(DESTDIR)$(docdir)/manual
|
@mv $(DESTDIR)$(docdir)/manual.tmp/html $(DESTDIR)$(docdir)/manual
|
||||||
@rm -rf $(DESTDIR)$(docdir)/manual.tmp
|
@rm -rf $(DESTDIR)$(docdir)/manual.tmp
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
6
doc/manual/rl-next/nix-profile-names.md
Normal file
6
doc/manual/rl-next/nix-profile-names.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
synopsis: "`nix profile` now allows referring to elements by human-readable name"
|
||||||
|
prs: 8678
|
||||||
|
---
|
||||||
|
|
||||||
|
[`nix profile`](@docroot@/command-ref/new-cli/nix3-profile.md) now uses names to refer to installed packages when running [`list`](@docroot@/command-ref/new-cli/nix3-profile-list.md), [`remove`](@docroot@/command-ref/new-cli/nix3-profile-remove.md) or [`upgrade`](@docroot@/command-ref/new-cli/nix3-profile-upgrade.md) as opposed to indices. Indices are deprecated and will be removed in a future version.
|
|
@ -64,6 +64,27 @@ $ nix build
|
||||||
|
|
||||||
You can also build Nix for one of the [supported platforms](#platforms).
|
You can also build Nix for one of the [supported platforms](#platforms).
|
||||||
|
|
||||||
|
## Makefile variables
|
||||||
|
|
||||||
|
You may need `profiledir=$out/etc/profile.d` and `sysconfdir=$out/etc` to run
|
||||||
|
`make install`.
|
||||||
|
|
||||||
|
You may want to set `MAKEFLAGS="-e -j $NIX_BUILD_CORES"` to allow environment
|
||||||
|
variables to override `Makefile` variables.
|
||||||
|
|
||||||
|
- `ENABLE_BUILD=yes` to enable building the C++ code.
|
||||||
|
- `ENABLE_DOC_GEN=yes` to enable building the documentation (manual, man pages, etc.).
|
||||||
|
|
||||||
|
The docs can take a while to build, so you may want to disable this for local development.
|
||||||
|
- `ENABLE_FUNCTIONAL_TESTS=yes` to enable building the functional tests.
|
||||||
|
- `ENABLE_UNIT_TESTS=yes` to enable building the unit tests.
|
||||||
|
- `OPTIMIZE=1` to enable optimizations.
|
||||||
|
- `libraries=libutil programs=` to only build a specific library (this will
|
||||||
|
fail in the linking phase if you don't have the other libraries built, but is
|
||||||
|
useful for checking types).
|
||||||
|
- `libraries= programs=nix` to only build a specific program (this will not, in
|
||||||
|
general, work, because the programs need the libraries).
|
||||||
|
|
||||||
## Building Nix
|
## Building Nix
|
||||||
|
|
||||||
To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found:
|
To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found:
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
This is an optional dependency and can be disabled
|
This is an optional dependency and can be disabled
|
||||||
by providing a `--disable-cpuid` to the `configure` script.
|
by providing a `--disable-cpuid` to the `configure` script.
|
||||||
|
|
||||||
- Unless `./configure --disable-tests` is specified, GoogleTest (GTest) and
|
- Unless `./configure --disable-unit-tests` is specified, GoogleTest (GTest) and
|
||||||
RapidCheck are required, which are available at
|
RapidCheck are required, which are available at
|
||||||
<https://google.github.io/googletest/> and
|
<https://google.github.io/googletest/> and
|
||||||
<https://github.com/emil-e/rapidcheck> respectively.
|
<https://github.com/emil-e/rapidcheck> respectively.
|
||||||
|
|
|
@ -395,7 +395,7 @@
|
||||||
stdenvs)));
|
stdenvs)));
|
||||||
|
|
||||||
devShells = let
|
devShells = let
|
||||||
makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; }).overrideAttrs (attrs: {
|
makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; }).overrideAttrs (attrs: {
|
||||||
installFlags = "sysconfdir=$(out)/etc";
|
installFlags = "sysconfdir=$(out)/etc";
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
PATH=$prefix/bin:$PATH
|
PATH=$prefix/bin:$PATH
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
# This file is only active for `./configure --disable-tests`.
|
|
||||||
# Running `make check` or `make installcheck` would indicate a mistake in the
|
|
||||||
# caller.
|
|
||||||
|
|
||||||
installcheck:
|
|
||||||
@echo "Tests are disabled. Configure without '--disable-tests', or avoid calling 'make installcheck'."
|
|
||||||
@exit 1
|
|
||||||
|
|
||||||
# This currently has little effect.
|
|
||||||
check:
|
|
||||||
@echo "Tests are disabled. Configure without '--disable-tests', or avoid calling 'make check'."
|
|
||||||
@exit 1
|
|
75
package.nix
75
package.nix
|
@ -87,6 +87,9 @@
|
||||||
, test-daemon ? null
|
, test-daemon ? null
|
||||||
, test-client ? null
|
, test-client ? null
|
||||||
|
|
||||||
|
# Avoid setting things that would interfere with a functioning devShell
|
||||||
|
, forDevShell ? false
|
||||||
|
|
||||||
# Not a real argument, just the only way to approximate let-binding some
|
# Not a real argument, just the only way to approximate let-binding some
|
||||||
# stuff for argument defaults.
|
# stuff for argument defaults.
|
||||||
, __forDefaults ? {
|
, __forDefaults ? {
|
||||||
|
@ -104,30 +107,6 @@ let
|
||||||
inherit doBuild doCheck doInstallCheck;
|
inherit doBuild doCheck doInstallCheck;
|
||||||
};
|
};
|
||||||
|
|
||||||
filesets = {
|
|
||||||
baseFiles = 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)
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
mkDerivation =
|
mkDerivation =
|
||||||
if withCoverageChecks
|
if withCoverageChecks
|
||||||
then
|
then
|
||||||
|
@ -151,32 +130,44 @@ mkDerivation (finalAttrs: let
|
||||||
# to be run later, requiresthe unit tests to be built.
|
# to be run later, requiresthe unit tests to be built.
|
||||||
buildUnitTests = doCheck || installUnitTests;
|
buildUnitTests = doCheck || installUnitTests;
|
||||||
|
|
||||||
anySortOfTesting = buildUnitTests || doInstallCheck;
|
|
||||||
|
|
||||||
in {
|
in {
|
||||||
inherit pname version;
|
inherit pname version;
|
||||||
|
|
||||||
src =
|
src =
|
||||||
let
|
let
|
||||||
|
baseFiles = fileset.fileFilter (f: f.name != ".gitignore") ./.;
|
||||||
in
|
in
|
||||||
fileset.toSource {
|
fileset.toSource {
|
||||||
root = ./.;
|
root = ./.;
|
||||||
fileset = fileset.intersect filesets.baseFiles (fileset.unions ([
|
fileset = fileset.intersect baseFiles (fileset.unions ([
|
||||||
filesets.configureFiles
|
# For configure
|
||||||
filesets.topLevelBuildFiles
|
./.version
|
||||||
./doc/internal-api
|
./configure.ac
|
||||||
|
./m4
|
||||||
|
# TODO: do we really need README.md? It doesn't seem used in the build.
|
||||||
|
./README.md
|
||||||
|
# For make, regardless of what we are building
|
||||||
|
./local.mk
|
||||||
|
./Makefile
|
||||||
|
./Makefile.config.in
|
||||||
|
./mk
|
||||||
|
(fileset.fileFilter (f: lib.strings.hasPrefix "nix-profile" f.name) ./scripts)
|
||||||
] ++ lib.optionals doBuild [
|
] ++ lib.optionals doBuild [
|
||||||
./boehmgc-coroutine-sp-fallback.diff
|
./boehmgc-coroutine-sp-fallback.diff
|
||||||
./doc
|
./doc
|
||||||
./misc
|
./misc
|
||||||
./precompiled-headers.h
|
./precompiled-headers.h
|
||||||
./src
|
./src
|
||||||
./tests/unit
|
|
||||||
./COPYING
|
./COPYING
|
||||||
./scripts/local.mk
|
./scripts/local.mk
|
||||||
] ++ lib.optionals anySortOfTesting [
|
] ++ lib.optionals buildUnitTests [
|
||||||
filesets.functionalTestFiles
|
./doc/manual
|
||||||
|
] ++ lib.optionals enableInternalAPIDocs [
|
||||||
|
./doc/internal-api
|
||||||
|
] ++ lib.optionals buildUnitTests [
|
||||||
|
./tests/unit
|
||||||
|
] ++ lib.optionals doInstallCheck [
|
||||||
|
./tests/functional
|
||||||
]));
|
]));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -275,12 +266,14 @@ in {
|
||||||
);
|
);
|
||||||
|
|
||||||
configureFlags = [
|
configureFlags = [
|
||||||
"--sysconfdir=/etc"
|
|
||||||
(lib.enableFeature doBuild "build")
|
(lib.enableFeature doBuild "build")
|
||||||
(lib.enableFeature anySortOfTesting "tests")
|
(lib.enableFeature buildUnitTests "unit-tests")
|
||||||
|
(lib.enableFeature doInstallCheck "functional-tests")
|
||||||
(lib.enableFeature enableInternalAPIDocs "internal-api-docs")
|
(lib.enableFeature enableInternalAPIDocs "internal-api-docs")
|
||||||
(lib.enableFeature enableManual "doc-gen")
|
(lib.enableFeature enableManual "doc-gen")
|
||||||
(lib.enableFeature installUnitTests "install-unit-tests")
|
(lib.enableFeature installUnitTests "install-unit-tests")
|
||||||
|
] ++ lib.optionals (!forDevShell) [
|
||||||
|
"--sysconfdir=/etc"
|
||||||
] ++ lib.optionals installUnitTests [
|
] ++ lib.optionals installUnitTests [
|
||||||
"--with-check-bin-dir=${builtins.placeholder "check"}/bin"
|
"--with-check-bin-dir=${builtins.placeholder "check"}/bin"
|
||||||
"--with-check-lib-dir=${builtins.placeholder "check"}/lib"
|
"--with-check-lib-dir=${builtins.placeholder "check"}/lib"
|
||||||
|
@ -310,10 +303,7 @@ in {
|
||||||
'';
|
'';
|
||||||
|
|
||||||
postInstall = lib.optionalString doBuild (
|
postInstall = lib.optionalString doBuild (
|
||||||
''
|
lib.optionalString stdenv.hostPlatform.isStatic ''
|
||||||
mkdir -p $doc/nix-support
|
|
||||||
echo "doc manual $doc/share/doc/nix/manual" >> $doc/nix-support/hydra-build-products
|
|
||||||
'' + lib.optionalString stdenv.hostPlatform.isStatic ''
|
|
||||||
mkdir -p $out/nix-support
|
mkdir -p $out/nix-support
|
||||||
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products
|
||||||
'' + lib.optionalString stdenv.isDarwin ''
|
'' + lib.optionalString stdenv.isDarwin ''
|
||||||
|
@ -322,7 +312,10 @@ in {
|
||||||
$out/lib/libboost_context.dylib \
|
$out/lib/libboost_context.dylib \
|
||||||
$out/lib/libnixutil.dylib
|
$out/lib/libnixutil.dylib
|
||||||
''
|
''
|
||||||
) + lib.optionalString enableInternalAPIDocs ''
|
) + lib.optionalString enableManual ''
|
||||||
|
mkdir -p ''${!outputDoc}/nix-support
|
||||||
|
echo "doc manual ''${!outputDoc}/share/doc/nix/manual" >> ''${!outputDoc}/nix-support/hydra-build-products
|
||||||
|
'' + lib.optionalString enableInternalAPIDocs ''
|
||||||
mkdir -p ''${!outputDoc}/nix-support
|
mkdir -p ''${!outputDoc}/nix-support
|
||||||
echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products
|
echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "crypto.hh"
|
#include "crypto.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
@ -205,7 +206,10 @@ void importPaths(int fd, int dontCheckSigs)
|
||||||
SV * hashPath(char * algo, int base32, char * path)
|
SV * hashPath(char * algo, int base32, char * path)
|
||||||
PPCODE:
|
PPCODE:
|
||||||
try {
|
try {
|
||||||
Hash h = hashPath(parseHashAlgo(algo), path).first;
|
PosixSourceAccessor accessor;
|
||||||
|
Hash h = hashPath(
|
||||||
|
accessor, CanonPath::fromCwd(path),
|
||||||
|
FileIngestionMethod::Recursive, parseHashAlgo(algo)).first;
|
||||||
auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false);
|
auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false);
|
||||||
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
|
@ -281,7 +285,11 @@ SV * addToStore(char * srcPath, int recursive, char * algo)
|
||||||
PPCODE:
|
PPCODE:
|
||||||
try {
|
try {
|
||||||
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
|
||||||
auto path = store()->addToStore(std::string(baseNameOf(srcPath)), srcPath, method, parseHashAlgo(algo));
|
PosixSourceAccessor accessor;
|
||||||
|
auto path = store()->addToStore(
|
||||||
|
std::string(baseNameOf(srcPath)),
|
||||||
|
accessor, CanonPath::fromCwd(srcPath),
|
||||||
|
method, parseHashAlgo(algo));
|
||||||
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(path).c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(store()->printStorePath(path).c_str(), 0)));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
croak("%s", e.what());
|
croak("%s", e.what());
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
set -eu
|
set -eu
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
|
# System specific settings
|
||||||
|
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-301}"
|
||||||
|
export NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
|
||||||
|
|
||||||
readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
readonly NIX_DAEMON_DEST=/Library/LaunchDaemons/org.nixos.nix-daemon.plist
|
||||||
# create by default; set 0 to DIY, use a symlink, etc.
|
# create by default; set 0 to DIY, use a symlink, etc.
|
||||||
readonly NIX_VOLUME_CREATE=${NIX_VOLUME_CREATE:-1} # now default
|
readonly NIX_VOLUME_CREATE=${NIX_VOLUME_CREATE:-1} # now default
|
||||||
NIX_FIRST_BUILD_UID="301"
|
|
||||||
NIX_BUILD_USER_NAME_TEMPLATE="_nixbld%d"
|
|
||||||
|
|
||||||
# caution: may update times on / if not run as normal non-root user
|
# caution: may update times on / if not run as normal non-root user
|
||||||
read_only_root() {
|
read_only_root() {
|
||||||
|
|
|
@ -25,9 +25,9 @@ readonly RED='\033[31m'
|
||||||
readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32}
|
readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32}
|
||||||
readonly NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}"
|
readonly NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}"
|
||||||
readonly NIX_BUILD_GROUP_NAME="nixbld"
|
readonly NIX_BUILD_GROUP_NAME="nixbld"
|
||||||
# darwin installer needs to override these
|
# each system specific installer must set these:
|
||||||
NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
|
# NIX_FIRST_BUILD_UID
|
||||||
NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
|
# NIX_BUILD_USER_NAME_TEMPLATE
|
||||||
# Please don't change this. We don't support it, because the
|
# Please don't change this. We don't support it, because the
|
||||||
# default shell profile that comes with Nix doesn't support it.
|
# default shell profile that comes with Nix doesn't support it.
|
||||||
readonly NIX_ROOT="/nix"
|
readonly NIX_ROOT="/nix"
|
||||||
|
@ -707,6 +707,12 @@ EOF
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_required_system_specific_settings() {
|
||||||
|
if [ -z "${NIX_FIRST_BUILD_UID+x}" ] || [ -z "${NIX_BUILD_USER_NAME_TEMPLATE+x}" ]; then
|
||||||
|
failure "Internal error: System specific installer for $(uname) ($1) does not export required settings."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
welcome_to_nix() {
|
welcome_to_nix() {
|
||||||
local -r NIX_UID_RANGES="${NIX_FIRST_BUILD_UID}..$((NIX_FIRST_BUILD_UID + NIX_USER_COUNT - 1))"
|
local -r NIX_UID_RANGES="${NIX_FIRST_BUILD_UID}..$((NIX_FIRST_BUILD_UID + NIX_USER_COUNT - 1))"
|
||||||
local -r RANGE_TEXT=$(echo -ne "${BLUE}(uids [${NIX_UID_RANGES}])${ESC}")
|
local -r RANGE_TEXT=$(echo -ne "${BLUE}(uids [${NIX_UID_RANGES}])${ESC}")
|
||||||
|
@ -726,7 +732,9 @@ manager. This will happen in a few stages:
|
||||||
if you are ready to continue.
|
if you are ready to continue.
|
||||||
|
|
||||||
3. Create the system users ${RANGE_TEXT} and groups ${GROUP_TEXT}
|
3. Create the system users ${RANGE_TEXT} and groups ${GROUP_TEXT}
|
||||||
that the Nix daemon uses to run builds.
|
that the Nix daemon uses to run builds. To create system users
|
||||||
|
in a different range, exit and run this tool again with
|
||||||
|
NIX_FIRST_BUILD_UID set.
|
||||||
|
|
||||||
4. Perform the basic installation of the Nix files daemon.
|
4. Perform the basic installation of the Nix files daemon.
|
||||||
|
|
||||||
|
@ -968,13 +976,16 @@ main() {
|
||||||
if is_os_darwin; then
|
if is_os_darwin; then
|
||||||
# shellcheck source=./install-darwin-multi-user.sh
|
# shellcheck source=./install-darwin-multi-user.sh
|
||||||
. "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh"
|
. "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh"
|
||||||
|
check_required_system_specific_settings "install-darwin-multi-user.sh"
|
||||||
elif is_os_linux; then
|
elif is_os_linux; then
|
||||||
# shellcheck source=./install-systemd-multi-user.sh
|
# shellcheck source=./install-systemd-multi-user.sh
|
||||||
. "$EXTRACTED_NIX_PATH/install-systemd-multi-user.sh" # most of this works on non-systemd distros also
|
. "$EXTRACTED_NIX_PATH/install-systemd-multi-user.sh" # most of this works on non-systemd distros also
|
||||||
|
check_required_system_specific_settings "install-systemd-multi-user.sh"
|
||||||
else
|
else
|
||||||
failure "Sorry, I don't know what to do on $(uname)"
|
failure "Sorry, I don't know what to do on $(uname)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
welcome_to_nix
|
welcome_to_nix
|
||||||
|
|
||||||
if ! is_root; then
|
if ! is_root; then
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
set -eu
|
set -eu
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
|
# System specific settings
|
||||||
|
export NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}"
|
||||||
|
export NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d"
|
||||||
|
|
||||||
readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service
|
readonly SERVICE_SRC=/lib/systemd/system/nix-daemon.service
|
||||||
readonly SERVICE_DEST=/etc/systemd/system/nix-daemon.service
|
readonly SERVICE_DEST=/etc/systemd/system/nix-daemon.service
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ ref<InstallableValue> InstallableValue::require(ref<Installable> installable)
|
||||||
std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx)
|
std::optional<DerivedPathWithInfo> InstallableValue::trySinglePathToDerivedPaths(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||||
{
|
{
|
||||||
if (v.type() == nPath) {
|
if (v.type() == nPath) {
|
||||||
auto storePath = v.path().fetchToStore(state->store);
|
auto storePath = v.path().fetchToStore(*state->store);
|
||||||
return {{
|
return {{
|
||||||
.path = DerivedPath::Opaque {
|
.path = DerivedPath::Opaque {
|
||||||
.path = std::move(storePath),
|
.path = std::move(storePath),
|
||||||
|
|
|
@ -2317,7 +2317,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat
|
||||||
auto dstPath = i != srcToStore.end()
|
auto dstPath = i != srcToStore.end()
|
||||||
? i->second
|
? i->second
|
||||||
: [&]() {
|
: [&]() {
|
||||||
auto dstPath = path.fetchToStore(store, path.baseName(), FileIngestionMethod::Recursive, nullptr, repair);
|
auto dstPath = path.fetchToStore(*store, path.baseName(), FileIngestionMethod::Recursive, nullptr, repair);
|
||||||
allowPath(dstPath);
|
allowPath(dstPath);
|
||||||
srcToStore.insert_or_assign(path, dstPath);
|
srcToStore.insert_or_assign(path, dstPath);
|
||||||
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath));
|
printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath));
|
||||||
|
|
|
@ -190,7 +190,7 @@ std::optional<std::pair<FlakeRef, std::string>> parseFlakeIdRef(
|
||||||
|
|
||||||
static std::regex flakeRegex(
|
static std::regex flakeRegex(
|
||||||
"((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)"
|
"((" + flakeIdRegexS + ")(?:/(?:" + refAndOrRevRegex + "))?)"
|
||||||
+ "(?:#(" + queryRegex + "))?",
|
+ "(?:#(" + fragmentRegex + "))?",
|
||||||
std::regex::ECMAScript);
|
std::regex::ECMAScript);
|
||||||
|
|
||||||
if (std::regex_match(url, match, flakeRegex)) {
|
if (std::regex_match(url, match, flakeRegex)) {
|
||||||
|
|
48
src/libexpr/flake/url-name.cc
Normal file
48
src/libexpr/flake/url-name.cc
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include "url-name.hh"
|
||||||
|
#include <regex>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
static const std::string attributeNamePattern("[a-z0-9_-]+");
|
||||||
|
static const std::regex lastAttributeRegex("(?:" + attributeNamePattern + "\\.)*(?!default)(" + attributeNamePattern +")(\\^.*)?");
|
||||||
|
static const std::string pathSegmentPattern("[a-zA-Z0-9_-]+");
|
||||||
|
static const std::regex lastPathSegmentRegex(".*/(" + pathSegmentPattern +")");
|
||||||
|
static const std::regex secondPathSegmentRegex("(?:" + pathSegmentPattern + ")/(" + pathSegmentPattern +")(?:/.*)?");
|
||||||
|
static const std::regex gitProviderRegex("github|gitlab|sourcehut");
|
||||||
|
static const std::regex gitSchemeRegex("git($|\\+.*)");
|
||||||
|
static const std::regex defaultOutputRegex(".*\\.default($|\\^.*)");
|
||||||
|
|
||||||
|
std::optional<std::string> getNameFromURL(const ParsedURL & url)
|
||||||
|
{
|
||||||
|
std::smatch match;
|
||||||
|
|
||||||
|
/* If there is a dir= argument, use its value */
|
||||||
|
if (url.query.count("dir") > 0)
|
||||||
|
return url.query.at("dir");
|
||||||
|
|
||||||
|
/* If the fragment isn't a "default" and contains two attribute elements, use the last one */
|
||||||
|
if (std::regex_match(url.fragment, match, lastAttributeRegex))
|
||||||
|
return match.str(1);
|
||||||
|
|
||||||
|
/* If this is a github/gitlab/sourcehut flake, use the repo name */
|
||||||
|
if (std::regex_match(url.scheme, gitProviderRegex) && std::regex_match(url.path, match, secondPathSegmentRegex))
|
||||||
|
return match.str(1);
|
||||||
|
|
||||||
|
/* If it is a regular git flake, use the directory name */
|
||||||
|
if (std::regex_match(url.scheme, gitSchemeRegex) && std::regex_match(url.path, match, lastPathSegmentRegex))
|
||||||
|
return match.str(1);
|
||||||
|
|
||||||
|
/* If everything failed but there is a non-default fragment, use it in full */
|
||||||
|
if (!url.fragment.empty() && !std::regex_match(url.fragment, defaultOutputRegex))
|
||||||
|
return url.fragment;
|
||||||
|
|
||||||
|
/* If there is no fragment, take the last element of the path */
|
||||||
|
if (std::regex_match(url.path, match, lastPathSegmentRegex))
|
||||||
|
return match.str(1);
|
||||||
|
|
||||||
|
/* If even that didn't work, the URL does not contain enough info to determine a useful name */
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
src/libexpr/flake/url-name.hh
Normal file
20
src/libexpr/flake/url-name.hh
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include "url.hh"
|
||||||
|
#include "url-parts.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
#include "split.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to extract a reasonably unique and meaningful, human-readable
|
||||||
|
* name of a flake output from a parsed URL.
|
||||||
|
* When nullopt is returned, the callsite should use information available
|
||||||
|
* to it outside of the URL to determine a useful name.
|
||||||
|
* This is a heuristic approach intended for user interfaces.
|
||||||
|
* @return nullopt if the extracted name is not useful to identify a
|
||||||
|
* flake output, for example because it is empty or "default".
|
||||||
|
* Otherwise returns the extracted name.
|
||||||
|
*/
|
||||||
|
std::optional<std::string> getNameFromURL(const ParsedURL & url);
|
||||||
|
|
||||||
|
}
|
|
@ -438,9 +438,7 @@ static RegisterPrimOp primop_isNull({
|
||||||
.doc = R"(
|
.doc = R"(
|
||||||
Return `true` if *e* evaluates to `null`, and `false` otherwise.
|
Return `true` if *e* evaluates to `null`, and `false` otherwise.
|
||||||
|
|
||||||
> **Warning**
|
This is equivalent to `e == null`.
|
||||||
>
|
|
||||||
> This function is *deprecated*; just write `e == null` instead.
|
|
||||||
)",
|
)",
|
||||||
.fun = prim_isNull,
|
.fun = prim_isNull,
|
||||||
});
|
});
|
||||||
|
@ -2072,8 +2070,14 @@ static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Val
|
||||||
}
|
}
|
||||||
|
|
||||||
auto storePath = settings.readOnlyMode
|
auto storePath = settings.readOnlyMode
|
||||||
? state.store->computeStorePathForText(name, contents, refs)
|
? state.store->makeFixedOutputPathFromCA(name, TextInfo {
|
||||||
: state.store->addTextToStore(name, contents, refs, state.repair);
|
.hash = hashString(HashAlgorithm::SHA256, contents),
|
||||||
|
.references = std::move(refs),
|
||||||
|
})
|
||||||
|
: ({
|
||||||
|
StringSource s { contents };
|
||||||
|
state.store->addToStoreFromDump(s, name, TextIngestionMethod {}, HashAlgorithm::SHA256, refs, state.repair);
|
||||||
|
});
|
||||||
|
|
||||||
/* Note: we don't need to add `context' to the context of the
|
/* Note: we don't need to add `context' to the context of the
|
||||||
result, since `storePath' itself has references to the paths
|
result, since `storePath' itself has references to the paths
|
||||||
|
@ -2229,7 +2233,7 @@ static void addPath(
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) {
|
||||||
auto dstPath = path.fetchToStore(state.store, name, method, filter.get(), state.repair);
|
auto dstPath = path.fetchToStore(*state.store, name, method, filter.get(), state.repair);
|
||||||
if (expectedHash && expectedStorePath != dstPath)
|
if (expectedHash && expectedStorePath != dstPath)
|
||||||
state.debugThrowLastTrace(Error("store path mismatch in (possibly filtered) path added from '%s'", path));
|
state.debugThrowLastTrace(Error("store path mismatch in (possibly filtered) path added from '%s'", path));
|
||||||
state.allowAndSetStorePathString(dstPath, v);
|
state.allowAndSetStorePathString(dstPath, v);
|
||||||
|
|
|
@ -106,7 +106,7 @@ struct CacheImpl : Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(
|
void add(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
const Attrs & inAttrs,
|
const Attrs & inAttrs,
|
||||||
const Attrs & infoAttrs,
|
const Attrs & infoAttrs,
|
||||||
const StorePath & storePath,
|
const StorePath & storePath,
|
||||||
|
@ -115,13 +115,13 @@ struct CacheImpl : Cache
|
||||||
_state.lock()->add.use()
|
_state.lock()->add.use()
|
||||||
(attrsToJSON(inAttrs).dump())
|
(attrsToJSON(inAttrs).dump())
|
||||||
(attrsToJSON(infoAttrs).dump())
|
(attrsToJSON(infoAttrs).dump())
|
||||||
(store->printStorePath(storePath))
|
(store.printStorePath(storePath))
|
||||||
(locked)
|
(locked)
|
||||||
(time(0)).exec();
|
(time(0)).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::pair<Attrs, StorePath>> lookup(
|
std::optional<std::pair<Attrs, StorePath>> lookup(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
const Attrs & inAttrs) override
|
const Attrs & inAttrs) override
|
||||||
{
|
{
|
||||||
if (auto res = lookupExpired(store, inAttrs)) {
|
if (auto res = lookupExpired(store, inAttrs)) {
|
||||||
|
@ -134,7 +134,7 @@ struct CacheImpl : Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Result> lookupExpired(
|
std::optional<Result> lookupExpired(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
const Attrs & inAttrs) override
|
const Attrs & inAttrs) override
|
||||||
{
|
{
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
|
@ -148,19 +148,19 @@ struct CacheImpl : Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
auto infoJSON = stmt.getStr(0);
|
auto infoJSON = stmt.getStr(0);
|
||||||
auto storePath = store->parseStorePath(stmt.getStr(1));
|
auto storePath = store.parseStorePath(stmt.getStr(1));
|
||||||
auto locked = stmt.getInt(2) != 0;
|
auto locked = stmt.getInt(2) != 0;
|
||||||
auto timestamp = stmt.getInt(3);
|
auto timestamp = stmt.getInt(3);
|
||||||
|
|
||||||
store->addTempRoot(storePath);
|
store.addTempRoot(storePath);
|
||||||
if (!store->isValidPath(storePath)) {
|
if (!store.isValidPath(storePath)) {
|
||||||
// FIXME: we could try to substitute 'storePath'.
|
// FIXME: we could try to substitute 'storePath'.
|
||||||
debug("ignoring disappeared cache entry '%s'", inAttrsJSON);
|
debug("ignoring disappeared cache entry '%s'", inAttrsJSON);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("using cache entry '%s' -> '%s', '%s'",
|
debug("using cache entry '%s' -> '%s', '%s'",
|
||||||
inAttrsJSON, infoJSON, store->printStorePath(storePath));
|
inAttrsJSON, infoJSON, store.printStorePath(storePath));
|
||||||
|
|
||||||
return Result {
|
return Result {
|
||||||
.expired = !locked && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)),
|
.expired = !locked && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)),
|
||||||
|
|
|
@ -50,14 +50,14 @@ struct Cache
|
||||||
|
|
||||||
/* Old cache for things that have a store path. */
|
/* Old cache for things that have a store path. */
|
||||||
virtual void add(
|
virtual void add(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
const Attrs & inAttrs,
|
const Attrs & inAttrs,
|
||||||
const Attrs & infoAttrs,
|
const Attrs & infoAttrs,
|
||||||
const StorePath & storePath,
|
const StorePath & storePath,
|
||||||
bool locked) = 0;
|
bool locked) = 0;
|
||||||
|
|
||||||
virtual std::optional<std::pair<Attrs, StorePath>> lookup(
|
virtual std::optional<std::pair<Attrs, StorePath>> lookup(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
const Attrs & inAttrs) = 0;
|
const Attrs & inAttrs) = 0;
|
||||||
|
|
||||||
struct Result
|
struct Result
|
||||||
|
@ -68,7 +68,7 @@ struct Cache
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual std::optional<Result> lookupExpired(
|
virtual std::optional<Result> lookupExpired(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
const Attrs & inAttrs) = 0;
|
const Attrs & inAttrs) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -374,7 +374,7 @@ void InputScheme::clone(const Input & input, const Path & destDir) const
|
||||||
std::pair<StorePath, Input> InputScheme::fetch(ref<Store> store, const Input & input)
|
std::pair<StorePath, Input> InputScheme::fetch(ref<Store> store, const Input & input)
|
||||||
{
|
{
|
||||||
auto [accessor, input2] = getAccessor(store, input);
|
auto [accessor, input2] = getAccessor(store, input);
|
||||||
auto storePath = SourcePath(accessor).fetchToStore(store, input2.getName());
|
auto storePath = SourcePath(accessor).fetchToStore(*store, input2.getName());
|
||||||
return {storePath, input2};
|
return {storePath, input2};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -368,14 +368,14 @@ struct GitInputScheme : InputScheme
|
||||||
|
|
||||||
RepoInfo getRepoInfo(const Input & input) const
|
RepoInfo getRepoInfo(const Input & input) const
|
||||||
{
|
{
|
||||||
auto checkHashType = [&](const std::optional<Hash> & hash)
|
auto checkHashAlgorithm = [&](const std::optional<Hash> & hash)
|
||||||
{
|
{
|
||||||
if (hash.has_value() && !(hash->algo == HashAlgorithm::SHA1 || hash->algo == HashAlgorithm::SHA256))
|
if (hash.has_value() && !(hash->algo == HashAlgorithm::SHA1 || hash->algo == HashAlgorithm::SHA256))
|
||||||
throw Error("Hash '%s' is not supported by Git. Supported types are sha1 and sha256.", hash->to_string(HashFormat::Base16, true));
|
throw Error("Hash '%s' is not supported by Git. Supported types are sha1 and sha256.", hash->to_string(HashFormat::Base16, true));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (auto rev = input.getRev())
|
if (auto rev = input.getRev())
|
||||||
checkHashType(rev);
|
checkHashAlgorithm(rev);
|
||||||
|
|
||||||
RepoInfo repoInfo;
|
RepoInfo repoInfo;
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
StorePath InputAccessor::fetchToStore(
|
StorePath InputAccessor::fetchToStore(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
const CanonPath & path,
|
const CanonPath & path,
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
FileIngestionMethod method,
|
ContentAddressMethod method,
|
||||||
PathFilter * filter,
|
PathFilter * filter,
|
||||||
RepairFlag repair)
|
RepairFlag repair)
|
||||||
{
|
{
|
||||||
|
@ -20,10 +20,24 @@ StorePath InputAccessor::fetchToStore(
|
||||||
if (!filter && fingerprint) {
|
if (!filter && fingerprint) {
|
||||||
cacheKey = fetchers::Attrs{
|
cacheKey = fetchers::Attrs{
|
||||||
{"_what", "fetchToStore"},
|
{"_what", "fetchToStore"},
|
||||||
{"store", store->storeDir},
|
{"store", store.storeDir},
|
||||||
{"name", std::string(name)},
|
{"name", std::string(name)},
|
||||||
{"fingerprint", *fingerprint},
|
{"fingerprint", *fingerprint},
|
||||||
{"method", (uint8_t) method},
|
{
|
||||||
|
"method",
|
||||||
|
std::visit(overloaded {
|
||||||
|
[](const TextIngestionMethod &) {
|
||||||
|
return "text";
|
||||||
|
},
|
||||||
|
[](const FileIngestionMethod & fim) {
|
||||||
|
switch (fim) {
|
||||||
|
case FileIngestionMethod::Flat: return "flat";
|
||||||
|
case FileIngestionMethod::Recursive: return "nar";
|
||||||
|
default: assert(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, method.raw),
|
||||||
|
},
|
||||||
{"path", path.abs()}
|
{"path", path.abs()}
|
||||||
};
|
};
|
||||||
if (auto res = fetchers::getCache()->lookup(store, *cacheKey)) {
|
if (auto res = fetchers::getCache()->lookup(store, *cacheKey)) {
|
||||||
|
@ -35,17 +49,14 @@ StorePath InputAccessor::fetchToStore(
|
||||||
|
|
||||||
Activity act(*logger, lvlChatty, actUnknown, fmt("copying '%s' to the store", showPath(path)));
|
Activity act(*logger, lvlChatty, actUnknown, fmt("copying '%s' to the store", showPath(path)));
|
||||||
|
|
||||||
auto source = sinkToSource([&](Sink & sink) {
|
auto filter2 = filter ? *filter : defaultPathFilter;
|
||||||
if (method == FileIngestionMethod::Recursive)
|
|
||||||
dumpPath(path, sink, filter ? *filter : defaultPathFilter);
|
|
||||||
else
|
|
||||||
readFile(path, sink);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto storePath =
|
auto storePath =
|
||||||
settings.readOnlyMode
|
settings.readOnlyMode
|
||||||
? store->computeStorePathFromDump(*source, name, method, HashAlgorithm::SHA256).first
|
? store.computeStorePath(
|
||||||
: store->addToStoreFromDump(*source, name, method, HashAlgorithm::SHA256, repair);
|
name, *this, path, method, HashAlgorithm::SHA256, {}, filter2).first
|
||||||
|
: store.addToStore(
|
||||||
|
name, *this, path, method, HashAlgorithm::SHA256, {}, filter2, repair);
|
||||||
|
|
||||||
if (cacheKey)
|
if (cacheKey)
|
||||||
fetchers::getCache()->add(store, *cacheKey, {}, storePath, true);
|
fetchers::getCache()->add(store, *cacheKey, {}, storePath, true);
|
||||||
|
@ -60,9 +71,9 @@ std::ostream & operator << (std::ostream & str, const SourcePath & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath SourcePath::fetchToStore(
|
StorePath SourcePath::fetchToStore(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
FileIngestionMethod method,
|
ContentAddressMethod method,
|
||||||
PathFilter * filter,
|
PathFilter * filter,
|
||||||
RepairFlag repair) const
|
RepairFlag repair) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,10 +30,10 @@ struct InputAccessor : virtual SourceAccessor, std::enable_shared_from_this<Inpu
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath fetchToStore(
|
StorePath fetchToStore(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
const CanonPath & path,
|
const CanonPath & path,
|
||||||
std::string_view name = "source",
|
std::string_view name = "source",
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive,
|
ContentAddressMethod method = FileIngestionMethod::Recursive,
|
||||||
PathFilter * filter = nullptr,
|
PathFilter * filter = nullptr,
|
||||||
RepairFlag repair = NoRepair);
|
RepairFlag repair = NoRepair);
|
||||||
};
|
};
|
||||||
|
@ -116,9 +116,9 @@ struct SourcePath
|
||||||
* Copy this `SourcePath` to the Nix store.
|
* Copy this `SourcePath` to the Nix store.
|
||||||
*/
|
*/
|
||||||
StorePath fetchToStore(
|
StorePath fetchToStore(
|
||||||
ref<Store> store,
|
Store & store,
|
||||||
std::string_view name = "source",
|
std::string_view name = "source",
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive,
|
ContentAddressMethod method = FileIngestionMethod::Recursive,
|
||||||
PathFilter * filter = nullptr,
|
PathFilter * filter = nullptr,
|
||||||
RepairFlag repair = NoRepair) const;
|
RepairFlag repair = NoRepair) const;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "tarfile.hh"
|
#include "tarfile.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "url-parts.hh"
|
#include "url-parts.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
#include "fetch-settings.hh"
|
#include "fetch-settings.hh"
|
||||||
|
|
||||||
|
@ -210,7 +211,12 @@ struct MercurialInputScheme : InputScheme
|
||||||
return files.count(file);
|
return files.count(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto storePath = store->addToStore(input.getName(), actualPath, FileIngestionMethod::Recursive, HashAlgorithm::SHA256, filter);
|
PosixSourceAccessor accessor;
|
||||||
|
auto storePath = store->addToStore(
|
||||||
|
input.getName(),
|
||||||
|
accessor, CanonPath { actualPath },
|
||||||
|
FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {},
|
||||||
|
filter);
|
||||||
|
|
||||||
return {std::move(storePath), input};
|
return {std::move(storePath), input};
|
||||||
}
|
}
|
||||||
|
@ -218,7 +224,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
|
|
||||||
if (!input.getRef()) input.attrs.insert_or_assign("ref", "default");
|
if (!input.getRef()) input.attrs.insert_or_assign("ref", "default");
|
||||||
|
|
||||||
auto checkHashType = [&](const std::optional<Hash> & hash)
|
auto checkHashAlgorithm = [&](const std::optional<Hash> & hash)
|
||||||
{
|
{
|
||||||
if (hash.has_value() && hash->algo != HashAlgorithm::SHA1)
|
if (hash.has_value() && hash->algo != HashAlgorithm::SHA1)
|
||||||
throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", hash->to_string(HashFormat::Base16, true));
|
throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", hash->to_string(HashFormat::Base16, true));
|
||||||
|
@ -227,7 +233,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
|
|
||||||
auto getLockedAttrs = [&]()
|
auto getLockedAttrs = [&]()
|
||||||
{
|
{
|
||||||
checkHashType(input.getRev());
|
checkHashAlgorithm(input.getRev());
|
||||||
|
|
||||||
return Attrs({
|
return Attrs({
|
||||||
{"type", "hg"},
|
{"type", "hg"},
|
||||||
|
@ -246,7 +252,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
};
|
};
|
||||||
|
|
||||||
if (input.getRev()) {
|
if (input.getRev()) {
|
||||||
if (auto res = getCache()->lookup(store, getLockedAttrs()))
|
if (auto res = getCache()->lookup(*store, getLockedAttrs()))
|
||||||
return makeResult(res->first, std::move(res->second));
|
return makeResult(res->first, std::move(res->second));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +265,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
{"ref", *input.getRef()},
|
{"ref", *input.getRef()},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (auto res = getCache()->lookup(store, unlockedAttrs)) {
|
if (auto res = getCache()->lookup(*store, unlockedAttrs)) {
|
||||||
auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), HashAlgorithm::SHA1);
|
auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), HashAlgorithm::SHA1);
|
||||||
if (!input.getRev() || input.getRev() == rev2) {
|
if (!input.getRev() || input.getRev() == rev2) {
|
||||||
input.attrs.insert_or_assign("rev", rev2.gitRev());
|
input.attrs.insert_or_assign("rev", rev2.gitRev());
|
||||||
|
@ -305,7 +311,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
auto revCount = std::stoull(tokens[1]);
|
auto revCount = std::stoull(tokens[1]);
|
||||||
input.attrs.insert_or_assign("ref", tokens[2]);
|
input.attrs.insert_or_assign("ref", tokens[2]);
|
||||||
|
|
||||||
if (auto res = getCache()->lookup(store, getLockedAttrs()))
|
if (auto res = getCache()->lookup(*store, getLockedAttrs()))
|
||||||
return makeResult(res->first, std::move(res->second));
|
return makeResult(res->first, std::move(res->second));
|
||||||
|
|
||||||
Path tmpDir = createTempDir();
|
Path tmpDir = createTempDir();
|
||||||
|
@ -315,7 +321,8 @@ struct MercurialInputScheme : InputScheme
|
||||||
|
|
||||||
deletePath(tmpDir + "/.hg_archival.txt");
|
deletePath(tmpDir + "/.hg_archival.txt");
|
||||||
|
|
||||||
auto storePath = store->addToStore(name, tmpDir);
|
PosixSourceAccessor accessor;
|
||||||
|
auto storePath = store->addToStore(name, accessor, CanonPath { tmpDir });
|
||||||
|
|
||||||
Attrs infoAttrs({
|
Attrs infoAttrs({
|
||||||
{"rev", input.getRev()->gitRev()},
|
{"rev", input.getRev()->gitRev()},
|
||||||
|
@ -324,14 +331,14 @@ struct MercurialInputScheme : InputScheme
|
||||||
|
|
||||||
if (!_input.getRev())
|
if (!_input.getRev())
|
||||||
getCache()->add(
|
getCache()->add(
|
||||||
store,
|
*store,
|
||||||
unlockedAttrs,
|
unlockedAttrs,
|
||||||
infoAttrs,
|
infoAttrs,
|
||||||
storePath,
|
storePath,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
getCache()->add(
|
getCache()->add(
|
||||||
store,
|
*store,
|
||||||
getLockedAttrs(),
|
getLockedAttrs(),
|
||||||
infoAttrs,
|
infoAttrs,
|
||||||
storePath,
|
storePath,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "tarfile.hh"
|
#include "tarfile.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "split.hh"
|
#include "split.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
namespace nix::fetchers {
|
namespace nix::fetchers {
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ DownloadFileResult downloadFile(
|
||||||
{"name", name},
|
{"name", name},
|
||||||
});
|
});
|
||||||
|
|
||||||
auto cached = getCache()->lookupExpired(store, inAttrs);
|
auto cached = getCache()->lookupExpired(*store, inAttrs);
|
||||||
|
|
||||||
auto useCached = [&]() -> DownloadFileResult
|
auto useCached = [&]() -> DownloadFileResult
|
||||||
{
|
{
|
||||||
|
@ -91,7 +92,7 @@ DownloadFileResult downloadFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
getCache()->add(
|
getCache()->add(
|
||||||
store,
|
*store,
|
||||||
inAttrs,
|
inAttrs,
|
||||||
infoAttrs,
|
infoAttrs,
|
||||||
*storePath,
|
*storePath,
|
||||||
|
@ -99,7 +100,7 @@ DownloadFileResult downloadFile(
|
||||||
|
|
||||||
if (url != res.effectiveUri)
|
if (url != res.effectiveUri)
|
||||||
getCache()->add(
|
getCache()->add(
|
||||||
store,
|
*store,
|
||||||
{
|
{
|
||||||
{"type", "file"},
|
{"type", "file"},
|
||||||
{"url", res.effectiveUri},
|
{"url", res.effectiveUri},
|
||||||
|
@ -130,7 +131,7 @@ DownloadTarballResult downloadTarball(
|
||||||
{"name", name},
|
{"name", name},
|
||||||
});
|
});
|
||||||
|
|
||||||
auto cached = getCache()->lookupExpired(store, inAttrs);
|
auto cached = getCache()->lookupExpired(*store, inAttrs);
|
||||||
|
|
||||||
if (cached && !cached->expired)
|
if (cached && !cached->expired)
|
||||||
return {
|
return {
|
||||||
|
@ -156,7 +157,8 @@ DownloadTarballResult downloadTarball(
|
||||||
throw nix::Error("tarball '%s' contains an unexpected number of top-level files", url);
|
throw nix::Error("tarball '%s' contains an unexpected number of top-level files", url);
|
||||||
auto topDir = tmpDir + "/" + members.begin()->name;
|
auto topDir = tmpDir + "/" + members.begin()->name;
|
||||||
lastModified = lstat(topDir).st_mtime;
|
lastModified = lstat(topDir).st_mtime;
|
||||||
unpackedStorePath = store->addToStore(name, topDir, FileIngestionMethod::Recursive, HashAlgorithm::SHA256, defaultPathFilter, NoRepair);
|
PosixSourceAccessor accessor;
|
||||||
|
unpackedStorePath = store->addToStore(name, accessor, CanonPath { topDir }, FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, defaultPathFilter, NoRepair);
|
||||||
}
|
}
|
||||||
|
|
||||||
Attrs infoAttrs({
|
Attrs infoAttrs({
|
||||||
|
@ -168,7 +170,7 @@ DownloadTarballResult downloadTarball(
|
||||||
infoAttrs.emplace("immutableUrl", *res.immutableUrl);
|
infoAttrs.emplace("immutableUrl", *res.immutableUrl);
|
||||||
|
|
||||||
getCache()->add(
|
getCache()->add(
|
||||||
store,
|
*store,
|
||||||
inAttrs,
|
inAttrs,
|
||||||
infoAttrs,
|
infoAttrs,
|
||||||
*unpackedStorePath,
|
*unpackedStorePath,
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "thread-pool.hh"
|
#include "thread-pool.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
|
#include "archive.hh"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <future>
|
#include <future>
|
||||||
|
@ -300,24 +301,60 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, std::string_view name,
|
StorePath BinaryCacheStore::addToStoreFromDump(
|
||||||
FileIngestionMethod method, HashAlgorithm hashAlgo, RepairFlag repair, const StorePathSet & references)
|
Source & dump,
|
||||||
|
std::string_view name,
|
||||||
|
ContentAddressMethod method,
|
||||||
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
|
RepairFlag repair)
|
||||||
{
|
{
|
||||||
|
std::optional<Hash> caHash;
|
||||||
|
std::string nar;
|
||||||
|
|
||||||
|
if (auto * dump2p = dynamic_cast<StringSource *>(&dump)) {
|
||||||
|
auto & dump2 = *dump2p;
|
||||||
|
// Hack, this gives us a "replayable" source so we can compute
|
||||||
|
// multiple hashes more easily.
|
||||||
|
caHash = hashString(HashAlgorithm::SHA256, dump2.s);
|
||||||
|
switch (method.getFileIngestionMethod()) {
|
||||||
|
case FileIngestionMethod::Recursive:
|
||||||
|
// The dump is already NAR in this case, just use it.
|
||||||
|
nar = dump2.s;
|
||||||
|
break;
|
||||||
|
case FileIngestionMethod::Flat:
|
||||||
|
// The dump is Flat, so we need to convert it to NAR with a
|
||||||
|
// single file.
|
||||||
|
StringSink s;
|
||||||
|
dumpString(dump2.s, s);
|
||||||
|
nar = std::move(s.s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, we have to do th same hashing as NAR so our single
|
||||||
|
// hash will suffice for both purposes.
|
||||||
if (method != FileIngestionMethod::Recursive || hashAlgo != HashAlgorithm::SHA256)
|
if (method != FileIngestionMethod::Recursive || hashAlgo != HashAlgorithm::SHA256)
|
||||||
unsupported("addToStoreFromDump");
|
unsupported("addToStoreFromDump");
|
||||||
return addToStoreCommon(dump, repair, CheckSigs, [&](HashResult nar) {
|
}
|
||||||
|
StringSource narDump { nar };
|
||||||
|
|
||||||
|
// Use `narDump` if we wrote to `nar`.
|
||||||
|
Source & narDump2 = nar.size() > 0
|
||||||
|
? static_cast<Source &>(narDump)
|
||||||
|
: dump;
|
||||||
|
|
||||||
|
return addToStoreCommon(narDump2, repair, CheckSigs, [&](HashResult nar) {
|
||||||
ValidPathInfo info {
|
ValidPathInfo info {
|
||||||
*this,
|
*this,
|
||||||
name,
|
name,
|
||||||
FixedOutputInfo {
|
ContentAddressWithReferences::fromParts(
|
||||||
.method = method,
|
method,
|
||||||
.hash = nar.first,
|
caHash ? *caHash : nar.first,
|
||||||
.references = {
|
{
|
||||||
.others = references,
|
.others = references,
|
||||||
// caller is not capable of creating a self-reference, because this is content-addressed without modulus
|
// caller is not capable of creating a self-reference, because this is content-addressed without modulus
|
||||||
.self = false,
|
.self = false,
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
nar.first,
|
nar.first,
|
||||||
};
|
};
|
||||||
info.narSize = nar.second;
|
info.narSize = nar.second;
|
||||||
|
@ -400,71 +437,35 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
|
||||||
|
|
||||||
StorePath BinaryCacheStore::addToStore(
|
StorePath BinaryCacheStore::addToStore(
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
const Path & srcPath,
|
SourceAccessor & accessor,
|
||||||
FileIngestionMethod method,
|
const CanonPath & path,
|
||||||
|
ContentAddressMethod method,
|
||||||
HashAlgorithm hashAlgo,
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
PathFilter & filter,
|
PathFilter & filter,
|
||||||
RepairFlag repair,
|
RepairFlag repair)
|
||||||
const StorePathSet & references)
|
|
||||||
{
|
{
|
||||||
/* FIXME: Make BinaryCacheStore::addToStoreCommon support
|
/* FIXME: Make BinaryCacheStore::addToStoreCommon support
|
||||||
non-recursive+sha256 so we can just use the default
|
non-recursive+sha256 so we can just use the default
|
||||||
implementation of this method in terms of addToStoreFromDump. */
|
implementation of this method in terms of addToStoreFromDump. */
|
||||||
|
|
||||||
HashSink sink { hashAlgo };
|
auto h = hashPath(accessor, path, method.getFileIngestionMethod(), hashAlgo, filter).first;
|
||||||
if (method == FileIngestionMethod::Recursive) {
|
|
||||||
dumpPath(srcPath, sink, filter);
|
|
||||||
} else {
|
|
||||||
readFile(srcPath, sink);
|
|
||||||
}
|
|
||||||
auto h = sink.finish().first;
|
|
||||||
|
|
||||||
auto source = sinkToSource([&](Sink & sink) {
|
auto source = sinkToSource([&](Sink & sink) {
|
||||||
dumpPath(srcPath, sink, filter);
|
accessor.dumpPath(path, sink, filter);
|
||||||
});
|
});
|
||||||
return addToStoreCommon(*source, repair, CheckSigs, [&](HashResult nar) {
|
return addToStoreCommon(*source, repair, CheckSigs, [&](HashResult nar) {
|
||||||
ValidPathInfo info {
|
ValidPathInfo info {
|
||||||
*this,
|
*this,
|
||||||
name,
|
name,
|
||||||
FixedOutputInfo {
|
ContentAddressWithReferences::fromParts(
|
||||||
.method = method,
|
method,
|
||||||
.hash = h,
|
h,
|
||||||
.references = {
|
{
|
||||||
.others = references,
|
.others = references,
|
||||||
// caller is not capable of creating a self-reference, because this is content-addressed without modulus
|
// caller is not capable of creating a self-reference, because this is content-addressed without modulus
|
||||||
.self = false,
|
.self = false,
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
nar.first,
|
|
||||||
};
|
|
||||||
info.narSize = nar.second;
|
|
||||||
return info;
|
|
||||||
})->path;
|
|
||||||
}
|
|
||||||
|
|
||||||
StorePath BinaryCacheStore::addTextToStore(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references,
|
|
||||||
RepairFlag repair)
|
|
||||||
{
|
|
||||||
auto textHash = hashString(HashAlgorithm::SHA256, s);
|
|
||||||
auto path = makeTextPath(name, TextInfo { { textHash }, references });
|
|
||||||
|
|
||||||
if (!repair && isValidPath(path))
|
|
||||||
return path;
|
|
||||||
|
|
||||||
StringSink sink;
|
|
||||||
dumpString(s, sink);
|
|
||||||
StringSource source(sink.s);
|
|
||||||
return addToStoreCommon(source, repair, CheckSigs, [&](HashResult nar) {
|
|
||||||
ValidPathInfo info {
|
|
||||||
*this,
|
|
||||||
std::string { name },
|
|
||||||
TextInfo {
|
|
||||||
.hash = textHash,
|
|
||||||
.references = references,
|
|
||||||
},
|
|
||||||
nar.first,
|
nar.first,
|
||||||
};
|
};
|
||||||
info.narSize = nar.second;
|
info.narSize = nar.second;
|
||||||
|
|
|
@ -123,22 +123,22 @@ public:
|
||||||
void addToStore(const ValidPathInfo & info, Source & narSource,
|
void addToStore(const ValidPathInfo & info, Source & narSource,
|
||||||
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
||||||
|
|
||||||
StorePath addToStoreFromDump(Source & dump, std::string_view name,
|
StorePath addToStoreFromDump(
|
||||||
FileIngestionMethod method, HashAlgorithm hashAlgo, RepairFlag repair, const StorePathSet & references) override;
|
Source & dump,
|
||||||
|
std::string_view name,
|
||||||
|
ContentAddressMethod method,
|
||||||
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
|
RepairFlag repair) override;
|
||||||
|
|
||||||
StorePath addToStore(
|
StorePath addToStore(
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
const Path & srcPath,
|
SourceAccessor & accessor,
|
||||||
FileIngestionMethod method,
|
const CanonPath & srcPath,
|
||||||
|
ContentAddressMethod method,
|
||||||
HashAlgorithm hashAlgo,
|
HashAlgorithm hashAlgo,
|
||||||
PathFilter & filter,
|
|
||||||
RepairFlag repair,
|
|
||||||
const StorePathSet & references) override;
|
|
||||||
|
|
||||||
StorePath addTextToStore(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references,
|
const StorePathSet & references,
|
||||||
|
PathFilter & filter,
|
||||||
RepairFlag repair) override;
|
RepairFlag repair) override;
|
||||||
|
|
||||||
void registerDrvOutput(const Realisation & info) override;
|
void registerDrvOutput(const Realisation & info) override;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "child.hh"
|
#include "child.hh"
|
||||||
#include "unix-domain-socket.hh"
|
#include "unix-domain-socket.hh"
|
||||||
#include "posix-fs-canonicalise.hh"
|
#include "posix-fs-canonicalise.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
@ -1291,12 +1292,13 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual In
|
||||||
|
|
||||||
StorePath addToStore(
|
StorePath addToStore(
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
const Path & srcPath,
|
SourceAccessor & accessor,
|
||||||
FileIngestionMethod method,
|
const CanonPath & srcPath,
|
||||||
|
ContentAddressMethod method,
|
||||||
HashAlgorithm hashAlgo,
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
PathFilter & filter,
|
PathFilter & filter,
|
||||||
RepairFlag repair,
|
RepairFlag repair) override
|
||||||
const StorePathSet & references) override
|
|
||||||
{ throw Error("addToStore"); }
|
{ throw Error("addToStore"); }
|
||||||
|
|
||||||
void addToStore(const ValidPathInfo & info, Source & narSource,
|
void addToStore(const ValidPathInfo & info, Source & narSource,
|
||||||
|
@ -1306,26 +1308,15 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual In
|
||||||
goal.addDependency(info.path);
|
goal.addDependency(info.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath addTextToStore(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references,
|
|
||||||
RepairFlag repair = NoRepair) override
|
|
||||||
{
|
|
||||||
auto path = next->addTextToStore(name, s, references, repair);
|
|
||||||
goal.addDependency(path);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
StorePath addToStoreFromDump(
|
StorePath addToStoreFromDump(
|
||||||
Source & dump,
|
Source & dump,
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
FileIngestionMethod method,
|
ContentAddressMethod method,
|
||||||
HashAlgorithm hashAlgo,
|
HashAlgorithm hashAlgo,
|
||||||
RepairFlag repair,
|
const StorePathSet & references,
|
||||||
const StorePathSet & references) override
|
RepairFlag repair) override
|
||||||
{
|
{
|
||||||
auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, repair, references);
|
auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, references, repair);
|
||||||
goal.addDependency(path);
|
goal.addDependency(path);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
@ -2453,8 +2444,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
throw BuildError(
|
throw BuildError(
|
||||||
"output path %1% without valid stats info",
|
"output path %1% without valid stats info",
|
||||||
actualPath);
|
actualPath);
|
||||||
if (outputHash.method == ContentAddressMethod { FileIngestionMethod::Flat } ||
|
if (outputHash.method.getFileIngestionMethod() == FileIngestionMethod::Flat)
|
||||||
outputHash.method == ContentAddressMethod { TextIngestionMethod {} })
|
|
||||||
{
|
{
|
||||||
/* The output path should be a regular file without execute permission. */
|
/* The output path should be a regular file without execute permission. */
|
||||||
if (!S_ISREG(st->st_mode) || (st->st_mode & S_IXUSR) != 0)
|
if (!S_ISREG(st->st_mode) || (st->st_mode & S_IXUSR) != 0)
|
||||||
|
@ -2466,38 +2456,23 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
rewriteOutput(outputRewrites);
|
rewriteOutput(outputRewrites);
|
||||||
/* FIXME optimize and deduplicate with addToStore */
|
/* FIXME optimize and deduplicate with addToStore */
|
||||||
std::string oldHashPart { scratchPath->hashPart() };
|
std::string oldHashPart { scratchPath->hashPart() };
|
||||||
HashModuloSink caSink {outputHash.hashAlgo, oldHashPart };
|
auto got = ({
|
||||||
std::visit(overloaded {
|
HashModuloSink caSink { outputHash.hashAlgo, oldHashPart };
|
||||||
[&](const TextIngestionMethod &) {
|
PosixSourceAccessor accessor;
|
||||||
readFile(actualPath, caSink);
|
dumpPath(
|
||||||
},
|
accessor, CanonPath { actualPath },
|
||||||
[&](const FileIngestionMethod & m2) {
|
caSink,
|
||||||
switch (m2) {
|
outputHash.method.getFileIngestionMethod());
|
||||||
case FileIngestionMethod::Recursive:
|
caSink.finish().first;
|
||||||
dumpPath(actualPath, caSink);
|
});
|
||||||
break;
|
|
||||||
case FileIngestionMethod::Flat:
|
|
||||||
readFile(actualPath, caSink);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, outputHash.method.raw);
|
|
||||||
auto got = caSink.finish().first;
|
|
||||||
|
|
||||||
auto optCA = ContentAddressWithReferences::fromPartsOpt(
|
|
||||||
outputHash.method,
|
|
||||||
std::move(got),
|
|
||||||
rewriteRefs());
|
|
||||||
if (!optCA) {
|
|
||||||
// TODO track distinct failure modes separately (at the time of
|
|
||||||
// writing there is just one but `nullopt` is unclear) so this
|
|
||||||
// message can't get out of sync.
|
|
||||||
throw BuildError("output path '%s' has illegal content address, probably a spurious self-reference with text hashing");
|
|
||||||
}
|
|
||||||
ValidPathInfo newInfo0 {
|
ValidPathInfo newInfo0 {
|
||||||
worker.store,
|
worker.store,
|
||||||
outputPathName(drv->name, outputName),
|
outputPathName(drv->name, outputName),
|
||||||
std::move(*optCA),
|
ContentAddressWithReferences::fromParts(
|
||||||
|
outputHash.method,
|
||||||
|
std::move(got),
|
||||||
|
rewriteRefs()),
|
||||||
Hash::dummy,
|
Hash::dummy,
|
||||||
};
|
};
|
||||||
if (*scratchPath != newInfo0.path) {
|
if (*scratchPath != newInfo0.path) {
|
||||||
|
@ -2511,9 +2486,14 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
std::string(newInfo0.path.hashPart())}});
|
std::string(newInfo0.path.hashPart())}});
|
||||||
}
|
}
|
||||||
|
|
||||||
HashResult narHashAndSize = hashPath(HashAlgorithm::SHA256, actualPath);
|
{
|
||||||
|
PosixSourceAccessor accessor;
|
||||||
|
HashResult narHashAndSize = hashPath(
|
||||||
|
accessor, CanonPath { actualPath },
|
||||||
|
FileIngestionMethod::Recursive, HashAlgorithm::SHA256);
|
||||||
newInfo0.narHash = narHashAndSize.first;
|
newInfo0.narHash = narHashAndSize.first;
|
||||||
newInfo0.narSize = narHashAndSize.second;
|
newInfo0.narSize = narHashAndSize.second;
|
||||||
|
}
|
||||||
|
|
||||||
assert(newInfo0.ca);
|
assert(newInfo0.ca);
|
||||||
return newInfo0;
|
return newInfo0;
|
||||||
|
@ -2531,7 +2511,10 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
std::string { scratchPath->hashPart() },
|
std::string { scratchPath->hashPart() },
|
||||||
std::string { requiredFinalPath.hashPart() });
|
std::string { requiredFinalPath.hashPart() });
|
||||||
rewriteOutput(outputRewrites);
|
rewriteOutput(outputRewrites);
|
||||||
auto narHashAndSize = hashPath(HashAlgorithm::SHA256, actualPath);
|
PosixSourceAccessor accessor;
|
||||||
|
HashResult narHashAndSize = hashPath(
|
||||||
|
accessor, CanonPath { actualPath },
|
||||||
|
FileIngestionMethod::Recursive, HashAlgorithm::SHA256);
|
||||||
ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first };
|
ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first };
|
||||||
newInfo0.narSize = narHashAndSize.second;
|
newInfo0.narSize = narHashAndSize.second;
|
||||||
auto refs = rewriteRefs();
|
auto refs = rewriteRefs();
|
||||||
|
|
|
@ -68,6 +68,7 @@ R""(
|
||||||
(allow file*
|
(allow file*
|
||||||
(literal "/dev/null")
|
(literal "/dev/null")
|
||||||
(literal "/dev/random")
|
(literal "/dev/random")
|
||||||
|
(literal "/dev/stderr")
|
||||||
(literal "/dev/stdin")
|
(literal "/dev/stdin")
|
||||||
(literal "/dev/stdout")
|
(literal "/dev/stdout")
|
||||||
(literal "/dev/tty")
|
(literal "/dev/tty")
|
||||||
|
|
|
@ -519,7 +519,9 @@ bool Worker::pathContentsGood(const StorePath & path)
|
||||||
if (!pathExists(store.printStorePath(path)))
|
if (!pathExists(store.printStorePath(path)))
|
||||||
res = false;
|
res = false;
|
||||||
else {
|
else {
|
||||||
HashResult current = hashPath(info->narHash.algo, store.printStorePath(path));
|
HashResult current = hashPath(
|
||||||
|
*store.getFSAccessor(), CanonPath { store.printStorePath(path) },
|
||||||
|
FileIngestionMethod::Recursive, info->narHash.algo);
|
||||||
Hash nullHash(HashAlgorithm::SHA256);
|
Hash nullHash(HashAlgorithm::SHA256);
|
||||||
res = info->narHash == nullHash || info->narHash == current.first;
|
res = info->narHash == nullHash || info->narHash == current.first;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,18 @@ std::string ContentAddressMethod::render(HashAlgorithm ha) const
|
||||||
}, raw);
|
}, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileIngestionMethod ContentAddressMethod::getFileIngestionMethod() const
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const TextIngestionMethod & th) {
|
||||||
|
return FileIngestionMethod::Flat;
|
||||||
|
},
|
||||||
|
[&](const FileIngestionMethod & fim) {
|
||||||
|
return fim;
|
||||||
|
}
|
||||||
|
}, raw);
|
||||||
|
}
|
||||||
|
|
||||||
std::string ContentAddress::render() const
|
std::string ContentAddress::render() const
|
||||||
{
|
{
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
|
@ -79,7 +91,7 @@ static std::pair<ContentAddressMethod, HashAlgorithm> parseContentAddressMethodP
|
||||||
prefix = *optPrefix;
|
prefix = *optPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto parseHashType_ = [&](){
|
auto parseHashAlgorithm_ = [&](){
|
||||||
auto hashTypeRaw = splitPrefixTo(rest, ':');
|
auto hashTypeRaw = splitPrefixTo(rest, ':');
|
||||||
if (!hashTypeRaw)
|
if (!hashTypeRaw)
|
||||||
throw UsageError("content address hash must be in form '<algo>:<hash>', but found: %s", wholeInput);
|
throw UsageError("content address hash must be in form '<algo>:<hash>', but found: %s", wholeInput);
|
||||||
|
@ -90,7 +102,7 @@ static std::pair<ContentAddressMethod, HashAlgorithm> parseContentAddressMethodP
|
||||||
// Switch on prefix
|
// Switch on prefix
|
||||||
if (prefix == "text") {
|
if (prefix == "text") {
|
||||||
// No parsing of the ingestion method, "text" only support flat.
|
// No parsing of the ingestion method, "text" only support flat.
|
||||||
HashAlgorithm hashAlgo = parseHashType_();
|
HashAlgorithm hashAlgo = parseHashAlgorithm_();
|
||||||
return {
|
return {
|
||||||
TextIngestionMethod {},
|
TextIngestionMethod {},
|
||||||
std::move(hashAlgo),
|
std::move(hashAlgo),
|
||||||
|
@ -100,7 +112,7 @@ static std::pair<ContentAddressMethod, HashAlgorithm> parseContentAddressMethodP
|
||||||
auto method = FileIngestionMethod::Flat;
|
auto method = FileIngestionMethod::Flat;
|
||||||
if (splitPrefix(rest, "r:"))
|
if (splitPrefix(rest, "r:"))
|
||||||
method = FileIngestionMethod::Recursive;
|
method = FileIngestionMethod::Recursive;
|
||||||
HashAlgorithm hashAlgo = parseHashType_();
|
HashAlgorithm hashAlgo = parseHashAlgorithm_();
|
||||||
return {
|
return {
|
||||||
std::move(method),
|
std::move(method),
|
||||||
std::move(hashAlgo),
|
std::move(hashAlgo),
|
||||||
|
@ -176,13 +188,13 @@ ContentAddressWithReferences ContentAddressWithReferences::withoutRefs(const Con
|
||||||
}, ca.method.raw);
|
}, ca.method.raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ContentAddressWithReferences> ContentAddressWithReferences::fromPartsOpt(
|
ContentAddressWithReferences ContentAddressWithReferences::fromParts(
|
||||||
ContentAddressMethod method, Hash hash, StoreReferences refs) noexcept
|
ContentAddressMethod method, Hash hash, StoreReferences refs)
|
||||||
{
|
{
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[&](TextIngestionMethod _) -> std::optional<ContentAddressWithReferences> {
|
[&](TextIngestionMethod _) -> ContentAddressWithReferences {
|
||||||
if (refs.self)
|
if (refs.self)
|
||||||
return std::nullopt;
|
throw Error("self-reference not allowed with text hashing");
|
||||||
return ContentAddressWithReferences {
|
return ContentAddressWithReferences {
|
||||||
TextInfo {
|
TextInfo {
|
||||||
.hash = std::move(hash),
|
.hash = std::move(hash),
|
||||||
|
@ -190,7 +202,7 @@ std::optional<ContentAddressWithReferences> ContentAddressWithReferences::fromPa
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[&](FileIngestionMethod m2) -> std::optional<ContentAddressWithReferences> {
|
[&](FileIngestionMethod m2) -> ContentAddressWithReferences {
|
||||||
return ContentAddressWithReferences {
|
return ContentAddressWithReferences {
|
||||||
FixedOutputInfo {
|
FixedOutputInfo {
|
||||||
.method = m2,
|
.method = m2,
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
#include "path.hh"
|
#include "path.hh"
|
||||||
|
#include "file-content-address.hh"
|
||||||
#include "comparator.hh"
|
#include "comparator.hh"
|
||||||
#include "variant-wrapper.hh"
|
#include "variant-wrapper.hh"
|
||||||
|
|
||||||
|
@ -31,22 +32,6 @@ namespace nix {
|
||||||
*/
|
*/
|
||||||
struct TextIngestionMethod : std::monostate { };
|
struct TextIngestionMethod : std::monostate { };
|
||||||
|
|
||||||
/**
|
|
||||||
* An enumeration of the main ways we can serialize file system
|
|
||||||
* objects.
|
|
||||||
*/
|
|
||||||
enum struct FileIngestionMethod : uint8_t {
|
|
||||||
/**
|
|
||||||
* Flat-file hashing. Directly ingest the contents of a single file
|
|
||||||
*/
|
|
||||||
Flat = 0,
|
|
||||||
/**
|
|
||||||
* Recursive (or NAR) hashing. Serializes the file-system object in Nix
|
|
||||||
* Archive format and ingest that
|
|
||||||
*/
|
|
||||||
Recursive = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the prefix to the hash algorithm which indicates how the
|
* Compute the prefix to the hash algorithm which indicates how the
|
||||||
* files were ingested.
|
* files were ingested.
|
||||||
|
@ -54,7 +39,7 @@ enum struct FileIngestionMethod : uint8_t {
|
||||||
std::string makeFileIngestionPrefix(FileIngestionMethod m);
|
std::string makeFileIngestionPrefix(FileIngestionMethod m);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An enumeration of all the ways we can serialize file system objects.
|
* An enumeration of all the ways we can content-address store objects.
|
||||||
*
|
*
|
||||||
* Just the type of a content address. Combine with the hash itself, and
|
* Just the type of a content address. Combine with the hash itself, and
|
||||||
* we have a `ContentAddress` as defined below. Combine that, in turn,
|
* we have a `ContentAddress` as defined below. Combine that, in turn,
|
||||||
|
@ -102,7 +87,15 @@ struct ContentAddressMethod
|
||||||
*
|
*
|
||||||
* The rough inverse of `parse()`.
|
* The rough inverse of `parse()`.
|
||||||
*/
|
*/
|
||||||
std::string render(HashAlgorithm ha) const;
|
std::string render(HashAlgorithm ht) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the underlying way to content-address file system objects.
|
||||||
|
*
|
||||||
|
* Different ways of hashing store objects may use the same method
|
||||||
|
* for hashing file systeme objects.
|
||||||
|
*/
|
||||||
|
FileIngestionMethod getFileIngestionMethod() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,11 +109,11 @@ struct ContentAddressMethod
|
||||||
* serialisation methods (flat file vs NAR). Thus, ‘ca’ has one of the
|
* serialisation methods (flat file vs NAR). Thus, ‘ca’ has one of the
|
||||||
* following forms:
|
* following forms:
|
||||||
*
|
*
|
||||||
* - ‘text:sha256:<sha256 hash of file contents>’: For paths
|
* - `TextIngestionMethod`:
|
||||||
* computed by Store::makeTextPath() / Store::addTextToStore().
|
* ‘text:sha256:<sha256 hash of file contents>’
|
||||||
*
|
*
|
||||||
* - ‘fixed:<r?>:<ht>:<h>’: For paths computed by
|
* - `FixedIngestionMethod`:
|
||||||
* Store::makeFixedOutputPath() / Store::addToStore().
|
* ‘fixed:<r?>:<hash type>:<hash of file contents>’
|
||||||
*/
|
*/
|
||||||
struct ContentAddress
|
struct ContentAddress
|
||||||
{
|
{
|
||||||
|
@ -266,11 +259,12 @@ struct ContentAddressWithReferences
|
||||||
*
|
*
|
||||||
* @param refs References to other store objects or oneself.
|
* @param refs References to other store objects or oneself.
|
||||||
*
|
*
|
||||||
* Do note that not all combinations are supported; `nullopt` is
|
* @note note that all combinations are supported. This is a
|
||||||
* returns for invalid combinations.
|
* *partial function* and exceptions will be thrown for invalid
|
||||||
|
* combinations.
|
||||||
*/
|
*/
|
||||||
static std::optional<ContentAddressWithReferences> fromPartsOpt(
|
static ContentAddressWithReferences fromParts(
|
||||||
ContentAddressMethod method, Hash hash, StoreReferences refs) noexcept;
|
ContentAddressMethod method, Hash hash, StoreReferences refs);
|
||||||
|
|
||||||
ContentAddressMethod getMethod() const;
|
ContentAddressMethod getMethod() const;
|
||||||
|
|
||||||
|
|
|
@ -403,22 +403,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
auto [contentAddressMethod, hashAlgo_] = ContentAddressMethod::parse(camStr);
|
auto [contentAddressMethod, hashAlgo_] = ContentAddressMethod::parse(camStr);
|
||||||
auto hashAlgo = hashAlgo_; // work around clang bug
|
auto hashAlgo = hashAlgo_; // work around clang bug
|
||||||
FramedSource source(from);
|
FramedSource source(from);
|
||||||
// TODO this is essentially RemoteStore::addCAToStore. Move it up to Store.
|
// TODO these two steps are essentially RemoteStore::addCAToStore. Move it up to Store.
|
||||||
return std::visit(overloaded {
|
auto path = store->addToStoreFromDump(source, name, contentAddressMethod, hashAlgo, refs, repair);
|
||||||
[&](const TextIngestionMethod &) {
|
|
||||||
if (hashAlgo != HashAlgorithm::SHA256)
|
|
||||||
throw UnimplementedError("When adding text-hashed data called '%s', only SHA-256 is supported but '%s' was given",
|
|
||||||
name, printHashAlgo(hashAlgo));
|
|
||||||
// We could stream this by changing Store
|
|
||||||
std::string contents = source.drain();
|
|
||||||
auto path = store->addTextToStore(name, contents, refs, repair);
|
|
||||||
return store->queryPathInfo(path);
|
return store->queryPathInfo(path);
|
||||||
},
|
|
||||||
[&](const FileIngestionMethod & fim) {
|
|
||||||
auto path = store->addToStoreFromDump(source, name, fim, hashAlgo, repair, refs);
|
|
||||||
return store->queryPathInfo(path);
|
|
||||||
},
|
|
||||||
}, contentAddressMethod.raw);
|
|
||||||
}();
|
}();
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
|
|
||||||
|
@ -496,7 +483,10 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
||||||
std::string s = readString(from);
|
std::string s = readString(from);
|
||||||
auto refs = WorkerProto::Serialise<StorePathSet>::read(*store, rconn);
|
auto refs = WorkerProto::Serialise<StorePathSet>::read(*store, rconn);
|
||||||
logger->startWork();
|
logger->startWork();
|
||||||
auto path = store->addTextToStore(suffix, s, refs, NoRepair);
|
auto path = ({
|
||||||
|
StringSource source { s };
|
||||||
|
store->addToStoreFromDump(source, suffix, TextIngestionMethod {}, HashAlgorithm::SHA256, refs, NoRepair);
|
||||||
|
});
|
||||||
logger->stopWork();
|
logger->stopWork();
|
||||||
to << store->printStorePath(path);
|
to << store->printStorePath(path);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -143,8 +143,14 @@ StorePath writeDerivation(Store & store,
|
||||||
auto suffix = std::string(drv.name) + drvExtension;
|
auto suffix = std::string(drv.name) + drvExtension;
|
||||||
auto contents = drv.unparse(store, false);
|
auto contents = drv.unparse(store, false);
|
||||||
return readOnly || settings.readOnlyMode
|
return readOnly || settings.readOnlyMode
|
||||||
? store.computeStorePathForText(suffix, contents, references)
|
? store.makeFixedOutputPathFromCA(suffix, TextInfo {
|
||||||
: store.addTextToStore(suffix, contents, references, repair);
|
.hash = hashString(HashAlgorithm::SHA256, contents),
|
||||||
|
.references = std::move(references),
|
||||||
|
})
|
||||||
|
: ({
|
||||||
|
StringSource s { contents };
|
||||||
|
store.addToStoreFromDump(s, suffix, TextIngestionMethod {}, HashAlgorithm::SHA256, references, repair);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,13 +58,6 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store
|
||||||
RepairFlag repair, CheckSigsFlag checkSigs) override
|
RepairFlag repair, CheckSigsFlag checkSigs) override
|
||||||
{ unsupported("addToStore"); }
|
{ unsupported("addToStore"); }
|
||||||
|
|
||||||
StorePath addTextToStore(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references,
|
|
||||||
RepairFlag repair) override
|
|
||||||
{ unsupported("addTextToStore"); }
|
|
||||||
|
|
||||||
void narFromPath(const StorePath & path, Sink & sink) override
|
void narFromPath(const StorePath & path, Sink & sink) override
|
||||||
{ unsupported("narFromPath"); }
|
{ unsupported("narFromPath"); }
|
||||||
|
|
||||||
|
|
|
@ -631,7 +631,7 @@ public:
|
||||||
|
|
||||||
At least one of the following condition must be met
|
At least one of the following condition must be met
|
||||||
for Nix to accept copying a store object from another
|
for Nix to accept copying a store object from another
|
||||||
Nix store (such as a substituter):
|
Nix store (such as a [substituter](#conf-substituters)):
|
||||||
|
|
||||||
- the store object has been signed using a key in the trusted keys list
|
- the store object has been signed using a key in the trusted keys list
|
||||||
- the [`require-sigs`](#conf-require-sigs) option has been set to `false`
|
- the [`require-sigs`](#conf-require-sigs) option has been set to `false`
|
||||||
|
|
|
@ -60,20 +60,14 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
|
||||||
|
|
||||||
StorePath addToStore(
|
StorePath addToStore(
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
const Path & srcPath,
|
SourceAccessor & accessor,
|
||||||
FileIngestionMethod method,
|
const CanonPath & srcPath,
|
||||||
|
ContentAddressMethod method,
|
||||||
HashAlgorithm hashAlgo,
|
HashAlgorithm hashAlgo,
|
||||||
PathFilter & filter,
|
|
||||||
RepairFlag repair,
|
|
||||||
const StorePathSet & references) override
|
|
||||||
{ unsupported("addToStore"); }
|
|
||||||
|
|
||||||
StorePath addTextToStore(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references,
|
const StorePathSet & references,
|
||||||
|
PathFilter & filter,
|
||||||
RepairFlag repair) override
|
RepairFlag repair) override
|
||||||
{ unsupported("addTextToStore"); }
|
{ unsupported("addToStore"); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "compression.hh"
|
#include "compression.hh"
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "posix-fs-canonicalise.hh"
|
#include "posix-fs-canonicalise.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -1088,11 +1089,22 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
|
|
||||||
if (info.ca) {
|
if (info.ca) {
|
||||||
auto & specified = *info.ca;
|
auto & specified = *info.ca;
|
||||||
auto actualHash = hashCAPath(
|
auto actualHash = ({
|
||||||
specified.method,
|
HashModuloSink caSink {
|
||||||
specified.hash.algo,
|
specified.hash.algo,
|
||||||
info.path
|
std::string { info.path.hashPart() },
|
||||||
);
|
};
|
||||||
|
PosixSourceAccessor accessor;
|
||||||
|
dumpPath(
|
||||||
|
*getFSAccessor(false),
|
||||||
|
CanonPath { printStorePath(info.path) },
|
||||||
|
caSink,
|
||||||
|
specified.method.getFileIngestionMethod());
|
||||||
|
ContentAddress {
|
||||||
|
.method = specified.method,
|
||||||
|
.hash = caSink.finish().first,
|
||||||
|
};
|
||||||
|
});
|
||||||
if (specified.hash != actualHash.hash) {
|
if (specified.hash != actualHash.hash) {
|
||||||
throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
|
throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
|
||||||
printStorePath(info.path),
|
printStorePath(info.path),
|
||||||
|
@ -1115,8 +1127,13 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name,
|
StorePath LocalStore::addToStoreFromDump(
|
||||||
FileIngestionMethod method, HashAlgorithm hashAlgo, RepairFlag repair, const StorePathSet & references)
|
Source & source0,
|
||||||
|
std::string_view name,
|
||||||
|
ContentAddressMethod method,
|
||||||
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
|
RepairFlag repair)
|
||||||
{
|
{
|
||||||
/* For computing the store path. */
|
/* For computing the store path. */
|
||||||
auto hashSink = std::make_unique<HashSink>(hashAlgo);
|
auto hashSink = std::make_unique<HashSink>(hashAlgo);
|
||||||
|
@ -1166,25 +1183,21 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name
|
||||||
delTempDir = std::make_unique<AutoDelete>(tempDir);
|
delTempDir = std::make_unique<AutoDelete>(tempDir);
|
||||||
tempPath = tempDir + "/x";
|
tempPath = tempDir + "/x";
|
||||||
|
|
||||||
if (method == FileIngestionMethod::Recursive)
|
restorePath(tempPath, bothSource, method.getFileIngestionMethod());
|
||||||
restorePath(tempPath, bothSource);
|
|
||||||
else
|
|
||||||
writeFile(tempPath, bothSource);
|
|
||||||
|
|
||||||
dump.clear();
|
dump.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [hash, size] = hashSink->finish();
|
auto [hash, size] = hashSink->finish();
|
||||||
|
|
||||||
ContentAddressWithReferences desc = FixedOutputInfo {
|
auto desc = ContentAddressWithReferences::fromParts(
|
||||||
.method = method,
|
method,
|
||||||
.hash = hash,
|
hash,
|
||||||
.references = {
|
{
|
||||||
.others = references,
|
.others = references,
|
||||||
// caller is not capable of creating a self-reference, because this is content-addressed without modulus
|
// caller is not capable of creating a self-reference, because this is content-addressed without modulus
|
||||||
.self = false,
|
.self = false,
|
||||||
},
|
});
|
||||||
};
|
|
||||||
|
|
||||||
auto dstPath = makeFixedOutputPathFromCA(name, desc);
|
auto dstPath = makeFixedOutputPathFromCA(name, desc);
|
||||||
|
|
||||||
|
@ -1207,11 +1220,8 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name
|
||||||
|
|
||||||
if (inMemory) {
|
if (inMemory) {
|
||||||
StringSource dumpSource { dump };
|
StringSource dumpSource { dump };
|
||||||
/* Restore from the NAR in memory. */
|
/* Restore from the buffer in memory. */
|
||||||
if (method == FileIngestionMethod::Recursive)
|
restorePath(realPath, dumpSource, method.getFileIngestionMethod());
|
||||||
restorePath(realPath, dumpSource);
|
|
||||||
else
|
|
||||||
writeFile(realPath, dumpSource);
|
|
||||||
} else {
|
} else {
|
||||||
/* Move the temporary path we restored above. */
|
/* Move the temporary path we restored above. */
|
||||||
moveFile(tempPath, realPath);
|
moveFile(tempPath, realPath);
|
||||||
|
@ -1247,58 +1257,6 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath LocalStore::addTextToStore(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references, RepairFlag repair)
|
|
||||||
{
|
|
||||||
auto hash = hashString(HashAlgorithm::SHA256, s);
|
|
||||||
auto dstPath = makeTextPath(name, TextInfo {
|
|
||||||
.hash = hash,
|
|
||||||
.references = references,
|
|
||||||
});
|
|
||||||
|
|
||||||
addTempRoot(dstPath);
|
|
||||||
|
|
||||||
if (repair || !isValidPath(dstPath)) {
|
|
||||||
|
|
||||||
auto realPath = Store::toRealPath(dstPath);
|
|
||||||
|
|
||||||
PathLocks outputLock({realPath});
|
|
||||||
|
|
||||||
if (repair || !isValidPath(dstPath)) {
|
|
||||||
|
|
||||||
deletePath(realPath);
|
|
||||||
|
|
||||||
autoGC();
|
|
||||||
|
|
||||||
writeFile(realPath, s);
|
|
||||||
|
|
||||||
canonicalisePathMetaData(realPath, {});
|
|
||||||
|
|
||||||
StringSink sink;
|
|
||||||
dumpString(s, sink);
|
|
||||||
auto narHash = hashString(HashAlgorithm::SHA256, sink.s);
|
|
||||||
|
|
||||||
optimisePath(realPath, repair);
|
|
||||||
|
|
||||||
ValidPathInfo info { dstPath, narHash };
|
|
||||||
info.narSize = sink.s.size();
|
|
||||||
info.references = references;
|
|
||||||
info.ca = {
|
|
||||||
.method = TextIngestionMethod {},
|
|
||||||
.hash = hash,
|
|
||||||
};
|
|
||||||
registerValidPath(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
outputLock.setDeletion(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dstPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Create a temporary directory in the store that won't be
|
/* Create a temporary directory in the store that won't be
|
||||||
garbage-collected until the returned FD is closed. */
|
garbage-collected until the returned FD is closed. */
|
||||||
std::pair<Path, AutoCloseFD> LocalStore::createTempDirInStore()
|
std::pair<Path, AutoCloseFD> LocalStore::createTempDirInStore()
|
||||||
|
@ -1389,7 +1347,10 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
||||||
for (auto & link : readDirectory(linksDir)) {
|
for (auto & link : readDirectory(linksDir)) {
|
||||||
printMsg(lvlTalkative, "checking contents of '%s'", link.name);
|
printMsg(lvlTalkative, "checking contents of '%s'", link.name);
|
||||||
Path linkPath = linksDir + "/" + link.name;
|
Path linkPath = linksDir + "/" + link.name;
|
||||||
std::string hash = hashPath(HashAlgorithm::SHA256, linkPath).first.to_string(HashFormat::Nix32, false);
|
PosixSourceAccessor accessor;
|
||||||
|
std::string hash = hashPath(
|
||||||
|
accessor, CanonPath { linkPath },
|
||||||
|
FileIngestionMethod::Recursive, HashAlgorithm::SHA256).first.to_string(HashFormat::Nix32, false);
|
||||||
if (hash != link.name) {
|
if (hash != link.name) {
|
||||||
printError("link '%s' was modified! expected hash '%s', got '%s'",
|
printError("link '%s' was modified! expected hash '%s', got '%s'",
|
||||||
linkPath, link.name, hash);
|
linkPath, link.name, hash);
|
||||||
|
@ -1696,42 +1657,6 @@ void LocalStore::queryRealisationUncached(const DrvOutput & id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentAddress LocalStore::hashCAPath(
|
|
||||||
const ContentAddressMethod & method, const HashAlgorithm & hashAlgo,
|
|
||||||
const StorePath & path)
|
|
||||||
{
|
|
||||||
return hashCAPath(method, hashAlgo, Store::toRealPath(path), path.hashPart());
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentAddress LocalStore::hashCAPath(
|
|
||||||
const ContentAddressMethod & method,
|
|
||||||
const HashAlgorithm & hashAlgo,
|
|
||||||
const Path & path,
|
|
||||||
const std::string_view pathHash
|
|
||||||
)
|
|
||||||
{
|
|
||||||
HashModuloSink caSink ( hashAlgo, std::string(pathHash) );
|
|
||||||
std::visit(overloaded {
|
|
||||||
[&](const TextIngestionMethod &) {
|
|
||||||
readFile(path, caSink);
|
|
||||||
},
|
|
||||||
[&](const FileIngestionMethod & m2) {
|
|
||||||
switch (m2) {
|
|
||||||
case FileIngestionMethod::Recursive:
|
|
||||||
dumpPath(path, caSink);
|
|
||||||
break;
|
|
||||||
case FileIngestionMethod::Flat:
|
|
||||||
readFile(path, caSink);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, method.raw);
|
|
||||||
return ContentAddress {
|
|
||||||
.method = method,
|
|
||||||
.hash = caSink.finish().first,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log)
|
void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log)
|
||||||
{
|
{
|
||||||
assert(drvPath.isDerivation());
|
assert(drvPath.isDerivation());
|
||||||
|
|
|
@ -177,12 +177,11 @@ public:
|
||||||
void addToStore(const ValidPathInfo & info, Source & source,
|
void addToStore(const ValidPathInfo & info, Source & source,
|
||||||
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
||||||
|
|
||||||
StorePath addToStoreFromDump(Source & dump, std::string_view name,
|
StorePath addToStoreFromDump(
|
||||||
FileIngestionMethod method, HashAlgorithm hashAlgo, RepairFlag repair, const StorePathSet & references) override;
|
Source & dump,
|
||||||
|
|
||||||
StorePath addTextToStore(
|
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
std::string_view s,
|
ContentAddressMethod method,
|
||||||
|
HashAlgorithm hashAlgo,
|
||||||
const StorePathSet & references,
|
const StorePathSet & references,
|
||||||
RepairFlag repair) override;
|
RepairFlag repair) override;
|
||||||
|
|
||||||
|
@ -350,19 +349,6 @@ private:
|
||||||
void signPathInfo(ValidPathInfo & info);
|
void signPathInfo(ValidPathInfo & info);
|
||||||
void signRealisation(Realisation &);
|
void signRealisation(Realisation &);
|
||||||
|
|
||||||
// XXX: Make a generic `Store` method
|
|
||||||
ContentAddress hashCAPath(
|
|
||||||
const ContentAddressMethod & method,
|
|
||||||
const HashAlgorithm & hashAlgo,
|
|
||||||
const StorePath & path);
|
|
||||||
|
|
||||||
ContentAddress hashCAPath(
|
|
||||||
const ContentAddressMethod & method,
|
|
||||||
const HashAlgorithm & hashAlgo,
|
|
||||||
const Path & path,
|
|
||||||
const std::string_view pathHash
|
|
||||||
);
|
|
||||||
|
|
||||||
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
|
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
|
||||||
|
|
||||||
friend struct LocalDerivationGoal;
|
friend struct LocalDerivationGoal;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
#include "posix-fs-canonicalise.hh"
|
#include "posix-fs-canonicalise.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -146,7 +147,12 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
Also note that if `path' is a symlink, then we're hashing the
|
Also note that if `path' is a symlink, then we're hashing the
|
||||||
contents of the symlink (i.e. the result of readlink()), not
|
contents of the symlink (i.e. the result of readlink()), not
|
||||||
the contents of the target (which may not even exist). */
|
the contents of the target (which may not even exist). */
|
||||||
Hash hash = hashPath(HashAlgorithm::SHA256, path).first;
|
Hash hash = ({
|
||||||
|
PosixSourceAccessor accessor;
|
||||||
|
hashPath(
|
||||||
|
accessor, CanonPath { path },
|
||||||
|
FileIngestionMethod::Recursive, HashAlgorithm::SHA256).first;
|
||||||
|
});
|
||||||
debug("'%1%' has hash '%2%'", path, hash.to_string(HashFormat::Nix32, true));
|
debug("'%1%' has hash '%2%'", path, hash.to_string(HashFormat::Nix32, true));
|
||||||
|
|
||||||
/* Check if this is a known hash. */
|
/* Check if this is a known hash. */
|
||||||
|
@ -156,7 +162,12 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
if (pathExists(linkPath)) {
|
if (pathExists(linkPath)) {
|
||||||
auto stLink = lstat(linkPath);
|
auto stLink = lstat(linkPath);
|
||||||
if (st.st_size != stLink.st_size
|
if (st.st_size != stLink.st_size
|
||||||
|| (repair && hash != hashPath(HashAlgorithm::SHA256, linkPath).first))
|
|| (repair && hash != ({
|
||||||
|
PosixSourceAccessor accessor;
|
||||||
|
hashPath(
|
||||||
|
accessor, CanonPath { linkPath },
|
||||||
|
FileIngestionMethod::Recursive, HashAlgorithm::SHA256).first;
|
||||||
|
})))
|
||||||
{
|
{
|
||||||
// XXX: Consider overwriting linkPath with our valid version.
|
// XXX: Consider overwriting linkPath with our valid version.
|
||||||
warn("removing corrupted link '%s'", linkPath);
|
warn("removing corrupted link '%s'", linkPath);
|
||||||
|
|
|
@ -502,8 +502,13 @@ ref<const ValidPathInfo> RemoteStore::addCAToStore(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath RemoteStore::addToStoreFromDump(Source & dump, std::string_view name,
|
StorePath RemoteStore::addToStoreFromDump(
|
||||||
FileIngestionMethod method, HashAlgorithm hashAlgo, RepairFlag repair, const StorePathSet & references)
|
Source & dump,
|
||||||
|
std::string_view name,
|
||||||
|
ContentAddressMethod method,
|
||||||
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
|
RepairFlag repair)
|
||||||
{
|
{
|
||||||
return addCAToStore(dump, name, method, hashAlgo, references, repair)->path;
|
return addCAToStore(dump, name, method, hashAlgo, references, repair)->path;
|
||||||
}
|
}
|
||||||
|
@ -603,16 +608,6 @@ void RemoteStore::addMultipleToStore(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath RemoteStore::addTextToStore(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references,
|
|
||||||
RepairFlag repair)
|
|
||||||
{
|
|
||||||
StringSource source(s);
|
|
||||||
return addCAToStore(source, name, TextIngestionMethod {}, HashAlgorithm::SHA256, references, repair)->path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoteStore::registerDrvOutput(const Realisation & info)
|
void RemoteStore::registerDrvOutput(const Realisation & info)
|
||||||
{
|
{
|
||||||
auto conn(getConnection());
|
auto conn(getConnection());
|
||||||
|
|
|
@ -82,10 +82,15 @@ public:
|
||||||
RepairFlag repair);
|
RepairFlag repair);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a content-addressable store path. Does not support references. `dump` will be drained.
|
* Add a content-addressable store path. `dump` will be drained.
|
||||||
*/
|
*/
|
||||||
StorePath addToStoreFromDump(Source & dump, std::string_view name,
|
StorePath addToStoreFromDump(
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()) override;
|
Source & dump,
|
||||||
|
std::string_view name,
|
||||||
|
ContentAddressMethod method = FileIngestionMethod::Recursive,
|
||||||
|
HashAlgorithm hashAlgo = HashAlgorithm::SHA256,
|
||||||
|
const StorePathSet & references = StorePathSet(),
|
||||||
|
RepairFlag repair = NoRepair) override;
|
||||||
|
|
||||||
void addToStore(const ValidPathInfo & info, Source & nar,
|
void addToStore(const ValidPathInfo & info, Source & nar,
|
||||||
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
RepairFlag repair, CheckSigsFlag checkSigs) override;
|
||||||
|
@ -101,12 +106,6 @@ public:
|
||||||
RepairFlag repair,
|
RepairFlag repair,
|
||||||
CheckSigsFlag checkSigs) override;
|
CheckSigsFlag checkSigs) override;
|
||||||
|
|
||||||
StorePath addTextToStore(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references,
|
|
||||||
RepairFlag repair) override;
|
|
||||||
|
|
||||||
void registerDrvOutput(const Realisation & info) override;
|
void registerDrvOutput(const Realisation & info) override;
|
||||||
|
|
||||||
void queryRealisationUncached(const DrvOutput &,
|
void queryRealisationUncached(const DrvOutput &,
|
||||||
|
|
|
@ -205,25 +205,19 @@ StorePath StoreDirConfig::makeFixedOutputPath(std::string_view name, const Fixed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath StoreDirConfig::makeTextPath(std::string_view name, const TextInfo & info) const
|
|
||||||
{
|
|
||||||
assert(info.hash.algo == HashAlgorithm::SHA256);
|
|
||||||
return makeStorePath(
|
|
||||||
makeType(*this, "text", StoreReferences {
|
|
||||||
.others = info.references,
|
|
||||||
.self = false,
|
|
||||||
}),
|
|
||||||
info.hash,
|
|
||||||
name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
StorePath StoreDirConfig::makeFixedOutputPathFromCA(std::string_view name, const ContentAddressWithReferences & ca) const
|
StorePath StoreDirConfig::makeFixedOutputPathFromCA(std::string_view name, const ContentAddressWithReferences & ca) const
|
||||||
{
|
{
|
||||||
// New template
|
// New template
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[&](const TextInfo & ti) {
|
[&](const TextInfo & ti) {
|
||||||
return makeTextPath(name, ti);
|
assert(ti.hash.algo == HashAlgorithm::SHA256);
|
||||||
|
return makeStorePath(
|
||||||
|
makeType(*this, "text", StoreReferences {
|
||||||
|
.others = ti.references,
|
||||||
|
.self = false,
|
||||||
|
}),
|
||||||
|
ti.hash,
|
||||||
|
name);
|
||||||
},
|
},
|
||||||
[&](const FixedOutputInfo & foi) {
|
[&](const FixedOutputInfo & foi) {
|
||||||
return makeFixedOutputPath(name, foi);
|
return makeFixedOutputPath(name, foi);
|
||||||
|
@ -232,54 +226,45 @@ StorePath StoreDirConfig::makeFixedOutputPathFromCA(std::string_view name, const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::pair<StorePath, Hash> StoreDirConfig::computeStorePathFromDump(
|
std::pair<StorePath, Hash> StoreDirConfig::computeStorePath(
|
||||||
Source & dump,
|
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
FileIngestionMethod method,
|
SourceAccessor & accessor,
|
||||||
|
const CanonPath & path,
|
||||||
|
ContentAddressMethod method,
|
||||||
HashAlgorithm hashAlgo,
|
HashAlgorithm hashAlgo,
|
||||||
const StorePathSet & references) const
|
const StorePathSet & references,
|
||||||
|
PathFilter & filter) const
|
||||||
{
|
{
|
||||||
HashSink sink(hashAlgo);
|
auto h = hashPath(accessor, path, method.getFileIngestionMethod(), hashAlgo, filter).first;
|
||||||
dump.drainInto(sink);
|
return {
|
||||||
auto h = sink.finish().first;
|
makeFixedOutputPathFromCA(
|
||||||
FixedOutputInfo caInfo {
|
name,
|
||||||
.method = method,
|
ContentAddressWithReferences::fromParts(
|
||||||
.hash = h,
|
method,
|
||||||
.references = {},
|
h,
|
||||||
|
{
|
||||||
|
.others = references,
|
||||||
|
.self = false,
|
||||||
|
})),
|
||||||
|
h,
|
||||||
};
|
};
|
||||||
return std::make_pair(makeFixedOutputPath(name, caInfo), h);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
StorePath StoreDirConfig::computeStorePathForText(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references) const
|
|
||||||
{
|
|
||||||
return makeTextPath(name, TextInfo {
|
|
||||||
.hash = hashString(HashAlgorithm::SHA256, s),
|
|
||||||
.references = references,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath Store::addToStore(
|
StorePath Store::addToStore(
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
const Path & _srcPath,
|
SourceAccessor & accessor,
|
||||||
FileIngestionMethod method,
|
const CanonPath & path,
|
||||||
|
ContentAddressMethod method,
|
||||||
HashAlgorithm hashAlgo,
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
PathFilter & filter,
|
PathFilter & filter,
|
||||||
RepairFlag repair,
|
RepairFlag repair)
|
||||||
const StorePathSet & references)
|
|
||||||
{
|
{
|
||||||
Path srcPath(absPath(_srcPath));
|
|
||||||
auto source = sinkToSource([&](Sink & sink) {
|
auto source = sinkToSource([&](Sink & sink) {
|
||||||
if (method == FileIngestionMethod::Recursive)
|
dumpPath(accessor, path, sink, method.getFileIngestionMethod(), filter);
|
||||||
dumpPath(srcPath, sink, filter);
|
|
||||||
else
|
|
||||||
readFile(srcPath, sink);
|
|
||||||
});
|
});
|
||||||
return addToStoreFromDump(*source, name, method, hashAlgo, repair, references);
|
return addToStoreFromDump(*source, name, method, hashAlgo, references, repair);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Store::addMultipleToStore(
|
void Store::addMultipleToStore(
|
||||||
|
@ -404,8 +389,12 @@ digraph graphname {
|
||||||
fileSink -> caHashSink
|
fileSink -> caHashSink
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath,
|
ValidPathInfo Store::addToStoreSlow(
|
||||||
FileIngestionMethod method, HashAlgorithm hashAlgo,
|
std::string_view name,
|
||||||
|
SourceAccessor & accessor,
|
||||||
|
const CanonPath & srcPath,
|
||||||
|
ContentAddressMethod method, HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
std::optional<Hash> expectedCAHash)
|
std::optional<Hash> expectedCAHash)
|
||||||
{
|
{
|
||||||
HashSink narHashSink { HashAlgorithm::SHA256 };
|
HashSink narHashSink { HashAlgorithm::SHA256 };
|
||||||
|
@ -425,7 +414,7 @@ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath,
|
||||||
srcPath. The fact that we use scratchpadSink as a temporary buffer here
|
srcPath. The fact that we use scratchpadSink as a temporary buffer here
|
||||||
is an implementation detail. */
|
is an implementation detail. */
|
||||||
auto fileSource = sinkToSource([&](Sink & scratchpadSink) {
|
auto fileSource = sinkToSource([&](Sink & scratchpadSink) {
|
||||||
dumpPath(srcPath, scratchpadSink);
|
accessor.dumpPath(srcPath, scratchpadSink);
|
||||||
});
|
});
|
||||||
|
|
||||||
/* tapped provides the same data as fileSource, but we also write all the
|
/* tapped provides the same data as fileSource, but we also write all the
|
||||||
|
@ -433,9 +422,11 @@ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath,
|
||||||
TeeSource tapped { *fileSource, narSink };
|
TeeSource tapped { *fileSource, narSink };
|
||||||
|
|
||||||
NullParseSink blank;
|
NullParseSink blank;
|
||||||
auto & parseSink = method == FileIngestionMethod::Flat
|
auto & parseSink = method.getFileIngestionMethod() == FileIngestionMethod::Flat
|
||||||
? (ParseSink &) fileSink
|
? (ParseSink &) fileSink
|
||||||
: (ParseSink &) blank;
|
: method.getFileIngestionMethod() == FileIngestionMethod::Recursive
|
||||||
|
? (ParseSink &) blank
|
||||||
|
: (abort(), (ParseSink &)*(ParseSink *)nullptr); // handled both cases
|
||||||
|
|
||||||
/* The information that flows from tapped (besides being replicated in
|
/* The information that flows from tapped (besides being replicated in
|
||||||
narSink), is now put in parseSink. */
|
narSink), is now put in parseSink. */
|
||||||
|
@ -452,21 +443,24 @@ ValidPathInfo Store::addToStoreSlow(std::string_view name, const Path & srcPath,
|
||||||
if (expectedCAHash && expectedCAHash != hash)
|
if (expectedCAHash && expectedCAHash != hash)
|
||||||
throw Error("hash mismatch for '%s'", srcPath);
|
throw Error("hash mismatch for '%s'", srcPath);
|
||||||
|
|
||||||
|
|
||||||
ValidPathInfo info {
|
ValidPathInfo info {
|
||||||
*this,
|
*this,
|
||||||
name,
|
name,
|
||||||
FixedOutputInfo {
|
ContentAddressWithReferences::fromParts(
|
||||||
.method = method,
|
method,
|
||||||
.hash = hash,
|
hash,
|
||||||
.references = {},
|
{
|
||||||
},
|
.others = references,
|
||||||
|
.self = false,
|
||||||
|
}),
|
||||||
narHash,
|
narHash,
|
||||||
};
|
};
|
||||||
info.narSize = narSize;
|
info.narSize = narSize;
|
||||||
|
|
||||||
if (!isValidPath(info.path)) {
|
if (!isValidPath(info.path)) {
|
||||||
auto source = sinkToSource([&](Sink & scratchpadSink) {
|
auto source = sinkToSource([&](Sink & scratchpadSink) {
|
||||||
dumpPath(srcPath, scratchpadSink);
|
accessor.dumpPath(srcPath, scratchpadSink);
|
||||||
});
|
});
|
||||||
addToStore(info, *source);
|
addToStore(info, *source);
|
||||||
}
|
}
|
||||||
|
|
|
@ -428,20 +428,26 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual StorePath addToStore(
|
virtual StorePath addToStore(
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
const Path & srcPath,
|
SourceAccessor & accessor,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive,
|
const CanonPath & path,
|
||||||
|
ContentAddressMethod method = FileIngestionMethod::Recursive,
|
||||||
HashAlgorithm hashAlgo = HashAlgorithm::SHA256,
|
HashAlgorithm hashAlgo = HashAlgorithm::SHA256,
|
||||||
|
const StorePathSet & references = StorePathSet(),
|
||||||
PathFilter & filter = defaultPathFilter,
|
PathFilter & filter = defaultPathFilter,
|
||||||
RepairFlag repair = NoRepair,
|
RepairFlag repair = NoRepair);
|
||||||
const StorePathSet & references = StorePathSet());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy the contents of a path to the store and register the
|
* Copy the contents of a path to the store and register the
|
||||||
* validity the resulting path, using a constant amount of
|
* validity the resulting path, using a constant amount of
|
||||||
* memory.
|
* memory.
|
||||||
*/
|
*/
|
||||||
ValidPathInfo addToStoreSlow(std::string_view name, const Path & srcPath,
|
ValidPathInfo addToStoreSlow(
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256,
|
std::string_view name,
|
||||||
|
SourceAccessor & accessor,
|
||||||
|
const CanonPath & path,
|
||||||
|
ContentAddressMethod method = FileIngestionMethod::Recursive,
|
||||||
|
HashAlgorithm hashAlgo = HashAlgorithm::SHA256,
|
||||||
|
const StorePathSet & references = StorePathSet(),
|
||||||
std::optional<Hash> expectedCAHash = {});
|
std::optional<Hash> expectedCAHash = {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -453,20 +459,14 @@ public:
|
||||||
*
|
*
|
||||||
* \todo remove?
|
* \todo remove?
|
||||||
*/
|
*/
|
||||||
virtual StorePath addToStoreFromDump(Source & dump, std::string_view name,
|
virtual StorePath addToStoreFromDump(
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, RepairFlag repair = NoRepair,
|
Source & dump,
|
||||||
const StorePathSet & references = StorePathSet())
|
|
||||||
{ unsupported("addToStoreFromDump"); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Like addToStore, but the contents written to the output path is a
|
|
||||||
* regular file containing the given string.
|
|
||||||
*/
|
|
||||||
virtual StorePath addTextToStore(
|
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
std::string_view s,
|
ContentAddressMethod method = FileIngestionMethod::Recursive,
|
||||||
const StorePathSet & references,
|
HashAlgorithm hashAlgo = HashAlgorithm::SHA256,
|
||||||
RepairFlag repair = NoRepair) = 0;
|
const StorePathSet & references = StorePathSet(),
|
||||||
|
RepairFlag repair = NoRepair)
|
||||||
|
{ unsupported("addToStoreFromDump"); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a mapping indicating that `deriver!outputName` maps to the output path
|
* Add a mapping indicating that `deriver!outputName` maps to the output path
|
||||||
|
|
|
@ -86,41 +86,20 @@ struct StoreDirConfig : public Config
|
||||||
|
|
||||||
StorePath makeFixedOutputPath(std::string_view name, const FixedOutputInfo & info) const;
|
StorePath makeFixedOutputPath(std::string_view name, const FixedOutputInfo & info) const;
|
||||||
|
|
||||||
StorePath makeTextPath(std::string_view name, const TextInfo & info) const;
|
|
||||||
|
|
||||||
StorePath makeFixedOutputPathFromCA(std::string_view name, const ContentAddressWithReferences & ca) const;
|
StorePath makeFixedOutputPathFromCA(std::string_view name, const ContentAddressWithReferences & ca) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read-only variant of addToStoreFromDump(). It returns the store
|
* Read-only variant of addToStore(). It returns the store
|
||||||
* path to which a NAR or flat file would be written.
|
* path for the given file sytem object.
|
||||||
*/
|
*/
|
||||||
std::pair<StorePath, Hash> computeStorePathFromDump(
|
std::pair<StorePath, Hash> computeStorePath(
|
||||||
Source & dump,
|
|
||||||
std::string_view name,
|
std::string_view name,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive,
|
SourceAccessor & accessor,
|
||||||
|
const CanonPath & path,
|
||||||
|
ContentAddressMethod method = FileIngestionMethod::Recursive,
|
||||||
HashAlgorithm hashAlgo = HashAlgorithm::SHA256,
|
HashAlgorithm hashAlgo = HashAlgorithm::SHA256,
|
||||||
const StorePathSet & references = {}) const;
|
const StorePathSet & references = {},
|
||||||
|
PathFilter & filter = defaultPathFilter) const;
|
||||||
/**
|
|
||||||
* Preparatory part of addTextToStore().
|
|
||||||
*
|
|
||||||
* !!! Computation of the path should take the references given to
|
|
||||||
* addTextToStore() into account, otherwise we have a (relatively
|
|
||||||
* minor) security hole: a caller can register a source file with
|
|
||||||
* bogus references. If there are too many references, the path may
|
|
||||||
* not be garbage collected when it has to be (not really a problem,
|
|
||||||
* the caller could create a root anyway), or it may be garbage
|
|
||||||
* collected when it shouldn't be (more serious).
|
|
||||||
*
|
|
||||||
* Hashing the references would solve this (bogus references would
|
|
||||||
* simply yield a different store path, so other users wouldn't be
|
|
||||||
* affected), but it has some backwards compatibility issues (the
|
|
||||||
* hashing scheme changes), so I'm not doing that for now.
|
|
||||||
*/
|
|
||||||
StorePath computeStorePathForText(
|
|
||||||
std::string_view name,
|
|
||||||
std::string_view s,
|
|
||||||
const StorePathSet & references) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,10 +173,9 @@ static bool printUnknownLocations = getEnv("_NIX_EVAL_SHOW_UNKNOWN_LOCATIONS").h
|
||||||
static bool printPosMaybe(std::ostream & oss, std::string_view indent, const std::shared_ptr<AbstractPos> & pos) {
|
static bool printPosMaybe(std::ostream & oss, std::string_view indent, const std::shared_ptr<AbstractPos> & pos) {
|
||||||
bool hasPos = pos && *pos;
|
bool hasPos = pos && *pos;
|
||||||
if (hasPos) {
|
if (hasPos) {
|
||||||
oss << "\n" << indent << ANSI_BLUE << "at " ANSI_WARNING << *pos << ANSI_NORMAL << ":";
|
oss << indent << ANSI_BLUE << "at " ANSI_WARNING << *pos << ANSI_NORMAL << ":";
|
||||||
|
|
||||||
if (auto loc = pos->getCodeLines()) {
|
if (auto loc = pos->getCodeLines()) {
|
||||||
oss << "\n";
|
|
||||||
printCodeLines(oss, "", *pos, *loc);
|
printCodeLines(oss, "", *pos, *loc);
|
||||||
oss << "\n";
|
oss << "\n";
|
||||||
}
|
}
|
||||||
|
|
49
src/libutil/file-content-address.cc
Normal file
49
src/libutil/file-content-address.cc
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#include "file-content-address.hh"
|
||||||
|
#include "archive.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
void dumpPath(
|
||||||
|
SourceAccessor & accessor, const CanonPath & path,
|
||||||
|
Sink & sink,
|
||||||
|
FileIngestionMethod method,
|
||||||
|
PathFilter & filter)
|
||||||
|
{
|
||||||
|
switch (method) {
|
||||||
|
case FileIngestionMethod::Flat:
|
||||||
|
accessor.readFile(path, sink);
|
||||||
|
break;
|
||||||
|
case FileIngestionMethod::Recursive:
|
||||||
|
accessor.dumpPath(path, sink, filter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void restorePath(
|
||||||
|
const Path & path,
|
||||||
|
Source & source,
|
||||||
|
FileIngestionMethod method)
|
||||||
|
{
|
||||||
|
switch (method) {
|
||||||
|
case FileIngestionMethod::Flat:
|
||||||
|
writeFile(path, source);
|
||||||
|
break;
|
||||||
|
case FileIngestionMethod::Recursive:
|
||||||
|
restorePath(path, source);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HashResult hashPath(
|
||||||
|
SourceAccessor & accessor, const CanonPath & path,
|
||||||
|
FileIngestionMethod method, HashAlgorithm ht,
|
||||||
|
PathFilter & filter)
|
||||||
|
{
|
||||||
|
HashSink sink { ht };
|
||||||
|
dumpPath(accessor, path, sink, method, filter);
|
||||||
|
return sink.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
56
src/libutil/file-content-address.hh
Normal file
56
src/libutil/file-content-address.hh
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "source-accessor.hh"
|
||||||
|
#include "fs-sink.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enumeration of the main ways we can serialize file system
|
||||||
|
* objects.
|
||||||
|
*/
|
||||||
|
enum struct FileIngestionMethod : uint8_t {
|
||||||
|
/**
|
||||||
|
* Flat-file hashing. Directly ingest the contents of a single file
|
||||||
|
*/
|
||||||
|
Flat = 0,
|
||||||
|
/**
|
||||||
|
* Recursive (or NAR) hashing. Serializes the file-system object in
|
||||||
|
* Nix Archive format and ingest that.
|
||||||
|
*/
|
||||||
|
Recursive = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump a serialization of the given file system object.
|
||||||
|
*/
|
||||||
|
void dumpPath(
|
||||||
|
SourceAccessor & accessor, const CanonPath & path,
|
||||||
|
Sink & sink,
|
||||||
|
FileIngestionMethod method,
|
||||||
|
PathFilter & filter = defaultPathFilter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore a serialization of the given file system object.
|
||||||
|
*
|
||||||
|
* @TODO use an arbitrary `ParseSink`.
|
||||||
|
*/
|
||||||
|
void restorePath(
|
||||||
|
const Path & path,
|
||||||
|
Source & source,
|
||||||
|
FileIngestionMethod method);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the hash of the given file system object according to the
|
||||||
|
* given method.
|
||||||
|
*
|
||||||
|
* The hash is defined as (essentially) hashString(ht, dumpPath(path)).
|
||||||
|
*/
|
||||||
|
HashResult hashPath(
|
||||||
|
SourceAccessor & accessor, const CanonPath & path,
|
||||||
|
FileIngestionMethod method, HashAlgorithm ht,
|
||||||
|
PathFilter & filter = defaultPathFilter);
|
||||||
|
|
||||||
|
}
|
|
@ -367,15 +367,6 @@ HashResult HashSink::currentHash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HashResult hashPath(
|
|
||||||
HashAlgorithm ha, const Path & path, PathFilter & filter)
|
|
||||||
{
|
|
||||||
HashSink sink(ha);
|
|
||||||
dumpPath(path, sink, filter);
|
|
||||||
return sink.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Hash compressHash(const Hash & hash, unsigned int newSize)
|
Hash compressHash(const Hash & hash, unsigned int newSize)
|
||||||
{
|
{
|
||||||
Hash h(hash.algo);
|
Hash h(hash.algo);
|
||||||
|
|
|
@ -168,14 +168,11 @@ Hash hashString(HashAlgorithm ha, std::string_view s);
|
||||||
Hash hashFile(HashAlgorithm ha, const Path & path);
|
Hash hashFile(HashAlgorithm ha, const Path & path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the hash of the given path, serializing as a Nix Archive and
|
* The final hash and the number of bytes digested.
|
||||||
* then hashing that.
|
|
||||||
*
|
*
|
||||||
* The hash is defined as (essentially) hashString(ht, dumpPath(path)).
|
* @todo Convert to proper struct
|
||||||
*/
|
*/
|
||||||
typedef std::pair<Hash, uint64_t> HashResult;
|
typedef std::pair<Hash, uint64_t> HashResult;
|
||||||
HashResult hashPath(HashAlgorithm ha, const Path & path,
|
|
||||||
PathFilter & filter = defaultPathFilter);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compress a hash to the specified number of bytes by cyclically
|
* Compress a hash to the specified number of bytes by cyclically
|
||||||
|
|
|
@ -19,13 +19,15 @@ const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + pctEncod
|
||||||
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
|
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
|
||||||
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
|
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
|
||||||
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
|
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
|
||||||
|
const static std::string fragmentRegex = "(?:" + pcharRegex + "|[/? \"^])*";
|
||||||
const static std::string segmentRegex = "(?:" + pcharRegex + "*)";
|
const static std::string segmentRegex = "(?:" + pcharRegex + "*)";
|
||||||
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
|
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
|
||||||
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
|
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
|
||||||
|
|
||||||
/// A Git ref (i.e. branch or tag name).
|
/// A Git ref (i.e. branch or tag name).
|
||||||
/// \todo check that this is correct.
|
/// \todo check that this is correct.
|
||||||
const static std::string refRegexS = "[a-zA-Z0-9@][a-zA-Z0-9_.\\/@-]*";
|
/// This regex incomplete. See https://git-scm.com/docs/git-check-ref-format
|
||||||
|
const static std::string refRegexS = "[a-zA-Z0-9@][a-zA-Z0-9_.\\/@+-]*";
|
||||||
extern std::regex refRegex;
|
extern std::regex refRegex;
|
||||||
|
|
||||||
/// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
|
/// Instead of defining what a good Git Ref is, we define what a bad Git Ref is
|
||||||
|
|
|
@ -16,7 +16,7 @@ ParsedURL parseURL(const std::string & url)
|
||||||
"((" + schemeNameRegex + "):"
|
"((" + schemeNameRegex + "):"
|
||||||
+ "(?:(?://(" + authorityRegex + ")(" + absPathRegex + "))|(/?" + pathRegex + ")))"
|
+ "(?:(?://(" + authorityRegex + ")(" + absPathRegex + "))|(/?" + pathRegex + ")))"
|
||||||
+ "(?:\\?(" + queryRegex + "))?"
|
+ "(?:\\?(" + queryRegex + "))?"
|
||||||
+ "(?:#(" + queryRegex + "))?",
|
+ "(?:#(" + fragmentRegex + "))?",
|
||||||
std::regex::ECMAScript);
|
std::regex::ECMAScript);
|
||||||
|
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
|
|
|
@ -104,10 +104,15 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
/* Also write a copy of the list of user environment elements to
|
/* Also write a copy of the list of user environment elements to
|
||||||
the store; we need it for future modifications of the
|
the store; we need it for future modifications of the
|
||||||
environment. */
|
environment. */
|
||||||
|
auto manifestFile = ({
|
||||||
std::ostringstream str;
|
std::ostringstream str;
|
||||||
manifest.print(state.symbols, str, true);
|
manifest.print(state.symbols, str, true);
|
||||||
auto manifestFile = state.store->addTextToStore("env-manifest.nix",
|
// TODO with C++20 we can use str.view() instead and avoid copy.
|
||||||
str.str(), references);
|
std::string str2 = str.str();
|
||||||
|
StringSource source { str2 };
|
||||||
|
state.store->addToStoreFromDump(
|
||||||
|
source, "env-manifest.nix", TextIngestionMethod {}, HashAlgorithm::SHA256, references);
|
||||||
|
});
|
||||||
|
|
||||||
/* Get the environment builder expression. */
|
/* Get the environment builder expression. */
|
||||||
Value envBuilder;
|
Value envBuilder;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "graphml.hh"
|
#include "graphml.hh"
|
||||||
#include "legacy.hh"
|
#include "legacy.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
#include "path-with-outputs.hh"
|
#include "path-with-outputs.hh"
|
||||||
#include "posix-fs-canonicalise.hh"
|
#include "posix-fs-canonicalise.hh"
|
||||||
|
|
||||||
|
@ -175,8 +176,12 @@ static void opAdd(Strings opFlags, Strings opArgs)
|
||||||
{
|
{
|
||||||
if (!opFlags.empty()) throw UsageError("unknown flag");
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
||||||
|
|
||||||
|
PosixSourceAccessor accessor;
|
||||||
for (auto & i : opArgs)
|
for (auto & i : opArgs)
|
||||||
cout << fmt("%s\n", store->printStorePath(store->addToStore(std::string(baseNameOf(i)), i)));
|
cout << fmt("%s\n", store->printStorePath(store->addToStore(
|
||||||
|
std::string(baseNameOf(i)),
|
||||||
|
accessor,
|
||||||
|
CanonPath::fromCwd(i))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,8 +201,14 @@ static void opAddFixed(Strings opFlags, Strings opArgs)
|
||||||
HashAlgorithm hashAlgo = parseHashAlgo(opArgs.front());
|
HashAlgorithm hashAlgo = parseHashAlgo(opArgs.front());
|
||||||
opArgs.pop_front();
|
opArgs.pop_front();
|
||||||
|
|
||||||
|
PosixSourceAccessor accessor;
|
||||||
for (auto & i : opArgs)
|
for (auto & i : opArgs)
|
||||||
std::cout << fmt("%s\n", store->printStorePath(store->addToStoreSlow(baseNameOf(i), i, method, hashAlgo).path));
|
std::cout << fmt("%s\n", store->printStorePath(store->addToStoreSlow(
|
||||||
|
baseNameOf(i),
|
||||||
|
accessor,
|
||||||
|
CanonPath::fromCwd(i),
|
||||||
|
method,
|
||||||
|
hashAlgo).path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -541,7 +552,10 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
|
||||||
if (canonicalise)
|
if (canonicalise)
|
||||||
canonicalisePathMetaData(store->printStorePath(info->path), {});
|
canonicalisePathMetaData(store->printStorePath(info->path), {});
|
||||||
if (!hashGiven) {
|
if (!hashGiven) {
|
||||||
HashResult hash = hashPath(HashAlgorithm::SHA256, store->printStorePath(info->path));
|
HashResult hash = hashPath(
|
||||||
|
*store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) },
|
||||||
|
|
||||||
|
FileIngestionMethod::Recursive, HashAlgorithm::SHA256);
|
||||||
info->narHash = hash.first;
|
info->narHash = hash.first;
|
||||||
info->narSize = hash.second;
|
info->narSize = hash.second;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand
|
||||||
{
|
{
|
||||||
Path path;
|
Path path;
|
||||||
std::optional<std::string> namePart;
|
std::optional<std::string> namePart;
|
||||||
FileIngestionMethod ingestionMethod = FileIngestionMethod::Recursive;
|
ContentAddressMethod caMethod = FileIngestionMethod::Recursive;
|
||||||
|
|
||||||
CmdAddToStore()
|
CmdAddToStore()
|
||||||
{
|
{
|
||||||
|
@ -48,7 +49,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand
|
||||||
)",
|
)",
|
||||||
.labels = {"hash-mode"},
|
.labels = {"hash-mode"},
|
||||||
.handler = {[this](std::string s) {
|
.handler = {[this](std::string s) {
|
||||||
this->ingestionMethod = parseIngestionMethod(s);
|
this->caMethod = parseIngestionMethod(s);
|
||||||
}},
|
}},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -57,36 +58,17 @@ struct CmdAddToStore : MixDryRun, StoreCommand
|
||||||
{
|
{
|
||||||
if (!namePart) namePart = baseNameOf(path);
|
if (!namePart) namePart = baseNameOf(path);
|
||||||
|
|
||||||
StringSink sink;
|
PosixSourceAccessor accessor;
|
||||||
dumpPath(path, sink);
|
|
||||||
|
|
||||||
auto narHash = hashString(HashAlgorithm::SHA256, sink.s);
|
auto path2 = CanonPath::fromCwd(path);
|
||||||
|
|
||||||
Hash hash = narHash;
|
auto storePath = dryRun
|
||||||
if (ingestionMethod == FileIngestionMethod::Flat) {
|
? store->computeStorePath(
|
||||||
HashSink hsink(HashAlgorithm::SHA256);
|
*namePart, accessor, path2, caMethod, HashAlgorithm::SHA256, {}).first
|
||||||
readFile(path, hsink);
|
: store->addToStoreSlow(
|
||||||
hash = hsink.finish().first;
|
*namePart, accessor, path2, caMethod, HashAlgorithm::SHA256, {}).path;
|
||||||
}
|
|
||||||
|
|
||||||
ValidPathInfo info {
|
logger->cout("%s", store->printStorePath(storePath));
|
||||||
*store,
|
|
||||||
std::move(*namePart),
|
|
||||||
FixedOutputInfo {
|
|
||||||
.method = std::move(ingestionMethod),
|
|
||||||
.hash = std::move(hash),
|
|
||||||
.references = {},
|
|
||||||
},
|
|
||||||
narHash,
|
|
||||||
};
|
|
||||||
info.narSize = sink.s.size();
|
|
||||||
|
|
||||||
if (!dryRun) {
|
|
||||||
auto source = StringSource(sink.s);
|
|
||||||
store->addToStore(info, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger->cout("%s", store->printStorePath(info.path));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,7 +92,7 @@ struct CmdAddFile : CmdAddToStore
|
||||||
{
|
{
|
||||||
CmdAddFile()
|
CmdAddFile()
|
||||||
{
|
{
|
||||||
ingestionMethod = FileIngestionMethod::Flat;
|
caMethod = FileIngestionMethod::Flat;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
|
|
|
@ -223,7 +223,11 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore
|
||||||
if (builder != "bash")
|
if (builder != "bash")
|
||||||
throw Error("'nix develop' only works on derivations that use 'bash' as their builder");
|
throw Error("'nix develop' only works on derivations that use 'bash' as their builder");
|
||||||
|
|
||||||
auto getEnvShPath = evalStore->addTextToStore("get-env.sh", getEnvSh, {});
|
auto getEnvShPath = ({
|
||||||
|
StringSource source { getEnvSh };
|
||||||
|
evalStore->addToStoreFromDump(
|
||||||
|
source, "get-env.sh", TextIngestionMethod {}, HashAlgorithm::SHA256, {});
|
||||||
|
});
|
||||||
|
|
||||||
drv.args = {store->printStorePath(getEnvShPath)};
|
drv.args = {store->printStorePath(getEnvShPath)};
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "references.hh"
|
#include "references.hh"
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
@ -88,14 +89,8 @@ struct CmdHashBase : Command
|
||||||
else
|
else
|
||||||
hashSink = std::make_unique<HashSink>(ha);
|
hashSink = std::make_unique<HashSink>(ha);
|
||||||
|
|
||||||
switch (mode) {
|
PosixSourceAccessor accessor;
|
||||||
case FileIngestionMethod::Flat:
|
dumpPath(accessor, CanonPath::fromCwd(path), *hashSink, mode);
|
||||||
readFile(path, *hashSink);
|
|
||||||
break;
|
|
||||||
case FileIngestionMethod::Recursive:
|
|
||||||
dumpPath(path, *hashSink);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Hash h = hashSink->finish().first;
|
Hash h = hashSink->finish().first;
|
||||||
if (truncate && h.hashSize > 20) h = compressHash(h, 20);
|
if (truncate && h.hashSize > 20) h = compressHash(h, 20);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "legacy.hh"
|
#include "legacy.hh"
|
||||||
|
#include "posix-source-accessor.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
@ -122,7 +123,11 @@ std::tuple<StorePath, Hash> prefetchFile(
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("adding '%s' to the store", url));
|
fmt("adding '%s' to the store", url));
|
||||||
|
|
||||||
auto info = store->addToStoreSlow(*name, tmpFile, ingestionMethod, hashAlgo, expectedHash);
|
PosixSourceAccessor accessor;
|
||||||
|
auto info = store->addToStoreSlow(
|
||||||
|
*name,
|
||||||
|
accessor, CanonPath::fromCwd(tmpFile),
|
||||||
|
ingestionMethod, hashAlgo, {}, expectedHash);
|
||||||
storePath = info.path;
|
storePath = info.path;
|
||||||
assert(info.ca);
|
assert(info.ca);
|
||||||
hash = info.ca->hash;
|
hash = info.ca->hash;
|
||||||
|
|
|
@ -6,12 +6,14 @@ R""(
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix profile list
|
# nix profile list
|
||||||
|
Name: gdb
|
||||||
Index: 0
|
Index: 0
|
||||||
Flake attribute: legacyPackages.x86_64-linux.gdb
|
Flake attribute: legacyPackages.x86_64-linux.gdb
|
||||||
Original flake URL: flake:nixpkgs
|
Original flake URL: flake:nixpkgs
|
||||||
Locked flake URL: github:NixOS/nixpkgs/7b38b03d76ab71bdc8dc325e3f6338d984cc35ca
|
Locked flake URL: github:NixOS/nixpkgs/7b38b03d76ab71bdc8dc325e3f6338d984cc35ca
|
||||||
Store paths: /nix/store/indzcw5wvlhx6vwk7k4iq29q15chvr3d-gdb-11.1
|
Store paths: /nix/store/indzcw5wvlhx6vwk7k4iq29q15chvr3d-gdb-11.1
|
||||||
|
|
||||||
|
Name: blender-bin
|
||||||
Index: 1
|
Index: 1
|
||||||
Flake attribute: packages.x86_64-linux.default
|
Flake attribute: packages.x86_64-linux.default
|
||||||
Original flake URL: flake:blender-bin
|
Original flake URL: flake:blender-bin
|
||||||
|
@ -26,7 +28,7 @@ R""(
|
||||||
# nix build github:edolstra/nix-warez/91f2ffee657bf834e4475865ae336e2379282d34?dir=blender#packages.x86_64-linux.default
|
# nix build github:edolstra/nix-warez/91f2ffee657bf834e4475865ae336e2379282d34?dir=blender#packages.x86_64-linux.default
|
||||||
```
|
```
|
||||||
|
|
||||||
will build the package with index 1 shown above.
|
will build the package `blender-bin` shown above.
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
|
@ -34,10 +36,14 @@ This command shows what packages are currently installed in a
|
||||||
profile. For each installed package, it shows the following
|
profile. For each installed package, it shows the following
|
||||||
information:
|
information:
|
||||||
|
|
||||||
* `Index`: An integer that can be used to unambiguously identify the
|
* `Name`: A unique name used to unambiguously identify the
|
||||||
package in invocations of `nix profile remove` and `nix profile
|
package in invocations of `nix profile remove` and `nix profile
|
||||||
upgrade`.
|
upgrade`.
|
||||||
|
|
||||||
|
* `Index`: An integer that can be used to unambiguously identify the
|
||||||
|
package in invocations of `nix profile remove` and `nix profile upgrade`.
|
||||||
|
(*Deprecated, will be removed in a future version in favor of `Name`.*)
|
||||||
|
|
||||||
* `Flake attribute`: The flake output attribute path that provides the
|
* `Flake attribute`: The flake output attribute path that provides the
|
||||||
package (e.g. `packages.x86_64-linux.hello`).
|
package (e.g. `packages.x86_64-linux.hello`).
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,19 @@ R""(
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
* Remove a package by position:
|
* Remove a package by name:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix profile remove hello
|
||||||
|
```
|
||||||
|
|
||||||
|
* Remove a package by index
|
||||||
|
*(deprecated, will be removed in a future version)*:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix profile remove 3
|
# nix profile remove 3
|
||||||
```
|
```
|
||||||
|
|
||||||
* Remove a package by attribute path:
|
|
||||||
|
|
||||||
```console
|
|
||||||
# nix profile remove packages.x86_64-linux.hello
|
|
||||||
```
|
|
||||||
|
|
||||||
* Remove all packages:
|
* Remove all packages:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
|
|
@ -9,18 +9,16 @@ R""(
|
||||||
# nix profile upgrade '.*'
|
# nix profile upgrade '.*'
|
||||||
```
|
```
|
||||||
|
|
||||||
* Upgrade a specific package:
|
* Upgrade a specific package by name:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix profile upgrade packages.x86_64-linux.hello
|
# nix profile upgrade hello
|
||||||
```
|
```
|
||||||
|
|
||||||
* Upgrade a specific profile element by number:
|
* Upgrade a specific package by index
|
||||||
|
*(deprecated, will be removed in a future version)*:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
# nix profile list
|
|
||||||
0 flake:nixpkgs#legacyPackages.x86_64-linux.spotify …
|
|
||||||
|
|
||||||
# nix profile upgrade 0
|
# nix profile upgrade 0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include "../nix-env/user-env.hh"
|
#include "../nix-env/user-env.hh"
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
#include "names.hh"
|
#include "names.hh"
|
||||||
|
#include "url.hh"
|
||||||
|
#include "flake/url-name.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
@ -43,6 +45,7 @@ const int defaultPriority = 5;
|
||||||
struct ProfileElement
|
struct ProfileElement
|
||||||
{
|
{
|
||||||
StorePathSet storePaths;
|
StorePathSet storePaths;
|
||||||
|
std::string name;
|
||||||
std::optional<ProfileElementSource> source;
|
std::optional<ProfileElementSource> source;
|
||||||
bool active = true;
|
bool active = true;
|
||||||
int priority = defaultPriority;
|
int priority = defaultPriority;
|
||||||
|
@ -116,6 +119,8 @@ struct ProfileManifest
|
||||||
|
|
||||||
if (pathExists(manifestPath)) {
|
if (pathExists(manifestPath)) {
|
||||||
auto json = nlohmann::json::parse(readFile(manifestPath));
|
auto json = nlohmann::json::parse(readFile(manifestPath));
|
||||||
|
/* Keep track of already found names to allow preventing duplicates. */
|
||||||
|
std::set<std::string> foundNames;
|
||||||
|
|
||||||
auto version = json.value("version", 0);
|
auto version = json.value("version", 0);
|
||||||
std::string sUrl;
|
std::string sUrl;
|
||||||
|
@ -149,6 +154,25 @@ struct ProfileManifest
|
||||||
e["outputs"].get<ExtendedOutputsSpec>()
|
e["outputs"].get<ExtendedOutputsSpec>()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string nameCandidate = element.identifier();
|
||||||
|
if (e.contains("name")) {
|
||||||
|
nameCandidate = e["name"];
|
||||||
|
}
|
||||||
|
else if (element.source) {
|
||||||
|
auto url = parseURL(element.source->to_string());
|
||||||
|
auto name = getNameFromURL(url);
|
||||||
|
if (name)
|
||||||
|
nameCandidate = *name;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto finalName = nameCandidate;
|
||||||
|
for (int i = 1; foundNames.contains(finalName); ++i) {
|
||||||
|
finalName = nameCandidate + std::to_string(i);
|
||||||
|
}
|
||||||
|
element.name = finalName;
|
||||||
|
foundNames.insert(element.name);
|
||||||
|
|
||||||
elements.emplace_back(std::move(element));
|
elements.emplace_back(std::move(element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,6 +187,7 @@ struct ProfileManifest
|
||||||
for (auto & drvInfo : drvInfos) {
|
for (auto & drvInfo : drvInfos) {
|
||||||
ProfileElement element;
|
ProfileElement element;
|
||||||
element.storePaths = {drvInfo.queryOutPath()};
|
element.storePaths = {drvInfo.queryOutPath()};
|
||||||
|
element.name = element.identifier();
|
||||||
elements.emplace_back(std::move(element));
|
elements.emplace_back(std::move(element));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,15 +476,25 @@ public:
|
||||||
{
|
{
|
||||||
std::vector<Matcher> res;
|
std::vector<Matcher> res;
|
||||||
|
|
||||||
|
auto anyIndexMatchers = false;
|
||||||
|
|
||||||
for (auto & s : _matchers) {
|
for (auto & s : _matchers) {
|
||||||
if (auto n = string2Int<size_t>(s))
|
if (auto n = string2Int<size_t>(s)) {
|
||||||
res.push_back(*n);
|
res.push_back(*n);
|
||||||
|
anyIndexMatchers = true;
|
||||||
|
}
|
||||||
else if (store->isStorePath(s))
|
else if (store->isStorePath(s))
|
||||||
res.push_back(s);
|
res.push_back(s);
|
||||||
else
|
else
|
||||||
res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)});
|
res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (anyIndexMatchers) {
|
||||||
|
warn("Indices are deprecated and will be removed in a future version!\n"
|
||||||
|
" Refer to packages by their `Name` as printed by `nix profile list`.\n"
|
||||||
|
" See https://github.com/NixOS/nix/issues/9171 for more information.");
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,8 +506,7 @@ public:
|
||||||
} else if (auto path = std::get_if<Path>(&matcher)) {
|
} else if (auto path = std::get_if<Path>(&matcher)) {
|
||||||
if (element.storePaths.count(store.parseStorePath(*path))) return true;
|
if (element.storePaths.count(store.parseStorePath(*path))) return true;
|
||||||
} else if (auto regex = std::get_if<RegexPattern>(&matcher)) {
|
} else if (auto regex = std::get_if<RegexPattern>(&matcher)) {
|
||||||
if (element.source
|
if (std::regex_match(element.name, regex->reg))
|
||||||
&& std::regex_match(element.source->attrPath, regex->reg))
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -556,14 +590,32 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
||||||
Installables installables;
|
Installables installables;
|
||||||
std::vector<size_t> indices;
|
std::vector<size_t> indices;
|
||||||
|
|
||||||
|
auto matchedCount = 0;
|
||||||
auto upgradedCount = 0;
|
auto upgradedCount = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||||
auto & element(manifest.elements[i]);
|
auto & element(manifest.elements[i]);
|
||||||
if (element.source
|
if (!matches(*store, element, i, matchers)) {
|
||||||
&& !element.source->originalRef.input.isLocked()
|
continue;
|
||||||
&& matches(*store, element, i, matchers))
|
}
|
||||||
{
|
|
||||||
|
matchedCount++;
|
||||||
|
|
||||||
|
if (!element.source) {
|
||||||
|
warn(
|
||||||
|
"Found package '%s', but it was not installed from a flake, so it can't be checked for upgrades!",
|
||||||
|
element.identifier()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (element.source->originalRef.input.isLocked()) {
|
||||||
|
warn(
|
||||||
|
"Found package '%s', but it was installed from a locked flake reference so it can't be upgraded!",
|
||||||
|
element.identifier()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
upgradedCount++;
|
upgradedCount++;
|
||||||
|
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
|
@ -601,9 +653,9 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
||||||
installables.push_back(installable);
|
installables.push_back(installable);
|
||||||
indices.push_back(i);
|
indices.push_back(i);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (upgradedCount == 0) {
|
if (upgradedCount == 0) {
|
||||||
|
if (matchedCount == 0) {
|
||||||
for (auto & matcher : matchers) {
|
for (auto & matcher : matchers) {
|
||||||
if (const size_t * index = std::get_if<size_t>(&matcher)){
|
if (const size_t * index = std::get_if<size_t>(&matcher)){
|
||||||
warn("'%d' is not a valid index", *index);
|
warn("'%d' is not a valid index", *index);
|
||||||
|
@ -613,6 +665,9 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
||||||
warn("'%s' does not match any packages", regex->pattern);
|
warn("'%s' does not match any packages", regex->pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
warn("Found some packages but none of them could be upgraded.");
|
||||||
|
}
|
||||||
warn ("Use 'nix profile list' to see the current profile.");
|
warn ("Use 'nix profile list' to see the current profile.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,9 +712,10 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
|
||||||
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||||
auto & element(manifest.elements[i]);
|
auto & element(manifest.elements[i]);
|
||||||
if (i) logger->cout("");
|
if (i) logger->cout("");
|
||||||
logger->cout("Index: " ANSI_BOLD "%s" ANSI_NORMAL "%s",
|
logger->cout("Name: " ANSI_BOLD "%s" ANSI_NORMAL "%s",
|
||||||
i,
|
element.name,
|
||||||
element.active ? "" : " " ANSI_RED "(inactive)" ANSI_NORMAL);
|
element.active ? "" : " " ANSI_RED "(inactive)" ANSI_NORMAL);
|
||||||
|
logger->cout("Index: %s", i);
|
||||||
if (element.source) {
|
if (element.source) {
|
||||||
logger->cout("Flake attribute: %s%s", element.source->attrPath, element.source->outputs.to_string());
|
logger->cout("Flake attribute: %s%s", element.source->attrPath, element.source->outputs.to_string());
|
||||||
logger->cout("Original flake URL: %s", element.source->originalRef.to_string());
|
logger->cout("Original flake URL: %s", element.source->originalRef.to_string());
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'abort' builtin
|
… while calling the 'abort' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-abort.nix:1:14:
|
at /pwd/lang/eval-fail-abort.nix:1:14:
|
||||||
|
|
||||||
1| if true then abort "this should fail" else 1
|
1| if true then abort "this should fail" else 1
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'addDrvOutputDependencies' builtin
|
… while calling the 'addDrvOutputDependencies' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-addDrvOutputDependencies-empty-context.nix:1:1:
|
at /pwd/lang/eval-fail-addDrvOutputDependencies-empty-context.nix:1:1:
|
||||||
|
|
||||||
1| builtins.addDrvOutputDependencies ""
|
1| builtins.addDrvOutputDependencies ""
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'addDrvOutputDependencies' builtin
|
… while calling the 'addDrvOutputDependencies' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-addDrvOutputDependencies-multi-elem-context.nix:18:4:
|
at /pwd/lang/eval-fail-addDrvOutputDependencies-multi-elem-context.nix:18:4:
|
||||||
|
|
||||||
17|
|
17|
|
||||||
18| in builtins.addDrvOutputDependencies combo-path
|
18| in builtins.addDrvOutputDependencies combo-path
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'addDrvOutputDependencies' builtin
|
… while calling the 'addDrvOutputDependencies' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-addDrvOutputDependencies-wrong-element-kind.nix:9:4:
|
at /pwd/lang/eval-fail-addDrvOutputDependencies-wrong-element-kind.nix:9:4:
|
||||||
|
|
||||||
8|
|
8|
|
||||||
9| in builtins.addDrvOutputDependencies drv.outPath
|
9| in builtins.addDrvOutputDependencies drv.outPath
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,35 +1,27 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating the attribute 'body'
|
… while evaluating the attribute 'body'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-assert.nix:4:3:
|
at /pwd/lang/eval-fail-assert.nix:4:3:
|
||||||
|
|
||||||
3|
|
3|
|
||||||
4| body = x "x";
|
4| body = x "x";
|
||||||
| ^
|
| ^
|
||||||
5| }
|
5| }
|
||||||
|
|
||||||
… from call site
|
… from call site
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-assert.nix:4:10:
|
at /pwd/lang/eval-fail-assert.nix:4:10:
|
||||||
|
|
||||||
3|
|
3|
|
||||||
4| body = x "x";
|
4| body = x "x";
|
||||||
| ^
|
| ^
|
||||||
5| }
|
5| }
|
||||||
|
|
||||||
… while calling 'x'
|
… while calling 'x'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-assert.nix:2:7:
|
at /pwd/lang/eval-fail-assert.nix:2:7:
|
||||||
|
|
||||||
1| let {
|
1| let {
|
||||||
2| x = arg: assert arg == "y"; 123;
|
2| x = arg: assert arg == "y"; 123;
|
||||||
| ^
|
| ^
|
||||||
3|
|
3|
|
||||||
|
|
||||||
error: assertion '(arg == "y")' failed
|
error: assertion '(arg == "y")' failed
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-assert.nix:2:12:
|
at /pwd/lang/eval-fail-assert.nix:2:12:
|
||||||
|
|
||||||
1| let {
|
1| let {
|
||||||
2| x = arg: assert arg == "y"; 123;
|
2| x = arg: assert arg == "y"; 123;
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating the attribute 'puppy."${key}"'
|
… while evaluating the attribute 'puppy."${key}"'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-attr-name-type.nix:3:5:
|
at /pwd/lang/eval-fail-attr-name-type.nix:3:5:
|
||||||
|
|
||||||
2| attrs = {
|
2| attrs = {
|
||||||
3| puppy.doggy = {};
|
3| puppy.doggy = {};
|
||||||
| ^
|
| ^
|
||||||
4| };
|
4| };
|
||||||
|
|
||||||
… while evaluating an attribute name
|
… while evaluating an attribute name
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-attr-name-type.nix:7:17:
|
at /pwd/lang/eval-fail-attr-name-type.nix:7:17:
|
||||||
|
|
||||||
6| in
|
6| in
|
||||||
7| attrs.puppy.${key}
|
7| attrs.puppy.${key}
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating a path segment
|
… while evaluating a path segment
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-bad-string-interpolation-1.nix:1:2:
|
at /pwd/lang/eval-fail-bad-string-interpolation-1.nix:1:2:
|
||||||
|
|
||||||
1| "${x: x}"
|
1| "${x: x}"
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating a path segment
|
… while evaluating a path segment
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-bad-string-interpolation-3.nix:1:3:
|
at /pwd/lang/eval-fail-bad-string-interpolation-3.nix:1:3:
|
||||||
|
|
||||||
1| ''${x: x}''
|
1| ''${x: x}''
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating a path segment
|
… while evaluating a path segment
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-bad-string-interpolation-4.nix:9:3:
|
at /pwd/lang/eval-fail-bad-string-interpolation-4.nix:9:3:
|
||||||
|
|
||||||
8| # The error message should not be too long.
|
8| # The error message should not be too long.
|
||||||
9| ''${pkgs}''
|
9| ''${pkgs}''
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating the attribute 'body'
|
… while evaluating the attribute 'body'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-blackhole.nix:2:3:
|
at /pwd/lang/eval-fail-blackhole.nix:2:3:
|
||||||
|
|
||||||
1| let {
|
1| let {
|
||||||
2| body = x;
|
2| body = x;
|
||||||
| ^
|
| ^
|
||||||
3| x = y;
|
3| x = y;
|
||||||
|
|
||||||
error: infinite recursion encountered
|
error: infinite recursion encountered
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-blackhole.nix:3:7:
|
at /pwd/lang/eval-fail-blackhole.nix:3:7:
|
||||||
|
|
||||||
2| body = x;
|
2| body = x;
|
||||||
3| x = y;
|
3| x = y;
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'length' builtin
|
… while calling the 'length' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-call-primop.nix:1:1:
|
at /pwd/lang/eval-fail-call-primop.nix:1:1:
|
||||||
|
|
||||||
1| builtins.length 1
|
1| builtins.length 1
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,24 +1,18 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'deepSeq' builtin
|
… while calling the 'deepSeq' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-deepseq.nix:1:1:
|
at /pwd/lang/eval-fail-deepseq.nix:1:1:
|
||||||
|
|
||||||
1| builtins.deepSeq { x = abort "foo"; } 456
|
1| builtins.deepSeq { x = abort "foo"; } 456
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
||||||
… while evaluating the attribute 'x'
|
… while evaluating the attribute 'x'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-deepseq.nix:1:20:
|
at /pwd/lang/eval-fail-deepseq.nix:1:20:
|
||||||
|
|
||||||
1| builtins.deepSeq { x = abort "foo"; } 456
|
1| builtins.deepSeq { x = abort "foo"; } 456
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
||||||
… while calling the 'abort' builtin
|
… while calling the 'abort' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-deepseq.nix:1:24:
|
at /pwd/lang/eval-fail-deepseq.nix:1:24:
|
||||||
|
|
||||||
1| builtins.deepSeq { x = abort "foo"; } 456
|
1| builtins.deepSeq { x = abort "foo"; } 456
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating the attribute 'set'
|
… while evaluating the attribute 'set'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:2:3:
|
at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:2:3:
|
||||||
|
|
||||||
1| {
|
1| {
|
||||||
2| set = { "${"" + "b"}" = 1; };
|
2| set = { "${"" + "b"}" = 1; };
|
||||||
| ^
|
| ^
|
||||||
3| set = { "${"b" + ""}" = 2; };
|
3| set = { "${"b" + ""}" = 2; };
|
||||||
|
|
||||||
error: dynamic attribute 'b' already defined at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:2:11
|
error: dynamic attribute 'b' already defined at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:2:11
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:3:11:
|
at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:3:11:
|
||||||
|
|
||||||
2| set = { "${"" + "b"}" = 1; };
|
2| set = { "${"" + "b"}" = 1; };
|
||||||
3| set = { "${"b" + ""}" = 2; };
|
3| set = { "${"b" + ""}" = 2; };
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,35 +1,27 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'foldl'' builtin
|
… while calling the 'foldl'' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:2:1:
|
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:2:1:
|
||||||
|
|
||||||
1| # Tests that the result of applying op is forced even if the value is never used
|
1| # Tests that the result of applying op is forced even if the value is never used
|
||||||
2| builtins.foldl'
|
2| builtins.foldl'
|
||||||
| ^
|
| ^
|
||||||
3| (_: f: f null)
|
3| (_: f: f null)
|
||||||
|
|
||||||
… while calling anonymous lambda
|
… while calling anonymous lambda
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:3:7:
|
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:3:7:
|
||||||
|
|
||||||
2| builtins.foldl'
|
2| builtins.foldl'
|
||||||
3| (_: f: f null)
|
3| (_: f: f null)
|
||||||
| ^
|
| ^
|
||||||
4| null
|
4| null
|
||||||
|
|
||||||
… from call site
|
… from call site
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:3:10:
|
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:3:10:
|
||||||
|
|
||||||
2| builtins.foldl'
|
2| builtins.foldl'
|
||||||
3| (_: f: f null)
|
3| (_: f: f null)
|
||||||
| ^
|
| ^
|
||||||
4| null
|
4| null
|
||||||
|
|
||||||
… while calling anonymous lambda
|
… while calling anonymous lambda
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:5:6:
|
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:5:6:
|
||||||
|
|
||||||
4| null
|
4| null
|
||||||
5| [ (_: throw "Not the final value, but is still forced!") (_: 23) ]
|
5| [ (_: throw "Not the final value, but is still forced!") (_: 23) ]
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'fromTOML' builtin
|
… while calling the 'fromTOML' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-fromTOML-timestamps.nix:1:1:
|
at /pwd/lang/eval-fail-fromTOML-timestamps.nix:1:1:
|
||||||
|
|
||||||
1| builtins.fromTOML ''
|
1| builtins.fromTOML ''
|
||||||
| ^
|
| ^
|
||||||
2| key = "value"
|
2| key = "value"
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'toString' builtin
|
… while calling the 'toString' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-hashfile-missing.nix:4:3:
|
at /pwd/lang/eval-fail-hashfile-missing.nix:4:3:
|
||||||
|
|
||||||
3| in
|
3| in
|
||||||
4| toString (builtins.concatLists (map (hash: map (builtins.hashFile hash) paths) ["md5" "sha1" "sha256" "sha512"]))
|
4| toString (builtins.concatLists (map (hash: map (builtins.hashFile hash) paths) ["md5" "sha1" "sha256" "sha512"]))
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating one of the elements to concatenate
|
… while evaluating one of the elements to concatenate
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-list.nix:1:2:
|
at /pwd/lang/eval-fail-list.nix:1:2:
|
||||||
|
|
||||||
1| 8++1
|
1| 8++1
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
error:
|
error:
|
||||||
… from call site
|
… from call site
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-missing-arg.nix:1:1:
|
at /pwd/lang/eval-fail-missing-arg.nix:1:1:
|
||||||
|
|
||||||
1| ({x, y, z}: x + y + z) {x = "foo"; z = "bar";}
|
1| ({x, y, z}: x + y + z) {x = "foo"; z = "bar";}
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
||||||
error: function 'anonymous lambda' called without required argument 'y'
|
error: function 'anonymous lambda' called without required argument 'y'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-missing-arg.nix:1:2:
|
at /pwd/lang/eval-fail-missing-arg.nix:1:2:
|
||||||
|
|
||||||
1| ({x, y, z}: x + y + z) {x = "foo"; z = "bar";}
|
1| ({x, y, z}: x + y + z) {x = "foo"; z = "bar";}
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
error:
|
error:
|
||||||
… in the argument of the not operator
|
… in the argument of the not operator
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-not-throws.nix:1:4:
|
at /pwd/lang/eval-fail-not-throws.nix:1:4:
|
||||||
|
|
||||||
1| ! (throw "uh oh!")
|
1| ! (throw "uh oh!")
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
||||||
… while calling the 'throw' builtin
|
… while calling the 'throw' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-not-throws.nix:1:4:
|
at /pwd/lang/eval-fail-not-throws.nix:1:4:
|
||||||
|
|
||||||
1| ! (throw "uh oh!")
|
1| ! (throw "uh oh!")
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
error: path has a trailing slash
|
error: path has a trailing slash
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-path-slash.nix:6:12:
|
at /pwd/lang/eval-fail-path-slash.nix:6:12:
|
||||||
|
|
||||||
5| # and https://nixos.org/nix-dev/2016-June/020829.html
|
5| # and https://nixos.org/nix-dev/2016-June/020829.html
|
||||||
6| /nix/store/
|
6| /nix/store/
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
error:
|
error:
|
||||||
… in the right operand of the update (//) operator
|
… in the right operand of the update (//) operator
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-recursion.nix:1:12:
|
at /pwd/lang/eval-fail-recursion.nix:1:12:
|
||||||
|
|
||||||
1| let a = {} // a; in a.foo
|
1| let a = {} // a; in a.foo
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
||||||
error: infinite recursion encountered
|
error: infinite recursion encountered
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-recursion.nix:1:15:
|
at /pwd/lang/eval-fail-recursion.nix:1:15:
|
||||||
|
|
||||||
1| let a = {} // a; in a.foo
|
1| let a = {} // a; in a.foo
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating the attribute 'body'
|
… while evaluating the attribute 'body'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-remove.nix:4:3:
|
at /pwd/lang/eval-fail-remove.nix:4:3:
|
||||||
|
|
||||||
3|
|
3|
|
||||||
4| body = (removeAttrs attrs ["x"]).x;
|
4| body = (removeAttrs attrs ["x"]).x;
|
||||||
| ^
|
| ^
|
||||||
5| }
|
5| }
|
||||||
|
|
||||||
error: attribute 'x' missing
|
error: attribute 'x' missing
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-remove.nix:4:10:
|
at /pwd/lang/eval-fail-remove.nix:4:10:
|
||||||
|
|
||||||
3|
|
3|
|
||||||
4| body = (removeAttrs attrs ["x"]).x;
|
4| body = (removeAttrs attrs ["x"]).x;
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,35 +1,27 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating the attribute 'body'
|
… while evaluating the attribute 'body'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-scope-5.nix:8:3:
|
at /pwd/lang/eval-fail-scope-5.nix:8:3:
|
||||||
|
|
||||||
7|
|
7|
|
||||||
8| body = f {};
|
8| body = f {};
|
||||||
| ^
|
| ^
|
||||||
9|
|
9|
|
||||||
|
|
||||||
… from call site
|
… from call site
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-scope-5.nix:8:10:
|
at /pwd/lang/eval-fail-scope-5.nix:8:10:
|
||||||
|
|
||||||
7|
|
7|
|
||||||
8| body = f {};
|
8| body = f {};
|
||||||
| ^
|
| ^
|
||||||
9|
|
9|
|
||||||
|
|
||||||
… while calling 'f'
|
… while calling 'f'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-scope-5.nix:6:7:
|
at /pwd/lang/eval-fail-scope-5.nix:6:7:
|
||||||
|
|
||||||
5|
|
5|
|
||||||
6| f = {x ? y, y ? x}: x + y;
|
6| f = {x ? y, y ? x}: x + y;
|
||||||
| ^
|
| ^
|
||||||
7|
|
7|
|
||||||
|
|
||||||
error: infinite recursion encountered
|
error: infinite recursion encountered
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-scope-5.nix:6:12:
|
at /pwd/lang/eval-fail-scope-5.nix:6:12:
|
||||||
|
|
||||||
5|
|
5|
|
||||||
6| f = {x ? y, y ? x}: x + y;
|
6| f = {x ? y, y ? x}: x + y;
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'seq' builtin
|
… while calling the 'seq' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-seq.nix:1:1:
|
at /pwd/lang/eval-fail-seq.nix:1:1:
|
||||||
|
|
||||||
1| builtins.seq (abort "foo") 2
|
1| builtins.seq (abort "foo") 2
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
||||||
… while calling the 'abort' builtin
|
… while calling the 'abort' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-seq.nix:1:15:
|
at /pwd/lang/eval-fail-seq.nix:1:15:
|
||||||
|
|
||||||
1| builtins.seq (abort "foo") 2
|
1| builtins.seq (abort "foo") 2
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
error: undefined variable 'x'
|
error: undefined variable 'x'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-set.nix:1:3:
|
at /pwd/lang/eval-fail-set.nix:1:3:
|
||||||
|
|
||||||
1| 8.x
|
1| 8.x
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'substring' builtin
|
… while calling the 'substring' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-substring.nix:1:1:
|
at /pwd/lang/eval-fail-substring.nix:1:1:
|
||||||
|
|
||||||
1| builtins.substring (builtins.sub 0 1) 1 "x"
|
1| builtins.substring (builtins.sub 0 1) 1 "x"
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'toPath' builtin
|
… while calling the 'toPath' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-to-path.nix:1:1:
|
at /pwd/lang/eval-fail-to-path.nix:1:1:
|
||||||
|
|
||||||
1| builtins.toPath "foo/bar"
|
1| builtins.toPath "foo/bar"
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,25 +1,19 @@
|
||||||
error:
|
error:
|
||||||
… while calling the 'toJSON' builtin
|
… while calling the 'toJSON' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-toJSON.nix:1:1:
|
at /pwd/lang/eval-fail-toJSON.nix:1:1:
|
||||||
|
|
||||||
1| builtins.toJSON {
|
1| builtins.toJSON {
|
||||||
| ^
|
| ^
|
||||||
2| a.b = [
|
2| a.b = [
|
||||||
|
|
||||||
… while evaluating attribute 'a'
|
… while evaluating attribute 'a'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-toJSON.nix:2:3:
|
at /pwd/lang/eval-fail-toJSON.nix:2:3:
|
||||||
|
|
||||||
1| builtins.toJSON {
|
1| builtins.toJSON {
|
||||||
2| a.b = [
|
2| a.b = [
|
||||||
| ^
|
| ^
|
||||||
3| true
|
3| true
|
||||||
|
|
||||||
… while evaluating attribute 'b'
|
… while evaluating attribute 'b'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-toJSON.nix:2:3:
|
at /pwd/lang/eval-fail-toJSON.nix:2:3:
|
||||||
|
|
||||||
1| builtins.toJSON {
|
1| builtins.toJSON {
|
||||||
2| a.b = [
|
2| a.b = [
|
||||||
| ^
|
| ^
|
||||||
|
@ -28,27 +22,21 @@ error:
|
||||||
… while evaluating list element at index 3
|
… while evaluating list element at index 3
|
||||||
|
|
||||||
… while evaluating attribute 'c'
|
… while evaluating attribute 'c'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-toJSON.nix:7:7:
|
at /pwd/lang/eval-fail-toJSON.nix:7:7:
|
||||||
|
|
||||||
6| {
|
6| {
|
||||||
7| c.d = throw "hah no";
|
7| c.d = throw "hah no";
|
||||||
| ^
|
| ^
|
||||||
8| }
|
8| }
|
||||||
|
|
||||||
… while evaluating attribute 'd'
|
… while evaluating attribute 'd'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-toJSON.nix:7:7:
|
at /pwd/lang/eval-fail-toJSON.nix:7:7:
|
||||||
|
|
||||||
6| {
|
6| {
|
||||||
7| c.d = throw "hah no";
|
7| c.d = throw "hah no";
|
||||||
| ^
|
| ^
|
||||||
8| }
|
8| }
|
||||||
|
|
||||||
… while calling the 'throw' builtin
|
… while calling the 'throw' builtin
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-toJSON.nix:7:13:
|
at /pwd/lang/eval-fail-toJSON.nix:7:13:
|
||||||
|
|
||||||
6| {
|
6| {
|
||||||
7| c.d = throw "hah no";
|
7| c.d = throw "hah no";
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
error:
|
error:
|
||||||
… from call site
|
… from call site
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-undeclared-arg.nix:1:1:
|
at /pwd/lang/eval-fail-undeclared-arg.nix:1:1:
|
||||||
|
|
||||||
1| ({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";}
|
1| ({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";}
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
||||||
error: function 'anonymous lambda' called with unexpected argument 'y'
|
error: function 'anonymous lambda' called with unexpected argument 'y'
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-undeclared-arg.nix:1:2:
|
at /pwd/lang/eval-fail-undeclared-arg.nix:1:2:
|
||||||
|
|
||||||
1| ({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";}
|
1| ({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";}
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
error:
|
error:
|
||||||
… while evaluating an attribute name
|
… while evaluating an attribute name
|
||||||
|
|
||||||
at /pwd/lang/eval-fail-using-set-as-attr-name.nix:5:10:
|
at /pwd/lang/eval-fail-using-set-as-attr-name.nix:5:10:
|
||||||
|
|
||||||
4| in
|
4| in
|
||||||
5| attr.${key}
|
5| attr.${key}
|
||||||
| ^
|
| ^
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
error: attribute 'x' already defined at «stdin»:1:3
|
error: attribute 'x' already defined at «stdin»:1:3
|
||||||
|
|
||||||
at «stdin»:3:3:
|
at «stdin»:3:3:
|
||||||
|
|
||||||
2| y = 456;
|
2| y = 456;
|
||||||
3| x = 789;
|
3| x = 789;
|
||||||
| ^
|
| ^
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue