mirror of
https://github.com/NixOS/nix
synced 2025-06-27 21:01:16 +02:00
Merge pull request #13063 from jfly/add-nix-fmt-print-command-option
Add `nix formatter build` and `nix formatter run` commands
This commit is contained in:
commit
d155bb9012
12 changed files with 244 additions and 103 deletions
|
@ -605,8 +605,8 @@
|
||||||
''^tests/functional/flakes/prefetch\.sh$''
|
''^tests/functional/flakes/prefetch\.sh$''
|
||||||
''^tests/functional/flakes/run\.sh$''
|
''^tests/functional/flakes/run\.sh$''
|
||||||
''^tests/functional/flakes/show\.sh$''
|
''^tests/functional/flakes/show\.sh$''
|
||||||
''^tests/functional/fmt\.sh$''
|
''^tests/functional/formatter\.sh$''
|
||||||
''^tests/functional/fmt\.simple\.sh$''
|
''^tests/functional/formatter\.simple\.sh$''
|
||||||
''^tests/functional/gc-auto\.sh$''
|
''^tests/functional/gc-auto\.sh$''
|
||||||
''^tests/functional/gc-concurrent\.builder\.sh$''
|
''^tests/functional/gc-concurrent\.builder\.sh$''
|
||||||
''^tests/functional/gc-concurrent\.sh$''
|
''^tests/functional/gc-concurrent\.sh$''
|
||||||
|
|
|
@ -21,6 +21,7 @@ struct App
|
||||||
struct UnresolvedApp
|
struct UnresolvedApp
|
||||||
{
|
{
|
||||||
App unresolved;
|
App unresolved;
|
||||||
|
std::vector<BuiltPathWithResult> build(ref<Store> evalStore, ref<Store> store);
|
||||||
App resolve(ref<Store> evalStore, ref<Store> store);
|
App resolve(ref<Store> evalStore, ref<Store> store);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -129,18 +129,23 @@ UnresolvedApp InstallableValue::toApp(EvalState & state)
|
||||||
throw Error("attribute '%s' has unsupported type '%s'", cursor->getAttrPathStr(), type);
|
throw Error("attribute '%s' has unsupported type '%s'", cursor->getAttrPathStr(), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: move to libcmd
|
std::vector<BuiltPathWithResult> UnresolvedApp::build(ref<Store> evalStore, ref<Store> store)
|
||||||
App UnresolvedApp::resolve(ref<Store> evalStore, ref<Store> store)
|
|
||||||
{
|
{
|
||||||
auto res = unresolved;
|
|
||||||
|
|
||||||
Installables installableContext;
|
Installables installableContext;
|
||||||
|
|
||||||
for (auto & ctxElt : unresolved.context)
|
for (auto & ctxElt : unresolved.context)
|
||||||
installableContext.push_back(
|
installableContext.push_back(
|
||||||
make_ref<InstallableDerivedPath>(store, DerivedPath { ctxElt }));
|
make_ref<InstallableDerivedPath>(store, DerivedPath { ctxElt }));
|
||||||
|
|
||||||
auto builtContext = Installable::build(evalStore, store, Realise::Outputs, installableContext);
|
return Installable::build(evalStore, store, Realise::Outputs, installableContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: move to libcmd
|
||||||
|
App UnresolvedApp::resolve(ref<Store> evalStore, ref<Store> store)
|
||||||
|
{
|
||||||
|
auto res = unresolved;
|
||||||
|
|
||||||
|
auto builtContext = build(evalStore, store);
|
||||||
res.program = resolveString(*store, unresolved.program, builtContext);
|
res.program = resolveString(*store, unresolved.program, builtContext);
|
||||||
if (!store->isInStore(res.program))
|
if (!store->isInStore(res.program))
|
||||||
throw Error("app program '%s' is not in the Nix store", res.program);
|
throw Error("app program '%s' is not in the Nix store", res.program);
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
#include "nix/cmd/command.hh"
|
|
||||||
#include "nix/cmd/installable-value.hh"
|
|
||||||
#include "nix/expr/eval.hh"
|
|
||||||
#include "run.hh"
|
|
||||||
|
|
||||||
using namespace nix;
|
|
||||||
|
|
||||||
struct CmdFmt : SourceExprCommand {
|
|
||||||
std::vector<std::string> args;
|
|
||||||
|
|
||||||
CmdFmt() { expectArgs({.label = "args", .handler = {&args}}); }
|
|
||||||
|
|
||||||
std::string description() override {
|
|
||||||
return "reformat your code in the standard style";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string doc() override {
|
|
||||||
return
|
|
||||||
#include "fmt.md"
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
Category category() override { return catSecondary; }
|
|
||||||
|
|
||||||
Strings getDefaultFlakeAttrPaths() override {
|
|
||||||
return Strings{"formatter." + settings.thisSystem.get()};
|
|
||||||
}
|
|
||||||
|
|
||||||
Strings getDefaultFlakeAttrPathPrefixes() override { return Strings{}; }
|
|
||||||
|
|
||||||
void run(ref<Store> store) override
|
|
||||||
{
|
|
||||||
auto evalState = getEvalState();
|
|
||||||
auto evalStore = getEvalStore();
|
|
||||||
|
|
||||||
auto installable_ = parseInstallable(store, ".");
|
|
||||||
auto & installable = InstallableValue::require(*installable_);
|
|
||||||
auto app = installable.toApp(*evalState).resolve(evalStore, store);
|
|
||||||
|
|
||||||
Strings programArgs{app.program};
|
|
||||||
|
|
||||||
// Propagate arguments from the CLI
|
|
||||||
for (auto &i : args) {
|
|
||||||
programArgs.push_back(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release our references to eval caches to ensure they are persisted to disk, because
|
|
||||||
// we are about to exec out of this process without running C++ destructors.
|
|
||||||
evalState->evalCaches.clear();
|
|
||||||
|
|
||||||
execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
static auto r2 = registerCommand<CmdFmt>("fmt");
|
|
23
src/nix/formatter-build.md
Normal file
23
src/nix/formatter-build.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
R""(
|
||||||
|
|
||||||
|
# Description
|
||||||
|
|
||||||
|
`nix formatter build` builds the formatter specified in the flake.
|
||||||
|
|
||||||
|
Similar to [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md),
|
||||||
|
unless `--no-link` is specified, after a successful
|
||||||
|
build, it creates a symlink to the store path of the formatter. This symlink is
|
||||||
|
named `./result` by default; this can be overridden using the
|
||||||
|
`--out-link` option.
|
||||||
|
|
||||||
|
It always prints the command to standard output.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
* Build the formatter:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix formatter build
|
||||||
|
/nix/store/cb9w44vkhk2x4adfxwgdkkf5gjmm856j-treefmt/bin/treefmt
|
||||||
|
```
|
||||||
|
)""
|
|
@ -2,7 +2,7 @@ R""(
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
`nix fmt` calls the formatter specified in the flake.
|
`nix fmt` (an alias for `nix formatter run`) calls the formatter specified in the flake.
|
||||||
|
|
||||||
Flags can be forwarded to the formatter by using `--` followed by the flags.
|
Flags can be forwarded to the formatter by using `--` followed by the flags.
|
||||||
|
|
143
src/nix/formatter.cc
Normal file
143
src/nix/formatter.cc
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#include "nix/cmd/command.hh"
|
||||||
|
#include "nix/cmd/installable-value.hh"
|
||||||
|
#include "nix/expr/eval.hh"
|
||||||
|
#include "nix/store/local-fs-store.hh"
|
||||||
|
#include "nix/cmd/installable-derived-path.hh"
|
||||||
|
#include "run.hh"
|
||||||
|
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
|
struct CmdFormatter : NixMultiCommand
|
||||||
|
{
|
||||||
|
CmdFormatter()
|
||||||
|
: NixMultiCommand("formatter", RegisterCommand::getCommandsFor({"formatter"}))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "build or run the formatter";
|
||||||
|
}
|
||||||
|
|
||||||
|
Category category() override
|
||||||
|
{
|
||||||
|
return catSecondary;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rCmdFormatter = registerCommand<CmdFormatter>("formatter");
|
||||||
|
|
||||||
|
/** Common implementation bits for the `nix formatter` subcommands. */
|
||||||
|
struct MixFormatter : SourceExprCommand
|
||||||
|
{
|
||||||
|
Strings getDefaultFlakeAttrPaths() override
|
||||||
|
{
|
||||||
|
return Strings{"formatter." + settings.thisSystem.get()};
|
||||||
|
}
|
||||||
|
|
||||||
|
Strings getDefaultFlakeAttrPathPrefixes() override
|
||||||
|
{
|
||||||
|
return Strings{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CmdFormatterRun : MixFormatter, MixJSON
|
||||||
|
{
|
||||||
|
std::vector<std::string> args;
|
||||||
|
|
||||||
|
CmdFormatterRun()
|
||||||
|
{
|
||||||
|
expectArgs({.label = "args", .handler = {&args}});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "reformat your code in the standard style";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "formatter-run.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
Category category() override
|
||||||
|
{
|
||||||
|
return catSecondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(ref<Store> store) override
|
||||||
|
{
|
||||||
|
auto evalState = getEvalState();
|
||||||
|
auto evalStore = getEvalStore();
|
||||||
|
|
||||||
|
auto installable_ = parseInstallable(store, ".");
|
||||||
|
auto & installable = InstallableValue::require(*installable_);
|
||||||
|
auto app = installable.toApp(*evalState).resolve(evalStore, store);
|
||||||
|
|
||||||
|
Strings programArgs{app.program};
|
||||||
|
|
||||||
|
// Propagate arguments from the CLI
|
||||||
|
for (auto & i : args) {
|
||||||
|
programArgs.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release our references to eval caches to ensure they are persisted to disk, because
|
||||||
|
// we are about to exec out of this process without running C++ destructors.
|
||||||
|
evalState->evalCaches.clear();
|
||||||
|
|
||||||
|
execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rFormatterRun = registerCommand2<CmdFormatterRun>({"formatter", "run"});
|
||||||
|
|
||||||
|
struct CmdFormatterBuild : MixFormatter, MixOutLinkByDefault
|
||||||
|
{
|
||||||
|
CmdFormatterBuild() {}
|
||||||
|
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "build the current flake's formatter";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "formatter-build.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
Category category() override
|
||||||
|
{
|
||||||
|
return catSecondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(ref<Store> store) override
|
||||||
|
{
|
||||||
|
auto evalState = getEvalState();
|
||||||
|
auto evalStore = getEvalStore();
|
||||||
|
|
||||||
|
auto installable_ = parseInstallable(store, ".");
|
||||||
|
auto & installable = InstallableValue::require(*installable_);
|
||||||
|
auto unresolvedApp = installable.toApp(*evalState);
|
||||||
|
auto app = unresolvedApp.resolve(evalStore, store);
|
||||||
|
auto buildables = unresolvedApp.build(evalStore, store);
|
||||||
|
createOutLinksMaybe(buildables, store);
|
||||||
|
|
||||||
|
logger->cout("%s", app.program);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rFormatterBuild = registerCommand2<CmdFormatterBuild>({"formatter", "build"});
|
||||||
|
|
||||||
|
struct CmdFmt : CmdFormatterRun
|
||||||
|
{
|
||||||
|
void run(ref<Store> store) override
|
||||||
|
{
|
||||||
|
CmdFormatterRun::run(store);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rFmt = registerCommand<CmdFmt>("fmt");
|
|
@ -78,7 +78,7 @@ nix_sources = [config_priv_h] + files(
|
||||||
'env.cc',
|
'env.cc',
|
||||||
'eval.cc',
|
'eval.cc',
|
||||||
'flake.cc',
|
'flake.cc',
|
||||||
'fmt.cc',
|
'formatter.cc',
|
||||||
'hash.cc',
|
'hash.cc',
|
||||||
'log.cc',
|
'log.cc',
|
||||||
'ls.cc',
|
'ls.cc',
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
source common.sh
|
|
||||||
|
|
||||||
TODO_NixOS # Provide a `shell` variable. Try not to `export` it, perhaps.
|
|
||||||
|
|
||||||
clearStoreIfPossible
|
|
||||||
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 "forward"
|
|
||||||
|
|
||||||
cat << EOF > flake.nix
|
|
||||||
{
|
|
||||||
outputs = _: {
|
|
||||||
formatter.$system =
|
|
||||||
with import ./config.nix;
|
|
||||||
mkDerivation {
|
|
||||||
name = "formatter";
|
|
||||||
buildCommand = ''
|
|
||||||
mkdir -p \$out/bin
|
|
||||||
echo "#! ${shell}" > \$out/bin/formatter
|
|
||||||
cat \${./fmt.simple.sh} >> \$out/bin/formatter
|
|
||||||
chmod +x \$out/bin/formatter
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
# No arguments check
|
|
||||||
[[ "$(nix fmt)" = "Formatting(0):" ]]
|
|
||||||
# Argument forwarding check
|
|
||||||
nix fmt ./file ./folder | grep 'Formatting(2): ./file ./folder'
|
|
||||||
nix flake check
|
|
||||||
nix flake show | grep -P "package 'formatter'"
|
|
62
tests/functional/formatter.sh
Executable file
62
tests/functional/formatter.sh
Executable file
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
TODO_NixOS # Provide a `shell` variable. Try not to `export` it, perhaps.
|
||||||
|
|
||||||
|
clearStoreIfPossible
|
||||||
|
rm -rf "$TEST_HOME"/.cache "$TEST_HOME"/.config "$TEST_HOME"/.local
|
||||||
|
|
||||||
|
cp ./simple.nix ./simple.builder.sh ./formatter.simple.sh "${config_nix}" "$TEST_HOME"
|
||||||
|
|
||||||
|
cd "$TEST_HOME"
|
||||||
|
|
||||||
|
nix formatter --help | grep "build or run the formatter"
|
||||||
|
nix fmt --help | grep "reformat your code"
|
||||||
|
nix fmt run --help | grep "reformat your code"
|
||||||
|
nix fmt build --help | grep "build"
|
||||||
|
|
||||||
|
cat << EOF > flake.nix
|
||||||
|
{
|
||||||
|
outputs = _: {
|
||||||
|
formatter.$system =
|
||||||
|
with import ./config.nix;
|
||||||
|
mkDerivation {
|
||||||
|
name = "formatter";
|
||||||
|
buildCommand = ''
|
||||||
|
mkdir -p \$out/bin
|
||||||
|
echo "#! ${shell}" > \$out/bin/formatter
|
||||||
|
cat \${./formatter.simple.sh} >> \$out/bin/formatter
|
||||||
|
chmod +x \$out/bin/formatter
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# No arguments check
|
||||||
|
[[ "$(nix fmt)" = "Formatting(0):" ]]
|
||||||
|
[[ "$(nix formatter run)" = "Formatting(0):" ]]
|
||||||
|
|
||||||
|
# Argument forwarding check
|
||||||
|
nix fmt ./file ./folder | grep 'Formatting(2): ./file ./folder'
|
||||||
|
nix formatter run ./file ./folder | grep 'Formatting(2): ./file ./folder'
|
||||||
|
|
||||||
|
# Build checks
|
||||||
|
## Defaults to a ./result.
|
||||||
|
nix formatter build | grep ".\+/bin/formatter"
|
||||||
|
[[ -L ./result ]]
|
||||||
|
rm result
|
||||||
|
|
||||||
|
## Can prevent the symlink.
|
||||||
|
nix formatter build --no-link
|
||||||
|
[[ ! -e ./result ]]
|
||||||
|
|
||||||
|
## Can change the symlink name.
|
||||||
|
nix formatter build --out-link my-result | grep ".\+/bin/formatter"
|
||||||
|
[[ -L ./my-result ]]
|
||||||
|
rm ./my-result
|
||||||
|
|
||||||
|
# Flake outputs check.
|
||||||
|
nix flake check
|
||||||
|
nix flake show | grep -P "package 'formatter'"
|
|
@ -132,7 +132,7 @@ suites = [
|
||||||
'nix-copy-ssh-ng.sh',
|
'nix-copy-ssh-ng.sh',
|
||||||
'post-hook.sh',
|
'post-hook.sh',
|
||||||
'function-trace.sh',
|
'function-trace.sh',
|
||||||
'fmt.sh',
|
'formatter.sh',
|
||||||
'eval-store.sh',
|
'eval-store.sh',
|
||||||
'why-depends.sh',
|
'why-depends.sh',
|
||||||
'derivation-json.sh',
|
'derivation-json.sh',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue