diff options
Diffstat (limited to 'doc')
-rwxr-xr-x | doc/edit-sgml.py | 160 | ||||
-rwxr-xr-x | doc/extract-man-list.py | 90 | ||||
-rw-r--r-- | doc/meson.build | 167 | ||||
-rwxr-xr-x | doc/run-quiet.py | 36 |
4 files changed, 453 insertions, 0 deletions
diff --git a/doc/edit-sgml.py b/doc/edit-sgml.py new file mode 100755 index 0000000..e83c008 --- /dev/null +++ b/doc/edit-sgml.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# +# fontconfig/doc/edit-sgml.py +# +# Copyright © 2003 Keith Packard +# Copyright © 2020 Tim-Philipp Müller +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of the author(s) not be used in +# advertising or publicity pertaining to distribution of the software without +# specific, written prior permission. The authors make no +# representations about the suitability of this software for any purpose. It +# is provided "as is" without express or implied warranty. +# +# THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +import argparse +import sys +import re + +parser = argparse.ArgumentParser() +parser.add_argument('template') +parser.add_argument('input') +parser.add_argument('output') + +args = parser.parse_known_args() + +template_fn = args[0].template +output_fn = args[0].output +input_fn = args[0].input + +# ------------- +# Read template +# ------------- + +with open(template_fn, 'r', encoding='utf8') as f: + template_text = f.read() + +template_lines = template_text.strip().split('\n') + +# ------------------------------------- +# Read replacement sets from .fncs file +# ------------------------------------- + +replacement_sets = [] + +# TODO: also allow '-' for stdin +with open(input_fn, 'r', encoding='utf8') as f: + fncs_text = f.read() + +# split into replacement sets +fncs_chunks = fncs_text.strip().split('@@') + +for chunk in fncs_chunks: + # get rid of any preamble such as license and FcFreeTypeQueryAll decl in fcfreetype.fncs + start = chunk.find('@') + if start: + chunk = chunk[start:] + + # split at '@' and remove empty lines (keep it simple instead of doing fancy + # things with regular expression matches, we control the input after all) + lines = [line for line in chunk.split('@') if line.strip()] + + replacement_set = {} + + while lines: + tag = lines.pop(0).strip() + # FIXME: this hard codes the tag used in funcs.sgml - we're lazy + if tag.startswith('PROTOTYPE'): + text = '' + else: + text = lines.pop(0).strip() + if text.endswith('%'): + text = text[:-1] + ' ' + + replacement_set[tag] = text + + if replacement_set: + replacement_sets += [replacement_set] + +# ---------------- +# Open output file +# ---------------- + +if output_fn == '-': + fout = sys.stdout +else: + fout = open(output_fn, "w", encoding='utf8') + +# ---------------- +# Process template +# ---------------- + +def do_replace(template_lines, rep, tag_suffix=''): + skip_tag = None + skip_lines = False + loop_lines = [] + loop_tag = None + + for t_line in template_lines: + # This makes processing easier and is the case for our templates + if t_line.startswith('@') and not t_line.endswith('@'): + sys.exit('Template lines starting with @ are expected to end with @, please fix me!') + + if loop_tag: + loop_lines += [t_line] + + # Check if line starts with a directive + if t_line.startswith('@?'): + tag = t_line[2:-1] + tag_suffix + if skip_tag: + sys.exit('Recursive skipping not supported, please fix me!') + skip_tag = tag + skip_lines = tag not in rep + elif t_line.startswith('@:'): + if not skip_tag: + sys.exit('Skip else but no active skip list?!') + skip_lines = skip_tag in rep + elif t_line.startswith('@;'): + if not skip_tag: + sys.exit('Skip end but no active skip list?!') + skip_tag = None + skip_lines = False + elif t_line.startswith('@{'): + if loop_tag or tag_suffix != '': + sys.exit('Recursive looping not supported, please fix me!') + loop_tag = t_line[2:-1] + elif t_line.startswith('@}'): + tag = t_line[2:-1] + tag_suffix + if not loop_tag: + sys.exit('Loop end but no active loop?!') + if loop_tag != tag: + sys.exit(f'Loop end but loop tag mismatch: {loop_tag} != {tag}!') + loop_lines.pop() # remove loop end directive + suffix = '+' + while loop_tag + suffix in rep: + do_replace(loop_lines, rep, suffix) + suffix += '+' + loop_tag = None + loop_lines = [] + else: + if not skip_lines: + # special-case inline optional substitution (hard-codes specific pattern in funcs.sgml because we're lazy) + output_line = re.sub(r'@\?(RET)@@RET@@:@(void)@;@', lambda m: rep.get(m.group(1) + tag_suffix, m.group(2)), t_line) + # replace any substitution tags with their respective substitution text + output_line = re.sub(r'@(\w+)@', lambda m: rep.get(m.group(1) + tag_suffix, ''), output_line) + print(output_line, file=fout) + +# process template for each replacement set +for rep in replacement_sets: + do_replace(template_lines, rep) diff --git a/doc/extract-man-list.py b/doc/extract-man-list.py new file mode 100755 index 0000000..9298041 --- /dev/null +++ b/doc/extract-man-list.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# +# fontconfig/doc/extract-man-list.py +# +# Parses .fncs files and extracts list of man pages that will be generated +# +# Copyright © 2020 Tim-Philipp Müller +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of the author(s) not be used in +# advertising or publicity pertaining to distribution of the software without +# specific, written prior permission. The authors make no +# representations about the suitability of this software for any purpose. It +# is provided "as is" without express or implied warranty. +# +# THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +import sys +import re + +replacement_sets = [] + +# ------------------------------------- +# Read replacement sets from .fncs file +# ------------------------------------- + +def read_fncs_file(fn): + global replacement_sets + + with open(fn, 'r', encoding='utf8') as f: + fncs_text = f.read() + + # split into replacement sets + fncs_chunks = fncs_text.strip().split('@@') + + for chunk in fncs_chunks: + # get rid of any preamble such as license and FcFreeTypeQueryAll decl in fcfreetype.fncs + start = chunk.find('@') + if start: + chunk = chunk[start:] + + # split at '@' and remove empty lines (keep it simple instead of doing fancy + # things with regular expression matches, we control the input after all) + lines = [line for line in chunk.split('@') if line.strip()] + + replacement_set = {} + + while lines: + tag = lines.pop(0).strip() + # FIXME: this hard codes the tag used in funcs.sgml - we're lazy + if tag.startswith('PROTOTYPE'): + text = '' + else: + text = lines.pop(0).strip() + if text.endswith('%'): + text = text[:-1] + ' ' + + replacement_set[tag] = text + + if replacement_set: + replacement_sets += [replacement_set] + +# ---------------------------------------------------------------------------- +# Main +# ---------------------------------------------------------------------------- + +if len(sys.argv) < 2: + sys.exit('Usage: {} FILE1.FNCS [FILE2.FNCS...]'.format(sys.argv[0])) + +fout = sys.stdout + +for input_fn in sys.argv[1:]: + read_fncs_file(input_fn) + +# process template for each replacement set +for rep in replacement_sets: + if 'FUNC+' in rep: + man_page_title = rep.get('TITLE', rep['FUNC']) + else: + man_page_title = rep['FUNC'] + print(man_page_title) diff --git a/doc/meson.build b/doc/meson.build new file mode 100644 index 0000000..8b74c19 --- /dev/null +++ b/doc/meson.build @@ -0,0 +1,167 @@ +docbook2man = find_program('docbook2man', required: get_option('doc-man')) +docbook2txt = find_program('docbook2txt', required: get_option('doc-txt')) +docbook2pdf = find_program('docbook2pdf', required: get_option('doc-pdf')) +docbook2html = find_program('docbook2html', required: get_option('doc-html')) + +# docbook is very spammy +run_quiet = find_program('run-quiet.py') + +# .fncs files +doc_funcs_fncs = [ + 'fcatomic', + 'fcblanks', + 'fccache', + 'fccharset', + 'fcconfig', + 'fcconstant', + 'fcdircache', + 'fcfile', + 'fcfontset', + 'fcformat', + 'fcfreetype', + 'fcinit', + 'fclangset', + 'fcmatrix', + 'fcobjectset', + 'fcobjecttype', + 'fcpattern', + 'fcrange', + 'fcstring', + 'fcstrset', + 'fcvalue', + 'fcweight', +] + +fncs_files = [] +foreach f : doc_funcs_fncs + fncs_files += files('@0@.fncs'.format(f)) +endforeach + +man_pages = [] + +extract_man_list = find_program('extract-man-list.py') +man_list = run_command(extract_man_list, fncs_files, check: true).stdout().split() + +foreach m : man_list + man_pages += ['@0@.3'.format(m)] +endforeach + +# Generate sgml pages for funcs +edit_sgml = find_program('edit-sgml.py') + +# copy into build directory, it includes generated files from build directory +fontconfig_devel_sgml = configure_file(output: 'fontconfig-devel.sgml', + input: 'fontconfig-devel.sgml', + copy: true) + +fontconfig_user_sgml = configure_file(output: 'fontconfig-user.sgml', + input: 'fontconfig-user.sgml', + copy: true) + +version_conf = configuration_data() +version_conf.set('VERSION', meson.project_version()) + +configure_file(output: 'version.sgml', + input: 'version.sgml.in', + configuration: version_conf) + +confdir_conf = configuration_data() +confdir_conf.set('BASECONFIGDIR', fc_configdir) + +confdir_sgml = configure_file(output: 'confdir.sgml', + input: 'confdir.sgml.in', + configuration: confdir_conf) + +funcs_sgml = [] + +foreach f : doc_funcs_fncs + funcs_sgml += [custom_target('@0@.sgml'.format(f), + input: [files('func.sgml'), files('@0@.fncs'.format(f))], + output: '@0@.sgml'.format(f), + command: [edit_sgml, '@INPUT0@', '@INPUT1@', '@OUTPUT@'], + install: false)] +endforeach + +if docbook2man.found() + custom_target('devel-man', + input: [fontconfig_devel_sgml, funcs_sgml], + output: man_pages, + command: [run_quiet, docbook2man, '@INPUT0@', '--output', '@OUTDIR@'], + build_by_default: true, + install_dir: get_option('mandir') / 'man3', + install: true) + + # fonts.conf(5) + custom_target('fonts-conf-5-man-page', + input: [fontconfig_user_sgml], + output: 'fonts-conf.5', + command: [run_quiet, docbook2man, '@INPUT0@', '--output', '@OUTDIR@'], + install_dir: get_option('mandir') / 'man5', + build_by_default: true, + install: true) + + # Generate man pages for tools + foreach t : tools_man_pages + # docbook2man doesn't seem to have a --quiet option unfortunately + custom_target('@0@-man-page'.format(t), + input: '../@0@/@0@.sgml'.format(t), + output: '@0@.1'.format(t), + command: [run_quiet, docbook2man, '@INPUT@', '--output', '@OUTDIR@'], + install_dir: get_option('mandir') / 'man1', + install: true) + endforeach +endif + +if docbook2pdf.found() + custom_target('devel-pdf', + input: [fontconfig_devel_sgml, funcs_sgml], + output: 'fontconfig-devel.pdf', + command: [run_quiet, docbook2pdf, '@INPUT0@', '--output', '@OUTDIR@'], + build_by_default: true, + install_dir: get_option('datadir') / 'doc' / 'fontconfig', + install: true) + + custom_target('user-pdf', + input: [fontconfig_user_sgml, funcs_sgml], + output: 'fontconfig-user.pdf', + command: [run_quiet, docbook2pdf, '@INPUT0@', '--output', '@OUTDIR@'], + build_by_default: true, + install_dir: get_option('datadir') / 'doc' / 'fontconfig', + install: true) +endif + +if docbook2txt.found() + custom_target('devel-txt', + input: [fontconfig_devel_sgml, funcs_sgml], + output: 'fontconfig-devel.txt', + command: [run_quiet, docbook2txt, '@INPUT0@', '--output', '@OUTDIR@'], + build_by_default: true, + install_dir: get_option('datadir') / 'doc' / 'fontconfig', + install: true) + + custom_target('user-txt', + input: [fontconfig_user_sgml, funcs_sgml], + output: 'fontconfig-user.txt', + command: [run_quiet, docbook2txt, '@INPUT0@', '--output', '@OUTDIR@'], + build_by_default: true, + install_dir: get_option('datadir') / 'doc' / 'fontconfig', + install: true) +endif + +if docbook2html.found() + custom_target('devel-html', + input: [fontconfig_devel_sgml, funcs_sgml], + output: 'fontconfig-devel.html', + command: [run_quiet, docbook2html, '--nochunks', '@INPUT0@', '--output', '@OUTDIR@'], + build_by_default: true, + install_dir: get_option('datadir') / 'doc' / 'fontconfig', + install: true) + + custom_target('user-html', + input: [fontconfig_user_sgml, funcs_sgml], + output: 'fontconfig-user.html', + command: [run_quiet, docbook2html, '--nochunks', '@INPUT0@', '--output', '@OUTDIR@'], + build_by_default: true, + install_dir: get_option('datadir') / 'doc' / 'fontconfig', + install: true) +endif diff --git a/doc/run-quiet.py b/doc/run-quiet.py new file mode 100755 index 0000000..dff5dae --- /dev/null +++ b/doc/run-quiet.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# fontconfig/doc/run-quiet.py +# +# Runs command and discards anything it sends to stdout +# +# Copyright © 2020 Tim-Philipp Müller +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of the author(s) not be used in +# advertising or publicity pertaining to distribution of the software without +# specific, written prior permission. The authors make no +# representations about the suitability of this software for any purpose. It +# is provided "as is" without express or implied warranty. +# +# THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +# EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +import subprocess +import sys +import os + +if len(sys.argv) < 2: + sys.exit('Usage: {} PROGRAM [ARGS..]'.format(sys.argv[0])) + +command = sys.argv[1:] + +with open(os.devnull, 'w') as out: + sys.exit(subprocess.run(command, stdout=out).returncode) |