forked from lix-project/lix
Merge remote-tracking branch 'upstream/master' into indexed-store-path-outputs
This commit is contained in:
commit
41e755bee4
40 changed files with 286 additions and 159 deletions
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
|
@ -8,7 +8,7 @@ jobs:
|
||||||
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
if: github.repository_owner == 'NixOS' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
# required to find all branches
|
# required to find all branches
|
||||||
|
|
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
|
@ -14,10 +14,10 @@ jobs:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: cachix/install-nix-action@v16
|
- uses: cachix/install-nix-action@v17
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- uses: cachix/cachix-action@v10
|
- uses: cachix/cachix-action@v10
|
||||||
if: needs.check_cachix.outputs.secret == 'true'
|
if: needs.check_cachix.outputs.secret == 'true'
|
||||||
|
@ -46,11 +46,11 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
|
installerURL: ${{ steps.prepare-installer.outputs.installerURL }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- uses: cachix/install-nix-action@v16
|
- uses: cachix/install-nix-action@v17
|
||||||
- uses: cachix/cachix-action@v10
|
- uses: cachix/cachix-action@v10
|
||||||
with:
|
with:
|
||||||
name: '${{ env.CACHIX_NAME }}'
|
name: '${{ env.CACHIX_NAME }}'
|
||||||
|
@ -67,9 +67,9 @@ jobs:
|
||||||
os: [ubuntu-latest, macos-latest]
|
os: [ubuntu-latest, macos-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- uses: cachix/install-nix-action@v16
|
- uses: cachix/install-nix-action@v17
|
||||||
with:
|
with:
|
||||||
install_url: '${{needs.installer.outputs.installerURL}}'
|
install_url: '${{needs.installer.outputs.installerURL}}'
|
||||||
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
|
||||||
|
@ -83,10 +83,10 @@ jobs:
|
||||||
needs.check_cachix.outputs.secret == 'true'
|
needs.check_cachix.outputs.secret == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: cachix/install-nix-action@v16
|
- uses: cachix/install-nix-action@v17
|
||||||
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
|
||||||
- run: echo NIX_VERSION="$(nix-instantiate --eval -E '(import ./default.nix).defaultPackage.${builtins.currentSystem}.version' | tr -d \")" >> $GITHUB_ENV
|
- run: echo NIX_VERSION="$(nix-instantiate --eval -E '(import ./default.nix).defaultPackage.${builtins.currentSystem}.version' | tr -d \")" >> $GITHUB_ENV
|
||||||
- uses: cachix/cachix-action@v10
|
- uses: cachix/cachix-action@v10
|
||||||
|
|
2
.github/workflows/hydra_status.yml
vendored
2
.github/workflows/hydra_status.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
||||||
if: github.repository_owner == 'NixOS'
|
if: github.repository_owner == 'NixOS'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2.4.0
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: bash scripts/check-hydra-status.sh
|
- run: bash scripts/check-hydra-status.sh
|
||||||
|
|
|
@ -41,5 +41,9 @@
|
||||||
As before, the old output will continue to work, but `nix flake check` will
|
As before, the old output will continue to work, but `nix flake check` will
|
||||||
issue a warning about it.
|
issue a warning about it.
|
||||||
|
|
||||||
|
* `nix run` is now stricter wrt what it accepts:
|
||||||
|
* Members of `apps` 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))
|
||||||
|
* Member of `packages` or `legacyPackages` cannot be of type "app" when used by `nix run`.
|
||||||
|
|
||||||
* Add experimental *indexed store derivations* installable syntax, part of the
|
* Add experimental *indexed store derivations* installable syntax, part of the
|
||||||
the `computed-derivations` experimental feature.
|
the `computed-derivations` experimental feature.
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -423,6 +423,18 @@ EOF
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [ "$(uname -s)" = "Linux" ] && [ ! -e /run/systemd/system ]; then
|
||||||
|
warning <<EOF
|
||||||
|
We did not detect systemd on your system. With a multi-user install
|
||||||
|
without systemd you will have to manually configure your init system to
|
||||||
|
launch the Nix daemon after installation.
|
||||||
|
EOF
|
||||||
|
if ! ui_confirm "Do you want to proceed with a multi-user installation?"; then
|
||||||
|
failure <<EOF
|
||||||
|
You have aborted the installation.
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_report() {
|
setup_report() {
|
||||||
|
|
|
@ -85,11 +85,12 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||||
{
|
{
|
||||||
std::optional<Path> file;
|
std::optional<Path> file;
|
||||||
std::optional<std::string> expr;
|
std::optional<std::string> expr;
|
||||||
|
bool readOnlyMode = false;
|
||||||
|
|
||||||
// FIXME: move this; not all commands (e.g. 'nix run') use it.
|
// FIXME: move this; not all commands (e.g. 'nix run') use it.
|
||||||
OperateOn operateOn = OperateOn::Output;
|
OperateOn operateOn = OperateOn::Output;
|
||||||
|
|
||||||
SourceExprCommand();
|
SourceExprCommand(bool supportReadOnlyMode = false);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Installable>> parseInstallables(
|
std::vector<std::shared_ptr<Installable>> parseInstallables(
|
||||||
ref<Store> store, std::vector<std::string> ss);
|
ref<Store> store, std::vector<std::string> ss);
|
||||||
|
@ -128,13 +129,13 @@ struct InstallableCommand : virtual Args, SourceExprCommand
|
||||||
{
|
{
|
||||||
std::shared_ptr<Installable> installable;
|
std::shared_ptr<Installable> installable;
|
||||||
|
|
||||||
InstallableCommand();
|
InstallableCommand(bool supportReadOnlyMode = false);
|
||||||
|
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
|
||||||
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
||||||
{
|
{
|
||||||
return parseFlakeRef(_installable, absPath("."));
|
return parseFlakeRefWithFragment(_installable, absPath(".")).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -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,4 +1,6 @@
|
||||||
|
#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"
|
||||||
|
@ -99,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);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -129,7 +139,7 @@ MixFlakeOptions::MixFlakeOptions()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
SourceExprCommand::SourceExprCommand()
|
SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
||||||
{
|
{
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "file",
|
.longName = "file",
|
||||||
|
@ -157,6 +167,17 @@ SourceExprCommand::SourceExprCommand()
|
||||||
.category = installablesCategory,
|
.category = installablesCategory,
|
||||||
.handler = {&operateOn, OperateOn::Derivation},
|
.handler = {&operateOn, OperateOn::Derivation},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (supportReadOnlyMode) {
|
||||||
|
addFlag({
|
||||||
|
.longName = "read-only",
|
||||||
|
.description =
|
||||||
|
"Do not instantiate each evaluated derivation. "
|
||||||
|
"This improves performance, but can cause errors when accessing "
|
||||||
|
"store paths of derivations during evaluation.",
|
||||||
|
.handler = {&readOnlyMode, true},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
|
Strings SourceExprCommand::getDefaultFlakeAttrPaths()
|
||||||
|
@ -182,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(
|
||||||
|
@ -210,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 = 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,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)));
|
||||||
|
@ -259,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);
|
||||||
|
@ -334,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);
|
||||||
|
@ -586,27 +609,9 @@ 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;
|
|
||||||
|
|
||||||
for (auto & attrPath : getActualAttrPaths()) {
|
|
||||||
debug("trying flake output attribute '%s'", attrPath);
|
|
||||||
|
|
||||||
auto attrOrSuggestions = root->findAlongAttrPath(
|
|
||||||
parseAttrPath(*state, attrPath),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!attrOrSuggestions) {
|
|
||||||
suggestions += attrOrSuggestions.getSuggestions();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto attr = *attrOrSuggestions;
|
|
||||||
|
|
||||||
if (!attr->isDerivation())
|
if (!attr->isDerivation())
|
||||||
throw Error("flake output attribute '%s' is not a derivation", attrPath);
|
throw Error("flake output attribute '%s' is not a derivation", attrPath);
|
||||||
|
@ -618,11 +623,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
|
||||||
attr->getAttr(state->sOutputName)->getString()
|
attr->getAttr(state->sOutputName)->getString()
|
||||||
};
|
};
|
||||||
|
|
||||||
return {attrPath, lockedFlake->flake.lockedRef, std::move(drvInfo)};
|
return {attrPath, getLockedFlake()->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()
|
||||||
|
@ -634,33 +635,10 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||||
|
|
||||||
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
std::pair<Value *, Pos> 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(
|
std::vector<ref<eval_cache::AttrCursor>>
|
||||||
suggestions,
|
|
||||||
"flake '%s' does not provide attribute %s",
|
|
||||||
flakeRef,
|
|
||||||
showAttrPaths(getActualAttrPaths())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
|
||||||
InstallableFlake::getCursors(EvalState & state)
|
InstallableFlake::getCursors(EvalState & state)
|
||||||
{
|
{
|
||||||
auto evalCache = openEvalCache(state,
|
auto evalCache = openEvalCache(state,
|
||||||
|
@ -668,21 +646,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
|
||||||
{
|
{
|
||||||
|
if (!_lockedFlake) {
|
||||||
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||||
lockFlagsApplyConfig.applyNixConfig = true;
|
lockFlagsApplyConfig.applyNixConfig = true;
|
||||||
if (!_lockedFlake) {
|
|
||||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
||||||
}
|
}
|
||||||
return _lockedFlake;
|
return _lockedFlake;
|
||||||
|
@ -707,6 +719,10 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
{
|
{
|
||||||
std::vector<std::shared_ptr<Installable>> result;
|
std::vector<std::shared_ptr<Installable>> result;
|
||||||
|
|
||||||
|
if (readOnlyMode) {
|
||||||
|
settings.readOnlyMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (file || expr) {
|
if (file || expr) {
|
||||||
if (file && expr)
|
if (file && expr)
|
||||||
throw UsageError("'--file' and '--expr' are exclusive");
|
throw UsageError("'--file' and '--expr' are exclusive");
|
||||||
|
@ -988,7 +1004,7 @@ InstallablesCommand::InstallablesCommand()
|
||||||
void InstallablesCommand::prepare()
|
void InstallablesCommand::prepare()
|
||||||
{
|
{
|
||||||
if (_installables.empty() && useDefaultInstallables())
|
if (_installables.empty() && useDefaultInstallables())
|
||||||
// FIXME: commands like "nix install" should not have a
|
// FIXME: commands like "nix profile install" should not have a
|
||||||
// default, probably.
|
// default, probably.
|
||||||
_installables.push_back(".");
|
_installables.push_back(".");
|
||||||
installables = parseInstallables(getStore(), _installables);
|
installables = parseInstallables(getStore(), _installables);
|
||||||
|
@ -998,13 +1014,14 @@ 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()
|
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
|
||||||
|
: SourceExprCommand(supportReadOnlyMode)
|
||||||
{
|
{
|
||||||
expectArgs({
|
expectArgs({
|
||||||
.label = "installable",
|
.label = "installable",
|
||||||
|
|
|
@ -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
|
||||||
|
@ -180,9 +180,15 @@ struct InstallableFlake : InstallableValue
|
||||||
|
|
||||||
std::pair<Value *, Pos> toValue(EvalState & state) override;
|
std::pair<Value *, Pos> 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;
|
||||||
|
|
|
@ -306,9 +306,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(
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -238,9 +238,18 @@ std::optional<std::string> Input::getRef() const
|
||||||
|
|
||||||
std::optional<Hash> Input::getRev() const
|
std::optional<Hash> Input::getRev() const
|
||||||
{
|
{
|
||||||
if (auto s = maybeGetStrAttr(attrs, "rev"))
|
std::optional<Hash> hash = {};
|
||||||
return Hash::parseAny(*s, htSHA1);
|
|
||||||
return {};
|
if (auto s = maybeGetStrAttr(attrs, "rev")) {
|
||||||
|
try {
|
||||||
|
hash = Hash::parseAnyPrefixed(*s);
|
||||||
|
} catch (BadHash &e) {
|
||||||
|
// Default to sha1 for backwards compatibility with existing flakes
|
||||||
|
hash = Hash::parseAny(*s, htSHA1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<uint64_t> Input::getRevCount() const
|
std::optional<uint64_t> Input::getRevCount() const
|
||||||
|
|
|
@ -28,9 +28,7 @@ static std::string readHead(const Path & path)
|
||||||
|
|
||||||
static bool isNotDotGitDirectory(const Path & path)
|
static bool isNotDotGitDirectory(const Path & path)
|
||||||
{
|
{
|
||||||
static const std::regex gitDirRegex("^(?:.*/)?\\.git$");
|
return baseNameOf(path) != ".git";
|
||||||
|
|
||||||
return not std::regex_match(path, gitDirRegex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GitInputScheme : InputScheme
|
struct GitInputScheme : InputScheme
|
||||||
|
@ -189,8 +187,16 @@ struct GitInputScheme : InputScheme
|
||||||
if (submodules) cacheType += "-submodules";
|
if (submodules) cacheType += "-submodules";
|
||||||
if (allRefs) cacheType += "-all-refs";
|
if (allRefs) cacheType += "-all-refs";
|
||||||
|
|
||||||
|
auto checkHashType = [&](const std::optional<Hash> & hash)
|
||||||
|
{
|
||||||
|
if (hash.has_value() && !(hash->type == htSHA1 || hash->type == htSHA256))
|
||||||
|
throw Error("Hash '%s' is not supported by Git. Supported types are sha1 and sha256.", hash->to_string(Base16, true));
|
||||||
|
};
|
||||||
|
|
||||||
auto getLockedAttrs = [&]()
|
auto getLockedAttrs = [&]()
|
||||||
{
|
{
|
||||||
|
checkHashType(input.getRev());
|
||||||
|
|
||||||
return Attrs({
|
return Attrs({
|
||||||
{"type", cacheType},
|
{"type", cacheType},
|
||||||
{"name", name},
|
{"name", name},
|
||||||
|
|
|
@ -178,9 +178,11 @@ struct MercurialInputScheme : InputScheme
|
||||||
auto files = tokenizeString<std::set<std::string>>(
|
auto files = tokenizeString<std::set<std::string>>(
|
||||||
runHg({ "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);
|
runHg({ "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);
|
||||||
|
|
||||||
|
Path actualPath(absPath(actualUrl));
|
||||||
|
|
||||||
PathFilter filter = [&](const Path & p) -> bool {
|
PathFilter filter = [&](const Path & p) -> bool {
|
||||||
assert(hasPrefix(p, actualUrl));
|
assert(hasPrefix(p, actualPath));
|
||||||
std::string file(p, actualUrl.size() + 1);
|
std::string file(p, actualPath.size() + 1);
|
||||||
|
|
||||||
auto st = lstat(p);
|
auto st = lstat(p);
|
||||||
|
|
||||||
|
@ -193,7 +195,7 @@ struct MercurialInputScheme : InputScheme
|
||||||
return files.count(file);
|
return files.count(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto storePath = store->addToStore(input.getName(), actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);
|
auto storePath = store->addToStore(input.getName(), actualPath, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||||
|
|
||||||
return {std::move(storePath), input};
|
return {std::move(storePath), input};
|
||||||
}
|
}
|
||||||
|
@ -201,8 +203,17 @@ struct MercurialInputScheme : InputScheme
|
||||||
|
|
||||||
if (!input.getRef()) input.attrs.insert_or_assign("ref", "default");
|
if (!input.getRef()) input.attrs.insert_or_assign("ref", "default");
|
||||||
|
|
||||||
|
auto checkHashType = [&](const std::optional<Hash> & hash)
|
||||||
|
{
|
||||||
|
if (hash.has_value() && hash->type != htSHA1)
|
||||||
|
throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", hash->to_string(Base16, true));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
auto getLockedAttrs = [&]()
|
auto getLockedAttrs = [&]()
|
||||||
{
|
{
|
||||||
|
checkHashType(input.getRev());
|
||||||
|
|
||||||
return Attrs({
|
return Attrs({
|
||||||
{"type", "hg"},
|
{"type", "hg"},
|
||||||
{"name", name},
|
{"name", name},
|
||||||
|
|
|
@ -31,6 +31,8 @@ struct BuildResult
|
||||||
ResolvesToAlreadyValid,
|
ResolvesToAlreadyValid,
|
||||||
NoSubstituters,
|
NoSubstituters,
|
||||||
} status = MiscFailure;
|
} status = MiscFailure;
|
||||||
|
|
||||||
|
// FIXME: include entire ErrorInfo object.
|
||||||
std::string errorMsg;
|
std::string errorMsg;
|
||||||
|
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
|
|
|
@ -1371,8 +1371,7 @@ void DerivationGoal::done(
|
||||||
{
|
{
|
||||||
buildResult.status = status;
|
buildResult.status = status;
|
||||||
if (ex)
|
if (ex)
|
||||||
// FIXME: strip: "error: "
|
buildResult.errorMsg = fmt("%s", normaltxt(ex->info().msg));
|
||||||
buildResult.errorMsg = ex->what();
|
|
||||||
if (buildResult.status == BuildResult::TimedOut)
|
if (buildResult.status == BuildResult::TimedOut)
|
||||||
worker.timedOut = true;
|
worker.timedOut = true;
|
||||||
if (buildResult.status == BuildResult::PermanentFailure)
|
if (buildResult.status == BuildResult::PermanentFailure)
|
||||||
|
|
|
@ -704,6 +704,9 @@ void LocalDerivationGoal::startBuilder()
|
||||||
|
|
||||||
/* Run the builder. */
|
/* Run the builder. */
|
||||||
printMsg(lvlChatty, "executing builder '%1%'", drv->builder);
|
printMsg(lvlChatty, "executing builder '%1%'", drv->builder);
|
||||||
|
printMsg(lvlChatty, "using builder args '%1%'", concatStringsSep(" ", drv->args));
|
||||||
|
for (auto & i : drv->env)
|
||||||
|
printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second);
|
||||||
|
|
||||||
/* Create the log file. */
|
/* Create the log file. */
|
||||||
Path logFile = openLogFile();
|
Path logFile = openLogFile();
|
||||||
|
|
|
@ -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{
|
||||||
|
@ -123,8 +123,6 @@ public:
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
FileTransferError(FileTransfer::Error error, std::optional<std::string> response, const Args & ... args);
|
FileTransferError(FileTransfer::Error error, std::optional<std::string> response, const Args & ... args);
|
||||||
|
|
||||||
virtual const char* sname() const override { return "FileTransferError"; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isUri(std::string_view s);
|
bool isUri(std::string_view s);
|
||||||
|
|
|
@ -215,7 +215,6 @@ void handleSQLiteBusy(const SQLiteBusy & e)
|
||||||
if (now > lastWarned + 10) {
|
if (now > lastWarned + 10) {
|
||||||
lastWarned = now;
|
lastWarned = now;
|
||||||
logWarning({
|
logWarning({
|
||||||
.name = "Sqlite busy",
|
|
||||||
.msg = hintfmt(e.what())
|
.msg = hintfmt(e.what())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,9 +127,9 @@ 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++);
|
||||||
|
@ -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)) {
|
||||||
|
ss.push_back(*prefix);
|
||||||
if (exp.completer)
|
if (exp.completer)
|
||||||
if (auto prefix = needsCompletion(s))
|
|
||||||
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);
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,9 @@ const std::string & BaseError::calcWhat() const
|
||||||
if (what_.has_value())
|
if (what_.has_value())
|
||||||
return *what_;
|
return *what_;
|
||||||
else {
|
else {
|
||||||
err.name = sname();
|
|
||||||
|
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
showErrorInfo(oss, err, loggerSettings.showTrace);
|
showErrorInfo(oss, err, loggerSettings.showTrace);
|
||||||
what_ = oss.str();
|
what_ = oss.str();
|
||||||
|
|
||||||
return *what_;
|
return *what_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,6 @@ struct Trace {
|
||||||
|
|
||||||
struct ErrorInfo {
|
struct ErrorInfo {
|
||||||
Verbosity level;
|
Verbosity level;
|
||||||
std::string name; // FIXME: rename
|
|
||||||
hintformat msg;
|
hintformat msg;
|
||||||
std::optional<ErrPos> errPos;
|
std::optional<ErrPos> errPos;
|
||||||
std::list<Trace> traces;
|
std::list<Trace> traces;
|
||||||
|
@ -162,8 +161,6 @@ public:
|
||||||
: err(e)
|
: err(e)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual const char* sname() const { return "BaseError"; }
|
|
||||||
|
|
||||||
#ifdef EXCEPTION_NEEDS_THROW_SPEC
|
#ifdef EXCEPTION_NEEDS_THROW_SPEC
|
||||||
~BaseError() throw () { };
|
~BaseError() throw () { };
|
||||||
const char * what() const throw () { return calcWhat().c_str(); }
|
const char * what() const throw () { return calcWhat().c_str(); }
|
||||||
|
@ -190,7 +187,6 @@ public:
|
||||||
{ \
|
{ \
|
||||||
public: \
|
public: \
|
||||||
using superClass::superClass; \
|
using superClass::superClass; \
|
||||||
virtual const char* sname() const override { return #newClass; } \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MakeError(Error, BaseError);
|
MakeError(Error, BaseError);
|
||||||
|
@ -210,8 +206,6 @@ public:
|
||||||
auto hf = hintfmt(args...);
|
auto hf = hintfmt(args...);
|
||||||
err.msg = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo));
|
err.msg = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual const char* sname() const override { return "SysError"; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,6 @@ public:
|
||||||
ExperimentalFeature missingFeature;
|
ExperimentalFeature missingFeature;
|
||||||
|
|
||||||
MissingExperimentalFeature(ExperimentalFeature);
|
MissingExperimentalFeature(ExperimentalFeature);
|
||||||
virtual const char * sname() const override
|
|
||||||
{
|
|
||||||
return "MissingExperimentalFeature";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,7 @@ static std::pair<std::optional<HashType>, bool> getParsedTypeAndSRI(std::string_
|
||||||
{
|
{
|
||||||
bool isSRI = false;
|
bool isSRI = false;
|
||||||
|
|
||||||
// Parse the has type before the separater, if there was one.
|
// Parse the hash type before the separator, if there was one.
|
||||||
std::optional<HashType> optParsedType;
|
std::optional<HashType> optParsedType;
|
||||||
{
|
{
|
||||||
auto hashRaw = splitPrefixTo(rest, ':');
|
auto hashRaw = splitPrefixTo(rest, ':');
|
||||||
|
|
|
@ -93,13 +93,11 @@ public:
|
||||||
|
|
||||||
std::string gitRev() const
|
std::string gitRev() const
|
||||||
{
|
{
|
||||||
assert(type == htSHA1);
|
|
||||||
return to_string(Base16, false);
|
return to_string(Base16, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string gitShortRev() const
|
std::string gitShortRev() const
|
||||||
{
|
{
|
||||||
assert(type == htSHA1);
|
|
||||||
return std::string(to_string(Base16, false), 0, 7);
|
return std::string(to_string(Base16, false), 0, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -357,7 +357,7 @@ Sink & operator << (Sink & sink, const Error & ex)
|
||||||
sink
|
sink
|
||||||
<< "Error"
|
<< "Error"
|
||||||
<< info.level
|
<< info.level
|
||||||
<< info.name
|
<< "Error" // removed
|
||||||
<< info.msg.str()
|
<< info.msg.str()
|
||||||
<< 0 // FIXME: info.errPos
|
<< 0 // FIXME: info.errPos
|
||||||
<< info.traces.size();
|
<< info.traces.size();
|
||||||
|
@ -426,11 +426,10 @@ Error readError(Source & source)
|
||||||
auto type = readString(source);
|
auto type = readString(source);
|
||||||
assert(type == "Error");
|
assert(type == "Error");
|
||||||
auto level = (Verbosity) readInt(source);
|
auto level = (Verbosity) readInt(source);
|
||||||
auto name = readString(source);
|
auto name = readString(source); // removed
|
||||||
auto msg = readString(source);
|
auto msg = readString(source);
|
||||||
ErrorInfo info {
|
ErrorInfo info {
|
||||||
.level = level,
|
.level = level,
|
||||||
.name = name,
|
|
||||||
.msg = hintformat(std::move(format("%s") % msg)),
|
.msg = hintformat(std::move(format("%s") % msg)),
|
||||||
};
|
};
|
||||||
auto havePos = readNum<size_t>(source);
|
auto havePos = readNum<size_t>(source);
|
||||||
|
|
|
@ -178,7 +178,7 @@ namespace nix {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(parseURL, parseFileURLWithQueryAndFragment) {
|
TEST(parseURL, parseFileURLWithQueryAndFragment) {
|
||||||
auto s = "file:///none/of/your/business";
|
auto s = "file:///none/of//your/business";
|
||||||
auto parsed = parseURL(s);
|
auto parsed = parseURL(s);
|
||||||
|
|
||||||
ParsedURL expected {
|
ParsedURL expected {
|
||||||
|
@ -186,7 +186,7 @@ namespace nix {
|
||||||
.base = "",
|
.base = "",
|
||||||
.scheme = "file",
|
.scheme = "file",
|
||||||
.authority = "",
|
.authority = "",
|
||||||
.path = "/none/of/your/business",
|
.path = "/none/of//your/business",
|
||||||
.query = (StringMap) { },
|
.query = (StringMap) { },
|
||||||
.fragment = "",
|
.fragment = "",
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ const static std::string userRegex = "(?:(?:" + unreservedRegex + "|" + pctEncod
|
||||||
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
|
const static std::string authorityRegex = "(?:" + userRegex + "@)?" + hostRegex + "(?::[0-9]+)?";
|
||||||
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
|
const static std::string pcharRegex = "(?:" + unreservedRegex + "|" + pctEncoded + "|" + subdelimsRegex + "|[:@])";
|
||||||
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
|
const static std::string queryRegex = "(?:" + pcharRegex + "|[/? \"])*";
|
||||||
const static std::string segmentRegex = "(?:" + pcharRegex + "+)";
|
const static std::string segmentRegex = "(?:" + pcharRegex + "*)";
|
||||||
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
|
const static std::string absPathRegex = "(?:(?:/" + segmentRegex + ")*/?)";
|
||||||
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
|
const static std::string pathRegex = "(?:" + segmentRegex + "(?:/" + segmentRegex + ")*/?)";
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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() && 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
|
||||||
|
|
|
@ -16,7 +16,7 @@ struct CmdEval : MixJSON, InstallableCommand
|
||||||
std::optional<std::string> apply;
|
std::optional<std::string> apply;
|
||||||
std::optional<Path> writeTo;
|
std::optional<Path> writeTo;
|
||||||
|
|
||||||
CmdEval()
|
CmdEval() : InstallableCommand(true /* supportReadOnlyMode */)
|
||||||
{
|
{
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "raw",
|
.longName = "raw",
|
||||||
|
|
|
@ -705,7 +705,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();
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -165,8 +165,8 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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)!");
|
||||||
|
|
|
@ -7,7 +7,9 @@ fi
|
||||||
|
|
||||||
clearStore
|
clearStore
|
||||||
|
|
||||||
repo=$TEST_ROOT/hg
|
# Intentionally not in a canonical form
|
||||||
|
# See https://github.com/NixOS/nix/issues/6195
|
||||||
|
repo=$TEST_ROOT/./hg
|
||||||
|
|
||||||
rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix
|
rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix
|
||||||
|
|
||||||
|
@ -28,6 +30,12 @@ echo world > $repo/hello
|
||||||
hg commit --cwd $repo -m 'Bla2'
|
hg commit --cwd $repo -m 'Bla2'
|
||||||
rev2=$(hg log --cwd $repo -r tip --template '{node}')
|
rev2=$(hg log --cwd $repo -r tip --template '{node}')
|
||||||
|
|
||||||
|
# Fetch an unclean branch.
|
||||||
|
echo unclean > $repo/hello
|
||||||
|
path=$(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).outPath")
|
||||||
|
[[ $(cat $path/hello) = unclean ]]
|
||||||
|
hg revert --cwd $repo --all
|
||||||
|
|
||||||
# Fetch the default branch.
|
# Fetch the default branch.
|
||||||
path=$(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).outPath")
|
path=$(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).outPath")
|
||||||
[[ $(cat $path/hello) = world ]]
|
[[ $(cat $path/hello) = world ]]
|
||||||
|
|
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
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
Loading…
Reference in a new issue