15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2006, Google Inc.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# All rights reserved.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Redistribution and use in source and binary forms, with or without
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# modification, are permitted provided that the following conditions are
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# met:
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#     * Redistributions of source code must retain the above copyright
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# notice, this list of conditions and the following disclaimer.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#     * Redistributions in binary form must reproduce the above
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# copyright notice, this list of conditions and the following disclaimer
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# in the documentation and/or other materials provided with the
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# distribution.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#     * Neither the name of Google Inc. nor the names of its
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# contributors may be used to endorse or promote products derived from
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# this software without specific prior written permission.
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""gflags2man runs a Google flags base program and generates a man page.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Run the program, parse the output, and then format that into a man
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)page.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Usage:
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gflags2man <program> [program] ...
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# TODO(csilvers): work with windows paths (\) as well as unix (/)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This may seem a bit of an end run, but it:  doesn't bloat flags, can
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# support python/java/C++, supports older executables, and can be
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# extended to other document formats.
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Inspired by help2man.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import stat
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import gflags
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)_VERSION = '0.1'
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _GetDefaultDestDir():
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  home = os.environ.get('HOME', '')
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  homeman = os.path.join(home, 'man', 'man1')
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if home and os.path.exists(homeman):
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return homeman
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return os.environ.get('TMPDIR', '/tmp')
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FLAGS = gflags.FLAGS
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gflags.DEFINE_string('dest_dir', _GetDefaultDestDir(),
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    'Directory to write resulting manpage to.'
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    ' Specify \'-\' for stdout')
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gflags.DEFINE_string('help_flag', '--help',
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    'Option to pass to target program in to get help')
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gflags.DEFINE_integer('v', 0, 'verbosity level to use for output')
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)_MIN_VALID_USAGE_MSG = 9         # if fewer lines than this, help is suspect
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Logging:
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """A super-simple logging class"""
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def error(self, msg): print >>sys.stderr, "ERROR: ", msg
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def warn(self, msg): print >>sys.stderr, "WARNING: ", msg
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def info(self, msg): print msg
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def debug(self, msg): self.vlog(1, msg)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def vlog(self, level, msg):
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if FLAGS.v >= level: print msg
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)logging = Logging()
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class App:
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def usage(self, shorthelp=0):
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >>sys.stderr, __doc__
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >>sys.stderr, "flags:"
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >>sys.stderr, str(FLAGS)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def run(self):
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    main(sys.argv)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)app = App()
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def GetRealPath(filename):
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Given an executable filename, find in the PATH or find absolute path.
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    filename  An executable filename (string)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Returns:
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Absolute version of filename.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    None if filename could not be found locally, absolutely, or in PATH
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.path.isabs(filename):                # already absolute
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return filename
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if filename.startswith('./') or  filename.startswith('../'): # relative
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return os.path.abspath(filename)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  path = os.getenv('PATH', '')
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for directory in path.split(':'):
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tryname = os.path.join(directory, filename)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if os.path.exists(tryname):
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not os.path.isabs(directory):  # relative directory
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return os.path.abspath(tryname)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return tryname
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os.path.exists(filename):
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return os.path.abspath(filename)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return None                         # could not determine
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Flag(object):
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """The information about a single flag."""
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, flag_desc, help):
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Create the flag object.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      flag_desc  The command line forms this could take. (string)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      help       The help text (string)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.desc = flag_desc               # the command line forms
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.help = help                    # the help text
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.default = ''                   # default value
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.tips = ''                      # parsing/syntax tips
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ProgramInfo(object):
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """All the information gleaned from running a program with --help."""
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Match a module block start, for python scripts --help
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # "goopy.logging:"
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  module_py_re = re.compile(r'(\S.+):$')
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # match the start of a flag listing
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # " -v,--verbosity:  Logging verbosity"
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  flag_py_re         = re.compile(r'\s+(-\S+):\s+(.*)$')
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # "   (default: '0')"
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  flag_default_py_re = re.compile(r'\s+\(default:\s+\'(.*)\'\)$')
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # "   (an integer)"
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  flag_tips_py_re    = re.compile(r'\s+\((.*)\)$')
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Match a module block start, for c++ programs --help
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # "google/base/commandlineflags":
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  module_c_re = re.compile(r'\s+Flags from (\S.+):$')
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # match the start of a flag listing
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # " -v,--verbosity:  Logging verbosity"
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  flag_c_re         = re.compile(r'\s+(-\S+)\s+(.*)$')
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Match a module block start, for java programs --help
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # "com.google.common.flags"
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  module_java_re = re.compile(r'\s+Flags for (\S.+):$')
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # match the start of a flag listing
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # " -v,--verbosity:  Logging verbosity"
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  flag_java_re         = re.compile(r'\s+(-\S+)\s+(.*)$')
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, executable):
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Create object with executable.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      executable  Program to execute (string)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.long_name = executable
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.name = os.path.basename(executable)  # name
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get name without extension (PAR files)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (self.short_name, self.ext) = os.path.splitext(self.name)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.executable = GetRealPath(executable)  # name of the program
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.output = []          # output from the program.  List of lines.
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.desc = []            # top level description.  List of lines
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.modules = {}         # { section_name(string), [ flags ] }
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.module_list = []     # list of module names in their original order
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.date = time.localtime(time.time())   # default date info
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Run(self):
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Run it and collect output.
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      1 (true)   If everything went well.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0 (false)  If there were problems.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.executable:
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logging.error('Could not locate "%s"' % self.long_name)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finfo = os.stat(self.executable)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.date = time.localtime(finfo[stat.ST_MTIME])
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    logging.info('Running: %s %s </dev/null 2>&1'
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 % (self.executable, FLAGS.help_flag))
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # --help output is often routed to stderr, so we combine with stdout.
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Re-direct stdin to /dev/null to encourage programs that
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # don't understand --help to exit.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (child_stdin, child_stdout_and_stderr) = os.popen4(
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      [self.executable, FLAGS.help_flag])
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    child_stdin.close()       # '</dev/null'
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.output = child_stdout_and_stderr.readlines()
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    child_stdout_and_stderr.close()
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if len(self.output) < _MIN_VALID_USAGE_MSG:
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logging.error('Error: "%s %s" returned only %d lines: %s'
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    % (self.name, FLAGS.help_flag,
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       len(self.output), self.output))
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Parse(self):
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Parse program output."""
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (start_line, lang) = self.ParseDesc()
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if start_line < 0:
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if 'python' == lang:
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ParsePythonFlags(start_line)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif 'c' == lang:
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ParseCFlags(start_line)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif 'java' == lang:
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.ParseJavaFlags(start_line)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ParseDesc(self, start_line=0):
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Parse the initial description.
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This could be Python or C++.
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (start_line, lang_type)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        start_line  Line to start parsing flags on (int)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        lang_type   Either 'python' or 'c'
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       (-1, '')  if the flags start could not be found
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exec_mod_start = self.executable + ':'
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    after_blank = 0
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    start_line = 0             # ignore the passed-in arg for now (?)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for start_line in range(start_line, len(self.output)): # collect top description
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      line = self.output[start_line].rstrip()
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Python flags start with 'flags:\n'
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ('flags:' == line
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          and len(self.output) > start_line+1
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          and '' == self.output[start_line+1].rstrip()):
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        start_line += 2
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Flags start (python): %s' % line)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return (start_line, 'python')
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # SWIG flags just have the module name followed by colon.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if exec_mod_start == line:
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Flags start (swig): %s' % line)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return (start_line, 'python')
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # C++ flags begin after a blank line and with a constant string
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if after_blank and line.startswith('  Flags from '):
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Flags start (c): %s' % line)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return (start_line, 'c')
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # java flags begin with a constant string
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if line == 'where flags are':
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Flags start (java): %s' % line)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        start_line += 2                        # skip "Standard flags:"
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return (start_line, 'java')
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logging.debug('Desc: %s' % line)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.desc.append(line)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      after_blank = (line == '')
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logging.warn('Never found the start of the flags section for "%s"!'
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   % self.long_name)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return (-1, '')
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ParsePythonFlags(self, start_line=0):
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Parse python/swig style flags."""
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    modname = None                      # name of current module
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    modlist = []
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flag = None
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for line_num in range(start_line, len(self.output)): # collect flags
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      line = self.output[line_num].rstrip()
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not line:                      # blank
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mobj = self.module_py_re.match(line)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if mobj:                          # start of a new module
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modname = mobj.group(1)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Module: %s' % line)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag:
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          modlist.append(flag)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.module_list.append(modname)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.modules.setdefault(modname, [])
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modlist = self.modules[modname]
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag = None
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mobj = self.flag_py_re.match(line)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if mobj:                          # start of a new flag
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag:
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          modlist.append(flag)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Flag: %s' % line)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag = Flag(mobj.group(1),  mobj.group(2))
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not flag:                    # continuation of a flag
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.error('Flag info, but no current flag "%s"' % line)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mobj = self.flag_default_py_re.match(line)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if mobj:                          # (default: '...')
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag.default = mobj.group(1)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Fdef: %s' % line)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mobj = self.flag_tips_py_re.match(line)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if mobj:                          # (tips)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag.tips = mobj.group(1)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Ftip: %s' % line)
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if flag and flag.help:
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag.help += line              # multiflags tack on an extra line
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.info('Extra: %s' % line)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if flag:
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modlist.append(flag)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ParseCFlags(self, start_line=0):
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Parse C style flags."""
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    modname = None                      # name of current module
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    modlist = []
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flag = None
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for line_num in range(start_line, len(self.output)):  # collect flags
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      line = self.output[line_num].rstrip()
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not line:                      # blank lines terminate flags
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag:                        # save last flag
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          modlist.append(flag)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          flag = None
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mobj = self.module_c_re.match(line)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if mobj:                          # start of a new module
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modname = mobj.group(1)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Module: %s' % line)
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag:
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          modlist.append(flag)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.module_list.append(modname)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.modules.setdefault(modname, [])
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modlist = self.modules[modname]
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag = None
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mobj = self.flag_c_re.match(line)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if mobj:                          # start of a new flag
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag:                        # save last flag
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          modlist.append(flag)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Flag: %s' % line)
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag = Flag(mobj.group(1),  mobj.group(2))
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # append to flag help.  type and default are part of the main text
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if flag:
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag.help += ' ' + line.strip()
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.info('Extra: %s' % line)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if flag:
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modlist.append(flag)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def ParseJavaFlags(self, start_line=0):
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Parse Java style flags (com.google.common.flags)."""
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The java flags prints starts with a "Standard flags" "module"
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # that doesn't follow the standard module syntax.
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    modname = 'Standard flags'          # name of current module
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.module_list.append(modname)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.modules.setdefault(modname, [])
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    modlist = self.modules[modname]
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flag = None
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for line_num in range(start_line, len(self.output)): # collect flags
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      line = self.output[line_num].rstrip()
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logging.vlog(2, 'Line: "%s"' % line)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not line:                      # blank lines terminate module
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag:                        # save last flag
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          modlist.append(flag)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          flag = None
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mobj = self.module_java_re.match(line)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if mobj:                          # start of a new module
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modname = mobj.group(1)
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Module: %s' % line)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag:
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          modlist.append(flag)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.module_list.append(modname)
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.modules.setdefault(modname, [])
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modlist = self.modules[modname]
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag = None
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mobj = self.flag_java_re.match(line)
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if mobj:                          # start of a new flag
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag:                        # save last flag
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          modlist.append(flag)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.debug('Flag: %s' % line)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag = Flag(mobj.group(1),  mobj.group(2))
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # append to flag help.  type and default are part of the main text
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if flag:
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        flag.help += ' ' + line.strip()
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        logging.info('Extra: %s' % line)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if flag:
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modlist.append(flag)
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Filter(self):
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Filter parsed data to create derived fields."""
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.desc:
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.short_desc = ''
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for i in range(len(self.desc)):   # replace full path with name
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if self.desc[i].find(self.executable) >= 0:
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.desc[i] = self.desc[i].replace(self.executable, self.name)
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.short_desc = self.desc[0]
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    word_list = self.short_desc.split(' ')
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    all_names = [ self.name, self.short_name, ]
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Since the short_desc is always listed right after the name,
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #  trim it from the short_desc
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while word_list and (word_list[0] in all_names
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         or word_list[0].lower() in all_names):
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      del word_list[0]
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.short_desc = ''              # signal need to reconstruct
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self.short_desc and word_list:
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.short_desc = ' '.join(word_list)
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class GenerateDoc(object):
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Base class to output flags information."""
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, proginfo, directory='.'):
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Create base object.
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      proginfo   A ProgramInfo object
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      directory  Directory to write output into
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.info = proginfo
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.dirname = directory
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Output(self):
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Output all sections of the page."""
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.Open()
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.Header()
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.Body()
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.Footer()
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Open(self): raise NotImplementedError    # define in subclass
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Header(self): raise NotImplementedError  # define in subclass
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Body(self): raise NotImplementedError    # define in subclass
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Footer(self): raise NotImplementedError  # define in subclass
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class GenerateMan(GenerateDoc):
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Output a man page."""
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, proginfo, directory='.'):
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Create base object.
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      proginfo   A ProgramInfo object
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      directory  Directory to write output into
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GenerateDoc.__init__(self, proginfo, directory)
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Open(self):
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.dirname == '-':
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logging.info('Writing to stdout')
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.fp = sys.stdout
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.file_path = '%s.1' % os.path.join(self.dirname, self.info.name)
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      logging.info('Writing: %s' % self.file_path)
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.fp = open(self.file_path, 'w')
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Header(self):
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.fp.write(
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '.\\" DO NOT MODIFY THIS FILE!  It was generated by gflags2man %s\n'
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      % _VERSION)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.fp.write(
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '.TH %s "1" "%s" "%s" "User Commands"\n'
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      % (self.info.name, time.strftime('%x', self.info.date), self.info.name))
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.fp.write(
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '.SH NAME\n%s \\- %s\n' % (self.info.name, self.info.short_desc))
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.fp.write(
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '.SH SYNOPSIS\n.B %s\n[\\fIFLAGS\\fR]...\n' % self.info.name)
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Body(self):
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.fp.write(
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '.SH DESCRIPTION\n.\\" Add any additional description here\n.PP\n')
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for ln in self.info.desc:
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.fp.write('%s\n' % ln)
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.fp.write(
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '.SH OPTIONS\n')
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # This shows flags in the original order
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for modname in self.info.module_list:
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if modname.find(self.info.executable) >= 0:
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mod = modname.replace(self.info.executable, self.info.name)
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mod = modname
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.fp.write('\n.P\n.I %s\n' % mod)
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for flag in self.info.modules[modname]:
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        help_string = flag.help
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag.default or flag.tips:
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          help_string += '\n.br\n'
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag.default:
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          help_string += '  (default: \'%s\')' % flag.default
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if flag.tips:
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          help_string += '  (%s)' % flag.tips
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.fp.write(
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          '.TP\n%s\n%s\n' % (flag.desc, help_string))
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Footer(self):
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.fp.write(
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '.SH COPYRIGHT\nCopyright \(co %s Google.\n'
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      % time.strftime('%Y', self.info.date))
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.fp.write('Gflags2man created this page from "%s %s" output.\n'
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  % (self.info.name, FLAGS.help_flag))
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.fp.write('\nGflags2man was written by Dan Christian. '
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  ' Note that the date on this'
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  ' page is the modification date of %s.\n' % self.info.name)
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main(argv):
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  argv = FLAGS(argv)           # handles help as well
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(argv) <= 1:
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    app.usage(shorthelp=1)
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for arg in argv[1:]:
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prog = ProgramInfo(arg)
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not prog.Run():
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prog.Parse()
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prog.Filter()
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    doc = GenerateMan(prog, FLAGS.dest_dir)
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    doc.Output()
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  app.run()
545