From c0c7cef16cb666524e4809834063cfee5262eca1 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Fri, 22 Dec 2023 21:03:47 -0500 Subject: Generate a UNIX man page (fixes #874) --- generate_auto_job | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 4 deletions(-) (limited to 'generate_auto_job') diff --git a/generate_auto_job b/generate_auto_job index 10d74d89..d4143d5a 100755 --- a/generate_auto_job +++ b/generate_auto_job @@ -134,6 +134,12 @@ BANNER = f'''// // clang-format off //''' +MAN_BANNER = f'''.\\" +.\\" This file is automatically generated by {whoami}. +.\\" Edits will be automatically overwritten if the build is +.\\" run in maintainer mode. +.\\" +''' def warn(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) @@ -156,9 +162,11 @@ class Main: SOURCES = [ # Keep this list in sync with CMakeLists.txt: auto_job_inputs whoami, + 'CMakeLists.txt', 'manual/_ext/qpdf.py', 'job.yml', 'manual/cli.rst', + 'manual/qpdf.1.in', ] # DESTS is a map to the output files this code generates. These # generated files, as well as those added to DESTS later in the @@ -172,6 +180,7 @@ class Main: 'schema': 'libqpdf/qpdf/auto_job_schema.hh', 'json_decl': 'libqpdf/qpdf/auto_job_json_decl.hh', 'json_init': 'libqpdf/qpdf/auto_job_json_init.hh', + 'man': 'manual/qpdf.1', # Others are added in top } # SUMS contains a checksum for each source and destination and is @@ -277,7 +286,7 @@ class Main: for k, v in hashes.items(): print(f'{k} {v}', file=f) - def generate_doc(self, df, f): + def generate_doc(self, df, f, f_man): st_top = 0 st_topic = 1 st_option = 2 @@ -324,6 +333,23 @@ class Main: return True return False + def manify(text): + lines = text.split('\n') + out = [] + last_was_item = False + for line in lines: + if line.startswith('- '): + last_was_item = True + out.append('.IP \\[bu]') + out.append(line[2:]) + elif last_was_item and line.startswith(' '): + out.append(line[2:]) + else: + last_was_item = False + out.append(line) + return '\n'.join(out) + + last_option_topic = '' lineno = 0 for line in df.readlines(): if help_lines == 0: @@ -366,6 +392,8 @@ class Main: self.all_topics.add(topic) print(f'ap.addHelpTopic("{topic}", "{short_text}",' f' R"({long_text})");', file=f) + print(f'.SH {topic.upper()} ({short_text})', file=f_man) + print(manify(long_text), file=f_man, end='') help_lines += 1 state = st_top elif state == st_option: @@ -389,6 +417,11 @@ class Main: self.jdata[option[2:]]['help'] = short_text print(f'ap.addOptionHelp("{option}", "{topic}",' f' "{short_text}", R"({long_text})");', file=f) + if last_option_topic != topic: + print('.PP\nRelated Options:', file=f_man) + last_option_topic = topic + print(f'.TP\n.B {option} \\-\\- {short_text}', file=f_man) + print(manify(long_text), file=f_man, end='') help_lines += 1 state = st_top if help_lines == 20: @@ -400,6 +433,11 @@ class Main: print('ap.addHelpFooter("For detailed help, visit' ' the qpdf manual: https://qpdf.readthedocs.io\\n");', file=f) print('}\n', file=f) + print('''.SH SEE ALSO +.PP +For a summary of qpdf's options, please run \\fBqpdf \\-\\-help\\fR. +A complete manual can be found at https://qpdf.readthedocs.io. +''', file=f_man, end='') for i in self.referenced_topics: if i not in self.all_topics: raise Exception(f'help text referenced --help={i}') @@ -412,6 +450,14 @@ class Main: warn(f'{whoami}: regenerating auto job files') self.validate(data) + version = None + with open('CMakeLists.txt', 'r') as f: + for line in f.readlines(): + if line.strip().startswith('VERSION '): + version = line.strip().split(' ')[1] + if version is None: + raise Exception("can't read version from CMakeLists.txt") + # Keep track of which options are help options since they are # handled specially. Add the built-in help options to tables # that we populate as we read job.yml since we won't encounter @@ -436,9 +482,15 @@ class Main: for i in self.init: print(i, file=f) with write_file(self.DESTS['help']) as f: - with open('manual/cli.rst', 'r') as df: - print(BANNER, file=f) - self.generate_doc(df, f) + with write_file(self.DESTS['man']) as f_man: + print(MAN_BANNER, file=f_man, end='') + with open('manual/qpdf.1.in', 'r') as m_in: + for line in m_in.readlines(): + line = line.replace('@PROJECT_VERSION@', version) + print(line, file=f_man, end='') + with open('manual/cli.rst', 'r') as df: + print(BANNER, file=f) + self.generate_doc(df, f, f_man) # Compute the json files after the config and arg parsing # files. We need to have full information about all the -- cgit v1.2.3-54-g00ecf