From eb0ccf7286a7167f52ed2183753aaf81f334c270 Mon Sep 17 00:00:00 2001 From: Robert Helgesson Date: Tue, 18 Dec 2018 18:09:56 +0100 Subject: [PATCH] docs: use nmd for generating documentation The nmd library is an external library for generating Nix-centric documentation. --- doc/default.nix | 343 ++++--------------------------------- doc/man-configuration.xml | 2 +- doc/manual.xml | 2 +- doc/options-to-docbook.xsl | 239 -------------------------- doc/overrides.css | 9 - doc/style.css | 271 ----------------------------- modules/manual.nix | 63 +------ 7 files changed, 42 insertions(+), 887 deletions(-) delete mode 100644 doc/options-to-docbook.xsl delete mode 100644 doc/overrides.css delete mode 100644 doc/style.css diff --git a/doc/default.nix b/doc/default.nix index c94d52c9..7abe278a 100644 --- a/doc/default.nix +++ b/doc/default.nix @@ -1,122 +1,33 @@ -{ pkgs, options, config, version, revision, extraSources ? [] }: - -with pkgs; +{ pkgs }: let + lib = pkgs.lib; - # Remove invisible and internal options. - optionsListVisible = lib.filter (opt: opt.visible && !opt.internal) (lib.optionAttrSetToDocList options); + nmdSrc = pkgs.fetchFromGitLab { + owner = "rycee"; + repo = "nmd"; + rev = "b57fc6657b6645086a286e62a05a1795f258daa6"; + sha256 = "1b6bdgn6d4awxi8al5hbw8vycxp4laf63l29rjrvxi2j2g69rgvc"; + }; - # Replace functions by the string - substFunction = x: - if builtins.isAttrs x then lib.mapAttrs (name: substFunction) x - else if builtins.isList x then map substFunction x - else if lib.isFunction x then "" - else x; + nmd = import nmdSrc { inherit pkgs; }; - # Generate DocBook documentation for a list of packages. This is - # what `relatedPackages` option of `mkOption` from - # ../../../lib/options.nix influences. - # - # Each element of `relatedPackages` can be either - # - a string: that will be interpreted as an attribute name from `pkgs`, - # - a list: that will be interpreted as an attribute path from `pkgs`, - # - an attrset: that can specify `name`, `path`, `package`, `comment` - # (either of `name`, `path` is required, the rest are optional). - genRelatedPackages = packages: - let - unpack = p: if lib.isString p then { name = p; } - else if lib.isList p then { path = p; } - else p; - describe = args: - let - name = args.name or (lib.concatStringsSep "." args.path); - path = args.path or [ args.name ]; - package = args.package or (lib.attrByPath path (throw "Invalid package attribute path `${toString path}'") pkgs); - in "" - + "pkgs.${name} (${package.meta.name})" - + lib.optionalString (!package.meta.available) " [UNAVAILABLE]" - + ": ${package.meta.description or "???"}." - + lib.optionalString (args ? comment) "\n${args.comment}" - # Lots of `longDescription's break DocBook, so we just wrap them into - + lib.optionalString (package.meta ? longDescription) "\n${package.meta.longDescription}" - + ""; - in "${lib.concatStringsSep "\n" (map (p: describe (unpack p)) packages)}"; + hmModulesDocs = nmd.buildModulesDocs { + modules = import ../modules/modules.nix { inherit lib pkgs; }; + moduleRootPaths = [ ./.. ]; + mkModuleUrl = path: + "https://github.com/rycee/home-manager/blob/master/${path}#blob-path"; + channelName = "home-manager"; + docBook.id = "home-manager-options"; + }; - optionsListDesc = lib.flip map optionsListVisible (opt: opt // { - # Clean up declaration sites to not refer to the NixOS source tree. - declarations = map stripAnyPrefixes opt.declarations; - } - // lib.optionalAttrs (opt ? example) { example = substFunction opt.example; } - // lib.optionalAttrs (opt ? default) { default = substFunction opt.default; } - // lib.optionalAttrs (opt ? type) { type = substFunction opt.type; } - // lib.optionalAttrs (opt ? relatedPackages) { relatedPackages = genRelatedPackages opt.relatedPackages; }); - - # We need to strip references to /nix/store/* from options, - # including any `extraSources` if some modules came from elsewhere, - # or else the build will fail. - # - # E.g. if some `options` came from modules in ${pkgs.customModules}/nix, - # you'd need to include `extraSources = [ pkgs.customModules ]` - prefixesToStrip = map (p: "${toString p}/") ([ ./.. ] ++ extraSources); - stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) prefixesToStrip; - - # Custom "less" that pushes up all the things ending in ".enable*" - # and ".package*" - optionLess = a: b: - let - ise = lib.hasPrefix "enable"; - isp = lib.hasPrefix "package"; - cmp = lib.splitByAndCompare ise lib.compare - (lib.splitByAndCompare isp lib.compare lib.compare); - in lib.compareLists cmp a.loc b.loc < 0; - - # Customly sort option list for the man page. - optionsList = lib.sort optionLess optionsListDesc; - - # Convert the list of options into an XML file. - optionsXML = builtins.toFile "options.xml" (builtins.toXML optionsList); - - optionsDocBook = runCommand "options-db.xml" - { - nativeBuildInputs = [ buildPackages.libxslt.bin ]; - } - '' - optionsXML=${optionsXML} - xsltproc \ - --stringparam program 'home-manager' \ - --stringparam revision '${revision}' \ - -o $out ${./options-to-docbook.xsl} $optionsXML - ''; - - sources = lib.sourceFilesBySuffices ./. [".xml"]; - - modulesDoc = builtins.toFile "modules.xml" '' -
- ${(lib.concatMapStrings (path: '' - - '') (lib.catAttrs "value" config.meta.doc))} -
- ''; - - generatedSources = runCommand "generated-docbook" {} '' - mkdir $out - ln -s ${modulesDoc} $out/modules.xml - ln -s ${optionsDocBook} $out/options-db.xml - printf "%s" "${version}" > $out/version - ''; - - copySources = - '' - cp -prd $sources/* . # */ - ln -s ${generatedSources} ./generated - chmod -R u+w . - ''; - - toc = builtins.toFile "toc.xml" - '' - + docs = nmd.buildDocBookDocs { + pathName = "home-manager"; + modulesDocs = [ hmModulesDocs ]; + documentsDirectory = ./.; + chunkToc = '' + @@ -124,206 +35,22 @@ let ''; + }; - manualXsltprocOptions = toString [ - "--param section.autolabel 1" - "--param section.label.includes.component.label 1" - "--stringparam html.stylesheet 'style.css overrides.css highlightjs/mono-blue.css'" - "--stringparam html.script './highlightjs/highlight.pack.js ./highlightjs/loader.js'" - "--param xref.with.number.and.title 1" - "--param toc.section.depth 3" - "--stringparam admon.style ''" - "--stringparam callout.graphics.extension .svg" - "--stringparam current.docid manual" - "--param chunk.section.depth 0" - "--param chunk.first.sections 1" - "--param use.id.as.filename 1" - "--stringparam generate.toc 'book toc appendix toc'" - "--stringparam chunk.toc ${toc}" - ]; +in - manual-combined = runCommand "home-manager-manual-combined" - { inherit sources; - nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; - meta.description = "The Home Manager manual as plain docbook XML"; - } - '' - ${copySources} +{ - xmllint --xinclude --output ./manual-combined.xml ./manual.xml - xmllint --xinclude --noxincludenode \ - --output ./man-pages-combined.xml ./man-pages.xml + options = { + json = hmModulesDocs.json.override { + path = "share/doc/home-manager/options.json"; + }; + }; - # outputs the context of an xmllint error output - # LEN lines around the failing line are printed - function context { - # length of context - local LEN=6 - # lines to print before error line - local BEFORE=4 + manPages = docs.manPages; - # xmllint output lines are: - # file.xml:1234: there was an error on line 1234 - while IFS=':' read -r file line rest; do - echo - if [[ -n "$rest" ]]; then - echo "$file:$line:$rest" - local FROM=$(($line>$BEFORE ? $line - $BEFORE : 1)) - # number lines & filter context - nl --body-numbering=a "$file" | sed -n "$FROM,+$LEN p" - else - if [[ -n "$line" ]]; then - echo "$file:$line" - else - echo "$file" - fi - fi - done - } - - function lintrng { - xmllint --debug --noout --nonet \ - --relaxng ${docbook5}/xml/rng/docbook/docbook.rng \ - "$1" \ - 2>&1 | context 1>&2 - # ^ redirect assumes xmllint doesn’t print to stdout - } - - lintrng manual-combined.xml - lintrng man-pages-combined.xml - - mkdir $out - cp manual-combined.xml $out/ - cp man-pages-combined.xml $out/ - ''; - - olinkDB = runCommand "manual-olinkdb" - { inherit sources; - nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; - } - '' - xsltproc \ - ${manualXsltprocOptions} \ - --stringparam collect.xref.targets only \ - --stringparam targets.filename "$out/manual.db" \ - --nonet \ - ${docbook5_xsl}/xml/xsl/docbook/xhtml/chunktoc.xsl \ - ${manual-combined}/manual-combined.xml - - cat > "$out/olinkdb.xml" < - - ]> - - - Allows for cross-referencing olinks between the manpages - and manual. - - - &manualtargets; - - EOF - ''; - -in rec { - inherit generatedSources; - - # The Home Manager options in JSON format. - optionsJSON = runCommand "options-json" - { meta.description = "List of Home Manager options in JSON format"; - } - '' - # Export list of options in different format. - dst=$out/share/doc/home-manager - mkdir -p $dst - - cp ${builtins.toFile "options.json" (builtins.unsafeDiscardStringContext (builtins.toJSON - (builtins.listToAttrs (map (o: { name = o.name; value = removeAttrs o ["name" "visible" "internal"]; }) optionsList)))) - } $dst/options.json - - mkdir -p $out/nix-support - echo "file json $dst/options.json" >> $out/nix-support/hydra-build-products - ''; # */ - - # Generate the Home Manager manual. - manual = runCommand "home-manager-manual" - { inherit sources; - nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; - meta.description = "The Home Manager manual in HTML format"; - allowedReferences = ["out"]; - } - '' - # Generate the HTML manual. - dst=$out/share/doc/home-manager - mkdir -p $dst - xsltproc \ - ${manualXsltprocOptions} \ - --stringparam target.database.document "${olinkDB}/olinkdb.xml" \ - --nonet --output $dst/ \ - ${docbook5_xsl}/xml/xsl/docbook/xhtml/chunktoc.xsl \ - ${manual-combined}/manual-combined.xml - - mkdir -p $dst/images/callouts - cp ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.svg $dst/images/callouts/ - - cp ${./style.css} $dst/style.css - cp ${./overrides.css} $dst/overrides.css - cp -r ${pkgs.documentation-highlighter} $dst/highlightjs - - mkdir -p $out/nix-support - echo "nix-build out $out" >> $out/nix-support/hydra-build-products - echo "doc manual $dst" >> $out/nix-support/hydra-build-products - ''; # */ - - - manualEpub = runCommand "home-manager-manual-epub" - { inherit sources; - buildInputs = [ libxml2.bin libxslt.bin zip ]; - } - '' - # Generate the epub manual. - dst=$out/share/doc/home-manager - - xsltproc \ - ${manualXsltprocOptions} \ - --stringparam target.database.document "${olinkDB}/olinkdb.xml" \ - --nonet --xinclude --output $dst/epub/ \ - ${docbook5_xsl}/xml/xsl/docbook/epub/docbook.xsl \ - ${manual-combined}/manual-combined.xml - - mkdir -p $dst/epub/OEBPS/images/callouts - cp -r ${docbook5_xsl}/xml/xsl/docbook/images/callouts/*.svg $dst/epub/OEBPS/images/callouts # */ - echo "application/epub+zip" > mimetype - manual="$dst/home-manager-manual.epub" - zip -0Xq "$manual" mimetype - cd $dst/epub && zip -Xr9D "$manual" * - - rm -rf $dst/epub - - mkdir -p $out/nix-support - echo "doc-epub manual $manual" >> $out/nix-support/hydra-build-products - ''; - - - # Generate the Home Manager manpages. - manpages = runCommand "home-manager-manpages" - { inherit sources; - nativeBuildInputs = [ buildPackages.libxml2.bin buildPackages.libxslt.bin ]; - allowedReferences = ["out"]; - } - '' - # Generate manpages. - mkdir -p $out/share/man - xsltproc --nonet \ - --param man.output.in.separate.dir 1 \ - --param man.output.base.dir "'$out/share/man/'" \ - --param man.endnotes.are.numbered 0 \ - --param man.break.after.slash 1 \ - --stringparam target.database.document "${olinkDB}/olinkdb.xml" \ - ${docbook5_xsl}/xml/xsl/docbook/manpages/docbook.xsl \ - ${manual-combined}/man-pages-combined.xml - ''; + manual = { + inherit (docs) html htmlOpenTool; + }; } diff --git a/doc/man-configuration.xml b/doc/man-configuration.xml index 1ddd3131..42962a75 100644 --- a/doc/man-configuration.xml +++ b/doc/man-configuration.xml @@ -26,7 +26,7 @@ You can use the following options in home-configuration.nix: - + See also diff --git a/doc/manual.xml b/doc/manual.xml index 014a4a7a..8ff81308 100644 --- a/doc/manual.xml +++ b/doc/manual.xml @@ -28,7 +28,7 @@ Configuration Options - + Tools diff --git a/doc/options-to-docbook.xsl b/doc/options-to-docbook.xsl deleted file mode 100644 index 0fef4e1d..00000000 --- a/doc/options-to-docbook.xsl +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - - - - - - - - Configuration Options - - - - - - - - - - - - - - - - - - Type: - - - - - (read only) - - - - - - - Default: - - - - - - - - Example: - - - - - - - - - - - - - - - Related packages: - - - - - - - - Declared by: - - - - - - - Defined by: - - - - - - - - - - - - - - - - - - - -'' -'' - - - - - - - - - - null - - - - - - - '''' - - - "" - - - - - - - - - - - - true - - - - - false - - - - - [ - - - - - ] - - - - - - - - - - { - - - = - ; - - } - - - - - (build of ) - - - - - - - - - - - - https://github.com/rycee/home-manager/blob//#blob-path - - - https://github.com/NixOS/nixpkgs/blob/master/ - - - https://github.com/NixOS/nixpkgs/blob// - - - - - https://github.com/NixOS/nixops/blob//nix/ - - - file:// - - - - - - <home-manager/> - - - <nixpkgs/> - - - <nixops/> - - - - - - - - - - - - - λ - - - - diff --git a/doc/overrides.css b/doc/overrides.css deleted file mode 100644 index 4c7d4a31..00000000 --- a/doc/overrides.css +++ /dev/null @@ -1,9 +0,0 @@ -.docbook .xref img[src^=images\/callouts\/], -.screen img, -.programlisting img { - width: 1em; -} - -.calloutlist img { - width: 1.5em; -} diff --git a/doc/style.css b/doc/style.css deleted file mode 100644 index 0db90781..00000000 --- a/doc/style.css +++ /dev/null @@ -1,271 +0,0 @@ -/* Copied from http://bakefile.sourceforge.net/, which appears - licensed under the GNU GPL. */ - - -/*************************************************************************** - Basic headers and text: - ***************************************************************************/ - -body -{ - font-family: "Nimbus Sans L", sans-serif; - background: white; - margin: 2em 1em 2em 1em; -} - -h1, h2, h3, h4 -{ - color: #005aa0; -} - -h1 /* title */ -{ - font-size: 200%; -} - -h2 /* chapters, appendices, subtitle */ -{ - font-size: 180%; -} - -/* Extra space between chapters, appendices. */ -div.chapter > div.titlepage h2, div.appendix > div.titlepage h2 -{ - margin-top: 1.5em; -} - -div.section > div.titlepage h2 /* sections */ -{ - font-size: 150%; - margin-top: 1.5em; -} - -h3 /* subsections */ -{ - font-size: 125%; -} - -div.simplesect h2 -{ - font-size: 110%; -} - -div.appendix h3 -{ - font-size: 150%; - margin-top: 1.5em; -} - -div.refnamediv h2, div.refsynopsisdiv h2, div.refsection h2 /* refentry parts */ -{ - margin-top: 1.4em; - font-size: 125%; -} - -div.refsection h3 -{ - font-size: 110%; -} - - -/*************************************************************************** - Examples: - ***************************************************************************/ - -div.example -{ - border: 1px solid #b0b0b0; - padding: 6px 6px; - margin-left: 1.5em; - margin-right: 1.5em; - background: #f4f4f8; - border-radius: 0.4em; - box-shadow: 0.4em 0.4em 0.5em #e0e0e0; -} - -div.example p.title -{ - margin-top: 0em; -} - -div.example pre -{ - box-shadow: none; -} - - -/*************************************************************************** - Screen dumps: - ***************************************************************************/ - -pre.screen, pre.programlisting -{ - border: 1px solid #b0b0b0; - padding: 3px 3px; - margin-left: 1.5em; - margin-right: 1.5em; - - background: #f4f4f8; - font-family: monospace; - border-radius: 0.4em; - box-shadow: 0.4em 0.4em 0.5em #e0e0e0; -} - -div.example pre.programlisting -{ - border: 0px; - padding: 0 0; - margin: 0 0 0 0; -} - -/*************************************************************************** - Notes, warnings etc: - ***************************************************************************/ - -.note, .warning -{ - border: 1px solid #b0b0b0; - padding: 3px 3px; - margin-left: 1.5em; - margin-right: 1.5em; - margin-bottom: 1em; - padding: 0.3em 0.3em 0.3em 0.3em; - background: #fffff5; - border-radius: 0.4em; - box-shadow: 0.4em 0.4em 0.5em #e0e0e0; -} - -div.note, div.warning -{ - font-style: italic; -} - -div.note h3, div.warning h3 -{ - color: red; - font-size: 100%; - padding-right: 0.5em; - display: inline; -} - -div.note p, div.warning p -{ - margin-bottom: 0em; -} - -div.note h3 + p, div.warning h3 + p -{ - display: inline; -} - -div.note h3 -{ - color: blue; - font-size: 100%; -} - -div.navfooter * -{ - font-size: 90%; -} - - -/*************************************************************************** - Links colors and highlighting: - ***************************************************************************/ - -a { text-decoration: none; } -a:hover { text-decoration: underline; } -a:link { color: #0048b3; } -a:visited { color: #002a6a; } - - -/*************************************************************************** - Table of contents: - ***************************************************************************/ - -div.toc -{ - font-size: 90%; -} - -div.toc dl -{ - margin-top: 0em; - margin-bottom: 0em; -} - - -/*************************************************************************** - Special elements: - ***************************************************************************/ - -tt, code -{ - color: #400000; -} - -.term -{ - font-weight: bold; - -} - -div.variablelist dd p, div.glosslist dd p -{ - margin-top: 0em; -} - -div.variablelist dd, div.glosslist dd -{ - margin-left: 1.5em; -} - -div.glosslist dt -{ - font-style: italic; -} - -.varname -{ - color: #400000; -} - -span.command strong -{ - font-weight: normal; - color: #400000; -} - -div.calloutlist table -{ - box-shadow: none; -} - -table -{ - border-collapse: collapse; - box-shadow: 0.4em 0.4em 0.5em #e0e0e0; -} - -table.simplelist -{ - text-align: left; - color: #005aa0; - border: 0; - padding: 5px; - background: #fffff5; - font-weight: normal; - font-style: italic; - box-shadow: none; - margin-bottom: 1em; -} - -div.navheader table, div.navfooter table { - box-shadow: none; -} - -div.affiliation -{ - font-style: italic; -} diff --git a/modules/manual.nix b/modules/manual.nix index 0c2681b3..1ee48b20 100644 --- a/modules/manual.nix +++ b/modules/manual.nix @@ -1,4 +1,4 @@ -{ config, lib, pkgs, baseModules, ... }: +{ config, lib, pkgs, ... }: with lib; @@ -6,55 +6,7 @@ let cfg = config.manual; - /* For the purpose of generating docs, evaluate options with each derivation - in `pkgs` (recursively) replaced by a fake with path "\${pkgs.attribute.path}". - It isn't perfect, but it seems to cover a vast majority of use cases. - Caveat: even if the package is reached by a different means, - the path above will be shown and not e.g. `${config.services.foo.package}`. */ - homeManagerManual = import ../doc { - inherit pkgs config; - version = "0.1"; - revision = "master"; - options = - let - scrubbedEval = evalModules { - modules = [ { nixpkgs.localSystem = config.nixpkgs.localSystem; } ] ++ baseModules; - args = (config._module.args) // { modules = [ ]; }; - specialArgs = { pkgs = scrubDerivations "pkgs" pkgs; }; - }; - scrubDerivations = namePrefix: pkgSet: mapAttrs - (name: value: - let wholeName = "${namePrefix}.${name}"; in - if isAttrs value then - scrubDerivations wholeName value - // (optionalAttrs (isDerivation value) { outPath = "\${${wholeName}}"; }) - else value - ) - pkgSet; - in scrubbedEval.options; - }; - - manualHtmlRoot = "${homeManagerManual.manual}/share/doc/home-manager/index.html"; - - helpScript = pkgs.writeShellScriptBin "home-manager-help" '' - #!${pkgs.bash}/bin/bash -e - - if [ -z "$BROWSER" ]; then - for candidate in xdg-open open w3m; do - BROWSER="$(type -P $candidate || true)" - if [ -x "$BROWSER" ]; then - break; - fi - done - fi - - if [ -z "$BROWSER" ]; then - echo "$0: unable to start a web browser; please set \$BROWSER" - exit 1 - fi - - exec "$BROWSER" ${manualHtmlRoot} - ''; + docs = import ../doc { inherit pkgs; }; in @@ -100,15 +52,10 @@ in config = { home.packages = mkMerge [ - (mkIf cfg.html.enable [ helpScript homeManagerManual.manual ]) - (mkIf cfg.manpages.enable [ homeManagerManual.manpages ]) - (mkIf cfg.json.enable [ homeManagerManual.optionsJSON ]) + (mkIf cfg.html.enable [ docs.manual.html docs.manual.htmlOpenTool ]) + (mkIf cfg.manpages.enable [ docs.manPages ]) + (mkIf cfg.json.enable [ docs.options.json ]) ]; }; - # To fix error during manpage build. - meta = { - maintainers = [ maintainers.rycee ]; - doc = builtins.toFile "nothingness" ""; - }; }