Skip to content

Commit

Permalink
Merge pull request #1380 from nicholasyang2022/help2adoc
Browse files Browse the repository at this point in the history
[crmsh-4.6] Dev: doc: implement help2adoc (#1374)
  • Loading branch information
nicholasyang2022 authored Apr 11, 2024
2 parents 9eb2f75 + 497303f commit bfd7f06
Show file tree
Hide file tree
Showing 10 changed files with 567 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
doc/website-v1/gen
Makefile.in
autom4te.cache
Makefile
./Makefile
aclocal.m4
autoconf
autoheader
Expand Down
1 change: 1 addition & 0 deletions doc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
generated-sources/
24 changes: 24 additions & 0 deletions doc/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.PHONY: all clean subdirs

all: subdirs crm.8.html

generated-sources:
mkdir -p $@

generated-sources/Makefile: crm.8.adoc generated-sources
adocxt gen-makefile < $< > $@

subdirs: generated-sources/Makefile
$(MAKE) -C generated-sources all

generated-sources/crm.8.adoc: crm.8.adoc generated-sources
adocxt gen-include < $< > $@

generated-sources/profiles.adoc: profiles.adoc generated-sources
cp $< $@

crm.8.html: generated-sources/crm.8.adoc generated-sources/profiles.adoc subdirs
asciidoctor $<

clean:
$(RM) -r generated-sources crm.8.html
16 changes: 16 additions & 0 deletions doc/toolchain/Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM docker.io/alpine:3.19.0

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache \
py3-lxml \
py3-yaml \
py3-dateutil\
bash \
make \
asciidoctor


env PYTHONPATH=/opt/crmsh
env PATH=/opt/crmsh/bin:/opt/crmsh/doc/toolchain/bin:"${PATH}"

CMD cd /opt/crmsh/doc && make
112 changes: 112 additions & 0 deletions doc/toolchain/bin/adocxt
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python3


import re
import shlex
import sys
import typing


RE_FROM_CODE = re.compile(r'^\[\[([^,]+),[^,]*,From Code]]$')
RE_TAG = re.compile('^cmdhelp_(.*)$')
RE_SECTION_TITLE = re.compile('^=')
RE_FROM_CODE_OR_SECTION_TITLE=re.compile(r'^(?:\[\[([^,]+),[^,]*,From Code]]$|=)')

TAG_EXCLUDES = {
'cmdhelp_node_online',
'cmdhelp_node_standby',
'cmdhelp_root_report',
}


def generate_makefile(stdin, stdout):
tags = list()
for line in stdin:
found = RE_FROM_CODE.match(line)
if found:
tag = found.group(1)
if tag in TAG_EXCLUDES:
continue
command = extract_command(tag)
tags.append(tag)
stdout.write(tag)
stdout.write('.txt:\n\t')
stdout.write(shlex.join(command))
stdout.write(' > "$@"\n\n')
end(tags, stdout)


def extract_command(tag: str) -> typing.Sequence[str]:
found = RE_TAG.match(tag)
if not found:
raise RuntimeError(f'Invalid tag {tag}')
args = ['crm']
args.extend(found.group(1).split('_', 1))
args.append('--help-without-redirect')
return args


def end(tags: typing.Sequence[str], stdout):
stdout.write(
'%.adoc: %.txt\n\thelp2adoc "$<" > "$*".adoc\n\n'
)
stdout.write(
'.PHONY: clean all\n\nclean:\n\t$RM *.txt *.adoc\n\nall: '
)
for tag in tags:
stdout.write(tag)
stdout.write('.adoc ')
stdout.write('\n\n')


def generate_include(stdin, stdout):
tag = None
section_title = None
for line in stdin:
go_next_line = False
while not go_next_line:
match tag:
case None:
# initial state
found = RE_FROM_CODE.match(line)
if found:
found_tag = found.group(1)
if found_tag not in TAG_EXCLUDES:
tag = found_tag
stdout.write(line)
go_next_line = True
case _:
# found a tag
match section_title:
case None:
# waiting a section title
found = RE_SECTION_TITLE.match(line)
if found:
section_title = line
stdout.write(section_title)
print(f'include::{tag}.adoc[]\n', file=stdout)
break
case _:
# waiting for next section
found = RE_FROM_CODE_OR_SECTION_TITLE.match(line)
if found:
tag = None
section_title = None
else:
go_next_line = True


def main():
if len(sys.argv) != 2:
print(f'Usage: {sys.argv[0]} gen-makefile|gen-include', file=sys.stderr)
return 1
match sys.argv[1]:
case 'gen-makefile':
generate_makefile(sys.stdin, sys.stdout)
case 'gen-include':
generate_include(sys.stdin, sys.stdout)



if __name__ == '__main__':
sys.exit(main())
5 changes: 5 additions & 0 deletions doc/toolchain/bin/help2adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
set -e
SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
export PYTHONPATH=$(readlink -f "${SCRIPT_DIR}"/../lib)
python3 -m help2adoc.main "$@"
Empty file.
51 changes: 51 additions & 0 deletions doc/toolchain/lib/help2adoc/generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from .parser import Parser

import typing
import re


class AsciiDocGenerator(Parser):
USAGE_RE = re.compile('^usage:\\s*')

def __init__(self, output: typing.Callable[[str], None]):
self.output = output

def on_usage(self, text: str):
usage = text[self.USAGE_RE.match(text).end():]
self.output('Usage:\n\n ')
self.output(usage)
self.output('\n\n')

def on_paragraph(self, text: str):
self.output(self.escape(text))
self.output('\n\n')

def enter_options(self):
self.output('Options:\n\n')

def exit_options(self):
self.output('\n')

def on_option(self, option: str, help: str):
self.output('* `+++')
self.output(option)
self.output('+++`: ')
self.output(self.escape(help))
self.output('\n\n')

def enter_option_group(self, name: str):
self.output(name)
self.output(':\n\n')

def exit_option_group(self, name: str):
self.output('\n')

def enter_description(self):
pass

def exit_description(self):
pass

def escape(self, text: str):
# TODO
return text
28 changes: 28 additions & 0 deletions doc/toolchain/lib/help2adoc/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from .parser import lexer, LookAheadIterator
from .generator import AsciiDocGenerator

from argparse import ArgumentParser
import sys

def main():
ap = ArgumentParser('help2adoc')
ap.add_argument('file')
args = ap.parse_args()
with open(args.file, 'r') as f:
tokens = LookAheadIterator(lexer(f))
AsciiDocGenerator(sys.stdout.write).parse_help(tokens)
token = tokens.lookahead()
if token is None:
return
epilog_start = token.lineno
f.seek(0)
for i in range(epilog_start):
next(f)
print('....')
for line in f:
print(line, end='')
print('....')


if __name__ == '__main__':
main()
Loading

0 comments on commit bfd7f06

Please sign in to comment.