1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-06 21:41:48 +02:00

Merge remote-tracking branch 'upstream/master' into fix-url-format

This commit is contained in:
John Ericson 2022-04-20 16:53:16 +00:00
commit 3c220442ff
26 changed files with 387 additions and 151 deletions

View file

@ -135,7 +135,7 @@ struct InstallableCommand : virtual Args, SourceExprCommand
std::optional<FlakeRef> getFlakeRefForCompletion() override
{
return parseFlakeRef(_installable, absPath("."));
return parseFlakeRefWithFragment(_installable, absPath(".")).first;
}
private:

View file

@ -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);
}}
});

View file

@ -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)

View file

@ -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;

View file

@ -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(

View file

@ -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();

View file

@ -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{

View file

@ -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);
}}
});

View file

@ -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;

View file

@ -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. */

View file

@ -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

View file

@ -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"))
)
{

View file

@ -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
View 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
View 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.
)""

View file

@ -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)!");