Skip to content

Commit f19b617

Browse files
authored
feat(wrapperModules.mdbook): per-item build command (#265)
__structuredAttrs refactor removed extraneous internal options
1 parent 60861d2 commit f19b617

File tree

2 files changed

+94
-136
lines changed

2 files changed

+94
-136
lines changed

docs/default.nix

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,10 @@ in
128128
name = "Intro";
129129
data = "numbered";
130130
path = "md/intro.md";
131-
src = pkgs.runCommand "intro.md" { src = ../README.md; } ''
132-
sed 's|# \[nix-wrapper-modules\](https://birdeehub.github.io/nix-wrapper-modules/)|# [nix-wrapper-modules](https://github.com/BirdeeHub/nix-wrapper-modules)|' < "$src" > "$out"
131+
src = "${placeholder "out"}/wrappers-lib/intro.md";
132+
build = ''
133+
mkdir -p $out/wrappers-lib
134+
sed 's|# \[nix-wrapper-modules\](https://birdeehub.github.io/nix-wrapper-modules/)|# [nix-wrapper-modules](https://github.com/BirdeeHub/nix-wrapper-modules)|' < '${../README.md}' > "$out/wrappers-lib/intro.md"
133135
'';
134136
}
135137
{
@@ -148,24 +150,27 @@ in
148150
name = "wlib";
149151
data = "numbered";
150152
path = "lib/wlib.md";
151-
src = pkgs.runCommand "wrapper-lib-docs" { src = ../lib/lib.nix; } ''
152-
${pkgs.nixdoc}/bin/nixdoc --category "" --description '`wlib` main set documentation' --file "$src" --prefix "wlib" >> $out
153+
src = "${placeholder "out"}/wrappers-lib/wlib.md";
154+
build = ''
155+
${pkgs.nixdoc}/bin/nixdoc --category "" --description '`wlib` main set documentation' --file '${../lib/lib.nix}' --prefix "wlib" >> $out/wrappers-lib/wlib.md
153156
'';
154157
}
155158
{
156159
name = "types";
157160
data = "numbered";
158161
path = "lib/types.md";
159-
src = pkgs.runCommand "wrapper-types-docs" { src = ../lib/types.nix; } ''
160-
${pkgs.nixdoc}/bin/nixdoc --category "types" --description '`wlib.types` set documentation' --file "$src" --prefix "wlib" >> $out
162+
src = "${placeholder "out"}/wrappers-lib/types.md";
163+
build = ''
164+
${pkgs.nixdoc}/bin/nixdoc --category "types" --description '`wlib.types` set documentation' --file '${../lib/types.nix}' --prefix "wlib" >> $out/wrappers-lib/types.md
161165
'';
162166
}
163167
{
164168
name = "dag";
165169
data = "numbered";
166170
path = "lib/dag.md";
167-
src = pkgs.runCommand "wrapper-dag-docs" { src = ../lib/dag.nix; } ''
168-
${pkgs.nixdoc}/bin/nixdoc --category "dag" --description '`wlib.dag` set documentation' --file "$src" --prefix "wlib" >> $out
171+
src = "${placeholder "out"}/wrappers-lib/dag.md";
172+
build = ''
173+
${pkgs.nixdoc}/bin/nixdoc --category "dag" --description '`wlib.dag` set documentation' --file '${../lib/dag.nix}' --prefix "wlib" >> $out/wrappers-lib/dag.md
169174
'';
170175
}
171176
];

wrapperModules/m/mdbook/module.nix

Lines changed: 81 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,19 @@ let
8686
this file will be linked to the location indicated by the `path` option.
8787
'';
8888
};
89+
options.build = lib.mkOption {
90+
type = lib.types.lines;
91+
default = "";
92+
description = ''
93+
If extra processing is required to generate a source file for this item,
94+
you may specify extra commands to run before the wrapper links it into place here.
95+
'';
96+
};
8997
}
9098
);
9199

92100
renderBook =
93-
subdir: book_src: summary: summaryVarname: bookVarname:
101+
bookSrc: summary: bookName:
94102
let
95103
renderItemSummary =
96104
node:
@@ -154,15 +162,6 @@ let
154162
in
155163
recsort 0;
156164

