From 723ddadf92d0e8d54197424e1a0cb839e9d3d6b6 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Mon, 8 Apr 2024 21:43:38 -0600 Subject: [PATCH] docs: generalize manpage generation script as json-to-tree.py This should be capable of replacing every invocation of nix eval --write-to. Change-Id: I60387bc9b0fc54a91244eddb639beaa64d705878 --- doc/manual/generate-manpage.py | 63 ---------------------------------- doc/manual/json-to-tree.py | 61 ++++++++++++++++++++++++++++++++ doc/manual/meson.build | 41 +++++++++++++--------- 3 files changed, 85 insertions(+), 80 deletions(-) delete mode 100755 doc/manual/generate-manpage.py create mode 100755 doc/manual/json-to-tree.py diff --git a/doc/manual/generate-manpage.py b/doc/manual/generate-manpage.py deleted file mode 100755 index f082caffa..000000000 --- a/doc/manual/generate-manpage.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 - -""" -This script is a helper for this project's Meson buildsystem, to generate -manpages as mdbook markdown for nix3 CLI commands. It is an analogue to an -inline sequence of bash commands in the autoconf+Make buildsystem, which works -around a limitation in `nix eval --write-to`, in that it refuses to write to any -directory that already exists. - -Basically, this script is a glorified but hopefully-more-robust version of: -$ rm -rf $output -$ nix eval --write-to $output.tmp --expr 'import doc/manual/generate-manpage.nix true - (builtins.readFile ./generate-manpage.nix)' -$ mv $output.tmp $output -""" - -import argparse -import os.path -import shlex -import shutil -import subprocess -import sys -import tempfile - -name = 'generate-manpage.py' - -def log(*args, **kwargs): - kwargs['file'] = sys.stderr - return print(f'{name}:', *args, **kwargs) - -def main(): - parser = argparse.ArgumentParser(name) - parser.add_argument('--nix', required=True, help='Full path to the nix binary to use') - parser.add_argument('-o', '--output', required=True, help='Output directory') - parser.add_argument('--generator', required=True, help='Path to generate-manpage.nix') - parser.add_argument('--cli-json', required=True, help='Path to the nix.json output from Nix') - args = parser.parse_args() - - with tempfile.TemporaryDirectory() as tempdir: - - temp_out = os.path.join(tempdir, 'new-cli') - - nix_args = [ - args.nix, - '--experimental-features', - 'nix-command', - 'eval', - '-I', 'nix/corepkgs=corepkgs', - '--store', 'dummy://', - '--impure', - '--raw', - '--write-to', temp_out, - '--expr', - f'import {args.generator} true (builtins.readFile {args.cli_json})', - ] - - log('generating nix3 man pages with', shlex.join(nix_args)) - - subprocess.check_call(nix_args) - - shutil.copytree(temp_out, args.output, dirs_exist_ok=True) - -sys.exit(main()) diff --git a/doc/manual/json-to-tree.py b/doc/manual/json-to-tree.py new file mode 100755 index 000000000..27c8e417c --- /dev/null +++ b/doc/manual/json-to-tree.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +""" +This script is a helper for this project's Meson buildsystem, to replace its +usage of `nix eval --write-to`. Writing a JSON object as a nested directory +tree is more generic, easier to maintain, and far, far less cursed. Nix +has 'good' support for JSON output. Let's just use it. +""" + +import argparse +from pathlib import Path +import json +import sys + +name = 'json-to-tree.py' + +def log(*args, **kwargs): + kwargs['file'] = sys.stderr + return print(f'{name}:', *args, **kwargs) + +def write_dict_to_directory(current_directory: Path, data: dict, files_written=0): + current_directory.mkdir(parents=True, exist_ok=True) + for key, value in data.items(): + nested_path = current_directory / key + match value: + case dict(nested_data): + files_written += write_dict_to_directory(nested_path, nested_data) + + case str(content): + nested_path.write_text(content) + files_written += 1 + + case rest: + assert False, \ + f'should have been called on a dict or string, not {type(rest)=}\n\t{rest=}' + + return files_written + +def main(): + parser = argparse.ArgumentParser(name) + parser.add_argument('-i', '--input', type=argparse.FileType('r'), default='-', + help='The JSON input to operate on and output as a directory tree', + ) + parser.add_argument('-o', '--output', type=Path, required=True, + help='The place to put the directory tree', + ) + args = parser.parse_args() + + json_string = args.input.read() + + try: + data = json.loads(json_string) + except json.JSONDecodeError: + log(f'could not decode JSON from input: {json_string}') + raise + + + files_written = write_dict_to_directory(args.output, data) + log(f'wrote {files_written} files') + +sys.exit(main()) diff --git a/doc/manual/meson.build b/doc/manual/meson.build index dafec9a4c..55373d15c 100644 --- a/doc/manual/meson.build +++ b/doc/manual/meson.build @@ -7,13 +7,13 @@ nix_env_for_docs = { } nix_for_docs = [ nix, '--experimental-features', 'nix-command' ] -nix_eval_for_docs = nix_for_docs + [ +nix_eval_for_docs_common = nix_for_docs + [ 'eval', '-I', 'nix/corepkgs=corepkgs', '--store', 'dummy://', '--impure', - '--raw', ] +nix_eval_for_docs = nix_eval_for_docs_common + '--raw' nix_conf_file_json = custom_target( command : nix_for_docs + [ 'show-config', '--json' ], @@ -132,27 +132,34 @@ nix3_cli_json = custom_target( capture : true, output : 'nix.json', ) -# `nix eval --write-to` is rather picky, and refuses to write to a directory that exists at all. -# This is fine for clean builds because it creates the directory and then is never run again, -# but during development if nix.json changes, then using `nix eval --write-to` will error, -# since that part of the build directory already exists. -# Thus, another Python script. It runs the relevant `nix eval --write-to` command in a temporary -# directory, and then copies that tree to our build output directory. + +# Intermediate step for manpage generation. +# This splorks the output of generate-manpage.nix as JSON, +# which gets written as a directory tree below. +nix3_cli_files_json = custom_target( + command : nix_eval_for_docs_common + [ + '--json', + '--expr', + 'import @INPUT0@ true (builtins.readFile @INPUT1@)', + ], + input : [ + 'generate-manpage.nix', + nix3_cli_json, + ], + capture : true, + output : 'new-cli.json', + env : nix_env_for_docs, +) nix3_cli_files = custom_target( command : [ python, '@INPUT0@', - '--nix=@INPUT1@', - '--generator=@INPUT2@', - '--cli-json=@INPUT3@', - '--output=@OUTPUT@', + '-i', '@INPUT1@', + '-o', '@OUTPUT@', ], input : [ - 'generate-manpage.py', # INPUT0 - nix, # INPUT1 - 'generate-manpage.nix', # INPUT2 - nix3_cli_json, # INPUT3 - 'utils.nix', + 'json-to-tree.py', + nix3_cli_files_json, ], output : 'new-cli', )