forked from lix-project/lix
Merge branch 'master' into debug-merge-master
This commit is contained in:
commit
6e19947993
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -79,6 +79,7 @@ perl/Makefile.config
|
||||||
/tests/shell.drv
|
/tests/shell.drv
|
||||||
/tests/config.nix
|
/tests/config.nix
|
||||||
/tests/ca/config.nix
|
/tests/ca/config.nix
|
||||||
|
/tests/repl-result-out
|
||||||
|
|
||||||
# /tests/lang/
|
# /tests/lang/
|
||||||
/tests/lang/*.out
|
/tests/lang/*.out
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
- [CLI guideline](contributing/cli-guideline.md)
|
- [CLI guideline](contributing/cli-guideline.md)
|
||||||
- [Release Notes](release-notes/release-notes.md)
|
- [Release Notes](release-notes/release-notes.md)
|
||||||
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
- [Release X.Y (202?-??-??)](release-notes/rl-next.md)
|
||||||
|
- [Release 2.8 (2022-04-19)](release-notes/rl-2.8.md)
|
||||||
- [Release 2.7 (2022-03-07)](release-notes/rl-2.7.md)
|
- [Release 2.7 (2022-03-07)](release-notes/rl-2.7.md)
|
||||||
- [Release 2.6 (2022-01-24)](release-notes/rl-2.6.md)
|
- [Release 2.6 (2022-01-24)](release-notes/rl-2.6.md)
|
||||||
- [Release 2.5 (2021-12-13)](release-notes/rl-2.5.md)
|
- [Release 2.5 (2021-12-13)](release-notes/rl-2.5.md)
|
||||||
|
|
53
doc/manual/src/release-notes/rl-2.8.md
Normal file
53
doc/manual/src/release-notes/rl-2.8.md
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# Release 2.8 (2022-04-19)
|
||||||
|
|
||||||
|
* New experimental command: `nix fmt`, which applies a formatter
|
||||||
|
defined by the `formatter.<system>` flake output to the Nix
|
||||||
|
expressions in a flake.
|
||||||
|
|
||||||
|
* Various Nix commands can now read expressions from standard input
|
||||||
|
using `--file -`.
|
||||||
|
|
||||||
|
* New experimental builtin function `builtins.fetchClosure` that
|
||||||
|
copies a closure from a binary cache at evaluation time and rewrites
|
||||||
|
it to content-addressed form (if it isn't already). Like
|
||||||
|
`builtins.storePath`, this allows importing pre-built store paths;
|
||||||
|
the difference is that it doesn't require the user to configure
|
||||||
|
binary caches and trusted public keys.
|
||||||
|
|
||||||
|
This function is only available if you enable the experimental
|
||||||
|
feature `fetch-closure`.
|
||||||
|
|
||||||
|
* New experimental feature: *impure derivations*. These are
|
||||||
|
derivations that can produce a different result every time they're
|
||||||
|
built. Here is an example:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
name = "impure";
|
||||||
|
__impure = true; # marks this derivation as impure
|
||||||
|
buildCommand = "date > $out";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Running `nix build` twice on this expression will build the
|
||||||
|
derivation twice, producing two different content-addressed store
|
||||||
|
paths. Like fixed-output derivations, impure derivations have access
|
||||||
|
to the network. Only fixed-output derivations and impure derivations
|
||||||
|
can depend on an impure derivation.
|
||||||
|
|
||||||
|
* `nix store make-content-addressable` has been renamed to `nix store
|
||||||
|
make-content-addressed`.
|
||||||
|
|
||||||
|
* The `nixosModule` flake output attribute has been renamed consistent
|
||||||
|
with the `.default` renames in Nix 2.7.
|
||||||
|
|
||||||
|
* `nixosModule` → `nixosModules.default`
|
||||||
|
|
||||||
|
As before, the old output will continue to work, but `nix flake check` will
|
||||||
|
issue a warning about it.
|
||||||
|
|
||||||
|
* `nix run` is now stricter in what it accepts: members of the `apps`
|
||||||
|
flake output are now required to be apps (as defined in [the
|
||||||
|
manual](https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-run.html#apps)),
|
||||||
|
and members of `packages` or `legacyPackages` must be derivations
|
||||||
|
(not apps).
|
|
@ -1,42 +1,16 @@
|
||||||
# Release X.Y (202?-??-??)
|
# Release X.Y (202?-??-??)
|
||||||
|
|
||||||
* Various nix commands can now read expressions from stdin with `--file -`.
|
* Nix now provides better integration with zsh's run-help feature. It is now
|
||||||
|
included in the Nix installation in the form of an autoloadable shell
|
||||||
|
function, run-help-nix. It picks up Nix subcommands from the currently typed
|
||||||
|
in command and directs the user to the associated man pages.
|
||||||
|
|
||||||
* `nix store make-content-addressable` has been renamed to `nix store
|
* `nix repl` has a new build-'n-link (`:bl`) command that builds a derivation
|
||||||
make-content-addressed`.
|
while creating GC root symlinks.
|
||||||
|
|
||||||
* New experimental builtin function `builtins.fetchClosure` that
|
* The path produced by `builtins.toFile` is now allowed to be imported or read
|
||||||
copies a closure from a binary cache at evaluation time and rewrites
|
even with restricted evaluation. Note that this will not work with a
|
||||||
it to content-addressed form (if it isn't already). Like
|
read-only store.
|
||||||
`builtins.storePath`, this allows importing pre-built store paths;
|
|
||||||
the difference is that it doesn't require the user to configure
|
|
||||||
binary caches and trusted public keys.
|
|
||||||
|
|
||||||
This function is only available if you enable the experimental
|
* `nix build` has a new `--print-out-paths` flag to print the resulting output paths.
|
||||||
feature `fetch-closure`.
|
This matches the default behaviour of `nix-build`.
|
||||||
|
|
||||||
* New experimental feature: *impure derivations*. These are
|
|
||||||
derivations that can produce a different result every time they're
|
|
||||||
built. Here is an example:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
stdenv.mkDerivation {
|
|
||||||
name = "impure";
|
|
||||||
__impure = true; # marks this derivation as impure
|
|
||||||
buildCommand = "date > $out";
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Running `nix build` twice on this expression will build the
|
|
||||||
derivation twice, producing two different content-addressed store
|
|
||||||
paths. Like fixed-output derivations, impure derivations have access
|
|
||||||
to the network. Only fixed-output derivations and impure derivations
|
|
||||||
can depend on an impure derivation.
|
|
||||||
|
|
||||||
* The `nixosModule` flake output attribute has been renamed consistent
|
|
||||||
with the `.default` renames in nix 2.7.
|
|
||||||
|
|
||||||
* `nixosModule` → `nixosModules.default`
|
|
||||||
|
|
||||||
As before, the old output will continue to work, but `nix flake check` will
|
|
||||||
issue a warning about it.
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ let
|
||||||
findutils
|
findutils
|
||||||
iana-etc
|
iana-etc
|
||||||
git
|
git
|
||||||
|
openssh
|
||||||
];
|
];
|
||||||
|
|
||||||
users = {
|
users = {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
crossSystems = [ "armv6l-linux" "armv7l-linux" ];
|
||||||
|
|
||||||
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" ];
|
stdenvs = [ "gccStdenv" "clangStdenv" "clang11Stdenv" "stdenv" "libcxxStdenv" ];
|
||||||
|
|
||||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
||||||
forAllSystemsAndStdenvs = f: forAllSystems (system:
|
forAllSystemsAndStdenvs = f: forAllSystems (system:
|
||||||
|
|
|
@ -15,7 +15,7 @@ function _complete_nix {
|
||||||
else
|
else
|
||||||
COMPREPLY+=("$completion")
|
COMPREPLY+=("$completion")
|
||||||
fi
|
fi
|
||||||
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}" 2>/dev/null)
|
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}" 2>/dev/null)
|
||||||
__ltrim_colon_completions "$cur"
|
__ltrim_colon_completions "$cur"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
$(eval $(call install-file-as, $(d)/completion.zsh, $(datarootdir)/zsh/site-functions/_nix, 0644))
|
$(eval $(call install-file-as, $(d)/completion.zsh, $(datarootdir)/zsh/site-functions/_nix, 0644))
|
||||||
|
$(eval $(call install-file-as, $(d)/run-help-nix, $(datarootdir)/zsh/site-functions/run-help-nix, 0644))
|
||||||
|
|
42
misc/zsh/run-help-nix
Normal file
42
misc/zsh/run-help-nix
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
emulate -L zsh
|
||||||
|
|
||||||
|
# run-help is a zsh widget that can be bound to a key. It mainly looks up the
|
||||||
|
# man page for the currently typed in command.
|
||||||
|
#
|
||||||
|
# Although run-help works for any command without requiring special support,
|
||||||
|
# it can only deduce the right man page based solely on the name of the
|
||||||
|
# command. Programs like Nix provide better integration with run-help by
|
||||||
|
# helping zsh identify Nix subcommands and their corresponding man pages. This
|
||||||
|
# is what this function does.
|
||||||
|
#
|
||||||
|
# To actually use run-help on zsh, place the following lines in your .zshrc:
|
||||||
|
#
|
||||||
|
# (( $+aliases[run-help] )) && unalias run-help
|
||||||
|
# autoload -Uz run-help run-help-nix
|
||||||
|
#
|
||||||
|
# Then also assign run-help to any key of choice:
|
||||||
|
#
|
||||||
|
# bindkey '^[h' run-help
|
||||||
|
|
||||||
|
while [[ "$#" != 0 && "$1" == -* ]]; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
local -a subcommands; subcommands=( nix3 )
|
||||||
|
|
||||||
|
local arg
|
||||||
|
for arg in "$@"; do
|
||||||
|
if man -w "${(j:-:)subcommands}-$arg" >/dev/null 2>&1; then
|
||||||
|
subcommands+="$arg"
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( $#subcommands > 1 )); then
|
||||||
|
man "${(j:-:)subcommands}"
|
||||||
|
else
|
||||||
|
man nix
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $?
|
|
@ -228,17 +228,17 @@ void StorePathCommand::run(ref<Store> store, std::vector<StorePath> && storePath
|
||||||
run(store, *storePaths.begin());
|
run(store, *storePaths.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings editorFor(const Pos & pos)
|
Strings editorFor(const Path & file, uint32_t line)
|
||||||
{
|
{
|
||||||
auto editor = getEnv("EDITOR").value_or("cat");
|
auto editor = getEnv("EDITOR").value_or("cat");
|
||||||
auto args = tokenizeString<Strings>(editor);
|
auto args = tokenizeString<Strings>(editor);
|
||||||
if (pos.line > 0 && (
|
if (line > 0 && (
|
||||||
editor.find("emacs") != std::string::npos ||
|
editor.find("emacs") != std::string::npos ||
|
||||||
editor.find("nano") != std::string::npos ||
|
editor.find("nano") != std::string::npos ||
|
||||||
editor.find("vim") != std::string::npos ||
|
editor.find("vim") != std::string::npos ||
|
||||||
editor.find("kak") != std::string::npos))
|
editor.find("kak") != std::string::npos))
|
||||||
args.push_back(fmt("+%d", pos.line));
|
args.push_back(fmt("+%d", line));
|
||||||
args.push_back(pos.file);
|
args.push_back(file);
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand
|
||||||
|
|
||||||
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
||||||
{
|
{
|
||||||
return parseFlakeRef(_installable, absPath("."));
|
return parseFlakeRefWithFragment(_installable, absPath(".")).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -221,7 +221,7 @@ static RegisterCommand registerCommand2(std::vector<std::string> && name)
|
||||||
|
|
||||||
/* Helper function to generate args that invoke $EDITOR on
|
/* Helper function to generate args that invoke $EDITOR on
|
||||||
filename:lineno. */
|
filename:lineno. */
|
||||||
Strings editorFor(const Pos & pos);
|
Strings editorFor(const Path & file, uint32_t line);
|
||||||
|
|
||||||
struct MixProfile : virtual StoreCommand
|
struct MixProfile : virtual StoreCommand
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "registry.hh"
|
#include "registry.hh"
|
||||||
#include "flake/flakeref.hh"
|
#include "flake/flakeref.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "command.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -59,6 +60,9 @@ MixEvalArgs::MixEvalArgs()
|
||||||
fetchers::Attrs extraAttrs;
|
fetchers::Attrs extraAttrs;
|
||||||
if (to.subdir != "") extraAttrs["dir"] = to.subdir;
|
if (to.subdir != "") extraAttrs["dir"] = to.subdir;
|
||||||
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
|
fetchers::overrideRegistry(from.input, to.input, extraAttrs);
|
||||||
|
}},
|
||||||
|
.completer = {[&](size_t, std::string_view prefix) {
|
||||||
|
completeFlakeRef(openStore(), prefix);
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "installables.hh"
|
#include "installables.hh"
|
||||||
|
#include "util.hh"
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "common-eval-args.hh"
|
#include "common-eval-args.hh"
|
||||||
|
@ -100,6 +101,14 @@ MixFlakeOptions::MixFlakeOptions()
|
||||||
lockFlags.inputOverrides.insert_or_assign(
|
lockFlags.inputOverrides.insert_or_assign(
|
||||||
flake::parseInputPath(inputPath),
|
flake::parseInputPath(inputPath),
|
||||||
parseFlakeRef(flakeRef, absPath("."), true));
|
parseFlakeRef(flakeRef, absPath("."), true));
|
||||||
|
}},
|
||||||
|
.completer = {[&](size_t n, std::string_view prefix) {
|
||||||
|
if (n == 0) {
|
||||||
|
if (auto flakeRef = getFlakeRefForCompletion())
|
||||||
|
completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
|
||||||
|
} else if (n == 1) {
|
||||||
|
completeFlakeRef(getEvalState()->store, prefix);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -194,6 +203,8 @@ Strings SourceExprCommand::getDefaultFlakeAttrPathPrefixes()
|
||||||
void SourceExprCommand::completeInstallable(std::string_view prefix)
|
void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||||
{
|
{
|
||||||
if (file) {
|
if (file) {
|
||||||
|
completionType = ctAttrs;
|
||||||
|
|
||||||
evalSettings.pureEval = false;
|
evalSettings.pureEval = false;
|
||||||
auto state = getEvalState();
|
auto state = getEvalState();
|
||||||
Expr *e = state->parseExprFromFile(
|
Expr *e = state->parseExprFromFile(
|
||||||
|
@ -222,13 +233,14 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||||
Value v2;
|
Value v2;
|
||||||
state->autoCallFunction(*autoArgs, v1, v2);
|
state->autoCallFunction(*autoArgs, v1, v2);
|
||||||
|
|
||||||
completionType = ctAttrs;
|
|
||||||
|
|
||||||
if (v2.type() == nAttrs) {
|
if (v2.type() == nAttrs) {
|
||||||
for (auto & i : *v2.attrs) {
|
for (auto & i : *v2.attrs) {
|
||||||
std::string name = i.name;
|
std::string name = state->symbols[i.name];
|
||||||
if (name.find(searchWord) == 0) {
|
if (name.find(searchWord) == 0) {
|
||||||
completions->add(i.name);
|
if (prefix_ == "")
|
||||||
|
completions->add(name);
|
||||||
|
else
|
||||||
|
completions->add(prefix_ + "." + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,10 +268,11 @@ void completeFlakeRefWithFragment(
|
||||||
if (hash == std::string::npos) {
|
if (hash == std::string::npos) {
|
||||||
completeFlakeRef(evalState->store, prefix);
|
completeFlakeRef(evalState->store, prefix);
|
||||||
} else {
|
} else {
|
||||||
|
completionType = ctAttrs;
|
||||||
|
|
||||||
auto fragment = prefix.substr(hash + 1);
|
auto fragment = prefix.substr(hash + 1);
|
||||||
auto flakeRefS = std::string(prefix.substr(0, hash));
|
auto flakeRefS = std::string(prefix.substr(0, hash));
|
||||||
// FIXME: do tilde expansion.
|
auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
|
||||||
auto flakeRef = parseFlakeRef(flakeRefS, absPath("."));
|
|
||||||
|
|
||||||
auto evalCache = openEvalCache(*evalState,
|
auto evalCache = openEvalCache(*evalState,
|
||||||
std::make_shared<flake::LockedFlake>(lockFlake(*evalState, flakeRef, lockFlags)));
|
std::make_shared<flake::LockedFlake>(lockFlake(*evalState, flakeRef, lockFlags)));
|
||||||
|
@ -271,8 +284,6 @@ void completeFlakeRefWithFragment(
|
||||||
flake. */
|
flake. */
|
||||||
attrPathPrefixes.push_back("");
|
attrPathPrefixes.push_back("");
|
||||||
|
|
||||||
completionType = ctAttrs;
|
|
||||||
|
|
||||||
for (auto & attrPathPrefixS : attrPathPrefixes) {
|
for (auto & attrPathPrefixS : attrPathPrefixes) {
|
||||||
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
|
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
|
||||||
auto attrPathS = attrPathPrefixS + std::string(fragment);
|
auto attrPathS = attrPathPrefixS + std::string(fragment);
|
||||||
|
@ -280,7 +291,7 @@ void completeFlakeRefWithFragment(
|
||||||
|
|
||||||
std::string lastAttr;
|
std::string lastAttr;
|
||||||
if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) {
|
if (!attrPath.empty() && !hasSuffix(attrPathS, ".")) {
|
||||||
lastAttr = attrPath.back();
|
lastAttr = evalState->symbols[attrPath.back()];
|
||||||
attrPath.pop_back();
|
attrPath.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,11 +299,11 @@ void completeFlakeRefWithFragment(
|
||||||
if (!attr) continue;
|
if (!attr) continue;
|
||||||
|
|
||||||
for (auto & attr2 : (*attr)->getAttrs()) {
|
for (auto & attr2 : (*attr)->getAttrs()) {
|
||||||
if (hasPrefix(attr2, lastAttr)) {
|
if (hasPrefix(evalState->symbols[attr2], lastAttr)) {
|
||||||
auto attrPath2 = (*attr)->getAttrPath(attr2);
|
auto attrPath2 = (*attr)->getAttrPath(attr2);
|
||||||
/* Strip the attrpath prefix. */
|
/* Strip the attrpath prefix. */
|
||||||
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
|
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
|
||||||
completions->add(flakeRefS + "#" + concatStringsSep(".", attrPath2));
|
completions->add(flakeRefS + "#" + concatStringsSep(".", evalState->symbols.resolve(attrPath2)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -346,16 +357,16 @@ DerivedPath Installable::toDerivedPath()
|
||||||
return std::move(buildables[0]);
|
return std::move(buildables[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
std::vector<ref<eval_cache::AttrCursor>>
|
||||||
Installable::getCursors(EvalState & state)
|
Installable::getCursors(EvalState & state)
|
||||||
{
|
{
|
||||||
auto evalCache =
|
auto evalCache =
|
||||||
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state,
|
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state,
|
||||||
[&]() { return toValue(state).first; });
|
[&]() { return toValue(state).first; });
|
||||||
return {{evalCache->getRoot(), ""}};
|
return {evalCache->getRoot()};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
|
ref<eval_cache::AttrCursor>
|
||||||
Installable::getCursor(EvalState & state)
|
Installable::getCursor(EvalState & state)
|
||||||
{
|
{
|
||||||
auto cursors = getCursors(state);
|
auto cursors = getCursors(state);
|
||||||
|
@ -462,7 +473,7 @@ struct InstallableAttrPath : InstallableValue
|
||||||
|
|
||||||
std::string what() const override { return attrPath; }
|
std::string what() const override { return attrPath; }
|
||||||
|
|
||||||
std::pair<Value *, Pos> toValue(EvalState & state) override
|
std::pair<Value *, PosIdx> 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, pos);
|
state.forceValue(*vRes, pos);
|
||||||
|
@ -578,43 +589,21 @@ InstallableFlake::InstallableFlake(
|
||||||
|
|
||||||
std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation()
|
std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation()
|
||||||
{
|
{
|
||||||
auto lockedFlake = getLockedFlake();
|
auto attr = getCursor(*state);
|
||||||
|
|
||||||
auto cache = openEvalCache(*state, lockedFlake);
|
auto attrPath = attr->getAttrPathStr();
|
||||||
auto root = cache->getRoot();
|
|
||||||
|
|
||||||
Suggestions suggestions;
|
if (!attr->isDerivation())
|
||||||
|
throw Error("flake output attribute '%s' is not a derivation", attrPath);
|
||||||
|
|
||||||
for (auto & attrPath : getActualAttrPaths()) {
|
auto drvPath = attr->forceDerivation();
|
||||||
debug("trying flake output attribute '%s'", attrPath);
|
|
||||||
|
|
||||||
auto attrOrSuggestions = root->findAlongAttrPath(
|
auto drvInfo = DerivationInfo {
|
||||||
parseAttrPath(*state, attrPath),
|
std::move(drvPath),
|
||||||
true
|
attr->getAttr(state->sOutputName)->getString()
|
||||||
);
|
};
|
||||||
|
|
||||||
if (!attrOrSuggestions) {
|
return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
|
||||||
suggestions += attrOrSuggestions.getSuggestions();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto attr = *attrOrSuggestions;
|
|
||||||
|
|
||||||
if (!attr->isDerivation())
|
|
||||||
throw Error("flake output attribute '%s' is not a derivation", attrPath);
|
|
||||||
|
|
||||||
auto drvPath = attr->forceDerivation();
|
|
||||||
|
|
||||||
auto drvInfo = DerivationInfo {
|
|
||||||
std::move(drvPath),
|
|
||||||
attr->getAttr(state->sOutputName)->getString()
|
|
||||||
};
|
|
||||||
|
|
||||||
return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Error(suggestions, "flake '%s' does not provide attribute %s",
|
|
||||||
flakeRef, showAttrPaths(getActualAttrPaths()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||||
|
@ -624,35 +613,12 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
|
||||||
{
|
{
|
||||||
auto lockedFlake = getLockedFlake();
|
return {&getCursor(state)->forceValue(), noPos};
|
||||||
|
|
||||||
auto vOutputs = getFlakeOutputs(state, *lockedFlake);
|
|
||||||
|
|
||||||
auto emptyArgs = state.allocBindings(0);
|
|
||||||
|
|
||||||
Suggestions suggestions;
|
|
||||||
|
|
||||||
for (auto & attrPath : getActualAttrPaths()) {
|
|
||||||
try {
|
|
||||||
auto [v, pos] = findAlongAttrPath(state, attrPath, *emptyArgs, *vOutputs);
|
|
||||||
state.forceValue(*v, pos);
|
|
||||||
return {v, pos};
|
|
||||||
} catch (AttrPathNotFound & e) {
|
|
||||||
suggestions += e.info().suggestions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Error(
|
|
||||||
suggestions,
|
|
||||||
"flake '%s' does not provide attribute %s",
|
|
||||||
flakeRef,
|
|
||||||
showAttrPaths(getActualAttrPaths())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
std::vector<ref<eval_cache::AttrCursor>>
|
||||||
InstallableFlake::getCursors(EvalState & state)
|
InstallableFlake::getCursors(EvalState & state)
|
||||||
{
|
{
|
||||||
auto evalCache = openEvalCache(state,
|
auto evalCache = openEvalCache(state,
|
||||||
|
@ -660,21 +626,55 @@ InstallableFlake::getCursors(EvalState & state)
|
||||||
|
|
||||||
auto root = evalCache->getRoot();
|
auto root = evalCache->getRoot();
|
||||||
|
|
||||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>> res;
|
std::vector<ref<eval_cache::AttrCursor>> res;
|
||||||
|
|
||||||
for (auto & attrPath : getActualAttrPaths()) {
|
for (auto & attrPath : getActualAttrPaths()) {
|
||||||
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
||||||
if (attr) res.push_back({*attr, attrPath});
|
if (attr) res.push_back(ref(*attr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref<eval_cache::AttrCursor> InstallableFlake::getCursor(EvalState & state)
|
||||||
|
{
|
||||||
|
auto lockedFlake = getLockedFlake();
|
||||||
|
|
||||||
|
auto cache = openEvalCache(state, lockedFlake);
|
||||||
|
auto root = cache->getRoot();
|
||||||
|
|
||||||
|
Suggestions suggestions;
|
||||||
|
|
||||||
|
auto attrPaths = getActualAttrPaths();
|
||||||
|
|
||||||
|
for (auto & attrPath : attrPaths) {
|
||||||
|
debug("trying flake output attribute '%s'", attrPath);
|
||||||
|
|
||||||
|
auto attrOrSuggestions = root->findAlongAttrPath(
|
||||||
|
parseAttrPath(state, attrPath),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!attrOrSuggestions) {
|
||||||
|
suggestions += attrOrSuggestions.getSuggestions();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *attrOrSuggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error(
|
||||||
|
suggestions,
|
||||||
|
"flake '%s' does not provide attribute %s",
|
||||||
|
flakeRef,
|
||||||
|
showAttrPaths(attrPaths));
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
|
||||||
{
|
{
|
||||||
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
|
||||||
lockFlagsApplyConfig.applyNixConfig = true;
|
|
||||||
if (!_lockedFlake) {
|
if (!_lockedFlake) {
|
||||||
|
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||||
|
lockFlagsApplyConfig.applyNixConfig = true;
|
||||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
||||||
}
|
}
|
||||||
return _lockedFlake;
|
return _lockedFlake;
|
||||||
|
@ -831,8 +831,8 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::bui
|
||||||
auto realisation = store->queryRealisation(outputId);
|
auto realisation = store->queryRealisation(outputId);
|
||||||
if (!realisation)
|
if (!realisation)
|
||||||
throw Error(
|
throw Error(
|
||||||
"cannot operate on an output of unbuilt "
|
"cannot operate on an output of the "
|
||||||
"content-addressed derivation '%s'",
|
"unbuilt derivation '%s'",
|
||||||
outputId.to_string());
|
outputId.to_string());
|
||||||
outputs.insert_or_assign(output, realisation->outPath);
|
outputs.insert_or_assign(output, realisation->outPath);
|
||||||
} else {
|
} else {
|
||||||
|
@ -980,10 +980,10 @@ std::optional<FlakeRef> InstallablesCommand::getFlakeRefForCompletion()
|
||||||
{
|
{
|
||||||
if (_installables.empty()) {
|
if (_installables.empty()) {
|
||||||
if (useDefaultInstallables())
|
if (useDefaultInstallables())
|
||||||
return parseFlakeRef(".", absPath("."));
|
return parseFlakeRefWithFragment(".", absPath(".")).first;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return parseFlakeRef(_installables.front(), absPath("."));
|
return parseFlakeRefWithFragment(_installables.front(), absPath(".")).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
|
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
|
||||||
|
|
|
@ -68,7 +68,7 @@ struct Installable
|
||||||
|
|
||||||
UnresolvedApp toApp(EvalState & state);
|
UnresolvedApp toApp(EvalState & state);
|
||||||
|
|
||||||
virtual std::pair<Value *, Pos> toValue(EvalState & state)
|
virtual std::pair<Value *, PosIdx> toValue(EvalState & state)
|
||||||
{
|
{
|
||||||
throw Error("argument '%s' cannot be evaluated", what());
|
throw Error("argument '%s' cannot be evaluated", what());
|
||||||
}
|
}
|
||||||
|
@ -80,10 +80,10 @@ struct Installable
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
virtual std::vector<ref<eval_cache::AttrCursor>>
|
||||||
getCursors(EvalState & state);
|
getCursors(EvalState & state);
|
||||||
|
|
||||||
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
|
virtual ref<eval_cache::AttrCursor>
|
||||||
getCursor(EvalState & state);
|
getCursor(EvalState & state);
|
||||||
|
|
||||||
virtual FlakeRef nixpkgsFlakeRef() const
|
virtual FlakeRef nixpkgsFlakeRef() const
|
||||||
|
@ -178,11 +178,17 @@ struct InstallableFlake : InstallableValue
|
||||||
|
|
||||||
std::vector<DerivationInfo> toDerivations() override;
|
std::vector<DerivationInfo> toDerivations() override;
|
||||||
|
|
||||||
std::pair<Value *, Pos> toValue(EvalState & state) override;
|
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
|
||||||
|
|
||||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
/* Get a cursor to every attrpath in getActualAttrPaths() that
|
||||||
|
exists. */
|
||||||
|
std::vector<ref<eval_cache::AttrCursor>>
|
||||||
getCursors(EvalState & state) override;
|
getCursors(EvalState & state) override;
|
||||||
|
|
||||||
|
/* Get a cursor to the first attrpath in getActualAttrPaths() that
|
||||||
|
exists, or throw an exception with suggestions if none exists. */
|
||||||
|
ref<eval_cache::AttrCursor> getCursor(EvalState & state) override;
|
||||||
|
|
||||||
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
|
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
|
||||||
|
|
||||||
FlakeRef nixpkgsFlakeRef() const override;
|
FlakeRef nixpkgsFlakeRef() const override;
|
||||||
|
|
|
@ -33,6 +33,7 @@ extern "C" {
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
#include "markdown.hh"
|
#include "markdown.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
|
|
||||||
#if HAVE_BOEHMGC
|
#if HAVE_BOEHMGC
|
||||||
#define GC_INCLUDE_NEW
|
#define GC_INCLUDE_NEW
|
||||||
|
@ -75,7 +76,7 @@ struct NixRepl
|
||||||
void loadFiles();
|
void loadFiles();
|
||||||
void reloadFiles();
|
void reloadFiles();
|
||||||
void addAttrsToScope(Value & attrs);
|
void addAttrsToScope(Value & attrs);
|
||||||
void addVarToScope(const Symbol & name, Value & v);
|
void addVarToScope(const Symbol name, Value & v);
|
||||||
Expr * parseString(std::string s);
|
Expr * parseString(std::string s);
|
||||||
void evalString(std::string s, Value & v);
|
void evalString(std::string s, Value & v);
|
||||||
void loadDebugTraceEnv(DebugTrace &dt);
|
void loadDebugTraceEnv(DebugTrace &dt);
|
||||||
|
@ -124,7 +125,7 @@ std::string runNix(Path program, const Strings & args,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!statusOk(res.first))
|
if (!statusOk(res.first))
|
||||||
throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first)));
|
throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));
|
||||||
|
|
||||||
return res.second;
|
return res.second;
|
||||||
}
|
}
|
||||||
|
@ -389,9 +390,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
|
||||||
state->forceAttrs(v, noPos);
|
state->forceAttrs(v, noPos);
|
||||||
|
|
||||||
for (auto & i : *v.attrs) {
|
for (auto & i : *v.attrs) {
|
||||||
std::string name = i.name;
|
std::string_view name = state->symbols[i.name];
|
||||||
if (name.substr(0, cur2.size()) != cur2) continue;
|
if (name.substr(0, cur2.size()) != cur2) continue;
|
||||||
completions.insert(prev + expr + "." + name);
|
completions.insert(concatStrings(prev, expr, ".", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (ParseError & e) {
|
} catch (ParseError & e) {
|
||||||
|
@ -480,7 +481,8 @@ bool NixRepl::processLine(std::string line)
|
||||||
<< " <expr> Evaluate and print expression\n"
|
<< " <expr> Evaluate and print expression\n"
|
||||||
<< " <x> = <expr> Bind expression to variable\n"
|
<< " <x> = <expr> Bind expression to variable\n"
|
||||||
<< " :a <expr> Add attributes from resulting set to scope\n"
|
<< " :a <expr> Add attributes from resulting set to scope\n"
|
||||||
<< " :b <expr> Build derivation\n"
|
<< " :b <expr> Build a derivation\n"
|
||||||
|
<< " :bl <expr> Build a derivation, creating GC roots in the working directory\n"
|
||||||
<< " :e <expr> Open package or function in $EDITOR\n"
|
<< " :e <expr> Open package or function in $EDITOR\n"
|
||||||
<< " :i <expr> Build derivation, then install result into current profile\n"
|
<< " :i <expr> Build derivation, then install result into current profile\n"
|
||||||
<< " :l <path> Load Nix expression and add it to scope\n"
|
<< " :l <path> Load Nix expression and add it to scope\n"
|
||||||
|
@ -586,21 +588,23 @@ bool NixRepl::processLine(std::string line)
|
||||||
Value v;
|
Value v;
|
||||||
evalString(arg, v);
|
evalString(arg, v);
|
||||||
|
|
||||||
Pos pos;
|
const auto [file, line] = [&] () -> std::pair<std::string, uint32_t> {
|
||||||
|
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).toOwned();
|
||||||
auto filename = state->coerceToString(noPos, v, context);
|
state->symbols.create(filename);
|
||||||
pos.file = state->symbols.create(*filename);
|
return {filename, 0};
|
||||||
} else if (v.isLambda()) {
|
} else if (v.isLambda()) {
|
||||||
pos = v.lambda.fun->pos;
|
auto pos = state->positions[v.lambda.fun->pos];
|
||||||
} else {
|
return {pos.file, pos.line};
|
||||||
// assume it's a derivation
|
} else {
|
||||||
pos = findPackageFilename(*state, v, arg);
|
// assume it's a derivation
|
||||||
}
|
return findPackageFilename(*state, v, arg);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
// Open in EDITOR
|
// Open in EDITOR
|
||||||
auto args = editorFor(pos);
|
auto args = editorFor(file, line);
|
||||||
auto editor = args.front();
|
auto editor = args.front();
|
||||||
args.pop_front();
|
args.pop_front();
|
||||||
|
|
||||||
|
@ -623,24 +627,32 @@ bool NixRepl::processLine(std::string line)
|
||||||
Value v, f, result;
|
Value v, f, result;
|
||||||
evalString(arg, v);
|
evalString(arg, v);
|
||||||
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
|
evalString("drv: (import <nixpkgs> {}).runCommand \"shell\" { buildInputs = [ drv ]; } \"\"", f);
|
||||||
state->callFunction(f, v, result, Pos());
|
state->callFunction(f, v, result, PosIdx());
|
||||||
|
|
||||||
StorePath drvPath = getDerivationPath(result);
|
StorePath drvPath = getDerivationPath(result);
|
||||||
runNix("nix-shell", {state->store->printStorePath(drvPath)});
|
runNix("nix-shell", {state->store->printStorePath(drvPath)});
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (command == ":b" || command == ":i" || command == ":s" || command == ":log") {
|
else if (command == ":b" || command == ":bl" || command == ":i" || command == ":s" || command == ":log") {
|
||||||
Value v;
|
Value v;
|
||||||
evalString(arg, v);
|
evalString(arg, v);
|
||||||
StorePath drvPath = getDerivationPath(v);
|
StorePath drvPath = getDerivationPath(v);
|
||||||
Path drvPathRaw = state->store->printStorePath(drvPath);
|
Path drvPathRaw = state->store->printStorePath(drvPath);
|
||||||
|
|
||||||
if (command == ":b") {
|
if (command == ":b" || command == ":bl") {
|
||||||
state->store->buildPaths({DerivedPath::Built{drvPath}});
|
state->store->buildPaths({DerivedPath::Built{drvPath}});
|
||||||
auto drv = state->store->readDerivation(drvPath);
|
auto drv = state->store->readDerivation(drvPath);
|
||||||
logger->cout("\nThis derivation produced the following outputs:");
|
logger->cout("\nThis derivation produced the following outputs:");
|
||||||
for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath))
|
for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath)) {
|
||||||
logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath));
|
auto localStore = state->store.dynamic_pointer_cast<LocalFSStore>();
|
||||||
|
if (localStore && command == ":bl") {
|
||||||
|
std::string symlink = "repl-result-" + outputName;
|
||||||
|
localStore->addPermRoot(outputPath, absPath(symlink));
|
||||||
|
logger->cout(" ./%s -> %s", symlink, state->store->printStorePath(outputPath));
|
||||||
|
} else {
|
||||||
|
logger->cout(" %s -> %s", outputName, state->store->printStorePath(outputPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (command == ":i") {
|
} else if (command == ":i") {
|
||||||
runNix("nix-env", {"-i", drvPathRaw});
|
runNix("nix-env", {"-i", drvPathRaw});
|
||||||
} else if (command == ":log") {
|
} else if (command == ":log") {
|
||||||
|
@ -791,7 +803,7 @@ void NixRepl::initEnv()
|
||||||
|
|
||||||
varNames.clear();
|
varNames.clear();
|
||||||
for (auto & i : state->staticBaseEnv->vars)
|
for (auto & i : state->staticBaseEnv->vars)
|
||||||
varNames.insert(i.first);
|
varNames.emplace(state->symbols[i.first]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -827,7 +839,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
||||||
for (auto & i : *attrs.attrs) {
|
for (auto & i : *attrs.attrs) {
|
||||||
staticEnv->vars.emplace_back(i.name, displ);
|
staticEnv->vars.emplace_back(i.name, displ);
|
||||||
env->values[displ++] = i.value;
|
env->values[displ++] = i.value;
|
||||||
varNames.insert((std::string) i.name);
|
varNames.emplace(state->symbols[i.name]);
|
||||||
}
|
}
|
||||||
staticEnv->sort();
|
staticEnv->sort();
|
||||||
staticEnv->deduplicate();
|
staticEnv->deduplicate();
|
||||||
|
@ -835,7 +847,7 @@ void NixRepl::addAttrsToScope(Value & attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void NixRepl::addVarToScope(const Symbol & name, Value & v)
|
void NixRepl::addVarToScope(const Symbol name, Value & v)
|
||||||
{
|
{
|
||||||
if (displ >= envSize)
|
if (displ >= envSize)
|
||||||
throw Error("environment full; cannot add more variables");
|
throw Error("environment full; cannot add more variables");
|
||||||
|
@ -844,7 +856,7 @@ void NixRepl::addVarToScope(const Symbol & name, Value & v)
|
||||||
staticEnv->vars.emplace_back(name, displ);
|
staticEnv->vars.emplace_back(name, displ);
|
||||||
staticEnv->sort();
|
staticEnv->sort();
|
||||||
env->values[displ++] = &v;
|
env->values[displ++] = &v;
|
||||||
varNames.insert((std::string) name);
|
varNames.emplace(state->symbols[name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -925,7 +937,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
Bindings::iterator i = v.attrs->find(state->sDrvPath);
|
||||||
PathSet context;
|
PathSet context;
|
||||||
if (i != v.attrs->end())
|
if (i != v.attrs->end())
|
||||||
str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context));
|
str << state->store->printStorePath(state->coerceToStorePath(i->pos, *i->value, context));
|
||||||
else
|
else
|
||||||
str << "???";
|
str << "???";
|
||||||
str << "»";
|
str << "»";
|
||||||
|
@ -937,7 +949,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
typedef std::map<std::string, Value *> Sorted;
|
typedef std::map<std::string, Value *> Sorted;
|
||||||
Sorted sorted;
|
Sorted sorted;
|
||||||
for (auto & i : *v.attrs)
|
for (auto & i : *v.attrs)
|
||||||
sorted[i.name] = i.value;
|
sorted.emplace(state->symbols[i.name], i.value);
|
||||||
|
|
||||||
for (auto & i : sorted) {
|
for (auto & i : sorted) {
|
||||||
if (isVarName(i.first))
|
if (isVarName(i.first))
|
||||||
|
@ -987,7 +999,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m
|
||||||
case nFunction:
|
case nFunction:
|
||||||
if (v.isLambda()) {
|
if (v.isLambda()) {
|
||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << v.lambda.fun->pos;
|
s << state->positions[v.lambda.fun->pos];
|
||||||
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
|
str << ANSI_BLUE "«lambda @ " << filterANSIEscapes(s.str()) << "»" ANSI_NORMAL;
|
||||||
} else if (v.isPrimOp()) {
|
} else if (v.isPrimOp()) {
|
||||||
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
|
str << ANSI_MAGENTA "«primop»" ANSI_NORMAL;
|
||||||
|
|
|
@ -41,13 +41,13 @@ std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string & attrPath,
|
std::pair<Value *, PosIdx> findAlongAttrPath(EvalState & state, const std::string & attrPath,
|
||||||
Bindings & autoArgs, Value & vIn)
|
Bindings & autoArgs, Value & vIn)
|
||||||
{
|
{
|
||||||
Strings tokens = parseAttrPath(attrPath);
|
Strings tokens = parseAttrPath(attrPath);
|
||||||
|
|
||||||
Value * v = &vIn;
|
Value * v = &vIn;
|
||||||
Pos pos = noPos;
|
PosIdx pos = noPos;
|
||||||
|
|
||||||
for (auto & attr : tokens) {
|
for (auto & attr : tokens) {
|
||||||
|
|
||||||
|
@ -77,13 +77,13 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string &
|
||||||
if (a == v->attrs->end()) {
|
if (a == v->attrs->end()) {
|
||||||
std::set<std::string> attrNames;
|
std::set<std::string> attrNames;
|
||||||
for (auto & attr : *v->attrs)
|
for (auto & attr : *v->attrs)
|
||||||
attrNames.insert(attr.name);
|
attrNames.insert(state.symbols[attr.name]);
|
||||||
|
|
||||||
auto suggestions = Suggestions::bestMatches(attrNames, attr);
|
auto suggestions = Suggestions::bestMatches(attrNames, attr);
|
||||||
throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
|
throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
|
||||||
}
|
}
|
||||||
v = &*a->value;
|
v = &*a->value;
|
||||||
pos = *a->pos;
|
pos = a->pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
@ -106,7 +106,7 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string &
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pos findPackageFilename(EvalState & state, Value & v, std::string what)
|
std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||||
{
|
{
|
||||||
Value * v2;
|
Value * v2;
|
||||||
try {
|
try {
|
||||||
|
@ -132,9 +132,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
|
||||||
throw ParseError("cannot parse line number '%s'", pos);
|
throw ParseError("cannot parse line number '%s'", pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol file = state.symbols.create(filename);
|
return { std::move(filename), lineno };
|
||||||
|
|
||||||
return { foFile, file, lineno, 0 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,14 @@ namespace nix {
|
||||||
MakeError(AttrPathNotFound, Error);
|
MakeError(AttrPathNotFound, Error);
|
||||||
MakeError(NoPositionInfo, Error);
|
MakeError(NoPositionInfo, Error);
|
||||||
|
|
||||||
std::pair<Value *, Pos> findAlongAttrPath(
|
std::pair<Value *, PosIdx> findAlongAttrPath(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const std::string & attrPath,
|
const std::string & attrPath,
|
||||||
Bindings & autoArgs,
|
Bindings & autoArgs,
|
||||||
Value & vIn);
|
Value & vIn);
|
||||||
|
|
||||||
/* Heuristic to find the filename and lineno or a nix value. */
|
/* Heuristic to find the filename and lineno or a nix value. */
|
||||||
Pos findPackageFilename(EvalState & state, Value & v, std::string what);
|
std::pair<std::string, uint32_t> findPackageFilename(EvalState & state, Value & v, std::string what);
|
||||||
|
|
||||||
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
std::vector<Symbol> parseAttrPath(EvalState & state, std::string_view s);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ Bindings * EvalState::allocBindings(size_t capacity)
|
||||||
/* Create a new attribute named 'name' on an existing attribute set stored
|
/* Create a new attribute named 'name' on an existing attribute set stored
|
||||||
in 'vAttrs' and return the newly allocated Value which is associated with
|
in 'vAttrs' and return the newly allocated Value which is associated with
|
||||||
this attribute. */
|
this attribute. */
|
||||||
Value * EvalState::allocAttr(Value & vAttrs, const Symbol & name)
|
Value * EvalState::allocAttr(Value & vAttrs, Symbol name)
|
||||||
{
|
{
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
vAttrs.attrs->push_back(Attr(name, v));
|
vAttrs.attrs->push_back(Attr(name, v));
|
||||||
|
@ -40,7 +40,7 @@ Value * EvalState::allocAttr(Value & vAttrs, std::string_view name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos)
|
Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
|
||||||
{
|
{
|
||||||
auto value = state.allocValue();
|
auto value = state.allocValue();
|
||||||
bindings->push_back(Attr(name, value, pos));
|
bindings->push_back(Attr(name, value, pos));
|
||||||
|
@ -48,7 +48,7 @@ Value & BindingsBuilder::alloc(const Symbol & name, ptr<Pos> pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value & BindingsBuilder::alloc(std::string_view name, ptr<Pos> pos)
|
Value & BindingsBuilder::alloc(std::string_view name, PosIdx pos)
|
||||||
{
|
{
|
||||||
return alloc(state.symbols.create(name), pos);
|
return alloc(state.symbols.create(name), pos);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,18 +15,27 @@ struct Value;
|
||||||
/* Map one attribute name to its value. */
|
/* Map one attribute name to its value. */
|
||||||
struct Attr
|
struct Attr
|
||||||
{
|
{
|
||||||
|
/* the placement of `name` and `pos` in this struct is important.
|
||||||
|
both of them are uint32 wrappers, they are next to each other
|
||||||
|
to make sure that Attr has no padding on 64 bit machines. that
|
||||||
|
way we keep Attr size at two words with no wasted space. */
|
||||||
Symbol name;
|
Symbol name;
|
||||||
|
PosIdx pos;
|
||||||
Value * value;
|
Value * value;
|
||||||
ptr<Pos> pos;
|
Attr(Symbol name, Value * value, PosIdx pos = noPos)
|
||||||
Attr(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
|
: name(name), pos(pos), value(value) { };
|
||||||
: name(name), value(value), pos(pos) { };
|
Attr() { };
|
||||||
Attr() : pos(&noPos) { };
|
|
||||||
bool operator < (const Attr & a) const
|
bool operator < (const Attr & a) const
|
||||||
{
|
{
|
||||||
return name < a.name;
|
return name < a.name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(Attr) == 2 * sizeof(uint32_t) + sizeof(Value *),
|
||||||
|
"performance of the evaluator is highly sensitive to the size of Attr. "
|
||||||
|
"avoid introducing any padding into Attr if at all possible, and do not "
|
||||||
|
"introduce new fields that need not be present for almost every instance.");
|
||||||
|
|
||||||
/* Bindings contains all the attributes of an attribute set. It is defined
|
/* Bindings contains all the attributes of an attribute set. It is defined
|
||||||
by its size and its capacity, the capacity being the number of Attr
|
by its size and its capacity, the capacity being the number of Attr
|
||||||
elements allocated after this structure, while the size corresponds to
|
elements allocated after this structure, while the size corresponds to
|
||||||
|
@ -35,13 +44,13 @@ class Bindings
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef uint32_t size_t;
|
typedef uint32_t size_t;
|
||||||
ptr<Pos> pos;
|
PosIdx pos;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t size_, capacity_;
|
size_t size_, capacity_;
|
||||||
Attr attrs[0];
|
Attr attrs[0];
|
||||||
|
|
||||||
Bindings(size_t capacity) : pos(&noPos), size_(0), capacity_(capacity) { }
|
Bindings(size_t capacity) : size_(0), capacity_(capacity) { }
|
||||||
Bindings(const Bindings & bindings) = delete;
|
Bindings(const Bindings & bindings) = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -57,7 +66,7 @@ public:
|
||||||
attrs[size_++] = attr;
|
attrs[size_++] = attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator find(const Symbol & name)
|
iterator find(Symbol name)
|
||||||
{
|
{
|
||||||
Attr key(name, 0);
|
Attr key(name, 0);
|
||||||
iterator i = std::lower_bound(begin(), end(), key);
|
iterator i = std::lower_bound(begin(), end(), key);
|
||||||
|
@ -65,7 +74,7 @@ public:
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
|
||||||
Attr * get(const Symbol & name)
|
Attr * get(Symbol name)
|
||||||
{
|
{
|
||||||
Attr key(name, 0);
|
Attr key(name, 0);
|
||||||
iterator i = std::lower_bound(begin(), end(), key);
|
iterator i = std::lower_bound(begin(), end(), key);
|
||||||
|
@ -73,18 +82,6 @@ public:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Attr & need(const Symbol & name, const Pos & pos = noPos)
|
|
||||||
{
|
|
||||||
auto a = get(name);
|
|
||||||
if (!a)
|
|
||||||
throw Error({
|
|
||||||
.msg = hintfmt("attribute '%s' missing", name),
|
|
||||||
.errPos = pos
|
|
||||||
});
|
|
||||||
|
|
||||||
return *a;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator begin() { return &attrs[0]; }
|
iterator begin() { return &attrs[0]; }
|
||||||
iterator end() { return &attrs[size_]; }
|
iterator end() { return &attrs[size_]; }
|
||||||
|
|
||||||
|
@ -98,14 +95,15 @@ public:
|
||||||
size_t capacity() { return capacity_; }
|
size_t capacity() { return capacity_; }
|
||||||
|
|
||||||
/* Returns the attributes in lexicographically sorted order. */
|
/* Returns the attributes in lexicographically sorted order. */
|
||||||
std::vector<const Attr *> lexicographicOrder() const
|
std::vector<const Attr *> lexicographicOrder(const SymbolTable & symbols) const
|
||||||
{
|
{
|
||||||
std::vector<const Attr *> res;
|
std::vector<const Attr *> res;
|
||||||
res.reserve(size_);
|
res.reserve(size_);
|
||||||
for (size_t n = 0; n < size_; n++)
|
for (size_t n = 0; n < size_; n++)
|
||||||
res.emplace_back(&attrs[n]);
|
res.emplace_back(&attrs[n]);
|
||||||
std::sort(res.begin(), res.end(), [](const Attr * a, const Attr * b) {
|
std::sort(res.begin(), res.end(), [&](const Attr * a, const Attr * b) {
|
||||||
return (const std::string &) a->name < (const std::string &) b->name;
|
std::string_view sa = symbols[a->name], sb = symbols[b->name];
|
||||||
|
return sa < sb;
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +128,7 @@ public:
|
||||||
: bindings(bindings), state(state)
|
: bindings(bindings), state(state)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void insert(Symbol name, Value * value, ptr<Pos> pos = ptr(&noPos))
|
void insert(Symbol name, Value * value, PosIdx pos = noPos)
|
||||||
{
|
{
|
||||||
insert(Attr(name, value, pos));
|
insert(Attr(name, value, pos));
|
||||||
}
|
}
|
||||||
|
@ -145,9 +143,9 @@ public:
|
||||||
bindings->push_back(attr);
|
bindings->push_back(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value & alloc(const Symbol & name, ptr<Pos> pos = ptr(&noPos));
|
Value & alloc(Symbol name, PosIdx pos = noPos);
|
||||||
|
|
||||||
Value & alloc(std::string_view name, ptr<Pos> pos = ptr(&noPos));
|
Value & alloc(std::string_view name, PosIdx pos = noPos);
|
||||||
|
|
||||||
Bindings * finish()
|
Bindings * finish()
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,9 +35,15 @@ struct AttrDb
|
||||||
|
|
||||||
std::unique_ptr<Sync<State>> _state;
|
std::unique_ptr<Sync<State>> _state;
|
||||||
|
|
||||||
AttrDb(const Store & cfg, const Hash & fingerprint)
|
SymbolTable & symbols;
|
||||||
|
|
||||||
|
AttrDb(
|
||||||
|
const Store & cfg,
|
||||||
|
const Hash & fingerprint,
|
||||||
|
SymbolTable & symbols)
|
||||||
: cfg(cfg)
|
: cfg(cfg)
|
||||||
, _state(std::make_unique<Sync<State>>())
|
, _state(std::make_unique<Sync<State>>())
|
||||||
|
, symbols(symbols)
|
||||||
{
|
{
|
||||||
auto state(_state->lock());
|
auto state(_state->lock());
|
||||||
|
|
||||||
|
@ -100,7 +106,7 @@ struct AttrDb
|
||||||
|
|
||||||
state->insertAttribute.use()
|
state->insertAttribute.use()
|
||||||
(key.first)
|
(key.first)
|
||||||
(key.second)
|
(symbols[key.second])
|
||||||
(AttrType::FullAttrs)
|
(AttrType::FullAttrs)
|
||||||
(0, false).exec();
|
(0, false).exec();
|
||||||
|
|
||||||
|
@ -110,7 +116,7 @@ struct AttrDb
|
||||||
for (auto & attr : attrs)
|
for (auto & attr : attrs)
|
||||||
state->insertAttribute.use()
|
state->insertAttribute.use()
|
||||||
(rowId)
|
(rowId)
|
||||||
(attr)
|
(symbols[attr])
|
||||||
(AttrType::Placeholder)
|
(AttrType::Placeholder)
|
||||||
(0, false).exec();
|
(0, false).exec();
|
||||||
|
|
||||||
|
@ -135,14 +141,14 @@ struct AttrDb
|
||||||
}
|
}
|
||||||
state->insertAttributeWithContext.use()
|
state->insertAttributeWithContext.use()
|
||||||
(key.first)
|
(key.first)
|
||||||
(key.second)
|
(symbols[key.second])
|
||||||
(AttrType::String)
|
(AttrType::String)
|
||||||
(s)
|
(s)
|
||||||
(ctx).exec();
|
(ctx).exec();
|
||||||
} else {
|
} else {
|
||||||
state->insertAttribute.use()
|
state->insertAttribute.use()
|
||||||
(key.first)
|
(key.first)
|
||||||
(key.second)
|
(symbols[key.second])
|
||||||
(AttrType::String)
|
(AttrType::String)
|
||||||
(s).exec();
|
(s).exec();
|
||||||
}
|
}
|
||||||
|
@ -161,7 +167,7 @@ struct AttrDb
|
||||||
|
|
||||||
state->insertAttribute.use()
|
state->insertAttribute.use()
|
||||||
(key.first)
|
(key.first)
|
||||||
(key.second)
|
(symbols[key.second])
|
||||||
(AttrType::Bool)
|
(AttrType::Bool)
|
||||||
(b ? 1 : 0).exec();
|
(b ? 1 : 0).exec();
|
||||||
|
|
||||||
|
@ -177,7 +183,7 @@ struct AttrDb
|
||||||
|
|
||||||
state->insertAttribute.use()
|
state->insertAttribute.use()
|
||||||
(key.first)
|
(key.first)
|
||||||
(key.second)
|
(symbols[key.second])
|
||||||
(AttrType::Placeholder)
|
(AttrType::Placeholder)
|
||||||
(0, false).exec();
|
(0, false).exec();
|
||||||
|
|
||||||
|
@ -193,7 +199,7 @@ struct AttrDb
|
||||||
|
|
||||||
state->insertAttribute.use()
|
state->insertAttribute.use()
|
||||||
(key.first)
|
(key.first)
|
||||||
(key.second)
|
(symbols[key.second])
|
||||||
(AttrType::Missing)
|
(AttrType::Missing)
|
||||||
(0, false).exec();
|
(0, false).exec();
|
||||||
|
|
||||||
|
@ -209,7 +215,7 @@ struct AttrDb
|
||||||
|
|
||||||
state->insertAttribute.use()
|
state->insertAttribute.use()
|
||||||
(key.first)
|
(key.first)
|
||||||
(key.second)
|
(symbols[key.second])
|
||||||
(AttrType::Misc)
|
(AttrType::Misc)
|
||||||
(0, false).exec();
|
(0, false).exec();
|
||||||
|
|
||||||
|
@ -225,7 +231,7 @@ struct AttrDb
|
||||||
|
|
||||||
state->insertAttribute.use()
|
state->insertAttribute.use()
|
||||||
(key.first)
|
(key.first)
|
||||||
(key.second)
|
(symbols[key.second])
|
||||||
(AttrType::Failed)
|
(AttrType::Failed)
|
||||||
(0, false).exec();
|
(0, false).exec();
|
||||||
|
|
||||||
|
@ -233,13 +239,11 @@ struct AttrDb
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::pair<AttrId, AttrValue>> getAttr(
|
std::optional<std::pair<AttrId, AttrValue>> getAttr(AttrKey key)
|
||||||
AttrKey key,
|
|
||||||
SymbolTable & symbols)
|
|
||||||
{
|
{
|
||||||
auto state(_state->lock());
|
auto state(_state->lock());
|
||||||
|
|
||||||
auto queryAttribute(state->queryAttribute.use()(key.first)(key.second));
|
auto queryAttribute(state->queryAttribute.use()(key.first)(symbols[key.second]));
|
||||||
if (!queryAttribute.next()) return {};
|
if (!queryAttribute.next()) return {};
|
||||||
|
|
||||||
auto rowId = (AttrType) queryAttribute.getInt(0);
|
auto rowId = (AttrType) queryAttribute.getInt(0);
|
||||||
|
@ -253,7 +257,7 @@ struct AttrDb
|
||||||
std::vector<Symbol> attrs;
|
std::vector<Symbol> attrs;
|
||||||
auto queryAttributes(state->queryAttributes.use()(rowId));
|
auto queryAttributes(state->queryAttributes.use()(rowId));
|
||||||
while (queryAttributes.next())
|
while (queryAttributes.next())
|
||||||
attrs.push_back(symbols.create(queryAttributes.getStr(0)));
|
attrs.emplace_back(symbols.create(queryAttributes.getStr(0)));
|
||||||
return {{rowId, attrs}};
|
return {{rowId, attrs}};
|
||||||
}
|
}
|
||||||
case AttrType::String: {
|
case AttrType::String: {
|
||||||
|
@ -277,10 +281,13 @@ struct AttrDb
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::shared_ptr<AttrDb> makeAttrDb(const Store & cfg, const Hash & fingerprint)
|
static std::shared_ptr<AttrDb> makeAttrDb(
|
||||||
|
const Store & cfg,
|
||||||
|
const Hash & fingerprint,
|
||||||
|
SymbolTable & symbols)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return std::make_shared<AttrDb>(cfg, fingerprint);
|
return std::make_shared<AttrDb>(cfg, fingerprint, symbols);
|
||||||
} catch (SQLiteError &) {
|
} catch (SQLiteError &) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -291,7 +298,7 @@ EvalCache::EvalCache(
|
||||||
std::optional<std::reference_wrapper<const Hash>> useCache,
|
std::optional<std::reference_wrapper<const Hash>> useCache,
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
RootLoader rootLoader)
|
RootLoader rootLoader)
|
||||||
: db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr)
|
: db(useCache ? makeAttrDb(*state.store, *useCache, state.symbols) : nullptr)
|
||||||
, state(state)
|
, state(state)
|
||||||
, rootLoader(rootLoader)
|
, rootLoader(rootLoader)
|
||||||
{
|
{
|
||||||
|
@ -306,9 +313,9 @@ Value * EvalCache::getRootValue()
|
||||||
return *value;
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<AttrCursor> EvalCache::getRoot()
|
ref<AttrCursor> EvalCache::getRoot()
|
||||||
{
|
{
|
||||||
return std::make_shared<AttrCursor>(ref(shared_from_this()), std::nullopt);
|
return make_ref<AttrCursor>(ref(shared_from_this()), std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
AttrCursor::AttrCursor(
|
AttrCursor::AttrCursor(
|
||||||
|
@ -327,8 +334,7 @@ AttrKey AttrCursor::getKey()
|
||||||
if (!parent)
|
if (!parent)
|
||||||
return {0, root->state.sEpsilon};
|
return {0, root->state.sEpsilon};
|
||||||
if (!parent->first->cachedValue) {
|
if (!parent->first->cachedValue) {
|
||||||
parent->first->cachedValue = root->db->getAttr(
|
parent->first->cachedValue = root->db->getAttr(parent->first->getKey());
|
||||||
parent->first->getKey(), root->state.symbols);
|
|
||||||
assert(parent->first->cachedValue);
|
assert(parent->first->cachedValue);
|
||||||
}
|
}
|
||||||
return {parent->first->cachedValue->first, parent->second};
|
return {parent->first->cachedValue->first, parent->second};
|
||||||
|
@ -369,12 +375,12 @@ std::vector<Symbol> AttrCursor::getAttrPath(Symbol name) const
|
||||||
|
|
||||||
std::string AttrCursor::getAttrPathStr() const
|
std::string AttrCursor::getAttrPathStr() const
|
||||||
{
|
{
|
||||||
return concatStringsSep(".", getAttrPath());
|
return concatStringsSep(".", root->state.symbols.resolve(getAttrPath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AttrCursor::getAttrPathStr(Symbol name) const
|
std::string AttrCursor::getAttrPathStr(Symbol name) const
|
||||||
{
|
{
|
||||||
return concatStringsSep(".", getAttrPath(name));
|
return concatStringsSep(".", root->state.symbols.resolve(getAttrPath(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Value & AttrCursor::forceValue()
|
Value & AttrCursor::forceValue()
|
||||||
|
@ -414,25 +420,25 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
|
||||||
auto attrNames = getAttrs();
|
auto attrNames = getAttrs();
|
||||||
std::set<std::string> strAttrNames;
|
std::set<std::string> strAttrNames;
|
||||||
for (auto & name : attrNames)
|
for (auto & name : attrNames)
|
||||||
strAttrNames.insert(std::string(name));
|
strAttrNames.insert(root->state.symbols[name]);
|
||||||
|
|
||||||
return Suggestions::bestMatches(strAttrNames, name);
|
return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
|
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
|
||||||
{
|
{
|
||||||
if (root->db) {
|
if (root->db) {
|
||||||
if (!cachedValue)
|
if (!cachedValue)
|
||||||
cachedValue = root->db->getAttr(getKey(), root->state.symbols);
|
cachedValue = root->db->getAttr(getKey());
|
||||||
|
|
||||||
if (cachedValue) {
|
if (cachedValue) {
|
||||||
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
|
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
|
||||||
for (auto & attr : *attrs)
|
for (auto & attr : *attrs)
|
||||||
if (attr == name)
|
if (attr == name)
|
||||||
return std::make_shared<AttrCursor>(root, std::make_pair(shared_from_this(), name));
|
return std::make_shared<AttrCursor>(root, std::make_pair(shared_from_this(), attr));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else if (std::get_if<placeholder_t>(&cachedValue->second)) {
|
} else if (std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||||
auto attr = root->db->getAttr({cachedValue->first, name}, root->state.symbols);
|
auto attr = root->db->getAttr({cachedValue->first, name});
|
||||||
if (attr) {
|
if (attr) {
|
||||||
if (std::get_if<missing_t>(&attr->second))
|
if (std::get_if<missing_t>(&attr->second))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -522,7 +528,7 @@ std::string AttrCursor::getString()
|
||||||
{
|
{
|
||||||
if (root->db) {
|
if (root->db) {
|
||||||
if (!cachedValue)
|
if (!cachedValue)
|
||||||
cachedValue = root->db->getAttr(getKey(), root->state.symbols);
|
cachedValue = root->db->getAttr(getKey());
|
||||||
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||||
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
|
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
|
||||||
debug("using cached string attribute '%s'", getAttrPathStr());
|
debug("using cached string attribute '%s'", getAttrPathStr());
|
||||||
|
@ -552,7 +558,7 @@ string_t AttrCursor::getStringWithContext()
|
||||||
{
|
{
|
||||||
if (root->db) {
|
if (root->db) {
|
||||||
if (!cachedValue)
|
if (!cachedValue)
|
||||||
cachedValue = root->db->getAttr(getKey(), root->state.symbols);
|
cachedValue = root->db->getAttr(getKey());
|
||||||
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||||
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
|
if (auto s = std::get_if<string_t>(&cachedValue->second)) {
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
|
@ -594,7 +600,7 @@ bool AttrCursor::getBool()
|
||||||
{
|
{
|
||||||
if (root->db) {
|
if (root->db) {
|
||||||
if (!cachedValue)
|
if (!cachedValue)
|
||||||
cachedValue = root->db->getAttr(getKey(), root->state.symbols);
|
cachedValue = root->db->getAttr(getKey());
|
||||||
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||||
if (auto b = std::get_if<bool>(&cachedValue->second)) {
|
if (auto b = std::get_if<bool>(&cachedValue->second)) {
|
||||||
debug("using cached Boolean attribute '%s'", getAttrPathStr());
|
debug("using cached Boolean attribute '%s'", getAttrPathStr());
|
||||||
|
@ -624,7 +630,7 @@ std::vector<Symbol> AttrCursor::getAttrs()
|
||||||
{
|
{
|
||||||
if (root->db) {
|
if (root->db) {
|
||||||
if (!cachedValue)
|
if (!cachedValue)
|
||||||
cachedValue = root->db->getAttr(getKey(), root->state.symbols);
|
cachedValue = root->db->getAttr(getKey());
|
||||||
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
if (cachedValue && !std::get_if<placeholder_t>(&cachedValue->second)) {
|
||||||
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
|
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
|
||||||
debug("using cached attrset attribute '%s'", getAttrPathStr());
|
debug("using cached attrset attribute '%s'", getAttrPathStr());
|
||||||
|
@ -650,8 +656,9 @@ std::vector<Symbol> AttrCursor::getAttrs()
|
||||||
std::vector<Symbol> attrs;
|
std::vector<Symbol> attrs;
|
||||||
for (auto & attr : *getValue().attrs)
|
for (auto & attr : *getValue().attrs)
|
||||||
attrs.push_back(attr.name);
|
attrs.push_back(attr.name);
|
||||||
std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
|
std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) {
|
||||||
return (const std::string &) a < (const std::string &) b;
|
std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b];
|
||||||
|
return sa < sb;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (root->db)
|
if (root->db)
|
||||||
|
|
|
@ -33,7 +33,7 @@ public:
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
RootLoader rootLoader);
|
RootLoader rootLoader);
|
||||||
|
|
||||||
std::shared_ptr<AttrCursor> getRoot();
|
ref<AttrCursor> getRoot();
|
||||||
};
|
};
|
||||||
|
|
||||||
enum AttrType {
|
enum AttrType {
|
||||||
|
@ -104,6 +104,8 @@ public:
|
||||||
|
|
||||||
ref<AttrCursor> getAttr(std::string_view name);
|
ref<AttrCursor> getAttr(std::string_view name);
|
||||||
|
|
||||||
|
/* Get an attribute along a chain of attrsets. Note that this does
|
||||||
|
not auto-call functors or functions. */
|
||||||
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
|
OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
|
||||||
|
|
||||||
std::string getString();
|
std::string getString();
|
||||||
|
|
|
@ -2,42 +2,8 @@
|
||||||
|
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
|
||||||
#define LocalNoInline(f) static f __attribute__((noinline)); f
|
|
||||||
#define LocalNoInlineNoReturn(f) static f __attribute__((noinline, noreturn)); f
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, EvalState &evalState))
|
|
||||||
{
|
|
||||||
auto error = EvalError({
|
|
||||||
.msg = hintfmt(s),
|
|
||||||
.errPos = pos
|
|
||||||
});
|
|
||||||
|
|
||||||
if (debuggerHook && !evalState.debugTraces.empty()) {
|
|
||||||
DebugTrace &last = evalState.debugTraces.front();
|
|
||||||
debuggerHook(&error, last.env, last.expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v, EvalState &evalState))
|
|
||||||
{
|
|
||||||
auto error = TypeError({
|
|
||||||
.msg = hintfmt(s, showType(v)),
|
|
||||||
.errPos = pos
|
|
||||||
});
|
|
||||||
|
|
||||||
if (debuggerHook && !evalState.debugTraces.empty()) {
|
|
||||||
DebugTrace &last = evalState.debugTraces.front();
|
|
||||||
debuggerHook(&error, last.env, last.expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Note: Various places expect the allocated memory to be zeroed. */
|
/* Note: Various places expect the allocated memory to be zeroed. */
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
inline void * allocBytes(size_t n)
|
inline void * allocBytes(size_t n)
|
||||||
|
@ -113,7 +79,7 @@ Env & EvalState::allocEnv(size_t size)
|
||||||
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
void EvalState::forceValue(Value & v, const Pos & pos)
|
void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, [&]() { return pos; });
|
forceValue(v, [&]() { return pos; });
|
||||||
}
|
}
|
||||||
|
@ -142,7 +108,7 @@ void EvalState::forceValue(Value & v, Callable getPos)
|
||||||
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
inline void EvalState::forceAttrs(Value & v, const Pos & pos)
|
inline void EvalState::forceAttrs(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceAttrs(v, [&]() { return pos; });
|
forceAttrs(v, [&]() { return pos; });
|
||||||
}
|
}
|
||||||
|
@ -159,7 +125,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos)
|
||||||
|
|
||||||
|
|
||||||
[[gnu::always_inline]]
|
[[gnu::always_inline]]
|
||||||
inline void EvalState::forceList(Value & v, const Pos & pos)
|
inline void EvalState::forceList(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (!v.isList())
|
if (!v.isList())
|
||||||
|
|
|
@ -96,7 +96,8 @@ RootValue allocRootValue(Value * v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Value::print(std::ostream & str, std::set<const void *> * seen) const
|
void Value::print(const SymbolTable & symbols, std::ostream & str,
|
||||||
|
std::set<const void *> * seen) const
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
|
@ -129,9 +130,9 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
|
||||||
str << "«repeated»";
|
str << "«repeated»";
|
||||||
else {
|
else {
|
||||||
str << "{ ";
|
str << "{ ";
|
||||||
for (auto & i : attrs->lexicographicOrder()) {
|
for (auto & i : attrs->lexicographicOrder(symbols)) {
|
||||||
str << i->name << " = ";
|
str << symbols[i->name] << " = ";
|
||||||
i->value->print(str, seen);
|
i->value->print(symbols, str, seen);
|
||||||
str << "; ";
|
str << "; ";
|
||||||
}
|
}
|
||||||
str << "}";
|
str << "}";
|
||||||
|
@ -146,7 +147,7 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
|
||||||
else {
|
else {
|
||||||
str << "[ ";
|
str << "[ ";
|
||||||
for (auto v2 : listItems()) {
|
for (auto v2 : listItems()) {
|
||||||
v2->print(str, seen);
|
v2->print(symbols, str, seen);
|
||||||
str << " ";
|
str << " ";
|
||||||
}
|
}
|
||||||
str << "]";
|
str << "]";
|
||||||
|
@ -177,17 +178,18 @@ void Value::print(std::ostream & str, std::set<const void *> * seen) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Value::print(std::ostream & str, bool showRepeated) const
|
void Value::print(const SymbolTable & symbols, std::ostream & str, bool showRepeated) const
|
||||||
{
|
{
|
||||||
std::set<const void *> seen;
|
std::set<const void *> seen;
|
||||||
print(str, showRepeated ? nullptr : &seen);
|
print(symbols, str, showRepeated ? nullptr : &seen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Value & v)
|
std::string printValue(const EvalState & state, const Value & v)
|
||||||
{
|
{
|
||||||
v.print(str, false);
|
std::ostringstream out;
|
||||||
return str;
|
v.print(state.symbols, out);
|
||||||
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,10 +238,10 @@ std::string showType(const Value & v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pos Value::determinePos(const Pos & pos) const
|
PosIdx Value::determinePos(const PosIdx pos) const
|
||||||
{
|
{
|
||||||
switch (internalType) {
|
switch (internalType) {
|
||||||
case tAttrs: return *attrs->pos;
|
case tAttrs: return attrs->pos;
|
||||||
case tLambda: return lambda.fun->pos;
|
case tLambda: return lambda.fun->pos;
|
||||||
case tApp: return app.left->determinePos(pos);
|
case tApp: return app.left->determinePos(pos);
|
||||||
default: return pos;
|
default: return pos;
|
||||||
|
@ -308,7 +310,7 @@ static BoehmGCStackAllocator boehmGCStackAllocator;
|
||||||
|
|
||||||
static Symbol getName(const AttrName & name, EvalState & state, Env & env)
|
static Symbol getName(const AttrName & name, EvalState & state, Env & env)
|
||||||
{
|
{
|
||||||
if (name.symbol.set()) {
|
if (name.symbol) {
|
||||||
return name.symbol;
|
return name.symbol;
|
||||||
} else {
|
} else {
|
||||||
Value nameValue;
|
Value nameValue;
|
||||||
|
@ -641,20 +643,20 @@ Value * EvalState::addPrimOp(const std::string & name,
|
||||||
size_t arity, PrimOpFun primOp)
|
size_t arity, PrimOpFun primOp)
|
||||||
{
|
{
|
||||||
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
|
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
|
||||||
Symbol sym = symbols.create(name2);
|
auto sym = symbols.create(name2);
|
||||||
|
|
||||||
/* Hack to make constants lazy: turn them into a application of
|
/* Hack to make constants lazy: turn them into a application of
|
||||||
the primop to a dummy value. */
|
the primop to a dummy value. */
|
||||||
if (arity == 0) {
|
if (arity == 0) {
|
||||||
auto vPrimOp = allocValue();
|
auto vPrimOp = allocValue();
|
||||||
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym });
|
vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = name2 });
|
||||||
Value v;
|
Value v;
|
||||||
v.mkApp(vPrimOp, vPrimOp);
|
v.mkApp(vPrimOp, vPrimOp);
|
||||||
return addConstant(name, v);
|
return addConstant(name, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = sym });
|
v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = name2 });
|
||||||
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
||||||
baseEnv.values[baseEnvDispl++] = v;
|
baseEnv.values[baseEnvDispl++] = v;
|
||||||
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
baseEnv.values[0]->attrs->push_back(Attr(sym, v));
|
||||||
|
@ -669,21 +671,21 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
||||||
if (primOp.arity == 0) {
|
if (primOp.arity == 0) {
|
||||||
primOp.arity = 1;
|
primOp.arity = 1;
|
||||||
auto vPrimOp = allocValue();
|
auto vPrimOp = allocValue();
|
||||||
vPrimOp->mkPrimOp(new PrimOp(std::move(primOp)));
|
vPrimOp->mkPrimOp(new PrimOp(primOp));
|
||||||
Value v;
|
Value v;
|
||||||
v.mkApp(vPrimOp, vPrimOp);
|
v.mkApp(vPrimOp, vPrimOp);
|
||||||
return addConstant(primOp.name, v);
|
return addConstant(primOp.name, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol envName = primOp.name;
|
auto envName = symbols.create(primOp.name);
|
||||||
if (hasPrefix(primOp.name, "__"))
|
if (hasPrefix(primOp.name, "__"))
|
||||||
primOp.name = symbols.create(std::string(primOp.name, 2));
|
primOp.name = primOp.name.substr(2);
|
||||||
|
|
||||||
Value * v = allocValue();
|
Value * v = allocValue();
|
||||||
v->mkPrimOp(new PrimOp(std::move(primOp)));
|
v->mkPrimOp(new PrimOp(primOp));
|
||||||
staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
|
staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
|
||||||
baseEnv.values[baseEnvDispl++] = v;
|
baseEnv.values[baseEnvDispl++] = v;
|
||||||
baseEnv.values[0]->attrs->push_back(Attr(primOp.name, v));
|
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v));
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,7 +702,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
||||||
auto v2 = &v;
|
auto v2 = &v;
|
||||||
if (v2->primOp->doc)
|
if (v2->primOp->doc)
|
||||||
return Doc {
|
return Doc {
|
||||||
.pos = noPos,
|
.pos = {},
|
||||||
.name = v2->primOp->name,
|
.name = v2->primOp->name,
|
||||||
.arity = v2->primOp->arity,
|
.arity = v2->primOp->arity,
|
||||||
.args = v2->primOp->args,
|
.args = v2->primOp->args,
|
||||||
|
@ -819,12 +821,42 @@ valmap * mapStaticEnvBindings(const StaticEnv &se, const Env &env)
|
||||||
evaluator. So here are some helper functions for throwing
|
evaluator. So here are some helper functions for throwing
|
||||||
exceptions. */
|
exceptions. */
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, EvalState &evalState))
|
void EvalState::throwEvalError(const PosIdx pos, const char * s) const
|
||||||
|
{
|
||||||
|
auto error = EvalError({
|
||||||
|
.msg = hintfmt(s),
|
||||||
|
.errPos = positions[pos]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (debuggerHook && !debugTraces.empty()) {
|
||||||
|
DebugTrace &last = debugTraces.front();
|
||||||
|
debuggerHook(&error, last.env, last.expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) const
|
||||||
|
{
|
||||||
|
auto error = TypeError({
|
||||||
|
.msg = hintfmt(s, showType(v)),
|
||||||
|
.errPos = positions[pos]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (debuggerHook && !debugTraces.empty()) {
|
||||||
|
DebugTrace &last = debugTraces.front();
|
||||||
|
debuggerHook(&error, last.env, last.expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EvalState::throwEvalError(const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
auto error = EvalError(s, s2);
|
auto error = EvalError(s, s2);
|
||||||
|
|
||||||
if (debuggerHook && !evalState.debugTraces.empty()) {
|
if (debuggerHook && !debugTraces.empty()) {
|
||||||
DebugTrace &last = evalState.debugTraces.front();
|
DebugTrace &last = debugTraces.front();
|
||||||
debuggerHook(&error, last.env, last.expr);
|
debuggerHook(&error, last.env, last.expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -840,12 +872,13 @@ void EvalState::debugLastTrace(Error & e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2, Env & env, Expr &expr))
|
void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||||
|
const std::string & s2, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
auto error = EvalError({
|
auto error = EvalError({
|
||||||
.msg = hintfmt(s, s2),
|
.msg = hintfmt(s, s2),
|
||||||
.errPos = pos,
|
.errPos = positions[pos],
|
||||||
.suggestions = suggestions
|
.suggestions = suggestions,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
|
@ -854,19 +887,19 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & s
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, EvalState &evalState))
|
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
auto error = EvalError({
|
auto error = EvalError({
|
||||||
.msg = hintfmt(s, s2),
|
.msg = hintfmt(s, s2),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
evalState.debugLastTrace(error);
|
debugLastTrace(error);
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, Env & env, Expr &expr))
|
void EvalState::throwEvalError(const char * s, const std::string & s2, const std::string & s3, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
auto error = EvalError({
|
auto error = EvalError({
|
||||||
.msg = hintfmt(s),
|
.msg = hintfmt(s),
|
||||||
|
@ -879,7 +912,8 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, Env &
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, Env & env, Expr &expr))
|
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2,
|
||||||
|
const std::string & s3, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
auto error = EvalError({
|
auto error = EvalError({
|
||||||
.msg = hintfmt(s, s2),
|
.msg = hintfmt(s, s2),
|
||||||
|
@ -892,11 +926,12 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3, EvalState &evalState))
|
LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3, EvalState &evalState))
|
||||||
{
|
{
|
||||||
auto error = EvalError({
|
auto error = EvalError({
|
||||||
.msg = hintfmt(s, s2, s3),
|
.msg = hintfmt(s, s2, s3),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
evalState.debugLastTrace(error);
|
evalState.debugLastTrace(error);
|
||||||
|
@ -915,13 +950,14 @@ LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2, Env & env, Expr &expr))
|
void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
// p1 is where the error occurred; p2 is a position mentioned in the message.
|
||||||
auto error = EvalError({
|
auto error = EvalError({
|
||||||
.msg = hintfmt(s, sym, p2),
|
.msg = hintfmt(s, symbols[sym], positions[p2]),
|
||||||
.errPos = p1
|
.errPos = positions[p1]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
|
@ -930,11 +966,11 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, EvalState &evalState))
|
void EvalState::throwTypeError(const PosIdx pos, const char * s) const
|
||||||
{
|
{
|
||||||
auto error = TypeError({
|
auto error = TypeError({
|
||||||
.msg = hintfmt(s),
|
.msg = hintfmt(s),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
evalState.debugLastTrace(error);
|
evalState.debugLastTrace(error);
|
||||||
|
@ -942,12 +978,12 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, EvalS
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun,
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v, Env & env, Expr &expr))
|
const Symbol s2, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
auto error = TypeError({
|
auto error = TypeError({
|
||||||
.msg = hintfmt(s, v),
|
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
|
@ -956,11 +992,13 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2, Env & env, Expr &expr))
|
void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||||
|
const ExprLambda & fun, const Symbol s2, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
auto error = TypeError({
|
auto error = TypeError(ErrorInfo {
|
||||||
.msg = hintfmt(s, fun.showNamePos(), s2),
|
.msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]),
|
||||||
.errPos = pos,
|
.errPos = positions[pos],
|
||||||
|
.suggestions = suggestions,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
|
@ -969,7 +1007,7 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2, Env & env, Expr &expr))
|
void EvalState::throwTypeError(const char * s, const Value & v, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
auto error = TypeError({
|
auto error = TypeError({
|
||||||
.msg = hintfmt(s, fun.showNamePos(), s2),
|
.msg = hintfmt(s, fun.showNamePos(), s2),
|
||||||
|
@ -983,11 +1021,11 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & s
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1, Env & env, Expr &expr))
|
void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
auto error = AssertionError({
|
auto error = AssertionError({
|
||||||
.msg = hintfmt(s, s1),
|
.msg = hintfmt(s, s1),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
|
@ -996,11 +1034,11 @@ LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s,
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char * s, const std::string & s1, Env & env, const Expr &expr))
|
void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
auto error = UndefinedVarError({
|
auto error = UndefinedVarError({
|
||||||
.msg = hintfmt(s, s1),
|
.msg = hintfmt(s, s1),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
|
@ -1009,11 +1047,11 @@ LocalNoInlineNoReturn(void throwUndefinedVarError(const Pos & pos, const char *
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char * s, const std::string & s1, Env & env, Expr &expr))
|
void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) const
|
||||||
{
|
{
|
||||||
auto error = MissingArgumentError({
|
auto error = MissingArgumentError({
|
||||||
.msg = hintfmt(s, s1),
|
.msg = hintfmt(s, s1),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
|
@ -1022,14 +1060,14 @@ LocalNoInlineNoReturn(void throwMissingArgumentError(const Pos & pos, const char
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInline(void addErrorTrace(Error & e, const char * s, const std::string & s2))
|
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
e.addTrace(std::nullopt, s, s2);
|
e.addTrace(std::nullopt, s, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, const std::string & s2))
|
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
|
||||||
{
|
{
|
||||||
e.addTrace(pos, s, s2);
|
e.addTrace(positions[pos], s, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalNoInline(std::unique_ptr<DebugTraceStacker>
|
LocalNoInline(std::unique_ptr<DebugTraceStacker>
|
||||||
|
@ -1108,12 +1146,11 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
||||||
}
|
}
|
||||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||||
if (j != env->values[0]->attrs->end()) {
|
if (j != env->values[0]->attrs->end()) {
|
||||||
if (countCalls) attrSelects[*j->pos]++;
|
if (countCalls) attrSelects[j->pos]++;
|
||||||
return j->value;
|
return j->value;
|
||||||
}
|
}
|
||||||
if (!env->prevWith) {
|
if (!env->prevWith)
|
||||||
throwUndefinedVarError(var.pos, "undefined variable '%1%'", var.name, *env, var);
|
throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name], *env, var);
|
||||||
}
|
|
||||||
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
for (size_t l = env->prevWith; l; --l, env = env->up) ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1142,13 +1179,14 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::mkPos(Value & v, ptr<Pos> pos)
|
void EvalState::mkPos(Value & v, PosIdx p)
|
||||||
{
|
{
|
||||||
if (pos->file.set()) {
|
auto pos = positions[p];
|
||||||
|
if (!pos.file.empty()) {
|
||||||
auto attrs = buildBindings(3);
|
auto attrs = buildBindings(3);
|
||||||
attrs.alloc(sFile).mkString(pos->file);
|
attrs.alloc(sFile).mkString(pos.file);
|
||||||
attrs.alloc(sLine).mkInt(pos->line);
|
attrs.alloc(sLine).mkInt(pos.line);
|
||||||
attrs.alloc(sColumn).mkInt(pos->column);
|
attrs.alloc(sColumn).mkInt(pos.column);
|
||||||
v.mkAttrs(attrs);
|
v.mkAttrs(attrs);
|
||||||
} else
|
} else
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
|
@ -1291,7 +1329,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos)
|
inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos)
|
||||||
{
|
{
|
||||||
Value v;
|
Value v;
|
||||||
e->eval(*this, env, v);
|
e->eval(*this, env, v);
|
||||||
|
@ -1365,7 +1403,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
} else
|
} else
|
||||||
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2);
|
||||||
env2.values[displ++] = vAttr;
|
env2.values[displ++] = vAttr;
|
||||||
v.attrs->push_back(Attr(i.first, vAttr, ptr(&i.second.pos)));
|
v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the rec contains an attribute called `__overrides', then
|
/* If the rec contains an attribute called `__overrides', then
|
||||||
|
@ -1397,7 +1435,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
else
|
else
|
||||||
for (auto & i : attrs)
|
for (auto & i : attrs)
|
||||||
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), ptr(&i.second.pos)));
|
v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos));
|
||||||
|
|
||||||
/* Dynamic attrs apply *after* rec and __overrides. */
|
/* Dynamic attrs apply *after* rec and __overrides. */
|
||||||
for (auto & i : dynamicAttrs) {
|
for (auto & i : dynamicAttrs) {
|
||||||
|
@ -1407,18 +1445,18 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||||
if (nameVal.type() == nNull)
|
if (nameVal.type() == nNull)
|
||||||
continue;
|
continue;
|
||||||
state.forceStringNoCtx(nameVal);
|
state.forceStringNoCtx(nameVal);
|
||||||
Symbol nameSym = state.symbols.create(nameVal.string.s);
|
auto nameSym = state.symbols.create(nameVal.string.s);
|
||||||
Bindings::iterator j = v.attrs->find(nameSym);
|
Bindings::iterator j = v.attrs->find(nameSym);
|
||||||
if (j != v.attrs->end())
|
if (j != v.attrs->end())
|
||||||
throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, *j->pos, env, *this);
|
state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos, env, *this);
|
||||||
|
|
||||||
i.valueExpr->setName(nameSym);
|
i.valueExpr->setName(nameSym);
|
||||||
/* Keep sorted order so find can catch duplicates */
|
/* Keep sorted order so find can catch duplicates */
|
||||||
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), ptr(&i.pos)));
|
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos));
|
||||||
v.attrs->sort(); // FIXME: inefficient
|
v.attrs->sort(); // FIXME: inefficient
|
||||||
}
|
}
|
||||||
|
|
||||||
v.attrs->pos = ptr(&pos);
|
v.attrs->pos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1463,10 +1501,12 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
|
||||||
for (auto & i : attrPath) {
|
for (auto & i : attrPath) {
|
||||||
if (!first) out << '.'; else first = false;
|
if (!first) out << '.'; else first = false;
|
||||||
try {
|
try {
|
||||||
out << getName(i, state, env);
|
out << state.symbols[getName(i, state, env)];
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
assert(!i.symbol.set());
|
assert(!i.symbol);
|
||||||
out << "\"${" << *i.expr << "}\"";
|
out << "\"${";
|
||||||
|
i.expr->show(state.symbols, out);
|
||||||
|
out << "}\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out.str();
|
return out.str();
|
||||||
|
@ -1476,7 +1516,7 @@ static std::string showAttrPath(EvalState & state, Env & env, const AttrPath & a
|
||||||
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
Value vTmp;
|
Value vTmp;
|
||||||
ptr<Pos> pos2(&noPos);
|
PosIdx pos2;
|
||||||
Value * vAttrs = &vTmp;
|
Value * vAttrs = &vTmp;
|
||||||
|
|
||||||
e->eval(state, env, vTmp);
|
e->eval(state, env, vTmp);
|
||||||
|
@ -1496,7 +1536,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
for (auto & i : attrPath) {
|
for (auto & i : attrPath) {
|
||||||
state.nrLookups++;
|
state.nrLookups++;
|
||||||
Bindings::iterator j;
|
Bindings::iterator j;
|
||||||
Symbol name = getName(i, state, env);
|
auto name = getName(i, state, env);
|
||||||
if (def) {
|
if (def) {
|
||||||
state.forceValue(*vAttrs, pos);
|
state.forceValue(*vAttrs, pos);
|
||||||
if (vAttrs->type() != nAttrs ||
|
if (vAttrs->type() != nAttrs ||
|
||||||
|
@ -1510,23 +1550,24 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
||||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
||||||
std::set<std::string> allAttrNames;
|
std::set<std::string> allAttrNames;
|
||||||
for (auto & attr : *vAttrs->attrs)
|
for (auto & attr : *vAttrs->attrs)
|
||||||
allAttrNames.insert(attr.name);
|
allAttrNames.insert(state.symbols[attr.name]);
|
||||||
throwEvalError(
|
state.throwEvalError(
|
||||||
pos,
|
pos,
|
||||||
Suggestions::bestMatches(allAttrNames, name),
|
Suggestions::bestMatches(allAttrNames, state.symbols[name]),
|
||||||
"attribute '%1%' missing", name, env, *this);
|
"attribute '%1%' missing", state.symbols[name], env, *this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vAttrs = j->value;
|
vAttrs = j->value;
|
||||||
pos2 = j->pos;
|
pos2 = j->pos;
|
||||||
if (state.countCalls) state.attrSelects[*pos2]++;
|
if (state.countCalls) state.attrSelects[pos2]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.forceValue(*vAttrs, (*pos2 != noPos ? *pos2 : this->pos ) );
|
state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (*pos2 != noPos && pos2->file != state.sDerivationNix)
|
auto pos2r = state.positions[pos2];
|
||||||
addErrorTrace(e, *pos2, "while evaluating the attribute '%1%'",
|
if (pos2 && pos2r.file != state.derivationNixPath)
|
||||||
|
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
|
||||||
showAttrPath(state, env, attrPath));
|
showAttrPath(state, env, attrPath));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -1545,7 +1586,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
||||||
for (auto & i : attrPath) {
|
for (auto & i : attrPath) {
|
||||||
state.forceValue(*vAttrs, noPos);
|
state.forceValue(*vAttrs, noPos);
|
||||||
Bindings::iterator j;
|
Bindings::iterator j;
|
||||||
Symbol name = getName(i, state, env);
|
auto name = getName(i, state, env);
|
||||||
if (vAttrs->type() != nAttrs ||
|
if (vAttrs->type() != nAttrs ||
|
||||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||||
{
|
{
|
||||||
|
@ -1566,9 +1607,11 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos)
|
void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos)
|
||||||
{
|
{
|
||||||
auto trace = evalSettings.traceFunctionCalls ? std::make_unique<FunctionCallTrace>(pos) : nullptr;
|
auto trace = evalSettings.traceFunctionCalls
|
||||||
|
? std::make_unique<FunctionCallTrace>(positions[pos])
|
||||||
|
: nullptr;
|
||||||
|
|
||||||
forceValue(fun, pos);
|
forceValue(fun, pos);
|
||||||
|
|
||||||
|
@ -1593,7 +1636,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
ExprLambda & lambda(*vCur.lambda.fun);
|
ExprLambda & lambda(*vCur.lambda.fun);
|
||||||
|
|
||||||
auto size =
|
auto size =
|
||||||
(lambda.arg.empty() ? 0 : 1) +
|
(!lambda.arg ? 0 : 1) +
|
||||||
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
||||||
Env & env2(allocEnv(size));
|
Env & env2(allocEnv(size));
|
||||||
env2.up = vCur.lambda.env;
|
env2.up = vCur.lambda.env;
|
||||||
|
@ -1605,7 +1648,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
else {
|
else {
|
||||||
forceAttrs(*args[0], pos);
|
forceAttrs(*args[0], pos);
|
||||||
|
|
||||||
if (!lambda.arg.empty())
|
if (lambda.arg)
|
||||||
env2.values[displ++] = args[0];
|
env2.values[displ++] = args[0];
|
||||||
|
|
||||||
/* For each formal argument, get the actual argument. If
|
/* For each formal argument, get the actual argument. If
|
||||||
|
@ -1633,10 +1676,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
if (!lambda.formals->has(i.name)) {
|
if (!lambda.formals->has(i.name)) {
|
||||||
std::set<std::string> formalNames;
|
std::set<std::string> formalNames;
|
||||||
for (auto & formal : lambda.formals->formals)
|
for (auto & formal : lambda.formals->formals)
|
||||||
formalNames.insert(formal.name);
|
formalNames.insert(symbols[formal.name]);
|
||||||
throwTypeError(
|
throwTypeError(
|
||||||
pos,
|
pos,
|
||||||
Suggestions::bestMatches(formalNames, i.name),
|
Suggestions::bestMatches(formalNames, symbols[i.name]),
|
||||||
"%1% called with unexpected argument '%2%'",
|
"%1% called with unexpected argument '%2%'",
|
||||||
lambda, i.name, *fun.lambda.env, lambda);
|
lambda, i.name, *fun.lambda.env, lambda);
|
||||||
}
|
}
|
||||||
|
@ -1662,8 +1705,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
if (loggerSettings.showTrace.get()) {
|
if (loggerSettings.showTrace.get()) {
|
||||||
addErrorTrace(e, lambda.pos, "while evaluating %s",
|
addErrorTrace(e, lambda.pos, "while evaluating %s",
|
||||||
(lambda.name.set()
|
(lambda.name
|
||||||
? "'" + (const std::string &) lambda.name + "'"
|
? concatStrings("'", symbols[lambda.name], "'")
|
||||||
: "anonymous lambda"));
|
: "anonymous lambda"));
|
||||||
addErrorTrace(e, pos, "from call site%s", "");
|
addErrorTrace(e, pos, "from call site%s", "");
|
||||||
}
|
}
|
||||||
|
@ -1812,8 +1855,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
||||||
Nix attempted to evaluate a function as a top level expression; in
|
Nix attempted to evaluate a function as a top level expression; in
|
||||||
this case it must have its arguments supplied either by default
|
this case it must have its arguments supplied either by default
|
||||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||||
https://nixos.org/manual/nix/stable/#ss-functions.)",
|
https://nixos.org/manual/nix/stable/#ss-functions.)", symbols[i.name],,
|
||||||
i.name,
|
|
||||||
*fun.lambda.env, *fun.lambda.fun);
|
*fun.lambda.env, *fun.lambda.fun);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1845,8 +1887,8 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
if (!state.evalBool(env, cond, pos)) {
|
if (!state.evalBool(env, cond, pos)) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
cond->show(out);
|
cond->show(state.symbols, out);
|
||||||
throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
|
state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this);
|
||||||
}
|
}
|
||||||
body->eval(state, env, v);
|
body->eval(state, env, v);
|
||||||
}
|
}
|
||||||
|
@ -1939,7 +1981,7 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos)
|
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos)
|
||||||
{
|
{
|
||||||
nrListConcats++;
|
nrListConcats++;
|
||||||
|
|
||||||
|
@ -2022,16 +2064,15 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
firstType = nFloat;
|
firstType = nFloat;
|
||||||
nf = n;
|
nf = n;
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else {
|
} else
|
||||||
throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this);
|
state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this);
|
||||||
}
|
|
||||||
} else if (firstType == nFloat) {
|
} else if (firstType == nFloat) {
|
||||||
if (vTmp.type() == nInt) {
|
if (vTmp.type() == nInt) {
|
||||||
nf += vTmp.integer;
|
nf += vTmp.integer;
|
||||||
} else if (vTmp.type() == nFloat) {
|
} else if (vTmp.type() == nFloat) {
|
||||||
nf += vTmp.fpoint;
|
nf += vTmp.fpoint;
|
||||||
} else
|
} else
|
||||||
throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this);
|
state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this);
|
||||||
} else {
|
} else {
|
||||||
if (s.empty()) s.reserve(es->size());
|
if (s.empty()) s.reserve(es->size());
|
||||||
/* skip canonization of first path, which would only be not
|
/* skip canonization of first path, which would only be not
|
||||||
|
@ -2051,7 +2092,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
v.mkFloat(nf);
|
v.mkFloat(nf);
|
||||||
else if (firstType == nPath) {
|
else if (firstType == nPath) {
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this);
|
state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this););
|
||||||
v.mkPath(canonPath(str()));
|
v.mkPath(canonPath(str()));
|
||||||
} else
|
} else
|
||||||
v.mkStringMove(c_str(), context);
|
v.mkStringMove(c_str(), context);
|
||||||
|
@ -2060,7 +2101,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
|
|
||||||
void ExprPos::eval(EvalState & state, Env & env, Value & v)
|
void ExprPos::eval(EvalState & state, Env & env, Value & v)
|
||||||
{
|
{
|
||||||
state.mkPos(v, ptr(&pos));
|
state.mkPos(v, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2090,7 +2131,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||||
|
|
||||||
recurse(*i.value);
|
recurse(*i.value);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
addErrorTrace(e, *i.pos, "while evaluating the attribute '%1%'", i.name);
|
addErrorTrace(e, i.pos, "while evaluating the attribute '%1%'", symbols[i.name]);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2105,7 +2146,7 @@ void EvalState::forceValueDeep(Value & v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
NixInt EvalState::forceInt(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nInt)
|
if (v.type() != nInt)
|
||||||
|
@ -2115,7 +2156,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
|
NixFloat EvalState::forceFloat(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() == nInt)
|
if (v.type() == nInt)
|
||||||
|
@ -2126,7 +2167,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EvalState::forceBool(Value & v, const Pos & pos)
|
bool EvalState::forceBool(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nBool)
|
if (v.type() != nBool)
|
||||||
|
@ -2141,7 +2182,7 @@ bool EvalState::isFunctor(Value & fun)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void EvalState::forceFunction(Value & v, const Pos & pos)
|
void EvalState::forceFunction(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nFunction && !isFunctor(v))
|
if (v.type() != nFunction && !isFunctor(v))
|
||||||
|
@ -2149,7 +2190,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string_view EvalState::forceString(Value & v, const Pos & pos)
|
std::string_view EvalState::forceString(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
if (v.type() != nString) {
|
if (v.type() != nString) {
|
||||||
|
@ -2199,7 +2240,7 @@ NixStringContext Value::getContext(const Store & store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos)
|
std::string_view EvalState::forceString(Value & v, PathSet & context, const PosIdx pos)
|
||||||
{
|
{
|
||||||
auto s = forceString(v, pos);
|
auto s = forceString(v, pos);
|
||||||
copyContext(v, context);
|
copyContext(v, context);
|
||||||
|
@ -2207,7 +2248,7 @@ std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos)
|
std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos)
|
||||||
{
|
{
|
||||||
auto s = forceString(v, pos);
|
auto s = forceString(v, pos);
|
||||||
if (v.string.context) {
|
if (v.string.context) {
|
||||||
|
@ -2227,13 +2268,13 @@ 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, *i->pos);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::optional<std::string> EvalState::tryAttrsToString(const Pos & pos, Value & v,
|
std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v,
|
||||||
PathSet & context, bool coerceMore, bool copyToStore)
|
PathSet & context, bool coerceMore, bool copyToStore)
|
||||||
{
|
{
|
||||||
auto i = v.attrs->find(sToString);
|
auto i = v.attrs->find(sToString);
|
||||||
|
@ -2246,7 +2287,7 @@ std::optional<std::string> EvalState::tryAttrsToString(const Pos & pos, Value &
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context,
|
BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet & context,
|
||||||
bool coerceMore, bool copyToStore, bool canonicalizePath)
|
bool coerceMore, bool copyToStore, bool canonicalizePath)
|
||||||
{
|
{
|
||||||
forceValue(v, pos);
|
forceValue(v, pos);
|
||||||
|
@ -2276,7 +2317,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet &
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.type() == nExternal)
|
if (v.type() == nExternal)
|
||||||
return v.external->coerceToString(pos, context, coerceMore, copyToStore);
|
return v.external->coerceToString(positions[pos], context, coerceMore, copyToStore);
|
||||||
|
|
||||||
if (coerceMore) {
|
if (coerceMore) {
|
||||||
/* Note that `false' is represented as an empty string for
|
/* Note that `false' is represented as an empty string for
|
||||||
|
@ -2328,7 +2369,7 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context)
|
||||||
{
|
{
|
||||||
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
||||||
if (path == "" || path[0] != '/')
|
if (path == "" || path[0] != '/')
|
||||||
|
@ -2337,14 +2378,14 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context)
|
StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & context)
|
||||||
{
|
{
|
||||||
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
auto path = coerceToString(pos, v, context, false, false).toOwned();
|
||||||
if (auto storePath = store->maybeParseStorePath(path))
|
if (auto storePath = store->maybeParseStorePath(path))
|
||||||
return *storePath;
|
return *storePath;
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("path '%1%' is not in the Nix store", path),
|
.msg = hintfmt("path '%1%' is not in the Nix store", path),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2514,14 +2555,14 @@ void EvalState::printStats()
|
||||||
auto list = topObj.list("functions");
|
auto list = topObj.list("functions");
|
||||||
for (auto & i : functionCalls) {
|
for (auto & i : functionCalls) {
|
||||||
auto obj = list.object();
|
auto obj = list.object();
|
||||||
if (i.first->name.set())
|
if (i.first->name)
|
||||||
obj.attr("name", (const std::string &) i.first->name);
|
obj.attr("name", (const std::string &) i.first->name);
|
||||||
else
|
else
|
||||||
obj.attr("name", nullptr);
|
obj.attr("name", nullptr);
|
||||||
if (i.first->pos) {
|
if (auto pos = positions[i.first->pos]) {
|
||||||
obj.attr("file", (const std::string &) i.first->pos.file);
|
obj.attr("file", (const std::string &) pos.file);
|
||||||
obj.attr("line", i.first->pos.line);
|
obj.attr("line", pos.line);
|
||||||
obj.attr("column", i.first->pos.column);
|
obj.attr("column", pos.column);
|
||||||
}
|
}
|
||||||
obj.attr("count", i.second);
|
obj.attr("count", i.second);
|
||||||
}
|
}
|
||||||
|
@ -2530,10 +2571,10 @@ void EvalState::printStats()
|
||||||
auto list = topObj.list("attributes");
|
auto list = topObj.list("attributes");
|
||||||
for (auto & i : attrSelects) {
|
for (auto & i : attrSelects) {
|
||||||
auto obj = list.object();
|
auto obj = list.object();
|
||||||
if (i.first) {
|
if (auto pos = positions[i.first]) {
|
||||||
obj.attr("file", (const std::string &) i.first.file);
|
obj.attr("file", (const std::string &) pos.file);
|
||||||
obj.attr("line", i.first.line);
|
obj.attr("line", pos.line);
|
||||||
obj.attr("column", i.first.column);
|
obj.attr("column", pos.column);
|
||||||
}
|
}
|
||||||
obj.attr("count", i.second);
|
obj.attr("count", i.second);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ class StorePath;
|
||||||
enum RepairFlag : bool;
|
enum RepairFlag : bool;
|
||||||
|
|
||||||
|
|
||||||
typedef void (* PrimOpFun) (EvalState & state, const Pos & pos, Value * * args, Value & v);
|
typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
|
|
||||||
void printEnvBindings(const Expr &expr, const Env &env);
|
void printEnvBindings(const Expr &expr, const Env &env);
|
||||||
void printEnvBindings(const StaticEnv &se, const Env &env, int lvl = 0);
|
void printEnvBindings(const StaticEnv &se, const Env &env, int lvl = 0);
|
||||||
|
@ -32,7 +32,7 @@ struct PrimOp
|
||||||
{
|
{
|
||||||
PrimOpFun fun;
|
PrimOpFun fun;
|
||||||
size_t arity;
|
size_t arity;
|
||||||
Symbol name;
|
std::string name;
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
const char * doc = nullptr;
|
const char * doc = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -57,7 +57,8 @@ void copyContext(const Value & v, PathSet & context);
|
||||||
typedef std::map<Path, StorePath> SrcToStore;
|
typedef std::map<Path, StorePath> SrcToStore;
|
||||||
|
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Value & v);
|
std::ostream & printValue(const EvalState & state, std::ostream & str, const Value & v);
|
||||||
|
std::string printValue(const EvalState & state, const Value & v);
|
||||||
|
|
||||||
|
|
||||||
typedef std::pair<std::string, std::string> SearchPathElem;
|
typedef std::pair<std::string, std::string> SearchPathElem;
|
||||||
|
@ -84,6 +85,9 @@ class EvalState
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SymbolTable symbols;
|
SymbolTable symbols;
|
||||||
|
PosTable positions;
|
||||||
|
|
||||||
|
static inline std::string derivationNixPath = "//builtin/derivation.nix";
|
||||||
|
|
||||||
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue,
|
||||||
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls,
|
||||||
|
@ -222,7 +226,7 @@ public:
|
||||||
|
|
||||||
/* Look up a file in the search path. */
|
/* Look up a file in the search path. */
|
||||||
Path findFile(const std::string_view path);
|
Path findFile(const std::string_view path);
|
||||||
Path findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos = noPos);
|
Path findFile(SearchPath & searchPath, const std::string_view path, const PosIdx 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);
|
||||||
|
@ -234,14 +238,14 @@ public:
|
||||||
/* Evaluation the expression, then verify that it has the expected
|
/* Evaluation the expression, then verify that it has the expected
|
||||||
type. */
|
type. */
|
||||||
inline bool evalBool(Env & env, Expr * e);
|
inline bool evalBool(Env & env, Expr * e);
|
||||||
inline bool evalBool(Env & env, Expr * e, const Pos & pos);
|
inline bool evalBool(Env & env, Expr * e, const PosIdx pos);
|
||||||
inline void evalAttrs(Env & env, Expr * e, Value & v);
|
inline void evalAttrs(Env & env, Expr * e, Value & v);
|
||||||
|
|
||||||
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
/* If `v' is a thunk, enter it and overwrite `v' with the result
|
||||||
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);
|
inline void forceValue(Value & v, const PosIdx pos);
|
||||||
|
|
||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
inline void forceValue(Value & v, Callable getPos);
|
inline void forceValue(Value & v, Callable getPos);
|
||||||
|
@ -251,33 +255,72 @@ public:
|
||||||
void forceValueDeep(Value & v);
|
void forceValueDeep(Value & v);
|
||||||
|
|
||||||
/* Force `v', and then verify that it has the expected type. */
|
/* Force `v', and then verify that it has the expected type. */
|
||||||
NixInt forceInt(Value & v, const Pos & pos);
|
NixInt forceInt(Value & v, const PosIdx pos);
|
||||||
NixFloat forceFloat(Value & v, const Pos & pos);
|
NixFloat forceFloat(Value & v, const PosIdx pos);
|
||||||
bool forceBool(Value & v, const Pos & pos);
|
bool forceBool(Value & v, const PosIdx pos);
|
||||||
|
|
||||||
void forceAttrs(Value & v, const Pos & pos);
|
void forceAttrs(Value & v, const PosIdx pos);
|
||||||
|
|
||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
inline void forceAttrs(Value & v, Callable getPos);
|
inline void forceAttrs(Value & v, Callable getPos);
|
||||||
|
|
||||||
inline void forceList(Value & v, const Pos & pos);
|
inline void forceList(Value & v, const PosIdx pos);
|
||||||
void forceFunction(Value & v, const Pos & pos); // either lambda or primop
|
void forceFunction(Value & v, const PosIdx pos); // either lambda or primop
|
||||||
std::string_view forceString(Value & v, const Pos & pos = noPos);
|
std::string_view forceString(Value & v, const PosIdx pos = noPos);
|
||||||
std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos);
|
std::string_view forceString(Value & v, PathSet & context, const PosIdx pos = noPos);
|
||||||
std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos);
|
std::string_view forceStringNoCtx(Value & v, const PosIdx pos = noPos);
|
||||||
|
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx pos, const char * s) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const PosIdx pos, const char * s, const Value & v) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const char * s, const std::string & s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||||
|
const std::string & s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const char * s, const std::string & s2, const std::string & s3) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const PosIdx pos, const char * s) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||||
|
const ExprLambda & fun, const Symbol s2) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwTypeError(const char * s, const Value & v) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const;
|
||||||
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const;
|
||||||
|
|
||||||
|
[[gnu::noinline]]
|
||||||
|
void addErrorTrace(Error & e, const char * s, const std::string & s2) const;
|
||||||
|
[[gnu::noinline]]
|
||||||
|
void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const;
|
||||||
|
|
||||||
|
public:
|
||||||
/* 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"'). */
|
||||||
bool isDerivation(Value & v);
|
bool isDerivation(Value & v);
|
||||||
|
|
||||||
std::optional<std::string> tryAttrsToString(const Pos & pos, Value & v,
|
std::optional<std::string> tryAttrsToString(const PosIdx pos, Value & v,
|
||||||
PathSet & context, bool coerceMore = false, bool copyToStore = true);
|
PathSet & context, bool coerceMore = false, bool copyToStore = true);
|
||||||
|
|
||||||
/* String coercion. Converts strings, paths and derivations to a
|
/* String coercion. Converts strings, paths and derivations to a
|
||||||
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. */
|
||||||
BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context,
|
BackedStringView coerceToString(const PosIdx pos, Value & v, PathSet & context,
|
||||||
bool coerceMore = false, bool copyToStore = true,
|
bool coerceMore = false, bool copyToStore = true,
|
||||||
bool canonicalizePath = true);
|
bool canonicalizePath = true);
|
||||||
|
|
||||||
|
@ -286,10 +329,10 @@ public:
|
||||||
/* Path coercion. Converts strings, paths and derivations to a
|
/* Path coercion. Converts strings, paths and derivations to a
|
||||||
path. The result is guaranteed to be a canonicalised, absolute
|
path. The result is guaranteed to be a canonicalised, absolute
|
||||||
path. Nothing is copied to the store. */
|
path. Nothing is copied to the store. */
|
||||||
Path coerceToPath(const Pos & pos, Value & v, PathSet & context);
|
Path coerceToPath(const PosIdx pos, Value & v, PathSet & context);
|
||||||
|
|
||||||
/* Like coerceToPath, but the result must be a store path. */
|
/* Like coerceToPath, but the result must be a store path. */
|
||||||
StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context);
|
StorePath coerceToStorePath(const PosIdx pos, Value & v, PathSet & context);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -322,7 +365,7 @@ public:
|
||||||
struct Doc
|
struct Doc
|
||||||
{
|
{
|
||||||
Pos pos;
|
Pos pos;
|
||||||
std::optional<Symbol> name;
|
std::optional<std::string> name;
|
||||||
size_t arity;
|
size_t arity;
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
const char * doc;
|
const char * doc;
|
||||||
|
@ -350,9 +393,9 @@ public:
|
||||||
bool isFunctor(Value & fun);
|
bool isFunctor(Value & fun);
|
||||||
|
|
||||||
// FIXME: use std::span
|
// FIXME: use std::span
|
||||||
void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos);
|
void callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos);
|
||||||
|
|
||||||
void callFunction(Value & fun, Value & arg, Value & vRes, const Pos & pos)
|
void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
|
||||||
{
|
{
|
||||||
Value * args[] = {&arg};
|
Value * args[] = {&arg};
|
||||||
callFunction(fun, 1, args, vRes, pos);
|
callFunction(fun, 1, args, vRes, pos);
|
||||||
|
@ -366,7 +409,7 @@ public:
|
||||||
inline Value * allocValue();
|
inline Value * allocValue();
|
||||||
inline Env & allocEnv(size_t size);
|
inline Env & allocEnv(size_t size);
|
||||||
|
|
||||||
Value * allocAttr(Value & vAttrs, const Symbol & name);
|
Value * allocAttr(Value & vAttrs, Symbol name);
|
||||||
Value * allocAttr(Value & vAttrs, std::string_view name);
|
Value * allocAttr(Value & vAttrs, std::string_view name);
|
||||||
|
|
||||||
Bindings * allocBindings(size_t capacity);
|
Bindings * allocBindings(size_t capacity);
|
||||||
|
@ -378,9 +421,9 @@ public:
|
||||||
|
|
||||||
void mkList(Value & v, size_t length);
|
void mkList(Value & v, size_t length);
|
||||||
void mkThunk_(Value & v, Expr * expr);
|
void mkThunk_(Value & v, Expr * expr);
|
||||||
void mkPos(Value & v, ptr<Pos> pos);
|
void mkPos(Value & v, PosIdx pos);
|
||||||
|
|
||||||
void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos);
|
void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos);
|
||||||
|
|
||||||
/* Print statistics. */
|
/* Print statistics. */
|
||||||
void printStats();
|
void printStats();
|
||||||
|
@ -408,7 +451,7 @@ private:
|
||||||
|
|
||||||
bool countCalls;
|
bool countCalls;
|
||||||
|
|
||||||
typedef std::map<Symbol, size_t> PrimOpCalls;
|
typedef std::map<std::string, size_t> PrimOpCalls;
|
||||||
PrimOpCalls primOpCalls;
|
PrimOpCalls primOpCalls;
|
||||||
|
|
||||||
typedef std::map<ExprLambda *, size_t> FunctionCalls;
|
typedef std::map<ExprLambda *, size_t> FunctionCalls;
|
||||||
|
@ -416,7 +459,7 @@ private:
|
||||||
|
|
||||||
void incrFunctionCall(ExprLambda * fun);
|
void incrFunctionCall(ExprLambda * fun);
|
||||||
|
|
||||||
typedef std::map<Pos, size_t> AttrSelects;
|
typedef std::map<PosIdx, size_t> AttrSelects;
|
||||||
AttrSelects attrSelects;
|
AttrSelects attrSelects;
|
||||||
|
|
||||||
friend struct ExprOpUpdate;
|
friend struct ExprOpUpdate;
|
||||||
|
@ -427,9 +470,9 @@ private:
|
||||||
friend struct ExprFloat;
|
friend struct ExprFloat;
|
||||||
friend struct ExprPath;
|
friend struct ExprPath;
|
||||||
friend struct ExprSelect;
|
friend struct ExprSelect;
|
||||||
friend void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
friend void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
friend void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
friend void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
friend void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
|
|
||||||
friend struct Value;
|
friend struct Value;
|
||||||
};
|
};
|
||||||
|
|
|
@ -72,7 +72,7 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree(
|
||||||
return {std::move(tree), resolvedRef, lockedRef};
|
return {std::move(tree), resolvedRef, lockedRef};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
|
static void forceTrivialValue(EvalState & state, Value & value, const PosIdx pos)
|
||||||
{
|
{
|
||||||
if (value.isThunk() && value.isTrivial())
|
if (value.isThunk() && value.isTrivial())
|
||||||
state.forceValue(value, pos);
|
state.forceValue(value, pos);
|
||||||
|
@ -80,20 +80,20 @@ static void forceTrivialValue(EvalState & state, Value & value, const Pos & pos)
|
||||||
|
|
||||||
|
|
||||||
static void expectType(EvalState & state, ValueType type,
|
static void expectType(EvalState & state, ValueType type,
|
||||||
Value & value, const Pos & pos)
|
Value & value, const PosIdx pos)
|
||||||
{
|
{
|
||||||
forceTrivialValue(state, value, pos);
|
forceTrivialValue(state, value, pos);
|
||||||
if (value.type() != type)
|
if (value.type() != type)
|
||||||
throw Error("expected %s but got %s at %s",
|
throw Error("expected %s but got %s at %s",
|
||||||
showType(type), showType(value.type()), pos);
|
showType(type), showType(value.type()), state.positions[pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
EvalState & state, Value * value, const Pos & pos,
|
EvalState & state, Value * value, const PosIdx pos,
|
||||||
const std::optional<Path> & baseDir, InputPath lockRootPath);
|
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 PosIdx pos,
|
||||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||||
{
|
{
|
||||||
expectType(state, nAttrs, *value, pos);
|
expectType(state, nAttrs, *value, pos);
|
||||||
|
@ -111,37 +111,39 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
for (nix::Attr attr : *(value->attrs)) {
|
for (nix::Attr attr : *(value->attrs)) {
|
||||||
try {
|
try {
|
||||||
if (attr.name == sUrl) {
|
if (attr.name == sUrl) {
|
||||||
expectType(state, nString, *attr.value, *attr.pos);
|
expectType(state, nString, *attr.value, attr.pos);
|
||||||
url = attr.value->string.s;
|
url = attr.value->string.s;
|
||||||
attrs.emplace("url", *url);
|
attrs.emplace("url", *url);
|
||||||
} else if (attr.name == sFlake) {
|
} else if (attr.name == sFlake) {
|
||||||
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, lockRootPath);
|
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);
|
||||||
auto follows(parseInputPath(attr.value->string.s));
|
auto follows(parseInputPath(attr.value->string.s));
|
||||||
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
|
||||||
input.follows = follows;
|
input.follows = follows;
|
||||||
} else {
|
} else {
|
||||||
switch (attr.value->type()) {
|
switch (attr.value->type()) {
|
||||||
case nString:
|
case nString:
|
||||||
attrs.emplace(attr.name, attr.value->string.s);
|
attrs.emplace(state.symbols[attr.name], attr.value->string.s);
|
||||||
break;
|
break;
|
||||||
case nBool:
|
case nBool:
|
||||||
attrs.emplace(attr.name, Explicit<bool> { attr.value->boolean });
|
attrs.emplace(state.symbols[attr.name], Explicit<bool> { attr.value->boolean });
|
||||||
break;
|
break;
|
||||||
case nInt:
|
case nInt:
|
||||||
attrs.emplace(attr.name, (long unsigned int)attr.value->integer);
|
attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
throw TypeError("flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
|
||||||
attr.name, showType(*attr.value));
|
state.symbols[attr.name], showType(*attr.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(*attr.pos, hintfmt("in flake attribute '%s'", attr.name));
|
e.addTrace(
|
||||||
|
state.positions[attr.pos],
|
||||||
|
hintfmt("in flake attribute '%s'", state.symbols[attr.name]));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,13 +152,13 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
try {
|
try {
|
||||||
input.ref = FlakeRef::fromAttrs(attrs);
|
input.ref = FlakeRef::fromAttrs(attrs);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("in flake input"));
|
e.addTrace(state.positions[pos], hintfmt("in flake input"));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
attrs.erase("url");
|
attrs.erase("url");
|
||||||
if (!attrs.empty())
|
if (!attrs.empty())
|
||||||
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, pos);
|
throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]);
|
||||||
if (url)
|
if (url)
|
||||||
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
|
input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake);
|
||||||
}
|
}
|
||||||
|
@ -168,7 +170,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 PosIdx pos,
|
||||||
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
const std::optional<Path> & baseDir, InputPath lockRootPath)
|
||||||
{
|
{
|
||||||
std::map<FlakeId, FlakeInput> inputs;
|
std::map<FlakeId, FlakeInput> inputs;
|
||||||
|
@ -176,11 +178,11 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
expectType(state, nAttrs, *value, pos);
|
expectType(state, nAttrs, *value, pos);
|
||||||
|
|
||||||
for (nix::Attr & inputAttr : *(*value).attrs) {
|
for (nix::Attr & inputAttr : *(*value).attrs) {
|
||||||
inputs.emplace(inputAttr.name,
|
inputs.emplace(state.symbols[inputAttr.name],
|
||||||
parseFlakeInput(state,
|
parseFlakeInput(state,
|
||||||
inputAttr.name,
|
state.symbols[inputAttr.name],
|
||||||
inputAttr.value,
|
inputAttr.value,
|
||||||
*inputAttr.pos,
|
inputAttr.pos,
|
||||||
baseDir,
|
baseDir,
|
||||||
lockRootPath));
|
lockRootPath));
|
||||||
}
|
}
|
||||||
|
@ -218,28 +220,28 @@ static Flake getFlake(
|
||||||
Value vInfo;
|
Value vInfo;
|
||||||
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
|
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
|
||||||
|
|
||||||
expectType(state, nAttrs, vInfo, Pos(foFile, state.symbols.create(flakeFile), 0, 0));
|
expectType(state, nAttrs, vInfo, state.positions.add({flakeFile, foFile}, 0, 0));
|
||||||
|
|
||||||
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||||
expectType(state, nString, *description->value, *description->pos);
|
expectType(state, nString, *description->value, description->pos);
|
||||||
flake.description = description->value->string.s;
|
flake.description = description->value->string.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
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, lockRootPath);
|
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath);
|
||||||
|
|
||||||
auto sOutputs = state.symbols.create("outputs");
|
auto sOutputs = state.symbols.create("outputs");
|
||||||
|
|
||||||
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
if (auto outputs = vInfo.attrs->get(sOutputs)) {
|
||||||
expectType(state, nFunction, *outputs->value, *outputs->pos);
|
expectType(state, nFunction, *outputs->value, outputs->pos);
|
||||||
|
|
||||||
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
|
if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) {
|
||||||
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
for (auto & formal : outputs->value->lambda.fun->formals->formals) {
|
||||||
if (formal.name != state.sSelf)
|
if (formal.name != state.sSelf)
|
||||||
flake.inputs.emplace(formal.name, FlakeInput {
|
flake.inputs.emplace(state.symbols[formal.name], FlakeInput {
|
||||||
.ref = parseFlakeRef(formal.name)
|
.ref = parseFlakeRef(state.symbols[formal.name])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,35 +252,41 @@ static Flake getFlake(
|
||||||
auto sNixConfig = state.symbols.create("nixConfig");
|
auto sNixConfig = state.symbols.create("nixConfig");
|
||||||
|
|
||||||
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
if (auto nixConfig = vInfo.attrs->get(sNixConfig)) {
|
||||||
expectType(state, nAttrs, *nixConfig->value, *nixConfig->pos);
|
expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
|
||||||
|
|
||||||
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, std::string(state.forceStringNoCtx(*setting.value, *setting.pos))});
|
flake.config.settings.emplace(
|
||||||
|
state.symbols[setting.name],
|
||||||
|
std::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.emplace(
|
flake.config.settings.emplace(
|
||||||
setting.name,
|
state.symbols[setting.name],
|
||||||
state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
|
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.emplace(
|
||||||
|
state.symbols[setting.name],
|
||||||
|
state.forceInt(*setting.value, setting.pos));
|
||||||
else if (setting.value->type() == nBool)
|
else if (setting.value->type() == nBool)
|
||||||
flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, *setting.pos) }});
|
flake.config.settings.emplace(
|
||||||
|
state.symbols[setting.name],
|
||||||
|
Explicit<bool> { state.forceBool(*setting.value, setting.pos) });
|
||||||
else if (setting.value->type() == nList) {
|
else if (setting.value->type() == nList) {
|
||||||
std::vector<std::string> ss;
|
std::vector<std::string> ss;
|
||||||
for (auto elem : setting.value->listItems()) {
|
for (auto elem : setting.value->listItems()) {
|
||||||
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));
|
state.symbols[setting.name], showType(*setting.value));
|
||||||
ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos));
|
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos));
|
||||||
}
|
}
|
||||||
flake.config.settings.insert({setting.name, ss});
|
flake.config.settings.emplace(state.symbols[setting.name], ss);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw TypeError("flake configuration setting '%s' is %s",
|
throw TypeError("flake configuration setting '%s' is %s",
|
||||||
setting.name, showType(*setting.value));
|
state.symbols[setting.name], showType(*setting.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +296,7 @@ static Flake getFlake(
|
||||||
attr.name != sOutputs &&
|
attr.name != sOutputs &&
|
||||||
attr.name != sNixConfig)
|
attr.name != sNixConfig)
|
||||||
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
|
throw Error("flake '%s' has an unsupported attribute '%s', at %s",
|
||||||
lockedRef, attr.name, *attr.pos);
|
lockedRef, state.symbols[attr.name], state.positions[attr.pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return flake;
|
return flake;
|
||||||
|
@ -704,12 +712,12 @@ void callFlake(EvalState & state,
|
||||||
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
|
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
|
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos));
|
||||||
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
|
||||||
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
if (evalSettings.pureEval && !flakeRef.input.isLocked())
|
||||||
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);
|
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]);
|
||||||
|
|
||||||
callFlake(state,
|
callFlake(state,
|
||||||
lockFlake(state, flakeRef,
|
lockFlake(state, flakeRef,
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace nix {
|
||||||
|
|
||||||
struct FunctionCallTrace
|
struct FunctionCallTrace
|
||||||
{
|
{
|
||||||
const Pos & pos;
|
const Pos pos;
|
||||||
FunctionCallTrace(const Pos & pos);
|
FunctionCallTrace(const Pos & pos);
|
||||||
~FunctionCallTrace();
|
~FunctionCallTrace();
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,7 +61,7 @@ std::string DrvInfo::querySystem() const
|
||||||
{
|
{
|
||||||
if (system == "" && attrs) {
|
if (system == "" && attrs) {
|
||||||
auto i = attrs->find(state->sSystem);
|
auto i = attrs->find(state->sSystem);
|
||||||
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos);
|
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, i->pos);
|
||||||
}
|
}
|
||||||
return system;
|
return system;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath() const
|
||||||
if (i == attrs->end())
|
if (i == attrs->end())
|
||||||
drvPath = {std::nullopt};
|
drvPath = {std::nullopt};
|
||||||
else
|
else
|
||||||
drvPath = {state->coerceToStorePath(*i->pos, *i->value, context)};
|
drvPath = {state->coerceToStorePath(i->pos, *i->value, context)};
|
||||||
}
|
}
|
||||||
return drvPath.value_or(std::nullopt);
|
return drvPath.value_or(std::nullopt);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ StorePath DrvInfo::queryOutPath() const
|
||||||
Bindings::iterator i = attrs->find(state->sOutPath);
|
Bindings::iterator i = attrs->find(state->sOutPath);
|
||||||
PathSet context;
|
PathSet context;
|
||||||
if (i != attrs->end())
|
if (i != attrs->end())
|
||||||
outPath = state->coerceToStorePath(*i->pos, *i->value, context);
|
outPath = state->coerceToStorePath(i->pos, *i->value, context);
|
||||||
}
|
}
|
||||||
if (!outPath)
|
if (!outPath)
|
||||||
throw UnimplementedError("CA derivations are not yet supported");
|
throw UnimplementedError("CA derivations are not yet supported");
|
||||||
|
@ -109,23 +109,23 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall
|
||||||
/* Get the ‘outputs’ list. */
|
/* Get the ‘outputs’ list. */
|
||||||
Bindings::iterator i;
|
Bindings::iterator i;
|
||||||
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
|
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
|
||||||
state->forceList(*i->value, *i->pos);
|
state->forceList(*i->value, i->pos);
|
||||||
|
|
||||||
/* For each output... */
|
/* For each output... */
|
||||||
for (auto elem : i->value->listItems()) {
|
for (auto elem : i->value->listItems()) {
|
||||||
std::string output(state->forceStringNoCtx(*elem, *i->pos));
|
std::string output(state->forceStringNoCtx(*elem, i->pos));
|
||||||
|
|
||||||
if (withPaths) {
|
if (withPaths) {
|
||||||
/* Evaluate the corresponding set. */
|
/* Evaluate the corresponding set. */
|
||||||
Bindings::iterator out = attrs->find(state->symbols.create(output));
|
Bindings::iterator out = attrs->find(state->symbols.create(output));
|
||||||
if (out == attrs->end()) continue; // FIXME: throw error?
|
if (out == attrs->end()) continue; // FIXME: throw error?
|
||||||
state->forceAttrs(*out->value, *i->pos);
|
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);
|
||||||
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
if (outPath == out->value->attrs->end()) continue; // FIXME: throw error?
|
||||||
PathSet context;
|
PathSet context;
|
||||||
outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context));
|
outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context));
|
||||||
} else
|
} else
|
||||||
outputs.emplace(output, std::nullopt);
|
outputs.emplace(output, std::nullopt);
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ Bindings * DrvInfo::getMeta()
|
||||||
if (!attrs) return 0;
|
if (!attrs) return 0;
|
||||||
Bindings::iterator a = attrs->find(state->sMeta);
|
Bindings::iterator a = attrs->find(state->sMeta);
|
||||||
if (a == attrs->end()) return 0;
|
if (a == attrs->end()) return 0;
|
||||||
state->forceAttrs(*a->value, *a->pos);
|
state->forceAttrs(*a->value, a->pos);
|
||||||
meta = a->value->attrs;
|
meta = a->value->attrs;
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ StringSet DrvInfo::queryMetaNames()
|
||||||
StringSet res;
|
StringSet res;
|
||||||
if (!getMeta()) return res;
|
if (!getMeta()) return res;
|
||||||
for (auto & i : *meta)
|
for (auto & i : *meta)
|
||||||
res.insert(i.name);
|
res.emplace(state->symbols[i.name]);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ void DrvInfo::setMeta(const std::string & name, Value * v)
|
||||||
{
|
{
|
||||||
getMeta();
|
getMeta();
|
||||||
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
|
auto attrs = state->buildBindings(1 + (meta ? meta->size() : 0));
|
||||||
Symbol sym = state->symbols.create(name);
|
auto sym = state->symbols.create(name);
|
||||||
if (meta)
|
if (meta)
|
||||||
for (auto i : *meta)
|
for (auto i : *meta)
|
||||||
if (i.name != sym)
|
if (i.name != sym)
|
||||||
|
@ -356,11 +356,11 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||||
there are names clashes between derivations, the derivation
|
there are names clashes between derivations, the derivation
|
||||||
bound to the attribute with the "lower" name should take
|
bound to the attribute with the "lower" name should take
|
||||||
precedence). */
|
precedence). */
|
||||||
for (auto & i : v.attrs->lexicographicOrder()) {
|
for (auto & i : v.attrs->lexicographicOrder(state.symbols)) {
|
||||||
debug("evaluating attribute '%1%'", i->name);
|
debug("evaluating attribute '%1%'", state.symbols[i->name]);
|
||||||
if (!std::regex_match(std::string(i->name), attrRegex))
|
if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex))
|
||||||
continue;
|
continue;
|
||||||
std::string pathPrefix2 = addToPath(pathPrefix, i->name);
|
std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]);
|
||||||
if (combineChannels)
|
if (combineChannels)
|
||||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||||
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) {
|
||||||
|
@ -369,7 +369,7 @@ static void getDerivations(EvalState & state, Value & vIn,
|
||||||
`recurseForDerivations = true' attribute. */
|
`recurseForDerivations = true' attribute. */
|
||||||
if (i->value->type() == nAttrs) {
|
if (i->value->type() == nAttrs) {
|
||||||
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
|
||||||
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos))
|
if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos))
|
||||||
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,9 +28,9 @@ using namespace nix;
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||||
{
|
{
|
||||||
return Pos(data->origin, data->file, loc.first_line, loc.first_column);
|
return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CUR_POS makeCurPos(*yylloc, data)
|
#define CUR_POS makeCurPos(*yylloc, data)
|
||||||
|
@ -155,7 +155,7 @@ or { return OR_KW; }
|
||||||
} catch (const boost::bad_lexical_cast &) {
|
} catch (const boost::bad_lexical_cast &) {
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("invalid integer '%1%'", yytext),
|
.msg = hintfmt("invalid integer '%1%'", yytext),
|
||||||
.errPos = CUR_POS,
|
.errPos = data->state.positions[CUR_POS],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return INT;
|
return INT;
|
||||||
|
@ -165,7 +165,7 @@ or { return OR_KW; }
|
||||||
if (errno != 0)
|
if (errno != 0)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("invalid float '%1%'", yytext),
|
.msg = hintfmt("invalid float '%1%'", yytext),
|
||||||
.errPos = CUR_POS,
|
.errPos = data->state.positions[CUR_POS],
|
||||||
});
|
});
|
||||||
return FLOAT;
|
return FLOAT;
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ or { return OR_KW; }
|
||||||
<INPATH_SLASH><<EOF>> {
|
<INPATH_SLASH><<EOF>> {
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("path has a trailing slash"),
|
.msg = hintfmt("path has a trailing slash"),
|
||||||
.errPos = CUR_POS,
|
.errPos = data->state.positions[CUR_POS],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
#include "eval.hh"
|
||||||
|
#include "symbol-table.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
@ -12,12 +14,6 @@ std::function<void(const Error * error, const Env & env, const Expr & expr)> deb
|
||||||
|
|
||||||
/* Displaying abstract syntax trees. */
|
/* Displaying abstract syntax trees. */
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Expr & e)
|
|
||||||
{
|
|
||||||
e.show(str);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void showString(std::ostream & str, std::string_view s)
|
static void showString(std::ostream & str, std::string_view s)
|
||||||
{
|
{
|
||||||
str << '"';
|
str << '"';
|
||||||
|
@ -30,8 +26,10 @@ static void showString(std::ostream & str, std::string_view s)
|
||||||
str << '"';
|
str << '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
static void showId(std::ostream & str, std::string_view s)
|
std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol)
|
||||||
{
|
{
|
||||||
|
std::string_view s = symbol;
|
||||||
|
|
||||||
if (s.empty())
|
if (s.empty())
|
||||||
str << "\"\"";
|
str << "\"\"";
|
||||||
else if (s == "if") // FIXME: handle other keywords
|
else if (s == "if") // FIXME: handle other keywords
|
||||||
|
@ -40,7 +38,7 @@ static void showId(std::ostream & str, std::string_view s)
|
||||||
char c = s[0];
|
char c = s[0];
|
||||||
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
|
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
|
||||||
showString(str, s);
|
showString(str, s);
|
||||||
return;
|
return str;
|
||||||
}
|
}
|
||||||
for (auto c : s)
|
for (auto c : s)
|
||||||
if (!((c >= 'a' && c <= 'z') ||
|
if (!((c >= 'a' && c <= 'z') ||
|
||||||
|
@ -48,89 +46,104 @@ static void showId(std::ostream & str, std::string_view s)
|
||||||
(c >= '0' && c <= '9') ||
|
(c >= '0' && c <= '9') ||
|
||||||
c == '_' || c == '\'' || c == '-')) {
|
c == '_' || c == '\'' || c == '-')) {
|
||||||
showString(str, s);
|
showString(str, s);
|
||||||
return;
|
return str;
|
||||||
}
|
}
|
||||||
str << s;
|
str << s;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Symbol & sym)
|
|
||||||
{
|
|
||||||
showId(str, *sym.s);
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Expr::show(std::ostream & str) const
|
void Expr::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprInt::show(std::ostream & str) const
|
void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << n;
|
str << n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprFloat::show(std::ostream & str) const
|
void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << nf;
|
str << nf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprString::show(std::ostream & str) const
|
void ExprString::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
showString(str, s);
|
showString(str, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPath::show(std::ostream & str) const
|
void ExprPath::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << s;
|
str << s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprVar::show(std::ostream & str) const
|
void ExprVar::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << name;
|
str << symbols[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprSelect::show(std::ostream & str) const
|
void ExprSelect::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(" << *e << ")." << showAttrPath(attrPath);
|
str << "(";
|
||||||
if (def) str << " or (" << *def << ")";
|
e->show(symbols, str);
|
||||||
|
str << ")." << showAttrPath(symbols, attrPath);
|
||||||
|
if (def) {
|
||||||
|
str << " or (";
|
||||||
|
def->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpHasAttr::show(std::ostream & str) const
|
void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "((" << *e << ") ? " << showAttrPath(attrPath) << ")";
|
str << "((";
|
||||||
|
e->show(symbols, str);
|
||||||
|
str << ") ? " << showAttrPath(symbols, attrPath) << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAttrs::show(std::ostream & str) const
|
void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
if (recursive) str << "rec ";
|
if (recursive) str << "rec ";
|
||||||
str << "{ ";
|
str << "{ ";
|
||||||
typedef const decltype(attrs)::value_type * Attr;
|
typedef const decltype(attrs)::value_type * Attr;
|
||||||
std::vector<Attr> sorted;
|
std::vector<Attr> sorted;
|
||||||
for (auto & i : attrs) sorted.push_back(&i);
|
for (auto & i : attrs) sorted.push_back(&i);
|
||||||
std::sort(sorted.begin(), sorted.end(), [](Attr a, Attr b) {
|
std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) {
|
||||||
return (const std::string &) a->first < (const std::string &) b->first;
|
std::string_view sa = symbols[a->first], sb = symbols[b->first];
|
||||||
});
|
return sa < sb;
|
||||||
|
});
|
||||||
for (auto & i : sorted) {
|
for (auto & i : sorted) {
|
||||||
if (i->second.inherited)
|
if (i->second.inherited)
|
||||||
str << "inherit " << i->first << " " << "; ";
|
str << "inherit " << symbols[i->first] << " " << "; ";
|
||||||
else
|
else {
|
||||||
str << i->first << " = " << *i->second.e << "; ";
|
str << symbols[i->first] << " = ";
|
||||||
|
i->second.e->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto & i : dynamicAttrs) {
|
||||||
|
str << "\"${";
|
||||||
|
i.nameExpr->show(symbols, str);
|
||||||
|
str << "}\" = ";
|
||||||
|
i.valueExpr->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
}
|
}
|
||||||
for (auto & i : dynamicAttrs)
|
|
||||||
str << "\"${" << *i.nameExpr << "}\" = " << *i.valueExpr << "; ";
|
|
||||||
str << "}";
|
str << "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprList::show(std::ostream & str) const
|
void ExprList::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "[ ";
|
str << "[ ";
|
||||||
for (auto & i : elems)
|
for (auto & i : elems) {
|
||||||
str << "(" << *i << ") ";
|
str << "(";
|
||||||
|
i->show(symbols, str);
|
||||||
|
str << ") ";
|
||||||
|
}
|
||||||
str << "]";
|
str << "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprLambda::show(std::ostream & str) const
|
void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(";
|
str << "(";
|
||||||
if (hasFormals()) {
|
if (hasFormals()) {
|
||||||
|
@ -138,74 +151,100 @@ void ExprLambda::show(std::ostream & str) const
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto & i : formals->formals) {
|
for (auto & i : formals->formals) {
|
||||||
if (first) first = false; else str << ", ";
|
if (first) first = false; else str << ", ";
|
||||||
str << i.name;
|
str << symbols[i.name];
|
||||||
if (i.def) str << " ? " << *i.def;
|
if (i.def) {
|
||||||
|
str << " ? ";
|
||||||
|
i.def->show(symbols, str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (formals->ellipsis) {
|
if (formals->ellipsis) {
|
||||||
if (!first) str << ", ";
|
if (!first) str << ", ";
|
||||||
str << "...";
|
str << "...";
|
||||||
}
|
}
|
||||||
str << " }";
|
str << " }";
|
||||||
if (!arg.empty()) str << " @ ";
|
if (arg) str << " @ ";
|
||||||
}
|
}
|
||||||
if (!arg.empty()) str << arg;
|
if (arg) str << symbols[arg];
|
||||||
str << ": " << *body << ")";
|
str << ": ";
|
||||||
|
body->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprCall::show(std::ostream & str) const
|
void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << '(' << *fun;
|
str << '(';
|
||||||
|
fun->show(symbols, str);
|
||||||
for (auto e : args) {
|
for (auto e : args) {
|
||||||
str << ' ';
|
str << ' ';
|
||||||
str << *e;
|
e->show(symbols, str);
|
||||||
}
|
}
|
||||||
str << ')';
|
str << ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprLet::show(std::ostream & str) const
|
void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(let ";
|
str << "(let ";
|
||||||
for (auto & i : attrs->attrs)
|
for (auto & i : attrs->attrs)
|
||||||
if (i.second.inherited) {
|
if (i.second.inherited) {
|
||||||
str << "inherit " << i.first << "; ";
|
str << "inherit " << symbols[i.first] << "; ";
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
str << i.first << " = " << *i.second.e << "; ";
|
str << symbols[i.first] << " = ";
|
||||||
str << "in " << *body << ")";
|
i.second.e->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
|
}
|
||||||
|
str << "in ";
|
||||||
|
body->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprWith::show(std::ostream & str) const
|
void ExprWith::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(with " << *attrs << "; " << *body << ")";
|
str << "(with ";
|
||||||
|
attrs->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
|
body->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprIf::show(std::ostream & str) const
|
void ExprIf::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(if " << *cond << " then " << *then << " else " << *else_ << ")";
|
str << "(if ";
|
||||||
|
cond->show(symbols, str);
|
||||||
|
str << " then ";
|
||||||
|
then->show(symbols, str);
|
||||||
|
str << " else ";
|
||||||
|
else_->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAssert::show(std::ostream & str) const
|
void ExprAssert::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "assert " << *cond << "; " << *body;
|
str << "assert ";
|
||||||
|
cond->show(symbols, str);
|
||||||
|
str << "; ";
|
||||||
|
body->show(symbols, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpNot::show(std::ostream & str) const
|
void ExprOpNot::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "(! " << *e << ")";
|
str << "(! ";
|
||||||
|
e->show(symbols, str);
|
||||||
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprConcatStrings::show(std::ostream & str) const
|
void ExprConcatStrings::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
bool first = true;
|
bool first = true;
|
||||||
str << "(";
|
str << "(";
|
||||||
for (auto & i : *es) {
|
for (auto & i : *es) {
|
||||||
if (first) first = false; else str << " + ";
|
if (first) first = false; else str << " + ";
|
||||||
str << *i.second;
|
i.second->show(symbols, str);
|
||||||
}
|
}
|
||||||
str << ")";
|
str << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPos::show(std::ostream & str) const
|
void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
|
||||||
{
|
{
|
||||||
str << "__curPos";
|
str << "__curPos";
|
||||||
}
|
}
|
||||||
|
@ -236,56 +275,57 @@ std::ostream & operator << (std::ostream & str, const Pos & pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string showAttrPath(const AttrPath & attrPath)
|
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath)
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto & i : attrPath) {
|
for (auto & i : attrPath) {
|
||||||
if (!first) out << '.'; else first = false;
|
if (!first) out << '.'; else first = false;
|
||||||
if (i.symbol.set())
|
if (i.symbol)
|
||||||
out << i.symbol;
|
out << symbols[i.symbol];
|
||||||
else
|
else {
|
||||||
out << "\"${" << *i.expr << "}\"";
|
out << "\"${";
|
||||||
|
i.expr->show(symbols, out);
|
||||||
|
out << "}\"";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pos noPos;
|
|
||||||
|
|
||||||
|
|
||||||
/* Computing levels/displacements for variables. */
|
/* Computing levels/displacements for variables. */
|
||||||
|
|
||||||
void Expr::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void Expr::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprInt::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprInt::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprFloat::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprFloat::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprString::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprString::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPath::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprPath::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprVar::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprVar::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
@ -315,38 +355,38 @@ void ExprVar::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
||||||
if (withLevel == -1)
|
if (withLevel == -1)
|
||||||
{
|
{
|
||||||
throw UndefinedVarError({
|
throw UndefinedVarError({
|
||||||
.msg = hintfmt("undefined variable '%1%'", name),
|
.msg = hintfmt("undefined variable '%1%'", es.symbols[name]),
|
||||||
.errPos = pos
|
.errPos = es.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fromWith = true;
|
fromWith = true;
|
||||||
this->level = withLevel;
|
this->level = withLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprSelect::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprSelect::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
|
||||||
e->bindVars(env);
|
e->bindVars(es, env);
|
||||||
if (def) def->bindVars(env);
|
if (def) def->bindVars(es, env);
|
||||||
for (auto & i : attrPath)
|
for (auto & i : attrPath)
|
||||||
if (!i.symbol.set())
|
if (!i.symbol)
|
||||||
i.expr->bindVars(env);
|
i.expr->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpHasAttr::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprOpHasAttr::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
|
||||||
e->bindVars(env);
|
e->bindVars(es, env);
|
||||||
for (auto & i : attrPath)
|
for (auto & i : attrPath)
|
||||||
if (!i.symbol.set())
|
if (!i.symbol)
|
||||||
i.expr->bindVars(env);
|
i.expr->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAttrs::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprAttrs::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
@ -361,34 +401,34 @@ void ExprAttrs::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
||||||
// No need to sort newEnv since attrs is in sorted order.
|
// No need to sort newEnv since attrs is in sorted order.
|
||||||
|
|
||||||
for (auto & i : attrs)
|
for (auto & i : attrs)
|
||||||
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
|
||||||
|
|
||||||
for (auto & i : dynamicAttrs) {
|
for (auto & i : dynamicAttrs) {
|
||||||
i.nameExpr->bindVars(newEnv);
|
i.nameExpr->bindVars(es, newEnv);
|
||||||
i.valueExpr->bindVars(newEnv);
|
i.valueExpr->bindVars(es, newEnv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (auto & i : attrs)
|
for (auto & i : attrs)
|
||||||
i.second.e->bindVars(env);
|
i.second.e->bindVars(es, env);
|
||||||
|
|
||||||
for (auto & i : dynamicAttrs) {
|
for (auto & i : dynamicAttrs) {
|
||||||
i.nameExpr->bindVars(env);
|
i.nameExpr->bindVars(es, env);
|
||||||
i.valueExpr->bindVars(env);
|
i.valueExpr->bindVars(es, env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprList::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprList::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
|
||||||
for (auto & i : elems)
|
for (auto & i : elems)
|
||||||
i->bindVars(env);
|
i->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprLambda::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprLambda::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
@ -397,11 +437,11 @@ void ExprLambda::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
||||||
new StaticEnv(
|
new StaticEnv(
|
||||||
false, env.get(),
|
false, env.get(),
|
||||||
(hasFormals() ? formals->formals.size() : 0) +
|
(hasFormals() ? formals->formals.size() : 0) +
|
||||||
(arg.empty() ? 0 : 1)));
|
(!arg ? 0 : 1));
|
||||||
|
|
||||||
Displacement displ = 0;
|
Displacement displ = 0;
|
||||||
|
|
||||||
if (!arg.empty()) newEnv->vars.emplace_back(arg, displ++);
|
if (arg) newEnv.vars.emplace_back(arg, displ++);
|
||||||
|
|
||||||
if (hasFormals()) {
|
if (hasFormals()) {
|
||||||
for (auto & i : formals->formals)
|
for (auto & i : formals->formals)
|
||||||
|
@ -410,23 +450,23 @@ void ExprLambda::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
||||||
newEnv->sort();
|
newEnv->sort();
|
||||||
|
|
||||||
for (auto & i : formals->formals)
|
for (auto & i : formals->formals)
|
||||||
if (i.def) i.def->bindVars(newEnv);
|
if (i.def) i.def->bindVars(es, newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
body->bindVars(newEnv);
|
body->bindVars(es, newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprCall::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprCall::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
|
||||||
fun->bindVars(env);
|
fun->bindVars(es, env);
|
||||||
for (auto e : args)
|
for (auto e : args)
|
||||||
e->bindVars(env);
|
e->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprLet::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprLet::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
@ -440,12 +480,12 @@ void ExprLet::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
||||||
// No need to sort newEnv since attrs->attrs is in sorted order.
|
// No need to sort newEnv since attrs->attrs is in sorted order.
|
||||||
|
|
||||||
for (auto & i : attrs->attrs)
|
for (auto & i : attrs->attrs)
|
||||||
i.second.e->bindVars(i.second.inherited ? env : newEnv);
|
i.second.e->bindVars(es, i.second.inherited ? env : newEnv);
|
||||||
|
|
||||||
body->bindVars(newEnv);
|
body->bindVars(es, newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprWith::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprWith::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
@ -462,48 +502,51 @@ void ExprWith::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs->bindVars(env);
|
if (debuggerHook)
|
||||||
auto newEnv = std::shared_ptr<StaticEnv>(new StaticEnv(true, env.get()));
|
staticenv = env;
|
||||||
body->bindVars(newEnv);
|
|
||||||
|
attrs->bindVars(es, env);
|
||||||
|
StaticEnv newEnv(true, &env);
|
||||||
|
body->bindVars(es, newEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprIf::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprIf::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
|
||||||
cond->bindVars(env);
|
cond->bindVars(es, env);
|
||||||
then->bindVars(env);
|
then->bindVars(es, env);
|
||||||
else_->bindVars(env);
|
else_->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAssert::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprAssert::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
|
||||||
cond->bindVars(env);
|
cond->bindVars(es, env);
|
||||||
body->bindVars(env);
|
body->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprOpNot::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprOpNot::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
|
||||||
e->bindVars(env);
|
e->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprConcatStrings::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprConcatStrings::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
|
||||||
for (auto & i : *es)
|
for (auto & i : *this->es)
|
||||||
i.second->bindVars(env);
|
i.second->bindVars(es, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprPos::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
void ExprPos::bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> &env)
|
||||||
{
|
{
|
||||||
if (debuggerHook)
|
if (debuggerHook)
|
||||||
staticenv = env;
|
staticenv = env;
|
||||||
|
@ -513,21 +556,24 @@ void ExprPos::bindVars(const std::shared_ptr<const StaticEnv> &env)
|
||||||
|
|
||||||
/* Storing function names. */
|
/* Storing function names. */
|
||||||
|
|
||||||
void Expr::setName(Symbol & name)
|
void Expr::setName(Symbol name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ExprLambda::setName(Symbol & name)
|
void ExprLambda::setName(Symbol name)
|
||||||
{
|
{
|
||||||
this->name = name;
|
this->name = name;
|
||||||
body->setName(name);
|
body->setName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string ExprLambda::showNamePos() const
|
std::string ExprLambda::showNamePos(const EvalState & state) const
|
||||||
{
|
{
|
||||||
return fmt("%1% at %2%", name.set() ? "'" + (std::string) name + "'" : "anonymous function", pos);
|
std::string id(name
|
||||||
|
? concatStrings("'", state.symbols[name], "'")
|
||||||
|
: "anonymous function");
|
||||||
|
return fmt("%1% at %2%", id, state.positions[pos]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -537,8 +583,7 @@ std::string ExprLambda::showNamePos() const
|
||||||
size_t SymbolTable::totalSize() const
|
size_t SymbolTable::totalSize() const
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
for (auto & i : store)
|
dump([&] (const std::string & s) { n += s.size(); });
|
||||||
n += i.size();
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "value.hh"
|
#include "value.hh"
|
||||||
#include "symbol-table.hh"
|
#include "symbol-table.hh"
|
||||||
#include "error.hh"
|
#include "error.hh"
|
||||||
|
#include "chunked-vector.hh"
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -24,32 +28,92 @@ extern std::function<void(const Error * error, const Env & env, const Expr & exp
|
||||||
|
|
||||||
struct Pos
|
struct Pos
|
||||||
{
|
{
|
||||||
Symbol file;
|
std::string file;
|
||||||
|
FileOrigin origin;
|
||||||
uint32_t line;
|
uint32_t line;
|
||||||
FileOrigin origin:2;
|
uint32_t column;
|
||||||
uint32_t column:30;
|
|
||||||
Pos() : line(0), origin(foString), column(0) { };
|
explicit operator bool() const { return line > 0; }
|
||||||
Pos(FileOrigin origin, const Symbol & file, uint32_t line, uint32_t column)
|
};
|
||||||
: file(file), line(line), origin(origin), column(column) { };
|
|
||||||
operator bool() const
|
class PosIdx {
|
||||||
|
friend class PosTable;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
|
explicit PosIdx(uint32_t id): id(id) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
PosIdx() : id(0) {}
|
||||||
|
|
||||||
|
explicit operator bool() const { return id > 0; }
|
||||||
|
|
||||||
|
bool operator<(const PosIdx other) const { return id < other.id; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class PosTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class Origin {
|
||||||
|
friend PosTable;
|
||||||
|
private:
|
||||||
|
// must always be invalid by default, add() replaces this with the actual value.
|
||||||
|
// subsequent add() calls use this index as a token to quickly check whether the
|
||||||
|
// current origins.back() can be reused or not.
|
||||||
|
mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
|
||||||
|
|
||||||
|
explicit Origin(uint32_t idx): idx(idx), file{}, origin{} {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const std::string file;
|
||||||
|
const FileOrigin origin;
|
||||||
|
|
||||||
|
Origin(std::string file, FileOrigin origin): file(std::move(file)), origin(origin) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Offset {
|
||||||
|
uint32_t line, column;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Origin> origins;
|
||||||
|
ChunkedVector<Offset, 8192> offsets;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PosTable(): offsets(1024)
|
||||||
{
|
{
|
||||||
return line != 0;
|
origins.reserve(1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator < (const Pos & p2) const
|
PosIdx add(const Origin & origin, uint32_t line, uint32_t column)
|
||||||
{
|
{
|
||||||
if (!line) return p2.line;
|
const auto idx = offsets.add({line, column}).second;
|
||||||
if (!p2.line) return false;
|
if (origins.empty() || origins.back().idx != origin.idx) {
|
||||||
int d = ((const std::string &) file).compare((const std::string &) p2.file);
|
origin.idx = idx;
|
||||||
if (d < 0) return true;
|
origins.push_back(origin);
|
||||||
if (d > 0) return false;
|
}
|
||||||
if (line < p2.line) return true;
|
return PosIdx(idx + 1);
|
||||||
if (line > p2.line) return false;
|
}
|
||||||
return column < p2.column;
|
|
||||||
|
Pos operator[](PosIdx p) const
|
||||||
|
{
|
||||||
|
if (p.id == 0 || p.id > offsets.size())
|
||||||
|
return {};
|
||||||
|
const auto idx = p.id - 1;
|
||||||
|
/* we want the last key <= idx, so we'll take prev(first key > idx).
|
||||||
|
this is guaranteed to never rewind origin.begin because the first
|
||||||
|
key is always 0. */
|
||||||
|
const auto pastOrigin = std::upper_bound(
|
||||||
|
origins.begin(), origins.end(), Origin(idx),
|
||||||
|
[] (const auto & a, const auto & b) { return a.idx < b.idx; });
|
||||||
|
const auto origin = *std::prev(pastOrigin);
|
||||||
|
const auto offset = offsets[idx];
|
||||||
|
return {origin.file, origin.origin, offset.line, offset.column};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Pos noPos;
|
inline PosIdx noPos = {};
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Pos & pos);
|
std::ostream & operator << (std::ostream & str, const Pos & pos);
|
||||||
|
|
||||||
|
@ -65,13 +129,13 @@ struct AttrName
|
||||||
{
|
{
|
||||||
Symbol symbol;
|
Symbol symbol;
|
||||||
Expr * expr;
|
Expr * expr;
|
||||||
AttrName(const Symbol & s) : symbol(s) {};
|
AttrName(Symbol s) : symbol(s) {};
|
||||||
AttrName(Expr * e) : expr(e) {};
|
AttrName(Expr * e) : expr(e) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<AttrName> AttrPath;
|
typedef std::vector<AttrName> AttrPath;
|
||||||
|
|
||||||
std::string showAttrPath(const AttrPath & attrPath);
|
std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath);
|
||||||
|
|
||||||
|
|
||||||
/* Abstract syntax of Nix expressions. */
|
/* Abstract syntax of Nix expressions. */
|
||||||
|
@ -79,22 +143,19 @@ std::string showAttrPath(const AttrPath & attrPath);
|
||||||
struct Expr
|
struct Expr
|
||||||
{
|
{
|
||||||
virtual ~Expr() { };
|
virtual ~Expr() { };
|
||||||
virtual void show(std::ostream & str) const;
|
virtual void show(const SymbolTable & symbols, std::ostream & str) const;
|
||||||
virtual void bindVars(const std::shared_ptr<const StaticEnv> & env);
|
virtual void bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||||
virtual Value * maybeThunk(EvalState & state, Env & env);
|
virtual Value * maybeThunk(EvalState & state, Env & env);
|
||||||
virtual void setName(Symbol & name);
|
virtual void setName(Symbol name);
|
||||||
|
|
||||||
std::shared_ptr<const StaticEnv> staticenv;
|
std::shared_ptr<const StaticEnv> staticenv;
|
||||||
virtual const Pos* getPos() const = 0;
|
virtual const PosIdx getPos() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const Expr & e);
|
|
||||||
|
|
||||||
#define COMMON_METHODS \
|
#define COMMON_METHODS \
|
||||||
void show(std::ostream & str) const; \
|
void show(const SymbolTable & symbols, std::ostream & str) const; \
|
||||||
void eval(EvalState & state, Env & env, Value & v); \
|
void eval(EvalState & state, Env & env, Value & v); \
|
||||||
void bindVars(const std::shared_ptr<const StaticEnv> & env);
|
void bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> & env);
|
||||||
|
|
||||||
struct ExprInt : Expr
|
struct ExprInt : Expr
|
||||||
{
|
{
|
||||||
|
@ -102,7 +163,7 @@ struct ExprInt : Expr
|
||||||
Value v;
|
Value v;
|
||||||
ExprInt(NixInt n) : n(n) { v.mkInt(n); };
|
ExprInt(NixInt n) : n(n) { v.mkInt(n); };
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
const Pos* getPos() const { return 0; }
|
const PosIdx getPos() const { return noPos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,7 +173,7 @@ struct ExprFloat : Expr
|
||||||
Value v;
|
Value v;
|
||||||
ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); };
|
ExprFloat(NixFloat nf) : nf(nf) { v.mkFloat(nf); };
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
const Pos* getPos() const { return 0; }
|
const PosIdx getPos() const { return noPos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -122,7 +183,7 @@ struct ExprString : Expr
|
||||||
Value v;
|
Value v;
|
||||||
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
ExprString(std::string s) : s(std::move(s)) { v.mkString(this->s.data()); };
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
const Pos* getPos() const { return 0; }
|
const PosIdx getPos() const { return noPos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -132,7 +193,7 @@ struct ExprPath : Expr
|
||||||
Value v;
|
Value v;
|
||||||
ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); };
|
ExprPath(std::string s) : s(std::move(s)) { v.mkPath(this->s.c_str()); };
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
const Pos* getPos() const { return 0; }
|
const PosIdx getPos() const { return noPos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -141,7 +202,7 @@ typedef uint32_t Displacement;
|
||||||
|
|
||||||
struct ExprVar : Expr
|
struct ExprVar : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Symbol name;
|
Symbol name;
|
||||||
|
|
||||||
/* Whether the variable comes from an environment (e.g. a rec, let
|
/* Whether the variable comes from an environment (e.g. a rec, let
|
||||||
|
@ -157,21 +218,21 @@ struct ExprVar : Expr
|
||||||
Level level;
|
Level level;
|
||||||
Displacement displ;
|
Displacement displ;
|
||||||
|
|
||||||
ExprVar(const Symbol & name) : name(name) { };
|
ExprVar(Symbol name) : name(name) { };
|
||||||
ExprVar(const Pos & pos, const Symbol & name) : pos(pos), name(name) { };
|
ExprVar(const PosIdx & pos, Symbol name) : pos(pos), name(name) { };
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprSelect : Expr
|
struct ExprSelect : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Expr * e, * def;
|
Expr * e, * def;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprSelect(const Pos & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
|
ExprSelect(const PosIdx & pos, Expr * e, const AttrPath & attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(attrPath) { };
|
||||||
ExprSelect(const Pos & pos, Expr * e, const Symbol & name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); };
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,20 +241,20 @@ struct ExprOpHasAttr : Expr
|
||||||
Expr * e;
|
Expr * e;
|
||||||
AttrPath attrPath;
|
AttrPath attrPath;
|
||||||
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
|
ExprOpHasAttr(Expr * e, const AttrPath & attrPath) : e(e), attrPath(attrPath) { };
|
||||||
const Pos* getPos() const { return e->getPos(); }
|
const PosIdx getPos() const { return e->getPos(); }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprAttrs : Expr
|
struct ExprAttrs : Expr
|
||||||
{
|
{
|
||||||
bool recursive;
|
bool recursive;
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
struct AttrDef {
|
struct AttrDef {
|
||||||
bool inherited;
|
bool inherited;
|
||||||
Expr * e;
|
Expr * e;
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Displacement displ; // displacement
|
Displacement displ; // displacement
|
||||||
AttrDef(Expr * e, const Pos & pos, bool inherited=false)
|
AttrDef(Expr * e, const PosIdx & pos, bool inherited=false)
|
||||||
: inherited(inherited), e(e), pos(pos) { };
|
: inherited(inherited), e(e), pos(pos) { };
|
||||||
AttrDef() { };
|
AttrDef() { };
|
||||||
};
|
};
|
||||||
|
@ -201,15 +262,15 @@ struct ExprAttrs : Expr
|
||||||
AttrDefs attrs;
|
AttrDefs attrs;
|
||||||
struct DynamicAttrDef {
|
struct DynamicAttrDef {
|
||||||
Expr * nameExpr, * valueExpr;
|
Expr * nameExpr, * valueExpr;
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const Pos & pos)
|
DynamicAttrDef(Expr * nameExpr, Expr * valueExpr, const PosIdx & pos)
|
||||||
: nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
|
: nameExpr(nameExpr), valueExpr(valueExpr), pos(pos) { };
|
||||||
};
|
};
|
||||||
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
typedef std::vector<DynamicAttrDef> DynamicAttrDefs;
|
||||||
DynamicAttrDefs dynamicAttrs;
|
DynamicAttrDefs dynamicAttrs;
|
||||||
ExprAttrs(const Pos &pos) : recursive(false), pos(pos) { };
|
ExprAttrs(const PosIdx &pos) : recursive(false), pos(pos) { };
|
||||||
ExprAttrs() : recursive(false), pos(noPos) { };
|
ExprAttrs() : recursive(false) { };
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -217,16 +278,15 @@ struct ExprList : Expr
|
||||||
{
|
{
|
||||||
std::vector<Expr *> elems;
|
std::vector<Expr *> elems;
|
||||||
ExprList() { };
|
ExprList() { };
|
||||||
const Pos* getPos() const { return 0; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Formal
|
struct Formal
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Symbol name;
|
Symbol name;
|
||||||
Expr * def;
|
Expr * def;
|
||||||
Formal(const Pos & pos, const Symbol & name, Expr * def) : pos(pos), name(name), def(def) { };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Formals
|
struct Formals
|
||||||
|
@ -235,18 +295,20 @@ struct Formals
|
||||||
Formals_ formals;
|
Formals_ formals;
|
||||||
bool ellipsis;
|
bool ellipsis;
|
||||||
|
|
||||||
bool has(Symbol arg) const {
|
bool has(Symbol arg) const
|
||||||
|
{
|
||||||
auto it = std::lower_bound(formals.begin(), formals.end(), arg,
|
auto it = std::lower_bound(formals.begin(), formals.end(), arg,
|
||||||
[] (const Formal & f, const Symbol & sym) { return f.name < sym; });
|
[] (const Formal & f, const Symbol & sym) { return f.name < sym; });
|
||||||
return it != formals.end() && it->name == arg;
|
return it != formals.end() && it->name == arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Formal> lexicographicOrder() const
|
std::vector<Formal> lexicographicOrder(const SymbolTable & symbols) const
|
||||||
{
|
{
|
||||||
std::vector<Formal> result(formals.begin(), formals.end());
|
std::vector<Formal> result(formals.begin(), formals.end());
|
||||||
std::sort(result.begin(), result.end(),
|
std::sort(result.begin(), result.end(),
|
||||||
[] (const Formal & a, const Formal & b) {
|
[&] (const Formal & a, const Formal & b) {
|
||||||
return std::string_view(a.name) < std::string_view(b.name);
|
std::string_view sa = symbols[a.name], sb = symbols[b.name];
|
||||||
|
return sa < sb;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -254,19 +316,23 @@ struct Formals
|
||||||
|
|
||||||
struct ExprLambda : Expr
|
struct ExprLambda : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Symbol name;
|
Symbol name;
|
||||||
Symbol arg;
|
Symbol arg;
|
||||||
Formals * formals;
|
Formals * formals;
|
||||||
Expr * body;
|
Expr * body;
|
||||||
ExprLambda(const Pos & pos, const Symbol & arg, Formals * formals, Expr * body)
|
ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body)
|
||||||
: pos(pos), arg(arg), formals(formals), body(body)
|
: pos(pos), arg(arg), formals(formals), body(body)
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
void setName(Symbol & name);
|
ExprLambda(PosIdx pos, Formals * formals, Expr * body)
|
||||||
std::string showNamePos() const;
|
: pos(pos), formals(formals), body(body)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void setName(Symbol name);
|
||||||
|
std::string showNamePos(const EvalState & state) const;
|
||||||
inline bool hasFormals() const { return formals != nullptr; }
|
inline bool hasFormals() const { return formals != nullptr; }
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -274,11 +340,11 @@ struct ExprCall : Expr
|
||||||
{
|
{
|
||||||
Expr * fun;
|
Expr * fun;
|
||||||
std::vector<Expr *> args;
|
std::vector<Expr *> args;
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
ExprCall(const Pos & pos, Expr * fun, std::vector<Expr *> && args)
|
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args)
|
||||||
: fun(fun), args(args), pos(pos)
|
: fun(fun), args(args), pos(pos)
|
||||||
{ }
|
{ }
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -287,35 +353,35 @@ struct ExprLet : Expr
|
||||||
ExprAttrs * attrs;
|
ExprAttrs * attrs;
|
||||||
Expr * body;
|
Expr * body;
|
||||||
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
|
ExprLet(ExprAttrs * attrs, Expr * body) : attrs(attrs), body(body) { };
|
||||||
const Pos* getPos() const { return 0; }
|
const PosIdx getPos() const { return noPos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprWith : Expr
|
struct ExprWith : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Expr * attrs, * body;
|
Expr * attrs, * body;
|
||||||
size_t prevWith;
|
size_t prevWith;
|
||||||
ExprWith(const Pos & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
ExprWith(const PosIdx & pos, Expr * attrs, Expr * body) : pos(pos), attrs(attrs), body(body) { };
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprIf : Expr
|
struct ExprIf : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Expr * cond, * then, * else_;
|
Expr * cond, * then, * else_;
|
||||||
ExprIf(const Pos & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
|
ExprIf(const PosIdx & pos, Expr * cond, Expr * then, Expr * else_) : pos(pos), cond(cond), then(then), else_(else_) { };
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprAssert : Expr
|
struct ExprAssert : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
Expr * cond, * body;
|
Expr * cond, * body;
|
||||||
ExprAssert(const Pos & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
ExprAssert(const PosIdx & pos, Expr * cond, Expr * body) : pos(pos), cond(cond), body(body) { };
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -330,20 +396,20 @@ struct ExprOpNot : Expr
|
||||||
#define MakeBinOp(name, s) \
|
#define MakeBinOp(name, s) \
|
||||||
struct name : Expr \
|
struct name : Expr \
|
||||||
{ \
|
{ \
|
||||||
Pos pos; \
|
PosIdx pos; \
|
||||||
Expr * e1, * e2; \
|
Expr * e1, * e2; \
|
||||||
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
|
name(Expr * e1, Expr * e2) : e1(e1), e2(e2) { }; \
|
||||||
name(const Pos & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
|
name(const PosIdx & pos, Expr * e1, Expr * e2) : pos(pos), e1(e1), e2(e2) { }; \
|
||||||
void show(std::ostream & str) const \
|
void show(const SymbolTable & symbols, std::ostream & str) const \
|
||||||
{ \
|
{ \
|
||||||
str << "(" << *e1 << " " s " " << *e2 << ")"; \
|
str << "("; e1->show(symbols, str); str << " " s " "; e2->show(symbols, str); str << ")"; \
|
||||||
} \
|
} \
|
||||||
void bindVars(const std::shared_ptr<const StaticEnv> & env) \
|
void bindVars(const EvalState & es, const std::shared_ptr<const StaticEnv> & env) \
|
||||||
{ \
|
{ \
|
||||||
e1->bindVars(env); e2->bindVars(env); \
|
e1->bindVars(es, env); e2->bindVars(es, env); \
|
||||||
} \
|
} \
|
||||||
void eval(EvalState & state, Env & env, Value & v); \
|
void eval(EvalState & state, Env & env, Value & v); \
|
||||||
const Pos* getPos() const { return &pos; } \
|
const PosIdx getPos() const { return pos; } \
|
||||||
};
|
};
|
||||||
|
|
||||||
MakeBinOp(ExprOpEq, "==")
|
MakeBinOp(ExprOpEq, "==")
|
||||||
|
@ -356,20 +422,20 @@ MakeBinOp(ExprOpConcatLists, "++")
|
||||||
|
|
||||||
struct ExprConcatStrings : Expr
|
struct ExprConcatStrings : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
bool forceString;
|
bool forceString;
|
||||||
std::vector<std::pair<Pos, Expr *> > * es;
|
std::vector<std::pair<PosIdx, Expr *> > * es;
|
||||||
ExprConcatStrings(const Pos & pos, bool forceString, std::vector<std::pair<Pos, Expr *> > * es)
|
ExprConcatStrings(const PosIdx & pos, bool forceString, std::vector<std::pair<PosIdx, Expr *> > * es)
|
||||||
: pos(pos), forceString(forceString), es(es) { };
|
: pos(pos), forceString(forceString), es(es) { };
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExprPos : Expr
|
struct ExprPos : Expr
|
||||||
{
|
{
|
||||||
Pos pos;
|
PosIdx pos;
|
||||||
ExprPos(const Pos & pos) : pos(pos) { };
|
ExprPos(const PosIdx & pos) : pos(pos) { };
|
||||||
const Pos* getPos() const { return &pos; }
|
const PosIdx getPos() const { return pos; }
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -407,7 +473,7 @@ struct StaticEnv
|
||||||
vars.erase(it, end);
|
vars.erase(it, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vars::const_iterator find(const Symbol & name) const
|
Vars::const_iterator find(Symbol name) const
|
||||||
{
|
{
|
||||||
Vars::value_type key(name, 0);
|
Vars::value_type key(name, 0);
|
||||||
auto i = std::lower_bound(vars.begin(), vars.end(), key);
|
auto i = std::lower_bound(vars.begin(), vars.end(), key);
|
||||||
|
|
|
@ -32,12 +32,12 @@ namespace nix {
|
||||||
SymbolTable & symbols;
|
SymbolTable & symbols;
|
||||||
Expr * result;
|
Expr * result;
|
||||||
Path basePath;
|
Path basePath;
|
||||||
Symbol file;
|
PosTable::Origin origin;
|
||||||
FileOrigin origin;
|
|
||||||
std::optional<ErrorInfo> error;
|
std::optional<ErrorInfo> error;
|
||||||
ParseData(EvalState & state)
|
ParseData(EvalState & state, PosTable::Origin origin)
|
||||||
: state(state)
|
: state(state)
|
||||||
, symbols(state.symbols)
|
, symbols(state.symbols)
|
||||||
|
, origin(std::move(origin))
|
||||||
{ };
|
{ };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,26 +77,26 @@ using namespace nix;
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
static void dupAttr(const AttrPath & attrPath, const Pos & pos, const Pos & prevPos)
|
static void dupAttr(const EvalState & state, const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos)
|
||||||
{
|
{
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("attribute '%1%' already defined at %2%",
|
.msg = hintfmt("attribute '%1%' already defined at %2%",
|
||||||
showAttrPath(attrPath), prevPos),
|
showAttrPath(state.symbols, attrPath), state.positions[prevPos]),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dupAttr(Symbol attr, const Pos & pos, const Pos & prevPos)
|
static void dupAttr(const EvalState & state, Symbol attr, const PosIdx pos, const PosIdx prevPos)
|
||||||
{
|
{
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("attribute '%1%' already defined at %2%", attr, prevPos),
|
.msg = hintfmt("attribute '%1%' already defined at %2%", state.symbols[attr], state.positions[prevPos]),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
Expr * e, const Pos & pos)
|
Expr * e, const PosIdx pos, const nix::EvalState & state)
|
||||||
{
|
{
|
||||||
AttrPath::iterator i;
|
AttrPath::iterator i;
|
||||||
// All attrpaths have at least one attr
|
// All attrpaths have at least one attr
|
||||||
|
@ -104,15 +104,15 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
// Checking attrPath validity.
|
// Checking attrPath validity.
|
||||||
// ===========================
|
// ===========================
|
||||||
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
|
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) {
|
||||||
if (i->symbol.set()) {
|
if (i->symbol) {
|
||||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||||
if (j != attrs->attrs.end()) {
|
if (j != attrs->attrs.end()) {
|
||||||
if (!j->second.inherited) {
|
if (!j->second.inherited) {
|
||||||
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
|
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e);
|
||||||
if (!attrs2) dupAttr(attrPath, pos, j->second.pos);
|
if (!attrs2) dupAttr(state, attrPath, pos, j->second.pos);
|
||||||
attrs = attrs2;
|
attrs = attrs2;
|
||||||
} else
|
} else
|
||||||
dupAttr(attrPath, pos, j->second.pos);
|
dupAttr(state, attrPath, pos, j->second.pos);
|
||||||
} else {
|
} else {
|
||||||
ExprAttrs * nested = new ExprAttrs;
|
ExprAttrs * nested = new ExprAttrs;
|
||||||
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos);
|
||||||
|
@ -126,7 +126,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
}
|
}
|
||||||
// Expr insertion.
|
// Expr insertion.
|
||||||
// ==========================
|
// ==========================
|
||||||
if (i->symbol.set()) {
|
if (i->symbol) {
|
||||||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol);
|
||||||
if (j != attrs->attrs.end()) {
|
if (j != attrs->attrs.end()) {
|
||||||
// This attr path is already defined. However, if both
|
// This attr path is already defined. However, if both
|
||||||
|
@ -139,11 +139,11 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
for (auto & ad : ae->attrs) {
|
for (auto & ad : ae->attrs) {
|
||||||
auto j2 = jAttrs->attrs.find(ad.first);
|
auto j2 = jAttrs->attrs.find(ad.first);
|
||||||
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
|
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error.
|
||||||
dupAttr(ad.first, j2->second.pos, ad.second.pos);
|
dupAttr(state, ad.first, j2->second.pos, ad.second.pos);
|
||||||
jAttrs->attrs.emplace(ad.first, ad.second);
|
jAttrs->attrs.emplace(ad.first, ad.second);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dupAttr(attrPath, pos, j->second.pos);
|
dupAttr(state, attrPath, pos, j->second.pos);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This attr path is not defined. Let's create it.
|
// This attr path is not defined. Let's create it.
|
||||||
|
@ -157,14 +157,14 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||||
|
|
||||||
|
|
||||||
static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
||||||
Pos pos = noPos, Symbol arg = {})
|
PosIdx pos = noPos, Symbol arg = {})
|
||||||
{
|
{
|
||||||
std::sort(formals->formals.begin(), formals->formals.end(),
|
std::sort(formals->formals.begin(), formals->formals.end(),
|
||||||
[] (const auto & a, const auto & b) {
|
[] (const auto & a, const auto & b) {
|
||||||
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
|
return std::tie(a.name, a.pos) < std::tie(b.name, b.pos);
|
||||||
});
|
});
|
||||||
|
|
||||||
std::optional<std::pair<Symbol, Pos>> duplicate;
|
std::optional<std::pair<Symbol, PosIdx>> duplicate;
|
||||||
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
|
for (size_t i = 0; i + 1 < formals->formals.size(); i++) {
|
||||||
if (formals->formals[i].name != formals->formals[i + 1].name)
|
if (formals->formals[i].name != formals->formals[i + 1].name)
|
||||||
continue;
|
continue;
|
||||||
|
@ -173,18 +173,18 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
||||||
}
|
}
|
||||||
if (duplicate)
|
if (duplicate)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("duplicate formal function argument '%1%'", duplicate->first),
|
.msg = hintfmt("duplicate formal function argument '%1%'", data.symbols[duplicate->first]),
|
||||||
.errPos = duplicate->second
|
.errPos = data.state.positions[duplicate->second]
|
||||||
});
|
});
|
||||||
|
|
||||||
Formals result;
|
Formals result;
|
||||||
result.ellipsis = formals->ellipsis;
|
result.ellipsis = formals->ellipsis;
|
||||||
result.formals = std::move(formals->formals);
|
result.formals = std::move(formals->formals);
|
||||||
|
|
||||||
if (arg.set() && result.has(arg))
|
if (arg && result.has(arg))
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("duplicate formal function argument '%1%'", arg),
|
.msg = hintfmt("duplicate formal function argument '%1%'", data.symbols[arg]),
|
||||||
.errPos = pos
|
.errPos = data.state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
delete formals;
|
delete formals;
|
||||||
|
@ -192,8 +192,8 @@ static Formals * toFormals(ParseData & data, ParserFormals * formals,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
|
static Expr * stripIndentation(const PosIdx pos, SymbolTable & symbols,
|
||||||
std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > > & es)
|
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > > & es)
|
||||||
{
|
{
|
||||||
if (es.empty()) return new ExprString("");
|
if (es.empty()) return new ExprString("");
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Strip spaces from each line. */
|
/* Strip spaces from each line. */
|
||||||
std::vector<std::pair<Pos, Expr *> > * es2 = new std::vector<std::pair<Pos, Expr *> >;
|
auto * es2 = new std::vector<std::pair<PosIdx, Expr *> >;
|
||||||
atStartOfLine = true;
|
atStartOfLine = true;
|
||||||
size_t curDropped = 0;
|
size_t curDropped = 0;
|
||||||
size_t n = es.size();
|
size_t n = es.size();
|
||||||
|
@ -284,9 +284,9 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data)
|
static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data)
|
||||||
{
|
{
|
||||||
return Pos(data->origin, data->file, loc.first_line, loc.first_column);
|
return data->state.positions.add(data->origin, loc.first_line, loc.first_column);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CUR_POS makeCurPos(*yylocp, data)
|
#define CUR_POS makeCurPos(*yylocp, data)
|
||||||
|
@ -299,7 +299,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
{
|
{
|
||||||
data->error = {
|
data->error = {
|
||||||
.msg = hintfmt(error),
|
.msg = hintfmt(error),
|
||||||
.errPos = makeCurPos(*loc, data)
|
.errPos = data->state.positions[makeCurPos(*loc, data)]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,8 +320,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * err
|
||||||
StringToken uri;
|
StringToken uri;
|
||||||
StringToken str;
|
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::PosIdx, nix::Expr *> > * string_parts;
|
||||||
std::vector<std::pair<nix::Pos, std::variant<nix::Expr *, StringToken> > > * ind_string_parts;
|
std::vector<std::pair<nix::PosIdx, 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
|
||||||
|
@ -369,15 +369,15 @@ 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(""), toFormals(*data, $2), $5); }
|
{ $$ = new ExprLambda(CUR_POS, toFormals(*data, $2), $5); }
|
||||||
| '{' formals '}' '@' ID ':' expr_function
|
| '{' formals '}' '@' ID ':' expr_function
|
||||||
{
|
{
|
||||||
Symbol arg = data->symbols.create($5);
|
auto arg = data->symbols.create($5);
|
||||||
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
|
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $2, CUR_POS, arg), $7);
|
||||||
}
|
}
|
||||||
| ID '@' '{' formals '}' ':' expr_function
|
| ID '@' '{' formals '}' ':' expr_function
|
||||||
{
|
{
|
||||||
Symbol arg = data->symbols.create($1);
|
auto arg = data->symbols.create($1);
|
||||||
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
|
$$ = new ExprLambda(CUR_POS, arg, toFormals(*data, $4, CUR_POS, arg), $7);
|
||||||
}
|
}
|
||||||
| ASSERT expr ';' expr_function
|
| ASSERT expr ';' expr_function
|
||||||
|
@ -388,7 +388,7 @@ expr_function
|
||||||
{ if (!$2->dynamicAttrs.empty())
|
{ if (!$2->dynamicAttrs.empty())
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("dynamic attributes not allowed in let"),
|
.msg = hintfmt("dynamic attributes not allowed in let"),
|
||||||
.errPos = CUR_POS
|
.errPos = data->state.positions[CUR_POS]
|
||||||
});
|
});
|
||||||
$$ = new ExprLet($2, $4);
|
$$ = new ExprLet($2, $4);
|
||||||
}
|
}
|
||||||
|
@ -415,7 +415,7 @@ expr_op
|
||||||
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
|
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); }
|
||||||
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
|
||||||
| expr_op '+' expr_op
|
| expr_op '+' expr_op
|
||||||
{ $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
{ $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<PosIdx, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
|
||||||
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
|
||||||
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
|
||||||
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); }
|
||||||
|
@ -477,7 +477,7 @@ expr_simple
|
||||||
if (noURLLiterals)
|
if (noURLLiterals)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("URL literals are disabled"),
|
.msg = hintfmt("URL literals are disabled"),
|
||||||
.errPos = CUR_POS
|
.errPos = data->state.positions[CUR_POS]
|
||||||
});
|
});
|
||||||
$$ = new ExprString(std::string($1));
|
$$ = new ExprString(std::string($1));
|
||||||
}
|
}
|
||||||
|
@ -503,9 +503,9 @@ string_parts_interpolated
|
||||||
: string_parts_interpolated STR
|
: string_parts_interpolated STR
|
||||||
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::string($2))); }
|
{ $$ = $1; $1->emplace_back(makeCurPos(@2, data), new ExprString(std::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 std::vector<std::pair<Pos, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
|
| DOLLAR_CURLY expr '}' { $$ = new std::vector<std::pair<PosIdx, Expr *> >; $$->emplace_back(makeCurPos(@1, data), $2); }
|
||||||
| STR DOLLAR_CURLY expr '}' {
|
| STR DOLLAR_CURLY expr '}' {
|
||||||
$$ = new std::vector<std::pair<Pos, Expr *> >;
|
$$ = new std::vector<std::pair<PosIdx, Expr *> >;
|
||||||
$$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1)));
|
$$->emplace_back(makeCurPos(@1, data), new ExprString(std::string($1)));
|
||||||
$$->emplace_back(makeCurPos(@2, data), $3);
|
$$->emplace_back(makeCurPos(@2, data), $3);
|
||||||
}
|
}
|
||||||
|
@ -528,17 +528,17 @@ 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 std::vector<std::pair<Pos, std::variant<Expr *, StringToken> > >; }
|
| { $$ = new std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken> > >; }
|
||||||
;
|
;
|
||||||
|
|
||||||
binds
|
binds
|
||||||
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data)); }
|
: binds attrpath '=' expr ';' { $$ = $1; addAttr($$, *$2, $4, makeCurPos(@2, data), data->state); }
|
||||||
| binds INHERIT attrs ';'
|
| binds INHERIT attrs ';'
|
||||||
{ $$ = $1;
|
{ $$ = $1;
|
||||||
for (auto & i : *$3) {
|
for (auto & i : *$3) {
|
||||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
dupAttr(i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
|
dupAttr(data->state, i.symbol, makeCurPos(@3, data), $$->attrs[i.symbol].pos);
|
||||||
Pos pos = makeCurPos(@3, data);
|
auto pos = makeCurPos(@3, data);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -547,7 +547,7 @@ binds
|
||||||
/* !!! Should ensure sharing of the expression in $4. */
|
/* !!! Should ensure sharing of the expression in $4. */
|
||||||
for (auto & i : *$6) {
|
for (auto & i : *$6) {
|
||||||
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
if ($$->attrs.find(i.symbol) != $$->attrs.end())
|
||||||
dupAttr(i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
dupAttr(data->state, i.symbol, makeCurPos(@6, data), $$->attrs[i.symbol].pos);
|
||||||
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
|
$$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), makeCurPos(@6, data)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -565,7 +565,7 @@ attrs
|
||||||
} else
|
} else
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("dynamic attributes not allowed in inherit"),
|
.msg = hintfmt("dynamic attributes not allowed in inherit"),
|
||||||
.errPos = makeCurPos(@2, data)
|
.errPos = data->state.positions[makeCurPos(@2, data)]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
| { $$ = new AttrPath; }
|
| { $$ = new AttrPath; }
|
||||||
|
@ -621,8 +621,8 @@ formals
|
||||||
;
|
;
|
||||||
|
|
||||||
formal
|
formal
|
||||||
: ID { $$ = new Formal(CUR_POS, data->symbols.create($1), 0); }
|
: ID { $$ = new Formal{CUR_POS, data->symbols.create($1), 0}; }
|
||||||
| ID '?' expr { $$ = new Formal(CUR_POS, data->symbols.create($1), $3); }
|
| ID '?' expr { $$ = new Formal{CUR_POS, data->symbols.create($1), $3}; }
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
@ -646,19 +646,19 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
||||||
const PathView path, const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
const PathView path, const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
||||||
{
|
{
|
||||||
yyscan_t scanner;
|
yyscan_t scanner;
|
||||||
ParseData data(*this);
|
std::string file;
|
||||||
data.origin = origin;
|
|
||||||
switch (origin) {
|
switch (origin) {
|
||||||
case foFile:
|
case foFile:
|
||||||
data.file = data.symbols.create(path);
|
file = path;
|
||||||
break;
|
break;
|
||||||
case foStdin:
|
case foStdin:
|
||||||
case foString:
|
case foString:
|
||||||
data.file = data.symbols.create(text);
|
file = text;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
ParseData data(*this, {file, origin});
|
||||||
data.basePath = basePath;
|
data.basePath = basePath;
|
||||||
|
|
||||||
yylex_init(&scanner);
|
yylex_init(&scanner);
|
||||||
|
@ -668,7 +668,7 @@ Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
||||||
|
|
||||||
if (res) throw ParseError(data.error.value());
|
if (res) throw ParseError(data.error.value());
|
||||||
|
|
||||||
data.result->bindVars(staticEnv);
|
data.result->bindVars(*this, staticEnv);
|
||||||
|
|
||||||
return data.result;
|
return data.result;
|
||||||
}
|
}
|
||||||
|
@ -760,7 +760,7 @@ Path EvalState::findFile(const std::string_view path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const Pos & pos)
|
Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, const PosIdx pos)
|
||||||
{
|
{
|
||||||
for (auto & i : searchPath) {
|
for (auto & i : searchPath) {
|
||||||
std::string suffix;
|
std::string suffix;
|
||||||
|
@ -787,7 +787,7 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c
|
||||||
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)"
|
||||||
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
: "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)",
|
||||||
path),
|
path),
|
||||||
.errPos = pos
|
.errPos = positions[pos]
|
||||||
});
|
});
|
||||||
debugLastTrace(e);
|
debugLastTrace(e);
|
||||||
throw e;
|
throw e;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -38,9 +38,9 @@ struct RegisterPrimOp
|
||||||
them. */
|
them. */
|
||||||
|
|
||||||
/* Load a ValueInitializer from a DSO and return whatever it initializes */
|
/* Load a ValueInitializer from a DSO and return whatever it initializes */
|
||||||
void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
|
|
||||||
/* Execute a program and parse its output */
|
/* Execute a program and parse its output */
|
||||||
void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v);
|
void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto s = state.coerceToString(pos, *args[0], context);
|
auto s = state.coerceToString(pos, *args[0], context);
|
||||||
|
@ -15,7 +15,7 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos,
|
||||||
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||||
|
|
||||||
|
|
||||||
static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_hasContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
state.forceString(*args[0], context, pos);
|
state.forceString(*args[0], context, pos);
|
||||||
|
@ -31,7 +31,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext);
|
||||||
source-only deployment). This primop marks the string context so
|
source-only deployment). This primop marks the string context so
|
||||||
that builtins.derivation adds the path to drv.inputSrcs rather than
|
that builtins.derivation adds the path to drv.inputSrcs rather than
|
||||||
drv.inputDrvs. */
|
drv.inputDrvs. */
|
||||||
static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_unsafeDiscardOutputDependency(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto s = state.coerceToString(pos, *args[0], context);
|
auto s = state.coerceToString(pos, *args[0], context);
|
||||||
|
@ -65,7 +65,7 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency("__unsafeDiscardOutpu
|
||||||
Note that for a given path any combination of the above attributes
|
Note that for a given path any combination of the above attributes
|
||||||
may be present.
|
may be present.
|
||||||
*/
|
*/
|
||||||
static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
struct ContextInfo {
|
struct ContextInfo {
|
||||||
bool path = false;
|
bool path = false;
|
||||||
|
@ -134,7 +134,7 @@ static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
|
||||||
See the commentary above unsafeGetContext for details of the
|
See the commentary above unsafeGetContext for details of the
|
||||||
context representation.
|
context representation.
|
||||||
*/
|
*/
|
||||||
static void prim_appendContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto orig = state.forceString(*args[0], context, pos);
|
auto orig = state.forceString(*args[0], context, pos);
|
||||||
|
@ -144,45 +144,46 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
|
||||||
auto sPath = state.symbols.create("path");
|
auto sPath = state.symbols.create("path");
|
||||||
auto sAllOutputs = state.symbols.create("allOutputs");
|
auto sAllOutputs = state.symbols.create("allOutputs");
|
||||||
for (auto & i : *args[1]->attrs) {
|
for (auto & i : *args[1]->attrs) {
|
||||||
if (!state.store->isStorePath(i.name))
|
const auto & name = state.symbols[i.name];
|
||||||
|
if (!state.store->isStorePath(name))
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("Context key '%s' is not a store path", i.name),
|
.msg = hintfmt("Context key '%s' is not a store path", name),
|
||||||
.errPos = *i.pos
|
.errPos = state.positions[i.pos]
|
||||||
});
|
});
|
||||||
if (!settings.readOnlyMode)
|
if (!settings.readOnlyMode)
|
||||||
state.store->ensurePath(state.store->parseStorePath(i.name));
|
state.store->ensurePath(state.store->parseStorePath(name));
|
||||||
state.forceAttrs(*i.value, *i.pos);
|
state.forceAttrs(*i.value, i.pos);
|
||||||
auto iter = i.value->attrs->find(sPath);
|
auto iter = i.value->attrs->find(sPath);
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
if (state.forceBool(*iter->value, *iter->pos))
|
if (state.forceBool(*iter->value, iter->pos))
|
||||||
context.insert(i.name);
|
context.emplace(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = i.value->attrs->find(sAllOutputs);
|
iter = i.value->attrs->find(sAllOutputs);
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
if (state.forceBool(*iter->value, *iter->pos)) {
|
if (state.forceBool(*iter->value, iter->pos)) {
|
||||||
if (!isDerivation(i.name)) {
|
if (!isDerivation(name)) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
|
.msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", name),
|
||||||
.errPos = *i.pos
|
.errPos = state.positions[i.pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
context.insert("=" + std::string(i.name));
|
context.insert(concatStrings("=", name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = i.value->attrs->find(state.sOutputs);
|
iter = i.value->attrs->find(state.sOutputs);
|
||||||
if (iter != i.value->attrs->end()) {
|
if (iter != i.value->attrs->end()) {
|
||||||
state.forceList(*iter->value, *iter->pos);
|
state.forceList(*iter->value, iter->pos);
|
||||||
if (iter->value->listSize() && !isDerivation(i.name)) {
|
if (iter->value->listSize() && !isDerivation(name)) {
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
|
.msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", name),
|
||||||
.errPos = *i.pos
|
.errPos = state.positions[i.pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (auto elem : iter->value->listItems()) {
|
for (auto elem : iter->value->listItems()) {
|
||||||
auto name = state.forceStringNoCtx(*elem, *iter->pos);
|
auto outputName = state.forceStringNoCtx(*elem, iter->pos);
|
||||||
context.insert(concatStrings("!", name, "!", i.name));
|
context.insert(concatStrings("!", outputName, "!", name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos);
|
||||||
|
|
||||||
|
@ -15,40 +15,42 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
std::optional<StorePath> toPath;
|
std::optional<StorePath> toPath;
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
if (attr.name == "fromPath") {
|
const auto & attrName = state.symbols[attr.name];
|
||||||
|
|
||||||
|
if (attrName == "fromPath") {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
fromPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
|
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (attr.name == "toPath") {
|
else if (attrName == "toPath") {
|
||||||
state.forceValue(*attr.value, *attr.pos);
|
state.forceValue(*attr.value, attr.pos);
|
||||||
toCA = true;
|
toCA = true;
|
||||||
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
|
if (attr.value->type() != nString || attr.value->string.s != std::string("")) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
toPath = state.coerceToStorePath(*attr.pos, *attr.value, context);
|
toPath = state.coerceToStorePath(attr.pos, *attr.value, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (attr.name == "fromStore")
|
else if (attrName == "fromStore")
|
||||||
fromStoreUrl = state.forceStringNoCtx(*attr.value, *attr.pos);
|
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos);
|
||||||
|
|
||||||
else
|
else
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attr.name),
|
.msg = hintfmt("attribute '%s' isn't supported in call to 'fetchClosure'", attrName),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fromPath)
|
if (!fromPath)
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
|
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromPath"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!fromStoreUrl)
|
if (!fromStoreUrl)
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
|
.msg = hintfmt("attribute '%s' is missing in call to 'fetchClosure'", "fromStore"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
auto parsedURL = parseURL(*fromStoreUrl);
|
auto parsedURL = parseURL(*fromStoreUrl);
|
||||||
|
@ -58,13 +60,13 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
|
!(getEnv("_NIX_IN_TEST").has_value() && parsedURL.scheme == "file"))
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"),
|
.msg = hintfmt("'fetchClosure' only supports http:// and https:// stores"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!parsedURL.query.empty())
|
if (!parsedURL.query.empty())
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
|
.msg = hintfmt("'fetchClosure' does not support URL query parameters (in '%s')", *fromStoreUrl),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
auto fromStore = openStore(parsedURL.to_string());
|
auto fromStore = openStore(parsedURL.to_string());
|
||||||
|
@ -80,7 +82,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
state.store->printStorePath(*fromPath),
|
state.store->printStorePath(*fromPath),
|
||||||
state.store->printStorePath(i->second),
|
state.store->printStorePath(i->second),
|
||||||
state.store->printStorePath(*toPath)),
|
state.store->printStorePath(*toPath)),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
if (!toPath)
|
if (!toPath)
|
||||||
throw Error({
|
throw Error({
|
||||||
|
@ -89,7 +91,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
"please set this in the 'toPath' attribute passed to 'fetchClosure'",
|
"please set this in the 'toPath' attribute passed to 'fetchClosure'",
|
||||||
state.store->printStorePath(*fromPath),
|
state.store->printStorePath(*fromPath),
|
||||||
state.store->printStorePath(i->second)),
|
state.store->printStorePath(i->second)),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -105,7 +107,7 @@ static void prim_fetchClosure(EvalState & state, const Pos & pos, Value * * args
|
||||||
throw Error({
|
throw Error({
|
||||||
.msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't",
|
.msg = hintfmt("in pure mode, 'fetchClosure' requires a content-addressed path, which '%s' isn't",
|
||||||
state.store->printStorePath(*toPath)),
|
state.store->printStorePath(*toPath)),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
std::string url;
|
std::string url;
|
||||||
std::optional<Hash> rev;
|
std::optional<Hash> rev;
|
||||||
|
@ -22,31 +22,31 @@ 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) {
|
||||||
std::string_view n(attr.name);
|
std::string_view n(state.symbols[attr.name]);
|
||||||
if (n == "url")
|
if (n == "url")
|
||||||
url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned();
|
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.begin(), value.end(), 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;
|
||||||
}
|
}
|
||||||
else if (n == "name")
|
else if (n == "name")
|
||||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
name = state.forceStringNoCtx(*attr.value, attr.pos);
|
||||||
else
|
else
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),
|
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]),
|
||||||
.errPos = *attr.pos
|
.errPos = state.positions[attr.pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.empty())
|
if (url.empty())
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("'url' argument required"),
|
.msg = hintfmt("'url' argument required"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
|
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -90,7 +90,7 @@ struct FetchTreeParams {
|
||||||
|
|
||||||
static void fetchTree(
|
static void fetchTree(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const Pos & pos,
|
const PosIdx pos,
|
||||||
Value * * args,
|
Value * * args,
|
||||||
Value & v,
|
Value & v,
|
||||||
std::optional<std::string> type,
|
std::optional<std::string> type,
|
||||||
|
@ -111,18 +111,17 @@ static void fetchTree(
|
||||||
{
|
{
|
||||||
auto e = EvalError({
|
auto e = EvalError({
|
||||||
.msg = hintfmt("unexpected attribute 'type'"),
|
.msg = hintfmt("unexpected attribute 'type'"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
state.debugLastTrace(e);
|
state.debugLastTrace(e);
|
||||||
throw e;
|
throw e;
|
||||||
|
|
||||||
}
|
}
|
||||||
type = state.forceStringNoCtx(*aType->value, *aType->pos);
|
type = state.forceStringNoCtx(*aType->value, aType->pos);
|
||||||
} else if (!type)
|
} else if (!type)
|
||||||
{
|
{
|
||||||
auto e = EvalError({
|
auto e = EvalError({
|
||||||
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
state.debugLastTrace(e);
|
state.debugLastTrace(e);
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -132,24 +131,24 @@ static void fetchTree(
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
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).toOwned();
|
auto s = state.coerceToString(attr.pos, *attr.value, context, false, false).toOwned();
|
||||||
attrs.emplace(attr.name,
|
attrs.emplace(state.symbols[attr.name],
|
||||||
attr.name == "url"
|
state.symbols[attr.name] == "url"
|
||||||
? type == "git"
|
? type == "git"
|
||||||
? fixURIForGit(s, state)
|
? fixURIForGit(s, state)
|
||||||
: fixURI(s, state)
|
: fixURI(s, state)
|
||||||
: s);
|
: s);
|
||||||
}
|
}
|
||||||
else if (attr.value->type() == nBool)
|
else if (attr.value->type() == nBool)
|
||||||
attrs.emplace(attr.name, Explicit<bool>{attr.value->boolean});
|
attrs.emplace(state.symbols[attr.name], Explicit<bool>{attr.value->boolean});
|
||||||
else if (attr.value->type() == nInt)
|
else if (attr.value->type() == nInt)
|
||||||
attrs.emplace(attr.name, uint64_t(attr.value->integer));
|
attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto e = TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
|
auto e = TypeError("fetchTree argument '%s' is %s while a string, Boolean or integer is expected",
|
||||||
attr.name, showType(*attr.value));
|
state.symbols[attr.name], showType(*attr.value));
|
||||||
state.debugLastTrace(e);
|
state.debugLastTrace(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -160,7 +159,7 @@ static void fetchTree(
|
||||||
{
|
{
|
||||||
auto e = EvalError({
|
auto e = EvalError({
|
||||||
.msg = hintfmt("attribute 'name' isn’t supported in call to 'fetchTree'"),
|
.msg = hintfmt("attribute 'name' isn’t supported in call to 'fetchTree'"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
state.debugLastTrace(e);
|
state.debugLastTrace(e);
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -185,7 +184,7 @@ static void fetchTree(
|
||||||
|
|
||||||
if (evalSettings.pureEval && !input.isLocked())
|
if (evalSettings.pureEval && !input.isLocked())
|
||||||
{
|
{
|
||||||
auto e = EvalError("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", pos);
|
auto e = EvalError("in pure evaluation mode, 'fetchTree' requires a locked input, at %s", state.positions[pos]);
|
||||||
state.debugLastTrace(e);
|
state.debugLastTrace(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -197,7 +196,7 @@ static void fetchTree(
|
||||||
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
|
emitTreeAttrs(state, tree, input2, v, params.emptyRevFallback, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature(Xp::Flakes);
|
settings.requireExperimentalFeature(Xp::Flakes);
|
||||||
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
|
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
|
||||||
|
@ -206,7 +205,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V
|
||||||
// FIXME: document
|
// FIXME: document
|
||||||
static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree);
|
static RegisterPrimOp primop_fetchTree("fetchTree", 1, prim_fetchTree);
|
||||||
|
|
||||||
static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v,
|
||||||
const std::string & who, bool unpack, std::string name)
|
const std::string & who, bool unpack, std::string name)
|
||||||
{
|
{
|
||||||
std::optional<std::string> url;
|
std::optional<std::string> url;
|
||||||
|
@ -219,18 +218,18 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
state.forceAttrs(*args[0], pos);
|
state.forceAttrs(*args[0], pos);
|
||||||
|
|
||||||
for (auto & attr : *args[0]->attrs) {
|
for (auto & attr : *args[0]->attrs) {
|
||||||
std::string n(attr.name);
|
std::string_view n(state.symbols[attr.name]);
|
||||||
if (n == "url")
|
if (n == "url")
|
||||||
url = state.forceStringNoCtx(*attr.value, *attr.pos);
|
url = state.forceStringNoCtx(*attr.value, attr.pos);
|
||||||
else if (n == "sha256")
|
else if (n == "sha256")
|
||||||
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256);
|
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos), htSHA256);
|
||||||
else if (n == "name")
|
else if (n == "name")
|
||||||
name = state.forceStringNoCtx(*attr.value, *attr.pos);
|
name = state.forceStringNoCtx(*attr.value, attr.pos);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto e = EvalError({
|
auto e = EvalError({
|
||||||
.msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
|
.msg = hintfmt("unsupported argument '%s' to '%s'", n, who),
|
||||||
.errPos = *attr.pos
|
.errPos = state.positions[attr.pos]
|
||||||
});
|
});
|
||||||
state.debugLastTrace(e);
|
state.debugLastTrace(e);
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -241,7 +240,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
{
|
{
|
||||||
auto e = EvalError({
|
auto e = EvalError({
|
||||||
.msg = hintfmt("'url' argument required"),
|
.msg = hintfmt("'url' argument required"),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
state.debugLastTrace(e);
|
state.debugLastTrace(e);
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -299,7 +298,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
|
||||||
state.allowAndSetStorePathString(storePath, v);
|
state.allowAndSetStorePathString(storePath, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prim_fetchurl(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
fetch(state, pos, args, v, "fetchurl", false, "");
|
fetch(state, pos, args, v, "fetchurl", false, "");
|
||||||
}
|
}
|
||||||
|
@ -315,7 +314,7 @@ static RegisterPrimOp primop_fetchurl({
|
||||||
.fun = prim_fetchurl,
|
.fun = prim_fetchurl,
|
||||||
});
|
});
|
||||||
|
|
||||||
static void prim_fetchTarball(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchTarball(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
fetch(state, pos, args, v, "fetchTarball", true, "source");
|
fetch(state, pos, args, v, "fetchTarball", true, "source");
|
||||||
}
|
}
|
||||||
|
@ -366,7 +365,7 @@ static RegisterPrimOp primop_fetchTarball({
|
||||||
.fun = prim_fetchTarball,
|
.fun = prim_fetchTarball,
|
||||||
});
|
});
|
||||||
|
|
||||||
static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_fetchGit(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
|
fetchTree(state, pos, args, v, "git", FetchTreeParams { .emptyRevFallback = true, .allowNameArgument = true });
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val)
|
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & val)
|
||||||
{
|
{
|
||||||
auto toml = state.forceStringNoCtx(*args[0], pos);
|
auto toml = state.forceStringNoCtx(*args[0], pos);
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Va
|
||||||
} catch (std::exception & e) { // TODO: toml::syntax_error
|
} catch (std::exception & e) { // TODO: toml::syntax_error
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
|
.msg = hintfmt("while parsing a TOML string: %s", e.what()),
|
||||||
.errPos = pos
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,44 +5,32 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
|
#include "chunked-vector.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
/* Symbol table used by the parser and evaluator to represent and look
|
/* Symbol table used by the parser and evaluator to represent and look
|
||||||
up identifiers and attributes efficiently. SymbolTable::create()
|
up identifiers and attributes efficiently. SymbolTable::create()
|
||||||
converts a string into a symbol. Symbols have the property that
|
converts a string into a symbol. Symbols have the property that
|
||||||
they can be compared efficiently (using a pointer equality test),
|
they can be compared efficiently (using an equality test),
|
||||||
because the symbol table stores only one copy of each string. */
|
because the symbol table stores only one copy of each string. */
|
||||||
|
|
||||||
class Symbol
|
/* This class mainly exists to give us an operator<< for ostreams. We could also
|
||||||
|
return plain strings from SymbolTable, but then we'd have to wrap every
|
||||||
|
instance of a symbol that is fmt()ed, which is inconvenient and error-prone. */
|
||||||
|
class SymbolStr
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
const std::string * s; // pointer into SymbolTable
|
|
||||||
Symbol(const std::string * s) : s(s) { };
|
|
||||||
friend class SymbolTable;
|
friend class SymbolTable;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string * s;
|
||||||
|
|
||||||
|
explicit SymbolStr(const std::string & symbol): s(&symbol) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Symbol() : s(0) { };
|
|
||||||
|
|
||||||
bool operator == (const Symbol & s2) const
|
|
||||||
{
|
|
||||||
return s == s2.s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: remove
|
|
||||||
bool operator == (std::string_view s2) const
|
bool operator == (std::string_view s2) const
|
||||||
{
|
{
|
||||||
return s->compare(s2) == 0;
|
return *s == s2;
|
||||||
}
|
|
||||||
|
|
||||||
bool operator != (const Symbol & s2) const
|
|
||||||
{
|
|
||||||
return s != s2.s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator < (const Symbol & s2) const
|
|
||||||
{
|
|
||||||
return s < s2.s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
operator const std::string & () const
|
operator const std::string & () const
|
||||||
|
@ -55,51 +43,78 @@ public:
|
||||||
return *s;
|
return *s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool set() const
|
friend std::ostream & operator <<(std::ostream & os, const SymbolStr & symbol);
|
||||||
{
|
};
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
class Symbol
|
||||||
{
|
{
|
||||||
return s->empty();
|
friend class SymbolTable;
|
||||||
}
|
|
||||||
|
|
||||||
friend std::ostream & operator << (std::ostream & str, const Symbol & sym);
|
private:
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
|
explicit Symbol(uint32_t id): id(id) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Symbol() : id(0) {}
|
||||||
|
|
||||||
|
explicit operator bool() const { return id > 0; }
|
||||||
|
|
||||||
|
bool operator<(const Symbol other) const { return id < other.id; }
|
||||||
|
bool operator==(const Symbol other) const { return id == other.id; }
|
||||||
|
bool operator!=(const Symbol other) const { return id != other.id; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SymbolTable
|
class SymbolTable
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string_view, Symbol> symbols;
|
std::unordered_map<std::string_view, std::pair<const std::string *, uint32_t>> symbols;
|
||||||
std::list<std::string> store;
|
ChunkedVector<std::string, 8192> store{16};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Symbol create(std::string_view s)
|
Symbol create(std::string_view s)
|
||||||
{
|
{
|
||||||
// Most symbols are looked up more than once, so we trade off insertion performance
|
// Most symbols are looked up more than once, so we trade off insertion performance
|
||||||
// for lookup performance.
|
// for lookup performance.
|
||||||
// TODO: could probably be done more efficiently with transparent Hash and Equals
|
// TODO: could probably be done more efficiently with transparent Hash and Equals
|
||||||
// on the original implementation using unordered_set
|
// on the original implementation using unordered_set
|
||||||
|
// FIXME: make this thread-safe.
|
||||||
auto it = symbols.find(s);
|
auto it = symbols.find(s);
|
||||||
if (it != symbols.end()) return it->second;
|
if (it != symbols.end()) return Symbol(it->second.second + 1);
|
||||||
|
|
||||||
auto & rawSym = store.emplace_back(s);
|
const auto & [rawSym, idx] = store.add(std::string(s));
|
||||||
return symbols.emplace(rawSym, Symbol(&rawSym)).first->second;
|
symbols.emplace(rawSym, std::make_pair(&rawSym, idx));
|
||||||
|
return Symbol(idx + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SymbolStr> resolve(const std::vector<Symbol> & symbols) const
|
||||||
|
{
|
||||||
|
std::vector<SymbolStr> result;
|
||||||
|
result.reserve(symbols.size());
|
||||||
|
for (auto sym : symbols)
|
||||||
|
result.push_back((*this)[sym]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolStr operator[](Symbol s) const
|
||||||
|
{
|
||||||
|
if (s.id == 0 || s.id > store.size())
|
||||||
|
abort();
|
||||||
|
return SymbolStr(store[s.id - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size() const
|
size_t size() const
|
||||||
{
|
{
|
||||||
return symbols.size();
|
return store.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t totalSize() const;
|
size_t totalSize() const;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void dump(T callback)
|
void dump(T callback) const
|
||||||
{
|
{
|
||||||
for (auto & s : store)
|
store.forEach(callback);
|
||||||
callback(s);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
void printValueAsJSON(EvalState & state, bool strict,
|
void printValueAsJSON(EvalState & state, bool strict,
|
||||||
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context)
|
Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
|
@ -50,14 +50,14 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||||
auto obj(out.object());
|
auto obj(out.object());
|
||||||
StringSet names;
|
StringSet names;
|
||||||
for (auto & j : *v.attrs)
|
for (auto & j : *v.attrs)
|
||||||
names.insert(j.name);
|
names.emplace(state.symbols[j.name]);
|
||||||
for (auto & j : names) {
|
for (auto & j : names) {
|
||||||
Attr & a(*v.attrs->find(state.symbols.create(j)));
|
Attr & a(*v.attrs->find(state.symbols.create(j)));
|
||||||
auto placeholder(obj.placeholder(j));
|
auto placeholder(obj.placeholder(j));
|
||||||
printValueAsJSON(state, strict, *a.value, *a.pos, placeholder, context);
|
printValueAsJSON(state, strict, *a.value, a.pos, placeholder, context);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
printValueAsJSON(state, strict, *i->value, *i->pos, out, context);
|
printValueAsJSON(state, strict, *i->value, i->pos, out, context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,16 +82,16 @@ void printValueAsJSON(EvalState & state, bool strict,
|
||||||
case nFunction:
|
case nFunction:
|
||||||
auto e = TypeError({
|
auto e = TypeError({
|
||||||
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
|
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
|
||||||
.errPos = v.determinePos(pos)
|
.errPos = state.positions[v.determinePos(pos)]
|
||||||
});
|
});
|
||||||
e.addTrace(pos, hintfmt("message for the trace"));
|
e.addTrace(state.positions[pos], hintfmt("message for the trace"));
|
||||||
state.debugLastTrace(e);
|
state.debugLastTrace(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void printValueAsJSON(EvalState & state, bool strict,
|
void printValueAsJSON(EvalState & state, bool strict,
|
||||||
Value & v, const Pos & pos, std::ostream & str, PathSet & context)
|
Value & v, const PosIdx pos, std::ostream & str, PathSet & context)
|
||||||
{
|
{
|
||||||
JSONPlaceholder out(str);
|
JSONPlaceholder out(str);
|
||||||
printValueAsJSON(state, strict, v, pos, out, context);
|
printValueAsJSON(state, strict, v, pos, out, context);
|
||||||
|
|
|
@ -11,9 +11,9 @@ namespace nix {
|
||||||
class JSONPlaceholder;
|
class JSONPlaceholder;
|
||||||
|
|
||||||
void printValueAsJSON(EvalState & state, bool strict,
|
void printValueAsJSON(EvalState & state, bool strict,
|
||||||
Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context);
|
Value & v, const PosIdx pos, JSONPlaceholder & out, PathSet & context);
|
||||||
|
|
||||||
void printValueAsJSON(EvalState & state, bool strict,
|
void printValueAsJSON(EvalState & state, bool strict,
|
||||||
Value & v, const Pos & pos, std::ostream & str, PathSet & context);
|
Value & v, const PosIdx pos, std::ostream & str, PathSet & context);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,10 @@ static XMLAttrs singletonAttrs(const std::string & name, const std::string & val
|
||||||
|
|
||||||
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||||
const Pos & pos);
|
const PosIdx pos);
|
||||||
|
|
||||||
|
|
||||||
static void posToXML(XMLAttrs & xmlAttrs, const Pos & pos)
|
static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
|
||||||
{
|
{
|
||||||
xmlAttrs["path"] = pos.file;
|
xmlAttrs["path"] = pos.file;
|
||||||
xmlAttrs["line"] = (format("%1%") % pos.line).str();
|
xmlAttrs["line"] = (format("%1%") % pos.line).str();
|
||||||
|
@ -36,25 +36,25 @@ static void showAttrs(EvalState & state, bool strict, bool location,
|
||||||
StringSet names;
|
StringSet names;
|
||||||
|
|
||||||
for (auto & i : attrs)
|
for (auto & i : attrs)
|
||||||
names.insert(i.name);
|
names.emplace(state.symbols[i.name]);
|
||||||
|
|
||||||
for (auto & i : names) {
|
for (auto & i : names) {
|
||||||
Attr & a(*attrs.find(state.symbols.create(i)));
|
Attr & a(*attrs.find(state.symbols.create(i)));
|
||||||
|
|
||||||
XMLAttrs xmlAttrs;
|
XMLAttrs xmlAttrs;
|
||||||
xmlAttrs["name"] = i;
|
xmlAttrs["name"] = i;
|
||||||
if (location && a.pos != ptr(&noPos)) posToXML(xmlAttrs, *a.pos);
|
if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]);
|
||||||
|
|
||||||
XMLOpenElement _(doc, "attr", xmlAttrs);
|
XMLOpenElement _(doc, "attr", xmlAttrs);
|
||||||
printValueAsXML(state, strict, location,
|
printValueAsXML(state, strict, location,
|
||||||
*a.value, doc, context, drvsSeen, *a.pos);
|
*a.value, doc, context, drvsSeen, a.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
Value & v, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||||
const Pos & pos)
|
const PosIdx pos)
|
||||||
{
|
{
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
|
@ -93,14 +93,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Path drvPath;
|
Path drvPath;
|
||||||
a = v.attrs->find(state.sDrvPath);
|
a = v.attrs->find(state.sDrvPath);
|
||||||
if (a != v.attrs->end()) {
|
if (a != v.attrs->end()) {
|
||||||
if (strict) state.forceValue(*a->value, *a->pos);
|
if (strict) state.forceValue(*a->value, a->pos);
|
||||||
if (a->value->type() == nString)
|
if (a->value->type() == nString)
|
||||||
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
|
xmlAttrs["drvPath"] = drvPath = a->value->string.s;
|
||||||
}
|
}
|
||||||
|
|
||||||
a = v.attrs->find(state.sOutPath);
|
a = v.attrs->find(state.sOutPath);
|
||||||
if (a != v.attrs->end()) {
|
if (a != v.attrs->end()) {
|
||||||
if (strict) state.forceValue(*a->value, *a->pos);
|
if (strict) state.forceValue(*a->value, a->pos);
|
||||||
if (a->value->type() == nString)
|
if (a->value->type() == nString)
|
||||||
xmlAttrs["outPath"] = a->value->string.s;
|
xmlAttrs["outPath"] = a->value->string.s;
|
||||||
}
|
}
|
||||||
|
@ -134,18 +134,18 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
XMLAttrs xmlAttrs;
|
XMLAttrs xmlAttrs;
|
||||||
if (location) posToXML(xmlAttrs, v.lambda.fun->pos);
|
if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]);
|
||||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||||
|
|
||||||
if (v.lambda.fun->hasFormals()) {
|
if (v.lambda.fun->hasFormals()) {
|
||||||
XMLAttrs attrs;
|
XMLAttrs attrs;
|
||||||
if (!v.lambda.fun->arg.empty()) attrs["name"] = v.lambda.fun->arg;
|
if (v.lambda.fun->arg) attrs["name"] = state.symbols[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->lexicographicOrder())
|
for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols))
|
||||||
doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
|
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
||||||
} else
|
} else
|
||||||
doc.writeEmptyElement("varpat", singletonAttrs("name", v.lambda.fun->arg));
|
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg]));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -166,14 +166,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
|
|
||||||
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
|
void ExternalValueBase::printValueAsXML(EvalState & state, bool strict,
|
||||||
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
bool location, XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||||
const Pos & pos) const
|
const PosIdx pos) const
|
||||||
{
|
{
|
||||||
doc.writeEmptyElement("unevaluated");
|
doc.writeEmptyElement("unevaluated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void printValueAsXML(EvalState & state, bool strict, bool location,
|
void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Value & v, std::ostream & out, PathSet & context, const Pos & pos)
|
Value & v, std::ostream & out, PathSet & context, const PosIdx pos)
|
||||||
{
|
{
|
||||||
XMLWriter doc(true, out);
|
XMLWriter doc(true, out);
|
||||||
XMLOpenElement root(doc, "expr");
|
XMLOpenElement root(doc, "expr");
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
void printValueAsXML(EvalState & state, bool strict, bool location,
|
void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
Value & v, std::ostream & out, PathSet & context, const Pos & pos);
|
Value & v, std::ostream & out, PathSet & context, const PosIdx pos);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ struct Expr;
|
||||||
struct ExprLambda;
|
struct ExprLambda;
|
||||||
struct PrimOp;
|
struct PrimOp;
|
||||||
class Symbol;
|
class Symbol;
|
||||||
|
class PosIdx;
|
||||||
struct Pos;
|
struct Pos;
|
||||||
class StorePath;
|
class StorePath;
|
||||||
class Store;
|
class Store;
|
||||||
|
@ -103,7 +104,7 @@ class ExternalValueBase
|
||||||
/* Print the value as XML. Defaults to unevaluated */
|
/* Print the value as XML. Defaults to unevaluated */
|
||||||
virtual void printValueAsXML(EvalState & state, bool strict, bool location,
|
virtual void printValueAsXML(EvalState & state, bool strict, bool location,
|
||||||
XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
XMLWriter & doc, PathSet & context, PathSet & drvsSeen,
|
||||||
const Pos & pos) const;
|
const PosIdx pos) const;
|
||||||
|
|
||||||
virtual ~ExternalValueBase()
|
virtual ~ExternalValueBase()
|
||||||
{
|
{
|
||||||
|
@ -120,11 +121,11 @@ private:
|
||||||
|
|
||||||
friend std::string showType(const Value & v);
|
friend std::string showType(const Value & v);
|
||||||
|
|
||||||
void print(std::ostream & str, std::set<const void *> * seen) const;
|
void print(const SymbolTable & symbols, std::ostream & str, std::set<const void *> * seen) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void print(std::ostream & str, bool showRepeated = false) const;
|
void print(const SymbolTable & symbols, std::ostream & str, bool showRepeated = false) const;
|
||||||
|
|
||||||
// Functions needed to distinguish the type
|
// Functions needed to distinguish the type
|
||||||
// These should be removed eventually, by putting the functionality that's
|
// These should be removed eventually, by putting the functionality that's
|
||||||
|
@ -250,11 +251,6 @@ public:
|
||||||
|
|
||||||
void mkStringMove(const char * s, const PathSet & context);
|
void mkStringMove(const char * s, const PathSet & context);
|
||||||
|
|
||||||
inline void mkString(const Symbol & s)
|
|
||||||
{
|
|
||||||
mkString(((const std::string &) s).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void mkPath(const char * s)
|
inline void mkPath(const char * s)
|
||||||
{
|
{
|
||||||
clearValue();
|
clearValue();
|
||||||
|
@ -368,7 +364,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;
|
PosIdx determinePos(const PosIdx 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
|
||||||
|
|
|
@ -21,9 +21,18 @@ namespace nix::fetchers {
|
||||||
// old version of git, which will ignore unrecognized `-c` options.
|
// old version of git, which will ignore unrecognized `-c` options.
|
||||||
const std::string gitInitialBranch = "__nix_dummy_branch";
|
const std::string gitInitialBranch = "__nix_dummy_branch";
|
||||||
|
|
||||||
|
static std::string getGitDir()
|
||||||
|
{
|
||||||
|
auto gitDir = getEnv("GIT_DIR");
|
||||||
|
if (!gitDir) {
|
||||||
|
return ".git";
|
||||||
|
}
|
||||||
|
return *gitDir;
|
||||||
|
}
|
||||||
|
|
||||||
static std::string readHead(const Path & path)
|
static std::string readHead(const Path & path)
|
||||||
{
|
{
|
||||||
return chomp(runProgram("git", true, { "-C", path, "rev-parse", "--abbrev-ref", "HEAD" }));
|
return chomp(runProgram("git", true, { "-C", path, "--git-dir", ".git", "rev-parse", "--abbrev-ref", "HEAD" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isNotDotGitDirectory(const Path & path)
|
static bool isNotDotGitDirectory(const Path & path)
|
||||||
|
@ -150,13 +159,14 @@ struct GitInputScheme : InputScheme
|
||||||
{
|
{
|
||||||
auto sourcePath = getSourcePath(input);
|
auto sourcePath = getSourcePath(input);
|
||||||
assert(sourcePath);
|
assert(sourcePath);
|
||||||
|
auto gitDir = getGitDir();
|
||||||
|
|
||||||
runProgram("git", true,
|
runProgram("git", true,
|
||||||
{ "-C", *sourcePath, "add", "--force", "--intent-to-add", "--", std::string(file) });
|
{ "-C", *sourcePath, "--git-dir", gitDir, "add", "--force", "--intent-to-add", "--", std::string(file) });
|
||||||
|
|
||||||
if (commitMsg)
|
if (commitMsg)
|
||||||
runProgram("git", true,
|
runProgram("git", true,
|
||||||
{ "-C", *sourcePath, "commit", std::string(file), "-m", *commitMsg });
|
{ "-C", *sourcePath, "--git-dir", gitDir, "commit", std::string(file), "-m", *commitMsg });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool, std::string> getActualUrl(const Input & input) const
|
std::pair<bool, std::string> getActualUrl(const Input & input) const
|
||||||
|
@ -175,6 +185,7 @@ struct GitInputScheme : InputScheme
|
||||||
std::pair<StorePath, 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);
|
||||||
|
auto gitDir = getGitDir();
|
||||||
|
|
||||||
std::string name = input.getName();
|
std::string name = input.getName();
|
||||||
|
|
||||||
|
@ -237,7 +248,7 @@ struct GitInputScheme : InputScheme
|
||||||
since that is the refrence we want to use later on. */
|
since that is the refrence we want to use later on. */
|
||||||
auto result = runProgram(RunOptions {
|
auto result = runProgram(RunOptions {
|
||||||
.program = "git",
|
.program = "git",
|
||||||
.args = { "-C", actualUrl, "--git-dir=.git", "rev-parse", "--verify", "--no-revs", "HEAD^{commit}" },
|
.args = { "-C", actualUrl, "--git-dir", gitDir, "rev-parse", "--verify", "--no-revs", "HEAD^{commit}" },
|
||||||
.environment = env,
|
.environment = env,
|
||||||
.mergeStderrToStdout = true
|
.mergeStderrToStdout = true
|
||||||
});
|
});
|
||||||
|
@ -259,7 +270,7 @@ struct GitInputScheme : InputScheme
|
||||||
if (hasHead) {
|
if (hasHead) {
|
||||||
// Using git diff is preferrable over lower-level operations here,
|
// Using git diff is preferrable over lower-level operations here,
|
||||||
// because its conceptually simpler and we only need the exit code anyways.
|
// because its conceptually simpler and we only need the exit code anyways.
|
||||||
auto gitDiffOpts = Strings({ "-C", actualUrl, "diff", "HEAD", "--quiet"});
|
auto gitDiffOpts = Strings({ "-C", actualUrl, "--git-dir", gitDir, "diff", "HEAD", "--quiet"});
|
||||||
if (!submodules) {
|
if (!submodules) {
|
||||||
// Changes in submodules should only make the tree dirty
|
// Changes in submodules should only make the tree dirty
|
||||||
// when those submodules will be copied as well.
|
// when those submodules will be copied as well.
|
||||||
|
@ -284,7 +295,7 @@ struct GitInputScheme : InputScheme
|
||||||
if (fetchSettings.warnDirty)
|
if (fetchSettings.warnDirty)
|
||||||
warn("Git tree '%s' is dirty", actualUrl);
|
warn("Git tree '%s' is dirty", actualUrl);
|
||||||
|
|
||||||
auto gitOpts = Strings({ "-C", actualUrl, "ls-files", "-z" });
|
auto gitOpts = Strings({ "-C", actualUrl, "--git-dir", gitDir, "ls-files", "-z" });
|
||||||
if (submodules)
|
if (submodules)
|
||||||
gitOpts.emplace_back("--recurse-submodules");
|
gitOpts.emplace_back("--recurse-submodules");
|
||||||
|
|
||||||
|
@ -314,7 +325,7 @@ struct GitInputScheme : InputScheme
|
||||||
// modified dirty file?
|
// modified dirty file?
|
||||||
input.attrs.insert_or_assign(
|
input.attrs.insert_or_assign(
|
||||||
"lastModified",
|
"lastModified",
|
||||||
hasHead ? std::stoull(runProgram("git", true, { "-C", actualPath, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
|
hasHead ? std::stoull(runProgram("git", true, { "-C", actualPath, "--git-dir", gitDir, "log", "-1", "--format=%ct", "--no-show-signature", "HEAD" })) : 0);
|
||||||
|
|
||||||
return {std::move(storePath), input};
|
return {std::move(storePath), input};
|
||||||
}
|
}
|
||||||
|
@ -335,7 +346,7 @@ struct GitInputScheme : InputScheme
|
||||||
|
|
||||||
if (!input.getRev())
|
if (!input.getRev())
|
||||||
input.attrs.insert_or_assign("rev",
|
input.attrs.insert_or_assign("rev",
|
||||||
Hash::parseAny(chomp(runProgram("git", true, { "-C", actualUrl, "rev-parse", *input.getRef() })), htSHA1).gitRev());
|
Hash::parseAny(chomp(runProgram("git", true, { "-C", actualUrl, "--git-dir", gitDir, "rev-parse", *input.getRef() })), htSHA1).gitRev());
|
||||||
|
|
||||||
repoDir = actualUrl;
|
repoDir = actualUrl;
|
||||||
|
|
||||||
|
@ -351,6 +362,7 @@ struct GitInputScheme : InputScheme
|
||||||
|
|
||||||
Path cacheDir = getCacheDir() + "/nix/gitv3/" + hashString(htSHA256, actualUrl).to_string(Base32, false);
|
Path cacheDir = getCacheDir() + "/nix/gitv3/" + hashString(htSHA256, actualUrl).to_string(Base32, false);
|
||||||
repoDir = cacheDir;
|
repoDir = cacheDir;
|
||||||
|
gitDir = ".";
|
||||||
|
|
||||||
createDirs(dirOf(cacheDir));
|
createDirs(dirOf(cacheDir));
|
||||||
PathLocks cacheDirLock({cacheDir + ".lock"});
|
PathLocks cacheDirLock({cacheDir + ".lock"});
|
||||||
|
@ -427,7 +439,7 @@ struct GitInputScheme : InputScheme
|
||||||
// cache dir lock is removed at scope end; we will only use read-only operations on specific revisions in the remainder
|
// cache dir lock is removed at scope end; we will only use read-only operations on specific revisions in the remainder
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "rev-parse", "--is-shallow-repository" })) == "true";
|
bool isShallow = chomp(runProgram("git", true, { "-C", repoDir, "--git-dir", gitDir, "rev-parse", "--is-shallow-repository" })) == "true";
|
||||||
|
|
||||||
if (isShallow && !shallow)
|
if (isShallow && !shallow)
|
||||||
throw Error("'%s' is a shallow Git repository, but a non-shallow repository is needed", actualUrl);
|
throw Error("'%s' is a shallow Git repository, but a non-shallow repository is needed", actualUrl);
|
||||||
|
|
|
@ -36,7 +36,7 @@ static std::string runHg(const Strings & args, const std::optional<std::string>
|
||||||
auto res = runProgram(std::move(opts));
|
auto res = runProgram(std::move(opts));
|
||||||
|
|
||||||
if (!statusOk(res.first))
|
if (!statusOk(res.first))
|
||||||
throw ExecError(res.first, fmt("hg %1%", statusToString(res.first)));
|
throw ExecError(res.first, "hg %1%", statusToString(res.first));
|
||||||
|
|
||||||
return res.second;
|
return res.second;
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
runHg({ "recover", "-R", cacheDir });
|
runHg({ "recover", "-R", cacheDir });
|
||||||
runHg({ "pull", "-R", cacheDir, "--", actualUrl });
|
runHg({ "pull", "-R", cacheDir, "--", actualUrl });
|
||||||
} else {
|
} else {
|
||||||
throw ExecError(e.status, fmt("'hg pull' %s", statusToString(e.status)));
|
throw ExecError(e.status, "'hg pull' %s", statusToString(e.status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -60,37 +60,37 @@ void printMissing(ref<Store> store, const StorePathSet & willBuild,
|
||||||
{
|
{
|
||||||
if (!willBuild.empty()) {
|
if (!willBuild.empty()) {
|
||||||
if (willBuild.size() == 1)
|
if (willBuild.size() == 1)
|
||||||
printMsg(lvl, fmt("this derivation will be built:"));
|
printMsg(lvl, "this derivation will be built:");
|
||||||
else
|
else
|
||||||
printMsg(lvl, fmt("these %d derivations will be built:", willBuild.size()));
|
printMsg(lvl, "these %d derivations will be built:", willBuild.size());
|
||||||
auto sorted = store->topoSortPaths(willBuild);
|
auto sorted = store->topoSortPaths(willBuild);
|
||||||
reverse(sorted.begin(), sorted.end());
|
reverse(sorted.begin(), sorted.end());
|
||||||
for (auto & i : sorted)
|
for (auto & i : sorted)
|
||||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
printMsg(lvl, " %s", store->printStorePath(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!willSubstitute.empty()) {
|
if (!willSubstitute.empty()) {
|
||||||
const float downloadSizeMiB = downloadSize / (1024.f * 1024.f);
|
const float downloadSizeMiB = downloadSize / (1024.f * 1024.f);
|
||||||
const float narSizeMiB = narSize / (1024.f * 1024.f);
|
const float narSizeMiB = narSize / (1024.f * 1024.f);
|
||||||
if (willSubstitute.size() == 1) {
|
if (willSubstitute.size() == 1) {
|
||||||
printMsg(lvl, fmt("this path will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
printMsg(lvl, "this path will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||||
downloadSizeMiB,
|
downloadSizeMiB,
|
||||||
narSizeMiB));
|
narSizeMiB);
|
||||||
} else {
|
} else {
|
||||||
printMsg(lvl, fmt("these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
printMsg(lvl, "these %d paths will be fetched (%.2f MiB download, %.2f MiB unpacked):",
|
||||||
willSubstitute.size(),
|
willSubstitute.size(),
|
||||||
downloadSizeMiB,
|
downloadSizeMiB,
|
||||||
narSizeMiB));
|
narSizeMiB);
|
||||||
}
|
}
|
||||||
for (auto & i : willSubstitute)
|
for (auto & i : willSubstitute)
|
||||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
printMsg(lvl, " %s", store->printStorePath(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unknown.empty()) {
|
if (!unknown.empty()) {
|
||||||
printMsg(lvl, fmt("don't know how to build these paths%s:",
|
printMsg(lvl, "don't know how to build these paths%s:",
|
||||||
(settings.readOnlyMode ? " (may be caused by read-only store access)" : "")));
|
(settings.readOnlyMode ? " (may be caused by read-only store access)" : ""));
|
||||||
for (auto & i : unknown)
|
for (auto & i : unknown)
|
||||||
printMsg(lvl, fmt(" %s", store->printStorePath(i)));
|
printMsg(lvl, " %s", store->printStorePath(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -786,8 +786,7 @@ void runPostBuildHook(
|
||||||
Store & store,
|
Store & store,
|
||||||
Logger & logger,
|
Logger & logger,
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath,
|
||||||
StorePathSet outputPaths
|
const StorePathSet & outputPaths)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
auto hook = settings.postBuildHook;
|
auto hook = settings.postBuildHook;
|
||||||
if (hook == "")
|
if (hook == "")
|
||||||
|
@ -906,7 +905,7 @@ void DerivationGoal::buildDone()
|
||||||
auto builtOutputs = registerOutputs();
|
auto builtOutputs = registerOutputs();
|
||||||
|
|
||||||
StorePathSet outputPaths;
|
StorePathSet outputPaths;
|
||||||
for (auto & [_, output] : buildResult.builtOutputs)
|
for (auto & [_, output] : builtOutputs)
|
||||||
outputPaths.insert(output.outPath);
|
outputPaths.insert(output.outPath);
|
||||||
runPostBuildHook(
|
runPostBuildHook(
|
||||||
worker.store,
|
worker.store,
|
||||||
|
|
|
@ -13,12 +13,27 @@ create table if not exists Realisations (
|
||||||
|
|
||||||
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
||||||
|
|
||||||
|
-- We can end-up in a weird edge-case where a path depends on itself because
|
||||||
|
-- it’s an output of a CA derivation, that happens to be the same as one of its
|
||||||
|
-- dependencies.
|
||||||
|
-- In that case we have a dependency loop (path -> realisation1 -> realisation2
|
||||||
|
-- -> path) that we need to break by removing the dependencies between the
|
||||||
|
-- realisations
|
||||||
|
create trigger if not exists DeleteSelfRefsViaRealisations before delete on ValidPaths
|
||||||
|
begin
|
||||||
|
delete from RealisationsRefs where realisationReference in (
|
||||||
|
select id from Realisations where outputPath = old.id
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
|
||||||
create table if not exists RealisationsRefs (
|
create table if not exists RealisationsRefs (
|
||||||
referrer integer not null,
|
referrer integer not null,
|
||||||
realisationReference integer,
|
realisationReference integer,
|
||||||
foreign key (referrer) references Realisations(id) on delete cascade,
|
foreign key (referrer) references Realisations(id) on delete cascade,
|
||||||
foreign key (realisationReference) references Realisations(id) on delete restrict
|
foreign key (realisationReference) references Realisations(id) on delete restrict
|
||||||
);
|
);
|
||||||
|
-- used by deletion trigger
|
||||||
|
create index if not exists IndexRealisationsRefsRealisationReference on RealisationsRefs(realisationReference);
|
||||||
|
|
||||||
-- used by QueryRealisationReferences
|
-- used by QueryRealisationReferences
|
||||||
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
|
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
|
||||||
|
|
|
@ -443,14 +443,13 @@ struct curlFileTransfer : public FileTransfer
|
||||||
: httpStatus != 0
|
: httpStatus != 0
|
||||||
? FileTransferError(err,
|
? FileTransferError(err,
|
||||||
std::move(response),
|
std::move(response),
|
||||||
fmt("unable to %s '%s': HTTP error %d ('%s')",
|
"unable to %s '%s': HTTP error %d%s",
|
||||||
request.verb(), request.uri, httpStatus, statusMsg)
|
request.verb(), request.uri, httpStatus,
|
||||||
+ (code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
|
code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
|
||||||
)
|
|
||||||
: FileTransferError(err,
|
: FileTransferError(err,
|
||||||
std::move(response),
|
std::move(response),
|
||||||
fmt("unable to %s '%s': %s (%d)",
|
"unable to %s '%s': %s (%d)",
|
||||||
request.verb(), request.uri, curl_easy_strerror(code), code));
|
request.verb(), request.uri, curl_easy_strerror(code), code);
|
||||||
|
|
||||||
/* If this is a transient error, then maybe retry the
|
/* If this is a transient error, then maybe retry the
|
||||||
download after a while. If we're writing to a
|
download after a while. If we're writing to a
|
||||||
|
@ -704,7 +703,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, {}, "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
|
||||||
|
|
|
@ -31,7 +31,7 @@ struct FileTransferSettings : Config
|
||||||
R"(
|
R"(
|
||||||
The timeout (in seconds) for establishing connections in the
|
The timeout (in seconds) for establishing connections in the
|
||||||
binary cache substituter. It corresponds to `curl`’s
|
binary cache substituter. It corresponds to `curl`’s
|
||||||
`--connect-timeout` option.
|
`--connect-timeout` option. A value of 0 means no limit.
|
||||||
)"};
|
)"};
|
||||||
|
|
||||||
Setting<unsigned long> stalledDownloadTimeout{
|
Setting<unsigned long> stalledDownloadTimeout{
|
||||||
|
|
|
@ -81,7 +81,7 @@ int getSchema(Path schemaPath)
|
||||||
|
|
||||||
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||||
{
|
{
|
||||||
const int nixCASchemaVersion = 3;
|
const int nixCASchemaVersion = 4;
|
||||||
int curCASchema = getSchema(schemaPath);
|
int curCASchema = getSchema(schemaPath);
|
||||||
if (curCASchema != nixCASchemaVersion) {
|
if (curCASchema != nixCASchemaVersion) {
|
||||||
if (curCASchema > nixCASchemaVersion) {
|
if (curCASchema > nixCASchemaVersion) {
|
||||||
|
@ -143,6 +143,21 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||||
)");
|
)");
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
if (curCASchema < 4) {
|
||||||
|
SQLiteTxn txn(db);
|
||||||
|
db.exec(R"(
|
||||||
|
create trigger if not exists DeleteSelfRefsViaRealisations before delete on ValidPaths
|
||||||
|
begin
|
||||||
|
delete from RealisationsRefs where realisationReference in (
|
||||||
|
select id from Realisations where outputPath = old.id
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
-- used by deletion trigger
|
||||||
|
create index if not exists IndexRealisationsRefsRealisationReference on RealisationsRefs(realisationReference);
|
||||||
|
)");
|
||||||
|
txn.commit();
|
||||||
|
}
|
||||||
|
|
||||||
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
||||||
lockFile(lockFd.get(), ltRead, true);
|
lockFile(lockFd.get(), ltRead, true);
|
||||||
}
|
}
|
||||||
|
@ -482,18 +497,18 @@ void LocalStore::openDB(State & state, bool create)
|
||||||
SQLiteStmt stmt;
|
SQLiteStmt stmt;
|
||||||
stmt.create(db, "pragma main.journal_mode;");
|
stmt.create(db, "pragma main.journal_mode;");
|
||||||
if (sqlite3_step(stmt) != SQLITE_ROW)
|
if (sqlite3_step(stmt) != SQLITE_ROW)
|
||||||
throwSQLiteError(db, "querying journal mode");
|
SQLiteError::throw_(db, "querying journal mode");
|
||||||
prevMode = std::string((const char *) sqlite3_column_text(stmt, 0));
|
prevMode = std::string((const char *) sqlite3_column_text(stmt, 0));
|
||||||
}
|
}
|
||||||
if (prevMode != mode &&
|
if (prevMode != mode &&
|
||||||
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "setting journal mode");
|
SQLiteError::throw_(db, "setting journal mode");
|
||||||
|
|
||||||
/* Increase the auto-checkpoint interval to 40000 pages. This
|
/* Increase the auto-checkpoint interval to 40000 pages. This
|
||||||
seems enough to ensure that instantiating the NixOS system
|
seems enough to ensure that instantiating the NixOS system
|
||||||
derivation is done in a single fsync(). */
|
derivation is done in a single fsync(). */
|
||||||
if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;", 0, 0, 0) != SQLITE_OK)
|
if (mode == "wal" && sqlite3_exec(db, "pragma wal_autocheckpoint = 40000;", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "setting autocheckpoint interval");
|
SQLiteError::throw_(db, "setting autocheckpoint interval");
|
||||||
|
|
||||||
/* Initialise the database schema, if necessary. */
|
/* Initialise the database schema, if necessary. */
|
||||||
if (create) {
|
if (create) {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "ref.hh"
|
#include "ref.hh"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Aws { namespace Client { class ClientConfiguration; } }
|
namespace Aws { namespace Client { class ClientConfiguration; } }
|
||||||
namespace Aws { namespace S3 { class S3Client; } }
|
namespace Aws { namespace S3 { class S3Client; } }
|
||||||
|
|
|
@ -8,22 +8,32 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
[[noreturn]] void throwSQLiteError(sqlite3 * db, const FormatOrString & fs)
|
SQLiteError::SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf)
|
||||||
|
: Error(""), path(path), errNo(errNo), extendedErrNo(extendedErrNo)
|
||||||
|
{
|
||||||
|
err.msg = hintfmt("%s: %s (in '%s')",
|
||||||
|
normaltxt(hf.str()),
|
||||||
|
sqlite3_errstr(extendedErrNo),
|
||||||
|
path ? path : "(in-memory)");
|
||||||
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void SQLiteError::throw_(sqlite3 * db, hintformat && hf)
|
||||||
{
|
{
|
||||||
int err = sqlite3_errcode(db);
|
int err = sqlite3_errcode(db);
|
||||||
int exterr = sqlite3_extended_errcode(db);
|
int exterr = sqlite3_extended_errcode(db);
|
||||||
|
|
||||||
auto path = sqlite3_db_filename(db, nullptr);
|
auto path = sqlite3_db_filename(db, nullptr);
|
||||||
if (!path) path = "(in-memory)";
|
|
||||||
|
|
||||||
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
|
if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {
|
||||||
throw SQLiteBusy(
|
auto exp = SQLiteBusy(path, err, exterr, std::move(hf));
|
||||||
|
exp.err.msg = hintfmt(
|
||||||
err == SQLITE_PROTOCOL
|
err == SQLITE_PROTOCOL
|
||||||
? fmt("SQLite database '%s' is busy (SQLITE_PROTOCOL)", path)
|
? "SQLite database '%s' is busy (SQLITE_PROTOCOL)"
|
||||||
: fmt("SQLite database '%s' is busy", path));
|
: "SQLite database '%s' is busy",
|
||||||
}
|
path ? path : "(in-memory)");
|
||||||
else
|
throw exp;
|
||||||
throw SQLiteError("%s: %s (in '%s')", fs.s, sqlite3_errstr(exterr), path);
|
} else
|
||||||
|
throw SQLiteError(path, err, exterr, std::move(hf));
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLite::SQLite(const Path & path, bool create)
|
SQLite::SQLite(const Path & path, bool create)
|
||||||
|
@ -37,7 +47,7 @@ SQLite::SQLite(const Path & path, bool create)
|
||||||
throw Error("cannot open SQLite database '%s'", path);
|
throw Error("cannot open SQLite database '%s'", path);
|
||||||
|
|
||||||
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "setting timeout");
|
SQLiteError::throw_(db, "setting timeout");
|
||||||
|
|
||||||
exec("pragma foreign_keys = 1");
|
exec("pragma foreign_keys = 1");
|
||||||
}
|
}
|
||||||
|
@ -46,7 +56,7 @@ SQLite::~SQLite()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (db && sqlite3_close(db) != SQLITE_OK)
|
if (db && sqlite3_close(db) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "closing database");
|
SQLiteError::throw_(db, "closing database");
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -62,7 +72,7 @@ void SQLite::exec(const std::string & stmt)
|
||||||
{
|
{
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
if (sqlite3_exec(db, stmt.c_str(), 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, stmt.c_str(), 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, format("executing SQLite statement '%s'") % stmt);
|
SQLiteError::throw_(db, "executing SQLite statement '%s'", stmt);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +86,7 @@ void SQLiteStmt::create(sqlite3 * db, const std::string & sql)
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
assert(!stmt);
|
assert(!stmt);
|
||||||
if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0) != SQLITE_OK)
|
if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, fmt("creating statement '%s'", sql));
|
SQLiteError::throw_(db, "creating statement '%s'", sql);
|
||||||
this->db = db;
|
this->db = db;
|
||||||
this->sql = sql;
|
this->sql = sql;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +95,7 @@ SQLiteStmt::~SQLiteStmt()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
|
if (stmt && sqlite3_finalize(stmt) != SQLITE_OK)
|
||||||
throwSQLiteError(db, fmt("finalizing statement '%s'", sql));
|
SQLiteError::throw_(db, "finalizing statement '%s'", sql);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
@ -109,7 +119,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (std::string_view value, bool not
|
||||||
{
|
{
|
||||||
if (notNull) {
|
if (notNull) {
|
||||||
if (sqlite3_bind_text(stmt, curArg++, value.data(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
if (sqlite3_bind_text(stmt, curArg++, value.data(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
throwSQLiteError(stmt.db, "binding argument");
|
SQLiteError::throw_(stmt.db, "binding argument");
|
||||||
} else
|
} else
|
||||||
bind();
|
bind();
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -119,7 +129,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (const unsigned char * data, size
|
||||||
{
|
{
|
||||||
if (notNull) {
|
if (notNull) {
|
||||||
if (sqlite3_bind_blob(stmt, curArg++, data, len, SQLITE_TRANSIENT) != SQLITE_OK)
|
if (sqlite3_bind_blob(stmt, curArg++, data, len, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||||
throwSQLiteError(stmt.db, "binding argument");
|
SQLiteError::throw_(stmt.db, "binding argument");
|
||||||
} else
|
} else
|
||||||
bind();
|
bind();
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -129,7 +139,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
|
||||||
{
|
{
|
||||||
if (notNull) {
|
if (notNull) {
|
||||||
if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
|
if (sqlite3_bind_int64(stmt, curArg++, value) != SQLITE_OK)
|
||||||
throwSQLiteError(stmt.db, "binding argument");
|
SQLiteError::throw_(stmt.db, "binding argument");
|
||||||
} else
|
} else
|
||||||
bind();
|
bind();
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -138,7 +148,7 @@ SQLiteStmt::Use & SQLiteStmt::Use::operator () (int64_t value, bool notNull)
|
||||||
SQLiteStmt::Use & SQLiteStmt::Use::bind()
|
SQLiteStmt::Use & SQLiteStmt::Use::bind()
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
|
if (sqlite3_bind_null(stmt, curArg++) != SQLITE_OK)
|
||||||
throwSQLiteError(stmt.db, "binding argument");
|
SQLiteError::throw_(stmt.db, "binding argument");
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,14 +162,14 @@ void SQLiteStmt::Use::exec()
|
||||||
int r = step();
|
int r = step();
|
||||||
assert(r != SQLITE_ROW);
|
assert(r != SQLITE_ROW);
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
throwSQLiteError(stmt.db, fmt("executing SQLite statement '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
SQLiteError::throw_(stmt.db, fmt("executing SQLite statement '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SQLiteStmt::Use::next()
|
bool SQLiteStmt::Use::next()
|
||||||
{
|
{
|
||||||
int r = step();
|
int r = step();
|
||||||
if (r != SQLITE_DONE && r != SQLITE_ROW)
|
if (r != SQLITE_DONE && r != SQLITE_ROW)
|
||||||
throwSQLiteError(stmt.db, fmt("executing SQLite query '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
SQLiteError::throw_(stmt.db, fmt("executing SQLite query '%s'", sqlite3_expanded_sql(stmt.stmt)));
|
||||||
return r == SQLITE_ROW;
|
return r == SQLITE_ROW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,14 +195,14 @@ SQLiteTxn::SQLiteTxn(sqlite3 * db)
|
||||||
{
|
{
|
||||||
this->db = db;
|
this->db = db;
|
||||||
if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, "begin;", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "starting transaction");
|
SQLiteError::throw_(db, "starting transaction");
|
||||||
active = true;
|
active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SQLiteTxn::commit()
|
void SQLiteTxn::commit()
|
||||||
{
|
{
|
||||||
if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(db, "commit;", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "committing transaction");
|
SQLiteError::throw_(db, "committing transaction");
|
||||||
active = false;
|
active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +210,7 @@ SQLiteTxn::~SQLiteTxn()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
|
if (active && sqlite3_exec(db, "rollback;", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "aborting transaction");
|
SQLiteError::throw_(db, "aborting transaction");
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,10 +96,30 @@ struct SQLiteTxn
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
MakeError(SQLiteError, Error);
|
struct SQLiteError : Error
|
||||||
MakeError(SQLiteBusy, SQLiteError);
|
{
|
||||||
|
const char *path;
|
||||||
|
int errNo, extendedErrNo;
|
||||||
|
|
||||||
[[noreturn]] void throwSQLiteError(sqlite3 * db, const FormatOrString & fs);
|
template<typename... Args>
|
||||||
|
[[noreturn]] static void throw_(sqlite3 * db, const std::string & fs, const Args & ... args) {
|
||||||
|
throw_(db, hintfmt(fs, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteError(const char *path, int errNo, int extendedErrNo, hintformat && hf);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
SQLiteError(const char *path, int errNo, int extendedErrNo, const std::string & fs, const Args & ... args)
|
||||||
|
: SQLiteError(path, errNo, extendedErrNo, hintfmt(fs, args...))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
[[noreturn]] static void throw_(sqlite3 * db, hintformat && hf);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
MakeError(SQLiteBusy, SQLiteError);
|
||||||
|
|
||||||
void handleSQLiteBusy(const SQLiteBusy & e);
|
void handleSQLiteBusy(const SQLiteBusy & e);
|
||||||
|
|
||||||
|
|
|
@ -127,11 +127,11 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
if (flag.handler.arity == ArityAny) break;
|
if (flag.handler.arity == ArityAny) break;
|
||||||
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
||||||
}
|
}
|
||||||
if (flag.completer)
|
if (auto prefix = needsCompletion(*pos)) {
|
||||||
if (auto prefix = needsCompletion(*pos)) {
|
anyCompleted = true;
|
||||||
anyCompleted = true;
|
if (flag.completer)
|
||||||
flag.completer(n, *prefix);
|
flag.completer(n, *prefix);
|
||||||
}
|
}
|
||||||
args.push_back(*pos++);
|
args.push_back(*pos++);
|
||||||
}
|
}
|
||||||
if (!anyCompleted)
|
if (!anyCompleted)
|
||||||
|
@ -146,6 +146,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
&& hasPrefix(name, std::string(*prefix, 2)))
|
&& hasPrefix(name, std::string(*prefix, 2)))
|
||||||
completions->add("--" + name, flag->description);
|
completions->add("--" + name, flag->description);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
auto i = longFlags.find(std::string(*pos, 2));
|
auto i = longFlags.find(std::string(*pos, 2));
|
||||||
if (i == longFlags.end()) return false;
|
if (i == longFlags.end()) return false;
|
||||||
|
@ -187,10 +188,12 @@ bool Args::processArgs(const Strings & args, bool finish)
|
||||||
{
|
{
|
||||||
std::vector<std::string> ss;
|
std::vector<std::string> ss;
|
||||||
for (const auto &[n, s] : enumerate(args)) {
|
for (const auto &[n, s] : enumerate(args)) {
|
||||||
ss.push_back(s);
|
if (auto prefix = needsCompletion(s)) {
|
||||||
if (exp.completer)
|
ss.push_back(*prefix);
|
||||||
if (auto prefix = needsCompletion(s))
|
if (exp.completer)
|
||||||
exp.completer(n, *prefix);
|
exp.completer(n, *prefix);
|
||||||
|
} else
|
||||||
|
ss.push_back(s);
|
||||||
}
|
}
|
||||||
exp.handler.fun(ss);
|
exp.handler.fun(ss);
|
||||||
expectedArgs.pop_front();
|
expectedArgs.pop_front();
|
||||||
|
@ -279,21 +282,22 @@ static void _completePath(std::string_view prefix, bool onlyDirs)
|
||||||
{
|
{
|
||||||
completionType = ctFilenames;
|
completionType = ctFilenames;
|
||||||
glob_t globbuf;
|
glob_t globbuf;
|
||||||
int flags = GLOB_NOESCAPE | GLOB_TILDE;
|
int flags = GLOB_NOESCAPE;
|
||||||
#ifdef GLOB_ONLYDIR
|
#ifdef GLOB_ONLYDIR
|
||||||
if (onlyDirs)
|
if (onlyDirs)
|
||||||
flags |= GLOB_ONLYDIR;
|
flags |= GLOB_ONLYDIR;
|
||||||
#endif
|
#endif
|
||||||
if (glob((std::string(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) {
|
// using expandTilde here instead of GLOB_TILDE(_CHECK) so that ~<Tab> expands to /home/user/
|
||||||
|
if (glob((expandTilde(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) {
|
||||||
for (size_t i = 0; i < globbuf.gl_pathc; ++i) {
|
for (size_t i = 0; i < globbuf.gl_pathc; ++i) {
|
||||||
if (onlyDirs) {
|
if (onlyDirs) {
|
||||||
auto st = lstat(globbuf.gl_pathv[i]);
|
auto st = stat(globbuf.gl_pathv[i]);
|
||||||
if (!S_ISDIR(st.st_mode)) continue;
|
if (!S_ISDIR(st.st_mode)) continue;
|
||||||
}
|
}
|
||||||
completions->add(globbuf.gl_pathv[i]);
|
completions->add(globbuf.gl_pathv[i]);
|
||||||
}
|
}
|
||||||
globfree(&globbuf);
|
|
||||||
}
|
}
|
||||||
|
globfree(&globbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void completePath(size_t, std::string_view prefix)
|
void completePath(size_t, std::string_view prefix)
|
||||||
|
@ -322,11 +326,6 @@ MultiCommand::MultiCommand(const Commands & commands_)
|
||||||
.optional = true,
|
.optional = true,
|
||||||
.handler = {[=](std::string s) {
|
.handler = {[=](std::string s) {
|
||||||
assert(!command);
|
assert(!command);
|
||||||
if (auto prefix = needsCompletion(s)) {
|
|
||||||
for (auto & [name, command] : commands)
|
|
||||||
if (hasPrefix(name, *prefix))
|
|
||||||
completions->add(name);
|
|
||||||
}
|
|
||||||
auto i = commands.find(s);
|
auto i = commands.find(s);
|
||||||
if (i == commands.end()) {
|
if (i == commands.end()) {
|
||||||
std::set<std::string> commandNames;
|
std::set<std::string> commandNames;
|
||||||
|
@ -337,6 +336,11 @@ MultiCommand::MultiCommand(const Commands & commands_)
|
||||||
}
|
}
|
||||||
command = {s, i->second()};
|
command = {s, i->second()};
|
||||||
command->second->parent = this;
|
command->second->parent = this;
|
||||||
|
}},
|
||||||
|
.completer = {[&](size_t, std::string_view prefix) {
|
||||||
|
for (auto & [name, command] : commands)
|
||||||
|
if (hasPrefix(name, prefix))
|
||||||
|
completions->add(name);
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
68
src/libutil/chunked-vector.hh
Normal file
68
src/libutil/chunked-vector.hh
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <vector>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* Provides an indexable container like vector<> with memory overhead
|
||||||
|
guarantees like list<> by allocating storage in chunks of ChunkSize
|
||||||
|
elements instead of using a contiguous memory allocation like vector<>
|
||||||
|
does. Not using a single vector that is resized reduces memory overhead
|
||||||
|
on large data sets by on average (growth factor)/2, mostly
|
||||||
|
eliminates copies within the vector during resizing, and provides stable
|
||||||
|
references to its elements. */
|
||||||
|
template<typename T, size_t ChunkSize>
|
||||||
|
class ChunkedVector {
|
||||||
|
private:
|
||||||
|
uint32_t size_ = 0;
|
||||||
|
std::vector<std::vector<T>> chunks;
|
||||||
|
|
||||||
|
/* keep this out of the ::add hot path */
|
||||||
|
[[gnu::noinline]]
|
||||||
|
auto & addChunk()
|
||||||
|
{
|
||||||
|
if (size_ >= std::numeric_limits<uint32_t>::max() - ChunkSize)
|
||||||
|
abort();
|
||||||
|
chunks.emplace_back();
|
||||||
|
chunks.back().reserve(ChunkSize);
|
||||||
|
return chunks.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChunkedVector(uint32_t reserve)
|
||||||
|
{
|
||||||
|
chunks.reserve(reserve);
|
||||||
|
addChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t size() const { return size_; }
|
||||||
|
|
||||||
|
std::pair<T &, uint32_t> add(T value)
|
||||||
|
{
|
||||||
|
const auto idx = size_++;
|
||||||
|
auto & chunk = [&] () -> auto & {
|
||||||
|
if (auto & back = chunks.back(); back.size() < ChunkSize)
|
||||||
|
return back;
|
||||||
|
return addChunk();
|
||||||
|
}();
|
||||||
|
auto & result = chunk.emplace_back(std::move(value));
|
||||||
|
return {result, idx};
|
||||||
|
}
|
||||||
|
|
||||||
|
const T & operator[](uint32_t idx) const
|
||||||
|
{
|
||||||
|
return chunks[idx / ChunkSize][idx % ChunkSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Fn>
|
||||||
|
void forEach(Fn fn) const
|
||||||
|
{
|
||||||
|
for (const auto & c : chunks)
|
||||||
|
for (const auto & e : c)
|
||||||
|
fn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -87,11 +87,7 @@ struct ErrPos {
|
||||||
origin = pos.origin;
|
origin = pos.origin;
|
||||||
line = pos.line;
|
line = pos.line;
|
||||||
column = pos.column;
|
column = pos.column;
|
||||||
// is file symbol null?
|
file = pos.file;
|
||||||
if (pos.file.set())
|
|
||||||
file = pos.file;
|
|
||||||
else
|
|
||||||
file = "";
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,4 +58,18 @@ std::ostream & operator <<(std::ostream & str, const ExperimentalFeature & featu
|
||||||
return str << showExperimentalFeature(feature);
|
return str << showExperimentalFeature(feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void to_json(nlohmann::json& j, const ExperimentalFeature& feature) {
|
||||||
|
j = showExperimentalFeature(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
void from_json(const nlohmann::json& j, ExperimentalFeature& feature) {
|
||||||
|
const std::string input = j;
|
||||||
|
const auto parsed = parseExperimentalFeature(input);
|
||||||
|
|
||||||
|
if (parsed.has_value())
|
||||||
|
feature = *parsed;
|
||||||
|
else
|
||||||
|
throw Error("Unknown experimental feature '%s' in JSON input", input);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,4 +51,11 @@ public:
|
||||||
MissingExperimentalFeature(ExperimentalFeature);
|
MissingExperimentalFeature(ExperimentalFeature);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semi-magic conversion to and from json.
|
||||||
|
* See the nlohmann/json readme for more details.
|
||||||
|
*/
|
||||||
|
void to_json(nlohmann::json&, const ExperimentalFeature&);
|
||||||
|
void from_json(const nlohmann::json&, ExperimentalFeature&);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <regex>
|
|
||||||
#include "ansicolor.hh"
|
#include "ansicolor.hh"
|
||||||
|
|
||||||
|
|
||||||
|
@ -155,15 +154,4 @@ inline hintformat hintfmt(std::string plain_string)
|
||||||
return hintfmt("%s", normaltxt(plain_string));
|
return hintfmt("%s", normaltxt(plain_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Highlight all the given matches in the given string `s` by wrapping
|
|
||||||
them between `prefix` and `postfix`.
|
|
||||||
|
|
||||||
If some matches overlap, then their union will be wrapped rather
|
|
||||||
than the individual matches. */
|
|
||||||
std::string hiliteMatches(
|
|
||||||
std::string_view s,
|
|
||||||
std::vector<std::smatch> matches,
|
|
||||||
std::string_view prefix,
|
|
||||||
std::string_view postfix);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#include "fmt.hh"
|
#include "hilite.hh"
|
||||||
|
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
20
src/libutil/hilite.hh
Normal file
20
src/libutil/hilite.hh
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* Highlight all the given matches in the given string `s` by wrapping
|
||||||
|
them between `prefix` and `postfix`.
|
||||||
|
|
||||||
|
If some matches overlap, then their union will be wrapped rather
|
||||||
|
than the individual matches. */
|
||||||
|
std::string hiliteMatches(
|
||||||
|
std::string_view s,
|
||||||
|
std::vector<std::smatch> matches,
|
||||||
|
std::string_view prefix,
|
||||||
|
std::string_view postfix);
|
||||||
|
|
||||||
|
}
|
|
@ -99,47 +99,4 @@ make_ref(Args&&... args)
|
||||||
return ref<T>(p);
|
return ref<T>(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* A non-nullable pointer.
|
|
||||||
This is similar to a C++ "& reference", but mutable.
|
|
||||||
This is similar to ref<T> but backed by a regular pointer instead of a smart pointer.
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
class ptr {
|
|
||||||
private:
|
|
||||||
T * p;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ptr<T>(const ptr<T> & r)
|
|
||||||
: p(r.p)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
explicit ptr<T>(T * p)
|
|
||||||
: p(p)
|
|
||||||
{
|
|
||||||
if (!p)
|
|
||||||
throw std::invalid_argument("null pointer cast to ptr");
|
|
||||||
}
|
|
||||||
|
|
||||||
T* operator ->() const
|
|
||||||
{
|
|
||||||
return &*p;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator *() const
|
|
||||||
{
|
|
||||||
return *p;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator == (const ptr<T> & other) const
|
|
||||||
{
|
|
||||||
return p == other.p;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator != (const ptr<T> & other) const
|
|
||||||
{
|
|
||||||
return p != other.p;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
54
src/libutil/tests/chunked-vector.cc
Normal file
54
src/libutil/tests/chunked-vector.cc
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include "chunked-vector.hh"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
TEST(ChunkedVector, InitEmpty) {
|
||||||
|
auto v = ChunkedVector<int, 2>(100);
|
||||||
|
ASSERT_EQ(v.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChunkedVector, GrowsCorrectly) {
|
||||||
|
auto v = ChunkedVector<int, 2>(100);
|
||||||
|
for (auto i = 1; i < 20; i++) {
|
||||||
|
v.add(i);
|
||||||
|
ASSERT_EQ(v.size(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChunkedVector, AddAndGet) {
|
||||||
|
auto v = ChunkedVector<int, 2>(100);
|
||||||
|
for (auto i = 1; i < 20; i++) {
|
||||||
|
auto [i2, idx] = v.add(i);
|
||||||
|
auto & i3 = v[idx];
|
||||||
|
ASSERT_EQ(i, i2);
|
||||||
|
ASSERT_EQ(&i2, &i3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChunkedVector, ForEach) {
|
||||||
|
auto v = ChunkedVector<int, 2>(100);
|
||||||
|
for (auto i = 1; i < 20; i++) {
|
||||||
|
v.add(i);
|
||||||
|
}
|
||||||
|
int count = 0;
|
||||||
|
v.forEach([&count](int elt) {
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
ASSERT_EQ(count, v.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ChunkedVector, OverflowOK) {
|
||||||
|
// Similar to the AddAndGet, but intentionnally use a small
|
||||||
|
// initial ChunkedVector to force it to overflow
|
||||||
|
auto v = ChunkedVector<int, 2>(2);
|
||||||
|
for (auto i = 1; i < 20; i++) {
|
||||||
|
auto [i2, idx] = v.add(i);
|
||||||
|
auto & i3 = v[idx];
|
||||||
|
ASSERT_EQ(i, i2);
|
||||||
|
ASSERT_EQ(&i2, &i3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#include "fmt.hh"
|
#include "hilite.hh"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
/* ----------- tests for fmt.hh -------------------------------------------------*/
|
/* ----------- tests for fmt.hh -------------------------------------------------*/
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
|
@ -198,6 +198,17 @@ std::string_view baseNameOf(std::string_view path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string expandTilde(std::string_view path)
|
||||||
|
{
|
||||||
|
// TODO: expand ~user ?
|
||||||
|
auto tilde = path.substr(0, 2);
|
||||||
|
if (tilde == "~/" || tilde == "~")
|
||||||
|
return getHome() + std::string(path.substr(1));
|
||||||
|
else
|
||||||
|
return std::string(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool isInDir(std::string_view path, std::string_view dir)
|
bool isInDir(std::string_view path, std::string_view dir)
|
||||||
{
|
{
|
||||||
return path.substr(0, 1) == "/"
|
return path.substr(0, 1) == "/"
|
||||||
|
@ -213,6 +224,15 @@ bool isDirOrInDir(std::string_view path, std::string_view dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct stat stat(const Path & path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
if (stat(path.c_str(), &st))
|
||||||
|
throw SysError("getting status of '%1%'", path);
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct stat lstat(const Path & path)
|
struct stat lstat(const Path & path)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -1062,7 +1082,7 @@ std::string runProgram(Path program, bool searchPath, const Strings & args,
|
||||||
auto res = runProgram(RunOptions {.program = program, .searchPath = searchPath, .args = args, .input = input});
|
auto res = runProgram(RunOptions {.program = program, .searchPath = searchPath, .args = args, .input = input});
|
||||||
|
|
||||||
if (!statusOk(res.first))
|
if (!statusOk(res.first))
|
||||||
throw ExecError(res.first, fmt("program '%1%' %2%", program, statusToString(res.first)));
|
throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));
|
||||||
|
|
||||||
return res.second;
|
return res.second;
|
||||||
}
|
}
|
||||||
|
@ -1190,7 +1210,7 @@ void runProgram2(const RunOptions & options)
|
||||||
if (source) promise.get_future().get();
|
if (source) promise.get_future().get();
|
||||||
|
|
||||||
if (status)
|
if (status)
|
||||||
throw ExecError(status, fmt("program '%1%' %2%", options.program, statusToString(status)));
|
throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,9 @@ Path dirOf(const PathView path);
|
||||||
following the final `/' (trailing slashes are removed). */
|
following the final `/' (trailing slashes are removed). */
|
||||||
std::string_view baseNameOf(std::string_view path);
|
std::string_view baseNameOf(std::string_view path);
|
||||||
|
|
||||||
|
/* Perform tilde expansion on a path. */
|
||||||
|
std::string expandTilde(std::string_view path);
|
||||||
|
|
||||||
/* Check whether 'path' is a descendant of 'dir'. Both paths must be
|
/* Check whether 'path' is a descendant of 'dir'. Both paths must be
|
||||||
canonicalized. */
|
canonicalized. */
|
||||||
bool isInDir(std::string_view path, std::string_view dir);
|
bool isInDir(std::string_view path, std::string_view dir);
|
||||||
|
@ -77,6 +80,7 @@ bool isInDir(std::string_view path, std::string_view dir);
|
||||||
bool isDirOrInDir(std::string_view path, std::string_view dir);
|
bool isDirOrInDir(std::string_view path, std::string_view dir);
|
||||||
|
|
||||||
/* Get status of `path'. */
|
/* Get status of `path'. */
|
||||||
|
struct stat stat(const Path & path);
|
||||||
struct stat lstat(const Path & path);
|
struct stat lstat(const Path & path);
|
||||||
|
|
||||||
/* Return true iff the given path exists. */
|
/* Return true iff the given path exists. */
|
||||||
|
|
|
@ -1241,7 +1241,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
Attr & a(*attrs.find(i.name));
|
Attr & a(*attrs.find(i.name));
|
||||||
if(a.value->type() != nString) continue;
|
if(a.value->type() != nString) continue;
|
||||||
XMLAttrs attrs3;
|
XMLAttrs attrs3;
|
||||||
attrs3["type"] = i.name;
|
attrs3["type"] = globals.state->symbols[i.name];
|
||||||
attrs3["value"] = a.value->string.s;
|
attrs3["value"] = a.value->string.s;
|
||||||
xml.writeEmptyElement("string", attrs3);
|
xml.writeEmptyElement("string", attrs3);
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
the store; we need it for future modifications of the
|
the store; we need it for future modifications of the
|
||||||
environment. */
|
environment. */
|
||||||
std::ostringstream str;
|
std::ostringstream str;
|
||||||
manifest.print(str, true);
|
manifest.print(state.symbols, str, true);
|
||||||
auto manifestFile = state.store->addTextToStore("env-manifest.nix",
|
auto manifestFile = state.store->addTextToStore("env-manifest.nix",
|
||||||
str.str(), references);
|
str.str(), references);
|
||||||
|
|
||||||
|
@ -134,9 +134,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); });
|
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.coerceToStorePath(*aDrvPath.pos, *aDrvPath.value, context);
|
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context);
|
||||||
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
|
Attr & aOutPath(*topLevel.attrs->find(state.sOutPath));
|
||||||
auto topLevelOut = state.coerceToStorePath(*aOutPath.pos, *aOutPath.value, context);
|
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context);
|
||||||
|
|
||||||
/* Realise the resulting store expression. */
|
/* Realise the resulting store expression. */
|
||||||
debug("building user environment");
|
debug("building user environment");
|
||||||
|
|
|
@ -31,7 +31,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||||
bool evalOnly, OutputKind output, bool location, Expr * e)
|
bool evalOnly, OutputKind output, bool location, Expr * e)
|
||||||
{
|
{
|
||||||
if (parseOnly) {
|
if (parseOnly) {
|
||||||
std::cout << format("%1%\n") % *e;
|
e->show(state.symbols, std::cout);
|
||||||
|
std::cout << "\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +56,8 @@ void processExpr(EvalState & state, const Strings & attrPaths,
|
||||||
printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context);
|
printValueAsJSON(state, strict, vRes, v.determinePos(noPos), std::cout, context);
|
||||||
else {
|
else {
|
||||||
if (strict) state.forceValueDeep(vRes);
|
if (strict) state.forceValueDeep(vRes);
|
||||||
std::cout << vRes << std::endl;
|
vRes.print(state.symbols, std::cout);
|
||||||
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DrvInfos drvs;
|
DrvInfos drvs;
|
||||||
|
|
|
@ -35,7 +35,7 @@ struct InstallableDerivedPath : Installable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the rewrites that are needed to resolve a string whose context is
|
* Return the rewrites that are needed to resolve a string whose context is
|
||||||
* included in `dependencies`
|
* included in `dependencies`.
|
||||||
*/
|
*/
|
||||||
StringPairs resolveRewrites(Store & store, const BuiltPaths dependencies)
|
StringPairs resolveRewrites(Store & store, const BuiltPaths dependencies)
|
||||||
{
|
{
|
||||||
|
@ -51,7 +51,7 @@ StringPairs resolveRewrites(Store & store, const BuiltPaths dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the given string assuming the given context
|
* Resolve the given string assuming the given context.
|
||||||
*/
|
*/
|
||||||
std::string resolveString(Store & store, const std::string & toResolve, const BuiltPaths dependencies)
|
std::string resolveString(Store & store, const std::string & toResolve, const BuiltPaths dependencies)
|
||||||
{
|
{
|
||||||
|
@ -61,14 +61,18 @@ std::string resolveString(Store & store, const std::string & toResolve, const Bu
|
||||||
|
|
||||||
UnresolvedApp Installable::toApp(EvalState & state)
|
UnresolvedApp Installable::toApp(EvalState & state)
|
||||||
{
|
{
|
||||||
auto [cursor, attrPath] = getCursor(state);
|
auto cursor = getCursor(state);
|
||||||
|
auto attrPath = cursor->getAttrPath();
|
||||||
|
|
||||||
auto type = cursor->getAttr("type")->getString();
|
auto type = cursor->getAttr("type")->getString();
|
||||||
|
|
||||||
|
std::string expected = !attrPath.empty() && state.symbols[attrPath[0]] == "apps" ? "app" : "derivation";
|
||||||
|
if (type != expected)
|
||||||
|
throw Error("attribute '%s' should have type '%s'", cursor->getAttrPathStr(), expected);
|
||||||
|
|
||||||
if (type == "app") {
|
if (type == "app") {
|
||||||
auto [program, context] = cursor->getAttr("program")->getStringWithContext();
|
auto [program, context] = cursor->getAttr("program")->getStringWithContext();
|
||||||
|
|
||||||
|
|
||||||
std::vector<StorePathWithOutputs> context2;
|
std::vector<StorePathWithOutputs> context2;
|
||||||
for (auto & [path, name] : context)
|
for (auto & [path, name] : context)
|
||||||
context2.push_back({path, {name}});
|
context2.push_back({path, {name}});
|
||||||
|
@ -101,7 +105,7 @@ UnresolvedApp Installable::toApp(EvalState & state)
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
throw Error("attribute '%s' has unsupported type '%s'", attrPath, type);
|
throw Error("attribute '%s' has unsupported type '%s'", cursor->getAttrPathStr(), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: move to libcmd
|
// FIXME: move to libcmd
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "local-fs-store.hh"
|
#include "local-fs-store.hh"
|
||||||
|
#include "progress-bar.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ using namespace nix;
|
||||||
struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
||||||
{
|
{
|
||||||
Path outLink = "result";
|
Path outLink = "result";
|
||||||
|
bool printOutputPaths = false;
|
||||||
BuildMode buildMode = bmNormal;
|
BuildMode buildMode = bmNormal;
|
||||||
|
|
||||||
CmdBuild()
|
CmdBuild()
|
||||||
|
@ -31,6 +33,12 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
||||||
.handler = {&outLink, Path("")},
|
.handler = {&outLink, Path("")},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addFlag({
|
||||||
|
.longName = "print-out-paths",
|
||||||
|
.description = "Print the resulting output paths",
|
||||||
|
.handler = {&printOutputPaths, true},
|
||||||
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "rebuild",
|
.longName = "rebuild",
|
||||||
.description = "Rebuild an already built package and compare the result to the existing store paths.",
|
.description = "Rebuild an already built package and compare the result to the existing store paths.",
|
||||||
|
@ -93,6 +101,22 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
||||||
}, buildable.raw());
|
}, buildable.raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (printOutputPaths) {
|
||||||
|
stopProgressBar();
|
||||||
|
for (auto & buildable : buildables) {
|
||||||
|
std::visit(overloaded {
|
||||||
|
[&](const BuiltPath::Opaque & bo) {
|
||||||
|
std::cout << store->printStorePath(bo.path) << std::endl;
|
||||||
|
},
|
||||||
|
[&](const BuiltPath::Built & bfd) {
|
||||||
|
for (auto & output : bfd.outputs) {
|
||||||
|
std::cout << store->printStorePath(output.second) << std::endl;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, buildable.raw());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateProfile(buildables);
|
updateProfile(buildables);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,6 +25,13 @@ R""(
|
||||||
lrwxrwxrwx 1 … result-1 -> /nix/store/rkfrm0z6x6jmi7d3gsmma4j53h15mg33-cowsay-3.03+dfsg2
|
lrwxrwxrwx 1 … result-1 -> /nix/store/rkfrm0z6x6jmi7d3gsmma4j53h15mg33-cowsay-3.03+dfsg2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Build GNU Hello and print the resulting store path.
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix build nixpkgs#hello --print-out-paths
|
||||||
|
/nix/store/v5sv61sszx301i0x6xysaqzla09nksnd-hello-2.10
|
||||||
|
```
|
||||||
|
|
||||||
* Build a specific output:
|
* Build a specific output:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
|
|
|
@ -97,21 +97,23 @@ struct CmdBundle : InstallableCommand
|
||||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||||
|
|
||||||
PathSet context2;
|
PathSet context2;
|
||||||
auto drvPath = evalState->coerceToStorePath(*attr1->pos, *attr1->value, context2);
|
auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2);
|
||||||
|
|
||||||
auto attr2 = vRes->attrs->get(evalState->sOutPath);
|
auto attr2 = vRes->attrs->get(evalState->sOutPath);
|
||||||
if (!attr2)
|
if (!attr2)
|
||||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||||
|
|
||||||
auto outPath = evalState->coerceToStorePath(*attr2->pos, *attr2->value, context2);
|
auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2);
|
||||||
|
|
||||||
store->buildPaths({ DerivedPath::Built { drvPath } });
|
store->buildPaths({ DerivedPath::Built { drvPath } });
|
||||||
|
|
||||||
auto outPathS = store->printStorePath(outPath);
|
auto outPathS = store->printStorePath(outPath);
|
||||||
|
|
||||||
if (!outLink) {
|
if (!outLink) {
|
||||||
auto &attr = vRes->attrs->need(evalState->sName);
|
auto * attr = vRes->attrs->get(evalState->sName);
|
||||||
outLink = evalState->forceStringNoCtx(*attr.value,*attr.pos);
|
if (!attr)
|
||||||
|
throw Error("attribute 'name' missing");
|
||||||
|
outLink = evalState->forceStringNoCtx(*attr->value, attr->pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: will crash if not a localFSStore?
|
// TODO: will crash if not a localFSStore?
|
||||||
|
|
|
@ -28,19 +28,19 @@ struct CmdEdit : InstallableCommand
|
||||||
{
|
{
|
||||||
auto state = getEvalState();
|
auto state = getEvalState();
|
||||||
|
|
||||||
auto [v, pos] = installable->toValue(*state);
|
const auto [file, line] = [&] {
|
||||||
|
auto [v, pos] = installable->toValue(*state);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pos = findPackageFilename(*state, *v, installable->what());
|
return findPackageFilename(*state, *v, installable->what());
|
||||||
} catch (NoPositionInfo &) {
|
} catch (NoPositionInfo &) {
|
||||||
}
|
throw Error("cannot find position information for '%s", installable->what());
|
||||||
|
}
|
||||||
if (pos == noPos)
|
}();
|
||||||
throw Error("cannot find position information for '%s", installable->what());
|
|
||||||
|
|
||||||
stopProgressBar();
|
stopProgressBar();
|
||||||
|
|
||||||
auto args = editorFor(pos);
|
auto args = editorFor(file, line);
|
||||||
|
|
||||||
restoreProcessContext();
|
restoreProcessContext();
|
||||||
|
|
||||||
|
|
|
@ -77,9 +77,9 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
if (pathExists(*writeTo))
|
if (pathExists(*writeTo))
|
||||||
throw Error("path '%s' already exists", *writeTo);
|
throw Error("path '%s' already exists", *writeTo);
|
||||||
|
|
||||||
std::function<void(Value & v, const Pos & pos, const Path & path)> recurse;
|
std::function<void(Value & v, const PosIdx pos, const Path & path)> recurse;
|
||||||
|
|
||||||
recurse = [&](Value & v, const Pos & pos, const Path & path)
|
recurse = [&](Value & v, const PosIdx pos, const Path & path)
|
||||||
{
|
{
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (v.type() == nString)
|
if (v.type() == nString)
|
||||||
|
@ -88,18 +88,22 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
else if (v.type() == nAttrs) {
|
else if (v.type() == nAttrs) {
|
||||||
if (mkdir(path.c_str(), 0777) == -1)
|
if (mkdir(path.c_str(), 0777) == -1)
|
||||||
throw SysError("creating directory '%s'", path);
|
throw SysError("creating directory '%s'", path);
|
||||||
for (auto & attr : *v.attrs)
|
for (auto & attr : *v.attrs) {
|
||||||
|
std::string_view name = state->symbols[attr.name];
|
||||||
try {
|
try {
|
||||||
if (attr.name == "." || attr.name == "..")
|
if (name == "." || name == "..")
|
||||||
throw Error("invalid file name '%s'", attr.name);
|
throw Error("invalid file name '%s'", name);
|
||||||
recurse(*attr.value, *attr.pos, path + "/" + std::string(attr.name));
|
recurse(*attr.value, attr.pos, concatStrings(path, "/", name));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(*attr.pos, hintfmt("while evaluating the attribute '%s'", attr.name));
|
e.addTrace(
|
||||||
|
state->positions[attr.pos],
|
||||||
|
hintfmt("while evaluating the attribute '%s'", name));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw TypeError("value at '%s' is not a string or an attribute set", pos);
|
throw TypeError("value at '%s' is not a string or an attribute set", state->positions[pos]);
|
||||||
};
|
};
|
||||||
|
|
||||||
recurse(*v, pos, *writeTo);
|
recurse(*v, pos, *writeTo);
|
||||||
|
@ -117,7 +121,7 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
|
|
||||||
else {
|
else {
|
||||||
state->forceValueDeep(*v);
|
state->forceValueDeep(*v);
|
||||||
logger->cout("%s", *v);
|
logger->cout("%s", printValue(*state, *v));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
253
src/nix/flake.cc
253
src/nix/flake.cc
|
@ -123,7 +123,7 @@ 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 PosIdx pos)> callback)
|
||||||
{
|
{
|
||||||
auto pos = vFlake.determinePos(noPos);
|
auto pos = vFlake.determinePos(noPos);
|
||||||
state.forceAttrs(vFlake, pos);
|
state.forceAttrs(vFlake, pos);
|
||||||
|
@ -139,11 +139,11 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
|
||||||
else. This way we can disable IFD for hydraJobs and then enable
|
else. This way we can disable IFD for hydraJobs and then enable
|
||||||
it for other outputs. */
|
it for other outputs. */
|
||||||
if (auto attr = aOutputs->value->attrs->get(sHydraJobs))
|
if (auto attr = aOutputs->value->attrs->get(sHydraJobs))
|
||||||
callback(attr->name, *attr->value, *attr->pos);
|
callback(state.symbols[attr->name], *attr->value, attr->pos);
|
||||||
|
|
||||||
for (auto & attr : *aOutputs->value->attrs) {
|
for (auto & attr : *aOutputs->value->attrs) {
|
||||||
if (attr.name != sHydraJobs)
|
if (attr.name != sHydraJobs)
|
||||||
callback(attr.name, *attr.value, *attr.pos);
|
callback(state.symbols[attr.name], *attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,14 +254,6 @@ struct CmdFlakeInfo : CmdFlakeMetadata
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool argHasName(std::string_view arg, std::string_view expected)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
arg == expected
|
|
||||||
|| arg == "_"
|
|
||||||
|| (hasPrefix(arg, "_") && arg.substr(1) == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CmdFlakeCheck : FlakeCommand
|
struct CmdFlakeCheck : FlakeCommand
|
||||||
{
|
{
|
||||||
bool build = true;
|
bool build = true;
|
||||||
|
@ -315,13 +307,25 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
|
|
||||||
// FIXME: rewrite to use EvalCache.
|
// FIXME: rewrite to use EvalCache.
|
||||||
|
|
||||||
auto checkSystemName = [&](const std::string & system, const Pos & pos) {
|
auto resolve = [&] (PosIdx p) {
|
||||||
// FIXME: what's the format of "system"?
|
return state->positions[p];
|
||||||
if (system.find('-') == std::string::npos)
|
|
||||||
reportError(Error("'%s' is not a valid system type, at %s", system, pos));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) -> std::optional<StorePath> {
|
auto argHasName = [&] (Symbol arg, std::string_view expected) {
|
||||||
|
std::string_view name = state->symbols[arg];
|
||||||
|
return
|
||||||
|
name == expected
|
||||||
|
|| name == "_"
|
||||||
|
|| (hasPrefix(name, "_") && name.substr(1) == expected);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto checkSystemName = [&](const std::string & system, const PosIdx pos) {
|
||||||
|
// FIXME: what's the format of "system"?
|
||||||
|
if (system.find('-') == std::string::npos)
|
||||||
|
reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos)));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto checkDerivation = [&](const std::string & attrPath, Value & v, const PosIdx pos) -> std::optional<StorePath> {
|
||||||
try {
|
try {
|
||||||
auto drvInfo = getDerivation(*state, v, false);
|
auto drvInfo = getDerivation(*state, v, false);
|
||||||
if (!drvInfo)
|
if (!drvInfo)
|
||||||
|
@ -329,7 +333,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
// FIXME: check meta attributes
|
// FIXME: check meta attributes
|
||||||
return drvInfo->queryDrvPath();
|
return drvInfo->queryDrvPath();
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the derivation '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the derivation '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -337,7 +341,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
|
|
||||||
std::vector<DerivedPath> drvPaths;
|
std::vector<DerivedPath> drvPaths;
|
||||||
|
|
||||||
auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkApp = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
#if 0
|
#if 0
|
||||||
// FIXME
|
// FIXME
|
||||||
|
@ -348,12 +352,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the app definition '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the app definition '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkOverlay = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (!v.isLambda()
|
if (!v.isLambda()
|
||||||
|
@ -368,12 +372,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
// FIXME: if we have a 'nixpkgs' input, use it to
|
// FIXME: if we have a 'nixpkgs' input, use it to
|
||||||
// evaluate the overlay.
|
// evaluate the overlay.
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the overlay '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the overlay '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkModule = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkModule = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
state->forceValue(v, pos);
|
state->forceValue(v, pos);
|
||||||
if (v.isLambda()) {
|
if (v.isLambda()) {
|
||||||
|
@ -382,9 +386,11 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
} else if (v.type() == nAttrs) {
|
} else if (v.type() == nAttrs) {
|
||||||
for (auto & attr : *v.attrs)
|
for (auto & attr : *v.attrs)
|
||||||
try {
|
try {
|
||||||
state->forceValue(*attr.value, *attr.pos);
|
state->forceValue(*attr.value, attr.pos);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(*attr.pos, hintfmt("while evaluating the option '%s'", attr.name));
|
e.addTrace(
|
||||||
|
state->positions[attr.pos],
|
||||||
|
hintfmt("while evaluating the option '%s'", state->symbols[attr.name]));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@ -392,14 +398,14 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
// FIXME: if we have a 'nixpkgs' input, use it to
|
// FIXME: if we have a 'nixpkgs' input, use it to
|
||||||
// check the module.
|
// check the module.
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the NixOS module '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the NixOS module '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<void(const std::string & attrPath, Value & v, const Pos & pos)> checkHydraJobs;
|
std::function<void(const std::string & attrPath, Value & v, const PosIdx pos)> checkHydraJobs;
|
||||||
|
|
||||||
checkHydraJobs = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
state->forceAttrs(v, pos);
|
state->forceAttrs(v, pos);
|
||||||
|
|
||||||
|
@ -407,23 +413,23 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
throw Error("jobset should not be a derivation at top-level");
|
throw Error("jobset should not be a derivation at top-level");
|
||||||
|
|
||||||
for (auto & attr : *v.attrs) {
|
for (auto & attr : *v.attrs) {
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
auto attrPath2 = attrPath + "." + (std::string) attr.name;
|
auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]);
|
||||||
if (state->isDerivation(*attr.value)) {
|
if (state->isDerivation(*attr.value)) {
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("checking Hydra job '%s'", attrPath2));
|
fmt("checking Hydra job '%s'", attrPath2));
|
||||||
checkDerivation(attrPath2, *attr.value, *attr.pos);
|
checkDerivation(attrPath2, *attr.value, attr.pos);
|
||||||
} else
|
} else
|
||||||
checkHydraJobs(attrPath2, *attr.value, *attr.pos);
|
checkHydraJobs(attrPath2, *attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the Hydra jobset '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the Hydra jobset '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("checking NixOS configuration '%s'", attrPath));
|
fmt("checking NixOS configuration '%s'", attrPath));
|
||||||
|
@ -433,12 +439,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
if (!state->isDerivation(*vToplevel))
|
if (!state->isDerivation(*vToplevel))
|
||||||
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
|
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the NixOS configuration '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the NixOS configuration '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkTemplate = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkTemplate = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("checking template '%s'", attrPath));
|
fmt("checking template '%s'", attrPath));
|
||||||
|
@ -448,7 +454,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
if (auto attr = v.attrs->get(state->symbols.create("path"))) {
|
if (auto attr = v.attrs->get(state->symbols.create("path"))) {
|
||||||
if (attr->name == state->symbols.create("path")) {
|
if (attr->name == state->symbols.create("path")) {
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto path = state->coerceToPath(*attr->pos, *attr->value, context);
|
auto path = state->coerceToPath(attr->pos, *attr->value, context);
|
||||||
if (!store->isInStore(path))
|
if (!store->isInStore(path))
|
||||||
throw Error("template '%s' has a bad 'path' attribute");
|
throw Error("template '%s' has a bad 'path' attribute");
|
||||||
// TODO: recursively check the flake in 'path'.
|
// TODO: recursively check the flake in 'path'.
|
||||||
|
@ -457,29 +463,29 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
throw Error("template '%s' lacks attribute 'path'", attrPath);
|
throw Error("template '%s' lacks attribute 'path'", attrPath);
|
||||||
|
|
||||||
if (auto attr = v.attrs->get(state->symbols.create("description")))
|
if (auto attr = v.attrs->get(state->symbols.create("description")))
|
||||||
state->forceStringNoCtx(*attr->value, *attr->pos);
|
state->forceStringNoCtx(*attr->value, attr->pos);
|
||||||
else
|
else
|
||||||
throw Error("template '%s' lacks attribute 'description'", attrPath);
|
throw Error("template '%s' lacks attribute 'description'", attrPath);
|
||||||
|
|
||||||
for (auto & attr : *v.attrs) {
|
for (auto & attr : *v.attrs) {
|
||||||
std::string name(attr.name);
|
std::string_view name(state->symbols[attr.name]);
|
||||||
if (name != "path" && name != "description" && name != "welcomeText")
|
if (name != "path" && name != "description" && name != "welcomeText")
|
||||||
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
|
throw Error("template '%s' has unsupported attribute '%s'", attrPath, name);
|
||||||
}
|
}
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto checkBundler = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
auto checkBundler = [&](const std::string & attrPath, Value & v, const PosIdx pos) {
|
||||||
try {
|
try {
|
||||||
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");
|
||||||
// TODO: check types of inputs/outputs?
|
// TODO: check types of inputs/outputs?
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking the template '%s'", attrPath));
|
e.addTrace(resolve(pos), hintfmt("while checking the template '%s'", attrPath));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -492,7 +498,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
|
|
||||||
enumerateOutputs(*state,
|
enumerateOutputs(*state,
|
||||||
*vFlake,
|
*vFlake,
|
||||||
[&](const std::string & name, Value & vOutput, const Pos & pos) {
|
[&](const std::string & name, Value & vOutput, const PosIdx pos) {
|
||||||
Activity act(*logger, lvlChatty, actUnknown,
|
Activity act(*logger, lvlChatty, actUnknown,
|
||||||
fmt("checking flake output '%s'", name));
|
fmt("checking flake output '%s'", name));
|
||||||
|
|
||||||
|
@ -516,66 +522,82 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
if (name == "checks") {
|
if (name == "checks") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
for (auto & attr2 : *attr.value->attrs) {
|
for (auto & attr2 : *attr.value->attrs) {
|
||||||
auto drvPath = checkDerivation(
|
auto drvPath = checkDerivation(
|
||||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
*attr2.value, *attr2.pos);
|
*attr2.value, attr2.pos);
|
||||||
if (drvPath && (std::string) attr.name == settings.thisSystem.get())
|
if (drvPath && attr_name == settings.thisSystem.get())
|
||||||
drvPaths.push_back(DerivedPath::Built{*drvPath});
|
drvPaths.push_back(DerivedPath::Built{*drvPath});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (name == "formatter") {
|
||||||
|
state->forceAttrs(vOutput, pos);
|
||||||
|
for (auto & attr : *vOutput.attrs) {
|
||||||
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
checkApp(
|
||||||
|
fmt("%s.%s", name, attr_name),
|
||||||
|
*attr.value, attr.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else if (name == "packages" || name == "devShells") {
|
else if (name == "packages" || name == "devShells") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
for (auto & attr2 : *attr.value->attrs)
|
for (auto & attr2 : *attr.value->attrs)
|
||||||
checkDerivation(
|
checkDerivation(
|
||||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
*attr2.value, *attr2.pos);
|
*attr2.value, attr2.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "apps") {
|
else if (name == "apps") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
for (auto & attr2 : *attr.value->attrs)
|
for (auto & attr2 : *attr.value->attrs)
|
||||||
checkApp(
|
checkApp(
|
||||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
*attr2.value, *attr2.pos);
|
*attr2.value, attr2.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultPackage" || name == "devShell") {
|
else if (name == "defaultPackage" || name == "devShell") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
|
checkSystemName(attr_name, attr.pos);
|
||||||
checkDerivation(
|
checkDerivation(
|
||||||
fmt("%s.%s", name, attr.name),
|
fmt("%s.%s", name, attr_name),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultApp") {
|
else if (name == "defaultApp") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
|
checkSystemName(attr_name, attr.pos);
|
||||||
checkApp(
|
checkApp(
|
||||||
fmt("%s.%s", name, attr.name),
|
fmt("%s.%s", name, attr_name),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "legacyPackages") {
|
else if (name == "legacyPackages") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
checkSystemName(state->symbols[attr.name], attr.pos);
|
||||||
// FIXME: do getDerivations?
|
// FIXME: do getDerivations?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -586,8 +608,8 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
else if (name == "overlays") {
|
else if (name == "overlays") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkOverlay(fmt("%s.%s", name, attr.name),
|
checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "nixosModule")
|
else if (name == "nixosModule")
|
||||||
|
@ -596,15 +618,15 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
else if (name == "nixosModules") {
|
else if (name == "nixosModules") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkModule(fmt("%s.%s", name, attr.name),
|
checkModule(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "nixosConfigurations") {
|
else if (name == "nixosConfigurations") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkNixOSConfiguration(fmt("%s.%s", name, attr.name),
|
checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "hydraJobs")
|
else if (name == "hydraJobs")
|
||||||
|
@ -616,29 +638,31 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
else if (name == "templates") {
|
else if (name == "templates") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs)
|
for (auto & attr : *vOutput.attrs)
|
||||||
checkTemplate(fmt("%s.%s", name, attr.name),
|
checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]),
|
||||||
*attr.value, *attr.pos);
|
*attr.value, attr.pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (name == "defaultBundler") {
|
else if (name == "defaultBundler") {
|
||||||
state->forceAttrs(vOutput, pos);
|
state->forceAttrs(vOutput, pos);
|
||||||
for (auto & attr : *vOutput.attrs) {
|
for (auto & attr : *vOutput.attrs) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
|
checkSystemName(attr_name, attr.pos);
|
||||||
checkBundler(
|
checkBundler(
|
||||||
fmt("%s.%s", name, attr.name),
|
fmt("%s.%s", name, attr_name),
|
||||||
*attr.value, *attr.pos);
|
*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) {
|
||||||
checkSystemName(attr.name, *attr.pos);
|
const auto & attr_name = state->symbols[attr.name];
|
||||||
state->forceAttrs(*attr.value, *attr.pos);
|
checkSystemName(attr_name, attr.pos);
|
||||||
|
state->forceAttrs(*attr.value, attr.pos);
|
||||||
for (auto & attr2 : *attr.value->attrs) {
|
for (auto & attr2 : *attr.value->attrs) {
|
||||||
checkBundler(
|
checkBundler(
|
||||||
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
*attr2.value, *attr2.pos);
|
*attr2.value, attr2.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -647,7 +671,7 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
warn("unknown flake output '%s'", name);
|
warn("unknown flake output '%s'", name);
|
||||||
|
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(pos, hintfmt("while checking flake output '%s'", name));
|
e.addTrace(resolve(pos), hintfmt("while checking flake output '%s'", name));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -705,7 +729,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
||||||
defaultTemplateAttrPathsPrefixes,
|
defaultTemplateAttrPathsPrefixes,
|
||||||
lockFlags);
|
lockFlags);
|
||||||
|
|
||||||
auto [cursor, attrPath] = installable.getCursor(*evalState);
|
auto cursor = installable.getCursor(*evalState);
|
||||||
|
|
||||||
auto templateDirAttr = cursor->getAttr("path");
|
auto templateDirAttr = cursor->getAttr("path");
|
||||||
auto templateDir = templateDirAttr->getString();
|
auto templateDir = templateDirAttr->getString();
|
||||||
|
@ -962,8 +986,11 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
{
|
{
|
||||||
auto j = nlohmann::json::object();
|
auto j = nlohmann::json::object();
|
||||||
|
|
||||||
|
auto attrPathS = state->symbols.resolve(attrPath);
|
||||||
|
|
||||||
Activity act(*logger, lvlInfo, actUnknown,
|
Activity act(*logger, lvlInfo, actUnknown,
|
||||||
fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
|
fmt("evaluating '%s'", concatStringsSep(".", attrPathS)));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto recurse = [&]()
|
auto recurse = [&]()
|
||||||
{
|
{
|
||||||
|
@ -971,14 +998,15 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
logger->cout("%s", headerPrefix);
|
logger->cout("%s", headerPrefix);
|
||||||
auto attrs = visitor.getAttrs();
|
auto attrs = visitor.getAttrs();
|
||||||
for (const auto & [i, attr] : enumerate(attrs)) {
|
for (const auto & [i, attr] : enumerate(attrs)) {
|
||||||
|
const auto & attrName = state->symbols[attr];
|
||||||
bool last = i + 1 == attrs.size();
|
bool last = i + 1 == attrs.size();
|
||||||
auto visitor2 = visitor.getAttr(attr);
|
auto visitor2 = visitor.getAttr(attrName);
|
||||||
auto attrPath2(attrPath);
|
auto attrPath2(attrPath);
|
||||||
attrPath2.push_back(attr);
|
attrPath2.push_back(attr);
|
||||||
auto j2 = visit(*visitor2, attrPath2,
|
auto j2 = visit(*visitor2, attrPath2,
|
||||||
fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD "%s" ANSI_NORMAL, nextPrefix, last ? treeLast : treeConn, attr),
|
fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD "%s" ANSI_NORMAL, nextPrefix, last ? treeLast : treeConn, attrName),
|
||||||
nextPrefix + (last ? treeNull : treeLine));
|
nextPrefix + (last ? treeNull : treeLine));
|
||||||
if (json) j.emplace(attr, std::move(j2));
|
if (json) j.emplace(attrName, std::move(j2));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -998,10 +1026,10 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
} else {
|
} else {
|
||||||
logger->cout("%s: %s '%s'",
|
logger->cout("%s: %s '%s'",
|
||||||
headerPrefix,
|
headerPrefix,
|
||||||
attrPath.size() == 2 && attrPath[0] == "devShell" ? "development environment" :
|
attrPath.size() == 2 && attrPathS[0] == "devShell" ? "development environment" :
|
||||||
attrPath.size() >= 2 && attrPath[0] == "devShells" ? "development environment" :
|
attrPath.size() >= 2 && attrPathS[0] == "devShells" ? "development environment" :
|
||||||
attrPath.size() == 3 && attrPath[0] == "checks" ? "derivation" :
|
attrPath.size() == 3 && attrPathS[0] == "checks" ? "derivation" :
|
||||||
attrPath.size() >= 1 && attrPath[0] == "hydraJobs" ? "derivation" :
|
attrPath.size() >= 1 && attrPathS[0] == "hydraJobs" ? "derivation" :
|
||||||
"package",
|
"package",
|
||||||
name);
|
name);
|
||||||
}
|
}
|
||||||
|
@ -1009,26 +1037,27 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
|
|
||||||
if (attrPath.size() == 0
|
if (attrPath.size() == 0
|
||||||
|| (attrPath.size() == 1 && (
|
|| (attrPath.size() == 1 && (
|
||||||
attrPath[0] == "defaultPackage"
|
attrPathS[0] == "defaultPackage"
|
||||||
|| attrPath[0] == "devShell"
|
|| attrPathS[0] == "devShell"
|
||||||
|| attrPath[0] == "nixosConfigurations"
|
|| attrPathS[0] == "formatter"
|
||||||
|| attrPath[0] == "nixosModules"
|
|| attrPathS[0] == "nixosConfigurations"
|
||||||
|| attrPath[0] == "defaultApp"
|
|| attrPathS[0] == "nixosModules"
|
||||||
|| attrPath[0] == "templates"
|
|| attrPathS[0] == "defaultApp"
|
||||||
|| attrPath[0] == "overlays"))
|
|| attrPathS[0] == "templates"
|
||||||
|
|| attrPathS[0] == "overlays"))
|
||||||
|| ((attrPath.size() == 1 || attrPath.size() == 2)
|
|| ((attrPath.size() == 1 || attrPath.size() == 2)
|
||||||
&& (attrPath[0] == "checks"
|
&& (attrPathS[0] == "checks"
|
||||||
|| attrPath[0] == "packages"
|
|| attrPathS[0] == "packages"
|
||||||
|| attrPath[0] == "devShells"
|
|| attrPathS[0] == "devShells"
|
||||||
|| attrPath[0] == "apps"))
|
|| attrPathS[0] == "apps"))
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
recurse();
|
recurse();
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (
|
else if (
|
||||||
(attrPath.size() == 2 && (attrPath[0] == "defaultPackage" || attrPath[0] == "devShell"))
|
(attrPath.size() == 2 && (attrPathS[0] == "defaultPackage" || attrPathS[0] == "devShell" || attrPathS[0] == "formatter"))
|
||||||
|| (attrPath.size() == 3 && (attrPath[0] == "checks" || attrPath[0] == "packages" || attrPath[0] == "devShells"))
|
|| (attrPath.size() == 3 && (attrPathS[0] == "checks" || attrPathS[0] == "packages" || attrPathS[0] == "devShells"))
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (visitor.isDerivation())
|
if (visitor.isDerivation())
|
||||||
|
@ -1037,14 +1066,14 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
throw Error("expected a derivation");
|
throw Error("expected a derivation");
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (attrPath.size() > 0 && attrPath[0] == "hydraJobs") {
|
else if (attrPath.size() > 0 && attrPathS[0] == "hydraJobs") {
|
||||||
if (visitor.isDerivation())
|
if (visitor.isDerivation())
|
||||||
showDerivation();
|
showDerivation();
|
||||||
else
|
else
|
||||||
recurse();
|
recurse();
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (attrPath.size() > 0 && attrPath[0] == "legacyPackages") {
|
else if (attrPath.size() > 0 && attrPathS[0] == "legacyPackages") {
|
||||||
if (attrPath.size() == 1)
|
if (attrPath.size() == 1)
|
||||||
recurse();
|
recurse();
|
||||||
else if (!showLegacy)
|
else if (!showLegacy)
|
||||||
|
@ -1059,8 +1088,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (
|
else if (
|
||||||
(attrPath.size() == 2 && attrPath[0] == "defaultApp") ||
|
(attrPath.size() == 2 && attrPathS[0] == "defaultApp") ||
|
||||||
(attrPath.size() == 3 && attrPath[0] == "apps"))
|
(attrPath.size() == 3 && attrPathS[0] == "apps"))
|
||||||
{
|
{
|
||||||
auto aType = visitor.maybeGetAttr("type");
|
auto aType = visitor.maybeGetAttr("type");
|
||||||
if (!aType || aType->getString() != "app")
|
if (!aType || aType->getString() != "app")
|
||||||
|
@ -1073,8 +1102,8 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (
|
else if (
|
||||||
(attrPath.size() == 1 && attrPath[0] == "defaultTemplate") ||
|
(attrPath.size() == 1 && attrPathS[0] == "defaultTemplate") ||
|
||||||
(attrPath.size() == 2 && attrPath[0] == "templates"))
|
(attrPath.size() == 2 && attrPathS[0] == "templates"))
|
||||||
{
|
{
|
||||||
auto description = visitor.getAttr("description")->getString();
|
auto description = visitor.getAttr("description")->getString();
|
||||||
if (json) {
|
if (json) {
|
||||||
|
@ -1087,11 +1116,11 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
|
|
||||||
else {
|
else {
|
||||||
auto [type, description] =
|
auto [type, description] =
|
||||||
(attrPath.size() == 1 && attrPath[0] == "overlay")
|
(attrPath.size() == 1 && attrPathS[0] == "overlay")
|
||||||
|| (attrPath.size() == 2 && attrPath[0] == "overlays") ? std::make_pair("nixpkgs-overlay", "Nixpkgs overlay") :
|
|| (attrPath.size() == 2 && attrPathS[0] == "overlays") ? std::make_pair("nixpkgs-overlay", "Nixpkgs overlay") :
|
||||||
attrPath.size() == 2 && attrPath[0] == "nixosConfigurations" ? std::make_pair("nixos-configuration", "NixOS configuration") :
|
attrPath.size() == 2 && attrPathS[0] == "nixosConfigurations" ? std::make_pair("nixos-configuration", "NixOS configuration") :
|
||||||
(attrPath.size() == 1 && attrPath[0] == "nixosModule")
|
(attrPath.size() == 1 && attrPathS[0] == "nixosModule")
|
||||||
|| (attrPath.size() == 2 && attrPath[0] == "nixosModules") ? std::make_pair("nixos-module", "NixOS module") :
|
|| (attrPath.size() == 2 && attrPathS[0] == "nixosModules") ? std::make_pair("nixos-module", "NixOS module") :
|
||||||
std::make_pair("unknown", "unknown");
|
std::make_pair("unknown", "unknown");
|
||||||
if (json) {
|
if (json) {
|
||||||
j.emplace("type", type);
|
j.emplace("type", type);
|
||||||
|
@ -1100,7 +1129,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (EvalError & e) {
|
} catch (EvalError & e) {
|
||||||
if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages"))
|
if (!(attrPath.size() > 0 && attrPathS[0] == "legacyPackages"))
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,8 +177,8 @@ Currently the `type` attribute can be one of the following:
|
||||||
attribute `url`.
|
attribute `url`.
|
||||||
|
|
||||||
In URL form, the schema must be `http://`, `https://` or `file://`
|
In URL form, the schema must be `http://`, `https://` or `file://`
|
||||||
URLs and the extension must be `.zip`, `.tar`, `.tar.gz`, `.tar.xz`,
|
URLs and the extension must be `.zip`, `.tar`, `.tgz`, `.tar.gz`,
|
||||||
`.tar.bz2` or `.tar.zst`.
|
`.tar.xz`, `.tar.bz2` or `.tar.zst`.
|
||||||
|
|
||||||
* `github`: A more efficient way to fetch repositories from
|
* `github`: A more efficient way to fetch repositories from
|
||||||
GitHub. The following attributes are required:
|
GitHub. The following attributes are required:
|
||||||
|
|
54
src/nix/fmt.cc
Normal file
54
src/nix/fmt.cc
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include "command.hh"
|
||||||
|
#include "run.hh"
|
||||||
|
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
|
struct CmdFmt : SourceExprCommand {
|
||||||
|
std::vector<std::string> args;
|
||||||
|
|
||||||
|
CmdFmt() { expectArgs({.label = "args", .handler = {&args}}); }
|
||||||
|
|
||||||
|
std::string description() override {
|
||||||
|
return "reformat your code in the standard style";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override {
|
||||||
|
return
|
||||||
|
#include "fmt.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
Category category() override { return catSecondary; }
|
||||||
|
|
||||||
|
Strings getDefaultFlakeAttrPaths() override {
|
||||||
|
return Strings{"formatter." + settings.thisSystem.get()};
|
||||||
|
}
|
||||||
|
|
||||||
|
Strings getDefaultFlakeAttrPathPrefixes() override { return Strings{}; }
|
||||||
|
|
||||||
|
void run(ref<Store> store) override
|
||||||
|
{
|
||||||
|
auto evalState = getEvalState();
|
||||||
|
auto evalStore = getEvalStore();
|
||||||
|
|
||||||
|
auto installable = parseInstallable(store, ".");
|
||||||
|
auto app = installable->toApp(*evalState).resolve(evalStore, store);
|
||||||
|
|
||||||
|
Strings programArgs{app.program};
|
||||||
|
|
||||||
|
// Propagate arguments from the CLI
|
||||||
|
if (args.empty()) {
|
||||||
|
// Format the current flake out of the box
|
||||||
|
programArgs.push_back(".");
|
||||||
|
} else {
|
||||||
|
// User wants more power, let them decide which paths to include/exclude
|
||||||
|
for (auto &i : args) {
|
||||||
|
programArgs.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runProgramInStore(store, app.program, programArgs);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto r2 = registerCommand<CmdFmt>("fmt");
|
53
src/nix/fmt.md
Normal file
53
src/nix/fmt.md
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
R""(
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
With [nixpkgs-fmt](https://github.com/nix-community/nixpkgs-fmt):
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# flake.nix
|
||||||
|
{
|
||||||
|
outputs = { nixpkgs, self }: {
|
||||||
|
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixpkgs-fmt;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Format the current flake: `$ nix fmt`
|
||||||
|
|
||||||
|
- Format a specific folder or file: `$ nix fmt ./folder ./file.nix`
|
||||||
|
|
||||||
|
With [nixfmt](https://github.com/serokell/nixfmt):
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# flake.nix
|
||||||
|
{
|
||||||
|
outputs = { nixpkgs, self }: {
|
||||||
|
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixfmt;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Format specific files: `$ nix fmt ./file1.nix ./file2.nix`
|
||||||
|
|
||||||
|
With [Alejandra](https://github.com/kamadorueda/alejandra):
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# flake.nix
|
||||||
|
{
|
||||||
|
outputs = { nixpkgs, self }: {
|
||||||
|
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.alejandra;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Format the current flake: `$ nix fmt`
|
||||||
|
|
||||||
|
- Format a specific folder or file: `$ nix fmt ./folder ./file.nix`
|
||||||
|
|
||||||
|
# Description
|
||||||
|
|
||||||
|
`nix fmt` will rewrite all Nix files (\*.nix) to a canonical format
|
||||||
|
using the formatter specified in your flake.
|
||||||
|
|
||||||
|
)""
|
|
@ -302,7 +302,7 @@ void mainWrapped(int argc, char * * argv)
|
||||||
b["arity"] = primOp->arity;
|
b["arity"] = primOp->arity;
|
||||||
b["args"] = primOp->args;
|
b["args"] = primOp->args;
|
||||||
b["doc"] = trim(stripIndentation(primOp->doc));
|
b["doc"] = trim(stripIndentation(primOp->doc));
|
||||||
res[(std::string) builtin.name] = std::move(b);
|
res[state.symbols[builtin.name]] = std::move(b);
|
||||||
}
|
}
|
||||||
std::cout << res.dump() << "\n";
|
std::cout << res.dump() << "\n";
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -199,11 +199,13 @@ static int main_nix_prefetch_url(int argc, char * * argv)
|
||||||
state->forceAttrs(v, noPos);
|
state->forceAttrs(v, noPos);
|
||||||
|
|
||||||
/* Extract the URL. */
|
/* Extract the URL. */
|
||||||
auto & attr = v.attrs->need(state->symbols.create("urls"));
|
auto * attr = v.attrs->get(state->symbols.create("urls"));
|
||||||
state->forceList(*attr.value, noPos);
|
if (!attr)
|
||||||
if (attr.value->listSize() < 1)
|
throw Error("attribute 'urls' missing");
|
||||||
|
state->forceList(*attr->value, noPos);
|
||||||
|
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]);
|
||||||
|
|
||||||
/* Extract the hash mode. */
|
/* Extract the hash mode. */
|
||||||
auto attr2 = v.attrs->get(state->symbols.create("outputHashMode"));
|
auto attr2 = v.attrs->get(state->symbols.create("outputHashMode"));
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "eval-cache.hh"
|
#include "eval-cache.hh"
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
#include "fmt.hh"
|
#include "hilite.hh"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -77,13 +77,15 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
|
|
||||||
visit = [&](eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse)
|
visit = [&](eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse)
|
||||||
{
|
{
|
||||||
|
auto attrPathS = state->symbols.resolve(attrPath);
|
||||||
|
|
||||||
Activity act(*logger, lvlInfo, actUnknown,
|
Activity act(*logger, lvlInfo, actUnknown,
|
||||||
fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
|
fmt("evaluating '%s'", concatStringsSep(".", attrPathS)));
|
||||||
try {
|
try {
|
||||||
auto recurse = [&]()
|
auto recurse = [&]()
|
||||||
{
|
{
|
||||||
for (const auto & attr : cursor.getAttrs()) {
|
for (const auto & attr : cursor.getAttrs()) {
|
||||||
auto cursor2 = cursor.getAttr(attr);
|
auto cursor2 = cursor.getAttr(state->symbols[attr]);
|
||||||
auto attrPath2(attrPath);
|
auto attrPath2(attrPath);
|
||||||
attrPath2.push_back(attr);
|
attrPath2.push_back(attr);
|
||||||
visit(*cursor2, attrPath2, false);
|
visit(*cursor2, attrPath2, false);
|
||||||
|
@ -97,7 +99,7 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
auto aDescription = aMeta ? aMeta->maybeGetAttr("description") : nullptr;
|
auto aDescription = aMeta ? aMeta->maybeGetAttr("description") : nullptr;
|
||||||
auto description = aDescription ? aDescription->getString() : "";
|
auto description = aDescription ? aDescription->getString() : "";
|
||||||
std::replace(description.begin(), description.end(), '\n', ' ');
|
std::replace(description.begin(), description.end(), '\n', ' ');
|
||||||
auto attrPath2 = concatStringsSep(".", attrPath);
|
auto attrPath2 = concatStringsSep(".", attrPathS);
|
||||||
|
|
||||||
std::vector<std::smatch> attrPathMatches;
|
std::vector<std::smatch> attrPathMatches;
|
||||||
std::vector<std::smatch> descriptionMatches;
|
std::vector<std::smatch> descriptionMatches;
|
||||||
|
@ -146,27 +148,27 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
|
|
||||||
else if (
|
else if (
|
||||||
attrPath.size() == 0
|
attrPath.size() == 0
|
||||||
|| (attrPath[0] == "legacyPackages" && attrPath.size() <= 2)
|
|| (attrPathS[0] == "legacyPackages" && attrPath.size() <= 2)
|
||||||
|| (attrPath[0] == "packages" && attrPath.size() <= 2))
|
|| (attrPathS[0] == "packages" && attrPath.size() <= 2))
|
||||||
recurse();
|
recurse();
|
||||||
|
|
||||||
else if (initialRecurse)
|
else if (initialRecurse)
|
||||||
recurse();
|
recurse();
|
||||||
|
|
||||||
else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) {
|
else if (attrPathS[0] == "legacyPackages" && attrPath.size() > 2) {
|
||||||
auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations);
|
auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations);
|
||||||
if (attr && attr->getBool())
|
if (attr && attr->getBool())
|
||||||
recurse();
|
recurse();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (EvalError & e) {
|
} catch (EvalError & e) {
|
||||||
if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages"))
|
if (!(attrPath.size() > 0 && attrPathS[0] == "legacyPackages"))
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto & [cursor, prefix] : installable->getCursors(*state))
|
for (auto & cursor : installable->getCursors(*state))
|
||||||
visit(*cursor, parseAttrPath(*state, prefix), true);
|
visit(*cursor, cursor->getAttrPath(), true);
|
||||||
|
|
||||||
if (!json && !results)
|
if (!json && !results)
|
||||||
throw Error("no results for the given search term(s)!");
|
throw Error("no results for the given search term(s)!");
|
||||||
|
|
|
@ -2,7 +2,7 @@ source common.sh
|
||||||
|
|
||||||
file=build-hook-ca-floating.nix
|
file=build-hook-ca-floating.nix
|
||||||
|
|
||||||
enableFeatures "ca-derivations ca-references"
|
enableFeatures "ca-derivations"
|
||||||
|
|
||||||
CONTENT_ADDRESSED=true
|
CONTENT_ADDRESSED=true
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,14 @@ outPath=$(readlink -f $TEST_ROOT/result)
|
||||||
|
|
||||||
grep 'FOO BAR BAZ' $TEST_ROOT/machine0/$outPath
|
grep 'FOO BAR BAZ' $TEST_ROOT/machine0/$outPath
|
||||||
|
|
||||||
|
testPrintOutPath=$(nix build -L -v -f $file --print-out-paths --max-jobs 0 \
|
||||||
|
--arg busybox $busybox \
|
||||||
|
--store $TEST_ROOT/machine0 \
|
||||||
|
--builders "$(join_by '; ' "${builders[@]}")"
|
||||||
|
)
|
||||||
|
|
||||||
|
[[ $testPrintOutPath =~ store.*build-remote ]]
|
||||||
|
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
# Ensure that input1 was built on store1 due to the required feature.
|
# Ensure that input1 was built on store1 due to the required feature.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
source ../common.sh
|
source ../common.sh
|
||||||
|
|
||||||
enableFeatures "ca-derivations ca-references"
|
enableFeatures "ca-derivations"
|
||||||
|
|
||||||
restartDaemon
|
restartDaemon
|
||||||
|
|
11
tests/ca/selfref-gc.sh
Executable file
11
tests/ca/selfref-gc.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
requireDaemonNewerThan "2.4pre20210626"
|
||||||
|
|
||||||
|
enableFeatures "ca-derivations nix-command flakes"
|
||||||
|
|
||||||
|
export NIX_TESTS_CA_BY_DEFAULT=1
|
||||||
|
cd ..
|
||||||
|
source ./selfref-gc.sh
|
|
@ -20,6 +20,8 @@ nix eval --expr 'assert 1 + 2 == 3; true'
|
||||||
[[ $(nix eval attr --json -f "./eval.nix") == '{"foo":"bar"}' ]]
|
[[ $(nix eval attr --json -f "./eval.nix") == '{"foo":"bar"}' ]]
|
||||||
[[ $(nix eval int -f - < "./eval.nix") == 123 ]]
|
[[ $(nix eval int -f - < "./eval.nix") == 123 ]]
|
||||||
|
|
||||||
|
# Check if toFile can be utilized during restricted eval
|
||||||
|
[[ $(nix eval --restrict-eval --expr 'import (builtins.toFile "source" "42")') == 42 ]]
|
||||||
|
|
||||||
nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
|
nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
|
||||||
[[ $(nix-instantiate -A int --eval "./eval.nix") == 123 ]]
|
[[ $(nix-instantiate -A int --eval "./eval.nix") == 123 ]]
|
||||||
|
|
29
tests/flakes-run.sh
Normal file
29
tests/flakes-run.sh
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local
|
||||||
|
cp ./shell-hello.nix ./config.nix $TEST_HOME
|
||||||
|
cd $TEST_HOME
|
||||||
|
|
||||||
|
cat <<EOF > flake.nix
|
||||||
|
{
|
||||||
|
outputs = {self}: {
|
||||||
|
packages.$system.pkgAsPkg = (import ./shell-hello.nix).hello;
|
||||||
|
packages.$system.appAsApp = self.packages.$system.appAsApp;
|
||||||
|
|
||||||
|
apps.$system.pkgAsApp = self.packages.$system.pkgAsPkg;
|
||||||
|
apps.$system.appAsApp = {
|
||||||
|
type = "app";
|
||||||
|
program = "\${(import ./shell-hello.nix).hello}/bin/hello";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
nix run --no-write-lock-file .#appAsApp
|
||||||
|
nix run --no-write-lock-file .#pkgAsPkg
|
||||||
|
|
||||||
|
! nix run --no-write-lock-file .#pkgAsApp || fail "'nix run' shouldn’t accept an 'app' defined under 'packages'"
|
||||||
|
! nix run --no-write-lock-file .#appAsPkg || fail "elements of 'apps' should be of type 'app'"
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
|
30
tests/fmt.sh
Normal file
30
tests/fmt.sh
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
clearStore
|
||||||
|
rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local
|
||||||
|
|
||||||
|
cp ./simple.nix ./simple.builder.sh ./fmt.simple.sh ./config.nix $TEST_HOME
|
||||||
|
|
||||||
|
cd $TEST_HOME
|
||||||
|
|
||||||
|
nix fmt --help | grep "Format"
|
||||||
|
|
||||||
|
cat << EOF > flake.nix
|
||||||
|
{
|
||||||
|
outputs = _: {
|
||||||
|
formatter.$system =
|
||||||
|
with import ./config.nix;
|
||||||
|
mkDerivation {
|
||||||
|
name = "formatter";
|
||||||
|
buildCommand = "mkdir -p \$out/bin; cp \${./fmt.simple.sh} \$out/bin/formatter";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
nix fmt ./file ./folder | grep 'Formatting: ./file ./folder'
|
||||||
|
nix flake check
|
||||||
|
nix flake show | grep -P "package 'formatter'"
|
||||||
|
|
||||||
|
clearStore
|
1
tests/fmt.simple.sh
Executable file
1
tests/fmt.simple.sh
Executable file
|
@ -0,0 +1 @@
|
||||||
|
echo Formatting: "${@}"
|
|
@ -1,5 +1,6 @@
|
||||||
nix_tests = \
|
nix_tests = \
|
||||||
flakes.sh \
|
flakes.sh \
|
||||||
|
flakes-run.sh \
|
||||||
ca/gc.sh \
|
ca/gc.sh \
|
||||||
gc.sh \
|
gc.sh \
|
||||||
remote-store.sh \
|
remote-store.sh \
|
||||||
|
@ -79,6 +80,7 @@ nix_tests = \
|
||||||
post-hook.sh \
|
post-hook.sh \
|
||||||
function-trace.sh \
|
function-trace.sh \
|
||||||
flake-local-settings.sh \
|
flake-local-settings.sh \
|
||||||
|
fmt.sh \
|
||||||
eval-store.sh \
|
eval-store.sh \
|
||||||
why-depends.sh \
|
why-depends.sh \
|
||||||
import-derivation.sh \
|
import-derivation.sh \
|
||||||
|
@ -90,6 +92,7 @@ nix_tests = \
|
||||||
plugins.sh \
|
plugins.sh \
|
||||||
build.sh \
|
build.sh \
|
||||||
ca/nix-run.sh \
|
ca/nix-run.sh \
|
||||||
|
selfref-gc.sh ca/selfref-gc.sh \
|
||||||
db-migration.sh \
|
db-migration.sh \
|
||||||
bash-profile.sh \
|
bash-profile.sh \
|
||||||
pass-as-file.sh \
|
pass-as-file.sh \
|
||||||
|
|
|
@ -3,7 +3,7 @@ source common.sh
|
||||||
clearStore
|
clearStore
|
||||||
clearProfiles
|
clearProfiles
|
||||||
|
|
||||||
enableFeatures "ca-derivations ca-references"
|
enableFeatures "ca-derivations"
|
||||||
restartDaemon
|
restartDaemon
|
||||||
|
|
||||||
# Make a flake.
|
# Make a flake.
|
||||||
|
|
|
@ -13,7 +13,7 @@ MySettings mySettings;
|
||||||
|
|
||||||
static GlobalConfig::Register rs(&mySettings);
|
static GlobalConfig::Register rs(&mySettings);
|
||||||
|
|
||||||
static void prim_anotherNull (EvalState & state, const Pos & pos, Value ** args, Value & v)
|
static void prim_anotherNull (EvalState & state, const PosIdx pos, Value ** args, Value & v)
|
||||||
{
|
{
|
||||||
if (mySettings.settingSet)
|
if (mySettings.settingSet)
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
|
|
|
@ -9,12 +9,12 @@ echo 'require-sigs = false' >> $NIX_CONF_DIR/nix.conf
|
||||||
|
|
||||||
restartDaemon
|
restartDaemon
|
||||||
|
|
||||||
# Build the dependencies and push them to the remote store
|
# Build the dependencies and push them to the remote store.
|
||||||
nix-build -o $TEST_ROOT/result dependencies.nix --post-build-hook $PWD/push-to-store.sh
|
nix-build -o $TEST_ROOT/result dependencies.nix --post-build-hook $PWD/push-to-store.sh
|
||||||
|
|
||||||
clearStore
|
clearStore
|
||||||
|
|
||||||
# Ensure that we the remote store contains both the runtime and buildtime
|
# Ensure that the remote store contains both the runtime and build-time
|
||||||
# closure of what we've just built
|
# closure of what we've just built.
|
||||||
nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix
|
nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix
|
||||||
nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix input1_drv
|
nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix input1_drv
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
set -x
|
set -x
|
||||||
|
set -e
|
||||||
|
|
||||||
|
[ -n "$OUT_PATHS" ]
|
||||||
|
[ -n "$DRV_PATH" ]
|
||||||
|
|
||||||
echo Pushing "$OUT_PATHS" to "$REMOTE_STORE"
|
echo Pushing "$OUT_PATHS" to "$REMOTE_STORE"
|
||||||
printf "%s" "$DRV_PATH" | xargs nix copy --to "$REMOTE_STORE" --no-require-sigs
|
printf "%s" "$DRV_PATH" | xargs nix copy --to "$REMOTE_STORE" --no-require-sigs
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue