Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: tools/nixysa/third_party/gflags-1.0/python/gflags2man.py

Issue 2043006: WTF NPAPI extension. Early draft. Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:executable
+ *
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2007, Google Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are
8 # met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following disclaimer
14 # in the documentation and/or other materials provided with the
15 # distribution.
16 # * Neither the name of Google Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived from
18 # this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 """gflags2man runs a Google flags base program and generates a man page.
33
34 Run the program, parse the output, and then format that into a man
35 page.
36
37 Usage:
38 gflags2man <program> [program] ...
39 """
40
41 # TODO(csilvers): work with windows paths (\) as well as unix (/)
42
43 # This may seem a bit of an end run, but it: doesn't bloat flags, can
44 # support python/java/C++, supports older executables, and can be
45 # extended to other document formats.
46 # Inspired by help2man.
47
48 __author__ = 'Dan Christian'
49
50 import os
51 import re
52 import sys
53 import stat
54 import time
55
56 import gflags
57
58 _VERSION = '0.1'
59
60
61 def _GetDefaultDestDir():
62 home = os.environ.get('HOME', '')
63 homeman = os.path.join(home, 'man', 'man1')
64 if home and os.path.exists(homeman):
65 return homeman
66 else:
67 return os.environ.get('TMPDIR', '/tmp')
68
69 FLAGS = gflags.FLAGS
70 gflags.DEFINE_string('dest_dir', _GetDefaultDestDir(),
71 'Directory to write resulting manpage to.'
72 ' Specify \'-\' for stdout')
73 gflags.DEFINE_string('help_flag', '--help',
74 'Option to pass to target program in to get help')
75 gflags.DEFINE_integer('v', 0, 'verbosity level to use for output')
76
77 _MIN_VALID_USAGE_MSG = 9 # if fewer lines than this, help is suspect
78
79
80 class Logging:
81 """A super-simple logging class"""
82 def error(self, msg): print >>sys.stderr, "ERROR: ", msg
83 def warn(self, msg): print >>sys.stderr, "WARNING: ", msg
84 def info(self, msg): print msg
85 def debug(self, msg): self.vlog(1, msg)
86 def vlog(self, level, msg):
87 if FLAGS.v >= level: print msg
88 logging = Logging()
89
90
91 def GetRealPath(filename):
92 """Given an executable filename, find in the PATH or find absolute path.
93 Args:
94 filename An executable filename (string)
95 Returns:
96 Absolute version of filename.
97 None if filename could not be found locally, absolutely, or in PATH
98 """
99 if os.path.isabs(filename): # already absolute
100 return filename
101
102 if filename.startswith('./') or filename.startswith('../'): # relative
103 return os.path.abspath(filename)
104
105 path = os.getenv('PATH', '')
106 for directory in path.split(':'):
107 tryname = os.path.join(directory, filename)
108 if os.path.exists(tryname):
109 if not os.path.isabs(directory): # relative directory
110 return os.path.abspath(tryname)
111 return tryname
112 if os.path.exists(filename):
113 return os.path.abspath(filename)
114 return None # could not determine
115
116 class Flag(object):
117 """The information about a single flag."""
118
119 def __init__(self, flag_desc, help):
120 """Create the flag object.
121 Args:
122 flag_desc The command line forms this could take. (string)
123 help The help text (string)
124 """
125 self.desc = flag_desc # the command line forms
126 self.help = help # the help text
127 self.default = '' # default value
128 self.tips = '' # parsing/syntax tips
129
130
131 class ProgramInfo(object):
132 """All the information gleaned from running a program with --help."""
133
134 # Match a module block start, for python scripts --help
135 # "goopy.logging:"
136 module_py_re = re.compile(r'(\S.+):$')
137 # match the start of a flag listing
138 # " -v,--verbosity: Logging verbosity"
139 flag_py_re = re.compile(r'\s+(-\S+):\s+(.*)$')
140 # " (default: '0')"
141 flag_default_py_re = re.compile(r'\s+\(default:\s+\'(.*)\'\)$')
142 # " (an integer)"
143 flag_tips_py_re = re.compile(r'\s+\((.*)\)$')
144
145 # Match a module block start, for c++ programs --help
146 # "google/base/commandlineflags"
147 module_c_re = re.compile(r'\s+Flags from (\S.+):$')
148 # match the start of a flag listing
149 # " -v,--verbosity: Logging verbosity"
150 flag_c_re = re.compile(r'\s+(-\S+)\s+(.*)$')
151
152 # Match a module block start, for java programs --help
153 # "com.google.common.flags"
154 module_java_re = re.compile(r'\s+Flags for (\S.+):$')
155 # match the start of a flag listing
156 # " -v,--verbosity: Logging verbosity"
157 flag_java_re = re.compile(r'\s+(-\S+)\s+(.*)$')
158
159 def __init__(self, executable):
160 """Create object with executable.
161 Args:
162 executable Program to execute (string)
163 """
164 self.long_name = executable
165 self.name = os.path.basename(executable) # name
166 # Get name without extension (PAR files)
167 (self.short_name, self.ext) = os.path.splitext(self.name)
168 self.executable = GetRealPath(executable) # name of the program
169 self.output = [] # output from the program. List of lines.
170 self.desc = [] # top level description. List of lines
171 self.modules = {} # { section_name(string), [ flags ] }
172 self.module_list = [] # list of module names in their original order
173 self.date = time.localtime(time.time()) # default date info
174
175 def Run(self):
176 """Run it and collect output.
177
178 Returns:
179 1 (true) If everything went well.
180 0 (false) If there were problems.
181 """
182 if not self.executable:
183 logging.error('Could not locate "%s"' % self.long_name)
184 return 0
185
186 finfo = os.stat(self.executable)
187 self.date = time.localtime(finfo[stat.ST_MTIME])
188
189 logging.info('Running: %s %s </dev/null 2>&1'
190 % (self.executable, FLAGS.help_flag))
191 # --help output is often routed to stderr, so we combine with stdout.
192 # Re-direct stdin to /dev/null to encourage programs that
193 # don't understand --help to exit.
194 (child_stdin, child_stdout_and_stderr) = os.popen4(
195 [self.executable, FLAGS.help_flag])
196 child_stdin.close() # '</dev/null'
197 self.output = child_stdout_and_stderr.readlines()
198 child_stdout_and_stderr.close()
199 if len(self.output) < _MIN_VALID_USAGE_MSG:
200 logging.error('Error: "%s %s" returned only %d lines: %s'
201 % (self.name, FLAGS.help_flag,
202 len(self.output), self.output))
203 return 0
204 return 1
205
206 def Parse(self):
207 """Parse program output."""
208 (start_line, lang) = self.ParseDesc()
209 if start_line < 0:
210 return
211 if 'python' == lang:
212 self.ParsePythonFlags(start_line)
213 elif 'c' == lang:
214 self.ParseCFlags(start_line)
215 elif 'java' == lang:
216 self.ParseJavaFlags(start_line)
217
218 def ParseDesc(self, start_line=0):
219 """Parse the initial description.
220
221 This could be Python or C++.
222
223 Returns:
224 (start_line, lang_type)
225 start_line Line to start parsing flags on (int)
226 lang_type Either 'python' or 'c'
227 (-1, '') if the flags start could not be found
228 """
229 exec_mod_start = self.executable + ':'
230
231 after_blank = 0
232 start_line = 0 # ignore the passed-in arg for now (?)
233 for start_line in range(start_line, len(self.output)): # collect top descrip tion
234 line = self.output[start_line].rstrip()
235 # Python flags start with 'flags:\n'
236 if ('flags:' == line
237 and len(self.output) > start_line+1
238 and '' == self.output[start_line+1].rstrip()):
239 start_line += 2
240 logging.debug('Flags start (python): %s' % line)
241 return (start_line, 'python')
242 # SWIG flags just have the module name followed by colon.
243 if exec_mod_start == line:
244 logging.debug('Flags start (swig): %s' % line)
245 return (start_line, 'python')
246 # C++ flags begin after a blank line and with a constant string
247 if after_blank and line.startswith(' Flags from '):
248 logging.debug('Flags start (c): %s' % line)
249 return (start_line, 'c')
250 # java flags begin with a constant string
251 if line == 'where flags are':
252 logging.debug('Flags start (java): %s' % line)
253 start_line += 2 # skip "Standard flags:"
254 return (start_line, 'java')
255
256 logging.debug('Desc: %s' % line)
257 self.desc.append(line)
258 after_blank = (line == '')
259 else:
260 logging.warn('Never found the start of the flags section for "%s"!'
261 % self.long_name)
262 return (-1, '')
263
264 def ParsePythonFlags(self, start_line=0):
265 """Parse python/swig style flags."""
266 modname = None # name of current module
267 modlist = []
268 flag = None
269 for line_num in range(start_line, len(self.output)): # collect flags
270 line = self.output[line_num].rstrip()
271 if not line: # blank
272 continue
273
274 mobj = self.module_py_re.match(line)
275 if mobj: # start of a new module
276 modname = mobj.group(1)
277 logging.debug('Module: %s' % line)
278 if flag:
279 modlist.append(flag)
280 self.module_list.append(modname)
281 self.modules.setdefault(modname, [])
282 modlist = self.modules[modname]
283 flag = None
284 continue
285
286 mobj = self.flag_py_re.match(line)
287 if mobj: # start of a new flag
288 if flag:
289 modlist.append(flag)
290 logging.debug('Flag: %s' % line)
291 flag = Flag(mobj.group(1), mobj.group(2))
292 continue
293
294 if not flag: # continuation of a flag
295 logging.error('Flag info, but no current flag "%s"' % line)
296 mobj = self.flag_default_py_re.match(line)
297 if mobj: # (default: '...')
298 flag.default = mobj.group(1)
299 logging.debug('Fdef: %s' % line)
300 continue
301 mobj = self.flag_tips_py_re.match(line)
302 if mobj: # (tips)
303 flag.tips = mobj.group(1)
304 logging.debug('Ftip: %s' % line)
305 continue
306 if flag and flag.help:
307 flag.help += line # multiflags tack on an extra line
308 else:
309 logging.info('Extra: %s' % line)
310 if flag:
311 modlist.append(flag)
312
313 def ParseCFlags(self, start_line=0):
314 """Parse C style flags."""
315 modname = None # name of current module
316 modlist = []
317 flag = None
318 for line_num in range(start_line, len(self.output)): # collect flags
319 line = self.output[line_num].rstrip()
320 if not line: # blank lines terminate flags
321 if flag: # save last flag
322 modlist.append(flag)
323 flag = None
324 continue
325
326 mobj = self.module_c_re.match(line)
327 if mobj: # start of a new module
328 modname = mobj.group(1)
329 logging.debug('Module: %s' % line)
330 if flag:
331 modlist.append(flag)
332 self.module_list.append(modname)
333 self.modules.setdefault(modname, [])
334 modlist = self.modules[modname]
335 flag = None
336 continue
337
338 mobj = self.flag_c_re.match(line)
339 if mobj: # start of a new flag
340 if flag: # save last flag
341 modlist.append(flag)
342 logging.debug('Flag: %s' % line)
343 flag = Flag(mobj.group(1), mobj.group(2))
344 continue
345
346 # append to flag help. type and default are part of the main text
347 if flag:
348 flag.help += ' ' + line.strip()
349 else:
350 logging.info('Extra: %s' % line)
351 if flag:
352 modlist.append(flag)
353
354 def ParseJavaFlags(self, start_line=0):
355 """Parse Java style flags (com.google.common.flags)."""
356 # The java flags prints starts with a "Standard flags" "module"
357 # that doesn't follow the standard module syntax.
358 modname = 'Standard flags' # name of current module
359 self.module_list.append(modname)
360 self.modules.setdefault(modname, [])
361 modlist = self.modules[modname]
362 flag = None
363
364 for line_num in range(start_line, len(self.output)): # collect flags
365 line = self.output[line_num].rstrip()
366 logging.vlog(2, 'Line: "%s"' % line)
367 if not line: # blank lines terminate module
368 if flag: # save last flag
369 modlist.append(flag)
370 flag = None
371 continue
372
373 mobj = self.module_java_re.match(line)
374 if mobj: # start of a new module
375 modname = mobj.group(1)
376 logging.debug('Module: %s' % line)
377 if flag:
378 modlist.append(flag)
379 self.module_list.append(modname)
380 self.modules.setdefault(modname, [])
381 modlist = self.modules[modname]
382 flag = None
383 continue
384
385 mobj = self.flag_java_re.match(line)
386 if mobj: # start of a new flag
387 if flag: # save last flag
388 modlist.append(flag)
389 logging.debug('Flag: %s' % line)
390 flag = Flag(mobj.group(1), mobj.group(2))
391 continue
392
393 # append to flag help. type and default are part of the main text
394 if flag:
395 flag.help += ' ' + line.strip()
396 else:
397 logging.info('Extra: %s' % line)
398 if flag:
399 modlist.append(flag)
400
401 def Filter(self):
402 """Filter parsed data to create derived fields."""
403 if not self.desc:
404 self.short_desc = ''
405 return
406
407 for i in range(len(self.desc)): # replace full path with name
408 if self.desc[i].find(self.executable) >= 0:
409 self.desc[i] = self.desc[i].replace(self.executable, self.name)
410
411 self.short_desc = self.desc[0]
412 word_list = self.short_desc.split(' ')
413 all_names = [ self.name, self.short_name, ]
414 # Since the short_desc is always listed right after the name,
415 # trim it from the short_desc
416 while word_list and (word_list[0] in all_names
417 or word_list[0].lower() in all_names):
418 del word_list[0]
419 self.short_desc = '' # signal need to reconstruct
420 if not self.short_desc and word_list:
421 self.short_desc = ' '.join(word_list)
422
423
424 class GenerateDoc(object):
425 """Base class to output flags information."""
426
427 def __init__(self, proginfo, directory='.'):
428 """Create base object.
429 Args:
430 proginfo A ProgramInfo object
431 directory Directory to write output into
432 """
433 self.info = proginfo
434 self.dirname = directory
435
436 def Output(self):
437 """Output all sections of the page."""
438 self.Open()
439 self.Header()
440 self.Body()
441 self.Footer()
442
443 def Open(self): raise NotImplementedError # define in subclass
444 def Header(self): raise NotImplementedError # define in subclass
445 def Body(self): raise NotImplementedError # define in subclass
446 def Footer(self): raise NotImplementedError # define in subclass
447
448
449 class GenerateMan(GenerateDoc):
450 """Output a man page."""
451
452 def __init__(self, proginfo, directory='.'):
453 """Create base object.
454 Args:
455 proginfo A ProgramInfo object
456 directory Directory to write output into
457 """
458 GenerateDoc.__init__(self, proginfo, directory)
459
460 def Open(self):
461 if self.dirname == '-':
462 logging.info('Writing to stdout')
463 self.fp = sys.stdout
464 else:
465 self.file_path = '%s.1' % os.path.join(self.dirname, self.info.name)
466 logging.info('Writing: %s' % self.file_path)
467 self.fp = open(self.file_path, 'w')
468
469 def Header(self):
470 self.fp.write(
471 '.\\" DO NOT MODIFY THIS FILE! It was generated by gflags2man %s\n'
472 % _VERSION)
473 self.fp.write(
474 '.TH %s "1" "%s" "%s" "User Commands"\n'
475 % (self.info.name, time.strftime('%x', self.info.date), self.info.name))
476 self.fp.write(
477 '.SH NAME\n%s \\- %s\n' % (self.info.name, self.info.short_desc))
478 self.fp.write(
479 '.SH SYNOPSIS\n.B %s\n[\\fIFLAGS\\fR]...\n' % self.info.name)
480
481 def Body(self):
482 self.fp.write(
483 '.SH DESCRIPTION\n.\\" Add any additional description here\n.PP\n')
484 for ln in self.info.desc:
485 self.fp.write('%s\n' % ln)
486 self.fp.write(
487 '.SH OPTIONS\n')
488 # This shows flags in the original order
489 for modname in self.info.module_list:
490 if modname.find(self.info.executable) >= 0:
491 mod = modname.replace(self.info.executable, self.info.name)
492 else:
493 mod = modname
494 self.fp.write('\n.P\n.I %s\n' % mod)
495 for flag in self.info.modules[modname]:
496 help_string = flag.help
497 if flag.default or flag.tips:
498 help_string += '\n.br\n'
499 if flag.default:
500 help_string += ' (default: \'%s\')' % flag.default
501 if flag.tips:
502 help_string += ' (%s)' % flag.tips
503 self.fp.write(
504 '.TP\n%s\n%s\n' % (flag.desc, help_string))
505
506 def Footer(self):
507 self.fp.write(
508 '.SH COPYRIGHT\nCopyright \(co %s Google.\n'
509 % time.strftime('%Y', self.info.date))
510 self.fp.write('Gflags2man created this page from "%s %s" output.\n'
511 % (self.info.name, FLAGS.help_flag))
512 self.fp.write('\nGflags2man was written by Dan Christian. '
513 ' Note that the date on this'
514 ' page is the modification date of %s.\n' % self.info.name)
515
516
517 def main(argv):
518 argv = FLAGS(argv) # handles help as well
519 if len(argv) <= 1:
520 print >>sys.stderr, __doc__
521 print >>sys.stderr, "flags:"
522 print >>sys.stderr, str(FLAGS)
523 return 1
524
525 for arg in argv[1:]:
526 prog = ProgramInfo(arg)
527 if not prog.Run():
528 continue
529 prog.Parse()
530 prog.Filter()
531 doc = GenerateMan(prog, FLAGS.dest_dir)
532 doc.Output()
533 return 0
534
535 if __name__ == '__main__':
536 main(sys.argv)
OLDNEW
« no previous file with comments | « tools/nixysa/third_party/gflags-1.0/python/gflags.py ('k') | tools/nixysa/third_party/gflags-1.0/python/gflags_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698