Thu Sep 10 12:53:04 2009 Google Inc. <opensource@google.com>
* google-gflags: version 1.2
* PORTABILITY: can now build and run tests under mingw (csilvers)
* Using a string arg for a bool flag is a compile-time error (rbayardo)
* Add --helpxml to gflags.py (salcianu)
* Protect against a hypothetical global d'tor mutex problem (csilvers)
* BUGFIX: can now define a flag after 'using namespace google' (hamaji)
git-svn-id: https://gflags.googlecode.com/svn/trunk@32 6586e3c6-dcc4-952a-343f-ff74eb82781d
diff --git a/ChangeLog b/ChangeLog
index 222ec31..dd9decf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Thu Sep 10 12:53:04 2009 Google Inc. <opensource@google.com>
+
+ * google-gflags: version 1.2
+ * PORTABILITY: can now build and run tests under mingw (csilvers)
+ * Using a string arg for a bool flag is a compile-time error (rbayardo)
+ * Add --helpxml to gflags.py (salcianu)
+ * Protect against a hypothetical global d'tor mutex problem (csilvers)
+ * BUGFIX: can now define a flag after 'using namespace google' (hamaji)
+
Tue Apr 14 12:35:25 2009 Google Inc. <opensource@google.com>
* google-gflags: version 1.1
diff --git a/Makefile.am b/Makefile.am
index d0db111..b7b45ea 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -67,7 +67,7 @@
libgflags_nothreads_la_SOURCES = $(GFLAGS_SOURCES)
libgflags_nothreads_la_CXXFLAGS = -DNDEBUG -DNO_THREADS
-TESTS += gflags_unittest$(EXEEXT)
+TESTS += gflags_unittest
gflags_unittest_SOURCES = $(gflagsinclude_HEADERS) src/config.h \
src/gflags_unittest.cc
gflags_unittest_CXXFLAGS = $(PTHREAD_CFLAGS)
@@ -75,14 +75,14 @@
gflags_unittest_LDADD = libgflags.la
# Also make sure this works when we don't link in pthreads
-TESTS += gflags_nothreads_unittest$(EXEEXT)
+TESTS += gflags_nothreads_unittest
gflags_nothreads_unittest_SOURCES = $(gflags_unittest_SOURCES)
gflags_nothreads_unittest_LDADD = libgflags_nothreads.la
# We also want to test that things work properly when the file that
# holds main() has a name ending with -main or _main. To keep the
# Makefile small :-), we test the no-threads version of these.
-TESTS += gflags_unittest2$(EXEEXT)
+TESTS += gflags_unittest2
gflags_unittest2_SOURCES = $(gflagsinclude_HEADERS) src/config.h \
src/gflags_unittest-main.cc
gflags_unittest2_LDADD = libgflags_nothreads.la
@@ -91,7 +91,7 @@
cp -p src/gflags_unittest.cc src/gflags_unittest-main.cc
CLEANFILES += src/gflags_unittest-main.cc
-TESTS += gflags_unittest3$(EXEEXT)
+TESTS += gflags_unittest3
gflags_unittest3_SOURCES = $(gflagsinclude_HEADERS) src/config.h \
src/gflags_unittest_main.cc
gflags_unittest3_LDADD = libgflags_nothreads.la
@@ -109,9 +109,32 @@
gflags_unittest_sh: gflags_unittest$(EXEEXT) \
gflags_unittest2$(EXEEXT) \
gflags_unittest3$(EXEEXT)
- bash --version >/dev/null && export SH=bash || export SH=sh; \
+ bash --version >/dev/null 2>&1 && export SH=bash || export SH=sh; \
$$SH "$(top_srcdir)/src/gflags_unittest.sh" "$(PWD)/gflags_unittest" \
- "$(top_srcdir)"
+ "$(top_srcdir)" "@TMPDIR@"
+
+# These are negative-compilation tests. We want to make sure these
+# erroneous use of the flags macros correctly fail to compile.
+# Again, we just bother testing with the no-threads version of the library.
+check_SCRIPTS += gflags_nc_test1
+gflags_nc_test1: $(gflagsinclude_HEADERS) src/config.h src/gflags_nc.cc
+ ! $(CXX) -DTEST_SWAPPED_ARGS $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gflags_nc_test1.o $(srcdir)/src/gflags_nc.cc && echo "Compile failed, like it was supposed to"
+
+check_SCRIPTS += gflags_nc_test2
+gflags_nc_test2: $(gflagsinclude_HEADERS) src/config.h src/gflags_nc.cc
+ ! $(CXX) -DTEST_INT_INSTEAD_OF_BOOL $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gflags_nc_test2.o $(srcdir)/src/gflags_nc.cc && echo "Compile failed, like it was supposed to"
+
+check_SCRIPTS += gflags_nc_test3
+gflags_nc_test3: $(gflagsinclude_HEADERS) src/config.h src/gflags_nc.cc
+ ! $(CXX) -DTEST_BOOL_IN_QUOTES $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gflags_nc_test3.o $(srcdir)/src/gflags_nc.cc && echo "Compile failed, like it was supposed to"
+
+# This one, on the other hand, should succeed.
+check_SCRIPTS += gflags_nc_test4
+gflags_nc_test4: $(gflagsinclude_HEADERS) src/config.h src/gflags_nc.cc
+ $(CXX) -DSANITY $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gflags_nc_test4.o $(srcdir)/src/gflags_nc.cc && echo "Compile failed, like it was supposed to"
+
+# This file isn't covered under any rule that would cause it to be distributed.
+dist_noinst_DATA += src/gflags_nc.cc
# These aren't part of the c++ source, but we want them to be distributed
PYTHON = python/setup.py \
diff --git a/Makefile.in b/Makefile.in
index 39bc78f..8d0fff8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -158,7 +158,7 @@
{ test ! -d $(distdir) \
|| { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
&& rm -fr $(distdir); }; }
-DIST_ARCHIVES = $(distdir).tar.gz
+DIST_ARCHIVES = $(distdir).tar.gz $(distdir).zip
GZIP_ENV = --best
distuninstallcheck_listfiles = find . -type f -print
distcleancheck_listfiles = find . -type f -print
@@ -225,6 +225,7 @@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
+TMPDIR = @TMPDIR@
VERSION = @VERSION@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
@@ -314,14 +315,21 @@
# We also want to test that things work properly when the file that
# holds main() has a name ending with -main or _main. To keep the
# Makefile small :-), we test the no-threads version of these.
-TESTS = gflags_unittest$(EXEEXT) gflags_nothreads_unittest$(EXEEXT) \
- gflags_unittest2$(EXEEXT) gflags_unittest3$(EXEEXT)
+TESTS = gflags_unittest gflags_nothreads_unittest gflags_unittest2 \
+ gflags_unittest3
TESTS_ENVIRONMENT = SRCDIR="$(top_srcdir)"
# Some buggy sh's ignore "" instead of treating it as a positional
# parameter. Since we use "" in this script, we prefer bash if we
# can. If there's no bash, we fall back to sh.
-check_SCRIPTS = gflags_unittest_sh
+
+# These are negative-compilation tests. We want to make sure these
+# erroneous use of the flags macros correctly fail to compile.
+# Again, we just bother testing with the no-threads version of the library.
+
+# This one, on the other hand, should succeed.
+check_SCRIPTS = gflags_unittest_sh gflags_nc_test1 gflags_nc_test2 \
+ gflags_nc_test3 gflags_nc_test4
# Every time you add a unittest to check_SCRIPTS, add it here too
noinst_SCRIPTS = src/gflags_unittest.sh
# Used for auto-generated source files
@@ -352,7 +360,9 @@
src/gflags_unittest_main.cc
gflags_unittest3_LDADD = libgflags_nothreads.la
-dist_noinst_DATA = src/gflags_unittest_flagfile
+
+# This file isn't covered under any rule that would cause it to be distributed.
+dist_noinst_DATA = src/gflags_unittest_flagfile src/gflags_nc.cc
# These aren't part of the c++ source, but we want them to be distributed
PYTHON = python/setup.py \
@@ -862,7 +872,6 @@
dist-shar: distdir
shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
$(am__remove_distdir)
-
dist-zip: distdir
-rm -f $(distdir).zip
zip -rq $(distdir).zip $(distdir)
@@ -870,6 +879,8 @@
dist dist-all: distdir
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ -rm -f $(distdir).zip
+ zip -rq $(distdir).zip $(distdir)
$(am__remove_distdir)
# This target untars the dist file and tries a VPATH configuration. Then
@@ -1060,9 +1071,17 @@
gflags_unittest_sh: gflags_unittest$(EXEEXT) \
gflags_unittest2$(EXEEXT) \
gflags_unittest3$(EXEEXT)
- bash --version >/dev/null && export SH=bash || export SH=sh; \
+ bash --version >/dev/null 2>&1 && export SH=bash || export SH=sh; \
$$SH "$(top_srcdir)/src/gflags_unittest.sh" "$(PWD)/gflags_unittest" \
- "$(top_srcdir)"
+ "$(top_srcdir)" "@TMPDIR@"
+gflags_nc_test1: $(gflagsinclude_HEADERS) src/config.h src/gflags_nc.cc
+ ! $(CXX) -DTEST_SWAPPED_ARGS $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gflags_nc_test1.o $(srcdir)/src/gflags_nc.cc && echo "Compile failed, like it was supposed to"
+gflags_nc_test2: $(gflagsinclude_HEADERS) src/config.h src/gflags_nc.cc
+ ! $(CXX) -DTEST_INT_INSTEAD_OF_BOOL $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gflags_nc_test2.o $(srcdir)/src/gflags_nc.cc && echo "Compile failed, like it was supposed to"
+gflags_nc_test3: $(gflagsinclude_HEADERS) src/config.h src/gflags_nc.cc
+ ! $(CXX) -DTEST_BOOL_IN_QUOTES $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gflags_nc_test3.o $(srcdir)/src/gflags_nc.cc && echo "Compile failed, like it was supposed to"
+gflags_nc_test4: $(gflagsinclude_HEADERS) src/config.h src/gflags_nc.cc
+ $(CXX) -DSANITY $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o gflags_nc_test4.o $(srcdir)/src/gflags_nc.cc && echo "Compile failed, like it was supposed to"
rpm: dist-gzip packages/rpm.sh packages/rpm/rpm.spec
@cd packages && ./rpm.sh ${PACKAGE} ${VERSION}
diff --git a/configure b/configure
index bfab10c..c21c737 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.61 for gflags 1.1.
+# Generated by GNU Autoconf 2.61 for gflags 1.2.
#
# Report bugs to <opensource@google.com>.
#
@@ -728,8 +728,8 @@
# Identity of this package.
PACKAGE_NAME='gflags'
PACKAGE_TARNAME='gflags'
-PACKAGE_VERSION='1.1'
-PACKAGE_STRING='gflags 1.1'
+PACKAGE_VERSION='1.2'
+PACKAGE_STRING='gflags 1.2'
PACKAGE_BUGREPORT='opensource@google.com'
ac_unique_file="README"
@@ -860,6 +860,7 @@
host_cpu
host_vendor
host_os
+TMPDIR
SED
GREP
EGREP
@@ -1410,7 +1411,7 @@
# 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 1.1 to adapt to many kinds of systems.
+\`configure' configures gflags 1.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1480,7 +1481,7 @@
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of gflags 1.1:";;
+ short | recursive ) echo "Configuration of gflags 1.2:";;
esac
cat <<\_ACEOF
@@ -1586,7 +1587,7 @@
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-gflags configure 1.1
+gflags configure 1.2
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1600,7 +1601,7 @@
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 1.1, which was
+It was created by gflags $as_me 1.2, which was
generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@
@@ -2273,7 +2274,7 @@
# Define the identity of the package.
PACKAGE='gflags'
- VERSION='1.1'
+ VERSION='1.2'
cat >>confdefs.h <<_ACEOF
@@ -4382,6 +4383,8 @@
+ # /tmp is a mount-point in mingw, and hard to use. use cwd instead
+ TMPDIR=gflags_testdir
;;
*)
# Check whether --enable-fast-install was given.
@@ -4408,9 +4411,11 @@
fi
+ TMPDIR=/tmp/gflags
;;
esac
+
# Uncomment this if you'll be exporting libraries (.so's)
# Check whether --enable-shared was given.
if test "${enable_shared+set}" = set; then
@@ -5113,7 +5118,7 @@
;;
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 5116 "configure"' > conftest.$ac_ext
+ echo '#line 5121 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -7468,11 +7473,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7471: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7476: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:7475: \$? = $ac_status" >&5
+ echo "$as_me:7480: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7758,11 +7763,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7761: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7766: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:7765: \$? = $ac_status" >&5
+ echo "$as_me:7770: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7862,11 +7867,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7865: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7870: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7869: \$? = $ac_status" >&5
+ echo "$as_me:7874: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -10239,7 +10244,7 @@
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 10242 "configure"
+#line 10247 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -10339,7 +10344,7 @@
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 10342 "configure"
+#line 10347 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -12740,11 +12745,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:12743: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:12748: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:12747: \$? = $ac_status" >&5
+ echo "$as_me:12752: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -12844,11 +12849,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:12847: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:12852: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:12851: \$? = $ac_status" >&5
+ echo "$as_me:12856: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -14442,11 +14447,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:14445: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:14450: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:14449: \$? = $ac_status" >&5
+ echo "$as_me:14454: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -14546,11 +14551,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:14549: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:14554: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:14553: \$? = $ac_status" >&5
+ echo "$as_me:14558: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -16766,11 +16771,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:16769: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:16774: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:16773: \$? = $ac_status" >&5
+ echo "$as_me:16778: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -17056,11 +17061,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:17059: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:17064: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:17063: \$? = $ac_status" >&5
+ echo "$as_me:17068: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -17160,11 +17165,11 @@
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:17163: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:17168: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:17167: \$? = $ac_status" >&5
+ echo "$as_me:17172: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -20863,6 +20868,101 @@
+for ac_func in setenv putenv
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }
+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$ac_func || defined __stub___$ac_func
+choke me
+#endif
+
+int
+main ()
+{
+return $ac_func ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ eval "$as_ac_var=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_var=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_var'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+ # MinGW has putenv but not setenv
+
+
{ echo "$as_me:$LINENO: checking for __attribute__" >&5
echo $ECHO_N "checking for __attribute__... $ECHO_C" >&6; }
if test "${ac_cv___attribute__+set}" = set; then
@@ -22323,7 +22423,7 @@
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by gflags $as_me 1.1, which was
+This file was extended by gflags $as_me 1.2, which was
generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -22376,7 +22476,7 @@
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
-gflags config.status 1.1
+gflags config.status 1.2
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
@@ -22647,12 +22747,12 @@
host_cpu!$host_cpu$ac_delim
host_vendor!$host_vendor$ac_delim
host_os!$host_os$ac_delim
+TMPDIR!$TMPDIR$ac_delim
SED!$SED$ac_delim
GREP!$GREP$ac_delim
EGREP!$EGREP$ac_delim
LN_S!$LN_S$ac_delim
ECHO!$ECHO$ac_delim
-AR!$AR$ac_delim
_ACEOF
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
@@ -22694,6 +22794,7 @@
ac_delim='%!_!# '
for ac_last_try in false false false false false :; do
cat >conf$$subs.sed <<_ACEOF
+AR!$AR$ac_delim
RANLIB!$RANLIB$ac_delim
DSYMUTIL!$DSYMUTIL$ac_delim
NMEDIT!$NMEDIT$ac_delim
@@ -22722,7 +22823,7 @@
LTLIBOBJS!$LTLIBOBJS$ac_delim
_ACEOF
- if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 26; then
+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 27; then
break
elif $ac_last_try; then
{ { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
diff --git a/configure.ac b/configure.ac
index 62b8141..84f8198 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,11 +4,11 @@
# make sure we're interpreted by some minimal autoconf
AC_PREREQ(2.57)
-AC_INIT(gflags, 1.1, opensource@google.com)
+AC_INIT(gflags, 1.2, opensource@google.com)
# The argument here is just something that should be in the current directory
# (for sanity checking)
AC_CONFIG_SRCDIR(README)
-AM_INIT_AUTOMAKE
+AM_INIT_AUTOMAKE([dist-zip])
AM_CONFIG_HEADER(src/config.h)
# Checks for programs.
@@ -27,11 +27,15 @@
# MinGW. Using this option means an extra link step is executed during
# "make install".
AC_DISABLE_FAST_INSTALL
+ # /tmp is a mount-point in mingw, and hard to use. use cwd instead
+ TMPDIR=gflags_testdir
;;
*)
AC_ENABLE_FAST_INSTALL
+ TMPDIR=/tmp/gflags
;;
esac
+AC_SUBST(TMPDIR)
# Uncomment this if you'll be exporting libraries (.so's)
AC_PROG_LIBTOOL
@@ -53,6 +57,7 @@
AC_CHECK_TYPE(__int16, ac_cv_have___int16=1, ac_cv_have___int16=0)
AC_CHECK_FUNCS([strtoll strtoq])
+AC_CHECK_FUNCS([setenv putenv]) # MinGW has putenv but not setenv
AX_C___ATTRIBUTE__
# We only care about __attribute__ ((unused))
diff --git a/packages/deb/changelog b/packages/deb/changelog
index 70c3526..07395ca 100644
--- a/packages/deb/changelog
+++ b/packages/deb/changelog
@@ -1,3 +1,9 @@
+gflags (1.2-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Google Inc. <opensource@google.com> Thu, 10 Sep 2009 12:53:04 -0700
+
gflags (1.1-1) unstable; urgency=low
* New upstream release.
diff --git a/packages/rpm.sh b/packages/rpm.sh
index 5395dc0..381bd88 100755
--- a/packages/rpm.sh
+++ b/packages/rpm.sh
@@ -47,7 +47,18 @@
cp "$archive" "$RPM_SOURCE_DIR"
-rpmbuild -bb rpm/rpm.spec \
+# rpmbuild -- as far as I can tell -- asks the OS what CPU it has.
+# This may differ from what kind of binaries gcc produces. dpkg
+# does a better job of this, so if we can run 'dpkg --print-architecture'
+# to get the build CPU, we use that in preference of the rpmbuild
+# default.
+target=`dpkg --print-architecture 2>/dev/null` # "" if dpkg isn't found
+if [ -n "$target" ]
+then
+ target=" --target $target"
+fi
+
+rpmbuild -bb rpm/rpm.spec $target \
--define "NAME $PACKAGE" \
--define "VERSION $VERSION" \
--define "_sourcedir $RPM_SOURCE_DIR" \
diff --git a/packages/rpm/rpm.spec b/packages/rpm/rpm.spec
index ba3b509..2a5d39e 100644
--- a/packages/rpm/rpm.spec
+++ b/packages/rpm/rpm.spec
@@ -32,6 +32,15 @@
files for developing applications that use the %name package.
%changelog
+ * Thu Sep 10 2009 <opensource@google.com>
+ - Change from '%configure' to something like it, but without -m32
+
+ * Mon Apr 20 2009 <opensource@google.com>
+ - Change build rule to use '%configure' rather than './configure'
+ - Change install to use DESTDIR instead of prefix for make install.
+ - Use wildcards for doc/ and lib/ directories
+ - Use {_libdir}/{_includedir}/etc instead of {prefix}/lib, etc
+
* Tue Dec 13 2006 <opensource@google.com>
- First draft
@@ -39,12 +48,15 @@
%setup
%build
-./configure
-make prefix=%prefix
+# I can't use '% configure', because it defines -m32 which breaks the
+# build somehow on my system. But I do take as much from % configure
+# (in /usr/lib/rpm/macros) as I can.
+./configure --prefix=%{_prefix} --exec-prefix=%{_exec_prefix} --bindir=%{_bindir} --sbindir=%{_sbindir} --sysconfdir=%{_sysconfdir} --datadir=%{_datadir} --includedir=%{_includedir} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --localstatedir=%{_localstatedir} --sharedstatedir=%{_sharedstatedir} --mandir=%{_mandir} --infodir=%{_infodir}
+make
%install
rm -rf $RPM_BUILD_ROOT
-make prefix=$RPM_BUILD_ROOT%{prefix} install
+make DESTDIR=$RPM_BUILD_ROOT install
%clean
rm -rf $RPM_BUILD_ROOT
@@ -52,28 +64,20 @@
%files
%defattr(-,root,root)
-## Mark all installed files within /usr/share/doc/{package name} as
-## documentation. This depends on the following two lines appearing in
-## Makefile.am:
-## docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
-## dist_doc_DATA = AUTHORS COPYING ChangeLog INSTALL NEWS README
%docdir %{prefix}/share/doc/%{NAME}-%{VERSION}
%{prefix}/share/doc/%{NAME}-%{VERSION}/*
-%{prefix}/lib/libgflags.so.0
-%{prefix}/lib/libgflags.so.0.0.0
-%{prefix}/lib/libgflags_nothreads.so.0
-%{prefix}/lib/libgflags_nothreads.so.0.0.0
-%{prefix}/bin/gflags_completions.sh
+%doc AUTHORS COPYING ChangeLog INSTALL NEWS README
+%doc doc/*
+
+%{_libdir}/*.so.*
+%{_bindir}/gflags_completions.sh
%files devel
%defattr(-,root,root)
-%{prefix}/include/google
-%{prefix}/include/gflags
-%{prefix}/lib/libgflags.a
-%{prefix}/lib/libgflags.la
-%{prefix}/lib/libgflags.so
-%{prefix}/lib/libgflags_nothreads.a
-%{prefix}/lib/libgflags_nothreads.la
-%{prefix}/lib/libgflags_nothreads.so
+%{_includedir}/gflags
+%{_includedir}/google
+%{_libdir}/*.a
+%{_libdir}/*.la
+%{_libdir}/*.so
diff --git a/python/gflags.py b/python/gflags.py
index 03301cd..2304049 100755
--- a/python/gflags.py
+++ b/python/gflags.py
@@ -36,29 +36,28 @@
# Eric Veach, Laurence Gonsalves, Matthew Springer
# Code reorganized a bit by Craig Silverstein
-"""
-This module is used to define and parse command line flags.
+"""This module is used to define and parse command line flags.
-This module defines a *distributed* flag-definition policy: rather
-than an application having to define all flags in or near main(), each
-python module defines flags that are useful to it. When one python
-module imports another, it gains access to the other's flags. (This
-is implemented by having all modules share a common, global registry
-object containing all the flag information.)
+This module defines a *distributed* flag-definition policy: rather than
+an application having to define all flags in or near main(), each python
+module defines flags that are useful to it. When one python module
+imports another, it gains access to the other's flags. (This is
+implemented by having all modules share a common, global registry object
+containing all the flag information.)
Flags are defined through the use of one of the DEFINE_xxx functions.
The specific function used determines how the flag is parsed, checked,
and optionally type-converted, when it's seen on the command line.
-IMPLEMENTATION: DEFINE_* creates a 'Flag' object and registers it with
-a 'FlagValues' object (typically the global FlagValues FLAGS, defined
-here). The 'FlagValues' object can scan the command line arguments
-and pass flag arguments to the corresponding 'Flag' objects for
+IMPLEMENTATION: DEFINE_* creates a 'Flag' object and registers it with a
+'FlagValues' object (typically the global FlagValues FLAGS, defined
+here). The 'FlagValues' object can scan the command line arguments and
+pass flag arguments to the corresponding 'Flag' objects for
value-checking and type conversion. The converted flag values are
-available as members of the 'FlagValues' object.
+available as attributes of the 'FlagValues' object.
-Code can access the flag through a FlagValues object, for instancee
+Code can access the flag through a FlagValues object, for instance
gflags.FLAGS.myflag. Typically, the __main__ module passes the
command line arguments to gflags.FLAGS for parsing.
@@ -70,10 +69,10 @@
The exception argument will be a human-readable string.
-FLAG TYPES: This is a list of the DEFINE_*'s that you can do. All
-flags take a name, default value, help-string, and optional 'short'
-name (one-letter name). Some flags have other arguments, which are
-described with the flag.
+FLAG TYPES: This is a list of the DEFINE_*'s that you can do. All flags
+take a name, default value, help-string, and optional 'short' name
+(one-letter name). Some flags have other arguments, which are described
+with the flag.
DEFINE_string: takes any input, and interprets it as a string.
@@ -85,9 +84,9 @@
--myflag=false or --myflag=f or --myflag=0
DEFINE_float: takes an input and interprets it as a floating point
- number. Takes optional args lower_bound and
- upper_bound; if the number specified on the command line
- is out of range, it will raise a FlagError.
+ number. Takes optional args lower_bound and upper_bound;
+ if the number specified on the command line is out of
+ range, it will raise a FlagError.
DEFINE_integer: takes an input and interprets it as an integer. Takes
optional args lower_bound and upper_bound as for floats.
@@ -101,6 +100,7 @@
DEFINE_spaceseplist: Takes a space-separated list of strings on the
commandline. Stores them in a python list object.
+ Example: --myspacesepflag "foo bar baz"
DEFINE_multistring: The same as DEFINE_string, except the flag can be
specified more than once on the commandline. The
@@ -109,13 +109,18 @@
DEFINE_multi_int: The same as DEFINE_integer, except the flag can be
specified more than once on the commandline. The
- result is a python list object (list of ints),
- even if the flag is only on the command line once.
+ result is a python list object (list of ints), even if
+ the flag is only on the command line once.
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
+ --help prints a list of all the flags in a human-readable fashion
+ --helpshort prints a list of all key flags (see below).
+ --helpxml prints a list of all flags, in XML format. DO NOT parse
+ the output of --help and --helpshort. Instead, parse
+ the output of --helpxml. As we add new flags, we may
+ add new XML elements. Hence, make sure your parser
+ does not crash when it encounters new XML elements.
--flagfile=foo read flags from foo.
--undefok=f1,f2 ignore unrecognized option errors for f1,f2.
For boolean flags, you should use --undefok=boolflag, and
@@ -129,14 +134,14 @@
Flags may be loaded from text files in addition to being specified on
the commandline.
-Any flags you don't feel like typing, throw them in a file, one flag
-per line, for instance:
+Any flags you don't feel like typing, throw them in a file, one flag per
+line, for instance:
--myflag=myvalue
--nomyboolean_flag
-You then specify your file with the special flag
-'--flagfile=somefile'. You CAN recursively nest flagfile= tokens OR
-use multiple files on the command line. Lines beginning with a single
-hash '#' or a double slash '//' are comments in your flagfile.
+You then specify your file with the special flag '--flagfile=somefile'.
+You CAN recursively nest flagfile= tokens OR use multiple files on the
+command line. Lines beginning with a single hash '#' or a double slash
+'//' are comments in your flagfile.
Any flagfile=<file> will be interpreted as having a relative path from
the current working directory rather than from the place the file was
@@ -147,10 +152,10 @@
referenced relative to the original CWD, not from the directory the
including flagfile was found in!
-The caveat applies to people who are including a series of nested
-files in a different dir than they are executing out of. Relative
-path names are always from CWD, not from the directory of the parent
-include flagfile. We do now support '~' expanded directory names.
+The caveat applies to people who are including a series of nested files
+in a different dir than they are executing out of. Relative path names
+are always from CWD, not from the directory of the parent include
+flagfile. We do now support '~' expanded directory names.
Absolute path names ALWAYS work!
@@ -165,7 +170,7 @@
# If there is a conflict, we'll get an error at import time.
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_boolean('debug', False, 'produces debugging output')
gflags.DEFINE_enum('gender', 'male', ['male', 'female'], 'your gender')
def main(argv):
@@ -196,16 +201,14 @@
We'll describe shortly how to declare which flags are key to a module.
For the moment, assume we know the set of key flags for each module.
-Assume we are using the special flags --help and --helpshort to get
-information on the available flags:
+Then, if you use the app.py module, you can use the --helpshort flag to
+print only the help for the flags that are key to the main module, in a
+human-readable format.
- --help prints the help for all flags (those declared in the main
- module and those declared in the transitively imported modules).
- Hence, --help generates complete but usually overly-verbose flag
- information.
-
- --helpshort prints only the help for the flags that are key to the
- main module.
+NOTE: If you need to parse the flag help, do NOT use the output of
+--help / --helpshort. That output is meant for human consumption, and
+may be changed in the future. Instead, use --helpxml; flags that are
+key for the main module are marked there with a <key>yes</key> element.
The set of key flags for a module M is composed of:
@@ -219,24 +222,24 @@
ADOPT_module_key_flags(<other_module>)
- This is a "bulk" declaration of key flags: each flag that is key
- for <other_module> becomes key for the current module too.
+ This is a "bulk" declaration of key flags: each flag that is key for
+ <other_module> becomes key for the current module too.
-Notice that if you do not use the functions described at points 2 and
-3 above, then --helpshort prints information only about the flags
-defined by the main module of our script. In many cases, this
-behavior is good enough. But if you move part of the main module code
-(together with the related flags) into a different module, then it is
-nice to use DECLARE_key_flag / ADOPT_module_key_flags and make sure
---helpshort lists all relevant flags (otherwise, your code refactoring
-may confuse your users).
+Notice that if you do not use the functions described at points 2 and 3
+above, then --helpshort prints information only about the flags defined
+by the main module of our script. In many cases, this behavior is good
+enough. But if you move part of the main module code (together with the
+related flags) into a different module, then it is nice to use
+DECLARE_key_flag / ADOPT_module_key_flags and make sure --helpshort
+lists all relevant flags (otherwise, your code refactoring may confuse
+your users).
Note: each of DECLARE_key_flag / ADOPT_module_key_flags has its own
pluses and minuses: DECLARE_key_flag is more targeted and may lead a
-more focused --helpshort documentation. ADOPT_module_key_flags is
-good for cases when an entire module is considered key to the current
-script. Also, it does not require updates to client scripts when a
-new flag is added to the module.
+more focused --helpshort documentation. ADOPT_module_key_flags is good
+for cases when an entire module is considered key to the current script.
+Also, it does not require updates to client scripts when a new flag is
+added to the module.
EXAMPLE USAGE 2 (WITH KEY FLAGS):
@@ -284,7 +287,6 @@
... some code ...
-
When myscript is invoked with the flag --helpshort, the resulted help
message lists information about all the key flags for myscript:
--num_iterations, --num_replicas, --rpc2, and --bar_gfs_path (in
@@ -293,13 +295,15 @@
Of course, myscript uses all the flags declared by it (in this case,
just --num_replicas) or by any of the modules it transitively imports
(e.g., the modules libfoo, libbar). E.g., it can access the value of
-FLAGS.bar_risky_hack, even if --bar_risky_hack is not declared as a
-key flag for myscript.
+FLAGS.bar_risky_hack, even if --bar_risky_hack is not declared as a key
+flag for myscript.
"""
+import cgi
import getopt
import os
import re
+import string
import sys
# Are we running at least python 2.2?
@@ -327,9 +331,10 @@
def _GetCallingModule():
- """
- Get the name of the module that's calling into this module; e.g.,
- the module calling a DEFINE_foo... function.
+ """Returns the name of the module that's calling into this module.
+
+ We generally use this function to get the name of 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()):
@@ -337,46 +342,57 @@
module_name = __GetModuleName(sys._getframe(depth).f_globals)
if module_name is not None:
return module_name
- raise AssertionError, "No module was found"
+ raise AssertionError("No module was found")
# module exceptions:
class FlagsError(Exception):
- """The base class for all flags errors"""
+ """The base class for all flags errors."""
+ pass
+
class DuplicateFlag(FlagsError):
- """Raised if there is a flag naming conflict"""
+ """Raised if there is a flag naming conflict."""
+ pass
-# A DuplicateFlagError conveys more information than
-# a DuplicateFlag. Since there are external modules
-# that create DuplicateFlags, the interface to
-# DuplicateFlag shouldn't change.
+
+# 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.FlagsByModuleDict()
for module in flags_by_module:
for flag in flags_by_module[module]:
- if flag.name == flagname:
+ if flag.name == flagname or flag.short_name == flagname:
message = message + " First from " + module + ","
break
message = message + " Second from " + _GetCallingModule()
- Exception.__init__(self, message)
+ DuplicateFlag.__init__(self, message)
-class IllegalFlagValue(FlagsError): "The flag command line argument is illegal"
+
+class IllegalFlagValue(FlagsError):
+ """The flag command line argument is illegal."""
+ pass
+
class UnrecognizedFlag(FlagsError):
- """Raised if a flag is unrecognized"""
+ """Raised if a flag is unrecognized."""
+ pass
-# An UnrecognizedFlagError conveys more information than
-# an UnrecognizedFlag. Since there are external modules
-# that create DuplicateFlags, the interface to
-# DuplicateFlag shouldn't change.
+
+# 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)
+ UnrecognizedFlag.__init__(
+ self, "Unknown command line flag '%s'" % flagname)
+
# Global variable used by expvar
_exported_flags = {}
@@ -384,27 +400,27 @@
def GetHelpWidth():
- """
- Length of help to be used in TextWrap
- """
- global _help_width
+ """Returns: an integer, the width of help lines that is used in TextWrap."""
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
+ """Removes a common space prefix from the lines of a multiline text.
+
+ If the first line does not start with a space, it is left as it 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
+ text: text to work on
Returns:
the resulting text
@@ -432,18 +448,17 @@
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).
+ """Wraps a given text to a maximum line length and returns it.
+
+ We turn lines that only contain whitespaces into empty lines. We keep
+ new lines and 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
+ firstline_indent: indent for first line; if None, fall back to indent
tabs: replacement for tabs
Returns:
@@ -460,8 +475,9 @@
indent = ''
if len(indent) >= length:
raise FlagsError('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.
+ # 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
@@ -470,8 +486,9 @@
if len(firstline_indent) >= length:
raise FlagsError('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 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:
@@ -479,13 +496,13 @@
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).
+ # 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
+ # 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)
@@ -495,13 +512,14 @@
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 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] == ' '):
+ (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
+ # 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:
@@ -518,10 +536,10 @@
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).
+ # 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])
@@ -543,11 +561,11 @@
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.
+ """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
@@ -572,8 +590,8 @@
"""Given a globals dict, returns the name of the module that defines it.
Args:
- globals_dict: A dictionary that should correspond to an
- environment providing the values of the globals.
+ globals_dict: A dictionary that should correspond to an environment
+ providing the values of the globals.
Returns:
A string (the name of the module) or None (if the module could not
@@ -588,32 +606,31 @@
def _GetMainModule():
- """Get the module name from which execution started."""
+ """Returns the name of the module from which execution started."""
for depth in range(1, sys.getrecursionlimit()):
try:
globals_of_main = sys._getframe(depth).f_globals
except ValueError:
return __GetModuleName(globals_of_main)
- raise AssertionError, "No module was found"
+ raise AssertionError("No module was found")
class FlagValues:
- """
- Used as a registry for 'Flag' objects.
+ """Registry of 'Flag' objects.
A 'FlagValues' can then scan command line arguments, passing flag
arguments through to the 'Flag' objects that it owns. It also
provides easy access to the flag values. Typically only one
- 'FlagValues' object is needed by an application: gflags.FLAGS
+ 'FlagValues' object is needed by an application: gflags.FLAGS
This class is heavily overloaded:
'Flag' objects are registered via __setitem__:
FLAGS['longname'] = x # register a new flag
- The .value member of the registered 'Flag' objects can be accessed as
- members of this 'FlagValues' object, through __getattr__. Both the
- long and short name of the original 'Flag' objects can be used to
+ The .value attribute of the registered 'Flag' objects can be accessed
+ as attributes of this 'FlagValues' object, through __getattr__. Both
+ the long and short name of the original 'Flag' objects can be used to
access its value:
FLAGS.longname # parsed flag value
FLAGS.x # parsed flag value (short name)
@@ -632,17 +649,16 @@
"""
def __init__(self):
- # Since everything in this class is so heavily overloaded,
- # the only way of defining and using fields is to access __dict__
- # directly.
+ # Since everything in this class is so heavily overloaded, the only
+ # way of defining and using fields is to access __dict__ directly.
# Dictionary: flag name (string) -> Flag object.
self.__dict__['__flags'] = {}
- # Dictionary: module name (string) -> list of Flag objects that
- # are defined by that module.
+ # Dictionary: module name (string) -> list of Flag objects that are defined
+ # by that module.
self.__dict__['__flags_by_module'] = {}
- # Dictionary: module name (string) -> list of Flag objects that
- # are key for that module.
+ # Dictionary: module name (string) -> list of Flag objects that are
+ # key for that module.
self.__dict__['__key_flags_by_module'] = {}
def FlagDict(self):
@@ -667,10 +683,10 @@
return self.__dict__['__key_flags_by_module']
def _RegisterFlagByModule(self, module_name, flag):
- """Record the module that defines a specific flag.
+ """Records the module that defines a specific flag.
- We keep track of which flag is defined by which module so that
- we can later sort the flags by module.
+ We keep track of which flag is defined by which module so that we
+ can later sort the flags by module.
Args:
module_name: A string, the name of a Python module.
@@ -680,7 +696,7 @@
flags_by_module.setdefault(module_name, []).append(flag)
def _RegisterKeyFlagForModule(self, module_name, flag):
- """Specify that a flag is a key flag for a module.
+ """Specifies that a flag is a key flag for a module.
Args:
module_name: A string, the name of a Python module.
@@ -700,9 +716,9 @@
module: A module object or a module name (a string).
Returns:
- A fresh list of Flag objects. The caller may update this list
- as he wishes: none of these changes will affect the internals
- of this FlagValue object.
+ A new list of Flag objects. Caller may update this list as he
+ wishes: none of those changes will affect the internals of this
+ FlagValue object.
"""
if not isinstance(module, str):
module = module.__name__
@@ -716,17 +732,16 @@
module: A module object or a module name (a string)
Returns:
- A list of Flag objects. This is a new list, disjoint from the
- internals of this FlagValue object. Hence, the caller may
- mutate this list as he wishes: none of these changes will
- affect this FlagValue object.
+ A new list of Flag objects. Caller may update this list as he
+ wishes: none of those changes will affect the internals of this
+ FlagValue object.
"""
if not isinstance(module, str):
module = module.__name__
# Any flag is a key flag for the module that defined it. NOTE:
- # key_flags is a fresh list: we can update it without affecting
- # the internals of this FlagValues object.
+ # key_flags is a fresh list: we can update it without affecting the
+ # internals of this FlagValues object.
key_flags = self._GetFlagsDefinedByModule(module)
# Take into account flags explicitly declared as key for a module.
@@ -736,33 +751,31 @@
return key_flags
def AppendFlagValues(self, flag_values):
- """Append flags registered in another FlagValues instance.
+ """Appends flags registered in another FlagValues instance.
Args:
flag_values: registry to copy from
"""
for flag_name, flag in flag_values.FlagDict().iteritems():
- # Flags with shortnames will appear here twice (once with under
- # its normal name, and again with its short name). To prevent
- # problems (DuplicateFlagError) that occur when doubly
- # registering flags, we perform a check to make sure that the
- # entry we're looking at is for its normal name.
+ # Each flags with shortname appears here twice (once under its
+ # normal name, and again with its short name). To prevent
+ # problems (DuplicateFlagError) with double flag registration, we
+ # perform a check to make sure that the entry we're looking at is
+ # for its normal name.
if flag_name == flag.name:
self[flag_name] = flag
def __setitem__(self, name, flag):
- """
- Register a new flag variable.
- """
+ """Registers a new flag variable."""
fl = self.FlagDict()
if not isinstance(flag, Flag):
- raise IllegalFlagValue, flag
+ raise IllegalFlagValue(flag)
if not isinstance(name, type("")):
- raise FlagsError, "Flag name must be a string"
+ raise FlagsError("Flag name must be a string")
if len(name) == 0:
- raise FlagsError, "Flag name cannot be empty"
- # If running under pychecker, duplicate keys are likely to be defined.
- # Disable check for duplicate keys when pycheck'ing.
+ raise FlagsError("Flag name cannot be empty")
+ # If running under pychecker, duplicate keys are likely to be
+ # defined. 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 DuplicateFlagError(name, self)
@@ -777,24 +790,18 @@
_exported_flags[name] = flag
def __getitem__(self, name):
- """
- Retrieve the flag object.
- """
+ """Retrieves the Flag object for the flag --name."""
return self.FlagDict()[name]
def __getattr__(self, name):
- """
- Retrieve the .value member of a flag object.
- """
+ """Retrieves the 'value' attribute of the flag --name."""
fl = self.FlagDict()
if not fl.has_key(name):
- raise AttributeError, name
+ raise AttributeError(name)
return fl[name].value
def __setattr__(self, name, value):
- """
- Set the .value member of a flag object.
- """
+ """Sets the 'value' attribute of the flag --name."""
fl = self.FlagDict()
fl[name].value = value
return value
@@ -802,12 +809,11 @@
def _FlagIsRegistered(self, flag_obj):
"""Checks whether a Flag object is registered under some name.
- Note: this is not as trivial as it seems: in addition to its
- normal name, a flag may have a short name too. In
- self.FlagDict(), both the normal and the short name are mapped to
- the same flag object. E.g., calling only "del FLAGS.short_name"
- is not unregistering the corresponding Flag object (it is still
- registered under the longer name).
+ Note: this is non trivial: in addition to its normal name, a flag
+ may have a short name too. In self.FlagDict(), both the normal and
+ the short name are mapped to the same flag object. E.g., calling
+ only "del FLAGS.short_name" is not unregistering the corresponding
+ Flag object (it is still registered under the longer name).
Args:
flag_obj: A Flag object.
@@ -830,7 +836,7 @@
return False
def __delattr__(self, flag_name):
- """Delete a previously-defined flag from a flag object.
+ """Deletes a previously-defined flag from a flag object.
This method makes sure we can delete a flag by using
@@ -856,9 +862,9 @@
if not self._FlagIsRegistered(flag_obj):
# If the Flag object indicated by flag_name is no longer
- # registered (please see the docstring of _FlagIsRegistered),
- # then we delete the occurences of the flag object in all our
- # internal dictionaries.
+ # registered (please see the docstring of _FlagIsRegistered), then
+ # we delete the occurences of the flag object in all our internal
+ # dictionaries.
self.__RemoveFlagFromDictByModule(self.FlagsByModuleDict(), flag_obj)
self.__RemoveFlagFromDictByModule(self.KeyFlagsByModuleDict(), flag_obj)
@@ -866,29 +872,25 @@
"""Removes a flag object from a module -> list of flags dictionary.
Args:
- flags_by_module_dict: A dictionary that maps module names to
- lists of flags.
+ flags_by_module_dict: A dictionary that maps module names to lists of
+ flags.
flag_obj: A flag object.
"""
for unused_module, flags_in_module in flags_by_module_dict.iteritems():
- # while (as opposed to if) takes care of multiple occurences
- # of a flag in the list for the same module.
+ # while (as opposed to if) takes care of multiple occurences of a
+ # flag in the list for the same module.
while flag_obj in flags_in_module:
flags_in_module.remove(flag_obj)
def SetDefault(self, name, value):
- """
- Change the default value of the named flag object.
- """
+ """Changes the default value of the named flag object."""
fl = self.FlagDict()
if not fl.has_key(name):
- raise AttributeError, name
+ raise AttributeError(name)
fl[name].SetDefault(value)
def __contains__(self, name):
- """
- Return True if name is a value (flag) in the dict.
- """
+ """Returns True if name is a value (flag) in the dict."""
return name in self.FlagDict()
has_key = __contains__ # a synonym for __contains__()
@@ -897,11 +899,10 @@
return self.FlagDict().iterkeys()
def __call__(self, argv):
- """
- Searches argv for flag arguments, parses them and then sets the flag
- values as attributes of this FlagValues object. All unparsed
- arguments are returned. Flags are parsed using the GNU Program
- Argument Syntax Conventions, using getopt:
+ """Parses flags from argv; stores parsed flags into this FlagValues object.
+
+ All unparsed arguments are returned. Flags are parsed using the GNU
+ Program Argument Syntax Conventions, using getopt:
http://www.gnu.org/software/libc/manual/html_mono/libc.html#Getopt
@@ -952,11 +953,11 @@
elif arg.startswith('--'+no_prefix) and ('--'+no_name).startswith(arg):
argv[arg_idx] = ('--%s=false' % name)
- # Loop over all of the flags, building up the lists of short options and
- # long options that will be passed to getopt. Short options are
- # specified as a string of letters, each letter followed by a colon if it
- # takes an argument. Long options are stored in an array of strings.
- # Each string ends with an '=' if it takes an argument.
+ # Loop over all of the flags, building up the lists of short options
+ # and long options that will be passed to getopt. Short options are
+ # specified as a string of letters, each letter followed by a colon
+ # if it takes an argument. Long options are stored in an array of
+ # strings. Each string ends with an '=' if it takes an argument.
for name, flag in fl.items():
longopts.append(name + "=")
if len(name) == 1: # one-letter option: allow short flag type also
@@ -1036,24 +1037,16 @@
return argv[:1]
def Reset(self):
- """
- Reset the values to the point before FLAGS(argv) was called.
- """
+ """Resets the values to the point before FLAGS(argv) was called."""
for f in self.FlagDict().values():
f.Unparse()
def RegisteredFlags(self):
- """Return a list of the names of all registered flags.
-
- Returns:
- A list of all names and short names which flags are registered under.
- """
+ """Returns: a list of the names and short names of all registered flags."""
return self.FlagDict().keys()
def FlagValuesDict(self):
- """
- Return a dictionary with flag names as keys and flag values as values.
- """
+ """Returns: a dictionary that maps flag names to flag values."""
flag_values = {}
for flag_name in self.RegisteredFlags():
@@ -1063,15 +1056,11 @@
return flag_values
def __str__(self):
- """
- Generate a help string for all known flags.
- """
+ """Generates a help string for all known flags."""
return self.GetHelp()
- def GetHelp(self, prefix=""):
- """
- Generate a help string for all known flags.
- """
+ def GetHelp(self, prefix=''):
+ """Generates a help string for all known flags."""
helplist = []
flags_by_module = self.FlagsByModuleDict()
@@ -1084,7 +1073,7 @@
main_module = _GetMainModule()
if main_module in modules:
modules.remove(main_module)
- modules = [ main_module ] + modules
+ modules = [main_module] + modules
for module in modules:
self.__RenderOurModuleFlags(module, helplist)
@@ -1102,22 +1091,18 @@
return '\n'.join(helplist)
def __RenderModuleFlags(self, module, flags, output_lines, prefix=""):
- """
- Generate a help string for a given module.
- """
+ """Generates 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.
- """
+ """Generates a help string for a given module."""
flags = self._GetFlagsDefinedByModule(module)
if flags:
self.__RenderModuleFlags(module, flags, output_lines, prefix)
def __RenderOurModuleKeyFlags(self, module, output_lines, prefix=""):
- """Generate a help string for the key flags of a given module.
+ """Generates a help string for the key flags of a given module.
Args:
module: A module object or a module name (a string).
@@ -1173,20 +1158,21 @@
output_lines.append(flaghelp)
def get(self, name, default):
- """
- Retrieve the .value member of a flag object, or default if .value is None
+ """Returns the value of a flag (if not None) or a default value.
+
+ Args:
+ name: A string, the name of a flag.
+ default: Default value to use if the flag value is None.
"""
value = self.__getattr__(name)
- if value is not None: # Can't do if not value, b/c value might be '0' or ""
+ if value is not None: # Can't do if not value, b/c value might be '0' or ""
return value
else:
return default
def ShortestUniquePrefixes(self, fl):
- """
- Returns a dictionary mapping flag names to their shortest unique prefix.
- """
+ """Returns: dictionary; maps flag names to their shortest unique prefix."""
# Sort the list of flag names
sorted_flags = []
for name, flag in fl.items():
@@ -1195,9 +1181,9 @@
sorted_flags.append('no%s' % name)
sorted_flags.sort()
- # For each name in the sorted list, determine the shortest unique prefix
- # by comparing itself to the next name and to the previous name (the latter
- # check uses cached info from the previous loop).
+ # For each name in the sorted list, determine the shortest unique
+ # prefix by comparing itself to the next name and to the previous
+ # name (the latter check uses cached info from the previous loop).
shortest_matches = {}
prev_idx = 0
for flag_idx in range(len(sorted_flags)):
@@ -1218,14 +1204,11 @@
else:
# curr shorter than (or equal to) next
shortest_matches[curr] = curr
- prev_idx = curr_idx + 1 # next will need at least one more char
+ prev_idx = curr_idx + 1 # next will need at least one more char
return shortest_matches
def __IsFlagFileDirective(self, flag_string):
- """ Detects the --flagfile= token.
- Takes a string which might contain a '--flagfile=<foo>' directive.
- Returns a Boolean.
- """
+ """Checks whether flag_string contain a --flagfile=<foo> directive."""
if isinstance(flag_string, type("")):
if flag_string.startswith('--flagfile='):
return 1
@@ -1240,11 +1223,11 @@
return 0
def ExtractFilename(self, flagfile_str):
- """Function to remove the --flagfile= (or variant) and return just the
- filename part. We can get strings that look like:
- --flagfile=foo, -flagfile=foo.
- The case of --flagfile foo and -flagfile foo shouldn't be hitting this
- function, as they are dealt with in the level above this funciton.
+ """Returns filename from a flagfile_str of form -[-]flagfile=filename.
+
+ The cases of --flagfile foo and -flagfile foo shouldn't be hitting
+ this function, as they are dealt with in the level above this
+ function.
"""
if flagfile_str.startswith('--flagfile='):
return os.path.expanduser((flagfile_str[(len('--flagfile=')):]).strip())
@@ -1252,20 +1235,21 @@
return os.path.expanduser((flagfile_str[(len('-flagfile=')):]).strip())
else:
raise FlagsError('Hit illegal --flagfile type: %s' % flagfile_str)
- return ''
-
def __GetFlagFileLines(self, filename, parsed_file_list):
- """Function to open a flag file, return its useful (!=comments,etc) lines.
- Takes:
- A filename to open and read
- A list of files we have already read THAT WILL BE CHANGED
+ """Returns the useful (!=comments, etc) lines from a file with flags.
+
+ Args:
+ filename: A string, the name of the flag file.
+ parsed_file_list: A list of the names of the files we have
+ already read. MUTATED BY THIS FUNCTION.
+
Returns:
- List of strings. See the note below.
+ List of strings. See the note below.
NOTE(springer): This function checks for a nested --flagfile=<foo>
- tag and handles the lower file recursively. It returns a list off
- all the lines that _could_ contain command flags. This is
+ tag and handles the lower file recursively. It returns a list of
+ all the lines that _could_ contain command flags. This is
EVERYTHING except whitespace lines and comments (lines starting
with '#' or '//').
"""
@@ -1287,7 +1271,7 @@
if line.isspace():
pass
# Checks for comment (a line that starts with '#').
- elif (line.startswith('#') or line.startswith('//')):
+ elif line.startswith('#') or line.startswith('//'):
pass
# Checks for a nested "--flagfile=<bar>" flag in the current file.
# If we find one, recursively parse down into that file.
@@ -1295,44 +1279,48 @@
sub_filename = self.ExtractFilename(line)
# We do a little safety check for reparsing a file we've already done.
if not sub_filename in parsed_file_list:
- included_flags = self.__GetFlagFileLines(sub_filename, parsed_file_list)
+ included_flags = self.__GetFlagFileLines(sub_filename,
+ parsed_file_list)
flag_line_list.extend(included_flags)
- else: # Case of hitting a circularly included file.
+ else: # Case of hitting a circularly included file.
print >>sys.stderr, ('Warning: Hit circular flagfile dependency: %s'
- % sub_filename)
+ % sub_filename)
else:
- # Any line that's not a comment or a nested flagfile should
- # get copied into 2nd position, this leaves earlier arguements
- # further back in the list, which makes them have higher priority.
+ # Any line that's not a comment or a nested flagfile should get
+ # copied into 2nd position. This leaves earlier arguements
+ # further back in the list, thus giving them higher priority.
flag_line_list.append(line.strip())
return flag_line_list
def ReadFlagsFromFiles(self, argv):
- """Process command line args, but also allow args to be read from file
- Usage:
- Takes: a list of strings, usually sys.argv, which may contain one or more
- flagfile directives of the form --flagfile="./filename"
- References: Global gflags.FLAG class instance
- Returns: a new list which has the original list combined with what we
- read from any flagfile(s).
+ """Processes command line args, but also allow args to be read from file.
+ Args:
+ argv: A list of strings, usually sys.argv, which may contain one
+ or more flagfile directives of the form --flagfile="./filename".
- This function should be called before the normal FLAGS(argv) call.
- This function simply scans the input list for a flag that looks like:
- --flagfile=<somefile>
- Then it opens <somefile>, reads all valid key and value pairs and inserts
- them into the input list between the first item of the list and any
- subsequent items in the list.
- Note that your application's flags are still defined the usual way using
- gflags DEFINE_flag() type functions.
+ Returns:
- Notes (assuming we're getting a commandline of some sort as our input):
- --> Any flags on the command line we were passed in _should_ always take
- precedence!!!
- --> a further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile.
- It will be processed after the parent flag file is done.
- --> For duplicate flags, first one we hit should "win".
- --> In a flagfile, a line beginning with # or // is a comment
- --> Entirely blank lines _should_ be ignored
+ A new list which has the original list combined with what we read
+ from any flagfile(s).
+
+ References: Global gflags.FLAG class instance.
+
+ This function should be called before the normal FLAGS(argv) call.
+ This function scans the input list for a flag that looks like:
+ --flagfile=<somefile>. Then it opens <somefile>, reads all valid key
+ and value pairs and inserts them into the input list between the
+ first item of the list and any subsequent items in the list.
+
+ Note that your application's flags are still defined the usual way
+ using gflags DEFINE_flag() type functions.
+
+ Notes (assuming we're getting a commandline of some sort as our input):
+ --> Flags from the command line argv _should_ always take precedence!
+ --> A further "--flagfile=<otherfile.cfg>" CAN be nested in a flagfile.
+ It will be processed after the parent flag file is done.
+ --> For duplicate flags, first one we hit should "win".
+ --> In a flagfile, a line beginning with # or // is a comment.
+ --> Entirely blank lines _should_ be ignored.
"""
parsed_file_list = []
rest_of_args = argv
@@ -1341,30 +1329,32 @@
current_arg = rest_of_args[0]
rest_of_args = rest_of_args[1:]
if self.__IsFlagFileDirective(current_arg):
- # This handles the case of -(-)flagfile foo. Inthis case the next arg
- # really is part of this one.
- if current_arg == '--flagfile' or current_arg =='-flagfile':
+ # This handles the case of -(-)flagfile foo. In this case the
+ # next arg really is part of this one.
+ if current_arg == '--flagfile' or current_arg == '-flagfile':
if not rest_of_args:
- raise IllegalFlagValue, '--flagfile with no argument'
+ raise IllegalFlagValue('--flagfile with no argument')
flag_filename = os.path.expanduser(rest_of_args[0])
rest_of_args = rest_of_args[1:]
else:
# This handles the case of (-)-flagfile=foo.
flag_filename = self.ExtractFilename(current_arg)
new_argv = (new_argv[:1] +
- self.__GetFlagFileLines(flag_filename, parsed_file_list) +
- new_argv[1:])
+ self.__GetFlagFileLines(flag_filename, parsed_file_list) +
+ new_argv[1:])
else:
new_argv.append(current_arg)
return new_argv
def FlagsIntoString(self):
- """
- 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.
+ """Returns a string with the flags assignments from this FlagValues object.
+
+ This function ignores flags whose value is None. Each flag
+ assignment is separated by a newline.
+
+ NOTE: MUST mirror the behavior of the C++ function
+ CommandlineFlagsIntoString from google3/base/commandlineflags.cc.
"""
s = ''
for flag in self.FlagDict().values():
@@ -1373,23 +1363,102 @@
return s
def AppendFlagsIntoFile(self, filename):
- """
- Appends all flags found in this FlagInfo object to the file
- specified. Output will be in the format of a flagfile. This
- should mirror the behavior of the c++ version of
- AppendFlagsIntoFile.
+ """Appends all flags assignments from this FlagInfo object to a file.
+
+ Output will be in the format of a flagfile.
+
+ NOTE: MUST mirror the behavior of the C++ version of
+ AppendFlagsIntoFile from google3/base/commandlineflags.cc.
"""
out_file = open(filename, 'a')
out_file.write(self.FlagsIntoString())
out_file.close()
+
+ def WriteHelpInXMLFormat(self, outfile=None):
+ """Outputs flag documentation in XML format.
+
+ NOTE: We use element names that are consistent with those used by
+ the C++ command-line flag library, from
+ google3/base/commandlineflags_reporting.cc. We also use a few new
+ elements (e.g., <key>), but we do not interfere / overlap with
+ existing XML elements used by the C++ library. Please maintain this
+ consistency.
+
+ Args:
+ outfile: File object we write to. Default None means sys.stdout.
+ """
+ outfile = outfile or sys.stdout
+
+ outfile.write('<?xml version=\"1.0\"?>\n')
+ outfile.write('<AllFlags>\n')
+ indent = ' '
+ _WriteSimpleXMLElement(outfile, 'program', os.path.basename(sys.argv[0]),
+ indent)
+
+ usage_doc = sys.modules['__main__'].__doc__
+ if not usage_doc:
+ usage_doc = '\nUSAGE: %s [flags]\n' % sys.argv[0]
+ else:
+ usage_doc = usage_doc.replace('%s', sys.argv[0])
+ _WriteSimpleXMLElement(outfile, 'usage', usage_doc, indent)
+
+ # Get list of key flags for the main module.
+ key_flags = self._GetKeyFlagsForModule(_GetMainModule())
+
+ # Sort flags by declaring module name and next by flag name.
+ flags_by_module = self.FlagsByModuleDict()
+ all_module_names = list(flags_by_module.keys())
+ all_module_names.sort()
+ for module_name in all_module_names:
+ flag_list = [(f.name, f) for f in flags_by_module[module_name]]
+ flag_list.sort()
+ for unused_flag_name, flag in flag_list:
+ is_key = flag in key_flags
+ flag.WriteInfoInXMLFormat(outfile, module_name,
+ is_key=is_key, indent=indent)
+
+ outfile.write('</AllFlags>\n')
+ outfile.flush()
# end of FlagValues definition
+
# The global FlagValues instance
FLAGS = FlagValues()
-class Flag:
+def _MakeXMLSafe(s):
+ """Escapes <, >, and & from s, and removes XML 1.0-illegal chars."""
+ s = cgi.escape(s) # Escape <, >, and &
+ # Remove characters that cannot appear in an XML 1.0 document
+ # (http://www.w3.org/TR/REC-xml/#charsets).
+ #
+ # NOTE: if there are problems with current solution, one may move to
+ # XML 1.1, which allows such chars, if they're entity-escaped (&#xHH;).
+ s = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f]', '', s)
+ return s
+
+
+def _WriteSimpleXMLElement(outfile, name, value, indent):
+ """Writes a simple XML element.
+
+ Args:
+ outfile: File object we write the XML element to.
+ name: A string, the name of XML element.
+ value: A Python object, whose string representation will be used
+ as the value of the XML element.
+ indent: A string, prepended to each line of generated output.
"""
+ value_str = str(value)
+ if isinstance(value, bool):
+ # Display boolean values as the C++ flag library does: no caps.
+ value_str = value_str.lower()
+ outfile.write('%s<%s>%s</%s>\n' %
+ (indent, name, _MakeXMLSafe(value_str), name))
+
+
+class Flag:
+ """Information about a command-line flag.
+
'Flag' objects define the following fields:
.name - the name for this flag
.default - the default value for this flag
@@ -1406,19 +1475,21 @@
The only public method of a 'Flag' object is Parse(), but it is
typically only called by a 'FlagValues' object. The Parse() method is
a thin wrapper around the 'ArgumentParser' Parse() method. The parsed
- value is saved in .value, and the .present member is updated. If this
- flag was already present, a FlagsError is raised.
+ value is saved in .value, and the .present attribute is updated. If
+ this flag was already present, a FlagsError is raised.
Parse() is also called during __init__ to parse the default value and
- initialize the .value member. This enables other python modules to
+ initialize the .value attribute. This enables other python modules to
safely use flags even if the __main__ module neglects to parse the
- command line arguments. The .present member is cleared after __init__
- parsing. If the default value is set to None, then the __init__
- parsing step is skipped and the .value member is initialized to None.
+ command line arguments. The .present attribute is cleared after
+ __init__ parsing. If the default value is set to None, then the
+ __init__ parsing step is skipped and the .value attribute is
+ initialized to None.
Note: The default value is also presented to the user in the help
string, so it is important that it be a legal value for this flag.
"""
+
def __init__(self, parser, serializer, name, default, help_string,
short_name=None, boolean=0, allow_override=0):
self.name = name
@@ -1453,7 +1524,7 @@
try:
self.value = self.parser.Parse(argument)
except ValueError, e: # recast ValueError as IllegalFlagValue
- raise IllegalFlagValue, ("flag --%s: " % self.name) + str(e)
+ raise IllegalFlagValue("flag --%s: %s" % (self.name, e))
self.present += 1
def Unparse(self):
@@ -1473,29 +1544,81 @@
return "--no%s" % self.name
else:
if not self.serializer:
- raise FlagsError, "Serializer not present for flag %s" % self.name
+ raise FlagsError("Serializer not present for flag %s" % self.name)
return "--%s=%s" % (self.name, self.serializer.Serialize(self.value))
def SetDefault(self, value):
- """
- Change the default value, and current value, of this flag object
- """
+ """Changes the default value (and current value too) for this Flag."""
# We can't allow a None override because it may end up not being
# passed to C++ code when we're overriding C++ flags. So we
# cowardly bail out until someone fixes the semantics of trying to
# pass None to a C++ flag. See swig_flags.Init() for details on
# this behavior.
if value is None and self.allow_override:
- raise DuplicateFlag, self.name
+ raise DuplicateFlag(self.name)
self.default = value
self.Unparse()
self.default_as_str = self.__GetParsedValueAsString(self.value)
+
+ def Type(self):
+ """Returns: a string that describes the type of this Flag."""
+ # NOTE: we use strings, and not the types.*Type constants because
+ # our flags can have more exotic types, e.g., 'comma separated list
+ # of strings', 'whitespace separated list of strings', etc.
+ return self.parser.Type()
+
+ def WriteInfoInXMLFormat(self, outfile, module_name, is_key=False, indent=''):
+ """Writes common info about this flag, in XML format.
+
+ This is information that is relevant to all flags (e.g., name,
+ meaning, etc.). If you defined a flag that has some other pieces of
+ info, then please override _WriteCustomInfoInXMLFormat.
+
+ Please do NOT override this method.
+
+ Args:
+ outfile: File object we write to.
+ module_name: A string, the name of the module that defines this flag.
+ is_key: A boolean, True iff this flag is key for main module.
+ indent: A string that is prepended to each generated line.
+ """
+ outfile.write(indent + '<flag>\n')
+ inner_indent = indent + ' '
+ if is_key:
+ _WriteSimpleXMLElement(outfile, 'key', 'yes', inner_indent)
+ _WriteSimpleXMLElement(outfile, 'file', module_name, inner_indent)
+ # Print flag features that are relevant for all flags.
+ _WriteSimpleXMLElement(outfile, 'name', self.name, inner_indent)
+ if self.short_name:
+ _WriteSimpleXMLElement(outfile, 'short_name', self.short_name,
+ inner_indent)
+ if self.help:
+ _WriteSimpleXMLElement(outfile, 'meaning', self.help, inner_indent)
+ _WriteSimpleXMLElement(outfile, 'default', self.default, inner_indent)
+ _WriteSimpleXMLElement(outfile, 'current', self.value, inner_indent)
+ _WriteSimpleXMLElement(outfile, 'type', self.Type(), inner_indent)
+ # Print extra flag features this flag may have.
+ self._WriteCustomInfoInXMLFormat(outfile, inner_indent)
+ outfile.write(indent + '</flag>\n')
+
+ def _WriteCustomInfoInXMLFormat(self, outfile, indent):
+ """Writes extra info about this flag, in XML format.
+
+ "Extra" means "not already printed by WriteInfoInXMLFormat above."
+
+ Args:
+ outfile: File object we write to.
+ indent: A string that is prepended to each generated line.
+ """
+ # Usually, the parser knows the extra details about the flag, so
+ # we just forward the call to it.
+ self.parser.WriteCustomInfoInXMLFormat(outfile, indent)
# End of Flag definition
+
class ArgumentParser:
- """
- This is a base class used to parse and convert arguments.
+ """Base class used to parse and convert arguments.
The Parse() method checks to make sure that the string argument is a
legal value and convert it to a native type. If the value cannot be
@@ -1506,22 +1629,27 @@
presented to the user to describe the form of the legal values.
"""
syntactic_help = ""
+
def Parse(self, argument):
- """
- The default implementation of Parse() accepts any value of argument,
- simply returning it unmodified.
- """
+ """Default implementation: always returns its argument unmodified."""
return argument
+ def Type(self):
+ return 'string'
+
+ def WriteCustomInfoInXMLFormat(self, outfile, indent):
+ pass
+
+
class ArgumentSerializer:
- """
- This is the base class for generating string representations of a
- flag value
- """
+ """Base class for generating string representations of a flag value."""
+
def Serialize(self, value):
return str(value)
+
class ListSerializer(ArgumentSerializer):
+
def __init__(self, list_sep):
self.list_sep = list_sep
@@ -1529,32 +1657,42 @@
return self.list_sep.join([str(x) for x in value])
-# The DEFINE functions are explained in the module doc string.
+# The DEFINE functions are explained in mode details in the module doc string.
+
def DEFINE(parser, name, default, help, flag_values=FLAGS, serializer=None,
**args):
- """
- This creates a generic 'Flag' object that parses its arguments with a
- 'Parser' and registers it with a 'FlagValues' object.
+ """Registers a generic Flag object.
- Developers who need to create their own 'Parser' classes should call
- this module function. to register their flags. For example:
+ NOTE: in the docstrings of all DEFINE* functions, "registers" is short
+ for "creates a new flag and registers it".
- DEFINE(DatabaseSpec(), "dbspec", "mysql:db0:readonly:hr",
- "The primary database")
+ Auxiliary function: clients should use the specialized DEFINE_<type>
+ function instead.
+
+ Args:
+ parser: ArgumentParser that is used to parse the flag arguments.
+ name: A string, the flag name.
+ default: The default value of the flag.
+ help: A help string.
+ flag_values: FlagValues object the flag will be registered with.
+ serializer: ArgumentSerializer that serializes the flag value.
+ args: Dictionary with extra keyword args that are passes to the
+ Flag __init__.
"""
DEFINE_flag(Flag(parser, serializer, name, default, help, **args),
flag_values)
+
def DEFINE_flag(flag, flag_values=FLAGS):
- """
- This registers a 'Flag' object with a 'FlagValues' object. By
- default, the global FLAGS 'FlagValue' object is used.
+ """Registers a 'Flag' object with a 'FlagValues' object.
+
+ By 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 DEFINE_integer. But developers
- who need to create Flag objects themselves should use this function to
- register their flags.
+ who need to create Flag objects themselves should use this function
+ to register their flags.
"""
# copying the reference to flag_values prevents pychecker warnings
fv = flag_values
@@ -1571,7 +1709,7 @@
def _InternalDeclareKeyFlags(flag_names, flag_values=FLAGS):
- """Declare a flag as key for the calling module.
+ """Declares a flag as key for the calling module.
Internal function. User code should call DECLARE_key_flag or
ADOPT_module_key_flags instead.
@@ -1596,12 +1734,12 @@
def DECLARE_key_flag(flag_name, flag_values=FLAGS):
- """Declare one flag as key to the current module.
+ """Declares one flag as key to the current module.
Key flags are flags that are deemed really important for a module.
They are important when listing help messages; e.g., if the
- --helpshort command-line flag is used, then only the key flags of
- the main module are listed (instead of all flags, as in the case of
+ --helpshort command-line flag is used, then only the key flags of the
+ main module are listed (instead of all flags, as in the case of
--help).
Sample usage:
@@ -1619,7 +1757,7 @@
def ADOPT_module_key_flags(module, flag_values=FLAGS):
- """Declare that all flags key to a module are key to the current module.
+ """Declares that all flags key to a module are key to the current module.
Args:
module: A module object.
@@ -1641,34 +1779,28 @@
flag_values=flag_values)
-###############################
-################# STRING FLAGS
-###############################
+#
+# STRING FLAGS
+#
+
def DEFINE_string(name, default, help, flag_values=FLAGS, **args):
- """
- This registers a flag whose value can be any string.
- """
+ """Registers a flag whose value can be any string."""
parser = ArgumentParser()
serializer = ArgumentSerializer()
DEFINE(parser, name, default, help, flag_values, serializer, **args)
-###############################
-################ BOOLEAN FLAGS
-###############################
-#### and the special HELP flag
-###############################
+#
+# BOOLEAN FLAGS
+#
+# and the special HELP flags.
class BooleanParser(ArgumentParser):
- """
- A boolean value
- """
+ """Parser of boolean values."""
def Convert(self, argument):
- """
- convert the argument to a boolean; raise ValueError on errors
- """
+ """Converts the argument to a boolean; raise ValueError on errors."""
if type(argument) == str:
if argument.lower() in ['true', 't', '1']:
return True
@@ -1687,30 +1819,38 @@
val = self.Convert(argument)
return val
-class BooleanFlag(Flag):
- """
- A basic boolean flag. Boolean flags do not take any arguments, and
- their value is either True (1) or False (0). The false value is
- specified on the command line by prepending the word 'no' to either
- the long or short flag name.
+ def Type(self):
+ return 'bool'
- For example, if a Boolean flag was created whose long name was 'update'
- and whose short name was 'x', then this flag could be explicitly unset
- through either --noupdate or --nox.
+
+class BooleanFlag(Flag):
+ """Basic boolean flag.
+
+ Boolean flags do not take any arguments, and their value is either
+ True (1) or False (0). The false value is specified on the command
+ line by prepending the word 'no' to either the long or the short flag
+ name.
+
+ For example, if a Boolean flag was created whose long name was
+ 'update' and whose short name was 'x', then this flag could be
+ explicitly unset through either --noupdate or --nox.
"""
+
def __init__(self, name, default, help, short_name=None, **args):
p = BooleanParser()
Flag.__init__(self, p, None, name, default, help, short_name, 1, **args)
if not self.help: self.help = "a boolean value"
-def DEFINE_boolean(name, default, help, flag_values=FLAGS, **args):
- """
- This registers a boolean flag - one that does not take an argument.
- If a user wants to specify a false value explicitly, the long option
- beginning with 'no' must be used: i.e. --noflag
- This flag will have a value of None, True or False. None is possible if
- default=None and the user does not specify the flag on the command
+def DEFINE_boolean(name, default, help, flag_values=FLAGS, **args):
+ """Registers a boolean flag.
+
+ Such a boolean flag does not take an argument. If a user wants to
+ specify a false value explicitly, the long option beginning with 'no'
+ must be used: i.e. --noflag
+
+ This flag will have a value of None, True or False. None is possible
+ if default=None and the user does not specify the flag on the command
line.
"""
DEFINE_flag(BooleanFlag(name, default, help, **args), flag_values)
@@ -1738,6 +1878,21 @@
print flags
sys.exit(1)
+
+class HelpXMLFlag(BooleanFlag):
+ """Similar to HelpFlag, but generates output in XML format."""
+
+ def __init__(self):
+ BooleanFlag.__init__(self, 'helpxml', False,
+ 'like --help, but generates XML output',
+ allow_override=1)
+
+ def Parse(self, arg):
+ if arg:
+ FLAGS.WriteHelpInXMLFormat(sys.stdout)
+ sys.exit(1)
+
+
class HelpshortFlag(BooleanFlag):
"""
HelpshortFlag is a special boolean flag that prints usage
@@ -1760,14 +1915,14 @@
sys.exit(1)
-###############################
-################## FLOAT FLAGS
-###############################
+#
+# FLOAT FLAGS
+#
class FloatParser(ArgumentParser):
- """
- A floating point value; optionally bounded to a given upper and lower
- bound.
+ """Parser of floating point values.
+
+ Parsed value may be bounded to a given upper and lower bound.
"""
number_article = "a"
number_name = "number"
@@ -1792,46 +1947,59 @@
self.syntactic_help = sh
def Convert(self, argument):
- """
- convert the argument to a float; raise ValueError on errors
- """
+ """Converts argument to a float; raises ValueError on errors."""
return float(argument)
def Parse(self, argument):
val = self.Convert(argument)
if ((self.lower_bound != None and val < self.lower_bound) or
(self.upper_bound != None and val > self.upper_bound)):
- raise ValueError, "%s is not %s" % (val, self.syntactic_help)
+ raise ValueError("%s is not %s" % (val, self.syntactic_help))
return val
+ def Type(self):
+ return 'float'
+
+ def WriteCustomInfoInXMLFormat(self, outfile, indent):
+ if self.lower_bound is not None:
+ _WriteSimpleXMLElement(outfile, 'lower_bound', self.lower_bound, indent)
+ if self.upper_bound is not None:
+ _WriteSimpleXMLElement(outfile, 'upper_bound', self.upper_bound, indent)
+# End of FloatParser
+
+
def DEFINE_float(name, default, help, lower_bound=None, upper_bound=None,
- flag_values = FLAGS, **args):
- """
- This registers a flag whose value must be a float. If lower_bound,
- or upper_bound are set, then this flag must be within the given range.
+ flag_values=FLAGS, **args):
+ """Registers a flag whose value must be a float.
+
+ If lower_bound or upper_bound are set, then this flag must be
+ within the given range.
"""
parser = FloatParser(lower_bound, upper_bound)
serializer = ArgumentSerializer()
DEFINE(parser, name, default, help, flag_values, serializer, **args)
-###############################
-################ INTEGER FLAGS
-###############################
+#
+# INTEGER FLAGS
+#
+
class IntegerParser(FloatParser):
- """
- An integer value; optionally bounded to a given upper or lower bound.
+ """Parser of an integer value.
+
+ Parsed value may be bounded to a given upper and lower bound.
"""
number_article = "an"
number_name = "integer"
syntactic_help = " ".join((number_article, number_name))
+
def Convert(self, argument):
__pychecker__ = 'no-returnvalues'
if type(argument) == str:
base = 10
if len(argument) > 2 and argument[0] == "0" and argument[1] == "x":
- base=16
+ base = 16
try:
return int(argument, base)
# ValueError is thrown when argument is a string, and overflows an int.
@@ -1844,74 +2012,84 @@
except OverflowError:
return long(argument)
+ def Type(self):
+ return 'int'
+
+
def DEFINE_integer(name, default, help, lower_bound=None, upper_bound=None,
- flag_values = FLAGS, **args):
- """
- This registers a flag whose value must be an integer. If lower_bound,
- or upper_bound are set, then this flag must be within the given range.
+ flag_values=FLAGS, **args):
+ """Registers a flag whose value must be an integer.
+
+ If lower_bound, or upper_bound are set, then this flag must be
+ within the given range.
"""
parser = IntegerParser(lower_bound, upper_bound)
serializer = ArgumentSerializer()
DEFINE(parser, name, default, help, flag_values, serializer, **args)
-###############################
-################### ENUM FLAGS
-###############################
+#
+# ENUM FLAGS
+#
+
class EnumParser(ArgumentParser):
- """
- A string enum value
+ """Parser of a string enum value (a string value from a given set).
+
+ If enum_values (see below) is not specified, any string is allowed.
"""
def __init__(self, enum_values=None):
self.enum_values = enum_values
def Parse(self, argument):
- """
- If enum_values is not specified, any string is allowed
- """
if self.enum_values and argument not in self.enum_values:
- raise ValueError, ("value should be one of <%s>"
- % "|".join(self.enum_values))
+ raise ValueError("value should be one of <%s>" %
+ "|".join(self.enum_values))
return argument
+ def Type(self):
+ return 'string enum'
+
+
class EnumFlag(Flag):
- """
- A basic enum flag. The flag's value can be any string from the list
- of enum_values.
- """
- def __init__(self, name, default, help, enum_values=[],
+ """Basic enum flag; its value can be any string from list of enum_values."""
+
+ def __init__(self, name, default, help, enum_values=None,
short_name=None, **args):
+ enum_values = enum_values or []
p = EnumParser(enum_values)
g = ArgumentSerializer()
Flag.__init__(self, p, g, name, default, help, short_name, **args)
if not self.help: self.help = "an enum string"
self.help = "<%s>: %s" % ("|".join(enum_values), self.help)
+ def _WriteCustomInfoInXMLFormat(self, outfile, indent):
+ for enum_value in self.parser.enum_values:
+ _WriteSimpleXMLElement(outfile, 'enum_value', enum_value, indent)
+
+
def DEFINE_enum(name, default, enum_values, help, flag_values=FLAGS,
**args):
- """
- This registers a flag whose value can be a string from a set of
- specified values.
- """
+ """Registers a flag whose value can be any string from enum_values."""
DEFINE_flag(EnumFlag(name, default, help, enum_values, ** args),
flag_values)
-###############################
-################### LIST FLAGS
-###############################
+#
+# LIST FLAGS
+#
+
class BaseListParser(ArgumentParser):
- """
- A base class for a string list parser.
- To extend, inherit from this class, and call
+ """Base class for a parser of lists of strings.
- BaseListParser.__init__(self, token, name)
+ To extend, inherit from this class; from the subclass __init__, call
- where token is a character used to tokenize, and
- name is a description of the separator
+ BaseListParser.__init__(self, token, name)
+
+ where token is a character used to tokenize, and name is a description
+ of the separator.
"""
def __init__(self, token=None, name=None):
@@ -1926,77 +2104,91 @@
else:
return [s.strip() for s in argument.split(self._token)]
+ def Type(self):
+ return '%s separated list of strings' % self._name
+
class ListParser(BaseListParser):
- """
- A string list parser (comma-separated)
- """
+ """Parser for a comma-separated list of strings."""
def __init__(self):
BaseListParser.__init__(self, ',', 'comma')
+ def WriteCustomInfoInXMLFormat(self, outfile, indent):
+ BaseListParser.WriteCustomInfoInXMLFormat(self, outfile, indent)
+ _WriteSimpleXMLElement(outfile, 'list_separator', repr(','), indent)
+
+
class WhitespaceSeparatedListParser(BaseListParser):
- """
- A string list parser (whitespace-separated)
- """
+ """Parser for a whitespace-separated list of strings."""
def __init__(self):
BaseListParser.__init__(self, None, 'whitespace')
+ def WriteCustomInfoInXMLFormat(self, outfile, indent):
+ BaseListParser.WriteCustomInfoInXMLFormat(self, outfile, indent)
+ separators = list(string.whitespace)
+ separators.sort()
+ for ws_char in string.whitespace:
+ _WriteSimpleXMLElement(outfile, 'list_separator', repr(ws_char), indent)
+
def DEFINE_list(name, default, help, flag_values=FLAGS, **args):
- """
- This registers a flag whose value is a list of strings, separated by commas
- """
+ """Registers a flag whose value is a comma-separated list of strings."""
parser = ListParser()
serializer = ListSerializer(',')
DEFINE(parser, name, default, help, flag_values, serializer, **args)
+
def DEFINE_spaceseplist(name, default, help, flag_values=FLAGS, **args):
- """
- This registers a flag whose value is a list of strings, separated by any
- whitespace
+ """Registers a flag whose value is a whitespace-separated list of strings.
+
+ Any whitespace can be used as a separator.
"""
parser = WhitespaceSeparatedListParser()
serializer = ListSerializer(' ')
DEFINE(parser, name, default, help, flag_values, serializer, **args)
-###############################
-################## MULTI FLAGS
-###############################
+#
+# MULTI FLAGS
+#
+
class MultiFlag(Flag):
- """
- MultiFlag is a specialized subclass of Flag that accumulates
- multiple values in a list when a command-line option appears
- multiple times.
+ """A flag that can appear multiple time on the command-line.
+
+ The value of such a flag is a list that contains the individual values
+ from all the appearances of that flag on the command-line.
See the __doc__ for Flag for most behavior of this class. Only
differences in behavior are described here:
- * the default value may be a single value -OR- a list of values
- * the value of the flag is always a list, even if the option was only
- supplied once, and even if the default value is a single value
+
+ * The default value may be either a single value or a list of values.
+ A single value is interpreted as the [value] singleton list.
+
+ * The value of the flag is always a list, even if the option was
+ only supplied once, and even if the default value is a single
+ value
"""
+
def __init__(self, *args, **kwargs):
Flag.__init__(self, *args, **kwargs)
-
- self.help = (self.help +
- ';\n repeat this option to specify a list of values')
+ self.help += ';\n repeat this option to specify a list of values'
def Parse(self, arguments):
- """Parse one or more arguments with the installed parser.
+ """Parses one or more arguments with the installed parser.
- Arguments:
- arguments: a single argument or a list of arguments (typically a list
- of default values); single arguments will be converted internally into
- a list containing one item
+ Args:
+ arguments: a single argument or a list of arguments (typically a
+ list of default values); a single argument is converted
+ internally into a list containing one item.
"""
if not isinstance(arguments, list):
- # Default value may be a list of values. Most other arguments will not
- # be, so convert them into a single-item list to make processing simpler
- # below.
- arguments = [ arguments ]
+ # Default value may be a list of values. Most other arguments
+ # will not be, so convert them into a single-item list to make
+ # processing simpler below.
+ arguments = [arguments]
if self.present:
# keep a backup reference to list of previously supplied option values
@@ -2010,12 +2202,12 @@
Flag.Parse(self, item) # also increments self.present
values.append(self.value)
- # put list of option values back in member variable
+ # put list of option values back in the 'value' attribute
self.value = values
def Serialize(self):
if not self.serializer:
- raise FlagsError, "Serializer not present for flag %s" % self.name
+ raise FlagsError("Serializer not present for flag %s" % self.name)
if self.value is None:
return ''
@@ -2031,37 +2223,45 @@
return s
+ def Type(self):
+ return 'multi ' + self.parser.Type()
+
def DEFINE_multi(parser, serializer, name, default, help, flag_values=FLAGS,
**args):
- """
- This creates a generic 'MultiFlag' object that parses its arguments with a
- 'Parser' and registers it with a 'FlagValues' object.
+ """Registers a generic MultiFlag that parses its args with a given parser.
- Developers who need to create their own 'Parser' classes for options which
- can appear multiple times can call this module function to register their
- flags.
+ Auxiliary function. Normal users should NOT use it directly.
+
+ Developers who need to create their own 'Parser' classes for options
+ which can appear multiple times can call this module function to
+ register their flags.
"""
- DEFINE_flag(MultiFlag(parser, serializer, name, default, help, **args), flag_values)
+ DEFINE_flag(MultiFlag(parser, serializer, name, default, help, **args),
+ flag_values)
+
def DEFINE_multistring(name, default, help, flag_values=FLAGS, **args):
- """
- This registers a flag whose value can be a list of any strings. Use the flag
- on the command line multiple times to place multiple string values into the
- list. The 'default' may be a single string (which will be converted into a
- single-element list) or a list of strings.
+ """Registers a flag whose value can be a list of any strings.
+
+ Use the flag on the command line multiple times to place multiple
+ string values into the list. The 'default' may be a single string
+ (which will be converted into a single-element list) or a list of
+ strings.
"""
parser = ArgumentParser()
serializer = ArgumentSerializer()
DEFINE_multi(parser, serializer, name, default, help, flag_values, **args)
+
def DEFINE_multi_int(name, default, help, lower_bound=None, upper_bound=None,
flag_values=FLAGS, **args):
- """
- This registers a flag whose value can be a list of any integers. Use the
- flag on the command line multiple times to place multiple integer values
- into the list. The 'default' may be a single integer (which will be
- converted into a single-element list) or a list of integers.
+ """Registers a flag whose value can be a list of arbitrary integers.
+
+ Use the flag on the command line multiple times to place multiple
+ integer values into the list. The 'default' may be a single integer
+ (which will be converted into a single-element list) or a list of
+ integers.
"""
parser = IntegerParser(lower_bound, upper_bound)
serializer = ArgumentSerializer()
@@ -2073,10 +2273,12 @@
# these flagnames for their own purposes, if they want.
DEFINE_flag(HelpFlag())
DEFINE_flag(HelpshortFlag())
+DEFINE_flag(HelpXMLFlag())
# 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.",
diff --git a/python/gflags_helpxml_test.py b/python/gflags_helpxml_test.py
new file mode 100755
index 0000000..aea2ffb
--- /dev/null
+++ b/python/gflags_helpxml_test.py
@@ -0,0 +1,563 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2009, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Unit tests for the XML-format help generated by the gflags.py module."""
+
+__author__ = 'Alex Salcianu'
+
+
+import string
+import StringIO
+import sys
+import unittest
+import xml.dom.minidom
+import xml.sax.saxutils
+
+# We use the name 'flags' internally in this test, for historical reasons.
+# Don't do this yourself! :-) Just do 'import gflags; FLAGS=gflags.FLAGS; etc'
+import gflags as flags
+
+# For historic reasons, we use the name module_bar instead of test_module_bar
+import test_module_bar as module_bar
+
+def MultiLineEqual(expected_help, help):
+ """Returns True if expected_help == help. Otherwise returns False
+ and logs the difference in a human-readable way.
+ """
+ if help == expected_help:
+ return True
+
+ 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."
+
+ return False
+
+
+class _MakeXMLSafeTest(unittest.TestCase):
+
+ def _Check(self, s, expected_output):
+ self.assertEqual(flags._MakeXMLSafe(s), expected_output)
+
+ def testMakeXMLSafe(self):
+ self._Check('plain text', 'plain text')
+ self._Check('(x < y) && (a >= b)',
+ '(x < y) && (a >= b)')
+ # Some characters with ASCII code < 32 are illegal in XML 1.0 and
+ # are removed by us. However, '\n', '\t', and '\r' are legal.
+ self._Check('\x09\x0btext \x02 with\x0dsome \x08 good & bad chars',
+ '\ttext with\rsome good & bad chars')
+
+
+def _ListSeparatorsInXMLFormat(separators, indent=''):
+ """Generates XML encoding of a list of list separators.
+
+ Args:
+ separators: A list of list separators. Usually, this should be a
+ string whose characters are the valid list separators, e.g., ','
+ means that both comma (',') and space (' ') are valid list
+ separators.
+ indent: A string that is added at the beginning of each generated
+ XML element.
+
+ Returns:
+ A string.
+ """
+ result = ''
+ separators = list(separators)
+ separators.sort()
+ for sep_char in separators:
+ result += ('%s<list_separator>%s</list_separator>\n' %
+ (indent, repr(sep_char)))
+ return result
+
+
+class WriteFlagHelpInXMLFormatTest(unittest.TestCase):
+ """Test the XML-format help for a single flag at a time.
+
+ There is one test* method for each kind of DEFINE_* declaration.
+ """
+
+ def setUp(self):
+ # self.fv is a FlagValues object, just like flags.FLAGS. Each
+ # test registers one flag with this FlagValues.
+ self.fv = flags.FlagValues()
+
+ def assertMultiLineEqual(self, expected, actual):
+ self.assert_(MultiLineEqual(expected, actual))
+
+ def _CheckFlagHelpInXML(self, flag_name, module_name,
+ expected_output, is_key=False):
+ # StringIO.StringIO is a file object that writes into a memory string.
+ sio = StringIO.StringIO()
+ flag_obj = self.fv[flag_name]
+ flag_obj.WriteInfoInXMLFormat(sio, module_name, is_key=is_key, indent=' ')
+ self.assertMultiLineEqual(sio.getvalue(), expected_output)
+ sio.close()
+
+ def testFlagHelpInXML_Int(self):
+ flags.DEFINE_integer('index', 17, 'An integer flag', flag_values=self.fv)
+ expected_output_pattern = (
+ ' <flag>\n'
+ ' <file>module.name</file>\n'
+ ' <name>index</name>\n'
+ ' <meaning>An integer flag</meaning>\n'
+ ' <default>17</default>\n'
+ ' <current>%d</current>\n'
+ ' <type>int</type>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('index', 'module.name',
+ expected_output_pattern % 17)
+ # Check that the output is correct even when the current value of
+ # a flag is different from the default one.
+ self.fv['index'].value = 20
+ self._CheckFlagHelpInXML('index', 'module.name',
+ expected_output_pattern % 20)
+
+ def testFlagHelpInXML_IntWithBounds(self):
+ flags.DEFINE_integer('nb_iters', 17, 'An integer flag',
+ lower_bound=5, upper_bound=27,
+ flag_values=self.fv)
+ expected_output = (
+ ' <flag>\n'
+ ' <key>yes</key>\n'
+ ' <file>module.name</file>\n'
+ ' <name>nb_iters</name>\n'
+ ' <meaning>An integer flag</meaning>\n'
+ ' <default>17</default>\n'
+ ' <current>17</current>\n'
+ ' <type>int</type>\n'
+ ' <lower_bound>5</lower_bound>\n'
+ ' <upper_bound>27</upper_bound>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('nb_iters', 'module.name',
+ expected_output, is_key=True)
+
+ def testFlagHelpInXML_String(self):
+ flags.DEFINE_string('file_path', '/path/to/my/dir', 'A test string flag.',
+ flag_values=self.fv)
+ expected_output = (
+ ' <flag>\n'
+ ' <file>simple_module</file>\n'
+ ' <name>file_path</name>\n'
+ ' <meaning>A test string flag.</meaning>\n'
+ ' <default>/path/to/my/dir</default>\n'
+ ' <current>/path/to/my/dir</current>\n'
+ ' <type>string</type>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('file_path', 'simple_module',
+ expected_output)
+
+ def testFlagHelpInXML_StringWithXMLIllegalChars(self):
+ flags.DEFINE_string('file_path', '/path/to/\x08my/dir',
+ 'A test string flag.', flag_values=self.fv)
+ # '\x08' is not a legal character in XML 1.0 documents. Our
+ # current code purges such characters from the generated XML.
+ expected_output = (
+ ' <flag>\n'
+ ' <file>simple_module</file>\n'
+ ' <name>file_path</name>\n'
+ ' <meaning>A test string flag.</meaning>\n'
+ ' <default>/path/to/my/dir</default>\n'
+ ' <current>/path/to/my/dir</current>\n'
+ ' <type>string</type>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('file_path', 'simple_module',
+ expected_output)
+
+ def testFlagHelpInXML_Boolean(self):
+ flags.DEFINE_boolean('use_hack', False, 'Use performance hack',
+ flag_values=self.fv)
+ expected_output = (
+ ' <flag>\n'
+ ' <key>yes</key>\n'
+ ' <file>a_module</file>\n'
+ ' <name>use_hack</name>\n'
+ ' <meaning>Use performance hack</meaning>\n'
+ ' <default>false</default>\n'
+ ' <current>false</current>\n'
+ ' <type>bool</type>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('use_hack', 'a_module',
+ expected_output, is_key=True)
+
+ def testFlagHelpInXML_Enum(self):
+ flags.DEFINE_enum('cc_version', 'stable', ['stable', 'experimental'],
+ 'Compiler version to use.', flag_values=self.fv)
+ expected_output = (
+ ' <flag>\n'
+ ' <file>tool</file>\n'
+ ' <name>cc_version</name>\n'
+ ' <meaning><stable|experimental>: '
+ 'Compiler version to use.</meaning>\n'
+ ' <default>stable</default>\n'
+ ' <current>stable</current>\n'
+ ' <type>string enum</type>\n'
+ ' <enum_value>stable</enum_value>\n'
+ ' <enum_value>experimental</enum_value>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('cc_version', 'tool', expected_output)
+
+ def testFlagHelpInXML_CommaSeparatedList(self):
+ flags.DEFINE_list('files', 'a.cc,a.h,archive/old.zip',
+ 'Files to process.', flag_values=self.fv)
+ expected_output = (
+ ' <flag>\n'
+ ' <file>tool</file>\n'
+ ' <name>files</name>\n'
+ ' <meaning>Files to process.</meaning>\n'
+ ' <default>a.cc,a.h,archive/old.zip</default>\n'
+ ' <current>[\'a.cc\', \'a.h\', \'archive/old.zip\']</current>\n'
+ ' <type>comma separated list of strings</type>\n'
+ ' <list_separator>\',\'</list_separator>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('files', 'tool', expected_output)
+
+ def testFlagHelpInXML_SpaceSeparatedList(self):
+ flags.DEFINE_spaceseplist('dirs', 'src libs bin',
+ 'Directories to search.', flag_values=self.fv)
+ expected_output = (
+ ' <flag>\n'
+ ' <file>tool</file>\n'
+ ' <name>dirs</name>\n'
+ ' <meaning>Directories to search.</meaning>\n'
+ ' <default>src libs bin</default>\n'
+ ' <current>[\'src\', \'libs\', \'bin\']</current>\n'
+ ' <type>whitespace separated list of strings</type>\n'
+ 'LIST_SEPARATORS'
+ ' </flag>\n').replace('LIST_SEPARATORS',
+ _ListSeparatorsInXMLFormat(string.whitespace,
+ indent=' '))
+ self._CheckFlagHelpInXML('dirs', 'tool', expected_output)
+
+ def testFlagHelpInXML_MultiString(self):
+ flags.DEFINE_multistring('to_delete', ['a.cc', 'b.h'],
+ 'Files to delete', flag_values=self.fv)
+ expected_output = (
+ ' <flag>\n'
+ ' <file>tool</file>\n'
+ ' <name>to_delete</name>\n'
+ ' <meaning>Files to delete;\n '
+ 'repeat this option to specify a list of values</meaning>\n'
+ ' <default>[\'a.cc\', \'b.h\']</default>\n'
+ ' <current>[\'a.cc\', \'b.h\']</current>\n'
+ ' <type>multi string</type>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('to_delete', 'tool', expected_output)
+
+ def testFlagHelpInXML_MultiInt(self):
+ flags.DEFINE_multi_int('cols', [5, 7, 23],
+ 'Columns to select', flag_values=self.fv)
+ expected_output = (
+ ' <flag>\n'
+ ' <file>tool</file>\n'
+ ' <name>cols</name>\n'
+ ' <meaning>Columns to select;\n '
+ 'repeat this option to specify a list of values</meaning>\n'
+ ' <default>[5, 7, 23]</default>\n'
+ ' <current>[5, 7, 23]</current>\n'
+ ' <type>multi int</type>\n'
+ ' </flag>\n')
+ self._CheckFlagHelpInXML('cols', 'tool', expected_output)
+
+
+# The next EXPECTED_HELP_XML_* constants are parts of a template for
+# the expected XML output from WriteHelpInXMLFormatTest below. When
+# we assemble these parts into a single big string, we'll take into
+# account the ordering between the name of the main module and the
+# name of module_bar. Next, we'll fill in the docstring for this
+# module (%(usage_doc)s), the name of the main module
+# (%(main_module_name)s) and the name of the module module_bar
+# (%(module_bar_name)s). See WriteHelpInXMLFormatTest below.
+#
+# NOTE: given the current implementation of _GetMainModule(), we
+# already know the ordering between the main module and module_bar.
+# However, there is no guarantee that _GetMainModule will never be
+# changed in the future (especially since it's far from perfect).
+EXPECTED_HELP_XML_START = """\
+<?xml version="1.0"?>
+<AllFlags>
+ <program>gflags_helpxml_test.py</program>
+ <usage>%(usage_doc)s</usage>
+"""
+
+EXPECTED_HELP_XML_FOR_FLAGS_FROM_MAIN_MODULE = """\
+ <flag>
+ <key>yes</key>
+ <file>%(main_module_name)s</file>
+ <name>cc_version</name>
+ <meaning><stable|experimental>: Compiler version to use.</meaning>
+ <default>stable</default>
+ <current>stable</current>
+ <type>string enum</type>
+ <enum_value>stable</enum_value>
+ <enum_value>experimental</enum_value>
+ </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(main_module_name)s</file>
+ <name>cols</name>
+ <meaning>Columns to select;
+ repeat this option to specify a list of values</meaning>
+ <default>[5, 7, 23]</default>
+ <current>[5, 7, 23]</current>
+ <type>multi int</type>
+ </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(main_module_name)s</file>
+ <name>dirs</name>
+ <meaning>Directories to create.</meaning>
+ <default>src libs bins</default>
+ <current>['src', 'libs', 'bins']</current>
+ <type>whitespace separated list of strings</type>
+%(whitespace_separators)s </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(main_module_name)s</file>
+ <name>file_path</name>
+ <meaning>A test string flag.</meaning>
+ <default>/path/to/my/dir</default>
+ <current>/path/to/my/dir</current>
+ <type>string</type>
+ </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(main_module_name)s</file>
+ <name>files</name>
+ <meaning>Files to process.</meaning>
+ <default>a.cc,a.h,archive/old.zip</default>
+ <current>['a.cc', 'a.h', 'archive/old.zip']</current>
+ <type>comma separated list of strings</type>
+ <list_separator>\',\'</list_separator>
+ </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(main_module_name)s</file>
+ <name>index</name>
+ <meaning>An integer flag</meaning>
+ <default>17</default>
+ <current>17</current>
+ <type>int</type>
+ </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(main_module_name)s</file>
+ <name>nb_iters</name>
+ <meaning>An integer flag</meaning>
+ <default>17</default>
+ <current>17</current>
+ <type>int</type>
+ <lower_bound>5</lower_bound>
+ <upper_bound>27</upper_bound>
+ </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(main_module_name)s</file>
+ <name>to_delete</name>
+ <meaning>Files to delete;
+ repeat this option to specify a list of values</meaning>
+ <default>['a.cc', 'b.h']</default>
+ <current>['a.cc', 'b.h']</current>
+ <type>multi string</type>
+ </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(main_module_name)s</file>
+ <name>use_hack</name>
+ <meaning>Use performance hack</meaning>
+ <default>false</default>
+ <current>false</current>
+ <type>bool</type>
+ </flag>
+"""
+
+EXPECTED_HELP_XML_FOR_FLAGS_FROM_MODULE_BAR = """\
+ <flag>
+ <file>%(module_bar_name)s</file>
+ <name>tmod_bar_t</name>
+ <meaning>Sample int flag.</meaning>
+ <default>4</default>
+ <current>4</current>
+ <type>int</type>
+ </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(module_bar_name)s</file>
+ <name>tmod_bar_u</name>
+ <meaning>Sample int flag.</meaning>
+ <default>5</default>
+ <current>5</current>
+ <type>int</type>
+ </flag>
+ <flag>
+ <file>%(module_bar_name)s</file>
+ <name>tmod_bar_v</name>
+ <meaning>Sample int flag.</meaning>
+ <default>6</default>
+ <current>6</current>
+ <type>int</type>
+ </flag>
+ <flag>
+ <file>%(module_bar_name)s</file>
+ <name>tmod_bar_x</name>
+ <meaning>Boolean flag.</meaning>
+ <default>true</default>
+ <current>true</current>
+ <type>bool</type>
+ </flag>
+ <flag>
+ <file>%(module_bar_name)s</file>
+ <name>tmod_bar_y</name>
+ <meaning>String flag.</meaning>
+ <default>default</default>
+ <current>default</current>
+ <type>string</type>
+ </flag>
+ <flag>
+ <key>yes</key>
+ <file>%(module_bar_name)s</file>
+ <name>tmod_bar_z</name>
+ <meaning>Another boolean flag from module bar.</meaning>
+ <default>false</default>
+ <current>false</current>
+ <type>bool</type>
+ </flag>
+"""
+
+EXPECTED_HELP_XML_END = """\
+</AllFlags>
+"""
+
+
+class WriteHelpInXMLFormatTest(unittest.TestCase):
+ """Big test of FlagValues.WriteHelpInXMLFormat, with several flags."""
+
+ def assertMultiLineEqual(self, expected, actual):
+ self.assert_(MultiLineEqual(expected, actual))
+
+ def testWriteHelpInXMLFormat(self):
+ fv = flags.FlagValues()
+ # Since these flags are defined by the top module, they are all key.
+ flags.DEFINE_integer('index', 17, 'An integer flag', flag_values=fv)
+ flags.DEFINE_integer('nb_iters', 17, 'An integer flag',
+ lower_bound=5, upper_bound=27, flag_values=fv)
+ flags.DEFINE_string('file_path', '/path/to/my/dir', 'A test string flag.',
+ flag_values=fv)
+ flags.DEFINE_boolean('use_hack', False, 'Use performance hack',
+ flag_values=fv)
+ flags.DEFINE_enum('cc_version', 'stable', ['stable', 'experimental'],
+ 'Compiler version to use.', flag_values=fv)
+ flags.DEFINE_list('files', 'a.cc,a.h,archive/old.zip',
+ 'Files to process.', flag_values=fv)
+ flags.DEFINE_spaceseplist('dirs', 'src libs bins',
+ 'Directories to create.', flag_values=fv)
+ flags.DEFINE_multistring('to_delete', ['a.cc', 'b.h'],
+ 'Files to delete', flag_values=fv)
+ flags.DEFINE_multi_int('cols', [5, 7, 23],
+ 'Columns to select', flag_values=fv)
+ # Define a few flags in a different module.
+ module_bar.DefineFlags(flag_values=fv)
+ # And declare only a few of them to be key. This way, we have
+ # different kinds of flags, defined in different modules, and not
+ # all of them are key flags.
+ flags.DECLARE_key_flag('tmod_bar_z', flag_values=fv)
+ flags.DECLARE_key_flag('tmod_bar_u', flag_values=fv)
+
+ # Generate flag help in XML format in the StringIO sio.
+ sio = StringIO.StringIO()
+ fv.WriteHelpInXMLFormat(sio)
+
+ # Check that we got the expected result.
+ expected_output_template = EXPECTED_HELP_XML_START
+ main_module_name = flags._GetMainModule()
+ module_bar_name = module_bar.__name__
+
+ if main_module_name < module_bar_name:
+ expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MAIN_MODULE
+ expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MODULE_BAR
+ else:
+ expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MODULE_BAR
+ expected_output_template += EXPECTED_HELP_XML_FOR_FLAGS_FROM_MAIN_MODULE
+
+ expected_output_template += EXPECTED_HELP_XML_END
+
+ # XML representation of the whitespace list separators.
+ whitespace_separators = _ListSeparatorsInXMLFormat(string.whitespace,
+ indent=' ')
+ expected_output = (
+ expected_output_template %
+ {'usage_doc': sys.modules['__main__'].__doc__,
+ 'main_module_name': main_module_name,
+ 'module_bar_name': module_bar_name,
+ 'whitespace_separators': whitespace_separators})
+
+ actual_output = sio.getvalue()
+ self.assertMultiLineEqual(actual_output, expected_output)
+
+ # Also check that our result is valid XML. minidom.parseString
+ # throws an xml.parsers.expat.ExpatError in case of an error.
+ xml.dom.minidom.parseString(actual_output)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/python/gflags_unittest.py b/python/gflags_unittest.py
index 53e3f86..8540b69 100755
--- a/python/gflags_unittest.py
+++ b/python/gflags_unittest.py
@@ -29,7 +29,7 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"Unittest for flags.py module"
+"Unittest for gflags.py module"
__pychecker__ = "no-local" # for unittest
@@ -495,7 +495,7 @@
"--testspacelist [] --x 10 "
"--noexec --quack "
"--test1 "
- "--testget1 --no? --nodebug --nohelp --nohelpshort "
+ "--testget1 --no? --nodebug --nohelp --nohelpshort --nohelpxml "
"--noq --notest0 --notestget2 "
"--notestget3 --notestnone")
@@ -520,7 +520,7 @@
"--testspacelist [] --x 10 "
"--debug --noexec --quack "
"--test1 "
- "--testget1 --no? --nohelp --nohelpshort "
+ "--testget1 --no? --nohelp --nohelpshort --nohelpxml "
"--noq --notest0 --notestget2 "
"--notestget3 --notestnone")
@@ -535,12 +535,25 @@
except flags.DuplicateFlag, e:
pass
+ # Duplicate short flag detection
try:
flags.DEFINE_boolean("zoom1", 0, "runhelp z1", short_name='z')
flags.DEFINE_boolean("zoom2", 0, "runhelp z2", short_name='z')
- raise AssertionError("duplicate flag detection failed")
+ raise AssertionError("duplicate short flag detection failed")
except flags.DuplicateFlag, e:
- pass
+ self.assertTrue("The flag 'z' is defined twice. " in e.args[0])
+ self.assertTrue("First from" in e.args[0])
+ self.assertTrue(", Second from" in e.args[0])
+
+ # Duplicate mixed flag detection
+ try:
+ flags.DEFINE_boolean("short1", 0, "runhelp s1", short_name='s')
+ flags.DEFINE_boolean("s", 0, "runhelp s2")
+ raise AssertionError("duplicate mixed flag detection failed")
+ except flags.DuplicateFlag, e:
+ self.assertTrue("The flag 's' is defined twice. " in e.args[0])
+ self.assertTrue("First from" in e.args[0])
+ self.assertTrue(", Second from" in e.args[0])
# Make sure allow_override works
try:
@@ -1165,6 +1178,7 @@
(default: 'false')
-?,--[no]help: show this help
--[no]helpshort: show usage only for this module
+ --[no]helpxml: like --help, but generates XML output
--kwery: <who|what|why|where|when>: ?
--l: how long to be
(default: '9223372032559808512')
@@ -1407,7 +1421,9 @@
try:
help_flag_help = (
" -?,--[no]help: show this help\n"
- " --[no]helpshort: show usage only for this module")
+ " --[no]helpshort: show usage only for this module\n"
+ " --[no]helpxml: like --help, but generates XML output"
+ )
expected_help = "\n%s:\n%s" % (sys.argv[0], help_flag_help)
@@ -1477,18 +1493,18 @@
self.assertEqual(flags._GetCallingModule(), sys.argv[0])
self.assertEqual(
module_foo.GetModuleName(),
- 'google3.pyglib.tests.flags_modules_for_testing.module_foo')
+ 'test_module_foo')
self.assertEqual(
module_bar.GetModuleName(),
- 'google3.pyglib.tests.flags_modules_for_testing.module_bar')
+ 'test_module_bar')
# We execute the following exec statements for their side-effect
# (i.e., not raising an error). They emphasize the case that not
# all code resides in one of the imported modules: Python is a
# really dynamic language, where we can dynamically construct some
# code and execute it.
- code = ("from google3.pyglib import flags\n"
- "module_name = flags._GetCallingModule()")
+ code = ("import gflags\n"
+ "module_name = gflags._GetCallingModule()")
exec code
# Next two exec statements executes code with a global environment
@@ -1517,7 +1533,7 @@
module_bar.ExecuteCode(code, global_dict)
self.assertEqual(
global_dict['module_name'],
- 'google3.pyglib.tests.flags_modules_for_testing.module_bar')
+ 'test_module_bar')
def main():
diff --git a/src/config.h.in b/src/config.h.in
index 3aa2f5a..c585b61 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -21,6 +21,12 @@
/* Define if you have POSIX threads libraries and header files. */
#undef HAVE_PTHREAD
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define to 1 if you have the `setenv' function. */
+#undef HAVE_SETENV
+
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
diff --git a/src/gflags.cc b/src/gflags.cc
index c2417c8..183a17e 100644
--- a/src/gflags.cc
+++ b/src/gflags.cc
@@ -669,7 +669,7 @@
};
FlagRegistry* FlagRegistry::global_registry_ = NULL;
-Mutex FlagRegistry::global_registry_lock_;
+Mutex FlagRegistry::global_registry_lock_(Mutex::LINKER_INITIALIZED);
FlagRegistry* FlagRegistry::GlobalRegistry() {
MutexLock acquire_lock(&global_registry_lock_);
@@ -1037,6 +1037,25 @@
break; // we treat this as an unrecoverable error
} else {
value = (*argv)[++i]; // read next arg for value
+
+ // Heuristic to detect the case where someone treats a string arg
+ // like a bool:
+ // --my_string_var --foo=bar
+ // We look for a flag of string type, whose value begins with a
+ // dash, and where the flag-name and value are separated by a
+ // space rather than an '='.
+ // To avoid false positives, we also require the word "true"
+ // or "false" in the help string. Without this, a valid usage
+ // "-lat -30.5" would trigger the warning. The common cases we
+ // want to solve talk about true and false as values.
+ if (value[0] == '-'
+ && strcmp(flag->type_name(), "string") == 0
+ && (strstr(flag->help(), "true")
+ || strstr(flag->help(), "false"))) {
+ fprintf(stderr, "Did you really mean to set flag '%s'"
+ " to the value '%s'?\n",
+ flag->name(), value);
+ }
}
}
@@ -1343,15 +1362,6 @@
// values in a global destructor.
// --------------------------------------------------------------------
-// TODO(csilvers): When we're ready to have this error be a fatal one,
-// change this to give a compilation error (via COMPILE_ASSERT(false)).
-bool FlagsTypeWarn(const char *name) {
- cerr << "Flag " << name << " is of type bool, but its default"
- << " value is not a boolean. NOTE: This will soon be a"
- << " compilations error!";
- return false;
-}
-
FlagRegisterer::FlagRegisterer(const char* name, const char* type,
const char* help, const char* filename,
void* current_storage, void* defvalue_storage) {
@@ -1530,7 +1540,7 @@
CommandLineFlagInfo GetCommandLineFlagInfoOrDie(const char* name) {
CommandLineFlagInfo info;
if (!GetCommandLineFlagInfo(name, &info)) {
- fprintf(stderr, "FATAL ERROR: flag name '%s' doesn't exit", name);
+ fprintf(stderr, "FATAL ERROR: flag name '%s' doesn't exist\n", name);
commandlineflags_exitfunc(1); // almost certainly exit()
}
return info;
diff --git a/src/gflags/gflags.h.in b/src/gflags/gflags.h.in
index 4bf91fe..4691db2 100644
--- a/src/gflags/gflags.h.in
+++ b/src/gflags/gflags.h.in
@@ -415,7 +415,7 @@
void* current_storage, void* defvalue_storage);
};
-#ifndef SWIG // In swig, ignore the main flag declarations
+extern bool FlagsTypeWarn(const char *name);
// If your application #defines STRIP_FLAG_HELP to a non-zero value
// before #including this file, we remove the help message from the
@@ -424,6 +424,10 @@
extern const char kStrippedFlagHelp[];
+@ac_google_end_namespace@
+
+#ifndef SWIG // In swig, ignore the main flag declarations
+
#if defined(STRIP_FLAG_HELP) && STRIP_FLAG_HELP > 0
// Need this construct to avoid the 'defined but not used' warning.
#define MAYBE_STRIPPED_HELP(txt) (false ? (txt) : kStrippedFlagHelp)
@@ -459,26 +463,30 @@
} \
using fL##shorttype::FLAGS_##name
-// For boolean flags, we want to do the extra check that the passed-in
+// For DEFINE_bool, we want to do the extra check that the passed-in
// value is actually a bool, and not a string or something that can be
// coerced to a bool. These declarations (no definition needed!) will
-// help us do that, and never evaluate from, which is important.
-// We'll use 'sizeof(IsBool(val))' to distinguish.
+// help us do that, and never evaluate From, which is important.
+// We'll use 'sizeof(IsBool(val))' to distinguish. This code requires
+// that the compiler have different sizes for bool & double. Since
+// this is not guaranteed by the standard, we check it with a
+// compile-time assert (msg[-1] will give a compile-time error).
namespace fLB {
+struct CompileAssert {};
+typedef CompileAssert expected_sizeof_double_neq_sizeof_bool[
+ (sizeof(double) != sizeof(bool)) ? 1 : -1];
template<typename From> double IsBoolFlag(const From& from);
bool IsBoolFlag(bool from);
-}
-extern bool FlagsTypeWarn(const char *name);
+} // namespace fLB
#define DECLARE_bool(name) DECLARE_VARIABLE(bool,B, name)
-// We have extra code here to make sure 'val' is actually a boolean.
-#define DEFINE_bool(name,val,txt) namespace fLB { \
- const bool FLAGS_nonono##name = \
- (sizeof(@ac_google_namespace@::fLB::IsBoolFlag(val)) \
- == sizeof(double)) \
- ? @ac_google_namespace@::FlagsTypeWarn(#name) : true; \
- } \
- DEFINE_VARIABLE(bool,B, name, val, txt)
+#define DEFINE_bool(name,val,txt) \
+ namespace fLB { \
+ typedef CompileAssert FLAG_##name##_value_is_not_a_bool[ \
+ (sizeof(::fLB::IsBoolFlag(val)) != sizeof(double)) ? 1 : -1]; \
+ } \
+ DEFINE_VARIABLE(bool,B, name, val, txt)
+
#define DECLARE_int32(name) DECLARE_VARIABLE(@ac_google_namespace@::int32,I, name)
#define DEFINE_int32(name,val,txt) DEFINE_VARIABLE(@ac_google_namespace@::int32,I, name, val, txt)
@@ -522,6 +530,4 @@
#endif // SWIG
-@ac_google_end_namespace@
-
#endif // GOOGLE_GFLAGS_H_
diff --git a/src/gflags_nc.cc b/src/gflags_nc.cc
new file mode 100644
index 0000000..14f18d4
--- /dev/null
+++ b/src/gflags_nc.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ---
+// Author: Roberto Bayardo
+//
+// A negative compile test for commandlineflags.
+
+#include <gflags/gflags.h>
+
+#if defined(TEST_SWAPPED_ARGS)
+
+DEFINE_bool(some_bool_flag,
+ "the default value should go here, not the description",
+ false);
+
+
+#elif defined(TEST_INT_INSTEAD_OF_BOOL)
+
+DEFINE_bool(some_bool_flag_2,
+ 0,
+ "should have been an int32 flag but mistakenly used bool instead");
+
+#elif defined(TEST_BOOL_IN_QUOTES)
+
+
+DEFINE_bool(some_bool_flag_3,
+ "false",
+ "false in in quotes, which is wrong");
+
+#elif defined(SANITY)
+
+DEFINE_bool(some_bool_flag_4,
+ true,
+ "this is the correct usage of DEFINE_bool");
+
+#endif
diff --git a/src/gflags_unittest.cc b/src/gflags_unittest.cc
index 19e6a8f..adc118b 100644
--- a/src/gflags_unittest.cc
+++ b/src/gflags_unittest.cc
@@ -57,6 +57,28 @@
// Returns the number of elements in an array.
#define GET_ARRAY_SIZE(arr) (sizeof(arr)/sizeof(*(arr)))
+#if !defined(HAVE_SETENV) && defined(HAVE_PUTENV) // mingw, at least
+void setenv(const char* name, const char* value, int) {
+ // In windows, it's impossible to set a variable to the empty string.
+ // We handle this by setting it to "0" and the NUL-ing out the \0.
+ // cf http://svn.apache.org/viewvc/stdcxx/trunk/tests/src/environ.cpp?r1=611451&r2=637508&pathrev=637508
+ static const char* const kFakeZero = "0";
+ if (*value == '\0')
+ value = kFakeZero;
+ // Apparently the semantics of putenv() is that the input
+ // must live forever, so we leak memory here. :-(
+ const int nameval_len = strlen(name) + 1 + strlen(value) + 1;
+ char* nameval = reinterpret_cast<char*>(malloc(nameval_len));
+ snprintf(nameval, nameval_len, "%s=%s", name, value);
+ putenv(nameval);
+ if (value == kFakeZero) {
+ nameval[nameval_len - 2] = '\0'; // works when putenv() makes no copy
+ if (*getenv(name) != '\0')
+ *getenv(name) = '\0'; // works when putenv() copies nameval
+ }
+}
+#endif
+
DECLARE_string(tryfromenv); // in gflags.cc
DEFINE_string(test_tmpdir, "/tmp/gflags_unittest", "Dir we use for temp files");
@@ -86,18 +108,6 @@
// This is used to test setting tryfromenv manually
DEFINE_string(test_tryfromenv, "initial", "");
-// boolean flag assigned correctly with bool
-DEFINE_bool(test_bool_bool, true, "");
-
-// boolean flag assigned with string
-DEFINE_bool(test_bool_string, "", "");
-
-// boolean flag assigned with float
-DEFINE_bool(test_bool_float, 1.0, "");
-
-// boolean flag assigned with int
-DEFINE_bool(test_bool_int, 1, "");
-
// Don't try this at home!
static int changeable_var = 12;
DEFINE_int32(changeable_var, ++changeable_var, "");
@@ -249,7 +259,11 @@
#define TEST(a, b) \
struct Test_##a##_##b { \
Test_##a##_##b() { g_testlist.push_back(&Run); } \
- static void Run() { FlagSaver fs; RunTest(); } \
+ static void Run() { \
+ FlagSaver fs; \
+ fprintf(stderr, "Running test %s/%s\n", #a, #b); \
+ RunTest(); \
+ } \
static void RunTest(); \
}; \
static Test_##a##_##b g_test_##a##_##b; \
@@ -416,6 +430,7 @@
-1.0);
}
+#ifdef HAVE_FNMATCH_H // otherwise glob isn't supported
TEST(FlagFileTest, FilenamesOurfileGlob) {
FLAGS_test_string = "initial";
FLAGS_test_bool = false;
@@ -467,6 +482,7 @@
1,
-1.0);
}
+#endif
// Tests that a failed flag-from-string read keeps flags at default values
TEST(FlagFileTest, FailReadFlagsFromString) {
@@ -540,8 +556,11 @@
// Tests that flags can be set to exceptional values.
+// Note: apparently MINGW doesn't parse inf and nan correctly:
+// http://www.mail-archive.com/bug-gnulib@gnu.org/msg09573.html
+// This url says FreeBSD also has a problem, but I didn't see that.
TEST(SetFlagValueTest, ExceptionalValues) {
-#ifdef isinf // on systems without isinf, inf stuff may not work at all
+#if defined(isinf) && !defined(__MINGW32__)
EXPECT_EQ("test_double set to inf\n",
SetCommandLineOption("test_double", "inf"));
EXPECT_INF(FLAGS_test_double);
@@ -558,14 +577,14 @@
SetCommandLineOption("test_double", " "));
EXPECT_EQ("",
SetCommandLineOption("test_double", ""));
-#ifdef isinf
+#if defined(isinf) && !defined(__MINGW32__)
EXPECT_EQ("test_double set to -inf\n",
SetCommandLineOption("test_double", "-inf"));
EXPECT_INF(FLAGS_test_double);
EXPECT_GT(0, FLAGS_test_double);
#endif
-#ifdef isnan
+#if defined(isnan) && !defined(__MINGW32__)
EXPECT_EQ("test_double set to nan\n",
SetCommandLineOption("test_double", "NaN"));
EXPECT_NAN(FLAGS_test_double);
@@ -1499,7 +1518,13 @@
SetUsageMessage(usage_message.c_str());
ParseCommandLineFlags(&argc, &argv, true);
+#ifdef __MINGW32__
+ // I had trouble creating a directory in /tmp from mingw
+ FLAGS_test_tmpdir = "./gflags_unittest_testdir";
+ mkdir(FLAGS_test_tmpdir.c_str()); // mingw has a weird one-arg mkdir
+#else
mkdir(FLAGS_test_tmpdir.c_str(), 0755);
+#endif
return RUN_ALL_TESTS();
}
diff --git a/src/gflags_unittest.sh b/src/gflags_unittest.sh
index f57e5f8..ce9e58e 100755
--- a/src/gflags_unittest.sh
+++ b/src/gflags_unittest.sh
@@ -215,14 +215,6 @@
# Make sure -- by itself stops argv processing
Expect $LINENO 0 "PASS" "" -- --help
-# Make sure boolean flags gives warning when type of default value is not bool
-Expect $LINENO 0 "Flag test_bool_string is of type bool, but its default value is not a boolean." ""
-Expect $LINENO 0 "Flag test_bool_float is of type bool, but its default value is not a boolean." ""
-Expect $LINENO 0 "Flag test_bool_int is of type bool, but its default value is not a boolean." ""
-
-# Make sure that boolean flags don't give warning when default value is bool
-Expect $LINENO 0 "" "Flag test_bool_bool is of type bool, but its default value is not a boolean."
-
# And we should die if the flag value doesn't pas the validator
Expect $LINENO 1 "ERROR: failed validation of new value 'true' for flag 'always_fail'" "" --always_fail
diff --git a/src/mutex.h b/src/mutex.h
index eda1f4e..6e1e897 100644
--- a/src/mutex.h
+++ b/src/mutex.h
@@ -38,9 +38,12 @@
// AC_RWLOCK
// The latter is defined in ../autoconf.
//
-// This class is meant to be internal-only, so it's defined in the
-// global namespace. If you want to expose it, you'll want to move
-// it to the Google namespace.
+// This class is meant to be internal-only and should be wrapped by an
+// internal namespace. Before you use this module, please give the
+// name of your internal namespace for this module. Or, if you want
+// to expose it, you'll want to move it to the Google namespace. We
+// cannot put this class in global namespace because there can be some
+// problems when we have multiple versions of Mutex in each shared object.
//
// NOTE: by default, we have #ifdef'ed out the TryLock() method.
// This is for two reasons:
@@ -95,6 +98,16 @@
// colon-initializer) and set it to true via a function that always
// evaluates to true, but that the compiler can't know always
// evaluates to true. This should be good enough.
+//
+// A related issue is code that could try to access the mutex
+// after it's been destroyed in the global destructors (because
+// the Mutex global destructor runs before some other global
+// destructor, that tries to acquire the mutex). The way we
+// deal with this is by taking a constructor arg that global
+// mutexes should pass in, that causes the destructor to do no
+// work. We still depend on the compiler not doing anything
+// weird to a Mutex's memory after it is destroyed, but for a
+// static global variable, that's pretty safe.
#ifndef GOOGLE_MUTEX_H_
#define GOOGLE_MUTEX_H_
@@ -132,13 +145,26 @@
# error Need to implement mutex.h for your architecture, or #define NO_THREADS
#endif
+#include <assert.h>
+#include <stdlib.h> // for abort()
+
+#define MUTEX_NAMESPACE gflags_mutex_namespace
+
+namespace MUTEX_NAMESPACE {
+
class Mutex {
public:
+ // This is used for the single-arg constructor
+ enum LinkerInitialized { LINKER_INITIALIZED };
+
// Create a Mutex that is not held by anybody. This constructor is
// typically used for Mutexes allocated on the heap or the stack.
- // See below for a recommendation for constructing global Mutex
- // objects.
inline Mutex();
+ // This constructor should be used for global, static Mutex objects.
+ // It inhibits work being done by the destructor, which makes it
+ // safer for code that tries to acqiure this mutex in their global
+ // destructor.
+ inline Mutex(LinkerInitialized);
// Destructor
inline ~Mutex();
@@ -163,6 +189,8 @@
// when we tell it to, and never makes assumptions is_safe_ is
// always true. volatile is the most reliable way to do that.
volatile bool is_safe_;
+ // This indicates which constructor was called.
+ bool destroy_;
inline void SetIsSafe() { is_safe_ = true; }
@@ -185,9 +213,9 @@
// In debug mode, we assert these invariants, while in non-debug mode
// we do nothing, for efficiency. That's why everything is in an
// assert.
-#include <assert.h>
Mutex::Mutex() : mutex_(0) { }
+Mutex::Mutex(Mutex::LinkerInitialized) : mutex_(0) { }
Mutex::~Mutex() { assert(mutex_ == 0); }
void Mutex::Lock() { assert(--mutex_ == -1); }
void Mutex::Unlock() { assert(mutex_++ == -1); }
@@ -199,8 +227,15 @@
#elif defined(_WIN32) || defined(__CYGWIN32__) || defined(__CYGWIN64__)
-Mutex::Mutex() { InitializeCriticalSection(&mutex_); SetIsSafe(); }
-Mutex::~Mutex() { DeleteCriticalSection(&mutex_); }
+Mutex::Mutex() : destroy_(true) {
+ InitializeCriticalSection(&mutex_);
+ SetIsSafe();
+}
+Mutex::Mutex(LinkerInitialized) : destroy_(false) {
+ InitializeCriticalSection(&mutex_);
+ SetIsSafe();
+}
+Mutex::~Mutex() { if (destroy_) DeleteCriticalSection(&mutex_); }
void Mutex::Lock() { if (is_safe_) EnterCriticalSection(&mutex_); }
void Mutex::Unlock() { if (is_safe_) LeaveCriticalSection(&mutex_); }
#ifdef GMUTEX_TRYLOCK
@@ -212,22 +247,24 @@
#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
-#include <stdlib.h> // for abort()
#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
if (is_safe_ && fncall(&mutex_) != 0) abort(); \
} while (0)
-Mutex::Mutex() {
+Mutex::Mutex() : destroy_(true) {
SetIsSafe();
if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort();
}
-Mutex::~Mutex() { SAFE_PTHREAD(pthread_rwlock_destroy); }
+Mutex::Mutex(Mutex::LinkerInitialized) : destroy_(false) {
+ SetIsSafe();
+ if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort();
+}
+Mutex::~Mutex() { if (destroy_) SAFE_PTHREAD(pthread_rwlock_destroy); }
void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock); }
void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock); }
#ifdef GMUTEX_TRYLOCK
bool Mutex::TryLock() { return is_safe_ ?
- pthread_rwlock_trywrlock(&mutex_) == 0 :
- true; }
+ pthread_rwlock_trywrlock(&mutex_) == 0 : true; }
#endif
void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock); }
void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock); }
@@ -235,16 +272,19 @@
#elif defined(HAVE_PTHREAD)
-#include <stdlib.h> // for abort()
#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
if (is_safe_ && fncall(&mutex_) != 0) abort(); \
} while (0)
-Mutex::Mutex() {
+Mutex::Mutex() : destroy_(true) {
SetIsSafe();
if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
}
-Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy); }
+Mutex::Mutex(Mutex::LinkerInitialized) : destroy_(false) {
+ SetIsSafe();
+ if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
+}
+Mutex::~Mutex() { if (destroy_) SAFE_PTHREAD(pthread_mutex_destroy); }
void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock); }
void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock); }
#ifdef GMUTEX_TRYLOCK
@@ -300,4 +340,10 @@
#define ReaderMutexLock(x) COMPILE_ASSERT(0, rmutex_lock_decl_missing_var_name)
#define WriterMutexLock(x) COMPILE_ASSERT(0, wmutex_lock_decl_missing_var_name)
+} // namespace MUTEX_NAMESPACE
+
+using namespace MUTEX_NAMESPACE;
+
+#undef MUTEX_NAMESPACE
+
#endif /* #define GOOGLE_MUTEX_H__ */