forked from lix-project/lix
Merge remote-tracking branch 'origin/master' into script-to-make-docker-release
This commit is contained in:
commit
bf435664d7
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
@ -25,7 +25,7 @@ jobs:
|
||||||
name: '${{ env.CACHIX_NAME }}'
|
name: '${{ env.CACHIX_NAME }}'
|
||||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
- run: nix-build -A checks.$(nix-instantiate --eval -E '(builtins.currentSystem)')
|
- run: nix --experimental-features 'nix-command flakes' flake check -L
|
||||||
|
|
||||||
check_cachix:
|
check_cachix:
|
||||||
name: Cachix secret present for installer tests
|
name: Cachix secret present for installer tests
|
||||||
|
@ -95,7 +95,7 @@ jobs:
|
||||||
name: '${{ env.CACHIX_NAME }}'
|
name: '${{ env.CACHIX_NAME }}'
|
||||||
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
- run: nix-build -A checks.$(nix-instantiate --eval -E 'builtins.currentSystem' --json).dockerImage
|
- run: nix --experimental-features 'nix-command flakes' build .#dockerImage -L
|
||||||
- run: docker load -i ./result/image.tar.gz
|
- run: docker load -i ./result/image.tar.gz
|
||||||
- run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION
|
- run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION
|
||||||
- run: docker tag nix:$NIX_VERSION nixos/nix:master
|
- run: docker tag nix:$NIX_VERSION nixos/nix:master
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -10,7 +10,6 @@ makefiles = \
|
||||||
src/libexpr/local.mk \
|
src/libexpr/local.mk \
|
||||||
src/libcmd/local.mk \
|
src/libcmd/local.mk \
|
||||||
src/nix/local.mk \
|
src/nix/local.mk \
|
||||||
src/nlohmann/local.mk \
|
|
||||||
src/resolve-system-dependencies/local.mk \
|
src/resolve-system-dependencies/local.mk \
|
||||||
scripts/local.mk \
|
scripts/local.mk \
|
||||||
misc/bash/local.mk \
|
misc/bash/local.mk \
|
||||||
|
|
|
@ -16,6 +16,7 @@ LDFLAGS = @LDFLAGS@
|
||||||
LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@
|
LIBARCHIVE_LIBS = @LIBARCHIVE_LIBS@
|
||||||
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@
|
LIBBROTLI_LIBS = @LIBBROTLI_LIBS@
|
||||||
LIBCURL_LIBS = @LIBCURL_LIBS@
|
LIBCURL_LIBS = @LIBCURL_LIBS@
|
||||||
|
LOWDOWN_LIBS = @LOWDOWN_LIBS@
|
||||||
OPENSSL_LIBS = @OPENSSL_LIBS@
|
OPENSSL_LIBS = @OPENSSL_LIBS@
|
||||||
LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
|
LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
|
||||||
PACKAGE_NAME = @PACKAGE_NAME@
|
PACKAGE_NAME = @PACKAGE_NAME@
|
||||||
|
|
|
@ -262,13 +262,17 @@ fi
|
||||||
PKG_CHECK_MODULES([GTEST], [gtest_main])
|
PKG_CHECK_MODULES([GTEST], [gtest_main])
|
||||||
|
|
||||||
|
|
||||||
|
# Look for nlohmann/json.
|
||||||
|
PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9])
|
||||||
|
|
||||||
|
|
||||||
# documentation generation switch
|
# documentation generation switch
|
||||||
AC_ARG_ENABLE(doc-gen, AS_HELP_STRING([--disable-doc-gen],[disable documentation generation]),
|
AC_ARG_ENABLE(doc-gen, AS_HELP_STRING([--disable-doc-gen],[disable documentation generation]),
|
||||||
doc_generate=$enableval, doc_generate=yes)
|
doc_generate=$enableval, doc_generate=yes)
|
||||||
AC_SUBST(doc_generate)
|
AC_SUBST(doc_generate)
|
||||||
|
|
||||||
# Look for lowdown library.
|
# Look for lowdown library.
|
||||||
PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.8.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
|
PKG_CHECK_MODULES([LOWDOWN], [lowdown >= 0.9.0], [CXXFLAGS="$LOWDOWN_CFLAGS $CXXFLAGS"])
|
||||||
|
|
||||||
# Setuid installations.
|
# Setuid installations.
|
||||||
AC_CHECK_FUNCS([setresuid setreuid lchown])
|
AC_CHECK_FUNCS([setresuid setreuid lchown])
|
||||||
|
|
|
@ -20,7 +20,7 @@ concatStrings (map
|
||||||
# JSON, but that converts to "{ }" here.
|
# JSON, but that converts to "{ }" here.
|
||||||
(if isAttrs option.value then "`\"\"`"
|
(if isAttrs option.value then "`\"\"`"
|
||||||
else "`" + toString option.value + "`")) + "\n\n"
|
else "`" + toString option.value + "`")) + "\n\n"
|
||||||
else " **Default:** *machine-specific*")
|
else " **Default:** *machine-specific*\n")
|
||||||
+ (if option.aliases != []
|
+ (if option.aliases != []
|
||||||
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"
|
then " **Deprecated alias:** " + (concatStringsSep ", " (map (s: "`${s}`") option.aliases)) + "\n\n"
|
||||||
else "")
|
else "")
|
||||||
|
|
|
@ -1,2 +1,9 @@
|
||||||
# Release X.Y (202?-??-??)
|
# Release X.Y (202?-??-??)
|
||||||
|
|
||||||
|
* `nix bundle` breaking API change now supports bundlers of the form
|
||||||
|
`bundler.<system>.<name>= derivation: another-derivation;`. This supports
|
||||||
|
additional functionality to inspect evaluation information during bundling. A
|
||||||
|
new [repository](https://github.com/NixOS/bundlers) has various bundlers
|
||||||
|
implemented.
|
||||||
|
|
||||||
|
* `nix store ping` now reports the version of the remote Nix daemon.
|
||||||
|
|
38
flake.nix
38
flake.nix
|
@ -133,6 +133,7 @@
|
||||||
./boehmgc-coroutine-sp-fallback.diff
|
./boehmgc-coroutine-sp-fallback.diff
|
||||||
];
|
];
|
||||||
}))
|
}))
|
||||||
|
nlohmann_json
|
||||||
];
|
];
|
||||||
|
|
||||||
perlDeps =
|
perlDeps =
|
||||||
|
@ -446,19 +447,7 @@
|
||||||
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
|
installerScriptForGHA = installScriptFor [ "x86_64-linux" "x86_64-darwin" "armv6l-linux" "armv7l-linux"];
|
||||||
|
|
||||||
# docker image with Nix inside
|
# docker image with Nix inside
|
||||||
dockerImage = nixpkgs.lib.genAttrs linux64BitSystems (system:
|
dockerImage = nixpkgs.lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage);
|
||||||
let
|
|
||||||
pkgs = nixpkgsFor.${system};
|
|
||||||
image = import ./docker.nix { inherit pkgs; tag = version; };
|
|
||||||
in pkgs.runCommand "docker-image-tarball-${version}"
|
|
||||||
{ meta.description = "Docker image with Nix for ${system}";
|
|
||||||
}
|
|
||||||
''
|
|
||||||
mkdir -p $out/nix-support
|
|
||||||
image=$out/image.tar.gz
|
|
||||||
ln -s ${image} $image
|
|
||||||
echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
|
|
||||||
'');
|
|
||||||
|
|
||||||
# Line coverage analysis.
|
# Line coverage analysis.
|
||||||
coverage =
|
coverage =
|
||||||
|
@ -605,6 +594,20 @@
|
||||||
|
|
||||||
hardeningDisable = [ "pie" ];
|
hardeningDisable = [ "pie" ];
|
||||||
};
|
};
|
||||||
|
dockerImage =
|
||||||
|
let
|
||||||
|
pkgs = nixpkgsFor.${system};
|
||||||
|
image = import ./docker.nix { inherit pkgs; tag = version; };
|
||||||
|
in
|
||||||
|
pkgs.runCommand
|
||||||
|
"docker-image-tarball-${version}"
|
||||||
|
{ meta.description = "Docker image with Nix for ${system}"; }
|
||||||
|
''
|
||||||
|
mkdir -p $out/nix-support
|
||||||
|
image=$out/image.tar.gz
|
||||||
|
ln -s ${image} $image
|
||||||
|
echo "file binary-dist $image" >> $out/nix-support/hydra-build-products
|
||||||
|
'';
|
||||||
} // builtins.listToAttrs (map (crossSystem: {
|
} // builtins.listToAttrs (map (crossSystem: {
|
||||||
name = "nix-${crossSystem}";
|
name = "nix-${crossSystem}";
|
||||||
value = let
|
value = let
|
||||||
|
@ -645,11 +648,10 @@
|
||||||
installCheckFlags = "sysconfdir=$(out)/etc";
|
installCheckFlags = "sysconfdir=$(out)/etc";
|
||||||
};
|
};
|
||||||
}) crossSystems)) // (builtins.listToAttrs (map (stdenvName:
|
}) crossSystems)) // (builtins.listToAttrs (map (stdenvName:
|
||||||
nixpkgsFor.${system}.lib.nameValuePair
|
nixpkgsFor.${system}.lib.nameValuePair
|
||||||
"nix-${stdenvName}"
|
"nix-${stdenvName}"
|
||||||
nixpkgsFor.${system}."${stdenvName}Packages".nix
|
nixpkgsFor.${system}."${stdenvName}Packages".nix
|
||||||
) stdenvs))
|
) stdenvs)));
|
||||||
);
|
|
||||||
|
|
||||||
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
|
defaultPackage = forAllSystems (system: self.packages.${system}.nix);
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ function _complete_nix {
|
||||||
else
|
else
|
||||||
COMPREPLY+=("$completion")
|
COMPREPLY+=("$completion")
|
||||||
fi
|
fi
|
||||||
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}")
|
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}" 2>/dev/null)
|
||||||
__ltrim_colon_completions "$cur"
|
__ltrim_colon_completions "$cur"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ function _nix() {
|
||||||
local ifs_bk="$IFS"
|
local ifs_bk="$IFS"
|
||||||
local input=("${(Q)words[@]}")
|
local input=("${(Q)words[@]}")
|
||||||
IFS=$'\n'
|
IFS=$'\n'
|
||||||
local res=($(NIX_GET_COMPLETIONS=$((CURRENT - 1)) "$input[@]"))
|
local res=($(NIX_GET_COMPLETIONS=$((CURRENT - 1)) "$input[@]" 2>/dev/null))
|
||||||
IFS="$ifs_bk"
|
IFS="$ifs_bk"
|
||||||
local tpe="${${res[1]}%%> *}"
|
local tpe="${${res[1]}%%> *}"
|
||||||
local -a suggestions
|
local -a suggestions
|
||||||
|
|
|
@ -16,12 +16,17 @@ someBuildFailed=0
|
||||||
for buildId in $BUILDS_FOR_LATEST_EVAL; do
|
for buildId in $BUILDS_FOR_LATEST_EVAL; do
|
||||||
buildInfo=$(curl -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
|
buildInfo=$(curl -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId")
|
||||||
|
|
||||||
buildStatus=$(echo "$buildInfo" | \
|
finished=$(echo "$buildInfo" | jq -r '.finished')
|
||||||
jq -r '.buildstatus')
|
|
||||||
|
|
||||||
if [[ "$buildStatus" -ne 0 ]]; then
|
if [[ $finished = 0 ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
buildStatus=$(echo "$buildInfo" | jq -r '.buildstatus')
|
||||||
|
|
||||||
|
if [[ $buildStatus != 0 ]]; then
|
||||||
someBuildFailed=1
|
someBuildFailed=1
|
||||||
echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra"
|
echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra: $buildInfo"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
@ -576,21 +576,40 @@ create_directories() {
|
||||||
# since this bit is cross-platform:
|
# since this bit is cross-platform:
|
||||||
# - first try with `command -vp` to try and find
|
# - first try with `command -vp` to try and find
|
||||||
# chown in the usual places
|
# chown in the usual places
|
||||||
|
# * to work around some sort of deficiency in
|
||||||
|
# `command -p` in macOS bash 3.2, we also add
|
||||||
|
# PATH="$(getconf PATH 2>/dev/null)". As long as
|
||||||
|
# getconf is found, this should set a sane PATH
|
||||||
|
# which `command -p` in bash 3.2 appears to use.
|
||||||
|
# A bash with a properly-working `command -p`
|
||||||
|
# should ignore this hard-set PATH in favor of
|
||||||
|
# whatever it obtains internally. See
|
||||||
|
# github.com/NixOS/nix/issues/5768
|
||||||
# - fall back on `command -v` which would find
|
# - fall back on `command -v` which would find
|
||||||
# any chown on path
|
# any chown on path
|
||||||
# if we don't find one, the command is already
|
# if we don't find one, the command is already
|
||||||
# hiding behind || true, and the general state
|
# hiding behind || true, and the general state
|
||||||
# should be one the user can repair once they
|
# should be one the user can repair once they
|
||||||
# figure out where chown is...
|
# figure out where chown is...
|
||||||
local get_chr_own="$(command -vp chown)"
|
local get_chr_own="$(PATH="$(getconf PATH 2>/dev/null)" command -vp chown)"
|
||||||
if [[ -z "$get_chr_own" ]]; then
|
if [[ -z "$get_chr_own" ]]; then
|
||||||
get_chr_own="$(command -v chown)"
|
get_chr_own="$(command -v chown)"
|
||||||
fi
|
fi
|
||||||
_sudo "to take root ownership of existing Nix store files" \
|
|
||||||
"$get_chr_own" -R "root:$NIX_BUILD_GROUP_NAME" "$NIX_ROOT" || true
|
if [[ -z "$get_chr_own" ]]; then
|
||||||
|
reminder <<EOF
|
||||||
|
I wanted to take root ownership of existing Nix store files,
|
||||||
|
but I couldn't locate 'chown'. (You may need to fix your PATH.)
|
||||||
|
To manually change file ownership, you can run:
|
||||||
|
sudo chown -R 'root:$NIX_BUILD_GROUP_NAME' '$NIX_ROOT'
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
_sudo "to take root ownership of existing Nix store files" \
|
||||||
|
"$get_chr_own" -R "root:$NIX_BUILD_GROUP_NAME" "$NIX_ROOT" || true
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
_sudo "to make the basic directory structure of Nix (part 1)" \
|
_sudo "to make the basic directory structure of Nix (part 1)" \
|
||||||
install -dv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool} /nix/var/nix/{gcroots,profiles}/per-user
|
install -dv -m 0755 /nix /nix/var /nix/var/log /nix/var/log/nix /nix/var/log/nix/drvs /nix/var/nix{,/db,/gcroots,/profiles,/temproots,/userpool,/daemon-socket} /nix/var/nix/{gcroots,profiles}/per-user
|
||||||
|
|
||||||
_sudo "to make the basic directory structure of Nix (part 2)" \
|
_sudo "to make the basic directory structure of Nix (part 2)" \
|
||||||
install -dv -g "$NIX_BUILD_GROUP_NAME" -m 1775 /nix/store
|
install -dv -g "$NIX_BUILD_GROUP_NAME" -m 1775 /nix/store
|
||||||
|
|
|
@ -97,7 +97,7 @@ MixFlakeOptions::MixFlakeOptions()
|
||||||
lockFlags.writeLockFile = false;
|
lockFlags.writeLockFile = false;
|
||||||
lockFlags.inputOverrides.insert_or_assign(
|
lockFlags.inputOverrides.insert_or_assign(
|
||||||
flake::parseInputPath(inputPath),
|
flake::parseInputPath(inputPath),
|
||||||
parseFlakeRef(flakeRef, absPath(".")));
|
parseFlakeRef(flakeRef, absPath("."), true));
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -198,8 +198,9 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||||
prefix_ = "";
|
prefix_ = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Value &v1(*findAlongAttrPath(*state, prefix_, *autoArgs, root).first);
|
auto [v, pos] = findAlongAttrPath(*state, prefix_, *autoArgs, root);
|
||||||
state->forceValue(v1);
|
Value &v1(*v);
|
||||||
|
state->forceValue(v1, pos);
|
||||||
Value v2;
|
Value v2;
|
||||||
state->autoCallFunction(*autoArgs, v1, v2);
|
state->autoCallFunction(*autoArgs, v1, v2);
|
||||||
|
|
||||||
|
@ -446,7 +447,7 @@ struct InstallableAttrPath : InstallableValue
|
||||||
std::pair<Value *, Pos> toValue(EvalState & state) override
|
std::pair<Value *, Pos> toValue(EvalState & state) override
|
||||||
{
|
{
|
||||||
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
auto [vRes, pos] = findAlongAttrPath(state, attrPath, *cmd.getAutoArgs(state), **v);
|
||||||
state.forceValue(*vRes);
|
state.forceValue(*vRes, pos);
|
||||||
return {vRes, pos};
|
return {vRes, pos};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,7 +497,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked
|
||||||
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
||||||
assert(aOutputs);
|
assert(aOutputs);
|
||||||
|
|
||||||
state.forceValue(*aOutputs->value);
|
state.forceValue(*aOutputs->value, [&]() { return aOutputs->value->determinePos(noPos); });
|
||||||
|
|
||||||
return aOutputs->value;
|
return aOutputs->value;
|
||||||
}
|
}
|
||||||
|
@ -521,7 +522,7 @@ ref<eval_cache::EvalCache> openEvalCache(
|
||||||
auto vFlake = state.allocValue();
|
auto vFlake = state.allocValue();
|
||||||
flake::callFlake(state, *lockedFlake, *vFlake);
|
flake::callFlake(state, *lockedFlake, *vFlake);
|
||||||
|
|
||||||
state.forceAttrs(*vFlake);
|
state.forceAttrs(*vFlake, noPos);
|
||||||
|
|
||||||
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
|
||||||
assert(aOutputs);
|
assert(aOutputs);
|
||||||
|
@ -544,13 +545,14 @@ InstallableFlake::InstallableFlake(
|
||||||
SourceExprCommand * cmd,
|
SourceExprCommand * cmd,
|
||||||
ref<EvalState> state,
|
ref<EvalState> state,
|
||||||
FlakeRef && flakeRef,
|
FlakeRef && flakeRef,
|
||||||
Strings && attrPaths,
|
std::string_view fragment,
|
||||||
Strings && prefixes,
|
Strings attrPaths,
|
||||||
|
Strings prefixes,
|
||||||
const flake::LockFlags & lockFlags)
|
const flake::LockFlags & lockFlags)
|
||||||
: InstallableValue(state),
|
: InstallableValue(state),
|
||||||
flakeRef(flakeRef),
|
flakeRef(flakeRef),
|
||||||
attrPaths(attrPaths),
|
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
|
||||||
prefixes(prefixes),
|
prefixes(fragment == "" ? Strings{} : prefixes),
|
||||||
lockFlags(lockFlags)
|
lockFlags(lockFlags)
|
||||||
{
|
{
|
||||||
if (cmd && cmd->getAutoArgs(*state)->size())
|
if (cmd && cmd->getAutoArgs(*state)->size())
|
||||||
|
@ -565,6 +567,8 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
|
||||||
auto root = cache->getRoot();
|
auto root = cache->getRoot();
|
||||||
|
|
||||||
for (auto & attrPath : getActualAttrPaths()) {
|
for (auto & attrPath : getActualAttrPaths()) {
|
||||||
|
debug("trying flake output attribute '%s'", attrPath);
|
||||||
|
|
||||||
auto attr = root->findAlongAttrPath(
|
auto attr = root->findAlongAttrPath(
|
||||||
parseAttrPath(*state, attrPath),
|
parseAttrPath(*state, attrPath),
|
||||||
true
|
true
|
||||||
|
@ -608,7 +612,7 @@ std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
||||||
for (auto & attrPath : getActualAttrPaths()) {
|
for (auto & attrPath : getActualAttrPaths()) {
|
||||||
try {
|
try {
|
||||||
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
|
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
|
||||||
state.forceValue(*v);
|
state.forceValue(*v, pos);
|
||||||
return {v, pos};
|
return {v, pos};
|
||||||
} catch (AttrPathNotFound & e) {
|
} catch (AttrPathNotFound & e) {
|
||||||
}
|
}
|
||||||
|
@ -707,7 +711,8 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
this,
|
this,
|
||||||
getEvalState(),
|
getEvalState(),
|
||||||
std::move(flakeRef),
|
std::move(flakeRef),
|
||||||
fragment == "" ? getDefaultFlakeAttrPaths() : Strings{fragment},
|
fragment,
|
||||||
|
getDefaultFlakeAttrPaths(),
|
||||||
getDefaultFlakeAttrPathPrefixes(),
|
getDefaultFlakeAttrPathPrefixes(),
|
||||||
lockFlags));
|
lockFlags));
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -102,8 +102,9 @@ struct InstallableFlake : InstallableValue
|
||||||
SourceExprCommand * cmd,
|
SourceExprCommand * cmd,
|
||||||
ref<EvalState> state,
|
ref<EvalState> state,
|
||||||
FlakeRef && flakeRef,
|
FlakeRef && flakeRef,
|
||||||
Strings && attrPaths,
|
std::string_view fragment,
|
||||||
Strings && prefixes,
|
Strings attrPaths,
|
||||||
|
Strings prefixes,
|
||||||
const flake::LockFlags & lockFlags);
|
const flake::LockFlags & lockFlags);
|
||||||
|
|
||||||
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
std::string what() const override { return flakeRef.to_string() + "#" + *attrPaths.begin(); }
|
||||||
|
|
|
@ -8,7 +8,7 @@ libcmd_SOURCES := $(wildcard $(d)/*.cc)
|
||||||
|
|
||||||
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
|
libcmd_CXXFLAGS += -I src/libutil -I src/libstore -I src/libexpr -I src/libmain -I src/libfetchers
|
||||||
|
|
||||||
libcmd_LDFLAGS += -llowdown -pthread
|
libcmd_LDFLAGS += $(LOWDOWN_LIBS) -pthread
|
||||||
|
|
||||||
libcmd_LIBS = libstore libutil libexpr libmain libfetchers
|
libcmd_LIBS = libstore libutil libexpr libmain libfetchers
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const string & attr
|
||||||
Value * vNew = state.allocValue();
|
Value * vNew = state.allocValue();
|
||||||
state.autoCallFunction(autoArgs, *v, *vNew);
|
state.autoCallFunction(autoArgs, *v, *vNew);
|
||||||
v = vNew;
|
v = vNew;
|
||||||
state.forceValue(*v);
|
state.forceValue(*v, noPos);
|
||||||
|
|
||||||
/* It should evaluate to either a set or an expression,
|
/* It should evaluate to either a set or an expression,
|
||||||
according to what is specified in the attrPath. */
|
according to what is specified in the attrPath. */
|
||||||
|
@ -121,7 +121,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||||
std::string filename(pos, 0, colon);
|
std::string filename(pos, 0, colon);
|
||||||
unsigned int lineno;
|
unsigned int lineno;
|
||||||
try {
|
try {
|
||||||
lineno = std::stoi(std::string(pos, colon + 1));
|
lineno = std::stoi(std::string(pos, colon + 1, string::npos));
|
||||||
} catch (std::invalid_argument & e) {
|
} catch (std::invalid_argument & e) {
|
||||||
throw ParseError("cannot parse line number '%s'", pos);
|
throw ParseError("cannot parse line number '%s'", pos);
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,7 +336,7 @@ Value & AttrCursor::getValue()
|
||||||
if (!_value) {
|
if (!_value) {
|
||||||
if (parent) {
|
if (parent) {
|
||||||
auto & vParent = parent->first->getValue();
|
auto & vParent = parent->first->getValue();
|
||||||
root->state.forceAttrs(vParent);
|
root->state.forceAttrs(vParent, noPos);
|
||||||
auto attr = vParent.attrs->get(parent->second);
|
auto attr = vParent.attrs->get(parent->second);
|
||||||
if (!attr)
|
if (!attr)
|
||||||
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
||||||
|
@ -381,7 +381,7 @@ Value & AttrCursor::forceValue()
|
||||||
auto & v = getValue();
|
auto & v = getValue();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
root->state.forceValue(v);
|
root->state.forceValue(v, noPos);
|
||||||
} catch (EvalError &) {
|
} catch (EvalError &) {
|
||||||
debug("setting '%s' to failed", getAttrPathStr());
|
debug("setting '%s' to failed", getAttrPathStr());
|
||||||
if (root->db)
|
if (root->db)
|
||||||
|
|
|
@ -15,12 +15,6 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
|
||||||
{
|
|
||||||
throw TypeError(s, showType(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
|
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
|
||||||
{
|
{
|
||||||
throw TypeError({
|
throw TypeError({
|
||||||
|
@ -31,6 +25,13 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
|
||||||
|
|
||||||
|
|
||||||
void EvalState::forceValue(Value & v, const Pos & pos)
|
void EvalState::forceValue(Value & v, const Pos & pos)
|
||||||
|
{
|
||||||
|
forceValue(v, [&]() { return pos; });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Callable>
|
||||||
|
void EvalState::forceValue(Value & v, Callable getPos)
|
||||||
{
|
{
|
||||||
if (v.isThunk()) {
|
if (v.isThunk()) {
|
||||||
Env * env = v.thunk.env;
|
Env * env = v.thunk.env;
|
||||||
|
@ -47,31 +48,22 @@ void EvalState::forceValue(Value & v, const Pos & pos)
|
||||||
else if (v.isApp())
|
else if (v.isApp())
|
||||||
callFunction(*v.app.left, *v.app.right, v, noPos);
|
callFunction(*v.app.left, *v.app.right, v, noPos);
|
||||||
else if (v.isBlackhole())
|
else if (v.isBlackhole())
|
||||||
throwEvalError(pos, "infinite recursion encountered");
|
throwEvalError(getPos(), "infinite recursion encountered");
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void EvalState::forceAttrs(Value & v)
|
|
||||||
{
|
|
||||||
forceValue(v);
|
|
||||||
if (v.type() != nAttrs)
|
|
||||||
throwTypeError("value is %1% while a set was expected", v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceAttrs(v, [&]() { return pos; });
|
||||||
if (v.type() != nAttrs)
|
|
||||||
throwTypeError(pos, "value is %1% while a set was expected", v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void EvalState::forceList(Value & v)
|
template <typename Callable>
|
||||||
|
inline void EvalState::forceAttrs(Value & v, Callable getPos)
|
||||||
{
|
{
|
||||||
forceValue(v);
|
forceValue(v, getPos);
|
||||||
if (!v.isList())
|
if (v.type() != nAttrs)
|
||||||
throwTypeError("value is %1% while a list was expected", v);
|
throwTypeError(getPos(), "value is %1% while a set was expected", v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
|
#include "types.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
@ -218,7 +219,7 @@ string showType(const Value & v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pos Value::determinePos(const Pos &pos) const
|
Pos Value::determinePos(const Pos & pos) const
|
||||||
{
|
{
|
||||||
switch (internalType) {
|
switch (internalType) {
|
||||||
case tAttrs: return *attrs->pos;
|
case tAttrs: return *attrs->pos;
|
||||||
|
@ -753,6 +754,11 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
|
||||||
|
{
|
||||||
|
throw TypeError(s, showType(v));
|
||||||
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
|
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const string & s1))
|
||||||
{
|
{
|
||||||
throw AssertionError({
|
throw AssertionError({
|
||||||
|
@ -1137,7 +1143,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
Hence we need __overrides.) */
|
Hence we need __overrides.) */
|
||||||
if (hasOverrides) {
|
if (hasOverrides) {
|
||||||
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
|
Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
|
||||||
state.forceAttrs(*vOverrides);
|
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); });
|
||||||
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
|
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
|
||||||
for (auto & i : *v.attrs)
|
for (auto & i : *v.attrs)
|
||||||
newBnds->push_back(i);
|
newBnds->push_back(i);
|
||||||
|
@ -1285,7 +1291,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||||
e->eval(state, env, vTmp);
|
e->eval(state, env, vTmp);
|
||||||
|
|
||||||
for (auto & i : attrPath) {
|
for (auto & i : attrPath) {
|
||||||
state.forceValue(*vAttrs);
|
state.forceValue(*vAttrs, noPos);
|
||||||
Bindings::iterator j;
|
Bindings::iterator j;
|
||||||
Symbol name = getName(i, state, env);
|
Symbol name = getName(i, state, env);
|
||||||
if (vAttrs->type() != nAttrs ||
|
if (vAttrs->type() != nAttrs ||
|
||||||
|
@ -1373,7 +1379,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
/* Nope, so show the first unexpected argument to the
|
/* Nope, so show the first unexpected argument to the
|
||||||
user. */
|
user. */
|
||||||
for (auto & i : *args[0]->attrs)
|
for (auto & i : *args[0]->attrs)
|
||||||
if (!lambda.formals->argNames.count(i.name))
|
if (!lambda.formals->has(i.name))
|
||||||
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
|
throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name);
|
||||||
abort(); // can't happen
|
abort(); // can't happen
|
||||||
}
|
}
|
||||||
|
@ -1499,14 +1505,16 @@ void EvalState::incrFunctionCall(ExprLambda * fun)
|
||||||
|
|
||||||
void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||||
{
|
{
|
||||||
forceValue(fun);
|
auto pos = fun.determinePos(noPos);
|
||||||
|
|
||||||
|
forceValue(fun, pos);
|
||||||
|
|
||||||
if (fun.type() == nAttrs) {
|
if (fun.type() == nAttrs) {
|
||||||
auto found = fun.attrs->find(sFunctor);
|
auto found = fun.attrs->find(sFunctor);
|
||||||
if (found != fun.attrs->end()) {
|
if (found != fun.attrs->end()) {
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
callFunction(*found->value, fun, *v, noPos);
|
callFunction(*found->value, fun, *v, pos);
|
||||||
forceValue(*v);
|
forceValue(*v, pos);
|
||||||
return autoCallFunction(args, *v, res);
|
return autoCallFunction(args, *v, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1694,7 +1702,7 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po
|
||||||
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
std::vector<std::string> s;
|
std::vector<BackedStringView> s;
|
||||||
size_t sSize = 0;
|
size_t sSize = 0;
|
||||||
NixInt n = 0;
|
NixInt n = 0;
|
||||||
NixFloat nf = 0;
|
NixFloat nf = 0;
|
||||||
|
@ -1705,7 +1713,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
const auto str = [&] {
|
const auto str = [&] {
|
||||||
std::string result;
|
std::string result;
|
||||||
result.reserve(sSize);
|
result.reserve(sSize);
|
||||||
for (const auto & part : s) result += part;
|
for (const auto & part : s) result += *part;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
/* c_str() is not str().c_str() because we want to create a string
|
/* c_str() is not str().c_str() because we want to create a string
|
||||||
|
@ -1715,15 +1723,18 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
char * result = allocString(sSize + 1);
|
char * result = allocString(sSize + 1);
|
||||||
char * tmp = result;
|
char * tmp = result;
|
||||||
for (const auto & part : s) {
|
for (const auto & part : s) {
|
||||||
memcpy(tmp, part.c_str(), part.size());
|
memcpy(tmp, part->data(), part->size());
|
||||||
tmp += part.size();
|
tmp += part->size();
|
||||||
}
|
}
|
||||||
*tmp = 0;
|
*tmp = 0;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Value values[es->size()];
|
||||||
|
Value * vTmpP = values;
|
||||||
|
|
||||||
for (auto & [i_pos, i] : *es) {
|
for (auto & [i_pos, i] : *es) {
|
||||||
Value vTmp;
|
Value & vTmp = *vTmpP++;
|
||||||
i->eval(state, env, vTmp);
|
i->eval(state, env, vTmp);
|
||||||
|
|
||||||
/* If the first element is a path, then the result will also
|
/* If the first element is a path, then the result will also
|
||||||
|
@ -1756,9 +1767,9 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
/* skip canonization of first path, which would only be not
|
/* skip canonization of first path, which would only be not
|
||||||
canonized in the first place if it's coming from a ./${foo} type
|
canonized in the first place if it's coming from a ./${foo} type
|
||||||
path */
|
path */
|
||||||
s.emplace_back(
|
auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first);
|
||||||
state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first));
|
sSize += part->size();
|
||||||
sSize += s.back().size();
|
s.emplace_back(std::move(part));
|
||||||
}
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
|
@ -1792,7 +1803,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||||
recurse = [&](Value & v) {
|
recurse = [&](Value & v) {
|
||||||
if (!seen.insert(&v).second) return;
|
if (!seen.insert(&v).second) return;
|
||||||
|
|
||||||
forceValue(v);
|
forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||||
|
|
||||||
if (v.type() == nAttrs) {
|
if (v.type() == nAttrs) {
|
||||||
for (auto & i : *v.attrs)
|
for (auto & i : *v.attrs)
|
||||||
|
@ -1857,7 +1868,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string EvalState::forceString(Value & v, const Pos & pos)
|
std::string_view EvalState::forceString(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nString) {
|
if (v.type() != nString) {
|
||||||
|
@ -1866,7 +1877,7 @@ string EvalState::forceString(Value & v, const Pos & pos)
|
||||||
else
|
else
|
||||||
throwTypeError("value is %1% while a string was expected", v);
|
throwTypeError("value is %1% while a string was expected", v);
|
||||||
}
|
}
|
||||||
return string(v.string.s);
|
return v.string.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1901,17 +1912,17 @@ std::vector<std::pair<Path, std::string>> Value::getContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
|
std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
|
||||||
{
|
{
|
||||||
string s = forceString(v, pos);
|
auto s = forceString(v, pos);
|
||||||
copyContext(v, context);
|
copyContext(v, context);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string EvalState::forceStringNoCtx(Value & v, const Pos & pos)
|
std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos)
|
||||||
{
|
{
|
||||||
string s = forceString(v, pos);
|
auto s = forceString(v, pos);
|
||||||
if (v.string.context) {
|
if (v.string.context) {
|
||||||
if (pos)
|
if (pos)
|
||||||
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')",
|
||||||
|
@ -1929,7 +1940,7 @@ bool EvalState::isDerivation(Value & v)
|
||||||
if (v.type() != nAttrs) return false;
|
if (v.type() != nAttrs) return false;
|
||||||
Bindings::iterator i = v.attrs->find(sType);
|
Bindings::iterator i = v.attrs->find(sType);
|
||||||
if (i == v.attrs->end()) return false;
|
if (i == v.attrs->end()) return false;
|
||||||
forceValue(*i->value);
|
forceValue(*i->value, *i->pos);
|
||||||
if (i->value->type() != nString) return false;
|
if (i->value->type() != nString) return false;
|
||||||
return strcmp(i->value->string.s, "derivation") == 0;
|
return strcmp(i->value->string.s, "derivation") == 0;
|
||||||
}
|
}
|
||||||
|
@ -1942,34 +1953,35 @@ std::optional<string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
|
||||||
if (i != v.attrs->end()) {
|
if (i != v.attrs->end()) {
|
||||||
Value v1;
|
Value v1;
|
||||||
callFunction(*i->value, v, v1, pos);
|
callFunction(*i->value, v, v1, pos);
|
||||||
return coerceToString(pos, v1, context, coerceMore, copyToStore);
|
return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||||
bool coerceMore, bool copyToStore, bool canonicalizePath)
|
bool coerceMore, bool copyToStore, bool canonicalizePath)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
|
|
||||||
string s;
|
|
||||||
|
|
||||||
if (v.type() == nString) {
|
if (v.type() == nString) {
|
||||||
copyContext(v, context);
|
copyContext(v, context);
|
||||||
return v.string.s;
|
return std::string_view(v.string.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.type() == nPath) {
|
if (v.type() == nPath) {
|
||||||
Path path(canonicalizePath ? canonPath(v.path) : v.path);
|
BackedStringView path(PathView(v.path));
|
||||||
return copyToStore ? copyPathToStore(context, path) : path;
|
if (canonicalizePath)
|
||||||
|
path = canonPath(*path);
|
||||||
|
if (copyToStore)
|
||||||
|
path = copyPathToStore(context, std::move(path).toOwned());
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.type() == nAttrs) {
|
if (v.type() == nAttrs) {
|
||||||
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
|
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
|
||||||
if (maybeString) {
|
if (maybeString)
|
||||||
return *maybeString;
|
return std::move(*maybeString);
|
||||||
}
|
|
||||||
auto i = v.attrs->find(sOutPath);
|
auto i = v.attrs->find(sOutPath);
|
||||||
if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
|
if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string");
|
||||||
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
return coerceToString(pos, *i->value, context, coerceMore, copyToStore);
|
||||||
|
@ -1991,14 +2003,13 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||||
if (v.isList()) {
|
if (v.isList()) {
|
||||||
string result;
|
string result;
|
||||||
for (auto [n, v2] : enumerate(v.listItems())) {
|
for (auto [n, v2] : enumerate(v.listItems())) {
|
||||||
result += coerceToString(pos, *v2,
|
result += *coerceToString(pos, *v2, context, coerceMore, copyToStore);
|
||||||
context, coerceMore, copyToStore);
|
|
||||||
if (n < v.listSize() - 1
|
if (n < v.listSize() - 1
|
||||||
/* !!! not quite correct */
|
/* !!! not quite correct */
|
||||||
&& (!v2->isList() || v2->listSize() != 0))
|
&& (!v2->isList() || v2->listSize() != 0))
|
||||||
result += " ";
|
result += " ";
|
||||||
}
|
}
|
||||||
return result;
|
return std::move(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2032,7 +2043,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||||
|
|
||||||
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
||||||
{
|
{
|
||||||
string path = coerceToString(pos, v, context, false, false);
|
string path = coerceToString(pos, v, context, false, false).toOwned();
|
||||||
if (path == "" || path[0] != '/')
|
if (path == "" || path[0] != '/')
|
||||||
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
|
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
|
||||||
return path;
|
return path;
|
||||||
|
@ -2041,8 +2052,8 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
||||||
|
|
||||||
bool EvalState::eqValues(Value & v1, Value & v2)
|
bool EvalState::eqValues(Value & v1, Value & v2)
|
||||||
{
|
{
|
||||||
forceValue(v1);
|
forceValue(v1, noPos);
|
||||||
forceValue(v2);
|
forceValue(v2, noPos);
|
||||||
|
|
||||||
/* !!! Hack to support some old broken code that relies on pointer
|
/* !!! Hack to support some old broken code that relies on pointer
|
||||||
equality tests between sets. (Specifically, builderDefs calls
|
equality tests between sets. (Specifically, builderDefs calls
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "attr-set.hh"
|
#include "attr-set.hh"
|
||||||
|
#include "types.hh"
|
||||||
#include "value.hh"
|
#include "value.hh"
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
|
@ -201,8 +202,8 @@ public:
|
||||||
void resetFileCache();
|
void resetFileCache();
|
||||||
|
|
||||||
/* Look up a file in the search path. */
|
/* Look up a file in the search path. */
|
||||||
Path findFile(const string & path);
|
Path findFile(const std::string_view path);
|
||||||
Path findFile(SearchPath & searchPath, const string & path, const Pos & pos = noPos);
|
Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
|
||||||
|
|
||||||
/* If the specified search path element is a URI, download it. */
|
/* If the specified search path element is a URI, download it. */
|
||||||
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
|
std::pair<bool, std::string> resolveSearchPathElem(const SearchPathElem & elem);
|
||||||
|
@ -221,7 +222,10 @@ public:
|
||||||
of the evaluation of the thunk. If `v' is a delayed function
|
of the evaluation of the thunk. If `v' is a delayed function
|
||||||
application, call the function and overwrite `v' with the
|
application, call the function and overwrite `v' with the
|
||||||
result. Otherwise, this is a no-op. */
|
result. Otherwise, this is a no-op. */
|
||||||
inline void forceValue(Value & v, const Pos & pos = noPos);
|
inline void forceValue(Value & v, const Pos & pos);
|
||||||
|
|
||||||
|
template <typename Callable>
|
||||||
|
inline void forceValue(Value & v, Callable getPos);
|
||||||
|
|
||||||
/* Force a value, then recursively force list elements and
|
/* Force a value, then recursively force list elements and
|
||||||
attributes. */
|
attributes. */
|
||||||
|
@ -231,14 +235,17 @@ public:
|
||||||
NixInt forceInt(Value & v, const Pos & pos);
|
NixInt forceInt(Value & v, const Pos & pos);
|
||||||
NixFloat forceFloat(Value & v, const Pos & pos);
|
NixFloat forceFloat(Value & v, const Pos & pos);
|
||||||
bool forceBool(Value & v, const Pos & pos);
|
bool forceBool(Value & v, const Pos & pos);
|
||||||
inline void forceAttrs(Value & v);
|
|
||||||
inline void forceAttrs(Value & v, const Pos & pos);
|
void forceAttrs(Value & v, const Pos & pos);
|
||||||
inline void forceList(Value & v);
|
|
||||||
|
template <typename Callable>
|
||||||
|
inline void forceAttrs(Value & v, Callable getPos);
|
||||||
|
|
||||||
inline void forceList(Value & v, const Pos & pos);
|
inline void forceList(Value & v, const Pos & pos);
|
||||||
void forceFunction(Value & v, const Pos & pos); // either lambda or primop
|
void forceFunction(Value & v, const Pos & pos); // either lambda or primop
|
||||||
string forceString(Value & v, const Pos & pos = noPos);
|
std::string_view forceString(Value & v, const Pos & pos = noPos);
|
||||||
string forceString(Value & v, PathSet & context, const Pos & pos = noPos);
|
std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos);
|
||||||
string forceStringNoCtx(Value & v, const Pos & pos = noPos);
|
std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos);
|
||||||
|
|
||||||
/* Return true iff the value `v' denotes a derivation (i.e. a
|
/* Return true iff the value `v' denotes a derivation (i.e. a
|
||||||
set with attribute `type = "derivation"'). */
|
set with attribute `type = "derivation"'). */
|
||||||
|
@ -251,7 +258,7 @@ public:
|
||||||
string. If `coerceMore' is set, also converts nulls, integers,
|
string. If `coerceMore' is set, also converts nulls, integers,
|
||||||
booleans and lists to a string. If `copyToStore' is set,
|
booleans and lists to a string. If `copyToStore' is set,
|
||||||
referenced paths are copied to the Nix store as a side effect. */
|
referenced paths are copied to the Nix store as a side effect. */
|
||||||
string coerceToString(const Pos & pos, Value & v, PathSet & context,
|
BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
|
||||||
bool coerceMore = false, bool copyToStore = true,
|
bool coerceMore = false, bool copyToStore = true,
|
||||||
bool canonicalizePath = true);
|
bool canonicalizePath = true);
|
||||||
|
|
||||||
|
@ -309,8 +316,8 @@ private:
|
||||||
friend struct ExprAttrs;
|
friend struct ExprAttrs;
|
||||||
friend struct ExprLet;
|
friend struct ExprLet;
|
||||||
|
|
||||||
Expr * parse(char * text, size_t length, FileOrigin origin, const Path & path,
|
Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
|
||||||
const Path & basePath, StaticEnv & staticEnv);
|
const PathView basePath, StaticEnv & staticEnv);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -89,11 +89,11 @@ static void expectType(EvalState & state, ValueType type,
|
||||||
|
|
||||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
EvalState & state, Value * value, const Pos & pos,
|
EvalState & state, Value * value, const Pos & pos,
|
||||||
const std::optional<Path> & baseDir);
|
const std::optional<Path> & baseDir, InputPath lockRootPath);
|
||||||
|
|
||||||
static FlakeInput parseFlakeInput(EvalState & state,
|
static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
const std::string & inputName, Value * value, const Pos & pos,
|
const std::string & inputName, Value * value, const Pos & pos,
|
||||||
const std::optional<Path> & baseDir)
|
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||||
{
|
{
|
||||||
expectType(state, nAttrs, *value, pos);
|
expectType(state, nAttrs, *value, pos);
|
||||||
|
|
||||||
|
@ -117,10 +117,12 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
expectType(state, nBool, *attr.value, *attr.pos);
|
expectType(state, nBool, *attr.value, *attr.pos);
|
||||||
input.isFlake = attr.value->boolean;
|
input.isFlake = attr.value->boolean;
|
||||||
} else if (attr.name == sInputs) {
|
} else if (attr.name == sInputs) {
|
||||||
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir);
|
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos, baseDir, lockRootPath);
|
||||||
} else if (attr.name == sFollows) {
|
} else if (attr.name == sFollows) {
|
||||||
expectType(state, nString, *attr.value, *attr.pos);
|
expectType(state, nString, *attr.value, *attr.pos);
|
||||||
input.follows = parseInputPath(attr.value->string.s);
|
auto follows(parseInputPath(attr.value->string.s));
|
||||||
|
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
||||||
|
input.follows = follows;
|
||||||
} else {
|
} else {
|
||||||
switch (attr.value->type()) {
|
switch (attr.value->type()) {
|
||||||
case nString:
|
case nString:
|
||||||
|
@ -166,7 +168,7 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
|
|
||||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
EvalState & state, Value * value, const Pos & pos,
|
EvalState & state, Value * value, const Pos & pos,
|
||||||
const std::optional<Path> & baseDir)
|
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||||
{
|
{
|
||||||
std::map<FlakeId, FlakeInput> inputs;
|
std::map<FlakeId, FlakeInput> inputs;
|
||||||
|
|
||||||
|
@ -178,7 +180,8 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
inputAttr.name,
|
inputAttr.name,
|
||||||
inputAttr.value,
|
inputAttr.value,
|
||||||
*inputAttr.pos,
|
*inputAttr.pos,
|
||||||
baseDir));
|
baseDir,
|
||||||
|
lockRootPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
return inputs;
|
return inputs;
|
||||||
|
@ -188,7 +191,8 @@ static Flake getFlake(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const FlakeRef & originalRef,
|
const FlakeRef & originalRef,
|
||||||
bool allowLookup,
|
bool allowLookup,
|
||||||
FlakeCache & flakeCache)
|
FlakeCache & flakeCache,
|
||||||
|
InputPath lockRootPath)
|
||||||
{
|
{
|
||||||
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree(
|
||||||
state, originalRef, allowLookup, flakeCache);
|
state, originalRef, allowLookup, flakeCache);
|
||||||
|
@ -223,7 +227,7 @@ static Flake getFlake(
|
||||||
auto sInputs = state.symbols.create("inputs");
|
auto sInputs = state.symbols.create("inputs");
|
||||||
|
|
||||||
if (auto inputs = vInfo.attrs->get(sInputs))
|
if (auto inputs = vInfo.attrs->get(sInputs))
|
||||||
flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir);
|
flake.inputs = parseFlakeInputs(state, inputs->value, *inputs->pos, flakeDir, lockRootPath);
|
||||||
|
|
||||||
auto sOutputs = state.symbols.create("outputs");
|
auto sOutputs = state.symbols.create("outputs");
|
||||||
|
|
||||||
|
@ -250,10 +254,12 @@ static Flake getFlake(
|
||||||
for (auto & setting : *nixConfig->value->attrs) {
|
for (auto & setting : *nixConfig->value->attrs) {
|
||||||
forceTrivialValue(state, *setting.value, *setting.pos);
|
forceTrivialValue(state, *setting.value, *setting.pos);
|
||||||
if (setting.value->type() == nString)
|
if (setting.value->type() == nString)
|
||||||
flake.config.settings.insert({setting.name, state.forceStringNoCtx(*setting.value, *setting.pos)});
|
flake.config.settings.insert({setting.name, string(state.forceStringNoCtx(*setting.value, *setting.pos))});
|
||||||
else if (setting.value->type() == nPath) {
|
else if (setting.value->type() == nPath) {
|
||||||
PathSet emptyContext = {};
|
PathSet emptyContext = {};
|
||||||
flake.config.settings.insert({setting.name, state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true)});
|
flake.config.settings.emplace(
|
||||||
|
setting.name,
|
||||||
|
state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
|
||||||
}
|
}
|
||||||
else if (setting.value->type() == nInt)
|
else if (setting.value->type() == nInt)
|
||||||
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
|
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)});
|
||||||
|
@ -265,7 +271,7 @@ static Flake getFlake(
|
||||||
if (elem->type() != nString)
|
if (elem->type() != nString)
|
||||||
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
|
||||||
setting.name, showType(*setting.value));
|
setting.name, showType(*setting.value));
|
||||||
ss.push_back(state.forceStringNoCtx(*elem, *setting.pos));
|
ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos));
|
||||||
}
|
}
|
||||||
flake.config.settings.insert({setting.name, ss});
|
flake.config.settings.insert({setting.name, ss});
|
||||||
}
|
}
|
||||||
|
@ -287,6 +293,11 @@ static Flake getFlake(
|
||||||
return flake;
|
return flake;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache)
|
||||||
|
{
|
||||||
|
return getFlake(state, originalRef, allowLookup, flakeCache, {});
|
||||||
|
}
|
||||||
|
|
||||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
|
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
|
||||||
{
|
{
|
||||||
FlakeCache flakeCache;
|
FlakeCache flakeCache;
|
||||||
|
@ -332,22 +343,12 @@ LockedFlake lockFlake(
|
||||||
|
|
||||||
std::vector<FlakeRef> parents;
|
std::vector<FlakeRef> parents;
|
||||||
|
|
||||||
struct LockParent {
|
|
||||||
/* The path to this parent. */
|
|
||||||
InputPath path;
|
|
||||||
|
|
||||||
/* Whether we are currently inside a top-level lockfile
|
|
||||||
(inputs absolute) or subordinate lockfile (inputs
|
|
||||||
relative). */
|
|
||||||
bool absolute;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::function<void(
|
std::function<void(
|
||||||
const FlakeInputs & flakeInputs,
|
const FlakeInputs & flakeInputs,
|
||||||
std::shared_ptr<Node> node,
|
std::shared_ptr<Node> node,
|
||||||
const InputPath & inputPathPrefix,
|
const InputPath & inputPathPrefix,
|
||||||
std::shared_ptr<const Node> oldNode,
|
std::shared_ptr<const Node> oldNode,
|
||||||
const LockParent & parent,
|
const InputPath & lockRootPath,
|
||||||
const Path & parentPath,
|
const Path & parentPath,
|
||||||
bool trustLock)>
|
bool trustLock)>
|
||||||
computeLocks;
|
computeLocks;
|
||||||
|
@ -357,7 +358,7 @@ LockedFlake lockFlake(
|
||||||
std::shared_ptr<Node> node,
|
std::shared_ptr<Node> node,
|
||||||
const InputPath & inputPathPrefix,
|
const InputPath & inputPathPrefix,
|
||||||
std::shared_ptr<const Node> oldNode,
|
std::shared_ptr<const Node> oldNode,
|
||||||
const LockParent & parent,
|
const InputPath & lockRootPath,
|
||||||
const Path & parentPath,
|
const Path & parentPath,
|
||||||
bool trustLock)
|
bool trustLock)
|
||||||
{
|
{
|
||||||
|
@ -402,17 +403,7 @@ LockedFlake lockFlake(
|
||||||
if (input.follows) {
|
if (input.follows) {
|
||||||
InputPath target;
|
InputPath target;
|
||||||
|
|
||||||
if (parent.absolute && !hasOverride) {
|
target.insert(target.end(), input.follows->begin(), input.follows->end());
|
||||||
target = *input.follows;
|
|
||||||
} else {
|
|
||||||
if (hasOverride) {
|
|
||||||
target = inputPathPrefix;
|
|
||||||
target.pop_back();
|
|
||||||
} else
|
|
||||||
target = parent.path;
|
|
||||||
|
|
||||||
for (auto & i : *input.follows) target.push_back(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("input '%s' follows '%s'", inputPathS, printInputPath(target));
|
debug("input '%s' follows '%s'", inputPathS, printInputPath(target));
|
||||||
node->inputs.insert_or_assign(id, target);
|
node->inputs.insert_or_assign(id, target);
|
||||||
|
@ -485,23 +476,25 @@ LockedFlake lockFlake(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto absoluteFollows(lockRootPath);
|
||||||
|
absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
|
||||||
fakeInputs.emplace(i.first, FlakeInput {
|
fakeInputs.emplace(i.first, FlakeInput {
|
||||||
.follows = *follows,
|
.follows = absoluteFollows,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LockParent newParent {
|
auto localPath(parentPath);
|
||||||
.path = inputPath,
|
// If this input is a path, recurse it down.
|
||||||
.absolute = true
|
// This allows us to resolve path inputs relative to the current flake.
|
||||||
};
|
if ((*input.ref).input.getType() == "path")
|
||||||
|
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
|
||||||
computeLocks(
|
computeLocks(
|
||||||
mustRefetch
|
mustRefetch
|
||||||
? getFlake(state, oldLock->lockedRef, false, flakeCache).inputs
|
? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs
|
||||||
: fakeInputs,
|
: fakeInputs,
|
||||||
childNode, inputPath, oldLock, newParent, parentPath, !mustRefetch);
|
childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* We need to create a new lock file entry. So fetch
|
/* We need to create a new lock file entry. So fetch
|
||||||
|
@ -520,7 +513,7 @@ LockedFlake lockFlake(
|
||||||
if (localRef.input.getType() == "path")
|
if (localRef.input.getType() == "path")
|
||||||
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
|
localPath = absPath(*input.ref->input.getSourcePath(), parentPath);
|
||||||
|
|
||||||
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache);
|
auto inputFlake = getFlake(state, localRef, useRegistries, flakeCache, inputPath);
|
||||||
|
|
||||||
/* Note: in case of an --override-input, we use
|
/* Note: in case of an --override-input, we use
|
||||||
the *original* ref (input2.ref) for the
|
the *original* ref (input2.ref) for the
|
||||||
|
@ -541,13 +534,6 @@ LockedFlake lockFlake(
|
||||||
parents.push_back(*input.ref);
|
parents.push_back(*input.ref);
|
||||||
Finally cleanup([&]() { parents.pop_back(); });
|
Finally cleanup([&]() { parents.pop_back(); });
|
||||||
|
|
||||||
// Follows paths from existing inputs in the top-level lockfile are absolute,
|
|
||||||
// whereas paths in subordinate lockfiles are relative to those lockfiles.
|
|
||||||
LockParent newParent {
|
|
||||||
.path = inputPath,
|
|
||||||
.absolute = oldLock ? true : false
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Recursively process the inputs of this
|
/* Recursively process the inputs of this
|
||||||
flake. Also, unless we already have this flake
|
flake. Also, unless we already have this flake
|
||||||
in the top-level lock file, use this flake's
|
in the top-level lock file, use this flake's
|
||||||
|
@ -558,7 +544,7 @@ LockedFlake lockFlake(
|
||||||
? std::dynamic_pointer_cast<const Node>(oldLock)
|
? std::dynamic_pointer_cast<const Node>(oldLock)
|
||||||
: LockFile::read(
|
: LockFile::read(
|
||||||
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
|
inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root,
|
||||||
newParent, localPath, false);
|
oldLock ? lockRootPath : inputPath, localPath, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -576,17 +562,12 @@ LockedFlake lockFlake(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
LockParent parent {
|
|
||||||
.path = {},
|
|
||||||
.absolute = true
|
|
||||||
};
|
|
||||||
|
|
||||||
// Bring in the current ref for relative path resolution if we have it
|
// Bring in the current ref for relative path resolution if we have it
|
||||||
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
|
auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true);
|
||||||
|
|
||||||
computeLocks(
|
computeLocks(
|
||||||
flake.inputs, newLockFile.root, {},
|
flake.inputs, newLockFile.root, {},
|
||||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, parent, parentPath, false);
|
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, parentPath, false);
|
||||||
|
|
||||||
for (auto & i : lockFlags.inputOverrides)
|
for (auto & i : lockFlags.inputOverrides)
|
||||||
if (!overridesUsed.count(i.first))
|
if (!overridesUsed.count(i.first))
|
||||||
|
@ -726,7 +707,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
{
|
{
|
||||||
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
|
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
|
||||||
|
|
||||||
auto flakeRefS = state.forceStringNoCtx(*args[0], pos);
|
string flakeRefS(state.forceStringNoCtx(*args[0], pos));
|
||||||
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
||||||
if (evalSettings.pureEval && !flakeRef.input.isImmutable())
|
if (evalSettings.pureEval && !flakeRef.input.isImmutable())
|
||||||
throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
|
throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
|
||||||
|
|
|
@ -104,10 +104,10 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
|
||||||
/* For each output... */
|
/* For each output... */
|
||||||
for (auto elem : i->value->listItems()) {
|
for (auto elem : i->value->listItems()) {
|
||||||
/* Evaluate the corresponding set. */
|
/* Evaluate the corresponding set. */
|
||||||
string name = state->forceStringNoCtx(*elem, *i->pos);
|
string name(state->forceStringNoCtx(*elem, *i->pos));
|
||||||
Bindings::iterator out = attrs->find(state->symbols.create(name));
|
Bindings::iterator out = attrs->find(state->symbols.create(name));
|
||||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||||
state->forceAttrs(*out->value);
|
state->forceAttrs(*out->value, *i->pos);
|
||||||
|
|
||||||
/* And evaluate its ‘outPath’ attribute. */
|
/* And evaluate its ‘outPath’ attribute. */
|
||||||
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
|
||||||
|
@ -172,7 +172,7 @@ StringSet DrvInfo::queryMetaNames()
|
||||||
|
|
||||||
bool DrvInfo::checkMeta(Value & v)
|
bool DrvInfo::checkMeta(Value & v)
|
||||||
{
|
{
|
||||||
state->forceValue(v);
|
state->forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||||
if (v.type() == nList) {
|
if (v.type() == nList) {
|
||||||
for (auto elem : v.listItems())
|
for (auto elem : v.listItems())
|
||||||
if (!checkMeta(*elem)) return false;
|
if (!checkMeta(*elem)) return false;
|
||||||
|
@ -278,7 +278,7 @@ static bool getDerivation(EvalState & state, Value & v,
|
||||||
bool ignoreAssertionFailures)
|
bool ignoreAssertionFailures)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
state.forceValue(v);
|
state.forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||||
if (!state.isDerivation(v)) return true;
|
if (!state.isDerivation(v)) return true;
|
||||||
|
|
||||||
/* Remove spurious duplicates (e.g., a set like `rec { x =
|
/* Remove spurious duplicates (e.g., a set like `rec { x =
|
||||||
|
|
|
@ -163,7 +163,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void parseJSON(EvalState & state, const string & s_, Value & v)
|
void parseJSON(EvalState & state, const std::string_view & s_, Value & v)
|
||||||
{
|
{
|
||||||
JSONSax parser(state, v);
|
JSONSax parser(state, v);
|
||||||
bool res = json::sax_parse(s_, &parser);
|
bool res = json::sax_parse(s_, &parser);
|
||||||
|
|
|
@ -8,6 +8,6 @@ namespace nix {
|
||||||
|
|
||||||
MakeError(JSONParseError, EvalError);
|
MakeError(JSONParseError, EvalError);
|
||||||
|
|
||||||
void parseJSON(EvalState & state, const string & s, Value & v);
|
void parseJSON(EvalState & state, const std::string_view & s, Value & v);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ static void adjustLoc(YYLTYPE * loc, const char * s, size_t len)
|
||||||
|
|
||||||
// we make use of the fact that the parser receives a private copy of the input
|
// we make use of the fact that the parser receives a private copy of the input
|
||||||
// string and can munge around in it.
|
// string and can munge around in it.
|
||||||
static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
|
static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length)
|
||||||
{
|
{
|
||||||
char * result = s;
|
char * result = s;
|
||||||
char * t = s;
|
char * t = s;
|
||||||
|
@ -89,7 +89,7 @@ static Expr * unescapeStr(SymbolTable & symbols, char * s, size_t length)
|
||||||
else *t = c;
|
else *t = c;
|
||||||
t++;
|
t++;
|
||||||
}
|
}
|
||||||
return new ExprString(symbols.create({result, size_t(t - result)}));
|
return {result, size_t(t - result)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ or { return OR_KW; }
|
||||||
/* It is impossible to match strings ending with '$' with one
|
/* It is impossible to match strings ending with '$' with one
|
||||||
regex because trailing contexts are only valid at the end
|
regex because trailing contexts are only valid at the end
|
||||||
of a rule. (A sane but undocumented limitation.) */
|
of a rule. (A sane but undocumented limitation.) */
|
||||||
yylval->e = unescapeStr(data->symbols, yytext, yyleng);
|
yylval->str = unescapeStr(data->symbols, yytext, yyleng);
|
||||||
return STR;
|
return STR;
|
||||||
}
|
}
|
||||||
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
<STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||||
|
@ -191,26 +191,26 @@ or { return OR_KW; }
|
||||||
|
|
||||||
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
|
\'\'(\ *\n)? { PUSH_STATE(IND_STRING); return IND_STRING_OPEN; }
|
||||||
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
<IND_STRING>([^\$\']|\$[^\{\']|\'[^\'\$])+ {
|
||||||
yylval->e = new ExprIndStr(yytext);
|
yylval->str = {yytext, (size_t) yyleng, true};
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\'\'\$ |
|
<IND_STRING>\'\'\$ |
|
||||||
<IND_STRING>\$ {
|
<IND_STRING>\$ {
|
||||||
yylval->e = new ExprIndStr("$");
|
yylval->str = {"$", 1};
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\'\'\' {
|
<IND_STRING>\'\'\' {
|
||||||
yylval->e = new ExprIndStr("''");
|
yylval->str = {"''", 2};
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\'\'\\{ANY} {
|
<IND_STRING>\'\'\\{ANY} {
|
||||||
yylval->e = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
|
yylval->str = unescapeStr(data->symbols, yytext + 2, yyleng - 2);
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
<IND_STRING>\$\{ { PUSH_STATE(DEFAULT); return DOLLAR_CURLY; }
|
||||||
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
|
<IND_STRING>\'\' { POP_STATE(); return IND_STRING_CLOSE; }
|
||||||
<IND_STRING>\' {
|
<IND_STRING>\' {
|
||||||
yylval->e = new ExprIndStr("'");
|
yylval->str = {"'", 1};
|
||||||
return IND_STR;
|
return IND_STR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +264,7 @@ or { return OR_KW; }
|
||||||
PUSH_STATE(INPATH_SLASH);
|
PUSH_STATE(INPATH_SLASH);
|
||||||
else
|
else
|
||||||
PUSH_STATE(INPATH);
|
PUSH_STATE(INPATH);
|
||||||
yylval->e = new ExprString(data->symbols.create(string(yytext)));
|
yylval->str = {yytext, (size_t) yyleng};
|
||||||
return STR;
|
return STR;
|
||||||
}
|
}
|
||||||
<INPATH>{ANY} |
|
<INPATH>{ANY} |
|
||||||
|
|
|
@ -110,20 +110,13 @@ struct ExprFloat : Expr
|
||||||
|
|
||||||
struct ExprString : Expr
|
struct ExprString : Expr
|
||||||
{
|
{
|
||||||
Symbol s;
|
string s;
|
||||||
Value v;
|
Value v;
|
||||||
ExprString(const Symbol & s) : s(s) { v.mkString(s); };
|
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Temporary class used during parsing of indented strings. */
|
|
||||||
struct ExprIndStr : Expr
|
|
||||||
{
|
|
||||||
string s;
|
|
||||||
ExprIndStr(const string & s) : s(s) { };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ExprPath : Expr
|
struct ExprPath : Expr
|
||||||
{
|
{
|
||||||
string s;
|
string s;
|
||||||
|
@ -223,10 +216,25 @@ struct Formal
|
||||||
|
|
||||||
struct Formals
|
struct Formals
|
||||||
{
|
{
|
||||||
typedef std::list<Formal> Formals_;
|
typedef std::vector<Formal> Formals_;
|
||||||
Formals_ formals;
|
Formals_ formals;
|
||||||
std::set<Symbol> argNames; // used during parsing
|
|
||||||
bool ellipsis;
|
bool ellipsis;
|
||||||
|
|
||||||
|
bool has(Symbol arg) const {
|
||||||
|
auto it = std::lower_bound(formals.begin(), formals.end(), arg,
|
||||||
|
[] (const Formal & f, const Symbol & sym) { return f.name < sym; });
|
||||||
|
return it != formals.end() && it->name == arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Formal> lexicographicOrder() const
|
||||||
|
{
|
||||||
|
std::vector<Formal> result(formals.begin(), formals.end());
|
||||||
|
std::sort(result.begin(), result.end(),
|
||||||
|
[] (const Formal & a, const Formal & b) {
|
||||||
|
return std::string_view(a.name) < std::string_view(b.name);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprLambda : Expr
|
struct ExprLambda : Expr
|
||||||
|
@ -239,11 +247,6 @@ struct ExprLambda : Expr
|
||||||
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
|
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
|
||||||
: pos(pos), arg(arg), formals(formals), body(body)
|
: pos(pos), arg(arg), formals(formals), body(body)
|
||||||
{
|
{
|
||||||
if (!arg.empty() && formals && formals->argNames.find(arg) != formals->argNames.end())
|
|
||||||
throw ParseError({
|
|
||||||
.msg = hintfmt("duplicate formal function argument '%1%'", arg),
|
|
||||||
.errPos = pos
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
void setName(Symbol & name);
|
void setName(Symbol & name);
|
||||||
string showNamePos() const;
|
string showNamePos() const;
|
||||||
|
@ -364,15 +367,19 @@ struct StaticEnv
|
||||||
|
|
||||||
void sort()
|
void sort()
|
||||||
{
|
{
|
||||||
std::sort(vars.begin(), vars.end(),
|
std::stable_sort(vars.begin(), vars.end(),
|
||||||
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
|
[](const Vars::value_type & a, const Vars::value_type & b) { return a.first < b.first; });
|
||||||
}
|
}
|
||||||
|
|
||||||
void deduplicate()
|
void deduplicate()
|
||||||
{
|
{
|
||||||
const auto last = std::unique(vars.begin(), vars.end(),
|
auto it = vars.begin(), jt = it, end = vars.end();
|
||||||
[] (const Vars::value_type & a, const Vars::value_type & b) { return a.first == b.first; });
|
while (jt != end) {
|
||||||
vars.erase(last, vars.end());
|
*it = *jt++;
|
||||||
|
while (jt != end && it->first == jt->first) *it = *jt++;
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
vars.erase(it, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vars::const_iterator find(const Symbol & name) const
|
Vars::const_iterator find(const Symbol & name) const
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#ifndef BISON_HEADER
|
#ifndef BISON_HEADER
|
||||||
#define BISON_HEADER
|
#define BISON_HEADER
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
|
@ -39,8 +41,22 @@ namespace nix {
|
||||||
{ };
|
{ };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ParserFormals {
|
||||||
|
std::vector<Formal> formals;
|
||||||
|
bool ellipsis = false;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// using C a struct allows us to avoid having to define the special
|
||||||
|
// members that using string_view here would implicitly delete.
|
||||||
|
struct StringToken {
|
||||||
|
const char * p;
|
||||||
|
size_t l;
|
||||||
|
bool hasIndentation;
|
||||||
|
operator std::string_view() const { return {p, l}; }
|
||||||
|
};
|
||||||
|
|
||||||
#define YY_DECL int yylex \
|
#define YY_DECL int yylex \
|
||||||
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data)
|
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParseData * data)
|
||||||
|
|
||||||
|
@ -140,21 +156,46 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void addFormal(const Pos & pos, Formals * formals, const Formal & formal)
|
static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
||||||
|
Pos pos = noPos, Symbol arg = {})
|
||||||
{
|
{
|
||||||
if (!formals->argNames.insert(formal.name).second)
|
std::sort(formals->formals.begin(), formals->formals.end(),
|
||||||
|
[] (const auto & a, const auto & b) {
|
||||||
|
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::optional<std::pair<Symbol, Pos>> duplicate;
|
||||||
|
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
|
||||||
|
if (formals->formals[i].name != formals->formals[i + 1].name)
|
||||||
|
continue;
|
||||||
|
std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos};
|
||||||
|
duplicate = std::min(thisDup, duplicate.value_or(thisDup));
|
||||||
|
}
|
||||||
|
if (duplicate)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("duplicate formal function argument '%1%'",
|
.msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first),
|
||||||
formal.name),
|
.errPos = duplicate->second
|
||||||
|
});
|
||||||
|
|
||||||
|
Formals result;
|
||||||
|
result.ellipsis = formals->ellipsis;
|
||||||
|
result.formals = std::move(formals->formals);
|
||||||
|
|
||||||
|
if (arg.set() && result.has(arg))
|
||||||
|
throw ParseError({
|
||||||
|
.msg = hintfmt("duplicate formal function argument '%1%'", arg),
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
});
|
});
|
||||||
formals->formals.push_front(formal);
|
|
||||||
|
delete formals;
|
||||||
|
return new Formals(std::move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<std::pair<Pos, Expr *> > & es)
|
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
|
||||||
|
vector<std::pair<Pos, std::variant<Expr *, StringToken> > > & es)
|
||||||
{
|
{
|
||||||
if (es.empty()) return new ExprString(symbols.create(""));
|
if (es.empty()) return new ExprString("");
|
||||||
|
|
||||||
/* Figure out the minimum indentation. Note that by design
|
/* Figure out the minimum indentation. Note that by design
|
||||||
whitespace-only final lines are not taken into account. (So
|
whitespace-only final lines are not taken into account. (So
|
||||||
|
@ -163,20 +204,20 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
|
||||||
size_t minIndent = 1000000;
|
size_t minIndent = 1000000;
|
||||||
size_t curIndent = 0;
|
size_t curIndent = 0;
|
||||||
for (auto & [i_pos, i] : es) {
|
for (auto & [i_pos, i] : es) {
|
||||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
|
auto * str = std::get_if<StringToken>(&i);
|
||||||
if (!e) {
|
if (!str || !str->hasIndentation) {
|
||||||
/* Anti-quotations end the current start-of-line whitespace. */
|
/* Anti-quotations and escaped characters end the current start-of-line whitespace. */
|
||||||
if (atStartOfLine) {
|
if (atStartOfLine) {
|
||||||
atStartOfLine = false;
|
atStartOfLine = false;
|
||||||
if (curIndent < minIndent) minIndent = curIndent;
|
if (curIndent < minIndent) minIndent = curIndent;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (size_t j = 0; j < e->s.size(); ++j) {
|
for (size_t j = 0; j < str->l; ++j) {
|
||||||
if (atStartOfLine) {
|
if (atStartOfLine) {
|
||||||
if (e->s[j] == ' ')
|
if (str->p[j] == ' ')
|
||||||
curIndent++;
|
curIndent++;
|
||||||
else if (e->s[j] == '\n') {
|
else if (str->p[j] == '\n') {
|
||||||
/* Empty line, doesn't influence minimum
|
/* Empty line, doesn't influence minimum
|
||||||
indentation. */
|
indentation. */
|
||||||
curIndent = 0;
|
curIndent = 0;
|
||||||
|
@ -184,7 +225,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
|
||||||
atStartOfLine = false;
|
atStartOfLine = false;
|
||||||
if (curIndent < minIndent) minIndent = curIndent;
|
if (curIndent < minIndent) minIndent = curIndent;
|
||||||
}
|
}
|
||||||
} else if (e->s[j] == '\n') {
|
} else if (str->p[j] == '\n') {
|
||||||
atStartOfLine = true;
|
atStartOfLine = true;
|
||||||
curIndent = 0;
|
curIndent = 0;
|
||||||
}
|
}
|
||||||
|
@ -196,33 +237,31 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
|
||||||
atStartOfLine = true;
|
atStartOfLine = true;
|
||||||
size_t curDropped = 0;
|
size_t curDropped = 0;
|
||||||
size_t n = es.size();
|
size_t n = es.size();
|
||||||
for (vector<std::pair<Pos, Expr *> >::iterator i = es.begin(); i != es.end(); ++i, --n) {
|
auto i = es.begin();
|
||||||
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i->second);
|
const auto trimExpr = [&] (Expr * e) {
|
||||||
if (!e) {
|
atStartOfLine = false;
|
||||||
atStartOfLine = false;
|
curDropped = 0;
|
||||||
curDropped = 0;
|
es2->emplace_back(i->first, e);
|
||||||
es2->push_back(*i);
|
};
|
||||||
continue;
|
const auto trimString = [&] (const StringToken & t) {
|
||||||
}
|
|
||||||
|
|
||||||
string s2;
|
string s2;
|
||||||
for (size_t j = 0; j < e->s.size(); ++j) {
|
for (size_t j = 0; j < t.l; ++j) {
|
||||||
if (atStartOfLine) {
|
if (atStartOfLine) {
|
||||||
if (e->s[j] == ' ') {
|
if (t.p[j] == ' ') {
|
||||||
if (curDropped++ >= minIndent)
|
if (curDropped++ >= minIndent)
|
||||||
s2 += e->s[j];
|
s2 += t.p[j];
|
||||||
}
|
}
|
||||||
else if (e->s[j] == '\n') {
|
else if (t.p[j] == '\n') {
|
||||||
curDropped = 0;
|
curDropped = 0;
|
||||||
s2 += e->s[j];
|
s2 += t.p[j];
|
||||||
} else {
|
} else {
|
||||||
atStartOfLine = false;
|
atStartOfLine = false;
|
||||||
curDropped = 0;
|
curDropped = 0;
|
||||||
s2 += e->s[j];
|
s2 += t.p[j];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s2 += e->s[j];
|
s2 += t.p[j];
|
||||||
if (e->s[j] == '\n') atStartOfLine = true;
|
if (t.p[j] == '\n') atStartOfLine = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +273,10 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<st
|
||||||
s2 = string(s2, 0, p + 1);
|
s2 = string(s2, 0, p + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
es2->emplace_back(i->first, new ExprString(symbols.create(s2)));
|
es2->emplace_back(i->first, new ExprString(s2));
|
||||||
|
};
|
||||||
|
for (; i != es.end(); ++i, --n) {
|
||||||
|
std::visit(overloaded { trimExpr, trimString }, i->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this is a single string, then don't do a concatenation. */
|
/* If this is a single string, then don't do a concatenation. */
|
||||||
|
@ -269,22 +311,17 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
nix::Expr * e;
|
nix::Expr * e;
|
||||||
nix::ExprList * list;
|
nix::ExprList * list;
|
||||||
nix::ExprAttrs * attrs;
|
nix::ExprAttrs * attrs;
|
||||||
nix::Formals * formals;
|
nix::ParserFormals * formals;
|
||||||
nix::Formal * formal;
|
nix::Formal * formal;
|
||||||
nix::NixInt n;
|
nix::NixInt n;
|
||||||
nix::NixFloat nf;
|
nix::NixFloat nf;
|
||||||
// using C a struct allows us to avoid having to define the special
|
|
||||||
// members that using string_view here would implicitly delete.
|
|
||||||
struct StringToken {
|
|
||||||
const char * p;
|
|
||||||
size_t l;
|
|
||||||
operator std::string_view() const { return {p, l}; }
|
|
||||||
};
|
|
||||||
StringToken id; // !!! -> Symbol
|
StringToken id; // !!! -> Symbol
|
||||||
StringToken path;
|
StringToken path;
|
||||||
StringToken uri;
|
StringToken uri;
|
||||||
|
StringToken str;
|
||||||
std::vector<nix::AttrName> * attrNames;
|
std::vector<nix::AttrName> * attrNames;
|
||||||
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
|
std::vector<std::pair<nix::Pos, nix::Expr *> > * string_parts;
|
||||||
|
std::vector<std::pair<nix::Pos, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
%type <e> start expr expr_function expr_if expr_op
|
%type <e> start expr expr_function expr_if expr_op
|
||||||
|
@ -294,11 +331,12 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
%type <formals> formals
|
%type <formals> formals
|
||||||
%type <formal> formal
|
%type <formal> formal
|
||||||
%type <attrNames> attrs attrpath
|
%type <attrNames> attrs attrpath
|
||||||
%type <string_parts> string_parts_interpolated ind_string_parts
|
%type <string_parts> string_parts_interpolated
|
||||||
|
%type <ind_string_parts> ind_string_parts
|
||||||
%type <e> path_start string_parts string_attr
|
%type <e> path_start string_parts string_attr
|
||||||
%type <id> attr
|
%type <id> attr
|
||||||
%token <id> ID ATTRPATH
|
%token <id> ID ATTRPATH
|
||||||
%token <e> STR IND_STR
|
%token <str> STR IND_STR
|
||||||
%token <n> INT
|
%token <n> INT
|
||||||
%token <nf> FLOAT
|
%token <nf> FLOAT
|
||||||
%token <path> PATH HPATH SPATH PATH_END
|
%token <path> PATH HPATH SPATH PATH_END
|
||||||
|
@ -331,11 +369,17 @@ expr_function
|
||||||
: ID ':' expr_function
|
: ID ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
|
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), 0, $3); }
|
||||||
| '{' formals '}' ':' expr_function
|
| '{' formals '}' ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), $2, $5); }
|
{ $$ = new ExprLambda(CUR_POS, data->symbols.create(""), toFormals(*data, $2), $5); }
|
||||||
| '{' formals '}' '@' ID ':' expr_function
|
| '{' formals '}' '@' ID ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($5), $2, $7); }
|
{
|
||||||
|
Symbol arg = data->symbols.create($5);
|
||||||
|
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
|
||||||
|
}
|
||||||
| ID '@' '{' formals '}' ':' expr_function
|
| ID '@' '{' formals '}' ':' expr_function
|
||||||
{ $$ = new ExprLambda(CUR_POS, data->symbols.create($1), $4, $7); }
|
{
|
||||||
|
Symbol arg = data->symbols.create($1);
|
||||||
|
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
|
||||||
|
}
|
||||||
| ASSERT expr ';' expr_function
|
| ASSERT expr ';' expr_function
|
||||||
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
{ $$ = new ExprAssert(CUR_POS, $2, $4); }
|
||||||
| WITH expr ';' expr_function
|
| WITH expr ';' expr_function
|
||||||
|
@ -426,7 +470,7 @@ expr_simple
|
||||||
$$ = new ExprCall(CUR_POS,
|
$$ = new ExprCall(CUR_POS,
|
||||||
new ExprVar(data->symbols.create("__findFile")),
|
new ExprVar(data->symbols.create("__findFile")),
|
||||||
{new ExprVar(data->symbols.create("__nixPath")),
|
{new ExprVar(data->symbols.create("__nixPath")),
|
||||||
new ExprString(data->symbols.create(path))});
|
new ExprString(path)});
|
||||||
}
|
}
|
||||||
| URI {
|
| URI {
|
||||||
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
|
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
|
||||||
|
@ -435,7 +479,7 @@ expr_simple
|
||||||
.msg = hintfmt("URL literals are disabled"),
|
.msg = hintfmt("URL literals are disabled"),
|
||||||
.errPos = CUR_POS
|
.errPos = CUR_POS
|
||||||
});
|
});
|
||||||
$$ = new ExprString(data->symbols.create($1));
|
$$ = new ExprString(string($1));
|
||||||
}
|
}
|
||||||
| '(' expr ')' { $$ = $2; }
|
| '(' expr ')' { $$ = $2; }
|
||||||
/* Let expressions `let {..., body = ...}' are just desugared
|
/* Let expressions `let {..., body = ...}' are just desugared
|
||||||
|
@ -450,18 +494,19 @@ expr_simple
|
||||||
;
|
;
|
||||||
|
|
||||||
string_parts
|
string_parts
|
||||||
: STR
|
: STR { $$ = new ExprString(string($1)); }
|
||||||
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
|
| string_parts_interpolated { $$ = new ExprConcatStrings(CUR_POS, true, $1); }
|
||||||
| { $$ = new ExprString(data->symbols.create("")); }
|
| { $$ = new ExprString(""); }
|
||||||
;
|
;
|
||||||
|
|
||||||
string_parts_interpolated
|
string_parts_interpolated
|
||||||
: string_parts_interpolated STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
|
: string_parts_interpolated STR
|
||||||
|
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(string($2))); }
|
||||||
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
| string_parts_interpolated DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
||||||
| DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
|
| DOLLAR_CURLY expr '}' { $$ = new vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
|
||||||
| STR DOLLAR_CURLY expr '}' {
|
| STR DOLLAR_CURLY expr '}' {
|
||||||
$$ = new vector<std::pair<Pos, Expr *> >;
|
$$ = new vector<std::pair<Pos, Expr *> >;
|
||||||
$$->emplace_back(makeCurPos(@1, data), $1);
|
$$->emplace_back(makeCurPos(@1, data), new ExprString(string($1)));
|
||||||
$$->emplace_back(makeCurPos(@2, data), $3);
|
$$->emplace_back(makeCurPos(@2, data), $3);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -483,7 +528,7 @@ path_start
|
||||||
ind_string_parts
|
ind_string_parts
|
||||||
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
|
: ind_string_parts IND_STR { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $2); }
|
||||||
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
| ind_string_parts DOLLAR_CURLY expr '}' { $$ = $1; $1->emplace_back(makeCurPos(@2, data), $3); }
|
||||||
| { $$ = new vector<std::pair<Pos, Expr *> >; }
|
| { $$ = new vector<std::pair<Pos, std::variant<Expr *, StringToken> > >; }
|
||||||
;
|
;
|
||||||
|
|
||||||
binds
|
binds
|
||||||
|
@ -515,7 +560,7 @@ attrs
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
ExprString * str = dynamic_cast<ExprString *>($2);
|
ExprString * str = dynamic_cast<ExprString *>($2);
|
||||||
if (str) {
|
if (str) {
|
||||||
$$->push_back(AttrName(str->s));
|
$$->push_back(AttrName(data->symbols.create(str->s)));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
|
@ -532,7 +577,7 @@ attrpath
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
ExprString * str = dynamic_cast<ExprString *>($3);
|
ExprString * str = dynamic_cast<ExprString *>($3);
|
||||||
if (str) {
|
if (str) {
|
||||||
$$->push_back(AttrName(str->s));
|
$$->push_back(AttrName(data->symbols.create(str->s)));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
$$->push_back(AttrName($3));
|
$$->push_back(AttrName($3));
|
||||||
|
@ -542,7 +587,7 @@ attrpath
|
||||||
{ $$ = new vector<AttrName>;
|
{ $$ = new vector<AttrName>;
|
||||||
ExprString *str = dynamic_cast<ExprString *>($1);
|
ExprString *str = dynamic_cast<ExprString *>($1);
|
||||||
if (str) {
|
if (str) {
|
||||||
$$->push_back(AttrName(str->s));
|
$$->push_back(AttrName(data->symbols.create(str->s)));
|
||||||
delete str;
|
delete str;
|
||||||
} else
|
} else
|
||||||
$$->push_back(AttrName($1));
|
$$->push_back(AttrName($1));
|
||||||
|
@ -566,13 +611,13 @@ expr_list
|
||||||
|
|
||||||
formals
|
formals
|
||||||
: formal ',' formals
|
: formal ',' formals
|
||||||
{ $$ = $3; addFormal(CUR_POS, $$, *$1); }
|
{ $$ = $3; $$->formals.push_back(*$1); }
|
||||||
| formal
|
| formal
|
||||||
{ $$ = new Formals; addFormal(CUR_POS, $$, *$1); $$->ellipsis = false; }
|
{ $$ = new ParserFormals; $$->formals.push_back(*$1); $$->ellipsis = false; }
|
||||||
|
|
|
|
||||||
{ $$ = new Formals; $$->ellipsis = false; }
|
{ $$ = new ParserFormals; $$->ellipsis = false; }
|
||||||
| ELLIPSIS
|
| ELLIPSIS
|
||||||
{ $$ = new Formals; $$->ellipsis = true; }
|
{ $$ = new ParserFormals; $$->ellipsis = true; }
|
||||||
;
|
;
|
||||||
|
|
||||||
formal
|
formal
|
||||||
|
@ -598,7 +643,7 @@ namespace nix {
|
||||||
|
|
||||||
|
|
||||||
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
||||||
const Path & path, const Path & basePath, StaticEnv & staticEnv)
|
const PathView path, const PathView basePath, StaticEnv & staticEnv)
|
||||||
{
|
{
|
||||||
yyscan_t scanner;
|
yyscan_t scanner;
|
||||||
ParseData data(*this);
|
ParseData data(*this);
|
||||||
|
@ -709,24 +754,24 @@ void EvalState::addToSearchPath(const string & s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path EvalState::findFile(const string & path)
|
Path EvalState::findFile(const std::string_view path)
|
||||||
{
|
{
|
||||||
return findFile(searchPath, path);
|
return findFile(searchPath, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos & pos)
|
Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
|
||||||
{
|
{
|
||||||
for (auto & i : searchPath) {
|
for (auto & i : searchPath) {
|
||||||
std::string suffix;
|
std::string suffix;
|
||||||
if (i.first.empty())
|
if (i.first.empty())
|
||||||
suffix = "/" + path;
|
suffix = concatStrings("/", path);
|
||||||
else {
|
else {
|
||||||
auto s = i.first.size();
|
auto s = i.first.size();
|
||||||
if (path.compare(0, s, i.first) != 0 ||
|
if (path.compare(0, s, i.first) != 0 ||
|
||||||
(path.size() > s && path[s] != '/'))
|
(path.size() > s && path[s] != '/'))
|
||||||
continue;
|
continue;
|
||||||
suffix = path.size() == s ? "" : "/" + string(path, s);
|
suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
|
||||||
}
|
}
|
||||||
auto r = resolveSearchPathElem(i);
|
auto r = resolveSearchPathElem(i);
|
||||||
if (!r.first) continue;
|
if (!r.first) continue;
|
||||||
|
@ -735,7 +780,7 @@ Path EvalState::findFile(SearchPath & searchPath, const string & path, const Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPrefix(path, "nix/"))
|
if (hasPrefix(path, "nix/"))
|
||||||
return corepkgsPrefix + path.substr(4);
|
return concatStrings(corepkgsPrefix, path.substr(4));
|
||||||
|
|
||||||
throw ThrownError({
|
throw ThrownError({
|
||||||
.msg = hintfmt(evalSettings.pureEval
|
.msg = hintfmt(evalSettings.pureEval
|
||||||
|
|
|
@ -92,8 +92,6 @@ StringMap EvalState::realiseContext(const PathSet & context)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RealisePathFlags {
|
struct RealisePathFlags {
|
||||||
// Whether to check whether the path is a valid absolute path
|
|
||||||
bool requireAbsolutePath = true;
|
|
||||||
// Whether to check that the path is allowed in pure eval mode
|
// Whether to check that the path is allowed in pure eval mode
|
||||||
bool checkForPureEval = true;
|
bool checkForPureEval = true;
|
||||||
};
|
};
|
||||||
|
@ -105,9 +103,7 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea
|
||||||
auto path = [&]()
|
auto path = [&]()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return flags.requireAbsolutePath
|
return state.coerceToPath(pos, v, context);
|
||||||
? state.coerceToPath(pos, v, context)
|
|
||||||
: state.coerceToString(pos, v, context, false, false);
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, "while realising the context of a path");
|
e.addTrace(pos, "while realising the context of a path");
|
||||||
throw;
|
throw;
|
||||||
|
@ -214,7 +210,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
|
||||||
if (!vScope)
|
if (!vScope)
|
||||||
state.evalFile(path, v);
|
state.evalFile(path, v);
|
||||||
else {
|
else {
|
||||||
state.forceAttrs(*vScope);
|
state.forceAttrs(*vScope, pos);
|
||||||
|
|
||||||
Env * env = &state.allocEnv(vScope->attrs->size());
|
Env * env = &state.allocEnv(vScope->attrs->size());
|
||||||
env->up = &state.baseEnv;
|
env->up = &state.baseEnv;
|
||||||
|
@ -318,7 +314,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
|
||||||
{
|
{
|
||||||
auto path = realisePath(state, pos, *args[0]);
|
auto path = realisePath(state, pos, *args[0]);
|
||||||
|
|
||||||
string sym = state.forceStringNoCtx(*args[1], pos);
|
string sym(state.forceStringNoCtx(*args[1], pos));
|
||||||
|
|
||||||
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||||
if (!handle)
|
if (!handle)
|
||||||
|
@ -354,10 +350,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto program = state.coerceToString(pos, *elems[0], context, false, false);
|
auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned();
|
||||||
Strings commandArgs;
|
Strings commandArgs;
|
||||||
for (unsigned int i = 1; i < args[0]->listSize(); ++i)
|
for (unsigned int i = 1; i < args[0]->listSize(); ++i) {
|
||||||
commandArgs.emplace_back(state.coerceToString(pos, *elems[i], context, false, false));
|
commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned());
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
|
auto _ = state.realiseContext(context); // FIXME: Handle CA derivations
|
||||||
} catch (InvalidPathError & e) {
|
} catch (InvalidPathError & e) {
|
||||||
|
@ -710,7 +707,7 @@ static RegisterPrimOp primop_abort({
|
||||||
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
|
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string s = state.coerceToString(pos, *args[0], context);
|
string s = state.coerceToString(pos, *args[0], context).toOwned();
|
||||||
throw Abort("evaluation aborted with the following error message: '%1%'", s);
|
throw Abort("evaluation aborted with the following error message: '%1%'", s);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -728,7 +725,7 @@ static RegisterPrimOp primop_throw({
|
||||||
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
|
.fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string s = state.coerceToString(pos, *args[0], context);
|
string s = state.coerceToString(pos, *args[0], context).toOwned();
|
||||||
throw ThrownError(s);
|
throw ThrownError(s);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -740,7 +737,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a
|
||||||
v = *args[1];
|
v = *args[1];
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context));
|
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -829,7 +826,7 @@ static RegisterPrimOp primop_tryEval({
|
||||||
/* Return an environment variable. Use with care. */
|
/* Return an environment variable. Use with care. */
|
||||||
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string name = state.forceStringNoCtx(*args[0], pos);
|
string name(state.forceStringNoCtx(*args[0], pos));
|
||||||
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
|
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -979,7 +976,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
const string & key = i->name;
|
const string & key = i->name;
|
||||||
vomit("processing attribute '%1%'", key);
|
vomit("processing attribute '%1%'", key);
|
||||||
|
|
||||||
auto handleHashMode = [&](const std::string & s) {
|
auto handleHashMode = [&](const std::string_view s) {
|
||||||
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
|
if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive;
|
||||||
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
|
else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat;
|
||||||
else
|
else
|
||||||
|
@ -1034,7 +1031,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
else if (i->name == state.sArgs) {
|
else if (i->name == state.sArgs) {
|
||||||
state.forceList(*i->value, pos);
|
state.forceList(*i->value, pos);
|
||||||
for (auto elem : i->value->listItems()) {
|
for (auto elem : i->value->listItems()) {
|
||||||
string s = state.coerceToString(posDrvName, *elem, context, true);
|
string s = state.coerceToString(posDrvName, *elem, context, true).toOwned();
|
||||||
drv.args.push_back(s);
|
drv.args.push_back(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1070,7 +1067,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auto s = state.coerceToString(*i->pos, *i->value, context, true);
|
auto s = state.coerceToString(*i->pos, *i->value, context, true).toOwned();
|
||||||
drv.env.emplace(key, s);
|
drv.env.emplace(key, s);
|
||||||
if (i->name == state.sBuilder) drv.builder = std::move(s);
|
if (i->name == state.sBuilder) drv.builder = std::move(s);
|
||||||
else if (i->name == state.sSystem) drv.platform = std::move(s);
|
else if (i->name == state.sSystem) drv.platform = std::move(s);
|
||||||
|
@ -1183,7 +1180,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
|
||||||
drv.outputs.insert_or_assign(i, DerivationOutput {
|
drv.outputs.insert_or_assign(i, DerivationOutput {
|
||||||
.output = DerivationOutputCAFloating {
|
.output = DerivationOutputCAFloating {
|
||||||
.method = ingestionMethod,
|
.method = ingestionMethod,
|
||||||
.hashType = std::move(ht),
|
.hashType = ht,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1273,7 +1270,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info {
|
||||||
substituted by the corresponding output path at build time. For
|
substituted by the corresponding output path at build time. For
|
||||||
example, 'placeholder "out"' returns the string
|
example, 'placeholder "out"' returns the string
|
||||||
/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build
|
/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9. At build
|
||||||
time, any occurence of this string in an derivation attribute will
|
time, any occurrence of this string in an derivation attribute will
|
||||||
be replaced with the concrete path in the Nix store of the output
|
be replaced with the concrete path in the Nix store of the output
|
||||||
‘out’. */
|
‘out’. */
|
||||||
static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
|
@ -1403,7 +1400,7 @@ static RegisterPrimOp primop_pathExists({
|
||||||
static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
v.mkString(baseNameOf(state.coerceToString(pos, *args[0], context, false, false)), context);
|
v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_baseNameOf({
|
static RegisterPrimOp primop_baseNameOf({
|
||||||
|
@ -1423,7 +1420,8 @@ static RegisterPrimOp primop_baseNameOf({
|
||||||
static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
Path dir = dirOf(state.coerceToString(pos, *args[0], context, false, false));
|
auto path = state.coerceToString(pos, *args[0], context, false, false);
|
||||||
|
auto dir = dirOf(*path);
|
||||||
if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context);
|
if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1489,12 +1487,24 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
|
||||||
auto path = realisePath(state, pos, *i->value, { .requireAbsolutePath = false });
|
PathSet context;
|
||||||
|
string path = state.coerceToString(pos, *i->value, context, false, false).toOwned();
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto rewrites = state.realiseContext(context);
|
||||||
|
path = rewriteStrings(path, rewrites);
|
||||||
|
} catch (InvalidPathError & e) {
|
||||||
|
throw EvalError({
|
||||||
|
.msg = hintfmt("cannot find '%1%', since path '%2%' is not valid", path, e.path),
|
||||||
|
.errPos = pos
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
searchPath.emplace_back(prefix, path);
|
searchPath.emplace_back(prefix, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
string path = state.forceStringNoCtx(*args[1], pos);
|
auto path = state.forceStringNoCtx(*args[1], pos);
|
||||||
|
|
||||||
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
|
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
|
||||||
}
|
}
|
||||||
|
@ -1508,7 +1518,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info {
|
||||||
/* Return the cryptographic hash of a file in base-16. */
|
/* Return the cryptographic hash of a file in base-16. */
|
||||||
static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string type = state.forceStringNoCtx(*args[0], pos);
|
auto type = state.forceStringNoCtx(*args[0], pos);
|
||||||
std::optional<HashType> ht = parseHashType(type);
|
std::optional<HashType> ht = parseHashType(type);
|
||||||
if (!ht)
|
if (!ht)
|
||||||
throw Error({
|
throw Error({
|
||||||
|
@ -1715,7 +1725,7 @@ static RegisterPrimOp primop_toJSON({
|
||||||
/* Parse a JSON string to a value. */
|
/* Parse a JSON string to a value. */
|
||||||
static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string s = state.forceStringNoCtx(*args[0], pos);
|
auto s = state.forceStringNoCtx(*args[0], pos);
|
||||||
try {
|
try {
|
||||||
parseJSON(state, s, v);
|
parseJSON(state, s, v);
|
||||||
} catch (JSONParseError &e) {
|
} catch (JSONParseError &e) {
|
||||||
|
@ -1744,8 +1754,8 @@ static RegisterPrimOp primop_fromJSON({
|
||||||
static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string name = state.forceStringNoCtx(*args[0], pos);
|
string name(state.forceStringNoCtx(*args[0], pos));
|
||||||
string contents = state.forceString(*args[1], context, pos);
|
string contents(state.forceString(*args[1], context, pos));
|
||||||
|
|
||||||
StorePathSet refs;
|
StorePathSet refs;
|
||||||
|
|
||||||
|
@ -2145,7 +2155,7 @@ static RegisterPrimOp primop_attrValues({
|
||||||
/* Dynamic version of the `.' operator. */
|
/* Dynamic version of the `.' operator. */
|
||||||
void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string attr = state.forceStringNoCtx(*args[0], pos);
|
auto attr = state.forceStringNoCtx(*args[0], pos);
|
||||||
state.forceAttrs(*args[1], pos);
|
state.forceAttrs(*args[1], pos);
|
||||||
Bindings::iterator i = getAttr(
|
Bindings::iterator i = getAttr(
|
||||||
state,
|
state,
|
||||||
|
@ -2175,7 +2185,7 @@ static RegisterPrimOp primop_getAttr({
|
||||||
/* Return position information of the specified attribute. */
|
/* Return position information of the specified attribute. */
|
||||||
static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string attr = state.forceStringNoCtx(*args[0], pos);
|
auto attr = state.forceStringNoCtx(*args[0], pos);
|
||||||
state.forceAttrs(*args[1], pos);
|
state.forceAttrs(*args[1], pos);
|
||||||
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
|
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
|
||||||
if (i == args[1]->attrs->end())
|
if (i == args[1]->attrs->end())
|
||||||
|
@ -2193,7 +2203,7 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
|
||||||
/* Dynamic version of the `?' operator. */
|
/* Dynamic version of the `?' operator. */
|
||||||
static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string attr = state.forceStringNoCtx(*args[0], pos);
|
auto attr = state.forceStringNoCtx(*args[0], pos);
|
||||||
state.forceAttrs(*args[1], pos);
|
state.forceAttrs(*args[1], pos);
|
||||||
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
|
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
|
||||||
}
|
}
|
||||||
|
@ -2271,7 +2281,7 @@ static RegisterPrimOp primop_removeAttrs({
|
||||||
/* Builds a set from a list specifying (name, value) pairs. To be
|
/* Builds a set from a list specifying (name, value) pairs. To be
|
||||||
precise, a list [{name = "name1"; value = value1;} ... {name =
|
precise, a list [{name = "name1"; value = value1;} ... {name =
|
||||||
"nameN"; value = valueN;}] is transformed to {name1 = value1;
|
"nameN"; value = valueN;}] is transformed to {name1 = value1;
|
||||||
... nameN = valueN;}. In case of duplicate occurences of the same
|
... nameN = valueN;}. In case of duplicate occurrences of the same
|
||||||
name, the first takes precedence. */
|
name, the first takes precedence. */
|
||||||
static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
|
@ -2292,7 +2302,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
|
|
||||||
string name = state.forceStringNoCtx(*j->value, *j->pos);
|
auto name = state.forceStringNoCtx(*j->value, *j->pos);
|
||||||
|
|
||||||
Symbol sym = state.symbols.create(name);
|
Symbol sym = state.symbols.create(name);
|
||||||
if (seen.insert(sym).second) {
|
if (seen.insert(sym).second) {
|
||||||
|
@ -2492,7 +2502,7 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args
|
||||||
for (unsigned int n = 0; n < listSize; ++n) {
|
for (unsigned int n = 0; n < listSize; ++n) {
|
||||||
Value * vElem = listElems[n];
|
Value * vElem = listElems[n];
|
||||||
try {
|
try {
|
||||||
state.forceAttrs(*vElem);
|
state.forceAttrs(*vElem, noPos);
|
||||||
for (auto & attr : *vElem->attrs)
|
for (auto & attr : *vElem->attrs)
|
||||||
attrsSeen[attr.name].first++;
|
attrsSeen[attr.name].first++;
|
||||||
} catch (TypeError & e) {
|
} catch (TypeError & e) {
|
||||||
|
@ -3024,7 +3034,7 @@ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Val
|
||||||
for (auto vElem : args[1]->listItems()) {
|
for (auto vElem : args[1]->listItems()) {
|
||||||
Value res;
|
Value res;
|
||||||
state.callFunction(*args[0], *vElem, res, pos);
|
state.callFunction(*args[0], *vElem, res, pos);
|
||||||
string name = state.forceStringNoCtx(res, pos);
|
auto name = state.forceStringNoCtx(res, pos);
|
||||||
Symbol sym = state.symbols.create(name);
|
Symbol sym = state.symbols.create(name);
|
||||||
auto vector = attrs.try_emplace(sym, ValueVector()).first;
|
auto vector = attrs.try_emplace(sym, ValueVector()).first;
|
||||||
vector->second.push_back(vElem);
|
vector->second.push_back(vElem);
|
||||||
|
@ -3280,8 +3290,8 @@ static RegisterPrimOp primop_lessThan({
|
||||||
static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string s = state.coerceToString(pos, *args[0], context, true, false);
|
auto s = state.coerceToString(pos, *args[0], context, true, false);
|
||||||
v.mkString(s, context);
|
v.mkString(*s, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_toString({
|
static RegisterPrimOp primop_toString({
|
||||||
|
@ -3317,7 +3327,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
|
||||||
int start = state.forceInt(*args[0], pos);
|
int start = state.forceInt(*args[0], pos);
|
||||||
int len = state.forceInt(*args[1], pos);
|
int len = state.forceInt(*args[1], pos);
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string s = state.coerceToString(pos, *args[2], context);
|
auto s = state.coerceToString(pos, *args[2], context);
|
||||||
|
|
||||||
if (start < 0)
|
if (start < 0)
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
|
@ -3325,7 +3335,7 @@ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, V
|
||||||
.errPos = pos
|
.errPos = pos
|
||||||
});
|
});
|
||||||
|
|
||||||
v.mkString((unsigned int) start >= s.size() ? "" : string(s, start, len), context);
|
v.mkString((unsigned int) start >= s->size() ? "" : s->substr(start, len), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_substring({
|
static RegisterPrimOp primop_substring({
|
||||||
|
@ -3351,8 +3361,8 @@ static RegisterPrimOp primop_substring({
|
||||||
static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string s = state.coerceToString(pos, *args[0], context);
|
auto s = state.coerceToString(pos, *args[0], context);
|
||||||
v.mkInt(s.size());
|
v.mkInt(s->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_stringLength({
|
static RegisterPrimOp primop_stringLength({
|
||||||
|
@ -3368,7 +3378,7 @@ static RegisterPrimOp primop_stringLength({
|
||||||
/* Return the cryptographic hash of a string in base-16. */
|
/* Return the cryptographic hash of a string in base-16. */
|
||||||
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string type = state.forceStringNoCtx(*args[0], pos);
|
auto type = state.forceStringNoCtx(*args[0], pos);
|
||||||
std::optional<HashType> ht = parseHashType(type);
|
std::optional<HashType> ht = parseHashType(type);
|
||||||
if (!ht)
|
if (!ht)
|
||||||
throw Error({
|
throw Error({
|
||||||
|
@ -3377,7 +3387,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
|
||||||
});
|
});
|
||||||
|
|
||||||
PathSet context; // discarded
|
PathSet context; // discarded
|
||||||
string s = state.forceString(*args[1], context, pos);
|
auto s = state.forceString(*args[1], context, pos);
|
||||||
|
|
||||||
v.mkString(hashString(*ht, s).to_string(Base16, false));
|
v.mkString(hashString(*ht, s).to_string(Base16, false));
|
||||||
}
|
}
|
||||||
|
@ -3395,7 +3405,18 @@ static RegisterPrimOp primop_hashString({
|
||||||
|
|
||||||
struct RegexCache
|
struct RegexCache
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, std::regex> cache;
|
// TODO use C++20 transparent comparison when available
|
||||||
|
std::unordered_map<std::string_view, std::regex> cache;
|
||||||
|
std::list<std::string> keys;
|
||||||
|
|
||||||
|
std::regex get(std::string_view re)
|
||||||
|
{
|
||||||
|
auto it = cache.find(re);
|
||||||
|
if (it != cache.end())
|
||||||
|
return it->second;
|
||||||
|
keys.emplace_back(re);
|
||||||
|
return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<RegexCache> makeRegexCache()
|
std::shared_ptr<RegexCache> makeRegexCache()
|
||||||
|
@ -3409,15 +3430,13 @@ void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
auto regex = state.regexCache->cache.find(re);
|
auto regex = state.regexCache->get(re);
|
||||||
if (regex == state.regexCache->cache.end())
|
|
||||||
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
|
|
||||||
|
|
||||||
PathSet context;
|
PathSet context;
|
||||||
const std::string str = state.forceString(*args[1], context, pos);
|
const auto str = state.forceString(*args[1], context, pos);
|
||||||
|
|
||||||
std::smatch match;
|
std::cmatch match;
|
||||||
if (!std::regex_match(str, match, regex->second)) {
|
if (!std::regex_match(str.begin(), str.end(), match, regex)) {
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3492,15 +3511,13 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
auto regex = state.regexCache->cache.find(re);
|
auto regex = state.regexCache->get(re);
|
||||||
if (regex == state.regexCache->cache.end())
|
|
||||||
regex = state.regexCache->cache.emplace(re, std::regex(re, std::regex::extended)).first;
|
|
||||||
|
|
||||||
PathSet context;
|
PathSet context;
|
||||||
const std::string str = state.forceString(*args[1], context, pos);
|
const auto str = state.forceString(*args[1], context, pos);
|
||||||
|
|
||||||
auto begin = std::sregex_iterator(str.begin(), str.end(), regex->second);
|
auto begin = std::cregex_iterator(str.begin(), str.end(), regex);
|
||||||
auto end = std::sregex_iterator();
|
auto end = std::cregex_iterator();
|
||||||
|
|
||||||
// Any matches results are surrounded by non-matching results.
|
// Any matches results are surrounded by non-matching results.
|
||||||
const size_t len = std::distance(begin, end);
|
const size_t len = std::distance(begin, end);
|
||||||
|
@ -3512,9 +3529,9 @@ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::sregex_iterator i = begin; i != end; ++i) {
|
for (auto i = begin; i != end; ++i) {
|
||||||
assert(idx <= 2 * len + 1 - 3);
|
assert(idx <= 2 * len + 1 - 3);
|
||||||
std::smatch match = *i;
|
auto match = *i;
|
||||||
|
|
||||||
// Add a string for non-matched characters.
|
// Add a string for non-matched characters.
|
||||||
(v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str());
|
(v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str());
|
||||||
|
@ -3605,7 +3622,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * *
|
||||||
|
|
||||||
for (auto elem : args[1]->listItems()) {
|
for (auto elem : args[1]->listItems()) {
|
||||||
if (first) first = false; else res += sep;
|
if (first) first = false; else res += sep;
|
||||||
res += state.coerceToString(pos, *elem, context);
|
res += *state.coerceToString(pos, *elem, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
v.mkString(res, context);
|
v.mkString(res, context);
|
||||||
|
@ -3635,14 +3652,14 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
|
||||||
vector<string> from;
|
vector<string> from;
|
||||||
from.reserve(args[0]->listSize());
|
from.reserve(args[0]->listSize());
|
||||||
for (auto elem : args[0]->listItems())
|
for (auto elem : args[0]->listItems())
|
||||||
from.push_back(state.forceString(*elem, pos));
|
from.emplace_back(state.forceString(*elem, pos));
|
||||||
|
|
||||||
vector<std::pair<string, PathSet>> to;
|
vector<std::pair<string, PathSet>> to;
|
||||||
to.reserve(args[1]->listSize());
|
to.reserve(args[1]->listSize());
|
||||||
for (auto elem : args[1]->listItems()) {
|
for (auto elem : args[1]->listItems()) {
|
||||||
PathSet ctx;
|
PathSet ctx;
|
||||||
auto s = state.forceString(*elem, ctx, pos);
|
auto s = state.forceString(*elem, ctx, pos);
|
||||||
to.push_back(std::make_pair(std::move(s), std::move(ctx)));
|
to.emplace_back(s, std::move(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
PathSet context;
|
PathSet context;
|
||||||
|
@ -3704,7 +3721,7 @@ static RegisterPrimOp primop_replaceStrings({
|
||||||
|
|
||||||
static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string name = state.forceStringNoCtx(*args[0], pos);
|
auto name = state.forceStringNoCtx(*args[0], pos);
|
||||||
DrvName parsed(name);
|
DrvName parsed(name);
|
||||||
auto attrs = state.buildBindings(2);
|
auto attrs = state.buildBindings(2);
|
||||||
attrs.alloc(state.sName).mkString(parsed.name);
|
attrs.alloc(state.sName).mkString(parsed.name);
|
||||||
|
@ -3728,8 +3745,8 @@ static RegisterPrimOp primop_parseDrvName({
|
||||||
|
|
||||||
static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string version1 = state.forceStringNoCtx(*args[0], pos);
|
auto version1 = state.forceStringNoCtx(*args[0], pos);
|
||||||
string version2 = state.forceStringNoCtx(*args[1], pos);
|
auto version2 = state.forceStringNoCtx(*args[1], pos);
|
||||||
v.mkInt(compareVersions(version1, version2));
|
v.mkInt(compareVersions(version1, version2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3748,14 +3765,14 @@ static RegisterPrimOp primop_compareVersions({
|
||||||
|
|
||||||
static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
string version = state.forceStringNoCtx(*args[0], pos);
|
auto version = state.forceStringNoCtx(*args[0], pos);
|
||||||
auto iter = version.cbegin();
|
auto iter = version.cbegin();
|
||||||
Strings components;
|
Strings components;
|
||||||
while (iter != version.cend()) {
|
while (iter != version.cend()) {
|
||||||
auto component = nextComponent(iter, version.cend());
|
auto component = nextComponent(iter, version.cend());
|
||||||
if (component.empty())
|
if (component.empty())
|
||||||
break;
|
break;
|
||||||
components.emplace_back(std::move(component));
|
components.emplace_back(component);
|
||||||
}
|
}
|
||||||
state.mkList(v, components.size());
|
state.mkList(v, components.size());
|
||||||
for (const auto & [n, component] : enumerate(components))
|
for (const auto & [n, component] : enumerate(components))
|
||||||
|
|
|
@ -7,7 +7,8 @@ namespace nix {
|
||||||
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
v.mkString(state.coerceToString(pos, *args[0], context));
|
auto s = state.coerceToString(pos, *args[0], context);
|
||||||
|
v.mkString(*s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||||
|
@ -32,13 +33,13 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
|
||||||
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
string s = state.coerceToString(pos, *args[0], context);
|
auto s = state.coerceToString(pos, *args[0], context);
|
||||||
|
|
||||||
PathSet context2;
|
PathSet context2;
|
||||||
for (auto & p : context)
|
for (auto & p : context)
|
||||||
context2.insert(p.at(0) == '=' ? string(p, 1) : p);
|
context2.insert(p.at(0) == '=' ? string(p, 1) : p);
|
||||||
|
|
||||||
v.mkString(s, context2);
|
v.mkString(*s, context2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
||||||
|
@ -180,7 +181,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||||
}
|
}
|
||||||
for (auto elem : iter->value->listItems()) {
|
for (auto elem : iter->value->listItems()) {
|
||||||
auto name = state.forceStringNoCtx(*elem, *iter->pos);
|
auto name = state.forceStringNoCtx(*elem, *iter->pos);
|
||||||
context.insert("!" + name + "!" + string(i.name));
|
context.insert(concatStrings("!", name, "!", i.name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
std::string url;
|
std::string url;
|
||||||
std::optional<Hash> rev;
|
std::optional<Hash> rev;
|
||||||
std::optional<std::string> ref;
|
std::optional<std::string> ref;
|
||||||
std::string name = "source";
|
std::string_view name = "source";
|
||||||
PathSet context;
|
PathSet context;
|
||||||
|
|
||||||
state.forceValue(*args[0], pos);
|
state.forceValue(*args[0], pos);
|
||||||
|
@ -22,14 +22,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos);
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
string n(attr.name);
|
std::string_view n(attr.name);
|
||||||
if (n == "url")
|
if (n == "url")
|
||||||
url = state.coerceToString(*attr.pos, *attr.value, context, false, false);
|
url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
|
||||||
else if (n == "rev") {
|
else if (n == "rev") {
|
||||||
// Ugly: unlike fetchGit, here the "rev" attribute can
|
// Ugly: unlike fetchGit, here the "rev" attribute can
|
||||||
// be both a revision or a branch/tag name.
|
// be both a revision or a branch/tag name.
|
||||||
auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
|
auto value = state.forceStringNoCtx(*attr.value, *attr.pos);
|
||||||
if (std::regex_match(value, revRegex))
|
if (std::regex_match(value.begin(), value.end(), revRegex))
|
||||||
rev = Hash::parseAny(value, htSHA1);
|
rev = Hash::parseAny(value, htSHA1);
|
||||||
else
|
else
|
||||||
ref = value;
|
ref = value;
|
||||||
|
@ -50,7 +50,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
});
|
});
|
||||||
|
|
||||||
} else
|
} else
|
||||||
url = state.coerceToString(pos, *args[0], context, false, false);
|
url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
|
||||||
|
|
||||||
// FIXME: git externals probably can be used to bypass the URI
|
// FIXME: git externals probably can be used to bypass the URI
|
||||||
// whitelist. Ah well.
|
// whitelist. Ah well.
|
||||||
|
@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
|
||||||
fetchers::Attrs attrs;
|
fetchers::Attrs attrs;
|
||||||
attrs.insert_or_assign("type", "hg");
|
attrs.insert_or_assign("type", "hg");
|
||||||
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
|
attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url);
|
||||||
attrs.insert_or_assign("name", name);
|
attrs.insert_or_assign("name", string(name));
|
||||||
if (ref) attrs.insert_or_assign("ref", *ref);
|
if (ref) attrs.insert_or_assign("ref", *ref);
|
||||||
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
|
if (rev) attrs.insert_or_assign("rev", rev->gitRev());
|
||||||
auto input = fetchers::Input::fromAttrs(std::move(attrs));
|
auto input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||||
|
|
|
@ -125,7 +125,7 @@ static void fetchTree(
|
||||||
if (attr.name == state.sType) continue;
|
if (attr.name == state.sType) continue;
|
||||||
state.forceValue(*attr.value, *attr.pos);
|
state.forceValue(*attr.value, *attr.pos);
|
||||||
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
if (attr.value->type() == nPath || attr.value->type() == nString) {
|
||||||
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false);
|
auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
|
||||||
attrs.emplace(attr.name,
|
attrs.emplace(attr.name,
|
||||||
attr.name == "url"
|
attr.name == "url"
|
||||||
? type == "git"
|
? type == "git"
|
||||||
|
@ -151,7 +151,7 @@ static void fetchTree(
|
||||||
|
|
||||||
input = fetchers::Input::fromAttrs(std::move(attrs));
|
input = fetchers::Input::fromAttrs(std::move(attrs));
|
||||||
} else {
|
} else {
|
||||||
auto url = state.coerceToString(pos, *args[0], context, false, false);
|
auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned();
|
||||||
|
|
||||||
if (type == "git") {
|
if (type == "git") {
|
||||||
fetchers::Attrs attrs;
|
fetchers::Attrs attrs;
|
||||||
|
|
|
@ -9,7 +9,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
{
|
{
|
||||||
auto toml = state.forceStringNoCtx(*args[0], pos);
|
auto toml = state.forceStringNoCtx(*args[0], pos);
|
||||||
|
|
||||||
std::istringstream tomlStream(toml);
|
std::istringstream tomlStream(string{toml});
|
||||||
|
|
||||||
std::function<void(Value &, toml::value)> visit;
|
std::function<void(Value &, toml::value)> visit;
|
||||||
|
|
||||||
|
|
|
@ -142,7 +142,7 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
||||||
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||||
XMLOpenElement _(doc, "attrspat", attrs);
|
XMLOpenElement _(doc, "attrspat", attrs);
|
||||||
for (auto & i : v.lambda.fun->formals->formals)
|
for (auto & i : v.lambda.fun->formals->lexicographicOrder())
|
||||||
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
|
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
|
||||||
} else
|
} else
|
||||||
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
|
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
|
||||||
|
|
|
@ -361,7 +361,7 @@ public:
|
||||||
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
|
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pos determinePos(const Pos &pos) const;
|
Pos determinePos(const Pos & pos) const;
|
||||||
|
|
||||||
/* Check whether forcing this value requires a trivial amount of
|
/* Check whether forcing this value requires a trivial amount of
|
||||||
computation. In particular, function applications are
|
computation. In particular, function applications are
|
||||||
|
|
|
@ -124,15 +124,13 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
|
||||||
debug("using substituted/cached input '%s' in '%s'",
|
debug("using substituted/cached input '%s' in '%s'",
|
||||||
to_string(), store->printStorePath(storePath));
|
to_string(), store->printStorePath(storePath));
|
||||||
|
|
||||||
auto actualPath = store->toRealPath(storePath);
|
return {Tree { .actualPath = store->toRealPath(storePath), .storePath = std::move(storePath) }, *this};
|
||||||
|
|
||||||
return {fetchers::Tree(std::move(actualPath), std::move(storePath)), *this};
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
debug("substitution of input '%s' failed: %s", to_string(), e.what());
|
debug("substitution of input '%s' failed: %s", to_string(), e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [tree, input] = [&]() -> std::pair<Tree, Input> {
|
auto [storePath, input] = [&]() -> std::pair<StorePath, Input> {
|
||||||
try {
|
try {
|
||||||
return scheme->fetch(store, *this);
|
return scheme->fetch(store, *this);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
|
@ -141,8 +139,10 @@ std::pair<Tree, Input> Input::fetch(ref<Store> store) const
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (tree.actualPath == "")
|
Tree tree {
|
||||||
tree.actualPath = store->toRealPath(tree.storePath);
|
.actualPath = store->toRealPath(storePath),
|
||||||
|
.storePath = storePath,
|
||||||
|
};
|
||||||
|
|
||||||
auto narHash = store->queryPathInfo(tree.storePath)->narHash;
|
auto narHash = store->queryPathInfo(tree.storePath)->narHash;
|
||||||
input.attrs.insert_or_assign("narHash", narHash.to_string(SRI, true));
|
input.attrs.insert_or_assign("narHash", narHash.to_string(SRI, true));
|
||||||
|
|
|
@ -16,7 +16,6 @@ struct Tree
|
||||||
{
|
{
|
||||||
Path actualPath;
|
Path actualPath;
|
||||||
StorePath storePath;
|
StorePath storePath;
|
||||||
Tree(Path && actualPath, StorePath && storePath) : actualPath(actualPath), storePath(std::move(storePath)) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputScheme;
|
struct InputScheme;
|
||||||
|
@ -131,7 +130,7 @@ struct InputScheme
|
||||||
|
|
||||||
virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg);
|
virtual void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg);
|
||||||
|
|
||||||
virtual std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) = 0;
|
virtual std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);
|
void registerInputScheme(std::shared_ptr<InputScheme> && fetcher);
|
||||||
|
|
|
@ -172,7 +172,7 @@ struct GitInputScheme : InputScheme
|
||||||
return {isLocal, isLocal ? url.path : url.base};
|
return {isLocal, isLocal ? url.path : url.base};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
|
||||||
{
|
{
|
||||||
Input input(_input);
|
Input input(_input);
|
||||||
|
|
||||||
|
@ -197,17 +197,14 @@ struct GitInputScheme : InputScheme
|
||||||
};
|
};
|
||||||
|
|
||||||
auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
|
auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
|
||||||
-> std::pair<Tree, Input>
|
-> std::pair<StorePath, Input>
|
||||||
{
|
{
|
||||||
assert(input.getRev());
|
assert(input.getRev());
|
||||||
assert(!_input.getRev() || _input.getRev() == input.getRev());
|
assert(!_input.getRev() || _input.getRev() == input.getRev());
|
||||||
if (!shallow)
|
if (!shallow)
|
||||||
input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
|
input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
|
||||||
input.attrs.insert_or_assign("lastModified", getIntAttr(infoAttrs, "lastModified"));
|
input.attrs.insert_or_assign("lastModified", getIntAttr(infoAttrs, "lastModified"));
|
||||||
return {
|
return {std::move(storePath), input};
|
||||||
Tree(store->toRealPath(storePath), std::move(storePath)),
|
|
||||||
input
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (input.getRev()) {
|
if (input.getRev()) {
|
||||||
|
@ -285,10 +282,7 @@ struct GitInputScheme : InputScheme
|
||||||
"lastModified",
|
"lastModified",
|
||||||
haveCommits ? std::stoull(runProgram("git", true, { "-C", actualUrl, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
|
haveCommits ? std::stoull(runProgram("git", true, { "-C", actualUrl, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
|
||||||
|
|
||||||
return {
|
return {std::move(storePath), input};
|
||||||
Tree(store->toRealPath(storePath), std::move(storePath)),
|
|
||||||
input
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||||
|
|
||||||
virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;
|
virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;
|
||||||
|
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
|
||||||
{
|
{
|
||||||
Input input(_input);
|
Input input(_input);
|
||||||
|
|
||||||
|
@ -199,10 +199,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||||
|
|
||||||
if (auto res = getCache()->lookup(store, immutableAttrs)) {
|
if (auto res = getCache()->lookup(store, immutableAttrs)) {
|
||||||
input.attrs.insert_or_assign("lastModified", getIntAttr(res->first, "lastModified"));
|
input.attrs.insert_or_assign("lastModified", getIntAttr(res->first, "lastModified"));
|
||||||
return {
|
return {std::move(res->second), input};
|
||||||
Tree(store->toRealPath(res->second), std::move(res->second)),
|
|
||||||
input
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto url = getDownloadUrl(input);
|
auto url = getDownloadUrl(input);
|
||||||
|
@ -221,7 +218,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||||
tree.storePath,
|
tree.storePath,
|
||||||
true);
|
true);
|
||||||
|
|
||||||
return {std::move(tree), input};
|
return {std::move(tree.storePath), input};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ struct IndirectInputScheme : InputScheme
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
|
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
|
||||||
{
|
{
|
||||||
throw Error("indirect input '%s' cannot be fetched directly", input.to_string());
|
throw Error("indirect input '%s' cannot be fetched directly", input.to_string());
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
return {isLocal, isLocal ? url.path : url.base};
|
return {isLocal, isLocal ? url.path : url.base};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
|
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & _input) override
|
||||||
{
|
{
|
||||||
Input input(_input);
|
Input input(_input);
|
||||||
|
|
||||||
|
@ -193,10 +193,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
|
|
||||||
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||||
|
|
||||||
return {
|
return {std::move(storePath), input};
|
||||||
Tree(store->toRealPath(storePath), std::move(storePath)),
|
|
||||||
input
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,15 +209,12 @@ struct MercurialInputScheme : InputScheme
|
||||||
};
|
};
|
||||||
|
|
||||||
auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
|
auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
|
||||||
-> std::pair<Tree, Input>
|
-> std::pair<StorePath, Input>
|
||||||
{
|
{
|
||||||
assert(input.getRev());
|
assert(input.getRev());
|
||||||
assert(!_input.getRev() || _input.getRev() == input.getRev());
|
assert(!_input.getRev() || _input.getRev() == input.getRev());
|
||||||
input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
|
input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
|
||||||
return {
|
return {std::move(storePath), input};
|
||||||
Tree(store->toRealPath(storePath), std::move(storePath)),
|
|
||||||
input
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (input.getRev()) {
|
if (input.getRev()) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ struct PathInputScheme : InputScheme
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
|
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
|
||||||
{
|
{
|
||||||
std::string absPath;
|
std::string absPath;
|
||||||
auto path = getStrAttr(input.attrs, "path");
|
auto path = getStrAttr(input.attrs, "path");
|
||||||
|
@ -115,10 +115,7 @@ struct PathInputScheme : InputScheme
|
||||||
// FIXME: try to substitute storePath.
|
// FIXME: try to substitute storePath.
|
||||||
storePath = store->addToStore("source", absPath);
|
storePath = store->addToStore("source", absPath);
|
||||||
|
|
||||||
return {
|
return {std::move(*storePath), input};
|
||||||
Tree(store->toRealPath(*storePath), std::move(*storePath)),
|
|
||||||
input
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ std::pair<Tree, time_t> downloadTarball(
|
||||||
|
|
||||||
if (cached && !cached->expired)
|
if (cached && !cached->expired)
|
||||||
return {
|
return {
|
||||||
Tree(store->toRealPath(cached->storePath), std::move(cached->storePath)),
|
Tree { .actualPath = store->toRealPath(cached->storePath), .storePath = std::move(cached->storePath) },
|
||||||
getIntAttr(cached->infoAttrs, "lastModified")
|
getIntAttr(cached->infoAttrs, "lastModified")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ std::pair<Tree, time_t> downloadTarball(
|
||||||
immutable);
|
immutable);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Tree(store->toRealPath(*unpackedStorePath), std::move(*unpackedStorePath)),
|
Tree { .actualPath = store->toRealPath(*unpackedStorePath), .storePath = std::move(*unpackedStorePath) },
|
||||||
lastModified,
|
lastModified,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -225,10 +225,10 @@ struct TarballInputScheme : InputScheme
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Tree, Input> fetch(ref<Store> store, const Input & input) override
|
std::pair<StorePath, Input> fetch(ref<Store> store, const Input & input) override
|
||||||
{
|
{
|
||||||
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first;
|
auto tree = downloadTarball(store, getStrAttr(input.attrs, "url"), input.getName(), false).first;
|
||||||
return {std::move(tree), input};
|
return {std::move(tree.storePath), input};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -307,7 +307,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, const string & name,
|
StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, std::string_view name,
|
||||||
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
|
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
|
||||||
{
|
{
|
||||||
if (method != FileIngestionMethod::Recursive || hashAlgo != htSHA256)
|
if (method != FileIngestionMethod::Recursive || hashAlgo != htSHA256)
|
||||||
|
|
|
@ -98,8 +98,8 @@ 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, const string & name,
|
StorePath addToStoreFromDump(Source & dump, std::string_view name,
|
||||||
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references ) override;
|
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override;
|
||||||
|
|
||||||
StorePath addToStore(const string & name, const Path & srcPath,
|
StorePath addToStore(const string & name, const Path & srcPath,
|
||||||
FileIngestionMethod method, HashType hashAlgo,
|
FileIngestionMethod method, HashType hashAlgo,
|
||||||
|
|
|
@ -912,9 +912,12 @@ void LocalDerivationGoal::startBuilder()
|
||||||
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
|
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
|
||||||
if (sandboxMountNamespace.get() == -1)
|
if (sandboxMountNamespace.get() == -1)
|
||||||
throw SysError("getting sandbox mount namespace");
|
throw SysError("getting sandbox mount namespace");
|
||||||
sandboxUserNamespace = open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY);
|
|
||||||
if (sandboxUserNamespace.get() == -1)
|
if (usingUserNamespace) {
|
||||||
throw SysError("getting sandbox user namespace");
|
sandboxUserNamespace = open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY);
|
||||||
|
if (sandboxUserNamespace.get() == -1)
|
||||||
|
throw SysError("getting sandbox user namespace");
|
||||||
|
}
|
||||||
|
|
||||||
/* Signal the builder that we've updated its user namespace. */
|
/* Signal the builder that we've updated its user namespace. */
|
||||||
writeFull(userNamespaceSync.writeSide.get(), "1");
|
writeFull(userNamespaceSync.writeSide.get(), "1");
|
||||||
|
@ -1205,7 +1208,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePath addToStoreFromDump(Source & dump, const string & name,
|
StorePath addToStoreFromDump(Source & dump, std::string_view name,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
|
||||||
const StorePathSet & references = StorePathSet()) override
|
const StorePathSet & references = StorePathSet()) override
|
||||||
{
|
{
|
||||||
|
|
|
@ -981,7 +981,11 @@ void processConnection(
|
||||||
readInt(from);
|
readInt(from);
|
||||||
}
|
}
|
||||||
|
|
||||||
readInt(from); // obsolete reserveSpace
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
|
||||||
|
readInt(from); // obsolete reserveSpace
|
||||||
|
|
||||||
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 33)
|
||||||
|
to << nixVersion;
|
||||||
|
|
||||||
/* Send startup error messages to the client. */
|
/* Send startup error messages to the client. */
|
||||||
tunnelLogger->startWork();
|
tunnelLogger->startWork();
|
||||||
|
|
|
@ -699,10 +699,10 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string hashPlaceholder(const std::string & outputName)
|
std::string hashPlaceholder(const std::string_view outputName)
|
||||||
{
|
{
|
||||||
// FIXME: memoize?
|
// FIXME: memoize?
|
||||||
return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false);
|
return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName)
|
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName)
|
||||||
|
|
|
@ -236,7 +236,7 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
|
||||||
It is used as a placeholder to allow derivations to refer to their
|
It is used as a placeholder to allow derivations to refer to their
|
||||||
own outputs without needing to use the hash of a derivation in
|
own outputs without needing to use the hash of a derivation in
|
||||||
itself, making the hash near-impossible to calculate. */
|
itself, making the hash near-impossible to calculate. */
|
||||||
std::string hashPlaceholder(const std::string & outputName);
|
std::string hashPlaceholder(const std::string_view outputName);
|
||||||
|
|
||||||
/* This creates an opaque and almost certainly unique string
|
/* This creates an opaque and almost certainly unique string
|
||||||
deterministically from a derivation path and output name.
|
deterministically from a derivation path and output name.
|
||||||
|
|
|
@ -128,7 +128,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
if (requestHeaders) curl_slist_free_all(requestHeaders);
|
if (requestHeaders) curl_slist_free_all(requestHeaders);
|
||||||
try {
|
try {
|
||||||
if (!done)
|
if (!done)
|
||||||
fail(FileTransferError(Interrupted, nullptr, "download of '%s' was interrupted", request.uri));
|
fail(FileTransferError(Interrupted, {}, "download of '%s' was interrupted", request.uri));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -704,7 +704,7 @@ struct curlFileTransfer : public FileTransfer
|
||||||
auto s3Res = s3Helper.getObject(bucketName, key);
|
auto s3Res = s3Helper.getObject(bucketName, key);
|
||||||
FileTransferResult res;
|
FileTransferResult res;
|
||||||
if (!s3Res.data)
|
if (!s3Res.data)
|
||||||
throw FileTransferError(NotFound, nullptr, "S3 object '%s' does not exist", request.uri);
|
throw FileTransferError(NotFound, {}, "S3 object '%s' does not exist", request.uri);
|
||||||
res.data = std::move(*s3Res.data);
|
res.data = std::move(*s3Res.data);
|
||||||
callback(std::move(res));
|
callback(std::move(res));
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -970,7 +970,7 @@ public:
|
||||||
Setting<std::string> commitLockFileSummary{
|
Setting<std::string> commitLockFileSummary{
|
||||||
this, "", "commit-lockfile-summary",
|
this, "", "commit-lockfile-summary",
|
||||||
R"(
|
R"(
|
||||||
The commit summary to use when commiting changed flake lock files. If
|
The commit summary to use when committing changed flake lock files. If
|
||||||
empty, the summary is generated based on the action performed.
|
empty, the summary is generated based on the action performed.
|
||||||
)"};
|
)"};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1318,7 +1318,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath LocalStore::addToStoreFromDump(Source & source0, const string & name,
|
StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name,
|
||||||
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
|
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
|
||||||
{
|
{
|
||||||
/* For computing the store path. */
|
/* For computing the store path. */
|
||||||
|
@ -1919,4 +1919,10 @@ void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log)
|
||||||
throw SysError("renaming '%1%' to '%2%'", tmpFile, logPath);
|
throw SysError("renaming '%1%' to '%2%'", tmpFile, logPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> LocalStore::getVersion()
|
||||||
|
{
|
||||||
|
return nixVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
@ -144,7 +144,7 @@ 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, const string & name,
|
StorePath addToStoreFromDump(Source & dump, std::string_view name,
|
||||||
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override;
|
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override;
|
||||||
|
|
||||||
StorePath addTextToStore(const string & name, const string & s,
|
StorePath addTextToStore(const string & name, const string & s,
|
||||||
|
@ -211,6 +211,8 @@ public:
|
||||||
void queryRealisationUncached(const DrvOutput&,
|
void queryRealisationUncached(const DrvOutput&,
|
||||||
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
||||||
|
|
||||||
|
std::optional<std::string> getVersion() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
int getSchema();
|
int getSchema();
|
||||||
|
|
|
@ -56,8 +56,8 @@ bool DrvName::matches(const DrvName & n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string nextComponent(string::const_iterator & p,
|
std::string_view nextComponent(std::string_view::const_iterator & p,
|
||||||
const string::const_iterator end)
|
const std::string_view::const_iterator end)
|
||||||
{
|
{
|
||||||
/* Skip any dots and dashes (component separators). */
|
/* Skip any dots and dashes (component separators). */
|
||||||
while (p != end && (*p == '.' || *p == '-')) ++p;
|
while (p != end && (*p == '.' || *p == '-')) ++p;
|
||||||
|
@ -67,18 +67,18 @@ string nextComponent(string::const_iterator & p,
|
||||||
/* If the first character is a digit, consume the longest sequence
|
/* If the first character is a digit, consume the longest sequence
|
||||||
of digits. Otherwise, consume the longest sequence of
|
of digits. Otherwise, consume the longest sequence of
|
||||||
non-digit, non-separator characters. */
|
non-digit, non-separator characters. */
|
||||||
string s;
|
auto s = p;
|
||||||
if (isdigit(*p))
|
if (isdigit(*p))
|
||||||
while (p != end && isdigit(*p)) s += *p++;
|
while (p != end && isdigit(*p)) p++;
|
||||||
else
|
else
|
||||||
while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
|
while (p != end && (!isdigit(*p) && *p != '.' && *p != '-'))
|
||||||
s += *p++;
|
p++;
|
||||||
|
|
||||||
return s;
|
return {s, size_t(p - s)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool componentsLT(const string & c1, const string & c2)
|
static bool componentsLT(const std::string_view c1, const std::string_view c2)
|
||||||
{
|
{
|
||||||
auto n1 = string2Int<int>(c1);
|
auto n1 = string2Int<int>(c1);
|
||||||
auto n2 = string2Int<int>(c2);
|
auto n2 = string2Int<int>(c2);
|
||||||
|
@ -94,14 +94,14 @@ static bool componentsLT(const string & c1, const string & c2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int compareVersions(const string & v1, const string & v2)
|
int compareVersions(const std::string_view v1, const std::string_view v2)
|
||||||
{
|
{
|
||||||
string::const_iterator p1 = v1.begin();
|
auto p1 = v1.begin();
|
||||||
string::const_iterator p2 = v2.begin();
|
auto p2 = v2.begin();
|
||||||
|
|
||||||
while (p1 != v1.end() || p2 != v2.end()) {
|
while (p1 != v1.end() || p2 != v2.end()) {
|
||||||
string c1 = nextComponent(p1, v1.end());
|
auto c1 = nextComponent(p1, v1.end());
|
||||||
string c2 = nextComponent(p2, v2.end());
|
auto c2 = nextComponent(p2, v2.end());
|
||||||
if (componentsLT(c1, c2)) return -1;
|
if (componentsLT(c1, c2)) return -1;
|
||||||
else if (componentsLT(c2, c1)) return 1;
|
else if (componentsLT(c2, c1)) return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,9 @@ private:
|
||||||
|
|
||||||
typedef list<DrvName> DrvNames;
|
typedef list<DrvName> DrvNames;
|
||||||
|
|
||||||
string nextComponent(string::const_iterator & p,
|
std::string_view nextComponent(std::string_view::const_iterator & p,
|
||||||
const string::const_iterator end);
|
const std::string_view::const_iterator end);
|
||||||
int compareVersions(const string & v1, const string & v2);
|
int compareVersions(const std::string_view v1, const std::string_view v2);
|
||||||
DrvNames drvNamesFromArgs(const Strings & opArgs);
|
DrvNames drvNamesFromArgs(const Strings & opArgs);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ static void makeWritable(const Path & path)
|
||||||
struct MakeReadOnly
|
struct MakeReadOnly
|
||||||
{
|
{
|
||||||
Path path;
|
Path path;
|
||||||
MakeReadOnly(const Path & path) : path(path) { }
|
MakeReadOnly(const PathView path) : path(path) { }
|
||||||
~MakeReadOnly()
|
~MakeReadOnly()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -205,12 +205,13 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||||
/* Make the containing directory writable, but only if it's not
|
/* Make the containing directory writable, but only if it's not
|
||||||
the store itself (we don't want or need to mess with its
|
the store itself (we don't want or need to mess with its
|
||||||
permissions). */
|
permissions). */
|
||||||
bool mustToggle = dirOf(path) != realStoreDir.get();
|
const Path dirOfPath(dirOf(path));
|
||||||
if (mustToggle) makeWritable(dirOf(path));
|
bool mustToggle = dirOfPath != realStoreDir.get();
|
||||||
|
if (mustToggle) makeWritable(dirOfPath);
|
||||||
|
|
||||||
/* When we're done, make the directory read-only again and reset
|
/* When we're done, make the directory read-only again and reset
|
||||||
its timestamp back to 0. */
|
its timestamp back to 0. */
|
||||||
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
|
MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
|
||||||
|
|
||||||
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
|
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
|
||||||
% realStoreDir % getpid() % random()).str();
|
% realStoreDir % getpid() % random()).str();
|
||||||
|
|
|
@ -170,7 +170,7 @@ std::string writeStructuredAttrsShell(const nlohmann::json & json)
|
||||||
|
|
||||||
auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> {
|
auto handleSimpleType = [](const nlohmann::json & value) -> std::optional<std::string> {
|
||||||
if (value.is_string())
|
if (value.is_string())
|
||||||
return shellEscape(value);
|
return shellEscape(value.get<std::string_view>());
|
||||||
|
|
||||||
if (value.is_number()) {
|
if (value.is_number()) {
|
||||||
auto f = value.get<float>();
|
auto f = value.get<float>();
|
||||||
|
|
|
@ -188,7 +188,12 @@ void RemoteStore::initConnection(Connection & conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 11)
|
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 11)
|
||||||
conn.to << false;
|
conn.to << false; // obsolete reserveSpace
|
||||||
|
|
||||||
|
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 33) {
|
||||||
|
conn.to.flush();
|
||||||
|
conn.daemonNixVersion = readString(conn.from);
|
||||||
|
}
|
||||||
|
|
||||||
auto ex = conn.processStderr();
|
auto ex = conn.processStderr();
|
||||||
if (ex) std::rethrow_exception(ex);
|
if (ex) std::rethrow_exception(ex);
|
||||||
|
@ -495,7 +500,7 @@ std::optional<StorePath> RemoteStore::queryPathFromHashPart(const std::string &
|
||||||
|
|
||||||
ref<const ValidPathInfo> RemoteStore::addCAToStore(
|
ref<const ValidPathInfo> RemoteStore::addCAToStore(
|
||||||
Source & dump,
|
Source & dump,
|
||||||
const string & name,
|
std::string_view name,
|
||||||
ContentAddressMethod caMethod,
|
ContentAddressMethod caMethod,
|
||||||
const StorePathSet & references,
|
const StorePathSet & references,
|
||||||
RepairFlag repair)
|
RepairFlag repair)
|
||||||
|
@ -577,7 +582,7 @@ ref<const ValidPathInfo> RemoteStore::addCAToStore(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath RemoteStore::addToStoreFromDump(Source & dump, const string & name,
|
StorePath RemoteStore::addToStoreFromDump(Source & dump, std::string_view name,
|
||||||
FileIngestionMethod method, HashType hashType, RepairFlag repair, const StorePathSet & references)
|
FileIngestionMethod method, HashType hashType, RepairFlag repair, const StorePathSet & references)
|
||||||
{
|
{
|
||||||
return addCAToStore(dump, name, FixedOutputHashMethod{ .fileIngestionMethod = method, .hashType = hashType }, references, repair)->path;
|
return addCAToStore(dump, name, FixedOutputHashMethod{ .fileIngestionMethod = method, .hashType = hashType }, references, repair)->path;
|
||||||
|
@ -920,6 +925,13 @@ void RemoteStore::addBuildLog(const StorePath & drvPath, std::string_view log)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::optional<std::string> RemoteStore::getVersion()
|
||||||
|
{
|
||||||
|
auto conn(getConnection());
|
||||||
|
return conn->daemonNixVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void RemoteStore::connect()
|
void RemoteStore::connect()
|
||||||
{
|
{
|
||||||
auto conn(getConnection());
|
auto conn(getConnection());
|
||||||
|
|
|
@ -66,13 +66,13 @@ public:
|
||||||
/* Add a content-addressable store path. `dump` will be drained. */
|
/* Add a content-addressable store path. `dump` will be drained. */
|
||||||
ref<const ValidPathInfo> addCAToStore(
|
ref<const ValidPathInfo> addCAToStore(
|
||||||
Source & dump,
|
Source & dump,
|
||||||
const string & name,
|
std::string_view name,
|
||||||
ContentAddressMethod caMethod,
|
ContentAddressMethod caMethod,
|
||||||
const StorePathSet & references,
|
const StorePathSet & references,
|
||||||
RepairFlag repair);
|
RepairFlag repair);
|
||||||
|
|
||||||
/* Add a content-addressable store path. Does not support references. `dump` will be drained. */
|
/* Add a content-addressable store path. Does not support references. `dump` will be drained. */
|
||||||
StorePath addToStoreFromDump(Source & dump, const string & name,
|
StorePath addToStoreFromDump(Source & dump, std::string_view name,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()) override;
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()) override;
|
||||||
|
|
||||||
void addToStore(const ValidPathInfo & info, Source & nar,
|
void addToStore(const ValidPathInfo & info, Source & nar,
|
||||||
|
@ -118,6 +118,8 @@ public:
|
||||||
|
|
||||||
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
|
void addBuildLog(const StorePath & drvPath, std::string_view log) override;
|
||||||
|
|
||||||
|
std::optional<std::string> getVersion() override;
|
||||||
|
|
||||||
void connect() override;
|
void connect() override;
|
||||||
|
|
||||||
unsigned int getProtocol() override;
|
unsigned int getProtocol() override;
|
||||||
|
@ -129,6 +131,7 @@ public:
|
||||||
FdSink to;
|
FdSink to;
|
||||||
FdSource from;
|
FdSource from;
|
||||||
unsigned int daemonVersion;
|
unsigned int daemonVersion;
|
||||||
|
std::optional<std::string> daemonNixVersion;
|
||||||
std::chrono::time_point<std::chrono::steady_clock> startTime;
|
std::chrono::time_point<std::chrono::steady_clock> startTime;
|
||||||
|
|
||||||
virtual ~Connection();
|
virtual ~Connection();
|
||||||
|
|
|
@ -499,7 +499,7 @@ public:
|
||||||
false).
|
false).
|
||||||
`dump` may be drained */
|
`dump` may be drained */
|
||||||
// FIXME: remove?
|
// FIXME: remove?
|
||||||
virtual StorePath addToStoreFromDump(Source & dump, const string & name,
|
virtual StorePath addToStoreFromDump(Source & dump, std::string_view name,
|
||||||
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
|
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
|
||||||
const StorePathSet & references = StorePathSet())
|
const StorePathSet & references = StorePathSet())
|
||||||
{ unsupported("addToStoreFromDump"); }
|
{ unsupported("addToStoreFromDump"); }
|
||||||
|
@ -765,6 +765,9 @@ public:
|
||||||
* (a no-op when there’s no daemon)
|
* (a no-op when there’s no daemon)
|
||||||
*/
|
*/
|
||||||
virtual void setOptions() { }
|
virtual void setOptions() { }
|
||||||
|
|
||||||
|
virtual std::optional<std::string> getVersion() { return {}; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
Stats stats;
|
Stats stats;
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace nix {
|
||||||
#define WORKER_MAGIC_1 0x6e697863
|
#define WORKER_MAGIC_1 0x6e697863
|
||||||
#define WORKER_MAGIC_2 0x6478696f
|
#define WORKER_MAGIC_2 0x6478696f
|
||||||
|
|
||||||
#define PROTOCOL_VERSION (1 << 8 | 32)
|
#define PROTOCOL_VERSION (1 << 8 | 33)
|
||||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||||
|
|
||||||
|
|
|
@ -259,7 +259,7 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI)
|
||||||
throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type));
|
throw BadHash("hash '%s' has wrong length for hash type '%s'", rest, printHashType(this->type));
|
||||||
}
|
}
|
||||||
|
|
||||||
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht)
|
Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht)
|
||||||
{
|
{
|
||||||
if (hashStr.empty()) {
|
if (hashStr.empty()) {
|
||||||
if (!ht)
|
if (!ht)
|
||||||
|
|
|
@ -107,7 +107,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Helper that defaults empty hashes to the 0 hash. */
|
/* Helper that defaults empty hashes to the 0 hash. */
|
||||||
Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht);
|
Hash newHashAllowEmpty(std::string_view hashStr, std::optional<HashType> ht);
|
||||||
|
|
||||||
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
|
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
|
||||||
string printHash16or32(const Hash & hash);
|
string printHash16or32(const Hash & hash);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -47,4 +48,63 @@ struct Explicit {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* This wants to be a little bit like rust's Cow type.
|
||||||
|
Some parts of the evaluator benefit greatly from being able to reuse
|
||||||
|
existing allocations for strings, but have to be able to also use
|
||||||
|
newly allocated storage for values.
|
||||||
|
|
||||||
|
We do not define implicit conversions, even with ref qualifiers,
|
||||||
|
since those can easily become ambiguous to the reader and can degrade
|
||||||
|
into copying behaviour we want to avoid. */
|
||||||
|
class BackedStringView {
|
||||||
|
private:
|
||||||
|
std::variant<std::string, std::string_view> data;
|
||||||
|
|
||||||
|
/* Needed to introduce a temporary since operator-> must return
|
||||||
|
a pointer. Without this we'd need to store the view object
|
||||||
|
even when we already own a string. */
|
||||||
|
class Ptr {
|
||||||
|
private:
|
||||||
|
std::string_view view;
|
||||||
|
public:
|
||||||
|
Ptr(std::string_view view): view(view) {}
|
||||||
|
const std::string_view * operator->() const { return &view; }
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
BackedStringView(std::string && s): data(std::move(s)) {}
|
||||||
|
BackedStringView(std::string_view sv): data(sv) {}
|
||||||
|
template<size_t N>
|
||||||
|
BackedStringView(const char (& lit)[N]): data(std::string_view(lit)) {}
|
||||||
|
|
||||||
|
BackedStringView(const BackedStringView &) = delete;
|
||||||
|
BackedStringView & operator=(const BackedStringView &) = delete;
|
||||||
|
|
||||||
|
/* We only want move operations defined since the sole purpose of
|
||||||
|
this type is to avoid copies. */
|
||||||
|
BackedStringView(BackedStringView && other) = default;
|
||||||
|
BackedStringView & operator=(BackedStringView && other) = default;
|
||||||
|
|
||||||
|
bool isOwned() const
|
||||||
|
{
|
||||||
|
return std::holds_alternative<std::string>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toOwned() &&
|
||||||
|
{
|
||||||
|
return isOwned()
|
||||||
|
? std::move(std::get<std::string>(data))
|
||||||
|
: std::string(std::get<std::string_view>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view operator*() const
|
||||||
|
{
|
||||||
|
return isOwned()
|
||||||
|
? std::get<std::string>(data)
|
||||||
|
: std::get<std::string_view>(data);
|
||||||
|
}
|
||||||
|
Ptr operator->() const { return Ptr(**this); }
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ void replaceEnv(std::map<std::string, std::string> newEnv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
|
Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
|
||||||
{
|
{
|
||||||
if (path[0] != '/') {
|
if (path[0] != '/') {
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
|
@ -95,12 +95,12 @@ Path absPath(Path path, std::optional<Path> dir, bool resolveSymlinks)
|
||||||
if (!getcwd(buf, sizeof(buf)))
|
if (!getcwd(buf, sizeof(buf)))
|
||||||
#endif
|
#endif
|
||||||
throw SysError("cannot get cwd");
|
throw SysError("cannot get cwd");
|
||||||
dir = buf;
|
path = concatStrings(buf, "/", path);
|
||||||
#ifdef __GNU__
|
#ifdef __GNU__
|
||||||
free(buf);
|
free(buf);
|
||||||
#endif
|
#endif
|
||||||
}
|
} else
|
||||||
path = *dir + "/" + path;
|
path = concatStrings(*dir, "/", path);
|
||||||
}
|
}
|
||||||
return canonPath(path, resolveSymlinks);
|
return canonPath(path, resolveSymlinks);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
||||||
path = {};
|
path = {};
|
||||||
} else {
|
} else {
|
||||||
s += path.substr(0, slash);
|
s += path.substr(0, slash);
|
||||||
path = path.substr(slash + 1);
|
path = path.substr(slash);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If s points to a symlink, resolve it and continue from there */
|
/* If s points to a symlink, resolve it and continue from there */
|
||||||
|
@ -172,7 +172,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path dirOf(const Path & path)
|
Path dirOf(const PathView path)
|
||||||
{
|
{
|
||||||
Path::size_type pos = path.rfind('/');
|
Path::size_type pos = path.rfind('/');
|
||||||
if (pos == string::npos)
|
if (pos == string::npos)
|
||||||
|
@ -1344,9 +1344,11 @@ std::string toLower(const std::string & s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string shellEscape(const std::string & s)
|
std::string shellEscape(const std::string_view s)
|
||||||
{
|
{
|
||||||
std::string r = "'";
|
std::string r;
|
||||||
|
r.reserve(s.size() + 2);
|
||||||
|
r += "'";
|
||||||
for (auto & i : s)
|
for (auto & i : s)
|
||||||
if (i == '\'') r += "'\\''"; else r += i;
|
if (i == '\'') r += "'\\''"; else r += i;
|
||||||
r += '\'';
|
r += '\'';
|
||||||
|
@ -1356,11 +1358,15 @@ std::string shellEscape(const std::string & s)
|
||||||
|
|
||||||
void ignoreException()
|
void ignoreException()
|
||||||
{
|
{
|
||||||
|
/* Make sure no exceptions leave this function.
|
||||||
|
printError() also throws when remote is closed. */
|
||||||
try {
|
try {
|
||||||
throw;
|
try {
|
||||||
} catch (std::exception & e) {
|
throw;
|
||||||
printError("error (ignored): %1%", e.what());
|
} catch (std::exception & e) {
|
||||||
}
|
printError("error (ignored): %1%", e.what());
|
||||||
|
}
|
||||||
|
} catch (...) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldANSI()
|
bool shouldANSI()
|
||||||
|
@ -1751,7 +1757,7 @@ void bind(int fd, const std::string & path)
|
||||||
|
|
||||||
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid = startProcess([&]() {
|
||||||
auto dir = dirOf(path);
|
Path dir = dirOf(path);
|
||||||
if (chdir(dir.c_str()) == -1)
|
if (chdir(dir.c_str()) == -1)
|
||||||
throw SysError("chdir to '%s' failed", dir);
|
throw SysError("chdir to '%s' failed", dir);
|
||||||
std::string base(baseNameOf(path));
|
std::string base(baseNameOf(path));
|
||||||
|
@ -1780,7 +1786,7 @@ void connect(int fd, const std::string & path)
|
||||||
|
|
||||||
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
if (path.size() + 1 >= sizeof(addr.sun_path)) {
|
||||||
Pid pid = startProcess([&]() {
|
Pid pid = startProcess([&]() {
|
||||||
auto dir = dirOf(path);
|
Path dir = dirOf(path);
|
||||||
if (chdir(dir.c_str()) == -1)
|
if (chdir(dir.c_str()) == -1)
|
||||||
throw SysError("chdir to '%s' failed", dir);
|
throw SysError("chdir to '%s' failed", dir);
|
||||||
std::string base(baseNameOf(path));
|
std::string base(baseNameOf(path));
|
||||||
|
|
|
@ -49,7 +49,7 @@ void clearEnv();
|
||||||
specified directory, or the current directory otherwise. The path
|
specified directory, or the current directory otherwise. The path
|
||||||
is also canonicalised. */
|
is also canonicalised. */
|
||||||
Path absPath(Path path,
|
Path absPath(Path path,
|
||||||
std::optional<Path> dir = {},
|
std::optional<PathView> dir = {},
|
||||||
bool resolveSymlinks = false);
|
bool resolveSymlinks = false);
|
||||||
|
|
||||||
/* Canonicalise a path by removing all `.' or `..' components and
|
/* Canonicalise a path by removing all `.' or `..' components and
|
||||||
|
@ -62,7 +62,7 @@ Path canonPath(PathView path, bool resolveSymlinks = false);
|
||||||
everything before the final `/'. If the path is the root or an
|
everything before the final `/'. If the path is the root or an
|
||||||
immediate child thereof (e.g., `/foo'), this means `/'
|
immediate child thereof (e.g., `/foo'), this means `/'
|
||||||
is returned.*/
|
is returned.*/
|
||||||
Path dirOf(const Path & path);
|
Path dirOf(const PathView path);
|
||||||
|
|
||||||
/* Return the base name of the given canonical path, i.e., everything
|
/* Return the base name of the given canonical path, i.e., everything
|
||||||
following the final `/' (trailing slashes are removed). */
|
following the final `/' (trailing slashes are removed). */
|
||||||
|
@ -148,6 +148,9 @@ Path getDataDir();
|
||||||
/* Create a directory and all its parents, if necessary. Returns the
|
/* Create a directory and all its parents, if necessary. Returns the
|
||||||
list of created directories, in order of creation. */
|
list of created directories, in order of creation. */
|
||||||
Paths createDirs(const Path & path);
|
Paths createDirs(const Path & path);
|
||||||
|
inline Paths createDirs(PathView path) {
|
||||||
|
return createDirs(Path(path));
|
||||||
|
}
|
||||||
|
|
||||||
/* Create a symlink. */
|
/* Create a symlink. */
|
||||||
void createSymlink(const Path & target, const Path & link,
|
void createSymlink(const Path & target, const Path & link,
|
||||||
|
@ -187,6 +190,7 @@ public:
|
||||||
void cancel();
|
void cancel();
|
||||||
void reset(const Path & p, bool recursive = true);
|
void reset(const Path & p, bool recursive = true);
|
||||||
operator Path() const { return path; }
|
operator Path() const { return path; }
|
||||||
|
operator PathView() const { return path; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -491,7 +495,7 @@ std::string toLower(const std::string & s);
|
||||||
|
|
||||||
|
|
||||||
/* Escape a string as a shell word. */
|
/* Escape a string as a shell word. */
|
||||||
std::string shellEscape(const std::string & s);
|
std::string shellEscape(const std::string_view s);
|
||||||
|
|
||||||
|
|
||||||
/* Exception handling in destructors: print an error message, then
|
/* Exception handling in destructors: print an error message, then
|
||||||
|
|
|
@ -318,7 +318,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
|
|
||||||
for (auto & i : attrPaths) {
|
for (auto & i : attrPaths) {
|
||||||
Value & v(*findAlongAttrPath(*state, i, *autoArgs, vRoot).first);
|
Value & v(*findAlongAttrPath(*state, i, *autoArgs, vRoot).first);
|
||||||
state->forceValue(v);
|
state->forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||||
getDerivations(*state, v, "", *autoArgs, drvs, false);
|
getDerivations(*state, v, "", *autoArgs, drvs, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,6 +500,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
"%3%"
|
"%3%"
|
||||||
"PATH=%4%:\"$PATH\"; "
|
"PATH=%4%:\"$PATH\"; "
|
||||||
"SHELL=%5%; "
|
"SHELL=%5%; "
|
||||||
|
"BASH=%5%; "
|
||||||
"set +e; "
|
"set +e; "
|
||||||
R"s([ -n "$PS1" -a -z "$NIX_SHELL_PRESERVE_PROMPT" ] && PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s"
|
R"s([ -n "$PS1" -a -z "$NIX_SHELL_PRESERVE_PROMPT" ] && PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s"
|
||||||
"if [ \"$(type -t runHook)\" = function ]; then runHook shellHook; fi; "
|
"if [ \"$(type -t runHook)\" = function ]; then runHook shellHook; fi; "
|
||||||
|
|
|
@ -129,7 +129,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
|
|
||||||
/* Evaluate it. */
|
/* Evaluate it. */
|
||||||
debug("evaluating user environment builder");
|
debug("evaluating user environment builder");
|
||||||
state.forceValue(topLevel);
|
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
|
||||||
PathSet context;
|
PathSet context;
|
||||||
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
|
Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath));
|
||||||
auto topLevelDrv = state.store->parseStorePath(state.coerceToPath(*aDrvPath.pos, *aDrvPath.value, context));
|
auto topLevelDrv = state.store->parseStorePath(state.coerceToPath(*aDrvPath.pos, *aDrvPath.value, context));
|
||||||
|
|
|
@ -40,7 +40,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||||
|
|
||||||
for (auto & i : attrPaths) {
|
for (auto & i : attrPaths) {
|
||||||
Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot).first);
|
Value & v(*findAlongAttrPath(state, i, autoArgs, vRoot).first);
|
||||||
state.forceValue(v);
|
state.forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||||
|
|
||||||
PathSet context;
|
PathSet context;
|
||||||
if (evalOnly) {
|
if (evalOnly) {
|
||||||
|
|
|
@ -51,7 +51,9 @@ struct CmdBundle : InstallableCommand
|
||||||
|
|
||||||
Strings getDefaultFlakeAttrPaths() override
|
Strings getDefaultFlakeAttrPaths() override
|
||||||
{
|
{
|
||||||
Strings res{"defaultApp." + settings.thisSystem.get()};
|
Strings res{
|
||||||
|
"defaultApp." + settings.thisSystem.get()
|
||||||
|
};
|
||||||
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPaths())
|
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPaths())
|
||||||
res.push_back(s);
|
res.push_back(s);
|
||||||
return res;
|
return res;
|
||||||
|
@ -59,7 +61,10 @@ struct CmdBundle : InstallableCommand
|
||||||
|
|
||||||
Strings getDefaultFlakeAttrPathPrefixes() override
|
Strings getDefaultFlakeAttrPathPrefixes() override
|
||||||
{
|
{
|
||||||
Strings res{"apps." + settings.thisSystem.get() + "."};
|
Strings res{
|
||||||
|
"apps." + settings.thisSystem.get() + "."
|
||||||
|
|
||||||
|
};
|
||||||
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPathPrefixes())
|
for (auto & s : SourceExprCommand::getDefaultFlakeAttrPathPrefixes())
|
||||||
res.push_back(s);
|
res.push_back(s);
|
||||||
return res;
|
return res;
|
||||||
|
@ -69,29 +74,19 @@ struct CmdBundle : InstallableCommand
|
||||||
{
|
{
|
||||||
auto evalState = getEvalState();
|
auto evalState = getEvalState();
|
||||||
|
|
||||||
auto app = installable->toApp(*evalState).resolve(getEvalStore(), store);
|
auto val = installable->toValue(*evalState).first;
|
||||||
|
|
||||||
auto [bundlerFlakeRef, bundlerName] = parseFlakeRefWithFragment(bundler, absPath("."));
|
auto [bundlerFlakeRef, bundlerName] = parseFlakeRefWithFragment(bundler, absPath("."));
|
||||||
const flake::LockFlags lockFlags{ .writeLockFile = false };
|
const flake::LockFlags lockFlags{ .writeLockFile = false };
|
||||||
auto bundler = InstallableFlake(this,
|
InstallableFlake bundler{this,
|
||||||
evalState, std::move(bundlerFlakeRef),
|
evalState, std::move(bundlerFlakeRef), bundlerName,
|
||||||
Strings{bundlerName == "" ? "defaultBundler" : bundlerName},
|
{"defaultBundler." + settings.thisSystem.get()},
|
||||||
Strings({"bundlers."}), lockFlags);
|
{"bundlers." + settings.thisSystem.get() + "."},
|
||||||
|
lockFlags
|
||||||
auto attrs = evalState->buildBindings(2);
|
};
|
||||||
|
|
||||||
PathSet context;
|
|
||||||
for (auto & i : app.context)
|
|
||||||
context.insert("=" + store->printStorePath(i.path));
|
|
||||||
attrs.alloc("program").mkString(app.program, context);
|
|
||||||
|
|
||||||
attrs.alloc("system").mkString(settings.thisSystem.get());
|
|
||||||
|
|
||||||
auto vRes = evalState->allocValue();
|
auto vRes = evalState->allocValue();
|
||||||
evalState->callFunction(
|
evalState->callFunction(*bundler.toValue(*evalState).first, *val, *vRes, noPos);
|
||||||
*bundler.toValue(*evalState).first,
|
|
||||||
evalState->allocValue()->mkAttrs(attrs),
|
|
||||||
*vRes, noPos);
|
|
||||||
|
|
||||||
if (!evalState->isDerivation(*vRes))
|
if (!evalState->isDerivation(*vRes))
|
||||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||||
|
@ -113,9 +108,12 @@ struct CmdBundle : InstallableCommand
|
||||||
|
|
||||||
auto outPathS = store->printStorePath(outPath);
|
auto outPathS = store->printStorePath(outPath);
|
||||||
|
|
||||||
if (!outLink)
|
if (!outLink) {
|
||||||
outLink = baseNameOf(app.program);
|
auto &attr = vRes->attrs->need(evalState->sName);
|
||||||
|
outLink = evalState->forceStringNoCtx(*attr.value,*attr.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: will crash if not a localFSStore?
|
||||||
store.dynamic_pointer_cast<LocalFSStore>()->addPermRoot(outPath, absPath(*outLink));
|
store.dynamic_pointer_cast<LocalFSStore>()->addPermRoot(outPath, absPath(*outLink));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,19 +18,51 @@ R""(
|
||||||
nix (Nix) 2.4pre20201215_e3ddffb
|
nix (Nix) 2.4pre20201215_e3ddffb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Bundle a Hello using a specific bundler:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix bundle --bundler github:NixOS/bundlers#toDockerImage nixpkgs#hello
|
||||||
|
# docker load < hello-2.10.tar.gz
|
||||||
|
# docker run hello-2.10:latest hello
|
||||||
|
Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
`nix bundle` packs the closure of the [Nix app](./nix3-run.md)
|
`nix bundle`, by default, packs the closure of the *installable* into a single
|
||||||
*installable* into a single self-extracting executable. See the
|
self-extracting executable. See the [`bundlers`
|
||||||
[`nix-bundle` homepage](https://github.com/matthewbauer/nix-bundle)
|
homepage](https://github.com/NixOS/bundlers) for more details.
|
||||||
for more details.
|
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
>
|
>
|
||||||
> This command only works on Linux.
|
> This command only works on Linux.
|
||||||
|
|
||||||
# Bundler definitions
|
# Flake output attributes
|
||||||
|
|
||||||
TODO
|
If no flake output attribute is given, `nix bundle` tries the following
|
||||||
|
flake output attributes:
|
||||||
|
|
||||||
|
* `defaultBundler.<system>`
|
||||||
|
|
||||||
|
If an attribute *name* is given, `nix run` tries the following flake
|
||||||
|
output attributes:
|
||||||
|
|
||||||
|
* `bundler.<system>.<name>`
|
||||||
|
|
||||||
|
# Bundlers
|
||||||
|
|
||||||
|
A bundler is specified by a flake output attribute named
|
||||||
|
`bundlers.<system>.<name>` or `defaultBundler.<system>`. It looks like this:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
bundlers.x86_64-linux.identity = drv: drv;
|
||||||
|
|
||||||
|
bundlers.x86_64-linux.blender_2_79 = drv: self.packages.x86_64-linux.blender_2_79;
|
||||||
|
|
||||||
|
defaultBundler.x86_64-linux = drv: drv;
|
||||||
|
```
|
||||||
|
|
||||||
|
A bundler must be a function that accepts an arbitrary value (typically a
|
||||||
|
derivation or app definition) and returns a derivation.
|
||||||
|
|
||||||
)""
|
)""
|
||||||
|
|
|
@ -472,9 +472,11 @@ struct CmdDevelop : Common, MixEnvironment
|
||||||
else {
|
else {
|
||||||
script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script;
|
script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script;
|
||||||
if (developSettings.bashPrompt != "")
|
if (developSettings.bashPrompt != "")
|
||||||
script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", shellEscape(developSettings.bashPrompt));
|
script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n",
|
||||||
|
shellEscape(developSettings.bashPrompt.get()));
|
||||||
if (developSettings.bashPromptSuffix != "")
|
if (developSettings.bashPromptSuffix != "")
|
||||||
script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n", shellEscape(developSettings.bashPromptSuffix));
|
script += fmt("[ -n \"$PS1\" ] && PS1+=%s;\n",
|
||||||
|
shellEscape(developSettings.bashPromptSuffix.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFull(rcFileFd.get(), script);
|
writeFull(rcFileFd.get(), script);
|
||||||
|
@ -496,7 +498,8 @@ struct CmdDevelop : Common, MixEnvironment
|
||||||
this,
|
this,
|
||||||
state,
|
state,
|
||||||
installable->nixpkgsFlakeRef(),
|
installable->nixpkgsFlakeRef(),
|
||||||
Strings{"bashInteractive"},
|
"bashInteractive",
|
||||||
|
Strings{},
|
||||||
Strings{"legacyPackages." + settings.thisSystem.get() + "."},
|
Strings{"legacyPackages." + settings.thisSystem.get() + "."},
|
||||||
nixpkgsLockFlags);
|
nixpkgsLockFlags);
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ R""(
|
||||||
# nix develop /tmp/my-build-env
|
# nix develop /tmp/my-build-env
|
||||||
```
|
```
|
||||||
|
|
||||||
* Replace all occurences of the store path corresponding to
|
* Replace all occurrences of the store path corresponding to
|
||||||
`glibc.dev` with a writable directory:
|
`glibc.dev` with a writable directory:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
|
|
@ -81,7 +81,7 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
|
|
||||||
recurse = [&](Value & v, const Pos & pos, const Path & path)
|
recurse = [&](Value & v, const Pos & pos, const Path & path)
|
||||||
{
|
{
|
||||||
state->forceValue(v);
|
state->forceValue(v, pos);
|
||||||
if (v.type() == nString)
|
if (v.type() == nString)
|
||||||
// FIXME: disallow strings with contexts?
|
// FIXME: disallow strings with contexts?
|
||||||
writeFile(path, v.string.s);
|
writeFile(path, v.string.s);
|
||||||
|
@ -107,7 +107,7 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
|
|
||||||
else if (raw) {
|
else if (raw) {
|
||||||
stopProgressBar();
|
stopProgressBar();
|
||||||
std::cout << state->coerceToString(noPos, *v, context);
|
std::cout << *state->coerceToString(noPos, *v, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (json) {
|
else if (json) {
|
||||||
|
|
|
@ -124,12 +124,13 @@ struct CmdFlakeLock : FlakeCommand
|
||||||
static void enumerateOutputs(EvalState & state, Value & vFlake,
|
static void enumerateOutputs(EvalState & state, Value & vFlake,
|
||||||
std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback)
|
std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback)
|
||||||
{
|
{
|
||||||
state.forceAttrs(vFlake);
|
auto pos = vFlake.determinePos(noPos);
|
||||||
|
state.forceAttrs(vFlake, pos);
|
||||||
|
|
||||||
auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs"));
|
auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs"));
|
||||||
assert(aOutputs);
|
assert(aOutputs);
|
||||||
|
|
||||||
state.forceAttrs(*aOutputs->value);
|
state.forceAttrs(*aOutputs->value, pos);
|
||||||
|
|
||||||
auto sHydraJobs = state.symbols.create("hydraJobs");
|
auto sHydraJobs = state.symbols.create("hydraJobs");
|
||||||
|
|
||||||
|
@ -475,10 +476,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (!v.isLambda())
|
if (!v.isLambda())
|
||||||
throw Error("bundler must be a function");
|
throw Error("bundler must be a function");
|
||||||
if (!v.lambda.fun->formals ||
|
// TODO: check types of inputs/outputs?
|
||||||
!v.lambda.fun->formals->argNames.count(state->symbols.create("program")) ||
|
|
||||||
!v.lambda.fun->formals->argNames.count(state->symbols.create("system")))
|
|
||||||
throw Error("bundler must take formal arguments 'program' and 'system'");
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
|
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
|
@ -609,14 +607,27 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
*attr.value, *attr.pos);
|
*attr.value, *attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultBundler")
|
else if (name == "defaultBundler") {
|
||||||
checkBundler(name, vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
|
for (auto & attr : *vOutput.attrs) {
|
||||||
|
checkSystemName(attr.name, *attr.pos);
|
||||||
|
checkBundler(
|
||||||
|
fmt("%s.%s", name, attr.name),
|
||||||
|
*attr.value, *attr.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (name == "bundlers") {
|
else if (name == "bundlers") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkBundler(fmt("%s.%s", name, attr.name),
|
checkSystemName(attr.name, *attr.pos);
|
||||||
*attr.value, *attr.pos);
|
state->forceAttrs(*attr.value, *attr.pos);
|
||||||
|
for (auto & attr2 : *attr.value->attrs) {
|
||||||
|
checkBundler(
|
||||||
|
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
||||||
|
*attr2.value, *attr2.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -638,12 +649,14 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Strings defaultTemplateAttrPathsPrefixes{"templates."};
|
||||||
|
static Strings defaultTemplateAttrPaths = {"defaultTemplate"};
|
||||||
|
|
||||||
struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
||||||
{
|
{
|
||||||
std::string templateUrl = "templates";
|
std::string templateUrl = "templates";
|
||||||
Path destDir;
|
Path destDir;
|
||||||
|
|
||||||
const Strings attrsPathPrefixes{"templates."};
|
|
||||||
const LockFlags lockFlags{ .writeLockFile = false };
|
const LockFlags lockFlags{ .writeLockFile = false };
|
||||||
|
|
||||||
CmdFlakeInitCommon()
|
CmdFlakeInitCommon()
|
||||||
|
@ -658,8 +671,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
||||||
completeFlakeRefWithFragment(
|
completeFlakeRefWithFragment(
|
||||||
getEvalState(),
|
getEvalState(),
|
||||||
lockFlags,
|
lockFlags,
|
||||||
attrsPathPrefixes,
|
defaultTemplateAttrPathsPrefixes,
|
||||||
{"defaultTemplate"},
|
defaultTemplateAttrPaths,
|
||||||
prefix);
|
prefix);
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
@ -674,9 +687,10 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
||||||
auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath("."));
|
auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath("."));
|
||||||
|
|
||||||
auto installable = InstallableFlake(nullptr,
|
auto installable = InstallableFlake(nullptr,
|
||||||
evalState, std::move(templateFlakeRef),
|
evalState, std::move(templateFlakeRef), templateName,
|
||||||
Strings{templateName == "" ? "defaultTemplate" : templateName},
|
defaultTemplateAttrPaths,
|
||||||
Strings(attrsPathPrefixes), lockFlags);
|
defaultTemplateAttrPathsPrefixes,
|
||||||
|
lockFlags);
|
||||||
|
|
||||||
auto [cursor, attrPath] = installable.getCursor(*evalState);
|
auto [cursor, attrPath] = installable.getCursor(*evalState);
|
||||||
|
|
||||||
|
|
|
@ -292,6 +292,12 @@ The following attributes are supported in `flake.nix`:
|
||||||
value (e.g. `packages.x86_64-linux` must be an attribute set of
|
value (e.g. `packages.x86_64-linux` must be an attribute set of
|
||||||
derivations built for the `x86_64-linux` platform).
|
derivations built for the `x86_64-linux` platform).
|
||||||
|
|
||||||
|
* `nixConfig`: a set of `nix.conf` options to be set when evaluating any
|
||||||
|
part of a flake. In the interests of security, only a small set of
|
||||||
|
whitelisted options (currently `bash-prompt`, `bash-prompt-suffix`,
|
||||||
|
and `flake-registry`) are allowed to be set without confirmation so long as
|
||||||
|
`accept-flake-config` is not set in the global configuration.
|
||||||
|
|
||||||
## Flake inputs
|
## Flake inputs
|
||||||
|
|
||||||
The attribute `inputs` specifies the dependencies of a flake, as an
|
The attribute `inputs` specifies the dependencies of a flake, as an
|
||||||
|
|
|
@ -76,7 +76,7 @@ the Nix store. Here are the recognised types of installables:
|
||||||
|
|
||||||
Note that the search will only include files indexed by git. In particular, files
|
Note that the search will only include files indexed by git. In particular, files
|
||||||
which are matched by `.gitignore` or have never been `git add`-ed will not be
|
which are matched by `.gitignore` or have never been `git add`-ed will not be
|
||||||
available in the flake. If this is undesireable, specify `path:<directory>` explicitly;
|
available in the flake. If this is undesirable, specify `path:<directory>` explicitly;
|
||||||
|
|
||||||
For example, if `/foo/bar` is a git repository with the following structure:
|
For example, if `/foo/bar` is a git repository with the following structure:
|
||||||
```
|
```
|
||||||
|
|
|
@ -20,7 +20,10 @@ struct CmdPingStore : StoreCommand
|
||||||
|
|
||||||
void run(ref<Store> store) override
|
void run(ref<Store> store) override
|
||||||
{
|
{
|
||||||
|
notice("Store URL: %s", store->getUri());
|
||||||
store->connect();
|
store->connect();
|
||||||
|
if (auto version = store->getVersion())
|
||||||
|
notice("Version: %s", *version);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,17 +28,17 @@ string resolveMirrorUrl(EvalState & state, string url)
|
||||||
Value vMirrors;
|
Value vMirrors;
|
||||||
// FIXME: use nixpkgs flake
|
// FIXME: use nixpkgs flake
|
||||||
state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
|
state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
|
||||||
state.forceAttrs(vMirrors);
|
state.forceAttrs(vMirrors, noPos);
|
||||||
|
|
||||||
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
|
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
|
||||||
if (mirrorList == vMirrors.attrs->end())
|
if (mirrorList == vMirrors.attrs->end())
|
||||||
throw Error("unknown mirror name '%s'", mirrorName);
|
throw Error("unknown mirror name '%s'", mirrorName);
|
||||||
state.forceList(*mirrorList->value);
|
state.forceList(*mirrorList->value, noPos);
|
||||||
|
|
||||||
if (mirrorList->value->listSize() < 1)
|
if (mirrorList->value->listSize() < 1)
|
||||||
throw Error("mirror URL '%s' did not expand to anything", url);
|
throw Error("mirror URL '%s' did not expand to anything", url);
|
||||||
|
|
||||||
auto mirror = state.forceString(*mirrorList->value->listElems()[0]);
|
string mirror(state.forceString(*mirrorList->value->listElems()[0]));
|
||||||
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
|
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + string(s, p + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,11 +196,11 @@ static int main_nix_prefetch_url(int argc, char * * argv)
|
||||||
Value vRoot;
|
Value vRoot;
|
||||||
state->evalFile(path, vRoot);
|
state->evalFile(path, vRoot);
|
||||||
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
|
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
|
||||||
state->forceAttrs(v);
|
state->forceAttrs(v, noPos);
|
||||||
|
|
||||||
/* Extract the URL. */
|
/* Extract the URL. */
|
||||||
auto & attr = v.attrs->need(state->symbols.create("urls"));
|
auto & attr = v.attrs->need(state->symbols.create("urls"));
|
||||||
state->forceList(*attr.value);
|
state->forceList(*attr.value, noPos);
|
||||||
if (attr.value->listSize() < 1)
|
if (attr.value->listSize() < 1)
|
||||||
throw Error("'urls' list is empty");
|
throw Error("'urls' list is empty");
|
||||||
url = state->forceString(*attr.value->listElems()[0]);
|
url = state->forceString(*attr.value->listElems()[0]);
|
||||||
|
|
|
@ -295,7 +295,11 @@ public:
|
||||||
expectArgs("elements", &_matchers);
|
expectArgs("elements", &_matchers);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::variant<size_t, Path, std::regex> Matcher;
|
struct RegexPattern {
|
||||||
|
std::string pattern;
|
||||||
|
std::regex reg;
|
||||||
|
};
|
||||||
|
typedef std::variant<size_t, Path, RegexPattern> Matcher;
|
||||||
|
|
||||||
std::vector<Matcher> getMatchers(ref<Store> store)
|
std::vector<Matcher> getMatchers(ref<Store> store)
|
||||||
{
|
{
|
||||||
|
@ -307,7 +311,7 @@ public:
|
||||||
else if (store->isStorePath(s))
|
else if (store->isStorePath(s))
|
||||||
res.push_back(s);
|
res.push_back(s);
|
||||||
else
|
else
|
||||||
res.push_back(std::regex(s, std::regex::extended | std::regex::icase));
|
res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -320,9 +324,9 @@ public:
|
||||||
if (*n == pos) return true;
|
if (*n == pos) return true;
|
||||||
} 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<std::regex>(&matcher)) {
|
} else if (auto regex = std::get_if<RegexPattern>(&matcher)) {
|
||||||
if (element.source
|
if (element.source
|
||||||
&& std::regex_match(element.source->attrPath, *regex))
|
&& std::regex_match(element.source->attrPath, regex->reg))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,16 +359,30 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem
|
||||||
|
|
||||||
for (size_t i = 0; i < oldManifest.elements.size(); ++i) {
|
for (size_t i = 0; i < oldManifest.elements.size(); ++i) {
|
||||||
auto & element(oldManifest.elements[i]);
|
auto & element(oldManifest.elements[i]);
|
||||||
if (!matches(*store, element, i, matchers))
|
if (!matches(*store, element, i, matchers)) {
|
||||||
newManifest.elements.push_back(std::move(element));
|
newManifest.elements.push_back(std::move(element));
|
||||||
|
} else {
|
||||||
|
notice("removing '%s'", element.describe());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: warn about unused matchers?
|
auto removedCount = oldManifest.elements.size() - newManifest.elements.size();
|
||||||
|
|
||||||
printInfo("removed %d packages, kept %d packages",
|
printInfo("removed %d packages, kept %d packages",
|
||||||
oldManifest.elements.size() - newManifest.elements.size(),
|
removedCount,
|
||||||
newManifest.elements.size());
|
newManifest.elements.size());
|
||||||
|
|
||||||
|
if (removedCount == 0) {
|
||||||
|
for (auto matcher: matchers) {
|
||||||
|
if (const size_t* index = std::get_if<size_t>(&matcher)){
|
||||||
|
warn("'%d' is not a valid index in profile", *index);
|
||||||
|
} else if (const Path* path = std::get_if<Path>(&matcher)){
|
||||||
|
warn("'%s' does not match any paths in profile", *path);
|
||||||
|
} else if (const RegexPattern* regex = std::get_if<RegexPattern>(&matcher)){
|
||||||
|
warn("'%s' does not match any packages in profile", regex->pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warn ("Try `nix profile list` to see the current profile.");
|
||||||
|
}
|
||||||
updateProfile(newManifest.build(store));
|
updateProfile(newManifest.build(store));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -405,6 +423,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
||||||
this,
|
this,
|
||||||
getEvalState(),
|
getEvalState(),
|
||||||
FlakeRef(element.source->originalRef),
|
FlakeRef(element.source->originalRef),
|
||||||
|
"",
|
||||||
{element.source->attrPath},
|
{element.source->attrPath},
|
||||||
{},
|
{},
|
||||||
lockFlags);
|
lockFlags);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
R"MdBoundary(
|
R"MdBoundary(
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
Display some informations about the given realisation
|
Display some information about the given realisation
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
|
|
|
@ -342,7 +342,7 @@ StringSet NixRepl::completePrefix(string prefix)
|
||||||
Expr * e = parseString(expr);
|
Expr * e = parseString(expr);
|
||||||
Value v;
|
Value v;
|
||||||
e->eval(*state, *env, v);
|
e->eval(*state, *env, v);
|
||||||
state->forceAttrs(v);
|
state->forceAttrs(v, noPos);
|
||||||
|
|
||||||
for (auto & i : *v.attrs) {
|
for (auto & i : *v.attrs) {
|
||||||
string name = i.name;
|
string name = i.name;
|
||||||
|
@ -463,7 +463,7 @@ bool NixRepl::processLine(string line)
|
||||||
if (v.type() == nPath || v.type() == nString) {
|
if (v.type() == nPath || v.type() == nString) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto filename = state->coerceToString(noPos, v, context);
|
auto filename = state->coerceToString(noPos, v, context);
|
||||||
pos.file = state->symbols.create(filename);
|
pos.file = state->symbols.create(*filename);
|
||||||
} else if (v.isLambda()) {
|
} else if (v.isLambda()) {
|
||||||
pos = v.lambda.fun->pos;
|
pos = v.lambda.fun->pos;
|
||||||
} else {
|
} else {
|
||||||
|
@ -623,6 +623,9 @@ void NixRepl::loadFile(const Path & path)
|
||||||
|
|
||||||
void NixRepl::loadFlake(const std::string & flakeRefS)
|
void NixRepl::loadFlake(const std::string & flakeRefS)
|
||||||
{
|
{
|
||||||
|
if (flakeRefS.empty())
|
||||||
|
throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)");
|
||||||
|
|
||||||
auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true);
|
auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true);
|
||||||
if (evalSettings.pureEval && !flakeRef.input.isImmutable())
|
if (evalSettings.pureEval && !flakeRef.input.isImmutable())
|
||||||
throw Error("cannot use ':load-flake' on mutable flake reference '%s' (use --impure to override)", flakeRefS);
|
throw Error("cannot use ':load-flake' on mutable flake reference '%s' (use --impure to override)", flakeRefS);
|
||||||
|
@ -673,7 +676,7 @@ void NixRepl::reloadFiles()
|
||||||
|
|
||||||
void NixRepl::addAttrsToScope(Value & attrs)
|
void NixRepl::addAttrsToScope(Value & attrs)
|
||||||
{
|
{
|
||||||
state->forceAttrs(attrs);
|
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); });
|
||||||
if (displ + attrs.attrs->size() >= envSize)
|
if (displ + attrs.attrs->size() >= envSize)
|
||||||
throw Error("environment full; cannot add more variables");
|
throw Error("environment full; cannot add more variables");
|
||||||
|
|
||||||
|
@ -712,7 +715,7 @@ void NixRepl::evalString(string s, Value & v)
|
||||||
{
|
{
|
||||||
Expr * e = parseString(s);
|
Expr * e = parseString(s);
|
||||||
e->eval(*state, *env, v);
|
e->eval(*state, *env, v);
|
||||||
state->forceValue(v);
|
state->forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -742,7 +745,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
str.flush();
|
str.flush();
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
state->forceValue(v);
|
state->forceValue(v, [&]() { return v.determinePos(noPos); });
|
||||||
|
|
||||||
switch (v.type()) {
|
switch (v.type()) {
|
||||||
|
|
||||||
|
|
25447
src/nlohmann/json.hpp
25447
src/nlohmann/json.hpp
File diff suppressed because it is too large
Load diff
|
@ -1,78 +0,0 @@
|
||||||
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
|
||||||
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
|
||||||
|
|
||||||
#include <cstdint> // int64_t, uint64_t
|
|
||||||
#include <map> // map
|
|
||||||
#include <memory> // allocator
|
|
||||||
#include <string> // string
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief namespace for Niels Lohmann
|
|
||||||
@see https://github.com/nlohmann
|
|
||||||
@since version 1.0.0
|
|
||||||
*/
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
@brief default JSONSerializer template argument
|
|
||||||
|
|
||||||
This serializer ignores the template arguments and uses ADL
|
|
||||||
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
|
||||||
for serialization.
|
|
||||||
*/
|
|
||||||
template<typename T = void, typename SFINAE = void>
|
|
||||||
struct adl_serializer;
|
|
||||||
|
|
||||||
template<template<typename U, typename V, typename... Args> class ObjectType =
|
|
||||||
std::map,
|
|
||||||
template<typename U, typename... Args> class ArrayType = std::vector,
|
|
||||||
class StringType = std::string, class BooleanType = bool,
|
|
||||||
class NumberIntegerType = std::int64_t,
|
|
||||||
class NumberUnsignedType = std::uint64_t,
|
|
||||||
class NumberFloatType = double,
|
|
||||||
template<typename U> class AllocatorType = std::allocator,
|
|
||||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
|
||||||
adl_serializer,
|
|
||||||
class BinaryType = std::vector<std::uint8_t>>
|
|
||||||
class basic_json;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief JSON Pointer
|
|
||||||
|
|
||||||
A JSON pointer defines a string syntax for identifying a specific value
|
|
||||||
within a JSON document. It can be used with functions `at` and
|
|
||||||
`operator[]`. Furthermore, JSON pointers are the base for JSON patches.
|
|
||||||
|
|
||||||
@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
|
|
||||||
|
|
||||||
@since version 2.0.0
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
class json_pointer;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief default JSON class
|
|
||||||
|
|
||||||
This type is the default specialization of the @ref basic_json class which
|
|
||||||
uses the standard template types.
|
|
||||||
|
|
||||||
@since version 1.0.0
|
|
||||||
*/
|
|
||||||
using json = basic_json<>;
|
|
||||||
|
|
||||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
|
||||||
struct ordered_map;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief ordered JSON class
|
|
||||||
|
|
||||||
This type preserves the insertion order of object keys.
|
|
||||||
|
|
||||||
@since version 3.9.0
|
|
||||||
*/
|
|
||||||
using ordered_json = basic_json<nlohmann::ordered_map>;
|
|
||||||
|
|
||||||
} // namespace nlohmann
|
|
||||||
|
|
||||||
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
|
|
@ -1,2 +0,0 @@
|
||||||
$(foreach i, $(wildcard src/nlohmann/*.hpp), \
|
|
||||||
$(eval $(call install-file-in, $(i), $(includedir)/nlohmann, 0644)))
|
|
|
@ -107,7 +107,7 @@ Path resolveSymlink(const Path & path)
|
||||||
auto target = readLink(path);
|
auto target = readLink(path);
|
||||||
return hasPrefix(target, "/")
|
return hasPrefix(target, "/")
|
||||||
? target
|
? target
|
||||||
: dirOf(path) + "/" + target;
|
: concatStrings(dirOf(path), "/", target);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<string> resolveTree(const Path & path, PathSet & deps)
|
std::set<string> resolveTree(const Path & path, PathSet & deps)
|
||||||
|
|
|
@ -50,6 +50,6 @@ with import ./config.nix;
|
||||||
|
|
||||||
fetchurl = import <nix/fetchurl.nix> {
|
fetchurl = import <nix/fetchurl.nix> {
|
||||||
url = "file://" + toString ./lang/eval-okay-xml.exp.xml;
|
url = "file://" + toString ./lang/eval-okay-xml.exp.xml;
|
||||||
sha256 = "0kg4sla7ihm8ijr8cb3117fhl99zrc2bwy1jrngsfmkh8bav4m0v";
|
sha256 = "sha256-behBlX+DQK/Pjvkuc8Tx68Jwi4E5v86wDq+ZLaHyhQE=";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
35
tests/flake-bundler.sh
Normal file
35
tests/flake-bundler.sh
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local
|
||||||
|
|
||||||
|
cp ./simple.nix ./simple.builder.sh ./config.nix $TEST_HOME
|
||||||
|
|
||||||
|
cd $TEST_HOME
|
||||||
|
|
||||||
|
cat <<EOF > flake.nix
|
||||||
|
{
|
||||||
|
outputs = {self}: {
|
||||||
|
bundlers.$system.simple = drv:
|
||||||
|
if drv?type && drv.type == "derivation"
|
||||||
|
then drv
|
||||||
|
else self.defaultPackage.$system;
|
||||||
|
defaultBundler.$system = self.bundlers.$system.simple;
|
||||||
|
defaultPackage.$system = import ./simple.nix;
|
||||||
|
defaultApp.$system = {
|
||||||
|
type = "app";
|
||||||
|
program = "\${import ./simple.nix}/hello";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
nix build .#
|
||||||
|
nix bundle --bundler .# .#
|
||||||
|
nix bundle --bundler .#defaultBundler.$system .#defaultPackage.$system
|
||||||
|
nix bundle --bundler .#bundlers.$system.simple .#defaultPackage.$system
|
||||||
|
|
||||||
|
nix bundle --bundler .#defaultBundler.$system .#defaultApp.$system
|
||||||
|
nix bundle --bundler .#bundlers.$system.simple .#defaultApp.$system
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
|
|
@ -730,6 +730,7 @@ cat > $flakeFollowsB/flake.nix <<EOF
|
||||||
description = "Flake B";
|
description = "Flake B";
|
||||||
inputs = {
|
inputs = {
|
||||||
foobar.url = "path:$flakeFollowsA/flakeE";
|
foobar.url = "path:$flakeFollowsA/flakeE";
|
||||||
|
goodoo.follows = "C/goodoo";
|
||||||
C = {
|
C = {
|
||||||
url = "path:./flakeC";
|
url = "path:./flakeC";
|
||||||
inputs.foobar.follows = "foobar";
|
inputs.foobar.follows = "foobar";
|
||||||
|
@ -744,6 +745,7 @@ cat > $flakeFollowsC/flake.nix <<EOF
|
||||||
description = "Flake C";
|
description = "Flake C";
|
||||||
inputs = {
|
inputs = {
|
||||||
foobar.url = "path:$flakeFollowsA/flakeE";
|
foobar.url = "path:$flakeFollowsA/flakeE";
|
||||||
|
goodoo.follows = "foobar";
|
||||||
};
|
};
|
||||||
outputs = { ... }: {};
|
outputs = { ... }: {};
|
||||||
}
|
}
|
||||||
|
@ -759,7 +761,7 @@ EOF
|
||||||
|
|
||||||
cat > $flakeFollowsE/flake.nix <<EOF
|
cat > $flakeFollowsE/flake.nix <<EOF
|
||||||
{
|
{
|
||||||
description = "Flake D";
|
description = "Flake E";
|
||||||
inputs = {};
|
inputs = {};
|
||||||
outputs = { ... }: {};
|
outputs = { ... }: {};
|
||||||
}
|
}
|
||||||
|
@ -768,6 +770,8 @@ EOF
|
||||||
git -C $flakeFollowsA add flake.nix flakeB/flake.nix \
|
git -C $flakeFollowsA add flake.nix flakeB/flake.nix \
|
||||||
flakeB/flakeC/flake.nix flakeD/flake.nix flakeE/flake.nix
|
flakeB/flakeC/flake.nix flakeD/flake.nix flakeE/flake.nix
|
||||||
|
|
||||||
|
nix flake metadata $flakeFollowsA
|
||||||
|
|
||||||
nix flake update $flakeFollowsA
|
nix flake update $flakeFollowsA
|
||||||
|
|
||||||
oldLock="$(cat "$flakeFollowsA/flake.lock")"
|
oldLock="$(cat "$flakeFollowsA/flake.lock")"
|
||||||
|
|
|
@ -31,9 +31,9 @@
|
||||||
<attr name="f">
|
<attr name="f">
|
||||||
<function>
|
<function>
|
||||||
<attrspat>
|
<attrspat>
|
||||||
<attr name="z" />
|
|
||||||
<attr name="x" />
|
<attr name="x" />
|
||||||
<attr name="y" />
|
<attr name="y" />
|
||||||
|
<attr name="z" />
|
||||||
</attrspat>
|
</attrspat>
|
||||||
</function>
|
</function>
|
||||||
</attr>
|
</attr>
|
||||||
|
|
|
@ -48,6 +48,7 @@ nix_tests = \
|
||||||
flakes.sh \
|
flakes.sh \
|
||||||
flake-local-settings.sh \
|
flake-local-settings.sh \
|
||||||
flake-searching.sh \
|
flake-searching.sh \
|
||||||
|
flake-bundler.sh \
|
||||||
build.sh \
|
build.sh \
|
||||||
repl.sh ca/repl.sh \
|
repl.sh ca/repl.sh \
|
||||||
ca/build.sh \
|
ca/build.sh \
|
||||||
|
@ -62,6 +63,8 @@ nix_tests = \
|
||||||
ca/nix-copy.sh \
|
ca/nix-copy.sh \
|
||||||
eval-store.sh \
|
eval-store.sh \
|
||||||
readfile-context.sh \
|
readfile-context.sh \
|
||||||
|
store-ping.sh \
|
||||||
|
nix_path.sh \
|
||||||
why-depends.sh
|
why-depends.sh
|
||||||
# parallel.sh
|
# parallel.sh
|
||||||
|
|
||||||
|
|
11
tests/nix_path.sh
Normal file
11
tests/nix_path.sh
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Regression for https://github.com/NixOS/nix/issues/5998 and https://github.com/NixOS/nix/issues/5980
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
export NIX_PATH=non-existent=/non-existent/but-unused-anyways:by-absolute-path=$PWD:by-relative-path=.
|
||||||
|
|
||||||
|
nix-instantiate --eval -E '<by-absolute-path/simple.nix>' --restrict-eval
|
||||||
|
nix-instantiate --eval -E '<by-relative-path/simple.nix>' --restrict-eval
|
||||||
|
|
||||||
|
# Should ideally also test this, but there’s no pure way to do it, so just trust me that it works
|
||||||
|
# nix-instantiate --eval -E '<nixpkgs>' -I nixpkgs=channel:nixos-unstable --restrict-eval
|
|
@ -40,3 +40,26 @@ testRepl () {
|
||||||
testRepl
|
testRepl
|
||||||
# Same thing (kind-of), but with a remote store.
|
# Same thing (kind-of), but with a remote store.
|
||||||
testRepl --store "$TEST_ROOT/store?real=$NIX_STORE_DIR"
|
testRepl --store "$TEST_ROOT/store?real=$NIX_STORE_DIR"
|
||||||
|
|
||||||
|
testReplResponse () {
|
||||||
|
local response="$(nix repl <<< "$1")"
|
||||||
|
echo "$response" | grep -qs "$2" \
|
||||||
|
|| fail "repl command set:
|
||||||
|
|
||||||
|
$1
|
||||||
|
|
||||||
|
does not respond with:
|
||||||
|
|
||||||
|
$2
|
||||||
|
|
||||||
|
but with:
|
||||||
|
|
||||||
|
$response"
|
||||||
|
}
|
||||||
|
|
||||||
|
# :a uses the newest version of a symbol
|
||||||
|
testReplResponse '
|
||||||
|
:a { a = "1"; }
|
||||||
|
:a { a = "2"; }
|
||||||
|
"result: ${a}"
|
||||||
|
' "result: 2"
|
||||||
|
|
13
tests/store-ping.sh
Normal file
13
tests/store-ping.sh
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
STORE_INFO=$(nix store ping 2>&1)
|
||||||
|
|
||||||
|
echo "$STORE_INFO" | grep "Store URL: ${NIX_REMOTE}"
|
||||||
|
|
||||||
|
if [[ -v NIX_DAEMON_PACKAGE ]] && isDaemonNewer "2.7.0pre20220126"; then
|
||||||
|
DAEMON_VERSION=$($NIX_DAEMON_PACKAGE/bin/nix-daemon --version | cut -d' ' -f3)
|
||||||
|
echo "$STORE_INFO" | grep "Version: $DAEMON_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
expect 127 NIX_REMOTE=unix:$PWD/store nix store ping || \
|
||||||
|
fail "nix store ping on a non-existent store should fail"
|
Loading…
Reference in a new issue