gen-manpages.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2022 The Limenka developers
   3  # Distributed under the MIT software license, see the accompanying
   4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  import os
   6  import subprocess
   7  import sys
   8  import tempfile
   9  import argparse
  10  
  11  BINARIES = [
  12  'bin/limenkad',
  13  'bin/limenka-cli',
  14  'bin/limenka-tx',
  15  'bin/limenka-wallet',
  16  'bin/limenka-util',
  17  'bin/limenka-qt',
  18  ]
  19  
  20  parser = argparse.ArgumentParser(
  21      formatter_class=argparse.RawDescriptionHelpFormatter,
  22  )
  23  parser.add_argument(
  24      "-s",
  25      "--skip-missing-binaries",
  26      action="store_true",
  27      default=False,
  28      help="skip generation for binaries that are not found in the build path",
  29  )
  30  args = parser.parse_args()
  31  
  32  # Paths to external utilities.
  33  git = os.getenv('GIT', 'git')
  34  help2man = os.getenv('HELP2MAN', 'help2man')
  35  
  36  # If not otherwise specified, get top directory from git.
  37  topdir = os.getenv('TOPDIR')
  38  if not topdir:
  39      r = subprocess.run([git, 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE, check=True, text=True)
  40      topdir = r.stdout.rstrip()
  41  
  42  # Get input and output directories.
  43  builddir = os.getenv('BUILDDIR', os.path.join(topdir, 'build'))
  44  mandir = os.getenv('MANDIR', os.path.join(topdir, 'doc/man'))
  45  
  46  # Verify that all the required binaries are usable, and extract copyright
  47  # message in a first pass.
  48  versions = []
  49  for relpath in BINARIES:
  50      abspath = os.path.join(builddir, relpath)
  51      try:
  52          r = subprocess.run([abspath, "--version"], stdout=subprocess.PIPE, check=True, text=True)
  53      except IOError:
  54          if(args.skip_missing_binaries):
  55              print(f'{abspath} not found or not an executable. Skipping...', file=sys.stderr)
  56              continue
  57          else:
  58              print(f'{abspath} not found or not an executable', file=sys.stderr)
  59              sys.exit(1)
  60      # take first line (which must contain version)
  61      verstr = r.stdout.splitlines()[0]
  62      # last word of line is the actual version e.g. v22.99.0-5c6b3d5b3508
  63      verstr = verstr.split()[-1]
  64      assert verstr.startswith('v')
  65      # remaining lines are copyright
  66      copyright = r.stdout.split('\n')[1:]
  67      assert copyright[0].startswith('Copyright (C)')
  68  
  69      versions.append((abspath, verstr, copyright))
  70  
  71  if not versions:
  72      print(f'No binaries found in {builddir}. Please ensure the binaries are present in {builddir}, or set another build path using the BUILDDIR env variable.')
  73      sys.exit(1)
  74  
  75  if any(verstr.endswith('-dirty') for (_, verstr, _) in versions):
  76      print("WARNING: Binaries were built from a dirty tree.")
  77      print('man pages generated from dirty binaries should NOT be committed.')
  78      print('To properly generate man pages, please commit your changes (or discard them), rebuild, then run this script again.')
  79      print()
  80  
  81  basename_binaries = tuple(s.rpartition('/')[2] for s in BINARIES)
  82  
  83  for (abspath, verstr, copyright) in versions:
  84    with tempfile.NamedTemporaryFile('w', suffix='.h2m') as footer:
  85      # Create copyright footer, and write it to a temporary include file.
  86      # Copyright is the same for all binaries, so just use the first.
  87      footer.write('[COPYRIGHT]\n')
  88      footer.write('\n'.join(versions[0][2]).strip())
  89      # Create SEE ALSO section
  90      footer.write('\n[SEE ALSO]\n')
  91      footer.write(', '.join(s + "(1)" for s in basename_binaries if not abspath.endswith("/" + s)))
  92      footer.write('\n')
  93      footer.flush()
  94  
  95      # Call the binaries through help2man to produce a manual page for each of them.
  96      if True:
  97          outname = os.path.join(mandir, os.path.basename(abspath) + '.1')
  98          print(f'Generating {outname}…')
  99          subprocess.run([help2man, '-N', '--version-string=' + verstr, '--include=' + footer.name, '-o', outname, abspath], check=True)
 100