mirror of
https://github.com/NixOS/nix.git
synced 2024-11-26 08:42:30 +00:00
Merge remote-tracking branch 'upstream/master' into fix-url-format
This commit is contained in:
commit
3c220442ff
@ -72,6 +72,7 @@
|
||||
- [CLI guideline](contributing/cli-guideline.md)
|
||||
- [Release Notes](release-notes/release-notes.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.6 (2022-01-24)](release-notes/rl-2.6.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 @@
|
||||
# Release X.Y (202?-??-??)
|
||||
|
||||
* Various nix commands can now read expressions from stdin with `--file -`.
|
||||
|
||||
* `nix store make-content-addressable` has been renamed to `nix store
|
||||
make-content-addressed`.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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.
|
||||
|
@ -15,7 +15,7 @@ function _complete_nix {
|
||||
else
|
||||
COMPREPLY+=("$completion")
|
||||
fi
|
||||
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]/#\~/$HOME}" 2>/dev/null)
|
||||
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}" 2>/dev/null)
|
||||
__ltrim_colon_completions "$cur"
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand
|
||||
|
||||
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
||||
{
|
||||
return parseFlakeRef(_installable, absPath("."));
|
||||
return parseFlakeRefWithFragment(_installable, absPath(".")).first;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "registry.hh"
|
||||
#include "flake/flakeref.hh"
|
||||
#include "store-api.hh"
|
||||
#include "command.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
@ -59,6 +60,9 @@ MixEvalArgs::MixEvalArgs()
|
||||
fetchers::Attrs extraAttrs;
|
||||
if (to.subdir != "") extraAttrs["dir"] = to.subdir;
|
||||
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 "installables.hh"
|
||||
#include "util.hh"
|
||||
#include "command.hh"
|
||||
#include "attr-path.hh"
|
||||
#include "common-eval-args.hh"
|
||||
@ -100,6 +101,14 @@ MixFlakeOptions::MixFlakeOptions()
|
||||
lockFlags.inputOverrides.insert_or_assign(
|
||||
flake::parseInputPath(inputPath),
|
||||
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)
|
||||
{
|
||||
if (file) {
|
||||
completionType = ctAttrs;
|
||||
|
||||
evalSettings.pureEval = false;
|
||||
auto state = getEvalState();
|
||||
Expr *e = state->parseExprFromFile(
|
||||
@ -222,13 +233,14 @@ void SourceExprCommand::completeInstallable(std::string_view prefix)
|
||||
Value v2;
|
||||
state->autoCallFunction(*autoArgs, v1, v2);
|
||||
|
||||
completionType = ctAttrs;
|
||||
|
||||
if (v2.type() == nAttrs) {
|
||||
for (auto & i : *v2.attrs) {
|
||||
std::string name = i.name;
|
||||
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) {
|
||||
completeFlakeRef(evalState->store, prefix);
|
||||
} else {
|
||||
completionType = ctAttrs;
|
||||
|
||||
auto fragment = prefix.substr(hash + 1);
|
||||
auto flakeRefS = std::string(prefix.substr(0, hash));
|
||||
// FIXME: do tilde expansion.
|
||||
auto flakeRef = parseFlakeRef(flakeRefS, absPath("."));
|
||||
auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath("."));
|
||||
|
||||
auto evalCache = openEvalCache(*evalState,
|
||||
std::make_shared<flake::LockedFlake>(lockFlake(*evalState, flakeRef, lockFlags)));
|
||||
@ -271,8 +284,6 @@ void completeFlakeRefWithFragment(
|
||||
flake. */
|
||||
attrPathPrefixes.push_back("");
|
||||
|
||||
completionType = ctAttrs;
|
||||
|
||||
for (auto & attrPathPrefixS : attrPathPrefixes) {
|
||||
auto attrPathPrefix = parseAttrPath(*evalState, attrPathPrefixS);
|
||||
auto attrPathS = attrPathPrefixS + std::string(fragment);
|
||||
@ -346,16 +357,16 @@ DerivedPath Installable::toDerivedPath()
|
||||
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)
|
||||
{
|
||||
auto evalCache =
|
||||
std::make_shared<nix::eval_cache::EvalCache>(std::nullopt, state,
|
||||
[&]() { 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)
|
||||
{
|
||||
auto cursors = getCursors(state);
|
||||
@ -578,43 +589,21 @@ InstallableFlake::InstallableFlake(
|
||||
|
||||
std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation()
|
||||
{
|
||||
auto lockedFlake = getLockedFlake();
|
||||
auto attr = getCursor(*state);
|
||||
|
||||
auto cache = openEvalCache(*state, lockedFlake);
|
||||
auto root = cache->getRoot();
|
||||
auto attrPath = attr->getAttrPathStr();
|
||||
|
||||
Suggestions suggestions;
|
||||
if (!attr->isDerivation())
|
||||
throw Error("flake output attribute '%s' is not a derivation", attrPath);
|
||||
|
||||
for (auto & attrPath : getActualAttrPaths()) {
|
||||
debug("trying flake output attribute '%s'", attrPath);
|
||||
auto drvPath = attr->forceDerivation();
|
||||
|
||||
auto attrOrSuggestions = root->findAlongAttrPath(
|
||||
parseAttrPath(*state, attrPath),
|
||||
true
|
||||
);
|
||||
auto drvInfo = DerivationInfo {
|
||||
std::move(drvPath),
|
||||
attr->getAttr(state->sOutputName)->getString()
|
||||
};
|
||||
|
||||
if (!attrOrSuggestions) {
|
||||
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()));
|
||||
return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
|
||||
}
|
||||
|
||||
std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||
@ -626,33 +615,10 @@ std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
|
||||
|
||||
std::pair<Value *, Pos> InstallableFlake::toValue(EvalState & state)
|
||||
{
|
||||
auto lockedFlake = getLockedFlake();
|
||||
|
||||
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())
|
||||
);
|
||||
return {&getCursor(state)->forceValue(), noPos};
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||
std::vector<ref<eval_cache::AttrCursor>>
|
||||
InstallableFlake::getCursors(EvalState & state)
|
||||
{
|
||||
auto evalCache = openEvalCache(state,
|
||||
@ -660,21 +626,55 @@ InstallableFlake::getCursors(EvalState & state)
|
||||
|
||||
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()) {
|
||||
auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
|
||||
if (attr) res.push_back({*attr, attrPath});
|
||||
if (attr) res.push_back(ref(*attr));
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||
lockFlagsApplyConfig.applyNixConfig = true;
|
||||
if (!_lockedFlake) {
|
||||
flake::LockFlags lockFlagsApplyConfig = lockFlags;
|
||||
lockFlagsApplyConfig.applyNixConfig = true;
|
||||
_lockedFlake = std::make_shared<flake::LockedFlake>(lockFlake(*state, flakeRef, lockFlagsApplyConfig));
|
||||
}
|
||||
return _lockedFlake;
|
||||
@ -980,10 +980,10 @@ std::optional<FlakeRef> InstallablesCommand::getFlakeRefForCompletion()
|
||||
{
|
||||
if (_installables.empty()) {
|
||||
if (useDefaultInstallables())
|
||||
return parseFlakeRef(".", absPath("."));
|
||||
return parseFlakeRefWithFragment(".", absPath(".")).first;
|
||||
return {};
|
||||
}
|
||||
return parseFlakeRef(_installables.front(), absPath("."));
|
||||
return parseFlakeRefWithFragment(_installables.front(), absPath(".")).first;
|
||||
}
|
||||
|
||||
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
|
||||
|
@ -80,10 +80,10 @@ struct Installable
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual std::vector<std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>>
|
||||
virtual std::vector<ref<eval_cache::AttrCursor>>
|
||||
getCursors(EvalState & state);
|
||||
|
||||
std::pair<std::shared_ptr<eval_cache::AttrCursor>, std::string>
|
||||
virtual ref<eval_cache::AttrCursor>
|
||||
getCursor(EvalState & state);
|
||||
|
||||
virtual FlakeRef nixpkgsFlakeRef() const
|
||||
@ -180,9 +180,15 @@ struct InstallableFlake : InstallableValue
|
||||
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
|
||||
FlakeRef nixpkgsFlakeRef() const override;
|
||||
|
@ -306,9 +306,9 @@ Value * EvalCache::getRootValue()
|
||||
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(
|
||||
|
@ -33,7 +33,7 @@ public:
|
||||
EvalState & state,
|
||||
RootLoader rootLoader);
|
||||
|
||||
std::shared_ptr<AttrCursor> getRoot();
|
||||
ref<AttrCursor> getRoot();
|
||||
};
|
||||
|
||||
enum AttrType {
|
||||
@ -104,6 +104,8 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
std::string getString();
|
||||
|
@ -31,7 +31,7 @@ struct FileTransferSettings : Config
|
||||
R"(
|
||||
The timeout (in seconds) for establishing connections in the
|
||||
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{
|
||||
|
@ -127,11 +127,11 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||
if (flag.handler.arity == ArityAny) break;
|
||||
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
||||
}
|
||||
if (flag.completer)
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
anyCompleted = true;
|
||||
if (auto prefix = needsCompletion(*pos)) {
|
||||
anyCompleted = true;
|
||||
if (flag.completer)
|
||||
flag.completer(n, *prefix);
|
||||
}
|
||||
}
|
||||
args.push_back(*pos++);
|
||||
}
|
||||
if (!anyCompleted)
|
||||
@ -146,6 +146,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||
&& hasPrefix(name, std::string(*prefix, 2)))
|
||||
completions->add("--" + name, flag->description);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
auto i = longFlags.find(std::string(*pos, 2));
|
||||
if (i == longFlags.end()) return false;
|
||||
@ -187,10 +188,12 @@ bool Args::processArgs(const Strings & args, bool finish)
|
||||
{
|
||||
std::vector<std::string> ss;
|
||||
for (const auto &[n, s] : enumerate(args)) {
|
||||
ss.push_back(s);
|
||||
if (exp.completer)
|
||||
if (auto prefix = needsCompletion(s))
|
||||
if (auto prefix = needsCompletion(s)) {
|
||||
ss.push_back(*prefix);
|
||||
if (exp.completer)
|
||||
exp.completer(n, *prefix);
|
||||
} else
|
||||
ss.push_back(s);
|
||||
}
|
||||
exp.handler.fun(ss);
|
||||
expectedArgs.pop_front();
|
||||
@ -279,21 +282,22 @@ static void _completePath(std::string_view prefix, bool onlyDirs)
|
||||
{
|
||||
completionType = ctFilenames;
|
||||
glob_t globbuf;
|
||||
int flags = GLOB_NOESCAPE | GLOB_TILDE;
|
||||
int flags = GLOB_NOESCAPE;
|
||||
#ifdef GLOB_ONLYDIR
|
||||
if (onlyDirs)
|
||||
flags |= GLOB_ONLYDIR;
|
||||
#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) {
|
||||
if (onlyDirs) {
|
||||
auto st = lstat(globbuf.gl_pathv[i]);
|
||||
auto st = stat(globbuf.gl_pathv[i]);
|
||||
if (!S_ISDIR(st.st_mode)) continue;
|
||||
}
|
||||
completions->add(globbuf.gl_pathv[i]);
|
||||
}
|
||||
globfree(&globbuf);
|
||||
}
|
||||
globfree(&globbuf);
|
||||
}
|
||||
|
||||
void completePath(size_t, std::string_view prefix)
|
||||
@ -322,11 +326,6 @@ MultiCommand::MultiCommand(const Commands & commands_)
|
||||
.optional = true,
|
||||
.handler = {[=](std::string s) {
|
||||
assert(!command);
|
||||
if (auto prefix = needsCompletion(s)) {
|
||||
for (auto & [name, command] : commands)
|
||||
if (hasPrefix(name, *prefix))
|
||||
completions->add(name);
|
||||
}
|
||||
auto i = commands.find(s);
|
||||
if (i == commands.end()) {
|
||||
std::set<std::string> commandNames;
|
||||
@ -337,6 +336,11 @@ MultiCommand::MultiCommand(const Commands & commands_)
|
||||
}
|
||||
command = {s, i->second()};
|
||||
command->second->parent = this;
|
||||
}},
|
||||
.completer = {[&](size_t, std::string_view prefix) {
|
||||
for (auto & [name, command] : commands)
|
||||
if (hasPrefix(name, prefix))
|
||||
completions->add(name);
|
||||
}}
|
||||
});
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
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 st;
|
||||
|
@ -68,6 +68,9 @@ Path dirOf(const PathView path);
|
||||
following the final `/' (trailing slashes are removed). */
|
||||
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
|
||||
canonicalized. */
|
||||
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);
|
||||
|
||||
/* Get status of `path'. */
|
||||
struct stat stat(const Path & path);
|
||||
struct stat lstat(const Path & path);
|
||||
|
||||
/* Return true iff the given path exists. */
|
||||
|
@ -35,7 +35,7 @@ struct InstallableDerivedPath : Installable
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
@ -61,14 +61,18 @@ std::string resolveString(Store & store, const std::string & toResolve, const Bu
|
||||
|
||||
UnresolvedApp Installable::toApp(EvalState & state)
|
||||
{
|
||||
auto [cursor, attrPath] = getCursor(state);
|
||||
auto cursor = getCursor(state);
|
||||
auto attrPath = cursor->getAttrPath();
|
||||
|
||||
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") {
|
||||
auto [program, context] = cursor->getAttr("program")->getStringWithContext();
|
||||
|
||||
|
||||
std::vector<StorePathWithOutputs> context2;
|
||||
for (auto & [path, name] : context)
|
||||
context2.push_back({path, {name}});
|
||||
@ -101,7 +105,7 @@ UnresolvedApp Installable::toApp(EvalState & state)
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -528,6 +528,16 @@ struct CmdFlakeCheck : FlakeCommand
|
||||
}
|
||||
}
|
||||
|
||||
else if (name == "formatter") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
checkSystemName(attr.name, *attr.pos);
|
||||
checkApp(
|
||||
fmt("%s.%s", name, attr.name),
|
||||
*attr.value, *attr.pos);
|
||||
}
|
||||
}
|
||||
|
||||
else if (name == "packages" || name == "devShells") {
|
||||
state->forceAttrs(vOutput, pos);
|
||||
for (auto & attr : *vOutput.attrs) {
|
||||
@ -705,7 +715,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
|
||||
defaultTemplateAttrPathsPrefixes,
|
||||
lockFlags);
|
||||
|
||||
auto [cursor, attrPath] = installable.getCursor(*evalState);
|
||||
auto cursor = installable.getCursor(*evalState);
|
||||
|
||||
auto templateDirAttr = cursor->getAttr("path");
|
||||
auto templateDir = templateDirAttr->getString();
|
||||
@ -1011,6 +1021,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||
|| (attrPath.size() == 1 && (
|
||||
attrPath[0] == "defaultPackage"
|
||||
|| attrPath[0] == "devShell"
|
||||
|| attrPath[0] == "formatter"
|
||||
|| attrPath[0] == "nixosConfigurations"
|
||||
|| attrPath[0] == "nixosModules"
|
||||
|| attrPath[0] == "defaultApp"
|
||||
@ -1027,7 +1038,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
|
||||
}
|
||||
|
||||
else if (
|
||||
(attrPath.size() == 2 && (attrPath[0] == "defaultPackage" || attrPath[0] == "devShell"))
|
||||
(attrPath.size() == 2 && (attrPath[0] == "defaultPackage" || attrPath[0] == "devShell" || attrPath[0] == "formatter"))
|
||||
|| (attrPath.size() == 3 && (attrPath[0] == "checks" || attrPath[0] == "packages" || attrPath[0] == "devShells"))
|
||||
)
|
||||
{
|
||||
|
@ -177,8 +177,8 @@ Currently the `type` attribute can be one of the following:
|
||||
attribute `url`.
|
||||
|
||||
In URL form, the schema must be `http://`, `https://` or `file://`
|
||||
URLs and the extension must be `.zip`, `.tar`, `.tar.gz`, `.tar.xz`,
|
||||
`.tar.bz2` or `.tar.zst`.
|
||||
URLs and the extension must be `.zip`, `.tar`, `.tgz`, `.tar.gz`,
|
||||
`.tar.xz`, `.tar.bz2` or `.tar.zst`.
|
||||
|
||||
* `github`: A more efficient way to fetch repositories from
|
||||
GitHub. The following attributes are required:
|
||||
|
53
src/nix/fmt.cc
Normal file
53
src/nix/fmt.cc
Normal file
@ -0,0 +1,53 @@
|
||||
#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) {
|
||||
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.
|
||||
|
||||
)""
|
@ -165,8 +165,8 @@ struct CmdSearch : InstallableCommand, MixJSON
|
||||
}
|
||||
};
|
||||
|
||||
for (auto & [cursor, prefix] : installable->getCursors(*state))
|
||||
visit(*cursor, parseAttrPath(*state, prefix), true);
|
||||
for (auto & cursor : installable->getCursors(*state))
|
||||
visit(*cursor, cursor->getAttrPath(), true);
|
||||
|
||||
if (!json && !results)
|
||||
throw Error("no results for the given search term(s)!");
|
||||
|
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 = \
|
||||
flakes.sh \
|
||||
flakes-run.sh \
|
||||
ca/gc.sh \
|
||||
gc.sh \
|
||||
remote-store.sh \
|
||||
@ -79,6 +80,7 @@ nix_tests = \
|
||||
post-hook.sh \
|
||||
function-trace.sh \
|
||||
flake-local-settings.sh \
|
||||
fmt.sh \
|
||||
eval-store.sh \
|
||||
why-depends.sh \
|
||||
import-derivation.sh \
|
||||
|
Loading…
Reference in New Issue
Block a user