mirror of
https://github.com/NixOS/nix.git
synced 2025-11-19 16:59:35 +01:00
Add 'nix doc' command
This command generates HTML docs (using mdbook) for a flake.
This commit is contained in:
parent
f731443384
commit
0b337fca34
8 changed files with 251 additions and 44 deletions
|
|
@ -12,14 +12,14 @@ let
|
|||
|
||||
in
|
||||
|
||||
{ description ? null, extends ? [], options ? {}, config ? ({ config }: {}) } @ inArgs:
|
||||
{ doc ? null, extends ? [], options ? {}, config ? ({ config }: {}) } @ inArgs:
|
||||
|
||||
let thisModule = rec {
|
||||
type = "module";
|
||||
|
||||
_module = {
|
||||
inherit description extends options config;
|
||||
};
|
||||
inherit extends options config;
|
||||
} // (if doc != null then { inherit doc; } else {});
|
||||
|
||||
_allModules = [thisModule] ++ builtins.concatLists (map (mod: assert mod.type or "<untyped>" == "module"; mod._allModules) extends);
|
||||
|
||||
|
|
|
|||
|
|
@ -98,14 +98,14 @@ struct CmdBundle : InstallableCommand
|
|||
if (!evalState->isDerivation(*vRes))
|
||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||
|
||||
auto attr1 = vRes->attrs->find(evalState->sDrvPath);
|
||||
auto attr1 = vRes->attrs->get(evalState->sDrvPath);
|
||||
if (!attr1)
|
||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||
|
||||
PathSet context2;
|
||||
StorePath drvPath = store->parseStorePath(evalState->coerceToPath(*attr1->pos, *attr1->value, context2));
|
||||
|
||||
auto attr2 = vRes->attrs->find(evalState->sOutPath);
|
||||
auto attr2 = vRes->attrs->get(evalState->sOutPath);
|
||||
if (!attr2)
|
||||
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,23 @@ struct MixFlakeOptions : virtual Args, EvalCommand
|
|||
{ return {}; }
|
||||
};
|
||||
|
||||
class FlakeCommand : virtual Args, public MixFlakeOptions
|
||||
{
|
||||
std::string flakeUrl = ".";
|
||||
|
||||
public:
|
||||
|
||||
FlakeCommand();
|
||||
|
||||
FlakeRef getFlakeRef();
|
||||
|
||||
flake::Flake getFlake();
|
||||
|
||||
flake::LockedFlake lockFlake();
|
||||
|
||||
std::optional<FlakeRef> getFlakeRefForCompletion() override;
|
||||
};
|
||||
|
||||
/* How to handle derivations in commands that operate on store paths. */
|
||||
enum class OperateOn {
|
||||
/* Operate on the output path. */
|
||||
|
|
|
|||
101
src/nix/doc.cc
Normal file
101
src/nix/doc.cc
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#include "command.hh"
|
||||
#include "common-args.hh"
|
||||
#include "eval-cache.hh"
|
||||
#include "eval-inline.hh"
|
||||
|
||||
using namespace nix;
|
||||
using namespace nix::flake;
|
||||
|
||||
// FIXME: move
|
||||
StorePath buildDerivation(EvalState & state, Value & vDerivation)
|
||||
{
|
||||
state.forceValue(vDerivation);
|
||||
if (!state.isDerivation(vDerivation))
|
||||
throw Error("value did not evaluate to a derivation");
|
||||
|
||||
auto aDrvPath = vDerivation.attrs->get(state.sDrvPath);
|
||||
assert(aDrvPath);
|
||||
PathSet context;
|
||||
auto drvPath = state.store->parseStorePath(state.coerceToPath(*aDrvPath->pos, *aDrvPath->value, context));
|
||||
|
||||
state.store->buildPaths({{drvPath}});
|
||||
|
||||
auto aOutPath = vDerivation.attrs->get(state.sOutPath);
|
||||
assert(aOutPath);
|
||||
auto outPath = state.store->parseStorePath(state.coerceToPath(*aOutPath->pos, *aOutPath->value, context));
|
||||
|
||||
assert(state.store->isValidPath(outPath));
|
||||
|
||||
return outPath;
|
||||
}
|
||||
|
||||
struct CmdDoc : FlakeCommand
|
||||
{
|
||||
std::optional<Path> outLink = "flake-doc";
|
||||
bool printMarkdown = false;
|
||||
|
||||
CmdDoc()
|
||||
{
|
||||
// FIXME: cut&paste from 'nix build'.
|
||||
addFlag({
|
||||
.longName = "out-link",
|
||||
.shortName = 'o',
|
||||
.description = "path of the symlink to the build result",
|
||||
.labels = {"path"},
|
||||
.handler = {&outLink},
|
||||
.completer = completePath
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "no-link",
|
||||
.description = "do not create a symlink to the build result",
|
||||
.handler = {&outLink, {}},
|
||||
});
|
||||
|
||||
addFlag({
|
||||
.longName = "print-markdown",
|
||||
.description = "show markdown, don't generate an HTML book",
|
||||
.handler = {&this->printMarkdown, true},
|
||||
});
|
||||
}
|
||||
|
||||
void run(nix::ref<nix::Store> store) override
|
||||
{
|
||||
auto state = getEvalState();
|
||||
auto flake = std::make_shared<LockedFlake>(lockFlake());
|
||||
|
||||
auto vFlake = state->allocValue();
|
||||
flake::callFlake(*state, *flake, *vFlake);
|
||||
|
||||
auto vFun = state->allocValue();
|
||||
state->eval(state->parseExprFromString(
|
||||
#include "doc.nix.gen.hh"
|
||||
, "/"), *vFun);
|
||||
|
||||
auto vRes = state->allocValue();
|
||||
state->callFunction(*vFun, *vFlake, *vRes, noPos);
|
||||
state->forceAttrs(*vRes, noPos);
|
||||
|
||||
auto markdown = vRes->attrs->get(state->symbols.create("markdown"));
|
||||
assert(markdown);
|
||||
|
||||
if (printMarkdown) {
|
||||
logger->stdout(state->forceString(*markdown->value));
|
||||
return;
|
||||
}
|
||||
|
||||
auto mdbook = vRes->attrs->get(state->symbols.create("mdbook"));
|
||||
assert(mdbook);
|
||||
|
||||
// FIXME: ugly, needed for getFlake.
|
||||
evalSettings.pureEval = false;
|
||||
|
||||
auto path = buildDerivation(*state, *mdbook->value);
|
||||
|
||||
if (outLink)
|
||||
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
|
||||
store2->addPermRoot(path, absPath(*outLink));
|
||||
}
|
||||
};
|
||||
|
||||
static auto r1 = registerCommand<CmdDoc>("doc");
|
||||
94
src/nix/doc.nix
Normal file
94
src/nix/doc.nix
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
with builtins;
|
||||
|
||||
flake:
|
||||
|
||||
let
|
||||
|
||||
splitLines = s: filter (x: !isList x) (split "\n" s);
|
||||
|
||||
concatStrings = concatStringsSep "";
|
||||
|
||||
modules = flake.modules or {};
|
||||
|
||||
in
|
||||
|
||||
rec {
|
||||
markdown =
|
||||
# FIXME: split into multiple files.
|
||||
|
||||
"# Outputs\n\n"
|
||||
|
||||
+ concatStrings (map
|
||||
(outputName:
|
||||
" - `${outputName}` \n\n")
|
||||
(attrNames flake.outputs))
|
||||
|
||||
+ (if modules != {} then
|
||||
"# Modules\n\n"
|
||||
+ concatStrings (map
|
||||
(moduleName:
|
||||
let
|
||||
module = modules.${moduleName};
|
||||
in
|
||||
"## `${moduleName}`\n\n"
|
||||
+ (if module._module.doc or "" != ""
|
||||
then "### Synopsis\n\n${module._module.doc}\n\n"
|
||||
else "")
|
||||
+ (if module._module.options != {}
|
||||
then
|
||||
"### Options\n\n"
|
||||
+ concatStrings (map
|
||||
(optionName:
|
||||
let option = module._module.options.${optionName}; in
|
||||
" - `${optionName}` \n\n"
|
||||
+ concatStrings (map (l: " ${l}\n") (splitLines option.doc))
|
||||
+ "\n"
|
||||
)
|
||||
(attrNames module._module.options))
|
||||
else "")
|
||||
)
|
||||
(attrNames modules))
|
||||
else "");
|
||||
|
||||
mdbook =
|
||||
let
|
||||
nixpkgs = getFlake "nixpkgs";
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux; # FIXME
|
||||
in
|
||||
pkgs.runCommand "flake-doc"
|
||||
{ buildInputs = [ pkgs.mdbook ];
|
||||
}
|
||||
''
|
||||
mkdir $out
|
||||
mkdir -p book/src
|
||||
|
||||
cat > book/book.toml <<EOF
|
||||
[output.html]
|
||||
additional-css = ["custom.css"]
|
||||
EOF
|
||||
|
||||
cat > book/custom.css <<EOF
|
||||
h1:not(:first-of-type) {
|
||||
margin-top: 1.3em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0.7em;
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > book/src/SUMMARY.md <<EOF
|
||||
# Table of Contents
|
||||
|
||||
- [Overview](overview.md)
|
||||
EOF
|
||||
|
||||
cp ${toFile "flake.md" markdown} book/src/overview.md
|
||||
|
||||
mdbook build -d $out book
|
||||
'';
|
||||
}
|
||||
|
|
@ -20,14 +20,8 @@
|
|||
using namespace nix;
|
||||
using namespace nix::flake;
|
||||
|
||||
class FlakeCommand : virtual Args, public MixFlakeOptions
|
||||
FlakeCommand::FlakeCommand()
|
||||
{
|
||||
std::string flakeUrl = ".";
|
||||
|
||||
public:
|
||||
|
||||
FlakeCommand()
|
||||
{
|
||||
expectArgs({
|
||||
.label = "flake-url",
|
||||
.optional = true,
|
||||
|
|
@ -36,29 +30,28 @@ public:
|
|||
completeFlakeRef(getStore(), prefix);
|
||||
}}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
FlakeRef getFlakeRef()
|
||||
{
|
||||
FlakeRef FlakeCommand::getFlakeRef()
|
||||
{
|
||||
return parseFlakeRef(flakeUrl, absPath(".")); //FIXME
|
||||
}
|
||||
}
|
||||
|
||||
Flake getFlake()
|
||||
{
|
||||
Flake FlakeCommand::getFlake()
|
||||
{
|
||||
auto evalState = getEvalState();
|
||||
return flake::getFlake(*evalState, getFlakeRef(), lockFlags.useRegistries);
|
||||
}
|
||||
}
|
||||
|
||||
LockedFlake lockFlake()
|
||||
{
|
||||
LockedFlake FlakeCommand::lockFlake()
|
||||
{
|
||||
return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
||||
{
|
||||
std::optional<FlakeRef> FlakeCommand::getFlakeRefForCompletion()
|
||||
{
|
||||
return getFlakeRef();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static void printFlakeInfo(const Store & store, const Flake & flake)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,3 +31,5 @@ src/nix-env/user-env.cc: src/nix-env/buildenv.nix.gen.hh
|
|||
src/nix/develop.cc: src/nix/get-env.sh.gen.hh
|
||||
|
||||
src/nix-channel/nix-channel.cc: src/nix-channel/unpack-channel.nix.gen.hh
|
||||
|
||||
src/nix/doc.cc: src/nix/doc.nix.gen.hh
|
||||
|
|
|
|||
|
|
@ -45,12 +45,12 @@ struct CmdListOptions : InstallableCommand
|
|||
|
||||
state->forceAttrs(*option->value);
|
||||
|
||||
std::string description = ANSI_ITALIC "<no description>" ANSI_NORMAL;
|
||||
auto aDescription = option->value->attrs->get(state->symbols.create("description"));
|
||||
if (aDescription)
|
||||
std::string doc = ANSI_ITALIC "<no description>" ANSI_NORMAL;
|
||||
auto aDoc = option->value->attrs->get(state->symbols.create("doc"));
|
||||
if (aDoc)
|
||||
// FIXME: render markdown.
|
||||
description = state->forceString(*aDescription->value);
|
||||
logger->stdout(" " ANSI_BOLD "Description:" ANSI_NORMAL " %s", description);
|
||||
doc = state->forceString(*aDoc->value);
|
||||
logger->stdout(" " ANSI_BOLD "Description:" ANSI_NORMAL " %s", doc);
|
||||
|
||||
auto aValue = aFinal->value->attrs->get(option->name);
|
||||
assert(aValue);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue