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