157-
sortedBook = sortBook summary;
158-
bookSrc =
159-
"${placeholder "out"}/" + subdir + "/" + lib.removePrefix "/" (lib.removeSuffix "/" book_src);
160-
summaryMD = builtins.concatStringsSep "\n" (
161-
map (
162-
v:
163-
builtins.addErrorContext "while rendering summary item ${builtins.toJSON v}" (renderItemSummary v)
164-
) sortedBook
165-
);
166165
mkLink =
167166
node:
168167
if node.src != null && node.path != null then
@@ -171,62 +170,27 @@ let
171170
in
172171
''
173172
mkdir -p "$(dirname ${p})"
173+
${node.build}
174174
ln -s ${lib.escapeShellArg node.src} ${lib.escapeShellArg "${bookSrc}/${lib.removePrefix "/" node.path}"}
175175
''
176176
else
177177
"";
178-
linkCmds = lib.pipe sortedBook [
179-
(map mkLink)
180-
(
181-
v:
182-
[
183-
"mkdir -p ${lib.escapeShellArg "${bookSrc}"}"
184-
(
185-
''{ [ -e "$''
186-
+ ''${summaryVarname}Path" ] && cat "$''
187-
+ ''${summaryVarname}Path" || echo "$''
188-
+ ''${summaryVarname}"; } > ${lib.escapeShellArg "${bookSrc}/SUMMARY.md"}''
189-
)
190-
(
191-
''json2toml "$''
192-
+ ''${bookVarname}Path" ${lib.escapeShellArg "${placeholder "out"}/${subdir}/book.toml"}''
193-
)
194-
]
195-
++ v
196-
)
197-
(builtins.concatStringsSep "\n")
198-
];
178+
sortedBook = sortBook summary;
199179
in
200180
{
201-
summaryMD = summaryMD;
202-
linkCmds = linkCmds;
181+
summaryMD = builtins.concatStringsSep "\n" (
182+
map (
183+
v:
184+
builtins.addErrorContext "while rendering summary item from book `${bookName}`: `${builtins.toJSON v}`" (
185+
renderItemSummary v
186+
)
187+
) sortedBook
188+
);
189+
linkCmds = builtins.concatStringsSep "\n" (map mkLink sortedBook);
203190
};
204191

205192
tomltype = (pkgs.formats.json { }).type;
206193

207-
sanitizeShellVar =
208-
s:
209-
let
210-
splitter = builtins.split "([^A-Za-z0-9_]+)";
211-
genStr = str: num: builtins.concatStringsSep "" (builtins.genList (_: str) num);
212-
body = lib.pipe s [
213-
splitter
214-
(map (
215-
v:
216-
if builtins.isList v then
217-
let
218-
bad = builtins.concatStringsSep "" v;
219-
in
220-
genStr "_" (builtins.stringLength bad)
221-
else
222-
v
223-
))
224-
(builtins.concatStringsSep "")
225-
];
226-
in
227-
# ensure valid first character
228-
if builtins.match "[A-Za-z_].*" body != null then body else "_" + body;
229-
230194
book-out-dir = "${top.config.binName}-book-dir";
231195
in
232196
{
@@ -239,25 +203,18 @@ in
239203
description = ''
240204
The books are generated to:
241205
242-
`''${passthru "out"}/''${config.book-out-dir}/''${name}`
206+
`''${placeholder "out"}/''${config.book-out-dir}/''${name}`
243207
'';
244208
};
245209
books = lib.mkOption {
246210
default = { };
247211
type = lib.types.attrsOf (
248212
lib.types.submodule (
249-
{ name, config, ... }:
250-
let
251-
pages =
252-
renderBook config.generated-book-subdir config.book.book.src config.summary
253-
config.generated-summary-varname
254-
config.generated-book-json-varname;
255-
in
213+
{ name, ... }:
256214
{
257215
options = {
258216
book = lib.mkOption {
259217
default = { };
260-
apply = x: lib.filterAttrsRecursive (_: v: !builtins.isFunction v && v != null) x;
261218
description = ''
262219
Values for the `book.toml` file for this book.
263220
@@ -378,37 +335,9 @@ in
378335
description = ''
379336
The directory within the wrapped derivation that contains the generated markdown for the book.
380337
381-
`''${passthru "out"}/''${config.books.<name>.generated-book-subdir}` is the root of this book.
338+
`''${placeholder "out"}/''${config.books.<name>.generated-book-subdir}` is the root of this book.
382339
'';
383340
};
384-
generatedSummary = lib.mkOption {
385-
type = lib.types.str;
386-
readOnly = true;
387-
internal = true;
388-
visible = false;
389-
default = pages.summaryMD;
390-
};
391-
buildCommands = lib.mkOption {
392-
type = lib.types.str;
393-
readOnly = true;
394-
internal = true;
395-
visible = false;
396-
default = pages.linkCmds;
397-
};
398-
generated-book-json-varname = lib.mkOption {
399-
type = lib.types.str;
400-
readOnly = true;
401-
internal = true;
402-
visible = false;
403-
default = "nix_generated_book_json_${sanitizeShellVar name}";
404-
};
405-
generated-summary-varname = lib.mkOption {
406-
type = lib.types.str;
407-
readOnly = true;
408-
internal = true;
409-
visible = false;
410-
default = "nix_generated_summary_${sanitizeShellVar name}";
411-
};
412341
};
413342
}
414343
)
@@ -473,40 +402,64 @@ in
473402
};
474403
config.exePath = config.exePath;
475404
}) config.books;
476-
drv =
477-
builtins.foldl' (acc: v: acc // v) { } (
478-
lib.mapAttrsToList (_: v: {
479-
${v.generated-summary-varname} = v.generatedSummary;
480-
${v.generated-book-json-varname} = builtins.toJSON v.book;
481-
}) config.books
482-
)
483-
// {
484-
passAsFile = builtins.concatLists (
485-
lib.mapAttrsToList (_: v: [
486-
v.generated-summary-varname
487-
v.generated-book-json-varname
488-
]) config.books
489-
);
490-
nativeBuildInputs = [ pkgs.remarshal ];
491-
buildPhase =
492-
"runHook preBuild\n"
493-
+ builtins.concatStringsSep "\n" (lib.mapAttrsToList (_: v: v.buildCommands) config.books)
494-
+ "\n"
495-
+ (
496-
if
497-
config.mainBook != null
498-
&& config.wrapperVariants."${config.mainBook}".enable or false == true
499-
&& config.books."${config.mainBook}".enable or false == true
500-
then
501-
''
502-
rm -f $out/bin/${config.binName}
503-
ln -s ${config.mainBook} $out/bin/${config.binName}
504-
''
505-
else
506-
""
405+
drv = {
406+
__structuredAttrs = true;
407+
nativeBuildInputs = [
408+
pkgs.remarshal
409+
pkgs.jq
410+
];
411+
bookData = lib.pipe config.books [
412+
(lib.filterAttrs (n: v: v.enable or false == true))
413+
(builtins.mapAttrs (
414+
n: v:
415+
let
416+
src = "${placeholder "out"}/${v.generated-book-subdir}/${
417+
lib.removePrefix "/" (lib.removeSuffix "/" (v.book.book.src or "src"))
418+
}";
419+
pages = renderBook src v.summary n;
420+
in
421+
{
422+
summary = pages.summaryMD;
423+
book = builtins.toJSON (
424+
lib.filterAttrsRecursive (_: v: !builtins.isFunction v && v != null) v.book
425+
);
426+
root = "${placeholder "out"}/${v.generated-book-subdir}";
427+
inherit src;
428+
build = pages.linkCmds;
429+
}
430+
))
431+
];
432+
buildPhase = # bash
433+
''
434+
runHook preBuild
435+
for book in $(jq -r '.bookData | keys[]' "$NIX_ATTRS_JSON_FILE"); do
436+
book_src="$(jq -r ".bookData[\"$book\"].src" "$NIX_ATTRS_JSON_FILE")"
437+
# this is the innermost dir this section handles we dont need more mkdir -p here
438+
mkdir -p "$book_src"
439+
# generate summary
440+
jq -r ".bookData[\"$book\"].summary" "$NIX_ATTRS_JSON_FILE" > "$book_src/SUMMARY.md"
441+
442+
# generate book.toml
443+
book_root="$(jq -r ".bookData[\"$book\"].root" "$NIX_ATTRS_JSON_FILE")"
444+
jq -r ".bookData[\"$book\"].book" "$NIX_ATTRS_JSON_FILE" | json2toml > "$book_root/book.toml"
445+
446+
# generate book contents
447+
eval "$(jq -r ".bookData[\"$book\"].build" "$NIX_ATTRS_JSON_FILE")"
448+
done
449+
''
450+
+
451+
lib.optionalString
452+
(
453+
config.mainBook != null
454+
&& config.wrapperVariants."${config.mainBook}".enable or false == true
455+
&& config.books."${config.mainBook}".enable or false == true
507456
)
508-
+ "\nrunHook postBuild";
509-
};
457+
''
458+
rm -f $out/bin/${config.binName}
459+
ln -s ${config.mainBook} $out/bin/${config.binName}
460+
''
461+
+ "\nrunHook postBuild";
462+
};
510463
passthru.book-out-dir = book-out-dir;
511464
package = lib.mkDefault pkgs.mdbook;
512465
meta.maintainers = [ wlib.maintainers.birdee ];
@@ -520,7 +473,7 @@ in
520473
you only have access to the other flags on these items at runtime.
521474
522475
To achieve greater runtime control, run the main executable with one of the generated books within the derivation
523-
as input yourself, either at runtime, or within the module via `''${passthru "out"}/''${config.book-out-dir}/''${name}`
476+
as input yourself, either at runtime, or within the module via `''${placeholder "out"}/''${config.book-out-dir}/''${name}`
524477
525478
Within the module, there is an option called `mainBook` to REPLACE the main executable with a symlink to the desired book generation script.
526479

0 commit comments

Comments
 (0)