summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
authorTim-Philipp Müller <tim@centricular.com>2020-07-31 07:26:11 +0000
committerAkira TAGOH <akira@tagoh.org>2020-07-31 07:26:11 +0000
commit57a224f51d6c019e4ce5d75efb22f34a8330423e (patch)
treee3d7acfe511c07650db57c485c6dcf134e2c78a5 /doc
parent03aa12c75e117acb0d160212536f6f832e0dc8d9 (diff)
Add Meson build system
See https://mesonbuild.com
Diffstat (limited to 'doc')
-rwxr-xr-xdoc/edit-sgml.py160
-rwxr-xr-xdoc/extract-man-list.py90
-rw-r--r--doc/meson.build167
-rwxr-xr-xdoc/run-quiet.py36
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)