[gnomemm-website] Add Meson support



commit 20197989c2b4d6ea533a0b1adc8875f1c673bced
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date:   Fri Jan 14 16:23:12 2022 +0100

    Add Meson support
    
    The website can be built with Meson or Autotools.

 Makefile.am                 |   9 ++
 docs/LINGUAS                |  13 +++
 docs/meson.build            | 139 +++++++++++++++++++++++++++
 meson.build                 |  87 +++++++++++++++++
 meson_options.txt           |   4 +
 tools/extra-dist-cmd.py     |  95 +++++++++++++++++++
 tools/website-custom-cmd.py | 225 ++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 572 insertions(+)
---
diff --git a/Makefile.am b/Makefile.am
index 9b82b26..392d43e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,3 +4,12 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-warnings=fatal
 SUBDIRS = docs
 
 dist_noinst_SCRIPTS = autogen.sh
+
+# Distribute files needed when building with Meson.
+EXTRA_DIST = \
+  meson.build \
+  meson_options.txt \
+  docs/meson.build \
+  docs/LINGUAS \
+  tools/extra-dist-cmd.py \
+  tools/website-custom-cmd.py
diff --git a/docs/LINGUAS b/docs/LINGUAS
new file mode 100644
index 0000000..ed421c1
--- /dev/null
+++ b/docs/LINGUAS
@@ -0,0 +1,13 @@
+# Please keep this list sorted alphabetically.
+#
+cs
+de
+el
+es
+fr
+hu
+ko
+pt_BR
+sl
+sv
+zh_CN
diff --git a/docs/meson.build b/docs/meson.build
new file mode 100644
index 0000000..edc694c
--- /dev/null
+++ b/docs/meson.build
@@ -0,0 +1,139 @@
+# docs
+
+# input: install_datadir, website_custom_cmd, python3
+# output: can_parse_and_validate, build_translations_by_default, can_build_translations
+
+# xsltproc is required by website_custom_cmd html.
+xsltproc = find_program('xsltproc', required: true)
+
+xmllint = find_program('xmllint', required: false)
+can_parse_and_validate = xmllint.found()
+validate = get_option('validation') ? 'true' : 'false'
+
+can_build_translations = find_program('msgfmt', required: false).found() and \
+                         find_program('itstool', required: false).found()
+build_translations_by_default = get_option('build-translations')
+
+xml_files = [
+  'index.docbook', # Must be the first element in the list
+  'books.xml',
+  'bugs.xml',
+  'commercial_support.xml',
+  'developers.xml',
+  'documentation.xml',
+  'download.xml',
+  'extra.xml',
+  'git.xml',
+  'license.xml',
+  'mailinglist.xml',
+  'main.xml',
+  'news.xml',
+]
+xml_C_files = []
+foreach f : xml_files
+  xml_C_files += 'C' / f
+endforeach
+
+if can_build_translations and build_translations_by_default
+  # Get a list of translated languages.
+  language_obj = run_command(
+    python3, website_custom_cmd, 'get_languages',
+    meson.current_source_dir(),
+  )
+  if language_obj.stderr() != ''
+    warning(language_obj.stderr())
+  endif
+  languages = language_obj.stdout().split()
+else
+  languages = []
+endif
+
+if can_parse_and_validate
+  # Parse and possibly validate the C locale's version of the DocBook.
+  custom_target('C-xmllint',
+    input: xml_C_files,
+    output: 'C_xmllint.stamp',
+    command: [
+      python3, website_custom_cmd, 'xmllint',
+      validate,
+      '@INPUT0@',
+      '@OUTPUT@'
+    ],
+    build_by_default: true,
+  )
+endif
+
+stylesheet_file = meson.current_source_dir() / 'param.xsl'
+
+publishing_targets = []
+
+# Create an html version of the C locale's version of the DocBook.
+publishing_targets += custom_target('en_html_index.html',
+  input: [stylesheet_file] + xml_C_files,
+  output: 'en_html.stamp',
+  command: [
+    python3, website_custom_cmd, 'html',
+    '@INPUT0@',
+    '@INPUT1@',
+    meson.current_build_dir() / 'html' / 'en',
+    '@OUTPUT@',
+  ],
+  build_by_default: true,
+  install: false,
+)
+
+# Install the C locale's XML files.
+install_data(xml_C_files, install_dir: install_datadir / 'help' / 'C' / meson.project_name())
+
+# gnome.yelp() is not used. It builds translations only at install time.
+# This project is not necessarily installed before it's published at www.gtkmm.org.
+# See https://github.com/mesonbuild/meson/issues/2775
+
+foreach language : languages
+  # Create translated XML files.
+  tanslated_xml_target = custom_target(language + '_xml_index.docbook',
+    input: [language / language + '.po'] + xml_C_files,
+    output: language + '_xml.stamp',
+    command: [
+      python3, website_custom_cmd, 'translate_xml',
+      '@INPUT0@',
+      meson.current_source_dir() / 'C',
+      meson.current_build_dir() / language, # Absolute path
+      '@OUTPUT@',
+      xml_files,
+    ],
+    build_by_default: true,
+    install: false,
+  )
+
+  # Create an html version of the translated version of the DocBook.
+  publishing_targets += custom_target(language + '_html_index.html',
+    input: [stylesheet_file, tanslated_xml_target],
+    output: language + '_html.stamp',
+    command: [
+      python3, website_custom_cmd, 'html',
+      '@INPUT0@',
+      meson.current_build_dir() / language / xml_files[0],
+      meson.current_build_dir() / 'html' / language,
+      '@OUTPUT@',
+    ],
+    build_by_default: true,
+    install: false,
+  )
+
+  # Install the translated XML files.
+  meson.add_install_script(
+    python3, website_custom_cmd, 'install',
+    meson.current_build_dir() / language,
+    install_datadir / 'help' / language / meson.project_name(),
+    xml_files,
+  )
+endforeach
+
+# Publish the website at www.gtkmm.org.
+run_target('publish',
+  command: [
+    python3, website_custom_cmd, 'publish',
+  ],
+  depends: publishing_targets,
+)
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..65f612d
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,87 @@
+# This file is part of gnomemm-website.
+
+project('gnomemm-website',
+  version: '2.91.4',
+  meson_version: '>= 0.50.0', # required for python3.path()
+)
+
+python3 = import('python').find_installation()
+python_version = python3.language_version()
+python_version_req = '>= 3.5'
+if not python_version.version_compare(python_version_req)
+  error('Requires Python @0@, found @1@.'.format(python_version_req, python_version))
+endif
+
+# Installation directories are relative to {prefix}.
+install_prefix = get_option('prefix')
+install_datadir = get_option('datadir')
+
+# Use these instead of meson.source_root() and meson.build_root() in subdirectories.
+# source_root() and build_root() are not useful, if this is a subproject.
+project_source_root = meson.current_source_dir()
+project_build_root = meson.current_build_dir()
+
+script_dir = project_source_root / 'tools'
+website_custom_cmd = script_dir / 'website-custom-cmd.py'
+extra_dist_cmd = script_dir / 'extra-dist-cmd.py'
+
+# Copy some files to the build directory.
+copy_obj = run_command(
+  python3, website_custom_cmd, 'copy_files_and_dirs',
+  project_source_root / 'docs' / 'html',
+  project_build_root / 'docs' / 'html',
+  'index.html',
+  '.htaccess',
+  'images',
+  'style',
+)
+if copy_obj.stdout() != ''
+  warning(copy_obj.stdout())
+endif
+
+# add_dist_script() is not allowed in a subproject if meson.version() < 0.58.0.
+can_add_dist_script = not meson.is_subproject() or meson.version().version_compare('>= 0.58.0')
+
+subdir('docs')
+
+if can_add_dist_script
+  # Don't distribute these files and directories.
+  dont_distribute = [
+    '.gitlab-ci.yml',
+  ]
+  # Modify the contents of the distribution directory.
+  meson.add_dist_script(
+    python3.path(), extra_dist_cmd,
+    project_source_root,
+    project_build_root,
+    dont_distribute,
+  )
+endif
+
+# Print a summary.
+validate = get_option('validation') and can_parse_and_validate
+explain_val = ''
+if get_option('validation') and not validate
+  explain_val = ' (requires xmllint)'
+endif
+
+build_translations = build_translations_by_default and can_build_translations
+explain_trans = ''
+if build_translations_by_default and not build_translations
+  explain_trans = ' (requires msgfmt and itstool)'
+endif
+
+summary = [
+  '',
+  '------',
+  meson.project_name() + ' ' + meson.project_version(),
+  '',
+  '    XML validation: @0@@1@'.format(validate, explain_val),
+  'Build translations: @0@@1@'.format(build_translations, explain_trans),
+  'Directories:',
+  '            prefix: @0@'.format(install_prefix),
+  '           datadir: @0@'.format(install_prefix / install_datadir),
+  '------'
+]
+
+message('\n'.join(summary))
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..34a224e
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,4 @@
+option('validation', type: 'boolean', value: false,
+  description: 'Validate the untranslated XML file')
+option('build-translations', type: 'boolean', value: true,
+  description: 'Build translated website')
diff --git a/tools/extra-dist-cmd.py b/tools/extra-dist-cmd.py
new file mode 100755
index 0000000..c9c59b0
--- /dev/null
+++ b/tools/extra-dist-cmd.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+
+# External command, intended to be called with meson.add_dist_script() in meson.build
+
+#                        argv[1]         argv[2]        argv[3:]
+# extra-dist-cmd.py <root_source_dir> <root_build_dir> <no_dist>...
+
+# Meson does not preserve timestamps on distributed files. Neither does this script.
+
+import os
+import sys
+import shutil
+import subprocess
+import glob
+
+root_source_dir = sys.argv[1]
+root_build_dir = sys.argv[2]
+
+# MESON_PROJECT_DIST_ROOT is set only if meson.version() >= 0.58.0.
+project_dist_root = os.getenv('MESON_PROJECT_DIST_ROOT', os.getenv('MESON_DIST_ROOT'))
+
+# Make a ChangeLog file for distribution.
+cmd = [
+  'git',
+  '--git-dir=' + os.path.join(root_source_dir, '.git'),
+  '--work-tree=' + root_source_dir,
+  'log',
+  '--no-merges',
+  '--date=short',
+  '--max-count=200',
+  '--pretty=tformat:%cd  %an  <%ae>%n%n  %s%n%w(0,0,2)%+b',
+]
+logfilename = os.path.join(project_dist_root, 'ChangeLog')
+with open(logfilename, mode='w', encoding='utf-8') as logfile:
+  result = subprocess.run(cmd, stdout=logfile)
+  if result.returncode:
+    sys.exit(result.returncode)
+
+# Distribute some built files in addition to the files in the local git clone.
+os.chdir(root_build_dir)
+dist_docs = os.path.join(project_dist_root, 'docs')
+
+# English html files.
+# shutil.copy() does not copy timestamps.
+shutil.copytree(os.path.join('docs', 'html', 'en'),
+                os.path.join(dist_docs, 'html', 'en'),
+                copy_function=shutil.copy)
+
+# Read the distributed LINGUAS file, containing a list of available translations.
+linguas = os.path.join(dist_docs, 'LINGUAS')
+langs = []
+try:
+  with open(linguas, encoding='utf-8') as f:
+    for line in f:
+      line = line.strip()
+      if line and not line.startswith('#'):
+        langs += line.split()
+except (FileNotFoundError, PermissionError):
+  print('=== Warning: File', linguas, 'not found.')
+
+# .mo files with translations and translated XML files and html files.
+for lang in langs:
+  dist_docs_lang = os.path.join(dist_docs, lang)
+  for file in [lang + '.mo', 'index.docbook']:
+    shutil.copy(os.path.join('docs', lang, file), dist_docs_lang)
+  for file in glob.glob(os.path.join('docs', lang, '*.xml')):
+    shutil.copy(file, dist_docs_lang)
+  shutil.copytree(os.path.join('docs', 'html', lang),
+                  os.path.join(dist_docs, 'html', lang),
+                  copy_function=shutil.copy)
+
+# Don't distribute .gitignore files.
+for dirpath, dirnames, filenames in os.walk(project_dist_root):
+  if '.gitignore' in filenames:
+    os.remove(os.path.join(dirpath, '.gitignore'))
+
+# Remove an empty MESON_PROJECT_DIST_ROOT/build directory.
+dist_build_dir = os.path.join(project_dist_root, 'build')
+if os.path.isdir(dist_build_dir):
+  try:
+    os.rmdir(dist_build_dir)
+  except OSError:
+    # Ignore the error, if not empty.
+    pass
+
+# Remove specified files and directories from the MESON_PROJECT_DIST_ROOT directory.
+for rel_path in sys.argv[3:]:
+  abs_path = os.path.join(project_dist_root, rel_path)
+  if os.path.isfile(abs_path):
+    os.remove(abs_path)
+  elif os.path.isdir(abs_path):
+    shutil.rmtree(abs_path, ignore_errors=True)
+  else:
+    # Ignore non-existent files and directories.
+    print('--- Info:', abs_path, 'not found, not removed.')
diff --git a/tools/website-custom-cmd.py b/tools/website-custom-cmd.py
new file mode 100755
index 0000000..2be8dcd
--- /dev/null
+++ b/tools/website-custom-cmd.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python3
+
+# External command, intended to be called in meson.build
+
+#                           argv[1]   argv[2:]
+# tutorial-custom-cmd.py <subcommand> <xxx>...
+
+import os
+import sys
+import subprocess
+import shutil
+from pathlib import Path
+
+subcommand = sys.argv[1]
+
+# Called from run_command()
+def copy_files_and_dirs():
+  #   argv[2]      argv[3]       argv[4:]
+  # <input_dir> <output_dir> <files_and_dirs>...
+
+  input_dir = sys.argv[2]
+  output_dir = sys.argv[3]
+  files_and_dirs =  sys.argv[4:]
+
+  for f in files_and_dirs:
+    input_path = os.path.join(input_dir, f)
+    output_path = os.path.join(output_dir, f)
+    if os.path.isfile(input_path):
+      # Create the destination directory, if it does not exist.
+      os.makedirs(os.path.dirname(output_path), exist_ok=True)
+      # shutil.copy2() copies timestamps and some other file metadata.
+      shutil.copy2(input_path, output_path)
+    elif os.path.isdir(input_path):
+      # Create the destination directory, if it does not exist.
+      os.makedirs(output_path, exist_ok=True)
+      shutil.copytree(input_path, output_path,
+                      copy_function=shutil.copy2, dirs_exist_ok=True)
+    else:
+      # Ignore non-existent files and directories.
+      print(input_path, 'not found, not copied.')
+  return 0
+
+# Called from run_command()
+def get_languages():
+  #   argv[2]
+  # <input_dir>
+
+  input_dir = sys.argv[2]
+
+  # Syntax of the LINGUAS file is documented here:
+  # https://www.gnu.org/software/gettext/manual/html_node/po_002fLINGUAS.html
+  linguas_file = os.path.join(input_dir, 'LINGUAS')
+  try:
+    with open(linguas_file, encoding='utf-8') as f:
+      for line in f:
+        line = line.strip()
+        if line and not line.startswith('#'):
+          print(line, end=' ')
+  except (FileNotFoundError, PermissionError):
+    print('Could not find file LINGUAS in', input_dir, file=sys.stderr)
+
+# Called from custom_target()
+def xmllint():
+  #  argv[2]       argv[3]          argv[4]
+  # <validate> <input_xml_file> <stamp_file_path>
+
+  validate = sys.argv[2]
+  input_xml_file = sys.argv[3]
+  stamp_file_path = sys.argv[4]
+
+  cmd = [
+    'xmllint',
+    '--noout',
+    '--noent',
+    '--xinclude',
+  ]
+  if validate == 'true':
+    cmd += ['--postvalid']
+  cmd += [input_xml_file]
+  result = subprocess.run(cmd)
+  if result.returncode:
+    return result.returncode
+
+  Path(stamp_file_path).touch(exist_ok=True)
+  return 0
+
+# Called from custom_target()
+def translate_xml():
+  #      argv[2]         argv[3]          argv[4]          argv[5]          argv[6:]
+  # <input_po_file> <input_xml_dir> <output_xml_dir> <stamp_file_path> <input_xml_files>...
+
+  input_po_file = sys.argv[2]
+  input_xml_dir = sys.argv[3]
+  output_xml_dir = sys.argv[4] # Absolute path
+  stamp_file_path = sys.argv[5]
+  input_xml_files = sys.argv[6:]
+
+  # Create the destination directory, if it does not exist.
+  os.makedirs(output_xml_dir, exist_ok=True)
+
+  # Create an .mo file.
+  language = os.path.splitext(os.path.basename(input_po_file))[0]
+  mo_file = os.path.join(output_xml_dir, language + '.mo')
+  cmd = [
+    'msgfmt',
+    '-o', mo_file,
+    input_po_file,
+  ]
+  result = subprocess.run(cmd)
+  if result.returncode:
+    return result.returncode
+
+  # Create translated XML files.
+  cmd = [
+    'itstool',
+    '-m', mo_file,
+    '-o', output_xml_dir,
+  ] + input_xml_files
+  result = subprocess.run(cmd, cwd=input_xml_dir)
+  if result.returncode:
+    return result.returncode
+
+  Path(stamp_file_path).touch(exist_ok=True)
+  return 0
+
+# Called from custom_target()
+def html():
+  #      argv[2]          argv[3]          argv[4]            argv[5]
+  # <stylesheet_file> <input_xml_file> <output_html_dir> <stamp_file_path>
+
+  stylesheet_file = sys.argv[2]
+  input_xml_file = sys.argv[3]
+  output_html_dir = sys.argv[4]
+  stamp_file_path = sys.argv[5]
+
+  # Remove old files and create the destination directory.
+  shutil.rmtree(output_html_dir, ignore_errors=True)
+  os.makedirs(output_html_dir, exist_ok=True)
+
+  cmd = [
+    'xsltproc',
+    '-o', output_html_dir + '/',
+    '--xinclude',
+    stylesheet_file,
+    input_xml_file,
+  ]
+  result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+                          universal_newlines=True)
+  # xsltproc prints the names of all written files. Don't print those lines.
+  for line in result.stdout.splitlines():
+    if not line.startswith('Writing '):
+      print(line)
+
+  if result.returncode:
+    return result.returncode
+
+  # Remove root.html.
+  root_html_file = os.path.join(output_html_dir, 'root.html')
+  if os.path.isfile(root_html_file):
+    os.remove(root_html_file)
+
+  Path(stamp_file_path).touch(exist_ok=True)
+  return 0
+
+# Called from meson.add_install_script()
+def install():
+  #      argv[2]           argv[3]              argv[4:]
+  # <input_xml_dir> <rel_install_xml_dir> <input_xml_files>...
+
+  # <rel_install_xml_dir> is the installation directory, relative to {prefix}.
+  input_xml_dir = sys.argv[2]
+  rel_install_xml_dir = sys.argv[3]
+  input_xml_files =  sys.argv[4:]
+  destdir_xmldir = os.path.join(os.getenv('MESON_INSTALL_DESTDIR_PREFIX'), rel_install_xml_dir)
+
+  # Create the installation directory, if it does not exist.
+  os.makedirs(destdir_xmldir, exist_ok=True)
+
+  for f in input_xml_files:
+    # shutil.copy2() copies timestamps and some other file metadata.
+    shutil.copy2(os.path.join(input_xml_dir, f), destdir_xmldir)
+  return 0
+
+# Called from run_target()
+def publish():
+  import glob
+
+  # Use environment variable GTKMM_WEB_USER if it exists, else USER
+  user_name = os.getenv('GTKMM_WEB_USER', os.getenv('USER'))
+
+  os.chdir(os.path.join(os.getenv('MESON_BUILD_ROOT'), 'docs'))
+  html_dirs = glob.glob(os.path.join('html', '*'))
+  web_path = '/home/murrayc/gtkmm.org/'
+
+  # rsync -avz --rsh ssh --cvs-exclude  html/.htaccess html/* $$USER www gtkmm org:$(web_path)
+  cmd = [
+    'rsync',
+    '-avz', '--rsh', 'ssh', '--cvs-exclude',
+    'html/.htaccess',
+  ] + html_dirs + [
+    user_name + '@www.gtkmm.org:' + web_path,
+  ]
+  #return subprocess.run(cmd).returncode
+  print('The "publish" target is disabled.')
+  print('To enable it, edit', sys.argv[0])
+  print(' '.join(cmd))
+  return 0
+
+# ----- Main -----
+if subcommand == 'copy_files_and_dirs':
+  sys.exit(copy_files_and_dirs())
+if subcommand == 'get_languages':
+  sys.exit(get_languages())
+if subcommand == 'xmllint':
+  sys.exit(xmllint())
+if subcommand == 'translate_xml':
+  sys.exit(translate_xml())
+if subcommand == 'html':
+  sys.exit(html())
+if subcommand == 'install':
+  sys.exit(install())
+if subcommand == 'publish':
+  sys.exit(publish())
+print(sys.argv[0], ': illegal subcommand,', subcommand)
+sys.exit(1)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]