Commit 83911c12 authored by Craig Silverstein's avatar Craig Silverstein

Wed Mar 26 15:20:18 2008 Google Inc. <opensource@google.com>

	* google-gflags: version 0.8
	* Export DescribeOneFlag() in the API
	* Add support for automatic line wrapping at 80 cols for gflags.py
	* Bugfix: do not treat an isolated "-" the same as an isolated "--"
	* Update rpm spec to point to Google Code rather than sourceforge (!)
	* Improve documentation (including documenting thread-safety)
	* Improve #include hygiene
	* Improve testing


git-svn-id: https://gflags.googlecode.com/svn/trunk@21 6586e3c6-dcc4-952a-343f-ff74eb82781d
Wed Mar 26 15:20:18 2008 Google Inc. <opensource@google.com>
* google-gflags: version 0.8
* Export DescribeOneFlag() in the API
* Add support for automatic line wrapping at 80 cols for gflags.py
* Bugfix: do not treat an isolated "-" the same as an isolated "--"
* Update rpm spec to point to Google Code rather than sourceforge (!)
* Improve documentation (including documenting thread-safety)
* Improve #include hygiene
* Improve testing
Thu Oct 18 11:33:20 2007 Google Inc. <opensource@google.com>
* google-gflags: version 0.7
......
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.59 for gflags 0.7.
# Generated by GNU Autoconf 2.59 for gflags 0.8.
#
# Report bugs to <opensource@google.com>.
#
......@@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='gflags'
PACKAGE_TARNAME='gflags'
PACKAGE_VERSION='0.7'
PACKAGE_STRING='gflags 0.7'
PACKAGE_VERSION='0.8'
PACKAGE_STRING='gflags 0.8'
PACKAGE_BUGREPORT='opensource@google.com'
ac_unique_file="README"
......@@ -954,7 +954,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures gflags 0.7 to adapt to many kinds of systems.
\`configure' configures gflags 0.8 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
......@@ -1020,7 +1020,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of gflags 0.7:";;
short | recursive ) echo "Configuration of gflags 0.8:";;
esac
cat <<\_ACEOF
......@@ -1163,7 +1163,7 @@ fi
test -n "$ac_init_help" && exit 0
if $ac_init_version; then
cat <<\_ACEOF
gflags configure 0.7
gflags configure 0.8
generated by GNU Autoconf 2.59
Copyright (C) 2003 Free Software Foundation, Inc.
......@@ -1177,7 +1177,7 @@ cat >&5 <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by gflags $as_me 0.7, which was
It was created by gflags $as_me 0.8, which was
generated by GNU Autoconf 2.59. Invocation command line was
$ $0 $@
......@@ -1823,7 +1823,7 @@ fi
# Define the identity of the package.
PACKAGE='gflags'
VERSION='0.7'
VERSION='0.8'
cat >>confdefs.h <<_ACEOF
......@@ -20943,7 +20943,7 @@ _ASBOX
} >&5
cat >&5 <<_CSEOF
This file was extended by gflags $as_me 0.7, which was
This file was extended by gflags $as_me 0.8, which was
generated by GNU Autoconf 2.59. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
......@@ -21006,7 +21006,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
gflags config.status 0.7
gflags config.status 0.8
configured by $0, generated by GNU Autoconf 2.59,
with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
......
......@@ -4,7 +4,7 @@
# make sure we're interpreted by some minimal autoconf
AC_PREREQ(2.57)
AC_INIT(gflags, 0.7, opensource@google.com)
AC_INIT(gflags, 0.8, opensource@google.com)
# The argument here is just something that should be in the current directory
# (for sanity checking)
AC_CONFIG_SRCDIR(README)
......
......@@ -81,11 +81,11 @@ translates directly to Python.</p>
<p> Defining a flag is easy: just use the appropriate macro for the
type you want the flag to be, as defined at the bottom of
<code>base/commandlineflags.h</code>. Here's an example file,
<code>google/gflags.h</code>. Here's an example file,
<code>foo.cc</code>:</p>
<pre>
#include "base/commandlineflags.h"
#include &lt;google/gflags.h&gt;
DEFINE_bool(big_menu, true, "Include 'advanced' options in the menu listing");
DEFINE_string(languages, "english,french,german",
......@@ -131,7 +131,7 @@ be in the global namespace.</p>
<h2> <A name=using>Accessing the Flag</A> </h2>
<p>All defined flags are available to the program as just a normal
variable, with the prefix <code>FLAGS_</code> appended. In the above
variable, with the prefix <code>FLAGS_</code> prepended. In the above
example, the macros define two variables, <code>FLAGS_big_menu</code>
(a bool), and <code>FLAGS_languages</code> (a C++ string).</p>
......@@ -145,7 +145,7 @@ variable:</p>
</pre>
<p>You can also get and set flag values via special functions in
<code>commandlineflags.h</code>. That's a rarer use case, though.</p>
<code>gflags.h</code>. That's a rarer use case, though.</p>
<h2> <A name=declare>DECLARE: Using the Flag in a Different File</A> </h2>
......@@ -173,16 +173,21 @@ recommend the following guideline:</p>
<blockquote>
If you DEFINE a flag in <code>foo.cc</code>, either don't DECLARE it
at all, or only DECLARE it in <code>foo.h</code>.
at all, only DECLARE it in tightly related tests, or only DECLARE
it in <code>foo.h</code>.
</blockquote>
<p>You should go the do-not-DECLARE route when the flag is only needed
by <code>foo.cc</code>, and not in any other file. If the flag does
span multiple files, DECLARE it in the associated <code>.h</code>
file, and make others <code>#include</code> that <code>.h</code> file
if they want to access the flag. The <code>#include</code> will make
explicit the dependency between the two files.</p>
by <code>foo.cc</code>, and not in any other file. If you want to
modify the value of the flag in the related test file to see if it is
functioning as expected, DECLARE it in the <code>foo_test.cc</code>
file.
<p>If the flag does span multiple files, DECLARE it in the associated
<code>.h</code> file, and make others <code>#include</code> that
<code>.h</code> file if they want to access the flag. The
<code>#include</code> will make explicit the dependency between the
two files. This causes the flag to be a global variable.</p>
<h2> <A name=together>Putting It Together: How to Set Up Flags</A> </h2>
......
google-gflags (0.8-1) unstable; urgency=low
* New upstream release.
-- Google Inc. <opensource@google.com> Wed, 26 Mar 2008 15:20:18 -0700
google-gflags (0.7-1) unstable; urgency=low
* New upstream release.
......
%define ver %VERSION
%define RELEASE 1
%define rel %{?CUSTOM_RELEASE} %{!?CUSTOM_RELEASE:%RELEASE}
%define prefix /usr
Name: %NAME
Summary: A commandline flags library that allows for distributed flags
Version: %ver
Version: %VERSION
Release: %rel
Group: Development/Libraries
URL: http://code.google.com/p/google-gflags
URL: http://code.google.com/p/gflags
License: BSD
Vendor: Google
Packager: Google Inc. <opensource@google.com>
Source: http://google-gflags.googlecode.com/files/%{NAME}-%{PACKAGE_VERSION}.tar.gz
Source: http://%{NAME}.googlecode.com/files/%{NAME}-%{VERSION}.tar.gz
Distribution: Redhat 7 and above.
Buildroot: %{_tmppath}/%{name}-root
Prefix: %prefix
......@@ -26,6 +25,7 @@ the ability to define flags in the source file in which they're used.
%package devel
Summary: A commandline flags library that allows for distributed flags
Group: Development/Libraries
Requires: %{NAME} = %{VERSION}
%description devel
The %name-devel package contains static and debug libraries and header
......
......@@ -116,6 +116,7 @@ SPECIAL FLAGS: There are a few flags that have special meaning:
--help (or -?) prints a list of all the flags in a human-readable fashion
--helpshort prints a list of all the flags in the 'main' .py file only
--flagfile=foo read flags from foo.
--undefok=f1,f2 ignore unrecognized option errors for f1,f2
-- as in getopt(), terminates flag-processing
Note on --flagfile:
......@@ -157,11 +158,10 @@ EXAMPLE USAGE:
# Flag names are globally defined! So in general, we need to be
# careful to pick names that are unlikely to be used by other libraries.
# If there is a conflict, we'll get an error at import time.
gflags.DEFINE_string("name", "Mr. President" "NAME: your name")
gflags.DEFINE_integer("age", None, "AGE: your age in years", lower_bound=0)
gflags.DEFINE_boolean("debug", 0, "produces debugging output")
gflags.DEFINE_enum("gender", "male", ["male", "female"],
"GENDER: your gender")
gflags.DEFINE_string('name', 'Mr. President', 'your name')
gflags.DEFINE_integer('age', None, 'your age in years', lower_bound=0)
gflags.DEFINE_boolean('debug', 0, 'produces debugging output')
gflags.DEFINE_enum('gender', 'male', ['male', 'female'], 'your gender')
def main(argv):
try:
......@@ -172,13 +172,15 @@ EXAMPLE USAGE:
if FLAGS.debug: print 'non-flag arguments:', argv
print 'Happy Birthday', FLAGS.name
if FLAGS.age is not None:
print "You are a %s, who is %d years old" % (FLAGS.gender, FLAGS.age)
print 'You are a %s, who is %d years old' % (FLAGS.gender, FLAGS.age)
if __name__ == '__main__': main(sys.argv)
if __name__ == '__main__':
main(sys.argv)
"""
import getopt
import os
import re
import sys
# Are we running at least python 2.2?
......@@ -188,16 +190,256 @@ try:
except AttributeError: # a very old python, that lacks sys.version_info
raise NotImplementedError("requires python 2.2.0 or later")
# If we're not running at least python 2.3, define True and False
# Thanks, Guido, for the code.
try:
True, False
except NameError:
False = 0
True = 1
# Are we running under pychecker?
_RUNNING_PYCHECKER = 'pychecker.python' in sys.modules
def _GetCallingModule():
"""
Get the name of the module that's calling into this module; e.g.,
the module calling a DEFINE_foo... function.
"""
# Walk down the stack to find the first globals dict that's not ours.
for depth in range(1, sys.getrecursionlimit()):
if not sys._getframe(depth).f_globals is globals():
return __GetModuleName(sys._getframe(depth).f_globals)
raise AssertionError, "No module was found"
# module exceptions:
class FlagsError(Exception): "The base class for all flags errors"
class DuplicateFlag(FlagsError): "Thrown if there is a flag naming conflict"
class FlagsError(Exception):
"""The base class for all flags errors"""
class DuplicateFlag(FlagsError):
""""Raised if there is a flag naming conflict"""
# A DuplicateFlagError conveys more information than
# a DuplicateFlag. Since there are external modules
# that create DuplicateFlags, the interface to
# DuplicateFlag shouldn't change.
class DuplicateFlagError(DuplicateFlag):
def __init__(self, flagname, flag_values):
self.flagname = flagname
message = "The flag '%s' is defined twice." % self.flagname
flags_by_module = flag_values.__dict__['__flags_by_module']
for module in flags_by_module:
for flag in flags_by_module[module]:
if flag.name == flagname:
message = message + " First from " + module + ","
break
message = message + " Second from " + _GetCallingModule()
Exception.__init__(self, message)
class IllegalFlagValue(FlagsError): "The flag command line argument is illegal"
class UnrecognizedFlag(FlagsError):
"""Raised if a flag is unrecognized"""
# An UnrecognizedFlagError conveys more information than
# an UnrecognizedFlag. Since there are external modules
# that create DuplicateFlags, the interface to
# DuplicateFlag shouldn't change.
class UnrecognizedFlagError(UnrecognizedFlag):
def __init__(self, flagname):
self.flagname = flagname
Exception.__init__(self, "Unknown command line flag '%s'" % flagname)
# Global variable used by expvar
_exported_flags = {}
_help_width = 80 # width of help output
def GetHelpWidth():
"""
Length of help to be used in TextWrap
"""
global _help_width
return _help_width
def CutCommonSpacePrefix(text):
"""
Cut out a common space prefix. If the first line does not start with a space
it is left as is and only in the remaining lines a common space prefix is
being searched for. That means the first line will stay untouched. This is
especially useful to turn doc strings into help texts. This is because some
people prefer to have the doc comment start already after the apostrophy and
then align the following lines while others have the apostrophies on a
seperately line. The function also drops trailing empty lines and ignores
empty lines following the initial content line while calculating the initial
common whitespace.
Args:
text: text to work on
Returns:
the resulting text
"""
text_lines = text.splitlines()
# Drop trailing empty lines
while text_lines and not text_lines[-1]:
text_lines = text_lines[:-1]
if text_lines:
# We got some content, is the first line starting with a space?
if text_lines[0] and text_lines[0][0].isspace():
text_first_line = []
else:
text_first_line = [text_lines.pop(0)]
# Calculate length of common leading whitesppace (only over content lines)
common_prefix = os.path.commonprefix([line for line in text_lines if line])
space_prefix_len = len(common_prefix) - len(common_prefix.lstrip())
# If we have a common space prefix, drop it from all lines
if space_prefix_len:
for index in xrange(len(text_lines)):
if text_lines[index]:
text_lines[index] = text_lines[index][space_prefix_len:]
return '\n'.join(text_first_line + text_lines)
return ''
def TextWrap(text, length=None, indent='', firstline_indent=None, tabs=' '):
"""
Wrap a given text to a maximum line length and return it.
We turn lines that only contain whitespace into empty lines.
We keep new lines.
We also keep tabs (e.g. we do not treat tabs as spaces).
Args:
text: text to wrap
length: maximum length of a line, includes indentation
if this is None then use GetHelpWidth()
indent: indent for all but first line
firstline_indent: indent for first line, if None then fall back to indent
tabs: replacement for tabs
Returns:
wrapped text
Raises:
CommandsError: if indent not shorter than length
CommandsError: if firstline_indent not shorter than length
"""
# Get defaults where callee used None
if length is None:
length = GetHelpWidth()
if indent is None:
indent = ''
if len(indent) >= length:
raise CommandsError('Indent must be shorter than length')
# In line we will be holding the current line which is to be started with
# indent (or firstline_indent if available) and then appended with words.
if firstline_indent is None:
firstline_indent = ''
line = indent
else:
line = firstline_indent
if len(firstline_indent) >= length:
raise CommandsError('First iline indent must be shorter than length')
# If the callee does not care about tabs we simply convert them to spaces
# If callee wanted tabs to be single space then we do that already here.
if not tabs or tabs == ' ':
text = text.replace('\t', ' ')
else:
tabs_are_whitespace = not tabs.strip()
line_regex = re.compile('([ ]*)(\t*)([^ \t]+)', re.MULTILINE)
# Split the text into lines and the lines with the regex above. The resulting
# lines are collected in result[]. For each split we get the spaces, the tabs
# and the next non white space (e.g. next word).
result = []
for text_line in text.splitlines():
# Store result length so we can find out whether processing the next line
# gave any new content
old_result_len = len(result)
# Process next line with line_regex. For optimization we do an rstrip().
# - process tabs (changes either line or word, see below)
# - process word (first try to squeeze on line, then wrap or force wrap)
# Spaces found on the line are ignored, they get added while wrapping as
# needed.
for spaces, current_tabs, word in line_regex.findall(text_line.rstrip()):
# If tabs weren't converted to spaces, handle them now
if current_tabs:
# If the last thing we added was a space anyway then drop it. But
# let's not get rid of the indentation.
if (((result and line != indent) or
(not result and line != firstline_indent)) and line[-1] == ' '):
line = line[:-1]
# Add the tabs, if that means adding whitespace, just add it at the
# line, the rstrip() code while shorten the line down if necessary
if tabs_are_whitespace:
line += tabs * len(current_tabs)
else:
# if not all tab replacement is whitespace we prepend it to the word
word = tabs * len(current_tabs) + word
# Handle the case where word cannot be squeezed onto current last line
if len(line) + len(word) > length and len(indent) + len(word) <= length:
result.append(line.rstrip())
line = indent + word
word = ''
# No space left on line or can we append a space?
if len(line) + 1 >= length:
result.append(line.rstrip())
line = indent
else:
line += ' '
# Add word and shorten it up to allowed line length. Restart next line
# with indent and repeat, or add a space if we're done (word finished)
# This deals with words that caanot fit on one line (e.g. indent + word
# longer than allowed line length).
while len(line) + len(word) >= length:
line += word
result.append(line[:length])
word = line[length:]
line = indent
# Default case, simply append the word and a space
if word:
line += word + ' '
# End of input line. If we have content we finish the line. If the
# current line is just the indent but we had content in during this
# original line then we need to add an emoty line.
if (result and line != indent) or (not result and line != firstline_indent):
result.append(line.rstrip())
elif len(result) == old_result_len:
result.append('')
line = indent
return '\n'.join(result)
def DocToHelp(doc):
"""
Takes a __doc__ string and reformats it as help.
"""
# Get rid of starting and ending white space. Using lstrip() or even strip()
# could drop more than maximum of first line and right space of last line.
doc = doc.strip()
# Get rid of all empty lines
whitespace_only_line = re.compile('^[ \t]+$', re.M)
doc = whitespace_only_line.sub('', doc)
# Cut out common space at line beginnings
doc = CutCommonSpacePrefix(doc)
# Just like this module's comment, comments tend to be aligned somehow.
# In other words they all start with the same amount of white space
# 1) keep double new lines
# 2) keep ws after new lines if not empty line
# 3) all other new lines shall be changed to a space
# Solution: Match new lines between non white space and replace with space.
doc = re.sub('(?<=\S)\n(?=\S)', ' ', doc, re.M)
return doc
def __GetModuleName(globals_dict):
......@@ -209,16 +451,6 @@ def __GetModuleName(globals_dict):
return name
raise AssertionError, "No module was found"
def __GetCallingModule():
"""Get the name of the module that's calling into this module; e.g.,
the module calling a DEFINE_foo... function.
"""
# Walk down the stack to find the first globals dict that's not ours.
for depth in range(1, sys.getrecursionlimit()):
if not sys._getframe(depth).f_globals is globals():
return __GetModuleName(sys._getframe(depth).f_globals)
raise AssertionError, "No module was found"
def _GetMainModule():
"""Get the module name from which execution started."""
for depth in range(1, sys.getrecursionlimit()):
......@@ -262,6 +494,7 @@ class FlagValues:
The str() operator of a 'FlagValues' object provides help for all of
the registered 'Flag' objects.
"""
def __init__(self):
# Since everything in this class is so heavily overloaded,
# the only way of defining and using fields is to access __dict__
......@@ -279,6 +512,15 @@ class FlagValues:
flags_by_module = self.__dict__['__flags_by_module']
flags_by_module.setdefault(module_name, []).append(flag)
def AppendFlagValues(self, flag_values):
"""Append flags registered in another FlagValues instance.
Args:
flag_values: registry to copy from
"""
for flag_name, flag in flag_values.FlagDict().iteritems():
self[flag_name] = flag
def __setitem__(self, name, flag):
"""
Register a new flag variable.
......@@ -294,12 +536,12 @@ class FlagValues:
# Disable check for duplicate keys when pycheck'ing.
if (fl.has_key(name) and not flag.allow_override and
not fl[name].allow_override and not _RUNNING_PYCHECKER):
raise DuplicateFlag, name
raise DuplicateFlagError(name, self)
short_name = flag.short_name
if short_name is not None:
if (fl.has_key(short_name) and not flag.allow_override and
not fl[short_name].allow_override and not _RUNNING_PYCHECKER):
raise DuplicateFlag, short_name
raise DuplicateFlagError(short_name, self)
fl[short_name] = flag
fl[name] = flag
global _exported_flags
......@@ -365,8 +607,16 @@ class FlagValues:
Argument Syntax Conventions, using getopt:
http://www.gnu.org/software/libc/manual/html_mono/libc.html#Getopt
"""
Args:
argv: argument list. Can be of any type that may be converted to a list.
Returns:
The list of arguments not parsed as options, including argv[0]
Raises:
FlagsError: on any parsing error
"""
# Support any sequence type that can be converted to a list
argv = list(argv)
......@@ -384,7 +634,7 @@ class FlagValues:
# having options that may or may not have a parameter. We replace
# instances of the short form --mybool and --nomybool with their
# full forms: --mybool=(true|false).
original_argv = list(argv)
original_argv = list(argv) # list() makes a copy
shortest_matches = None
for name, flag in fl.items():
if not flag.boolean:
......@@ -417,11 +667,40 @@ class FlagValues:
if not flag.boolean:
shortopts += ":"
longopts.append('undefok=')
undefok_flags = []
# In case --undefok is specified, loop to pick up unrecognized
# options one by one.
unrecognized_opts = []
args = argv[1:]
while True:
try:
optlist, unparsed_args = getopt.getopt(argv[1:], shortopts, longopts)
optlist, unparsed_args = getopt.getopt(args, shortopts, longopts)
break
except getopt.GetoptError, e:
raise FlagsError, e
if not e.opt or e.opt in fl:
# Not an unrecognized option, reraise the exception as a FlagsError
raise FlagsError(e)
# Handle an unrecognized option.
unrecognized_opts.append(e.opt)
# Remove offender from args and try again
for arg_index in range(len(args)):
if ((args[arg_index] == '--' + e.opt) or
(args[arg_index] == '-' + e.opt) or
args[arg_index].startswith('--' + e.opt + '=')):
args = args[0:arg_index] + args[arg_index+1:]
break
else:
# We should have found the option, so we don't expect to get
# here. We could assert, but raising the original exception
# might work better.
raise FlagsError(e)
for name, arg in optlist:
if name == '--undefok':
undefok_flags.extend(arg.split(','))
continue
if name.startswith('--'):
# long option
name = name[2:]
......@@ -435,6 +714,12 @@ class FlagValues:
if flag.boolean and short_option: arg = 1
flag.Parse(arg)
# If there were unrecognized options, raise an exception unless
# the options were named via --undefok.
for opt in unrecognized_opts:
if opt not in undefok_flags:
raise UnrecognizedFlagError(opt)
if unparsed_args:
# unparsed_args becomes the first non-flag detected by getopt to
# the end of argv. Because argv may have been modified above,
......@@ -469,6 +754,12 @@ class FlagValues:
return flag_values
def __str__(self):
"""
Generate a help string for all known flags.
"""
return self.GetHelp()
def GetHelp(self, prefix=""):
"""
Generate a help string for all known flags.
"""
......@@ -487,33 +778,47 @@ class FlagValues:
modules = [ main_module ] + modules
for module in modules:
self.__RenderModuleFlags(module, helplist)
self.__RenderOurModuleFlags(module, helplist)
self.__RenderModuleFlags('google3.pyglib.flags',
_SPECIAL_FLAGS.FlagDict().values(),
helplist)
else:
# Just print one long list of flags.
self.__RenderFlagList(self.FlagDict().values(), helplist)
self.__RenderFlagList(
self.FlagDict().values() + _SPECIAL_FLAGS.FlagDict().values(),
helplist, prefix)
return '\n'.join(helplist)
def __RenderModuleFlags(self, module, output_lines):
def __RenderModuleFlags(self, module, flags, output_lines, prefix=""):
"""
Generate a help string for a given module.
"""
output_lines.append('\n%s%s:' % (prefix, module))
self.__RenderFlagList(flags, output_lines, prefix + " ")
def __RenderOurModuleFlags(self, module, output_lines, prefix=""):
"""
Generate a help string for a given module.
"""
flags_by_module = self.__dict__['__flags_by_module']
if module in flags_by_module:
output_lines.append('\n%s:' % module)
self.__RenderFlagList(flags_by_module[module], output_lines)
self.__RenderModuleFlags(module, flags_by_module[module],
output_lines, prefix)
def MainModuleHelp(self):
"""
Generate a help string for all known flags of the main module.
"""
helplist = []
self.__RenderModuleFlags(_GetMainModule(), helplist)
self.__RenderOurModuleFlags(_GetMainModule(), helplist)
return '\n'.join(helplist)
def __RenderFlagList(self, flaglist, output_lines):
def __RenderFlagList(self, flaglist, output_lines, prefix=" "):
fl = self.FlagDict()
special_fl = _SPECIAL_FLAGS.FlagDict()
flaglist = [(flag.name, flag) for flag in flaglist]
flaglist.sort()
flagset = {}
......@@ -521,12 +826,13 @@ class FlagValues:
# It's possible this flag got deleted or overridden since being
# registered in the per-module flaglist. Check now against the
# canonical source of current flag information, the FlagDict.
if fl.get(name, None) != flag: # a different flag is using this name now
if fl.get(name, None) != flag and special_fl.get(name, None) != flag:
# a different flag is using this name now
continue
# only print help once
if flagset.has_key(flag): continue
flagset[flag] = 1
flaghelp = " "
flaghelp = ""
if flag.short_name: flaghelp += "-%s," % flag.short_name
if flag.boolean:
flaghelp += "--[no]%s" % flag.name + ":"
......@@ -535,10 +841,16 @@ class FlagValues:
flaghelp += " "
if flag.help:
flaghelp += flag.help
flaghelp = TextWrap(flaghelp, indent=prefix+" ",
firstline_indent=prefix)
if flag.default_as_str:
flaghelp += "\n (default: %s)" % flag.default_as_str
flaghelp += "\n"
flaghelp += TextWrap("(default: %s)" % flag.default_as_str,
indent=prefix+" ")
if flag.parser.syntactic_help:
flaghelp += "\n (%s)" % flag.parser.syntactic_help
flaghelp += "\n"
flaghelp += TextWrap("(%s)" % flag.parser.syntactic_help,
indent=prefix+" ")
output_lines.append(flaghelp)
def get(self, name, default):
......@@ -730,7 +1042,7 @@ class FlagValues:
def FlagsIntoString(self):
"""
Retreive a string version of all the flags with assignments stored
Retrieve a string version of all the flags with assignments stored
in this FlagValues object. Should mirror the behavior of the c++
version of FlagsIntoString. Each flag assignment is seperated by
a newline.
......@@ -751,9 +1063,7 @@ class FlagValues:
out_file = open(filename, 'a')
out_file.write(self.FlagsIntoString())
out_file.close()
#end of the FLAGS registry class
# end of FlagValues definition
# The global FlagValues instance
FLAGS = FlagValues()
......@@ -869,6 +1179,7 @@ class Flag:
self.value = None
self.default = value
self.default_as_str = self.__GetParsedValueAsString(value)
# End of Flag definition
class ArgumentParser:
"""
......@@ -929,7 +1240,7 @@ def DEFINE_flag(flag, flag_values=FLAGS):
default, the global FLAGS 'FlagValue' object is used.
Typical users will use one of the more specialized DEFINE_xxx
functions, such as DEFINE_string or DEFINEE_integer. But developers
functions, such as DEFINE_string or DEFINE_integer. But developers
who need to create Flag objects themselves should use this function to
register their flags.
"""
......@@ -944,7 +1255,7 @@ def DEFINE_flag(flag, flag_values=FLAGS):
# of which module is creating the flags.
# Tell FLAGS who's defining flag.
FLAGS._RegisterFlagByModule(__GetCallingModule(), flag)
FLAGS._RegisterFlagByModule(_GetCallingModule(), flag)
###############################
......@@ -1369,3 +1680,18 @@ def DEFINE_multi_int(name, default, help, lower_bound=None, upper_bound=None,
# these flagnames for their own purposes, if they want.
DEFINE_flag(HelpFlag())
DEFINE_flag(HelpshortFlag())
# Define special flags here so that help may be generated for them.
_SPECIAL_FLAGS = FlagValues()
DEFINE_string(
'flagfile', "",
"Insert flag definitions from the given file into the command line.",
_SPECIAL_FLAGS)
DEFINE_string(
'undefok', "",
"comma-separated list of flag names that it is okay to specify "
"on the command line even if the program does not define a flag "
"with that name. IMPORTANT: flags in this list that have "
"arguments MUST use the --flag=value format.", _SPECIAL_FLAGS)
......@@ -44,6 +44,15 @@ import unittest
import gflags as flags
FLAGS=flags.FLAGS
# If we're not running at least python 2.3, as is the case when
# invoked from flags_unittest_2_2, define True and False.
# Thanks, Guido, for the code.
try:
True, False
except NameError:
False = 0
True = 1
class FlagsUnitTest(unittest.TestCase):
"Flags Unit Test"
......@@ -179,10 +188,10 @@ class FlagsUnitTest(unittest.TestCase):
assert len(argv) == 1, "wrong number of arguments pulled"
assert argv[0] == './program', "program name not preserved"
assert FLAGS['debug'].present == 1
assert FLAGS['debug'].value == True
assert FLAGS['debug'].value
FLAGS.Reset()
assert FLAGS['debug'].present == 0
assert FLAGS['debug'].value == False
assert not FLAGS['debug'].value
# Test that reset restores default value when default value is None.
argv = ('./program', '--kwery=who')
......@@ -531,11 +540,32 @@ class FlagsUnitTest(unittest.TestCase):
self.assert_(str(FLAGS).find('runhelp d31') == -1)
self.assert_(str(FLAGS).find('runhelp d32') != -1)
# Make sure AppendFlagValues works
new_flags = flags.FlagValues()
flags.DEFINE_boolean("new1", 0, "runhelp n1", flag_values=new_flags)
flags.DEFINE_boolean("new2", 0, "runhelp n2", flag_values=new_flags)
self.assertEqual(len(new_flags.FlagDict()), 2)
old_len = len(FLAGS.FlagDict())
FLAGS.AppendFlagValues(new_flags)
self.assertEqual(len(FLAGS.FlagDict())-old_len, 2)
self.assertEqual("new1" in FLAGS.FlagDict(), True)
self.assertEqual("new2" in FLAGS.FlagDict(), True)
# Make sure AppendFlagValues fails on duplicates
flags.DEFINE_boolean("dup4", 0, "runhelp d41")
new_flags = flags.FlagValues()
flags.DEFINE_boolean("dup4", 0, "runhelp d42", flag_values=new_flags)
try:
FLAGS.AppendFlagValues(new_flags)
raise AssertionError("ignore_copy was not set but caused no exception")
except flags.DuplicateFlag, e:
pass
# Integer out of bounds
try:
argv = ('./program', '--repeat=-4')
FLAGS(argv)
raise AssertionError('integer bounds exception not thrown:'
raise AssertionError('integer bounds exception not raised:'
+ str(FLAGS.repeat))
except flags.IllegalFlagValue:
pass
......@@ -544,7 +574,7 @@ class FlagsUnitTest(unittest.TestCase):
try:
argv = ('./program', '--repeat=2.5')
FLAGS(argv)
raise AssertionError("malformed integer value exception not thrown")
raise AssertionError("malformed integer value exception not raised")
except flags.IllegalFlagValue:
pass
......@@ -552,7 +582,7 @@ class FlagsUnitTest(unittest.TestCase):
try:
argv = ('./program', '--name')
FLAGS(argv)
raise AssertionError("Flag argument required exception not thrown")
raise AssertionError("Flag argument required exception not raised")
except flags.FlagsError:
pass
......@@ -560,23 +590,16 @@ class FlagsUnitTest(unittest.TestCase):
try:
argv = ('./program', '--debug=goofup')
FLAGS(argv)
raise AssertionError("No argument allowed exception not thrown")
raise AssertionError("No argument allowed exception not raised")
except flags.FlagsError:
pass
# Unknown argument --nosuchflag
try:
argv = ('./program', '--nosuchflag', '--name=Bob', 'extra')
FLAGS(argv)
raise AssertionError("Unknown argument exception not thrown")
except flags.FlagsError:
pass
# Non-numeric argument for integer flag --repeat
try:
argv = ('./program', '--repeat', 'Bob', 'extra')
FLAGS(argv)
raise AssertionError("Illegal flag value exception not thrown")
raise AssertionError("Illegal flag value exception not raised")
except flags.IllegalFlagValue:
pass
......@@ -708,7 +731,7 @@ class FlagsUnitTest(unittest.TestCase):
# end testThree def
def testMethod_flagfiles_4(self):
"""Tests parsing self referetial files + arguments of simulated argv.
"""Tests parsing self-referential files + arguments of simulated argv.
This test should print a warning to stderr of some sort.
"""
self.__DeclareSomeFlags()
......@@ -854,6 +877,303 @@ class FlagsUnitTest(unittest.TestCase):
FLAGS.__delattr__('zz')
FLAGS.__delattr__('nozz')
def test_twodasharg_first(self):
flags.DEFINE_string("twodash_name", "Bob", "namehelp")
flags.DEFINE_string("twodash_blame", "Rob", "blamehelp")
argv = ('./program',
'--',
'--twodash_name=Harry')
argv = FLAGS(argv)
self.assertEqual('Bob', FLAGS.twodash_name)
self.assertEqual(argv[1], '--twodash_name=Harry')
if __name__ == '__main__':
def test_twodasharg_middle(self):
flags.DEFINE_string("twodash2_name", "Bob", "namehelp")
flags.DEFINE_string("twodash2_blame", "Rob", "blamehelp")
argv = ('./program',
'--twodash2_blame=Larry',
'--',
'--twodash2_name=Harry')
argv = FLAGS(argv)
self.assertEqual('Bob', FLAGS.twodash2_name)
self.assertEqual('Larry', FLAGS.twodash2_blame)
self.assertEqual(argv[1], '--twodash2_name=Harry')
def test_onedasharg_first(self):
flags.DEFINE_string("onedash_name", "Bob", "namehelp")
flags.DEFINE_string("onedash_blame", "Rob", "blamehelp")
argv = ('./program',
'-',
'--onedash_name=Harry')
argv = FLAGS(argv)
self.assertEqual(argv[1], '-')
# TODO(csilvers): we should still parse --onedash_name=Harry as a
# flag, but currently we don't (we stop flag processing as soon as
# we see the first non-flag).
def test_unrecognized_flags(self):
# Unknown flag --nosuchflag
try:
argv = ('./program', '--nosuchflag', '--name=Bob', 'extra')
FLAGS(argv)
raise AssertionError("Unknown flag exception not raised")
except flags.UnrecognizedFlag, e:
assert e.flagname == 'nosuchflag'
# Unknown flag -w (short option)
try:
argv = ('./program', '-w', '--name=Bob', 'extra')
FLAGS(argv)
raise AssertionError("Unknown flag exception not raised")
except flags.UnrecognizedFlag, e:
assert e.flagname == 'w'
# Unknown flag --nosuchflagwithparam=foo
try:
argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob', 'extra')
FLAGS(argv)
raise AssertionError("Unknown flag exception not raised")
except flags.UnrecognizedFlag, e:
assert e.flagname == 'nosuchflagwithparam'
# Allow unknown flag --nosuchflag if specified with undefok
argv = ('./program', '--nosuchflag', '--name=Bob',
'--undefok=nosuchflag', 'extra')
argv = FLAGS(argv)
assert len(argv) == 2, "wrong number of arguments pulled"
assert argv[0]=='./program', "program name not preserved"
assert argv[1]=='extra', "extra argument not preserved"
# But not if the flagname is misspelled:
try:
argv = ('./program', '--nosuchflag', '--name=Bob',
'--undefok=nosuchfla', 'extra')
FLAGS(argv)
raise AssertionError("Unknown flag exception not raised")
except flags.UnrecognizedFlag, e:
assert e.flagname == 'nosuchflag'
try:
argv = ('./program', '--nosuchflag', '--name=Bob',
'--undefok=nosuchflagg', 'extra')
FLAGS(argv)
raise AssertionError("Unknown flag exception not raised")
except flags.UnrecognizedFlag:
assert e.flagname == 'nosuchflag'
# Allow unknown short flag -w if specified with undefok
argv = ('./program', '-w', '--name=Bob', '--undefok=w', 'extra')
argv = FLAGS(argv)
assert len(argv) == 2, "wrong number of arguments pulled"
assert argv[0]=='./program', "program name not preserved"
assert argv[1]=='extra', "extra argument not preserved"
# Allow unknown flag --nosuchflagwithparam=foo if specified
# with undefok
argv = ('./program', '--nosuchflagwithparam=foo', '--name=Bob',
'--undefok=nosuchflagwithparam', 'extra')
argv = FLAGS(argv)
assert len(argv) == 2, "wrong number of arguments pulled"
assert argv[0]=='./program', "program name not preserved"
assert argv[1]=='extra', "extra argument not preserved"
# Even if undefok specifies multiple flags
argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo',
'--name=Bob',
'--undefok=nosuchflag,w,nosuchflagwithparam',
'extra')
argv = FLAGS(argv)
assert len(argv) == 2, "wrong number of arguments pulled"
assert argv[0]=='./program', "program name not preserved"
assert argv[1]=='extra', "extra argument not preserved"
# However, not if undefok doesn't specify the flag
try:
argv = ('./program', '--nosuchflag', '--name=Bob',
'--undefok=another_such', 'extra')
FLAGS(argv)
raise AssertionError("Unknown flag exception not raised")
except flags.UnrecognizedFlag, e:
assert e.flagname == 'nosuchflag'
# Make sure --undefok doesn't mask other option errors.
try:
# Provide an option requiring a parameter but not giving it one.
argv = ('./program', '--undefok=name', '--name')
FLAGS(argv)
raise AssertionError("Missing option parameter exception not raised")
except flags.UnrecognizedFlag:
raise AssertionError("Wrong kind of error exception raised")
except flags.FlagsError:
pass
# Test --undefok <list>
argv = ('./program', '--nosuchflag', '-w', '--nosuchflagwithparam=foo',
'--name=Bob',
'--undefok',
'nosuchflag,w,nosuchflagwithparam',
'extra')
argv = FLAGS(argv)
assert len(argv) == 2, "wrong number of arguments pulled"
assert argv[0]=='./program', "program name not preserved"
assert argv[1]=='extra', "extra argument not preserved"
def test_nonglobal_flags(self):
"""Test use of non-global FlagValues"""
nonglobal_flags = flags.FlagValues()
flags.DEFINE_string("nonglobal_flag", "Bob", "flaghelp", nonglobal_flags)
argv = ('./program',
'--nonglobal_flag=Mary',
'extra')
argv = nonglobal_flags(argv)
assert len(argv) == 2, "wrong number of arguments pulled"
assert argv[0]=='./program', "program name not preserved"
assert argv[1]=='extra', "extra argument not preserved"
assert nonglobal_flags['nonglobal_flag'].value == 'Mary'
def test_unrecognized_nonglobal_flags(self):
"""Test unrecognized non-global flags"""
nonglobal_flags = flags.FlagValues()
argv = ('./program',
'--nosuchflag')
try:
argv = nonglobal_flags(argv)
raise AssertionError("Unknown flag exception not raised")
except flags.UnrecognizedFlag, e:
assert e.flagname == 'nosuchflag'
pass
argv = ('./program',
'--nosuchflag',
'--undefok=nosuchflag')
argv = nonglobal_flags(argv)
assert len(argv) == 1, "wrong number of arguments pulled"
assert argv[0]=='./program', "program name not preserved"
def test_main_module_help(self):
"""Test MainModuleHelp()"""
help = FLAGS.MainModuleHelp()
# When this test is invoked on behalf of flags_unittest_2_2,
# the main module has not defined any flags. Since there's
# no easy way to run this script in our test environment
# directly from python2.2, don't bother to test the output
# of MainModuleHelp() in that scenario.
if sys.version.startswith('2.2.'):
return
expected_help = "\n" + sys.argv[0] + ':' + """
--[no]debug: debughelp
(default: 'false')
-u,--[no]dup1: runhelp d12
(default: 'true')
-u,--[no]dup2: runhelp d22
(default: 'true')
-u,--[no]dup3: runhelp d32
(default: 'true')
--[no]dup4: runhelp d41
(default: 'false')
-?,--[no]help: show this help
--[no]helpshort: show usage only for this module
--kwery: <who|what|why|where|when>: ?
--l: how long to be
(default: '9223372032559808512')
(an integer)
--letters: a list of letters
(default: 'a,b,c')
(a comma separated list)
-m,--m_str: string option that can occur multiple times;
repeat this option to specify a list of values
(default: "['def1', 'def2']")
--name: namehelp
(default: 'Bob')
--[no]noexec: boolean flag with no as prefix
(default: 'true')
--[no]q: quiet mode
(default: 'true')
--[no]quack: superstring of 'q'
(default: 'false')
-r,--repeat: how many times to repeat (0-5)
(default: '4')
(a non-negative integer)
-s,--s_str: string option that can occur multiple times;
repeat this option to specify a list of values
(default: "['sing1']")
--[no]test0: test boolean parsing
--[no]test1: test boolean parsing
--[no]testget1: test parsing with defaults
--[no]testget2: test parsing with defaults
--[no]testget3: test parsing with defaults
--testget4: test parsing with defaults
(an integer)
--testlist: test lists parsing
(default: '')
(a comma separated list)
--[no]testnone: test boolean parsing
--testspacelist: tests space lists parsing
(default: '')
(a whitespace separated list)
--x: how eXtreme to be
(default: '3')
(an integer)
-z,--[no]zoom1: runhelp z1
(default: 'false')"""
if help != expected_help:
print "Error: FLAGS.MainModuleHelp() didn't return the expected result."
print "Got:"
print help
print "[End of got]"
help_lines = help.split('\n')
expected_help_lines = expected_help.split('\n')
num_help_lines = len(help_lines)
num_expected_help_lines = len(expected_help_lines)
if num_help_lines != num_expected_help_lines:
print "Number of help lines = %d, expected %d" % (
num_help_lines, num_expected_help_lines)
num_to_match = min(num_help_lines, num_expected_help_lines)
for i in range(num_to_match):
if help_lines[i] != expected_help_lines[i]:
print "One discrepancy: Got:"
print help_lines[i]
print "Expected:"
print expected_help_lines[i]
break
else:
# If we got here, found no discrepancy, print first new line.
if num_help_lines > num_expected_help_lines:
print "New help line:"
print help_lines[num_expected_help_lines]
elif num_expected_help_lines > num_help_lines:
print "Missing expected help line:"
print expected_help_lines[num_help_lines]
else:
print "Bug in this test -- discrepancy detected but not found."
self.fail()
def test_create_flag_errors(self):
# Since the exception classes are exposed, nothing stops users
# from creating their own instances. This test makes sure that
# people modifying the flags module understand that the external
# mechanisms for creating the exceptions should continue to work.
e = flags.FlagsError()
e = flags.FlagsError("message")
e = flags.DuplicateFlag()
e = flags.DuplicateFlag("message")
e = flags.IllegalFlagValue()
e = flags.IllegalFlagValue("message")
e = flags.UnrecognizedFlag()
e = flags.UnrecognizedFlag("message")
def main():
unittest.main()
if __name__ == '__main__':
main()
......@@ -32,7 +32,7 @@
from distutils.core import setup
setup(name='gflags',
version='0.6',
version='0.8',
description='Google Commandline Flags Module',
license='BSD',
author='Google Inc.',
......
......@@ -40,7 +40,6 @@
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <pthread.h>
#include <fnmatch.h>
#include <pthread.h>
#include <string>
......@@ -96,8 +95,7 @@ static const char kError[] = "ERROR: ";
// The help message indicating that the commandline flag has been
// 'stripped'. It will not show up when doing "-help" and its
// variants. The flag is stripped if STRIP_FLAG_HELP is set to 1
// before including base/commandlineflags.h (or in
// base/global_strip_options.h).
// before including google/gflags.h.
const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
......@@ -105,7 +103,7 @@ const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
// Enables deferred processing of flags in dynamically loaded libraries.
static bool allow_command_line_reparsing = false;
static bool logging_is_probably_set_up = false; // google3-specific
static bool logging_is_probably_set_up = false;
// This is used by the unittest to test error-exit code
void (*commandlineflags_exitfunc)(int) = &exit; // from stdlib.h
......@@ -114,7 +112,7 @@ void (*commandlineflags_exitfunc)(int) = &exit; // from stdlib.h
// FlagValue
// This represent the value a single flag might have. The major
// functionality is to convert from a string to an object of a
// given type, and back.
// given type, and back. Thread-compatible.
// --------------------------------------------------------------------
class FlagValue {
......@@ -375,21 +373,19 @@ CommandLineFlag::~CommandLineFlag() {
const char* CommandLineFlag::CleanFileName() const {
// Compute top-level directory & file that this appears in
// search full path backwards.
// Stop going backwards at kGoogle; and skip by the first slash.
// E.g.
// filename_where_defined = "froogle/wrapping/autowrap/clustering/**.cc"
// filename_where_defined = "file/util/fileutil.cc"
static const char kGoogle[] = ""; // can set this to whatever
// Stop going backwards at kRootDir; and skip by the first slash.
static const char kRootDir[] = ""; // can set this to root directory,
// e.g. "myproject"
if (sizeof(kGoogle)-1 == 0) // no prefix to strip
if (sizeof(kRootDir)-1 == 0) // no prefix to strip
return filename();
const char* clean_name = filename() + strlen(filename()) - 1;
while ( clean_name > filename() ) {
if (*clean_name == PATH_SEPARATOR) {
if (strncmp(clean_name, kGoogle, sizeof(kGoogle)-1) == 0) {
// ".../google/base/logging.cc" ==> "base/logging.cc"
clean_name += sizeof(kGoogle)-1; // past "/google/"
if (strncmp(clean_name, kRootDir, sizeof(kRootDir)-1) == 0) {
// ".../myproject/base/logging.cc" ==> "base/logging.cc"
clean_name += sizeof(kRootDir)-1; // past "/myproject/"
break;
}
}
......@@ -790,7 +786,7 @@ const char* ProgramInvocationName() { // like the GNU libc fn
}
const char* ProgramInvocationShortName() { // like the GNU libc fn
const char* slash = strrchr(argv0, '/');
#ifdef OS_WINDOWS
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
if (!slash) slash = strrchr(argv0, '\\');
#endif
return slash ? slash + 1 : argv0;
......@@ -939,7 +935,8 @@ uint32 CommandLineFlagParser::ParseNewCommandLineFlags(int* argc, char*** argv,
char* arg = (*argv)[i];
// Like getopt(), we permute non-option flags to be at the end.
if (arg[0] != '-') { // must be a program argument
if (arg[0] != '-' || // must be a program argument
(arg[0] == '-' && arg[1] == '\0')) { // "-" is an argument, not a flag
memmove((*argv) + i, (*argv) + i+1, (*argc - (i+1)) * sizeof((*argv)[i]));
(*argv)[*argc-1] = arg; // we go last
first_nonopt--; // we've been pushed onto the stack
......@@ -950,7 +947,7 @@ uint32 CommandLineFlagParser::ParseNewCommandLineFlags(int* argc, char*** argv,
if (arg[0] == '-') arg++; // allow leading '-'
if (arg[0] == '-') arg++; // or leading '--'
// - and -- alone mean what they do for GNU: stop options parsing
// -- alone means what it does for GNU: stop options parsing
if (*arg == '\0') {
first_nonopt = i+1;
break;
......
......@@ -37,8 +37,8 @@
// reporting flags, but we also have flags like --helpxml, etc.
//
// There's only one function that's meant to be called externally:
// HandleCommandLineHelpFlags(). (Well, actually,
// ShowUsageWithFlags() and ShowUsageWithFlagsRestrict() can be called
// HandleCommandLineHelpFlags(). (Well, actually, ShowUsageWithFlags(),
// ShowUsageWithFlagsRestrict(), and DescribeOneFlag() can be called
// externally too, but there's little need for it.) These are all
// declared in the main commandlineflags.h header file.
//
......@@ -110,7 +110,7 @@ static void AddString(const string& s,
// Create a descriptive string for a flag.
// Goes to some trouble to make pretty line breaks.
static string DescribeOneFlag(const CommandLineFlagInfo& flag) {
string DescribeOneFlag(const CommandLineFlagInfo& flag) {
string main_part = (string(" -") + flag.name +
" (" + flag.description + ')');
const char* c_string = main_part.c_str();
......@@ -242,8 +242,8 @@ static bool FileMatchesSubstring(const string& filename,
// Show help for every filename which matches any of the target substrings.
// If substrings is empty, shows help for every file. If a flag's help message
// has been stripped (e.g. by adding '#define STRIP_FLAG_HELP 1' to
// base/global_strip_options.h), then this flag will not be displayed by
// has been stripped (e.g. by adding '#define STRIP_FLAG_HELP 1' before
// including google/gflags.h), then this flag will not be displayed by
// '--help' and its variants.
static void ShowUsageWithFlagsMatching(const char *argv0,
const vector<string> &substrings) {
......
......@@ -35,6 +35,7 @@
#include "config.h"
#include <stdio.h>
#include <stdlib.h> // for &exit
#include <string.h>
#include <unistd.h> // for unlink()
#include <sys/stat.h> // for mkdir()
......@@ -46,8 +47,7 @@
using std::vector;
using std::string;
// Returns the number of elements in an array. We don't use the safer
// version in base/basictypes.h as commandlineflags is open-sourced.
// Returns the number of elements in an array.
#define GET_ARRAY_SIZE(arr) (sizeof(arr)/sizeof(*(arr)))
DECLARE_string(tryfromenv); // in commandlineflags.cc
......@@ -1109,6 +1109,43 @@ TEST(ParseCommandLineFlagsUsesLastDefinitionTest,
EXPECT_EQ(3, ParseTestFlag(false, GET_ARRAY_SIZE(argv) - 1, argv));
}
TEST(ParseCommandLineFlagsAndDashArgs, TwoDashArgFirst) {
const char* argv[] = {
"my_test",
"--",
"--test_flag=0",
NULL,
};
EXPECT_EQ(-1, ParseTestFlag(true, GET_ARRAY_SIZE(argv) - 1, argv));
EXPECT_EQ(-1, ParseTestFlag(false, GET_ARRAY_SIZE(argv) - 1, argv));
}
TEST(ParseCommandLineFlagsAndDashArgs, TwoDashArgMiddle) {
const char* argv[] = {
"my_test",
"--test_flag=7",
"--",
"--test_flag=0",
NULL,
};
EXPECT_EQ(7, ParseTestFlag(true, GET_ARRAY_SIZE(argv) - 1, argv));
EXPECT_EQ(7, ParseTestFlag(false, GET_ARRAY_SIZE(argv) - 1, argv));
}
TEST(ParseCommandLineFlagsAndDashArgs, OneDashArg) {
const char* argv[] = {
"my_test",
"-",
"--test_flag=0",
NULL,
};
EXPECT_EQ(0, ParseTestFlag(true, GET_ARRAY_SIZE(argv) - 1, argv));
EXPECT_EQ(0, ParseTestFlag(false, GET_ARRAY_SIZE(argv) - 1, argv));
}
static int Main(int argc, char **argv) {
// We need to call SetArgv before InitGoogle, so our "test" argv will
// win out over this executable's real argv. That makes running this
......
......@@ -50,6 +50,24 @@
//
// For more details, see
// doc/gflags.html
//
// --- A note about thread-safety:
//
// We describe many functions in this routine as being thread-hostile,
// thread-compatible, or thread-safe. Here are the meanings we use:
//
// thread-safe: it is safe for multiple threads to call this routine
// (or, when referring to a class, methods of this class)
// concurrently.
// thread-hostile: it is not safe for multiple threads to call this
// routine (or methods of this class) concurrently. In gflags,
// most thread-hostile routines are intended to be called early in,
// or even before, main() -- that is, before threads are spawned.
// thread-compatible: it is safe for multiple threads to read from
// this variable (when applied to variables), or to call const
// methods of this class (when applied to classes), as long as no
// other thread is writing to the variable or calling non-const
// methods of this class.
#ifndef BASE_COMMANDLINEFLAGS_H__
#define BASE_COMMANDLINEFLAGS_H__
......@@ -120,13 +138,22 @@ extern void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT);
extern void ShowUsageWithFlags(const char *argv0); // what --help does
extern void ShowUsageWithFlagsRestrict(const char *argv0, const char *restrict);
// Create a descriptive string for a flag.
// Goes to some trouble to make pretty line breaks.
extern std::string DescribeOneFlag(const CommandLineFlagInfo& flag);
// Thread-hostile; meant to be called before any threads are spawned.
extern void SetArgv(int argc, const char** argv);
// The following functions are thread-safe as long as SetArgv() is
// only called before any threads start.
extern const std::vector<std::string>& GetArgvs(); // all of argv as a vector
extern const char* GetArgv(); // all of argv as a string
extern const char* GetArgv0(); // only argv0
extern uint32 GetArgvSum(); // simple checksum of argv
extern const char* ProgramInvocationName(); // argv0, or "UNKNOWN" if not set
extern const char* ProgramInvocationShortName(); // basename(argv0)
// ProgramUsage() is thread-safe as long as SetUsageMessage() is only
// called before any threads start.
extern const char* ProgramUsage(); // string set by SetUsageMessage()
......@@ -135,6 +162,8 @@ extern const char* ProgramUsage(); // string set by SetUsageMessage()
// or whatever, and set them by calling "FLAGS_foo = bar" (or, more
// commonly, via the DEFINE_foo macro). But if you need a bit more
// control, we have programmatic ways to get/set the flags as well.
// These programmatic ways to access flags are thread-safe, but direct
// access is only thread-compatible.
// Return true iff the flagname was found.
// OUTPUT is set to the flag's value, or unchanged if we return false.
......@@ -196,6 +225,8 @@ extern std::string SetCommandLineOptionWithMode(const char* name, const char* va
// work is done in the constructor and destructor, so in the standard
// usage example above, the compiler would complain that it's an
// unused variable.
//
// This class is thread-safe.
class FlagSaver {
public:
......@@ -251,6 +282,7 @@ extern const char *StringFromEnv(const char *varname, const char *defval);
// usage += argv[0] + " <uselessarg1> <uselessarg2>";
// SetUsageMessage(usage);
// Do not include commandline flags in the usage: we do that for you!
// Thread-hostile; meant to be called before any threads are spawned.
extern void SetUsageMessage(const std::string& usage);
// Looks for flags in argv and parses them. Rearranges argv to put
......@@ -280,8 +312,10 @@ extern uint32 ParseCommandLineNonHelpFlags(int *argc, char*** argv,
// it's too late to change that now. :-(
extern void HandleCommandLineHelpFlags(); // in commandlineflags_reporting.cc
// Allow command line reparsing. Disables the error normaly generated
// when an unknown flag is found, since it may be found in a later parse.
// Allow command line reparsing. Disables the error normally
// generated when an unknown flag is found, since it may be found in a
// later parse. Thread-hostile; meant to be called before any threads
// are spawned.
extern void AllowCommandLineReparsing();
// Reparse the flags that have not yet been recognized.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment