From c79c32d98c4dc705ecc2fea2d7e8712afb6938fb Mon Sep 17 00:00:00 2001 From: Craig Silverstein Date: Tue, 22 Jul 2008 23:29:39 +0000 Subject: [PATCH] Mon Jul 21 23:01:38 2008 Google Inc. * google-gflags: version 0.9 * Add the ability to validate a command-line flag (csilvers) * Add completion support for commandline flags in bash (daven) * Add -W compile flags to Makefile, when using gcc (csilvers) * Allow helpstring to be NULL (cristianoc) * Improved documentation of classes in the .cc file (csilvers) * Fix python bug with AppendFlagValues + shortnames (jjtswan) * Use bool instead of int for boolean flags in gflags.py (bcmills) * Simplify the way we declare flags, now more foolproof (csilvers) * Better error messages when bool flags collide (colohan) * Only evaluate DEFINE_foo macro args once (csilvers) git-svn-id: https://gflags.googlecode.com/svn/trunk@23 6586e3c6-dcc4-952a-343f-ff74eb82781d --- ChangeLog | 14 + Makefile.am | 19 +- Makefile.in | 93 +++-- configure | 325 ++++++++------ configure.ac | 5 +- doc/gflags.html | 32 ++ m4/acx_pthread.m4 | 218 +++++----- m4/google_namespace.m4 | 8 +- packages/deb/changelog | 6 + packages/deb/libgoogle-gflags0.dirs | 1 + packages/deb/libgoogle-gflags0.install | 2 + packages/rpm/rpm.spec | 1 + python/gflags.py | 67 +-- python/gflags_unittest.py | 125 +++--- src/gflags.cc | 743 +++++++++++++++++++++++---------- src/gflags_completions.cc | 743 +++++++++++++++++++++++++++++++++ src/gflags_completions.sh | 117 ++++++ src/gflags_reporting.cc | 3 + src/gflags_unittest.cc | 279 ++++++++++++- src/gflags_unittest.sh | 13 +- src/google/gflags.h.in | 202 +++++---- src/google/gflags_completions.h.in | 121 ++++++ 22 files changed, 2494 insertions(+), 643 deletions(-) create mode 100644 src/gflags_completions.cc create mode 100755 src/gflags_completions.sh create mode 100644 src/google/gflags_completions.h.in diff --git a/ChangeLog b/ChangeLog index af505ab..24d9931 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +Mon Jul 21 23:01:38 2008 Google Inc. + + * google-gflags: version 0.9 + * Add the ability to validate a command-line flag (csilvers) + * Add completion support for commandline flags in bash (daven) + * Add -W compile flags to Makefile, when using gcc (csilvers) + * Allow helpstring to be NULL (cristianoc) + * Improved documentation of classes in the .cc file (csilvers) + * Fix python bug with AppendFlagValues + shortnames (jjtswan) + * Use bool instead of int for boolean flags in gflags.py (bcmills) + * Simplify the way we declare flags, now more foolproof (csilvers) + * Better error messages when bool flags collide (colohan) + * Only evaluate DEFINE_foo macro args once (csilvers) + Wed Mar 26 15:20:18 2008 Google Inc. * google-gflags: version 0.8 diff --git a/Makefile.am b/Makefile.am index 7173895..71c7c2b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,10 +11,20 @@ ACLOCAL_AMFLAGS = -I m4 # This is so we can #include AM_CPPFLAGS = -I$(top_srcdir)/src +# This is mostly based on configure options +AM_CXXFLAGS = + +# These are good warnings to turn on by default, +if GCC +AM_CXXFLAGS += -Wall -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare +endif + googleincludedir = $(includedir)/google ## The .h files you want to install (that is, .h files that people ## who install this package can include in their own applications.) -googleinclude_HEADERS = src/google/gflags.h +googleinclude_HEADERS = src/google/gflags.h src/google/gflags_completions.h + +bin_SCRIPTS = src/gflags_completions.sh docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION) ## This is for HTML and other documentation you want to install. @@ -42,9 +52,10 @@ CLEANFILES = lib_LTLIBRARIES += libgflags.la libgflags_la_SOURCES = $(googleinclude_HEADERS) src/config.h \ - src/gflags.cc src/gflags_reporting.cc -libgflags_la_CXXFLAGS = $(PTRHEAD_CFLAGS) -DNDEBUG -libgflags_la_LDFLAGS = $(PTRHEAD_CFLAGS) + src/gflags.cc src/gflags_reporting.cc \ + src/gflags_completions.cc +libgflags_la_CXXFLAGS = $(PTHREAD_CFLAGS) -DNDEBUG +libgflags_la_LDFLAGS = $(PTHREAD_CFLAGS) libgflags_la_LIBADD = $(PTHREAD_LIBS) TESTS += gflags_unittest diff --git a/Makefile.in b/Makefile.in index 727e672..b353fca 100644 --- a/Makefile.in +++ b/Makefile.in @@ -40,14 +40,18 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ + +# These are good warnings to turn on by default, +@GCC_TRUE@am__append_1 = -Wall -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare noinst_PROGRAMS = $(am__EXEEXT_1) DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \ $(dist_noinst_DATA) $(googleinclude_HEADERS) \ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(top_srcdir)/configure $(top_srcdir)/src/config.h.in \ - $(top_srcdir)/src/google/gflags.h.in AUTHORS COPYING ChangeLog \ - INSTALL NEWS compile config.guess config.sub depcomp \ - install-sh ltmain.sh missing mkinstalldirs + $(top_srcdir)/src/google/gflags.h.in \ + $(top_srcdir)/src/google/gflags_completions.h.in AUTHORS \ + COPYING ChangeLog INSTALL NEWS compile config.guess config.sub \ + depcomp install-sh ltmain.sh missing mkinstalldirs subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ac_have_attribute.m4 \ @@ -61,22 +65,24 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno configure.status.lineno mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = $(top_builddir)/src/config.h -CONFIG_CLEAN_FILES = src/google/gflags.h +CONFIG_CLEAN_FILES = src/google/gflags.h \ + src/google/gflags_completions.h am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; -am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(docdir)" \ - "$(DESTDIR)$(googleincludedir)" +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(docdir)" "$(DESTDIR)$(googleincludedir)" libLTLIBRARIES_INSTALL = $(INSTALL) LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = libgflags_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am__objects_1 = am_libgflags_la_OBJECTS = $(am__objects_1) libgflags_la-gflags.lo \ - libgflags_la-gflags_reporting.lo + libgflags_la-gflags_reporting.lo \ + libgflags_la-gflags_completions.lo libgflags_la_OBJECTS = $(am_libgflags_la_OBJECTS) am__EXEEXT_1 = gflags_unittest$(EXEEXT) \ gflags_nothreads_unittest$(EXEEXT) gflags_unittest2$(EXEEXT) \ @@ -99,7 +105,8 @@ am_gflags_unittest3_OBJECTS = $(am__objects_1) \ gflags_unittest_main.$(OBJEXT) gflags_unittest3_OBJECTS = $(am_gflags_unittest3_OBJECTS) gflags_unittest3_DEPENDENCIES = libgflags.la -SCRIPTS = $(noinst_SCRIPTS) +binSCRIPT_INSTALL = $(INSTALL_SCRIPT) +SCRIPTS = $(bin_SCRIPTS) $(noinst_SCRIPTS) DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/src depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -172,6 +179,8 @@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ F77 = @F77@ FFLAGS = @FFLAGS@ +GCC_FALSE = @GCC_FALSE@ +GCC_TRUE = @GCC_TRUE@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ @@ -260,8 +269,12 @@ ACLOCAL_AMFLAGS = -I m4 # This is so we can #include AM_CPPFLAGS = -I$(top_srcdir)/src + +# This is mostly based on configure options +AM_CXXFLAGS = $(am__append_1) googleincludedir = $(includedir)/google -googleinclude_HEADERS = src/google/gflags.h +googleinclude_HEADERS = src/google/gflags.h src/google/gflags_completions.h +bin_SCRIPTS = src/gflags_completions.sh docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION) dist_doc_DATA = AUTHORS COPYING ChangeLog INSTALL NEWS README \ doc/designstyle.css doc/gflags.html @@ -282,10 +295,11 @@ noinst_SCRIPTS = src/gflags_unittest.sh # Used for auto-generated source files CLEANFILES = src/gflags_unittest-main.cc src/gflags_unittest_main.cc libgflags_la_SOURCES = $(googleinclude_HEADERS) src/config.h \ - src/gflags.cc src/gflags_reporting.cc + src/gflags.cc src/gflags_reporting.cc \ + src/gflags_completions.cc -libgflags_la_CXXFLAGS = $(PTRHEAD_CFLAGS) -DNDEBUG -libgflags_la_LDFLAGS = $(PTRHEAD_CFLAGS) +libgflags_la_CXXFLAGS = $(PTHREAD_CFLAGS) -DNDEBUG +libgflags_la_LDFLAGS = $(PTHREAD_CFLAGS) libgflags_la_LIBADD = $(PTHREAD_LIBS) gflags_unittest_SOURCES = $(googleinclude_HEADERS) src/config.h \ src/gflags_unittest.cc @@ -370,6 +384,8 @@ distclean-hdr: -rm -f src/config.h src/stamp-h1 src/google/gflags.h: $(top_builddir)/config.status $(top_srcdir)/src/google/gflags.h.in cd $(top_builddir) && $(SHELL) ./config.status $@ +src/google/gflags_completions.h: $(top_builddir)/config.status $(top_srcdir)/src/google/gflags_completions.h.in + cd $(top_builddir) && $(SHELL) ./config.status $@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) @$(NORMAL_INSTALL) test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" @@ -418,6 +434,25 @@ gflags_unittest2$(EXEEXT): $(gflags_unittest2_OBJECTS) $(gflags_unittest2_DEPEND gflags_unittest3$(EXEEXT): $(gflags_unittest3_OBJECTS) $(gflags_unittest3_DEPENDENCIES) @rm -f gflags_unittest3$(EXEEXT) $(CXXLINK) $(gflags_unittest3_LDFLAGS) $(gflags_unittest3_OBJECTS) $(gflags_unittest3_LDADD) $(LIBS) +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f $$d$$p; then \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \ + else :; fi; \ + done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -430,6 +465,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gflags_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gflags_unittest_main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgflags_la-gflags.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgflags_la-gflags_completions.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgflags_la-gflags_reporting.Plo@am__quote@ .cc.o: @@ -467,6 +503,13 @@ libgflags_la-gflags_reporting.lo: src/gflags_reporting.cc @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgflags_la_CXXFLAGS) $(CXXFLAGS) -c -o libgflags_la-gflags_reporting.lo `test -f 'src/gflags_reporting.cc' || echo '$(srcdir)/'`src/gflags_reporting.cc +libgflags_la-gflags_completions.lo: src/gflags_completions.cc +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgflags_la_CXXFLAGS) $(CXXFLAGS) -MT libgflags_la-gflags_completions.lo -MD -MP -MF "$(DEPDIR)/libgflags_la-gflags_completions.Tpo" -c -o libgflags_la-gflags_completions.lo `test -f 'src/gflags_completions.cc' || echo '$(srcdir)/'`src/gflags_completions.cc; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/libgflags_la-gflags_completions.Tpo" "$(DEPDIR)/libgflags_la-gflags_completions.Plo"; else rm -f "$(DEPDIR)/libgflags_la-gflags_completions.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/gflags_completions.cc' object='libgflags_la-gflags_completions.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgflags_la_CXXFLAGS) $(CXXFLAGS) -c -o libgflags_la-gflags_completions.lo `test -f 'src/gflags_completions.cc' || echo '$(srcdir)/'`src/gflags_completions.cc + gflags_unittest.o: src/gflags_unittest.cc @am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT gflags_unittest.o -MD -MP -MF "$(DEPDIR)/gflags_unittest.Tpo" -c -o gflags_unittest.o `test -f 'src/gflags_unittest.cc' || echo '$(srcdir)/'`src/gflags_unittest.cc; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/gflags_unittest.Tpo" "$(DEPDIR)/gflags_unittest.Po"; else rm -f "$(DEPDIR)/gflags_unittest.Tpo"; exit 1; fi @@ -822,7 +865,7 @@ check: check-am all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) $(DATA) \ $(HEADERS) installdirs: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(googleincludedir)"; do \ + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(googleincludedir)"; do \ test -z "$$dir" || $(mkdir_p) "$$dir"; \ done install: install-am @@ -874,7 +917,7 @@ info-am: install-data-am: install-dist_docDATA install-googleincludeHEADERS -install-exec-am: install-libLTLIBRARIES +install-exec-am: install-binSCRIPTS install-libLTLIBRARIES install-info: install-info-am @@ -902,8 +945,9 @@ ps: ps-am ps-am: -uninstall-am: uninstall-dist_docDATA uninstall-googleincludeHEADERS \ - uninstall-info-am uninstall-libLTLIBRARIES +uninstall-am: uninstall-binSCRIPTS uninstall-dist_docDATA \ + uninstall-googleincludeHEADERS uninstall-info-am \ + uninstall-libLTLIBRARIES .PHONY: CTAGS GTAGS all all-am am--refresh check check-TESTS check-am \ clean clean-generic clean-libLTLIBRARIES clean-libtool \ @@ -912,14 +956,15 @@ uninstall-am: uninstall-dist_docDATA uninstall-googleincludeHEADERS \ distclean-compile distclean-generic distclean-hdr \ distclean-libtool distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-dist_docDATA install-exec install-exec-am \ - install-googleincludeHEADERS install-info install-info-am \ - install-libLTLIBRARIES install-man install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags uninstall uninstall-am uninstall-dist_docDATA \ + install install-am install-binSCRIPTS install-data \ + install-data-am install-dist_docDATA install-exec \ + install-exec-am install-googleincludeHEADERS install-info \ + install-info-am install-libLTLIBRARIES install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-binSCRIPTS uninstall-dist_docDATA \ uninstall-googleincludeHEADERS uninstall-info-am \ uninstall-libLTLIBRARIES diff --git a/configure b/configure index fd5a2d9..29b59c0 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.59 for gflags 0.8. +# Generated by GNU Autoconf 2.59 for gflags 0.9. # # Report bugs to . # @@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='gflags' PACKAGE_TARNAME='gflags' -PACKAGE_VERSION='0.8' -PACKAGE_STRING='gflags 0.8' +PACKAGE_VERSION='0.9' +PACKAGE_STRING='gflags 0.9' PACKAGE_BUGREPORT='opensource@google.com' ac_unique_file="README" @@ -465,7 +465,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CPP CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE build build_cpu build_vendor build_os host host_cpu host_vendor host_os EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL LIBTOOL_DEPS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS ac_google_start_namespace ac_google_end_namespace ac_google_namespace ac_cv___attribute__unused ac_cv_have_stdint_h ac_cv_have_systypes_h ac_cv_have_inttypes_h ac_cv_have_uint16_t ac_cv_have_u_int16_t ac_cv_have___uint16 LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CPP CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE GCC_TRUE GCC_FALSE build build_cpu build_vendor build_os host host_cpu host_vendor host_os EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL LIBTOOL_DEPS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS ac_google_start_namespace ac_google_end_namespace ac_google_namespace ac_cv___attribute__unused ac_cv_have_stdint_h ac_cv_have_systypes_h ac_cv_have_inttypes_h ac_cv_have_uint16_t ac_cv_have_u_int16_t ac_cv_have___uint16 LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -954,7 +954,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures gflags 0.8 to adapt to many kinds of systems. +\`configure' configures gflags 0.9 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1020,7 +1020,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of gflags 0.8:";; + short | recursive ) echo "Configuration of gflags 0.9:";; esac cat <<\_ACEOF @@ -1163,7 +1163,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -gflags configure 0.8 +gflags configure 0.9 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1177,7 +1177,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by gflags $as_me 0.8, which was +It was created by gflags $as_me 0.9, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1823,7 +1823,7 @@ fi # Define the identity of the package. PACKAGE='gflags' - VERSION='0.8' + VERSION='0.9' cat >>confdefs.h <<_ACEOF @@ -3750,6 +3750,16 @@ fi + +if test "$GCC" = yes; then + GCC_TRUE= + GCC_FALSE='#' +else + GCC_TRUE='#' + GCC_FALSE= +fi + # let the Makefile know if we're gcc + # Uncomment this if you'll be exporting libraries (.so's) # Check whether --enable-shared or --disable-shared was given. if test "${enable_shared+set}" = set; then @@ -4371,7 +4381,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 4374 "configure"' > conftest.$ac_ext + echo '#line 4384 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -5268,7 +5278,7 @@ fi # Provide some information about the compiler. -echo "$as_me:5271:" \ +echo "$as_me:5281:" \ "checking for Fortran 77 compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 @@ -6329,11 +6339,11 @@ else -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:6332: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6342: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6336: \$? = $ac_status" >&5 + echo "$as_me:6346: \$? = $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. @@ -6597,11 +6607,11 @@ else -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:6600: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6610: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:6604: \$? = $ac_status" >&5 + echo "$as_me:6614: \$? = $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. @@ -6701,11 +6711,11 @@ else -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:6704: $lt_compile\"" >&5) + (eval echo "\"\$as_me:6714: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:6708: \$? = $ac_status" >&5 + echo "$as_me:6718: \$? = $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 @@ -9059,7 +9069,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:11510: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:11504: \$? = $ac_status" >&5 + echo "$as_me:11514: \$? = $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. @@ -11601,11 +11611,11 @@ else -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:11604: $lt_compile\"" >&5) + (eval echo "\"\$as_me:11614: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:11608: \$? = $ac_status" >&5 + echo "$as_me:11618: \$? = $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 @@ -13189,11 +13199,11 @@ else -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:13192: $lt_compile\"" >&5) + (eval echo "\"\$as_me:13202: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:13196: \$? = $ac_status" >&5 + echo "$as_me:13206: \$? = $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. @@ -13293,11 +13303,11 @@ else -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:13296: $lt_compile\"" >&5) + (eval echo "\"\$as_me:13306: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:13300: \$? = $ac_status" >&5 + echo "$as_me:13310: \$? = $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 @@ -15516,11 +15526,11 @@ else -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:15519: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15529: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:15523: \$? = $ac_status" >&5 + echo "$as_me:15533: \$? = $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. @@ -15784,11 +15794,11 @@ else -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:15787: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15797: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:15791: \$? = $ac_status" >&5 + echo "$as_me:15801: \$? = $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. @@ -15888,11 +15898,11 @@ else -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:15891: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15901: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:15895: \$? = $ac_status" >&5 + echo "$as_me:15905: \$? = $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 @@ -20039,50 +20049,99 @@ test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}" PTHREAD_CC=$CC fi - # The next part tries to detect GCC inconsistency with -shared on some - # architectures and systems. The problem is that in certain - # configurations, when -shared is specified, GCC "forgets" to - # internally use various flags which are still necessary. + # The next part tries to detect GCC inconsistency with -shared on some + # architectures and systems. The problem is that in certain + # configurations, when -shared is specified, GCC "forgets" to + # internally use various flags which are still necessary. - echo "$as_me:$LINENO: checking whether to check for GCC pthread/shared inconsistencies" >&5 + # + # Prepare the flags + # + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_CC="$CC" + + # Try with the flags determined by the earlier checks. + # + # -Wl,-z,defs forces link-time symbol resolution, so that the + # linking checks with -shared actually have any value + # + # FIXME: -fPIC is required for -shared on many architectures, + # so we specify it here, but the right way would probably be to + # properly detect whether it is actually required. + CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CC="$PTHREAD_CC" + + # In order not to create several levels of indentation, we test + # the value of "$done" until we find the cure or run out of ideas. + done="no" + + # First, make sure the CFLAGS we added are actually accepted by our + # compiler. If not (and OS X's ld, for instance, does not accept -z), + # then we can't do this test. + if test x"$done" = xno; then + echo "$as_me:$LINENO: checking whether to check for GCC pthread/shared inconsistencies" >&5 echo $ECHO_N "checking whether to check for GCC pthread/shared inconsistencies... $ECHO_C" >&6 - check_inconsistencies=yes - case "${host_cpu}-${host_os}" in - *-darwin*) check_inconsistencies=no ;; - esac - if test x"$GCC" != xyes -o "x$check_inconsistencies" != xyes ; then - echo "$as_me:$LINENO: result: no" >&5 + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&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); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +done=yes +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + if test "x$done" = xyes ; then + echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 - else - echo "$as_me:$LINENO: result: yes" >&5 + else + echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 + fi + fi - # In order not to create several levels of indentation, we test - # the value of "$ok" until we find out the cure or run out of - # ideas. - ok="no" - - # - # Prepare the flags - # - save_CFLAGS="$CFLAGS" - save_LIBS="$LIBS" - save_CC="$CC" - # Try with the flags determined by the earlier checks. - # - # -Wl,-z,defs forces link-time symbol resolution, so that the - # linking checks with -shared actually have any value - # - # FIXME: -fPIC is required for -shared on many architectures, - # so we specify it here, but the right way would probably be to - # properly detect whether it is actually required. - CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - CC="$PTHREAD_CC" - - echo "$as_me:$LINENO: checking whether -pthread is sufficient with -shared" >&5 + if test x"$done" = xno; then + echo "$as_me:$LINENO: checking whether -pthread is sufficient with -shared" >&5 echo $ECHO_N "checking whether -pthread is sufficient with -shared... $ECHO_C" >&6 - cat >conftest.$ac_ext <<_ACEOF + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext @@ -20093,8 +20152,8 @@ int main () { pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ; return 0; } @@ -20120,7 +20179,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ok=yes + done=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 @@ -20129,23 +20188,24 @@ fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - if test "x$ok" = xyes; then - echo "$as_me:$LINENO: result: yes" >&5 + if test "x$done" = xyes; then + echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 - else - echo "$as_me:$LINENO: result: no" >&5 + else + echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 - fi + fi + fi - # - # Linux gcc on some architectures such as mips/mipsel forgets - # about -lpthread - # - if test x"$ok" = xno; then - echo "$as_me:$LINENO: checking whether -lpthread fixes that" >&5 + # + # Linux gcc on some architectures such as mips/mipsel forgets + # about -lpthread + # + if test x"$done" = xno; then + echo "$as_me:$LINENO: checking whether -lpthread fixes that" >&5 echo $ECHO_N "checking whether -lpthread fixes that... $ECHO_C" >&6 - LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" - cat >conftest.$ac_ext <<_ACEOF + LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext @@ -20156,8 +20216,8 @@ int main () { pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ; return 0; } @@ -20183,7 +20243,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ok=yes + done=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 @@ -20192,23 +20252,23 @@ fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - if test "x$ok" = xyes; then - echo "$as_me:$LINENO: result: yes" >&5 + if test "x$done" = xyes; then + echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 - PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" - else - echo "$as_me:$LINENO: result: no" >&5 + PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" + else + echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 - fi - fi - # - # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc - # - if test x"$ok" = xno; then - echo "$as_me:$LINENO: checking whether -lc_r fixes that" >&5 + fi + fi + # + # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc + # + if test x"$done" = xno; then + echo "$as_me:$LINENO: checking whether -lc_r fixes that" >&5 echo $ECHO_N "checking whether -lc_r fixes that... $ECHO_C" >&6 - LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" - cat >conftest.$ac_ext <<_ACEOF + LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" + cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext @@ -20219,8 +20279,8 @@ int main () { pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ; return 0; } @@ -20246,7 +20306,7 @@ if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then - ok=yes + done=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 @@ -20255,28 +20315,27 @@ fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext - if test "x$ok" = xyes; then - echo "$as_me:$LINENO: result: yes" >&5 + if test "x$done" = xyes; then + echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 - PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" - else - echo "$as_me:$LINENO: result: no" >&5 + PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" + else + echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 - fi - fi - if test x"$ok" = xno; then - # OK, we have run out of ideas - { echo "$as_me:$LINENO: WARNING: Impossible to determine how to use pthreads with shared libraries" >&5 + fi + fi + if test x"$done" = xno; then + # OK, we have run out of ideas + { echo "$as_me:$LINENO: WARNING: Impossible to determine how to use pthreads with shared libraries" >&5 echo "$as_me: WARNING: Impossible to determine how to use pthreads with shared libraries" >&2;} - # so it's not safe to assume that we may use pthreads - acx_pthread_ok=no - fi + # so it's not safe to assume that we may use pthreads + acx_pthread_ok=no + fi - CFLAGS="$save_CFLAGS" - LIBS="$save_LIBS" - CC="$save_CC" - fi + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + CC="$save_CC" else PTHREAD_CC="$CC" fi @@ -20518,7 +20577,7 @@ else google_namespace="$google_namespace_default" fi; if test -n "$google_namespace"; then - ac_google_namespace="$google_namespace" + ac_google_namespace="::$google_namespace" ac_google_start_namespace="namespace $google_namespace {" ac_google_end_namespace="}" else @@ -20558,7 +20617,7 @@ _ACEOF ## Check out ../autoconf/ for other macros you can call to do useful stuff # Write generated configuration file, and also .h files - ac_config_files="$ac_config_files Makefile src/google/gflags.h" + ac_config_files="$ac_config_files Makefile src/google/gflags.h src/google/gflags_completions.h" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -20672,6 +20731,13 @@ echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." >&2;} { (exit 1); exit 1; }; } fi +if test -z "${GCC_TRUE}" && test -z "${GCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"GCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"GCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files @@ -20943,7 +21009,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by gflags $as_me 0.8, which was +This file was extended by gflags $as_me 0.9, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -21006,7 +21072,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -gflags config.status 0.8 +gflags config.status 0.9 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" @@ -21118,6 +21184,7 @@ do # Handling of arguments. "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; "src/google/gflags.h" ) CONFIG_FILES="$CONFIG_FILES src/google/gflags.h" ;; + "src/google/gflags_completions.h" ) CONFIG_FILES="$CONFIG_FILES src/google/gflags_completions.h" ;; "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "src/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 @@ -21250,6 +21317,8 @@ s,@ac_ct_CXX@,$ac_ct_CXX,;t t s,@CXXDEPMODE@,$CXXDEPMODE,;t t s,@am__fastdepCXX_TRUE@,$am__fastdepCXX_TRUE,;t t s,@am__fastdepCXX_FALSE@,$am__fastdepCXX_FALSE,;t t +s,@GCC_TRUE@,$GCC_TRUE,;t t +s,@GCC_FALSE@,$GCC_FALSE,;t t s,@build@,$build,;t t s,@build_cpu@,$build_cpu,;t t s,@build_vendor@,$build_vendor,;t t diff --git a/configure.ac b/configure.ac index e0e5511..c0a8e03 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ # make sure we're interpreted by some minimal autoconf AC_PREREQ(2.57) -AC_INIT(gflags, 0.8, opensource@google.com) +AC_INIT(gflags, 0.9, opensource@google.com) # The argument here is just something that should be in the current directory # (for sanity checking) AC_CONFIG_SRCDIR(README) @@ -15,6 +15,7 @@ AM_CONFIG_HEADER(src/config.h) AC_PROG_CC AC_PROG_CPP AC_PROG_CXX +AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc # Uncomment this if you'll be exporting libraries (.so's) AC_PROG_LIBTOOL @@ -67,5 +68,5 @@ AC_SUBST(ac_cv_have___uint16) ## Check out ../autoconf/ for other macros you can call to do useful stuff # Write generated configuration file, and also .h files -AC_CONFIG_FILES([Makefile src/google/gflags.h]) +AC_CONFIG_FILES([Makefile src/google/gflags.h src/google/gflags_completions.h]) AC_OUTPUT diff --git a/doc/gflags.html b/doc/gflags.html index e059a7b..5ac362b 100644 --- a/doc/gflags.html +++ b/doc/gflags.html @@ -189,6 +189,38 @@ file. #include will make explicit the dependency between the two files. This causes the flag to be a global variable.

+ +

RegisterFlagValidator: Sanity-checking Flag Values

+ +

After DEFINE-ing a flag, you may optionally register a validator +function with the flag. If you do this, after the flag is parsed from +the commandline, and whenever its value is changes via a call to +SetCommandLineOption(), the validator function is called +with the new value as an argument. The validator function should +return 'true' if the flag value is valid, and false otherwise. + +

Here is an example use of this functionality:

+
+static bool ValidatePort(const char* flagname, int32 value) {
+   if (value > 0 && value < 32768)   // value is ok
+     return true;
+   printf("Invalid value for --%s: %d\n", flagname, (int)value);
+   return false;
+}
+DEFINE_int32(port, 0, "What port to listen on");
+static const bool port_dummy = RegisterFlagValidator(&FLAGS_port, &ValidatePort);
+
+ +

By doing the registration at global initialization time (right +after the DEFINE), we ensure that the registration happens before +the commandline is parsed at the beginning of main().

+ +

RegisterFlagValidator() returns true if the +registration is successful. It return false if the registration fails +because a) the first argument does not refer to a commandline flag, or +b) a different validator has already been registered for this flag.

+ +

Putting It Together: How to Set Up Flags

The final piece is the one that tells the executable to process the diff --git a/m4/acx_pthread.m4 b/m4/acx_pthread.m4 index f5db4f0..2cf20de 100644 --- a/m4/acx_pthread.m4 +++ b/m4/acx_pthread.m4 @@ -1,7 +1,12 @@ # This was retrieved from -# http://0pointer.de/cgi-bin/viewcvs.cgi/trunk/common/acx_pthread.m4?rev=1227 +# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?revision=1277&root=avahi # See also (perhaps for new versions?) -# http://0pointer.de/cgi-bin/viewcvs.cgi/trunk/common/acx_pthread.m4 +# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?root=avahi +# +# We've rewritten the inconsistency check code (from avahi), to work +# more broadly. In particular, it no longer assumes ld accepts -zdefs. +# This caused a restructing of the code, but the functionality has only +# changed a little. dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) dnl @@ -231,108 +236,113 @@ if test "x$acx_pthread_ok" = xyes; then PTHREAD_CC=$CC fi - # The next part tries to detect GCC inconsistency with -shared on some - # architectures and systems. The problem is that in certain - # configurations, when -shared is specified, GCC "forgets" to - # internally use various flags which are still necessary. - - AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies]) - check_inconsistencies=yes - case "${host_cpu}-${host_os}" in - *-darwin*) check_inconsistencies=no ;; - esac - if test x"$GCC" != xyes -o "x$check_inconsistencies" != xyes ; then - AC_MSG_RESULT([no]) - else - AC_MSG_RESULT([yes]) - - # In order not to create several levels of indentation, we test - # the value of "$ok" until we find out the cure or run out of - # ideas. - ok="no" - - # - # Prepare the flags - # - save_CFLAGS="$CFLAGS" - save_LIBS="$LIBS" - save_CC="$CC" - # Try with the flags determined by the earlier checks. - # - # -Wl,-z,defs forces link-time symbol resolution, so that the - # linking checks with -shared actually have any value - # - # FIXME: -fPIC is required for -shared on many architectures, - # so we specify it here, but the right way would probably be to - # properly detect whether it is actually required. - CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" - CC="$PTHREAD_CC" - - AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) - AC_TRY_LINK([#include ], - [pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], - [ok=yes]) - - if test "x$ok" = xyes; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - fi - - # - # Linux gcc on some architectures such as mips/mipsel forgets - # about -lpthread - # - if test x"$ok" = xno; then - AC_MSG_CHECKING([whether -lpthread fixes that]) - LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" - AC_TRY_LINK([#include ], - [pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], - [ok=yes]) - - if test "x$ok" = xyes; then - AC_MSG_RESULT([yes]) - PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" - else - AC_MSG_RESULT([no]) - fi - fi - # - # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc - # - if test x"$ok" = xno; then - AC_MSG_CHECKING([whether -lc_r fixes that]) - LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" - AC_TRY_LINK([#include ], - [pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], - [ok=yes]) - - if test "x$ok" = xyes; then - AC_MSG_RESULT([yes]) - PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" - else - AC_MSG_RESULT([no]) - fi - fi - if test x"$ok" = xno; then - # OK, we have run out of ideas - AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries]) - - # so it's not safe to assume that we may use pthreads - acx_pthread_ok=no - fi - - CFLAGS="$save_CFLAGS" - LIBS="$save_LIBS" - CC="$save_CC" - fi + # The next part tries to detect GCC inconsistency with -shared on some + # architectures and systems. The problem is that in certain + # configurations, when -shared is specified, GCC "forgets" to + # internally use various flags which are still necessary. + + # + # Prepare the flags + # + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_CC="$CC" + + # Try with the flags determined by the earlier checks. + # + # -Wl,-z,defs forces link-time symbol resolution, so that the + # linking checks with -shared actually have any value + # + # FIXME: -fPIC is required for -shared on many architectures, + # so we specify it here, but the right way would probably be to + # properly detect whether it is actually required. + CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CC="$PTHREAD_CC" + + # In order not to create several levels of indentation, we test + # the value of "$done" until we find the cure or run out of ideas. + done="no" + + # First, make sure the CFLAGS we added are actually accepted by our + # compiler. If not (and OS X's ld, for instance, does not accept -z), + # then we can't do this test. + if test x"$done" = xno; then + AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies]) + AC_TRY_LINK(,, , [done=yes]) + + if test "x$done" = xyes ; then + AC_MSG_RESULT([no]) + else + AC_MSG_RESULT([yes]) + fi + fi + + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + fi + + # + # Linux gcc on some architectures such as mips/mipsel forgets + # about -lpthread + # + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -lpthread fixes that]) + LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" + else + AC_MSG_RESULT([no]) + fi + fi + # + # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc + # + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -lc_r fixes that]) + LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" + else + AC_MSG_RESULT([no]) + fi + fi + if test x"$done" = xno; then + # OK, we have run out of ideas + AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries]) + + # so it's not safe to assume that we may use pthreads + acx_pthread_ok=no + fi + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + CC="$save_CC" else PTHREAD_CC="$CC" fi diff --git a/m4/google_namespace.m4 b/m4/google_namespace.m4 index 79e0a6d..7f244cc 100644 --- a/m4/google_namespace.m4 +++ b/m4/google_namespace.m4 @@ -6,6 +6,12 @@ # when it makes sense -- for instance, when publishing stl-like code -- you # may want to go with a different default, like 'std'. +# We guarantee the invariant that GOOGLE_NAMESPACE starts with ::, +# unless it's the empty string. Thus, it's always safe to do +# GOOGLE_NAMESPACE::foo and be sure you're getting the foo that's +# actually in the google namespace, and not some other namespace that +# the namespace rules might kick in. + AC_DEFUN([AC_DEFINE_GOOGLE_NAMESPACE], [google_namespace_default=[$1] AC_ARG_ENABLE(namespace, [ --enable-namespace=FOO to define these Google @@ -19,7 +25,7 @@ AC_DEFUN([AC_DEFINE_GOOGLE_NAMESPACE], esac], [google_namespace="$google_namespace_default"]) if test -n "$google_namespace"; then - ac_google_namespace="$google_namespace" + ac_google_namespace="::$google_namespace" ac_google_start_namespace="namespace $google_namespace {" ac_google_end_namespace="}" else diff --git a/packages/deb/changelog b/packages/deb/changelog index fcdbd3c..361b387 100644 --- a/packages/deb/changelog +++ b/packages/deb/changelog @@ -1,3 +1,9 @@ +google-gflags (0.9-1) unstable; urgency=low + + * New upstream release. + + -- Google Inc. Mon, 21 Jul 2008 23:01:38 -0700 + google-gflags (0.8-1) unstable; urgency=low * New upstream release. diff --git a/packages/deb/libgoogle-gflags0.dirs b/packages/deb/libgoogle-gflags0.dirs index 6845771..14f5b95 100644 --- a/packages/deb/libgoogle-gflags0.dirs +++ b/packages/deb/libgoogle-gflags0.dirs @@ -1 +1,2 @@ usr/lib +usr/bin diff --git a/packages/deb/libgoogle-gflags0.install b/packages/deb/libgoogle-gflags0.install index 704ea87..434db32 100644 --- a/packages/deb/libgoogle-gflags0.install +++ b/packages/deb/libgoogle-gflags0.install @@ -1,2 +1,4 @@ usr/lib/lib*.so.* debian/tmp/usr/lib/lib*.so.* +usr/bin/* +debian/tmp/usr/bin/* diff --git a/packages/rpm/rpm.spec b/packages/rpm/rpm.spec index fa68885..f2259cc 100644 --- a/packages/rpm/rpm.spec +++ b/packages/rpm/rpm.spec @@ -56,6 +56,7 @@ rm -rf $RPM_BUILD_ROOT %{prefix}/lib/libgflags.so.0 %{prefix}/lib/libgflags.so.0.0.0 +%{prefix}/bin/gflags_completions.sh %files devel %defattr(-,root,root) diff --git a/python/gflags.py b/python/gflags.py index bd35d81..32fb1e1 100755 --- a/python/gflags.py +++ b/python/gflags.py @@ -190,13 +190,18 @@ try: except AttributeError: # a very old python, that lacks sys.version_info raise NotImplementedError("requires python 2.2.0 or later") -# If we're not running at least python 2.3, define True and False +# If we're not running at least python 2.2.1, define True, False, and bool. # Thanks, Guido, for the code. try: - True, False + True, False, bool except NameError: False = 0 True = 1 + def bool(x): + if x: + return True + else: + return False # Are we running under pychecker? _RUNNING_PYCHECKER = 'pychecker.python' in sys.modules @@ -219,7 +224,7 @@ class FlagsError(Exception): """The base class for all flags errors""" class DuplicateFlag(FlagsError): - """"Raised if there is a flag naming conflict""" + """Raised if there is a flag naming conflict""" # A DuplicateFlagError conveys more information than # a DuplicateFlag. Since there are external modules @@ -519,7 +524,13 @@ class FlagValues: flag_values: registry to copy from """ for flag_name, flag in flag_values.FlagDict().iteritems(): - self[flag_name] = flag + # 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. + if flag_name == flag.name: + self[flag_name] = flag def __setitem__(self, name, flag): """ @@ -1103,7 +1114,6 @@ class Flag: def __init__(self, parser, serializer, name, default, help_string, short_name=None, boolean=0, allow_override=0): self.name = name - self.default = default if not help_string: help_string = '(no help available)' @@ -1117,17 +1127,7 @@ class Flag: self.allow_override = allow_override self.value = None - # 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 default is None and allow_override: - raise DuplicateFlag, name - - self.Unparse() - - self.default_as_str = self.__GetParsedValueAsString(self.value) + self.SetDefault(default) def __GetParsedValueAsString(self, value): if value is None: @@ -1172,13 +1172,17 @@ class Flag: """ Change the default value, and current value, of this flag object """ - if value is not None: # See __init__ for logic details - self.Parse(value) - self.present -= 1 # reset .present after parsing new default value - else: - self.value = None + # 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 + self.default = value - self.default_as_str = self.__GetParsedValueAsString(value) + self.Unparse() + self.default_as_str = self.__GetParsedValueAsString(self.value) # End of Flag definition class ArgumentParser: @@ -1284,14 +1288,21 @@ class BooleanParser(ArgumentParser): def Convert(self, argument): """ - convert the argument to a boolean (integer); raise ValueError on errors + convert the argument to a boolean; raise ValueError on errors """ if type(argument) == str: if argument.lower() in ['true', 't', '1']: - return 1 + return True elif argument.lower() in ['false', 'f', '0']: - return 0 - return int(argument) + return False + + bool_argument = bool(argument) + if argument == bool_argument: + # The argument is a valid boolean (True, False, 0, or 1), and not just + # something that always converts to bool (list, string, int, etc.). + return bool_argument + + raise ValueError('Non-boolean argument to boolean flag', argument) def Parse(self, argument): val = self.Convert(argument) @@ -1300,7 +1311,7 @@ class BooleanParser(ArgumentParser): class BooleanFlag(Flag): """ A basic boolean flag. Boolean flags do not take any arguments, and - their value is either 0 (false) or 1 (true). The false value is + 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. @@ -1319,7 +1330,7 @@ def DEFINE_boolean(name, default, help, flag_values=FLAGS, **args): 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, 0 or 1. None is possible if + 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. """ diff --git a/python/gflags_unittest.py b/python/gflags_unittest.py index c574070..1661440 100755 --- a/python/gflags_unittest.py +++ b/python/gflags_unittest.py @@ -44,14 +44,50 @@ import unittest import gflags as flags FLAGS=flags.FLAGS -# If we're not running at least python 2.3, as is the case when -# invoked from flags_unittest_2_2, define True and False. -# Thanks, Guido, for the code. -try: - True, False -except NameError: - False = 0 - True = 1 +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 FlagsUnitTest(unittest.TestCase): "Flags Unit Test" @@ -551,6 +587,20 @@ class FlagsUnitTest(unittest.TestCase): self.assertEqual("new1" in FLAGS.FlagDict(), True) self.assertEqual("new2" in FLAGS.FlagDict(), True) + # Make sure AppendFlagValues works with flags with shortnames. + new_flags = flags.FlagValues() + flags.DEFINE_boolean("new3", 0, "runhelp n3", flag_values=new_flags) + flags.DEFINE_boolean("new4", 0, "runhelp n4", flag_values=new_flags, + short_name="n4") + self.assertEqual(len(new_flags.FlagDict()), 3) + old_len = len(FLAGS.FlagDict()) + FLAGS.AppendFlagValues(new_flags) + self.assertEqual(len(FLAGS.FlagDict())-old_len, 3) + self.assertTrue("new3" in FLAGS.FlagDict()) + self.assertTrue("new4" in FLAGS.FlagDict()) + self.assertTrue("n4" in FLAGS.FlagDict()) + self.assertEqual(FLAGS.FlagDict()['n4'], FLAGS.FlagDict()['new4']) + # Make sure AppendFlagValues fails on duplicates flags.DEFINE_boolean("dup4", 0, "runhelp d41") new_flags = flags.FlagValues() @@ -586,12 +636,19 @@ class FlagsUnitTest(unittest.TestCase): except flags.FlagsError: pass - # Argument erroneously supplied for boolean + # Non-boolean arguments for boolean try: argv = ('./program', '--debug=goofup') FLAGS(argv) - raise AssertionError("No argument allowed exception not raised") - except flags.FlagsError: + raise AssertionError("Illegal flag value exception not raised") + except flags.IllegalFlagValue: + pass + + try: + argv = ('./program', '--debug=42') + FLAGS(argv) + raise AssertionError("Illegal flag value exception not raised") + except flags.IllegalFlagValue: pass @@ -667,12 +724,14 @@ class FlagsUnitTest(unittest.TestCase): flags.DEFINE_boolean('UnitTestBoolFlag', 0, 'Some Boolean thing') flags.DEFINE_integer('UnitTestNumber', 12345, 'Some integer', lower_bound=0) + flags.DEFINE_list('UnitTestList', "1,2,3", 'Some list') def _UndeclareSomeFlags(self): FLAGS.__delattr__('UnitTestMessage1') FLAGS.__delattr__('UnitTestMessage2') FLAGS.__delattr__('UnitTestBoolFlag') FLAGS.__delattr__('UnitTestNumber') + FLAGS.__delattr__('UnitTestList') #### Flagfile Unit Tests #### def testMethod_flagfiles_1(self): @@ -825,6 +884,13 @@ class FlagsUnitTest(unittest.TestCase): FLAGS([ 'dummyscript', '--UnitTestBoolFlag=true' ]) self.assertEqual(FLAGS.UnitTestBoolFlag, True) + # Test that setting a list default works correctly. + FLAGS['UnitTestList'].SetDefault('4,5,6') + self.assertEqual(FLAGS.UnitTestList, ['4', '5', '6']) + self.assertEqual(FLAGS['UnitTestList'].default_as_str, "'4,5,6'") + FLAGS([ 'dummyscript', '--UnitTestList=7,8,9' ]) + self.assertEqual(FLAGS.UnitTestList, ['7', '8', '9']) + # Test that setting invalid defaults raises exceptions self.assertRaises(flags.IllegalFlagValue, FLAGS['UnitTestNumber'].SetDefault, 'oops') @@ -1120,42 +1186,7 @@ class FlagsUnitTest(unittest.TestCase): -z,--[no]zoom1: runhelp z1 (default: 'false')""" - if help != expected_help: - print "Error: FLAGS.MainModuleHelp() didn't return the expected result." - print "Got:" - print help - print "[End of got]" - - help_lines = help.split('\n') - expected_help_lines = expected_help.split('\n') - - num_help_lines = len(help_lines) - num_expected_help_lines = len(expected_help_lines) - - if num_help_lines != num_expected_help_lines: - print "Number of help lines = %d, expected %d" % ( - num_help_lines, num_expected_help_lines) - - num_to_match = min(num_help_lines, num_expected_help_lines) - - for i in range(num_to_match): - if help_lines[i] != expected_help_lines[i]: - print "One discrepancy: Got:" - print help_lines[i] - print "Expected:" - print expected_help_lines[i] - break - else: - # If we got here, found no discrepancy, print first new line. - if num_help_lines > num_expected_help_lines: - print "New help line:" - print help_lines[num_expected_help_lines] - elif num_expected_help_lines > num_help_lines: - print "Missing expected help line:" - print expected_help_lines[num_help_lines] - else: - print "Bug in this test -- discrepancy detected but not found." - + if not MultiLineEqual(expected_help, help): self.fail() def test_create_flag_errors(self): diff --git a/src/gflags.cc b/src/gflags.cc index 78aad91..3d4881a 100644 --- a/src/gflags.cc +++ b/src/gflags.cc @@ -32,7 +32,61 @@ // Revamped and reorganized by Craig Silverstein // // This file contains the implementation of all our command line flags -// stuff. +// stuff. Here's how everything fits together +// +// * FlagRegistry owns CommandLineFlags owns FlagValue. +// * FlagSaver holds a FlagRegistry (saves it at construct time, +// restores it at destroy time). +// * CommandLineFlagParser lives outside that hierarchy, but works on +// CommandLineFlags (modifying the FlagValues). +// * Free functions like SetCommandLineOption() work via one of the +// above (such as CommandLineFlagParser). +// +// In more detail: +// +// -- The main classes that hold flag data: +// +// FlagValue holds the current value of a flag. It's +// pseudo-templatized: every operation on a FlagValue is typed. It +// also deals with storage-lifetime issues (so flag values don't go +// away in a destructor), which is why we need a whole class to hold a +// variable's value. +// +// CommandLineFlag is all the information about a single command-line +// flag. It has a FlagValue for the flag's current value, but also +// the flag's name, type, etc. +// +// FlagRegistry is a collection of CommandLineFlags. There's the +// global registry, which is where flags defined via DEFINE_foo() +// live. But it's possible to define your own flag, manually, in a +// different registry you create. (In practice, multiple registries +// are used only by FlagSaver). +// +// A given FlagValue is owned by exactly one CommandLineFlag. A given +// CommandLineFlag is owned by exactly one FlagRegistry. FlagRegistry +// has a lock; any operation that writes to a FlagValue or +// CommandLineFlag owned by that registry must acquire the +// FlagRegistry lock before doing so. +// +// --- Some other classes and free functions: +// +// CommandLineFlagInfo is a client-exposed version of CommandLineFlag. +// Once it's instantiated, it has no dependencies or relationships +// with any other part of this file. +// +// FlagRegisterer is the helper class used by the DEFINE_* macros to +// allow work to be done at global initialization time. +// +// CommandLineFlagParser is the class that reads from the commandline +// and instantiates flag values based on that. It needs to poke into +// the innards of the FlagValue->CommandLineFlag->FlagRegistry class +// hierarchy to do that. It's careful to acquire the FlagRegistry +// lock before doing any writing or other non-const actions. +// +// GetCommandLineOption is just a hook into registry routines to +// retrieve a flag based on its name. SetCommandLineOption, on the +// other hand, hooks into CommandLineFlagParser. Other API functions +// are, similarly, mostly hooks into the functionality described above. #include "config.h" #include // for snprintf @@ -42,6 +96,7 @@ #include #include #include +#include // for cerr #include #include #include @@ -70,6 +125,7 @@ using std::string; using std::map; using std::vector; using std::pair; +using std::cerr; // Special flags, type 1: the 'recursive' flags. They set another flag's val. DEFINE_string(flagfile, "", @@ -88,25 +144,36 @@ DEFINE_string(undefok, "", _START_GOOGLE_NAMESPACE_ -// There are also 'reporting' flags, in commandlineflags_reporting.cc. - -static const char kError[] = "ERROR: "; - // The help message indicating that the commandline flag has been // 'stripped'. It will not show up when doing "-help" and its // variants. The flag is stripped if STRIP_FLAG_HELP is set to 1 // before including google/gflags.h. +// This is used by this file, and also in commandlineflags_reporting.cc const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001"; +// This is used by the unittest to test error-exit code +void (*commandlineflags_exitfunc)(int) = &exit; // from stdlib.h + +namespace { + +// There are also 'reporting' flags, in commandlineflags_reporting.cc. + +static const char kError[] = "ERROR: "; + // Indicates that undefined options are to be ignored. // Enables deferred processing of flags in dynamically loaded libraries. static bool allow_command_line_reparsing = false; static bool logging_is_probably_set_up = false; -// This is used by the unittest to test error-exit code -void (*commandlineflags_exitfunc)(int) = &exit; // from stdlib.h +// This is a 'prototype' validate-function. 'Real' validate +// functions, take a flag-value as an argument: ValidateFn(bool) or +// ValidateFn(uint64). However, for easier storage, we strip off this +// argument and then restore it when actually calling the function on +// a flag value. +typedef bool (*ValidateFnProto)(); + // -------------------------------------------------------------------- // FlagValue @@ -124,9 +191,12 @@ class FlagValue { string ToString() const; private: - friend class CommandLineFlag; - friend class FlagSaverImpl; // calls New() + friend class CommandLineFlag; // for many things, including Validate() + friend class GOOGLE_NAMESPACE::FlagSaverImpl; // calls New() + friend class FlagRegistry; // checks value_buffer_ for flags_by_ptr_ map template friend T GetFromEnv(const char*, const char*, T); + friend bool TryParseLocked(const class CommandLineFlag*, FlagValue*, + const char*, string*); // for New(), CopyFrom() enum ValueType {FV_BOOL, FV_INT32, FV_INT64, FV_UINT64, FV_DOUBLE, FV_STRING}; @@ -135,8 +205,13 @@ class FlagValue { FlagValue* New() const; // creates a new one with default value void CopyFrom(const FlagValue& x); + // Calls the given validate-fn on value_buffer_, and returns + // whatever it returns. But first casts validate_fn_proto to a + // function that takes our value as an argument (eg void + // (*validate_fn)(bool) for a bool flag). + bool Validate(const char* flagname, ValidateFnProto validate_fn_proto) const; + void* value_buffer_; // points to the buffer holding our data - bool we_own_buffer_; // true iff we new-ed the buffer ValueType type_; // how to interpret value_ FlagValue(const FlagValue&); // no copying! @@ -262,6 +337,33 @@ string FlagValue::ToString() const { } } +bool FlagValue::Validate(const char* flagname, + ValidateFnProto validate_fn_proto) const { + switch (type_) { + case FV_BOOL: + return reinterpret_cast( + validate_fn_proto)(flagname, VALUE_AS(bool)); + case FV_INT32: + return reinterpret_cast( + validate_fn_proto)(flagname, VALUE_AS(int32)); + case FV_INT64: + return reinterpret_cast( + validate_fn_proto)(flagname, VALUE_AS(int64)); + case FV_UINT64: + return reinterpret_cast( + validate_fn_proto)(flagname, VALUE_AS(uint64)); + case FV_DOUBLE: + return reinterpret_cast( + validate_fn_proto)(flagname, VALUE_AS(double)); + case FV_STRING: + return reinterpret_cast( + validate_fn_proto)(flagname, VALUE_AS(string)); + default: + assert(false); // unknown type + return false; + } +} + const char* FlagValue::TypeName() const { switch (type_) { case FV_BOOL: return "bool"; @@ -318,6 +420,10 @@ void FlagValue::CopyFrom(const FlagValue& x) { // This represents a single flag, including its name, description, // default value, and current value. Mostly this serves as a // struct, though it also knows how to register itself. +// All CommandLineFlags are owned by a (exactly one) +// FlagRegistry. If you wish to modify fields in this class, you +// should acquire the FlagRegistry lock for the registry that owns +// this flag. // -------------------------------------------------------------------- class CommandLineFlag { @@ -334,13 +440,19 @@ class CommandLineFlag { string current_value() const { return current_->ToString(); } string default_value() const { return defvalue_->ToString(); } const char* type_name() const { return defvalue_->TypeName(); } + ValidateFnProto validate_function() const { return validate_fn_proto_; } void FillCommandLineFlagInfo(struct CommandLineFlagInfo* result); + // If validate_fn_proto_ is non-NULL, calls it on value, returns result. + bool Validate(const FlagValue& value) const; + bool ValidateCurrent() const { return Validate(*current_); } + private: - friend class FlagRegistry; // for SetFlagLocked() - friend class FlagSaverImpl; // for cloning the values + friend class FlagRegistry; // for SetFlagLocked() and setting flags_by_ptr_ + friend class GOOGLE_NAMESPACE::FlagSaverImpl; // for cloning the values friend bool GetCommandLineOption(const char*, string*, bool*); + friend bool AddFlagValidator(const void*, ValidateFnProto); // set validate_fn // This copies all the non-const members: modified, processed, defvalue, etc. void CopyFrom(const CommandLineFlag& src); @@ -353,6 +465,11 @@ class CommandLineFlag { bool modified_; // Set after default assignment? FlagValue* defvalue_; // Default value for flag FlagValue* current_; // Current value for flag + // This is a casted, 'generic' version of validate_fn, which actually + // takes a flag-value as an arg (void (*validate_fn)(bool), say). + // When we pass this to current_->Validate(), it will cast it back to + // the proper type. This may be NULL to mean we have no validate_fn. + ValidateFnProto validate_fn_proto_; CommandLineFlag(const CommandLineFlag&); // no copying! void operator=(const CommandLineFlag&); @@ -362,7 +479,7 @@ CommandLineFlag::CommandLineFlag(const char* name, const char* help, const char* filename, FlagValue* current_val, FlagValue* default_val) : name_(name), help_(help), file_(filename), modified_(false), - defvalue_(default_val), current_(current_val) { + defvalue_(default_val), current_(current_val), validate_fn_proto_(NULL) { } CommandLineFlag::~CommandLineFlag() { @@ -405,6 +522,7 @@ void CommandLineFlag::FillCommandLineFlagInfo( result->filename = CleanFileName(); UpdateModifiedBit(); result->is_default = !modified_; + result->has_validator_fn = validate_function() != NULL; } void CommandLineFlag::UpdateModifiedBit() { @@ -420,6 +538,14 @@ void CommandLineFlag::CopyFrom(const CommandLineFlag& src) { modified_ = src.modified_; current_->CopyFrom(*src.current_); defvalue_->CopyFrom(*src.defvalue_); + validate_fn_proto_ = src.validate_fn_proto_; +} + +bool CommandLineFlag::Validate(const FlagValue& value) const { + if (validate_function() == NULL) + return true; + else + return value.Validate(name(), validate_function()); } @@ -455,12 +581,18 @@ class FlagRegistry { // Returns the flag object for the specified name, or NULL if not found. CommandLineFlag* FindFlagLocked(const char* name); + // Returns the flag object whose current-value is stored at flag_ptr. + // That is, for whom current_->value_buffer_ == flag_ptr + CommandLineFlag* FindFlagViaPtrLocked(const void* flag_ptr); + // A fancier form of FindFlag that works correctly if name is of the // form flag=value. In that case, we set key to point to flag, and - // modify v to point to the value, and return the flag with the - // given name (or NULL if not found). + // modify v to point to the value (if present), and return the flag + // with the given name. If the flag does not exist, returns NULL + // and sets error_message. CommandLineFlag* SplitArgumentLocked(const char* argument, - string* key, const char** v); + string* key, const char** v, + string* error_message); // Set the value of a flag. If the flag was successfully set to // value, set msg to indicate the new flag-value, and return true. @@ -472,14 +604,22 @@ class FlagRegistry { static FlagRegistry* GlobalRegistry(); // returns a singleton registry private: - friend class FlagSaverImpl; // reads all the flags in order to copy them - friend void GetAllFlags(vector*); + friend class GOOGLE_NAMESPACE::FlagSaverImpl; // reads all the flags in order to copy them + friend class CommandLineFlagParser; // for ValidateAllFlags + friend void GOOGLE_NAMESPACE::GetAllFlags(vector*); + // The map from name to flag, for FindFlagLocked(). typedef map FlagMap; typedef FlagMap::iterator FlagIterator; typedef FlagMap::const_iterator FlagConstIterator; FlagMap flags_; + + // The map from current-value pointer to flag, fo FindFlagViaPtrLocked(). + typedef map FlagPtrMap; + FlagPtrMap flags_by_ptr_; + pthread_mutex_t lock_; + static FlagRegistry* global_registry_; // a singleton registry static pthread_once_t global_registry_once_; static int global_registry_once_nothreads_; // when we don't link pthreads @@ -491,6 +631,15 @@ class FlagRegistry { FlagRegistry& operator=(const FlagRegistry&); }; +class FlagRegistryLock { + public: + explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); } + ~FlagRegistryLock() { fr_->Unlock(); } + private: + FlagRegistry *const fr_; +}; + + void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { Lock(); pair ins = @@ -513,6 +662,8 @@ void FlagRegistry::RegisterFlag(CommandLineFlag* flag) { } commandlineflags_exitfunc(1); // almost certainly exit() } + // Also add to the flags_by_ptr_ map. + flags_by_ptr_[flag->current_->value_buffer_] = flag; Unlock(); } @@ -525,9 +676,19 @@ CommandLineFlag* FlagRegistry::FindFlagLocked(const char* name) { } } +CommandLineFlag* FlagRegistry::FindFlagViaPtrLocked(const void* flag_ptr) { + FlagPtrMap::const_iterator i = flags_by_ptr_.find(flag_ptr); + if (i == flags_by_ptr_.end()) { + return NULL; + } else { + return i->second; + } +} + CommandLineFlag* FlagRegistry::SplitArgumentLocked(const char* arg, string* key, - const char** v) { + const char** v, + string* error_message) { // Find the flag object for this option const char* flag_name; const char* value = strchr(arg, '='); @@ -542,24 +703,36 @@ CommandLineFlag* FlagRegistry::SplitArgumentLocked(const char* arg, flag_name = key->c_str(); CommandLineFlag* flag = FindFlagLocked(flag_name); - if (flag == NULL && (flag_name[0] == 'n') && (flag_name[1] == 'o')) { - // See if we can find a boolean flag named "x" for an option - // named "nox". - flag = FindFlagLocked(flag_name+2); - if (flag != NULL) { - if (strcmp(flag->type_name(), "bool") != 0) { - // This is not a boolean flag, so we should not strip the "no" prefix - flag = NULL; - } else { - // Make up a fake value to replace the "no" we stripped out - key->assign(flag_name+2); // the name without the "no" - *v = "0"; - } - } - } if (flag == NULL) { - return NULL; + // If we can't find the flag-name, then we should return an error. + // The one exception is if 1) the flag-name is 'nox', 2) there + // exists a flag named 'x', and 3) 'x' is a boolean flag. + // In that case, we want to return flag 'x'. + if (!(flag_name[0] == 'n' && flag_name[1] == 'o')) { + // flag-name is not 'nox', so we're not in the exception case. + *error_message = (string(kError) + + "unknown command line flag '" + *key + "'\n"); + return NULL; + } + flag = FindFlagLocked(flag_name+2); + if (flag == NULL) { + // No flag named 'x' exists, so we're not in the exception case. + *error_message = (string(kError) + + "unknown command line flag '" + *key + "'\n"); + return NULL; + } + if (strcmp(flag->type_name(), "bool") != 0) { + // 'x' exists but is not boolean, so we're not in the exception case. + *error_message = (string(kError) + + "boolean value (" + *key + ") specified for " + + flag->type_name() + " command line flag\n"); + return NULL; + } + // We're in the exception case! + // Make up a fake value to replace the "no" we stripped out + key->assign(flag_name+2); // the name without the "no" + *v = "0"; } // Assign a value if this is a boolean flag @@ -570,20 +743,34 @@ CommandLineFlag* FlagRegistry::SplitArgumentLocked(const char* arg, return flag; } -// Can't make this static because of friendship. -inline bool TryParse(const CommandLineFlag* flag, FlagValue* flag_value, - const char* value, string* msg) { - if (flag_value->ParseFrom(value)) { - if (msg) - *msg += (string(flag->name()) + " set to " + flag_value->ToString() - + "\n"); - return true; - } else { - if (msg) +bool TryParseLocked(const CommandLineFlag* flag, FlagValue* flag_value, + const char* value, string* msg) { + // Use tenative_value, not flag_value, until we know value is valid. + FlagValue* tentative_value = flag_value->New(); + if (!tentative_value->ParseFrom(value)) { + if (msg) { *msg += (string(kError) + "illegal value '" + value + + "' specified for " + flag->type_name() + " flag '" + flag->name() + "'\n"); + } + delete tentative_value; return false; + } else if (!flag->Validate(*tentative_value)) { + if (msg){ + *msg += (string(kError) + "failed validation of new value " + + "'" + tentative_value->ToString() + "' for flag '" + + + flag->name() + "'\n"); + } + delete tentative_value; + return false; + } else { + flag_value->CopyFrom(*tentative_value); + if (msg) { + *msg += (string(flag->name()) + " set to " + flag_value->ToString() + + "\n"); + } + delete tentative_value; + return true; } } @@ -595,7 +782,7 @@ bool FlagRegistry::SetFlagLocked(CommandLineFlag* flag, switch (set_mode) { case SET_FLAGS_VALUE: { // set or modify the flag's value - if (!TryParse(flag, flag->current_, value, msg)) + if (!TryParseLocked(flag, flag->current_, value, msg)) return false; flag->modified_ = true; break; @@ -603,7 +790,7 @@ bool FlagRegistry::SetFlagLocked(CommandLineFlag* flag, case SET_FLAG_IF_DEFAULT: { // set the flag's value, but only if it hasn't been set by someone else if (!flag->modified_) { - if (!TryParse(flag, flag->current_, value, msg)) + if (!TryParseLocked(flag, flag->current_, value, msg)) return false; flag->modified_ = true; } else { @@ -613,11 +800,11 @@ bool FlagRegistry::SetFlagLocked(CommandLineFlag* flag, } case SET_FLAGS_DEFAULT: { // modify the flag's default-value - if (!TryParse(flag, flag->defvalue_, value, msg)) + if (!TryParseLocked(flag, flag->defvalue_, value, msg)) return false; if (!flag->modified_) { // Need to set both defvalue *and* current, in this case - TryParse(flag, flag->current_, value, NULL); + TryParseLocked(flag, flag->current_, value, NULL); } break; } @@ -664,148 +851,6 @@ FlagRegistry* FlagRegistry::GlobalRegistry() { return global_registry_; } - -void FlagsTypeWarn(const char *name) { - fprintf(stderr, "ERROR: Flag %s is of type bool, " - "but its default value is not a boolean.\n", name); - // This can (and one day should) become a compilations error - //commandlineflags_exitfunc(1); // almost certainly exit() -} - -// -------------------------------------------------------------------- -// FlagRegisterer -// This class exists merely to have a global constructor (the -// kind that runs before main(), that goes an initializes each -// flag that's been declared. Note that it's very important we -// don't have a destructor that deletes flag_, because that would -// cause us to delete current_storage/defvalue_storage as well, -// which can cause a crash if anything tries to access the flag -// values in a global destructor. -// -------------------------------------------------------------------- - -FlagRegisterer::FlagRegisterer(const char* name, const char* type, - const char* help, const char* filename, - void* current_storage, void* defvalue_storage) { - FlagValue* current = new FlagValue(current_storage, type); - FlagValue* defvalue = new FlagValue(defvalue_storage, type); - // Importantly, flag_ will never be deleted, so storage is always good. - flag_ = new CommandLineFlag(name, help, filename, current, defvalue); - FlagRegistry::GlobalRegistry()->RegisterFlag(flag_); // default registry -} - - -// -------------------------------------------------------------------- -// GetAllFlags() -// The main way the FlagRegistry class exposes its data. This -// returns, as strings, all the info about all the flags in -// the main registry, sorted first by filename they are defined -// in, and then by flagname. -// -------------------------------------------------------------------- - -struct FilenameFlagnameCmp { - bool operator()(const CommandLineFlagInfo& a, - const CommandLineFlagInfo& b) const { - int cmp = strcmp(a.filename.c_str(), b.filename.c_str()); - if (cmp == 0) - cmp = strcmp(a.name.c_str(), b.name.c_str()); // secondary sort key - return cmp < 0; - } -}; - -void GetAllFlags(vector* OUTPUT) { - FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); - registry->Lock(); - for (FlagRegistry::FlagConstIterator i = registry->flags_.begin(); - i != registry->flags_.end(); ++i) { - CommandLineFlagInfo fi; - i->second->FillCommandLineFlagInfo(&fi); - OUTPUT->push_back(fi); - } - registry->Unlock(); - // Now sort the flags, first by filename they occur in, then alphabetically - sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameCmp()); -} - -// -------------------------------------------------------------------- -// SetArgv() -// GetArgvs() -// GetArgv() -// GetArgv0() -// ProgramInvocationName() -// ProgramInvocationShortName() -// SetUsageMessage() -// ProgramUsage() -// Functions to set and get argv. Typically the setter is called -// by ParseCommandLineFlags. Also can get the ProgramUsage string, -// set by SetUsageMessage. -// -------------------------------------------------------------------- - -// These values are not protected by a Mutex because they are normally -// set only once during program startup. -static const char* argv0 = "UNKNOWN"; // just the program name -static const char* cmdline = ""; // the entire command-line -static vector argvs; -static uint32 argv_sum = 0; -static const char* program_usage = "Warning: SetUsageMessage() never called"; -static bool program_usage_set = false; - -void SetArgv(int argc, const char** argv) { - static bool called_set_argv = false; - if (called_set_argv) // we already have an argv for you - return; - - called_set_argv = true; - - assert(argc > 0); // every program has at least a progname - argv0 = strdup(argv[0]); // small memory leak, but fn only called once - assert(argv0); - - string cmdline_string = string(""); // easier than doing strcats - argvs.clear(); - for (int i = 0; i < argc; i++) { - if (i != 0) - cmdline_string += " "; - cmdline_string += argv[i]; - argvs.push_back(argv[i]); - } - cmdline = strdup(cmdline_string.c_str()); // another small memory leak - assert(cmdline); - - // Compute a simple sum of all the chars in argv - argv_sum = 0; - for (const char* c = cmdline; *c; c++) - argv_sum += *c; -} - -const vector& GetArgvs() { return argvs; } -const char* GetArgv() { return cmdline; } -const char* GetArgv0() { return argv0; } -uint32 GetArgvSum() { return argv_sum; } -const char* ProgramInvocationName() { // like the GNU libc fn - return GetArgv0(); -} -const char* ProgramInvocationShortName() { // like the GNU libc fn - const char* slash = strrchr(argv0, '/'); -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) - if (!slash) slash = strrchr(argv0, '\\'); -#endif - return slash ? slash + 1 : argv0; -} - -void SetUsageMessage(const string& usage) { - if (program_usage_set) { - fprintf(stderr, "ERROR: SetUsageMessage() called more than once\n"); - commandlineflags_exitfunc(1); // almost certainly exit() - } - - program_usage = strdup(usage.c_str()); // small memory leak - program_usage_set = true; -} - -const char* ProgramUsage() { - return program_usage; -} - // -------------------------------------------------------------------- // CommandLineFlagParser // Parsing is done in two stages. In the first, we go through @@ -815,7 +860,7 @@ const char* ProgramUsage() { // along with an explanation of the trouble. In stage 2, we // handle the 'reporting' flags like --help and --mpm_version. // (This is via a call to HandleCommandLineHelpFlags(), in -// commandlineflags_reporting.cc.) +// gflags_reporting.cc.) // An optional stage 3 prints out the error messages. // This is a bit of a simplification. For instance, --flagfile // is handled as soon as it's seen in stage 1, not in stage 2. @@ -835,9 +880,13 @@ class CommandLineFlagParser { uint32 ParseNewCommandLineFlags(int* argc, char*** argv, bool remove_flags); // Stage 2: print reporting info and exit, if requested. - // In commandlineflags_reporting.cc:HandleCommandLineHelpFlags(). + // In gflags_reporting.cc:HandleCommandLineHelpFlags(). + + // Stage 3: validate all the commandline flags that have validators + // registered. + void ValidateAllFlags(); - // Stage 3: report any errors and return true if any were found. + // Stage 4: report any errors and return true if any were found. bool ReportErrors(); // Set a particular command line option. "newval" is a string @@ -956,11 +1005,12 @@ uint32 CommandLineFlagParser::ParseNewCommandLineFlags(int* argc, char*** argv, // Find the flag object for this option string key; const char* value; - CommandLineFlag* flag = registry_->SplitArgumentLocked(arg, &key, &value); + string error_message; + CommandLineFlag* flag = registry_->SplitArgumentLocked(arg, &key, &value, + &error_message); if (flag == NULL) { undefined_names_[key] = ""; // value isn't actually used - error_flags_[key] = (string(kError) + - "unknown command line flag '" + key + "'\n"); + error_flags_[key] = error_message; continue; } @@ -1079,6 +1129,23 @@ string CommandLineFlagParser::ProcessSingleOptionLocked( return msg; } +void CommandLineFlagParser::ValidateAllFlags() { + FlagRegistryLock frl(registry_); + for (FlagRegistry::FlagConstIterator i = registry_->flags_.begin(); + i != registry_->flags_.end(); ++i) { + if (!i->second->ValidateCurrent()) { + // only set a message if one isn't already there. (If there's + // an error message, our job is done, even if it's not exactly + // the same error.) + if (error_flags_[i->second->name()].empty()) + error_flags_[i->second->name()] = (string(kError) + + "--" + i->second->name() + + " must be set on the commandline" + + " (default value fails validation)"); + } + } +} + bool CommandLineFlagParser::ReportErrors() { // error_flags_ indicates errors we saw while parsing. // But we ignore undefined-names if ok'ed by --undef_ok @@ -1144,8 +1211,10 @@ string CommandLineFlagParser::ProcessOptionsFromStringLocked( name_and_val++; // skip second - too string key; const char* value; + string error_message; CommandLineFlag* flag = registry_->SplitArgumentLocked(name_and_val, - &key, &value); + &key, &value, + &error_message); // By API, errors parsing flagfile lines are silently ignored. if (flag == NULL) { // "WARNING: flagname '" + key + "' not found\n" @@ -1181,6 +1250,208 @@ string CommandLineFlagParser::ProcessOptionsFromStringLocked( return retval; } +// -------------------------------------------------------------------- +// GetFromEnv() +// AddFlagValidator() +// These are helper functions for routines like BoolFromEnv() and +// RegisterFlagValidator, defined below. They're defined here so +// they can live in the unnamed namespace (which makes friendship +// declarations for these classes possible). +// -------------------------------------------------------------------- + +template +T GetFromEnv(const char *varname, const char* type, T dflt) { + const char* const valstr = getenv(varname); + if (!valstr) + return dflt; + FlagValue ifv(new T, type); + if (!ifv.ParseFrom(valstr)) { + fprintf(stderr, "ERROR: error parsing env variable '%s' with value '%s'\n", + varname, valstr); + commandlineflags_exitfunc(1); + } + return OTHER_VALUE_AS(ifv, T); +} + +bool AddFlagValidator(const void* flag_ptr, ValidateFnProto validate_fn_proto) { + // We want a lock around this routine, in case two threads try to + // add a validator (hopefully the same one!) at once. We could use + // our own thread, but we need to loook at the registry anyway, so + // we just steal that one. + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + FlagRegistryLock frl(registry); + // First, find the flag whose current-flag storage is 'flag'. + // This is the CommandLineFlag whose current_->value_buffer_ == flag + CommandLineFlag* flag = registry->FindFlagViaPtrLocked(flag_ptr); + if (!flag) { + // WARNING << "Ignoring RegisterValidateFunction() for flag pointer " + // << flag_ptr << ": no flag found at that address"; + return false; + } else if (validate_fn_proto == flag->validate_function()) { + return true; // ok to register the same function over and over again + } else if (validate_fn_proto != NULL && flag->validate_function() != NULL) { + // WARNING << "Ignoring RegisterValidateFunction() for flag '" + // << flag->name() << "': validate-fn already registered"; + return false; + } else { + flag->validate_fn_proto_ = validate_fn_proto; + return true; + } +} + +} // end unnamed namespaces + + +// Now define the functions that are exported via the .h file + +// -------------------------------------------------------------------- +// FlagRegisterer +// This class exists merely to have a global constructor (the +// kind that runs before main(), that goes an initializes each +// flag that's been declared. Note that it's very important we +// don't have a destructor that deletes flag_, because that would +// cause us to delete current_storage/defvalue_storage as well, +// which can cause a crash if anything tries to access the flag +// 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) { + if (help == NULL) + help = ""; + // FlagValue expects the type-name to not include any namespace + // components, so we get rid of those, if any. + if (strchr(type, ':')) + type = strrchr(type, ':') + 1; + FlagValue* current = new FlagValue(current_storage, type); + FlagValue* defvalue = new FlagValue(defvalue_storage, type); + // Importantly, flag_ will never be deleted, so storage is always good. + CommandLineFlag* flag = new CommandLineFlag(name, help, filename, + current, defvalue); + FlagRegistry::GlobalRegistry()->RegisterFlag(flag); // default registry +} + +// -------------------------------------------------------------------- +// GetAllFlags() +// The main way the FlagRegistry class exposes its data. This +// returns, as strings, all the info about all the flags in +// the main registry, sorted first by filename they are defined +// in, and then by flagname. +// -------------------------------------------------------------------- + +struct FilenameFlagnameCmp { + bool operator()(const CommandLineFlagInfo& a, + const CommandLineFlagInfo& b) const { + int cmp = strcmp(a.filename.c_str(), b.filename.c_str()); + if (cmp == 0) + cmp = strcmp(a.name.c_str(), b.name.c_str()); // secondary sort key + return cmp < 0; + } +}; + +void GetAllFlags(vector* OUTPUT) { + FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); + registry->Lock(); + for (FlagRegistry::FlagConstIterator i = registry->flags_.begin(); + i != registry->flags_.end(); ++i) { + CommandLineFlagInfo fi; + i->second->FillCommandLineFlagInfo(&fi); + OUTPUT->push_back(fi); + } + registry->Unlock(); + // Now sort the flags, first by filename they occur in, then alphabetically + sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameCmp()); +} + +// -------------------------------------------------------------------- +// SetArgv() +// GetArgvs() +// GetArgv() +// GetArgv0() +// ProgramInvocationName() +// ProgramInvocationShortName() +// SetUsageMessage() +// ProgramUsage() +// Functions to set and get argv. Typically the setter is called +// by ParseCommandLineFlags. Also can get the ProgramUsage string, +// set by SetUsageMessage. +// -------------------------------------------------------------------- + +// These values are not protected by a Mutex because they are normally +// set only once during program startup. +static const char* argv0 = "UNKNOWN"; // just the program name +static const char* cmdline = ""; // the entire command-line +static vector argvs; +static uint32 argv_sum = 0; +static const char* program_usage = "Warning: SetUsageMessage() never called"; +static bool program_usage_set = false; + +void SetArgv(int argc, const char** argv) { + static bool called_set_argv = false; + if (called_set_argv) // we already have an argv for you + return; + + called_set_argv = true; + + assert(argc > 0); // every program has at least a progname + argv0 = strdup(argv[0]); // small memory leak, but fn only called once + assert(argv0); + + string cmdline_string = string(""); // easier than doing strcats + argvs.clear(); + for (int i = 0; i < argc; i++) { + if (i != 0) + cmdline_string += " "; + cmdline_string += argv[i]; + argvs.push_back(argv[i]); + } + cmdline = strdup(cmdline_string.c_str()); // another small memory leak + assert(cmdline); + + // Compute a simple sum of all the chars in argv + argv_sum = 0; + for (const char* c = cmdline; *c; c++) + argv_sum += *c; +} + +const vector& GetArgvs() { return argvs; } +const char* GetArgv() { return cmdline; } +const char* GetArgv0() { return argv0; } +uint32 GetArgvSum() { return argv_sum; } +const char* ProgramInvocationName() { // like the GNU libc fn + return GetArgv0(); +} +const char* ProgramInvocationShortName() { // like the GNU libc fn + const char* slash = strrchr(argv0, '/'); +#ifdef OS_WINDOWS + if (!slash) slash = strrchr(argv0, '\\'); +#endif + return slash ? slash + 1 : argv0; +} + +void SetUsageMessage(const string& usage) { + if (program_usage_set) { + fprintf(stderr, "ERROR: SetUsageMessage() called more than once\n"); + exit(1); + } + + program_usage = strdup(usage.c_str()); // small memory leak + program_usage_set = true; +} + +const char* ProgramUsage() { + return program_usage; +} // -------------------------------------------------------------------- // GetCommandLineOption() @@ -1207,14 +1478,12 @@ bool GetCommandLineOption(const char* name, string* value) { assert(value); FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); - registry->Lock(); + FlagRegistryLock frl(registry); CommandLineFlag* flag = registry->FindFlagLocked(name); if (flag == NULL) { - registry->Unlock(); return false; } else { *value = flag->current_value(); - registry->Unlock(); return true; } } @@ -1222,15 +1491,13 @@ bool GetCommandLineOption(const char* name, string* value) { bool GetCommandLineFlagInfo(const char* name, CommandLineFlagInfo* OUTPUT) { if (NULL == name) return false; FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); - registry->Lock(); + FlagRegistryLock frl(registry); CommandLineFlag* flag = registry->FindFlagLocked(name); if (flag == NULL) { - registry->Unlock(); return false; } else { assert(OUTPUT); flag->FillCommandLineFlagInfo(OUTPUT); - registry->Unlock(); return true; } } @@ -1248,7 +1515,7 @@ string SetCommandLineOptionWithMode(const char* name, const char* value, FlagSettingMode set_mode) { string result; FlagRegistry* const registry = FlagRegistry::GlobalRegistry(); - registry->Lock(); + FlagRegistryLock frl(registry); CommandLineFlag* flag = registry->FindFlagLocked(name); if (flag) { CommandLineFlagParser parser(registry); @@ -1260,7 +1527,6 @@ string SetCommandLineOptionWithMode(const char* name, const char* value, // result); } } - registry->Unlock(); // The API of this function is that we return empty string on error return result; } @@ -1269,7 +1535,6 @@ string SetCommandLineOption(const char* name, const char* value) { return SetCommandLineOptionWithMode(name, value, SET_FLAGS_VALUE); } - // -------------------------------------------------------------------- // FlagSaver // FlagSaverImpl @@ -1296,7 +1561,7 @@ class FlagSaverImpl { // It's an error to call this more than once. // Must be called when the registry mutex is not held. void SaveFromRegistry() { - main_registry_->Lock(); + FlagRegistryLock frl(main_registry_); assert(backup_registry_.empty()); // call only once! for (FlagRegistry::FlagConstIterator it = main_registry_->flags_.begin(); it != main_registry_->flags_.end(); @@ -1310,7 +1575,6 @@ class FlagSaverImpl { backup->CopyFrom(*main); backup_registry_.push_back(backup); // add it to a convenient list } - main_registry_->Unlock(); } // Restores the saved flag states into the flag registry. We @@ -1318,7 +1582,7 @@ class FlagSaverImpl { // the SaveFromRegistry; if they were, that's trouble! Must be // called when the registry mutex is not held. void RestoreToRegistry() { - main_registry_->Lock(); + FlagRegistryLock frl(main_registry_); vector::const_iterator it; for (it = backup_registry_.begin(); it != backup_registry_.end(); ++it) { CommandLineFlag* main = main_registry_->FindFlagLocked((*it)->name()); @@ -1326,7 +1590,6 @@ class FlagSaverImpl { main->CopyFrom(**it); } } - main_registry_->Unlock(); } private: @@ -1459,20 +1722,6 @@ bool ReadFromFlagsFile(const string& filename, const char* prog_name, // DEFINE_bool(myflag, BoolFromEnv("MYFLAG_DEFAULT", false), "whatever"); // -------------------------------------------------------------------- -template -T GetFromEnv(const char *varname, const char* type, T dflt) { - const char* const valstr = getenv(varname); - if (!valstr) - return dflt; - FlagValue ifv(new T, type); - if (!ifv.ParseFrom(valstr)) { - fprintf(stderr, "ERROR: error parsing env variable '%s' with value '%s'\n", - varname, valstr); - commandlineflags_exitfunc(1); // almost certainly exit() - } - return OTHER_VALUE_AS(ifv, T); -} - bool BoolFromEnv(const char *v, bool dflt) { return GetFromEnv(v, "bool", dflt); } @@ -1495,12 +1744,50 @@ const char *StringFromEnv(const char *varname, const char *dflt) { // -------------------------------------------------------------------- +// RegisterFlagValidator() +// RegisterFlagValidator() is the function that clients use to +// 'decorate' a flag with a validation function. Once this is +// done, every time the flag is set (including when the flag +// is parsed from argv), the validator-function is called. +// These functions return true if the validator was added +// successfully, or false if not: the flag already has a validator, +// (only one allowed per flag), the 1st arg isn't a flag, etc. +// This function is not thread-safe. +// -------------------------------------------------------------------- + +bool RegisterFlagValidator(const bool* flag, + bool (*validate_fn)(const char*, bool)) { + return AddFlagValidator(flag, reinterpret_cast(validate_fn)); +} +bool RegisterFlagValidator(const int32* flag, + bool (*validate_fn)(const char*, int32)) { + return AddFlagValidator(flag, reinterpret_cast(validate_fn)); +} +bool RegisterFlagValidator(const int64* flag, + bool (*validate_fn)(const char*, int64)) { + return AddFlagValidator(flag, reinterpret_cast(validate_fn)); +} +bool RegisterFlagValidator(const uint64* flag, + bool (*validate_fn)(const char*, uint64)) { + return AddFlagValidator(flag, reinterpret_cast(validate_fn)); +} +bool RegisterFlagValidator(const double* flag, + bool (*validate_fn)(const char*, double)) { + return AddFlagValidator(flag, reinterpret_cast(validate_fn)); +} +bool RegisterFlagValidator(const string* flag, + bool (*validate_fn)(const char*, const string&)) { + return AddFlagValidator(flag, reinterpret_cast(validate_fn)); +} + + +// -------------------------------------------------------------------- // ParseCommandLineFlags() // ParseCommandLineNonHelpFlags() // HandleCommandLineHelpFlags() // This is the main function called from main(), to actually // parse the commandline. It modifies argc and argv as described -// at the top of commandlineflags.h. You can also divide this +// at the top of gflags.h. You can also divide this // function into two parts, if you want to do work between // the parsing of the flags and the printing of any help output. // -------------------------------------------------------------------- @@ -1529,6 +1816,10 @@ static uint32 ParseCommandLineFlagsInternal(int* argc, char*** argv, if (do_report) HandleCommandLineHelpFlags(); // may cause us to exit on --help, etc. + + // See if any of the unset flags fail their validation checks + parser.ValidateAllFlags(); + if (parser.ReportErrors()) // may cause us to exit on illegal flags commandlineflags_exitfunc(1); // almost certainly exit() return r; diff --git a/src/gflags_completions.cc b/src/gflags_completions.cc new file mode 100644 index 0000000..a573cfa --- /dev/null +++ b/src/gflags_completions.cc @@ -0,0 +1,743 @@ +// Copyright (c) 2008, 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: Dave Nicponski +// +// Bash-style command line flag completion for C++ binaries +// +// This module implements bash-style completions. It achieves this +// goal in the following broad chunks: +// +// 1) Take a to-be-completed word, and examine it for search hints +// 2) Identify all potentially matching flags +// 2a) If there are no matching flags, do nothing. +// 2b) If all matching flags share a common prefix longer than the +// completion word, output just that matching prefix +// 3) Categorize those flags to produce a rough ordering of relevence. +// 4) Potentially trim the set of flags returned to a smaller number +// that bash is happier with +// 5) Output the matching flags in groups ordered by relevence. +// 5a) Force bash to place most-relevent groups at the top of the list +// 5b) Trim most flag's descriptions to fit on a single terminal line + + +#include "config.h" +#include + +#include +#include +#include +#include + +#include "google/gflags.h" + +using std::set; +using std::string; +using std::vector; + +#ifndef PATH_SEPARATOR +#define PATH_SEPARATOR '/' +#endif + +DEFINE_string(tab_completion_word, "", + "If non-empty, HandleCommandLineCompletions() will hijack the " + "process and attempt to do bash-style command line flag " + "completion on this value."); +DEFINE_int32(tab_completion_columns, 80, + "Number of columns to use in output for tab completion"); + +_START_GOOGLE_NAMESPACE_ + +namespace { + +// Function prototypes and Type forward declarations. Code may be +// more easily understood if it is roughly ordered according to +// control flow, rather than by C's "declare before use" ordering +struct CompletionOptions; +struct NotableFlags; + +// The entry point if flag completion is to be used. +static void PrintFlagCompletionInfo(void); + + +// 1) Examine search word +static void CanonicalizeCursorWordAndSearchOptions( + const string &cursor_word, + string *canonical_search_token, + CompletionOptions *options); + +static bool RemoveTrailingChar(string *str, char c); + + +// 2) Find all matches +static void FindMatchingFlags( + const vector &all_flags, + const CompletionOptions &options, + const string &match_token, + set *all_matches, + string *longest_common_prefix); + +static bool DoesSingleFlagMatch( + const CommandLineFlagInfo &flag, + const CompletionOptions &options, + const string &match_token); + + +// 3) Categorize matches +static void CategorizeAllMatchingFlags( + const set &all_matches, + const string &search_token, + const string &module, + const string &package_dir, + NotableFlags *notable_flags); + +static void TryFindModuleAndPackageDir( + const vector all_flags, + string *module, + string *package_dir); + + +// 4) Decide which flags to use +static void FinalizeCompletionOutput( + const set &matching_flags, + CompletionOptions *options, + NotableFlags *notable_flags, + vector *completions); + +static void RetrieveUnusedFlags( + const set &matching_flags, + const NotableFlags ¬able_flags, + set *unused_flags); + + +// 5) Output matches +static void OutputSingleGroupWithLimit( + const set &group, + const string &line_indentation, + const string &header, + const string &footer, + bool long_output_format, + int *remaining_line_limit, + int *completion_elements_added, + vector *completions); + +// (helpers for #5) +static string GetShortFlagLine( + const string &line_indentation, + const CommandLineFlagInfo &info); + +static string GetLongFlagLine( + const string &line_indentation, + const CommandLineFlagInfo &info); + + +// +// Useful types + +// Try to deduce the intentions behind this completion attempt. Return the +// canonical search term in 'canonical_search_token'. Binary search options +// are returned in the various booleans, which should all have intuitive +// semantics, possibly except: +// - return_all_matching_flags: Generally, we'll trim the number of +// returned candidates to some small number, showing those that are +// most likely to be useful first. If this is set, however, the user +// really does want us to return every single flag as an option. +// - force_no_update: Any time we output lines, all of which share a +// common prefix, bash will 'helpfully' not even bother to show the +// output, instead changing the current word to be that common prefix. +// If it's clear this shouldn't happen, we'll set this boolean +struct CompletionOptions { + bool flag_name_substring_search; + bool flag_location_substring_search; + bool flag_description_substring_search; + bool return_all_matching_flags; + bool force_no_update; +}; + +// Notable flags are flags that are special or preferred for some +// reason. For example, flags that are defined in the binary's module +// are expected to be much more relevent than flags defined in some +// other random location. These sets are specified roughly in precedence +// order. Once a flag is placed in one of these 'higher' sets, it won't +// be placed in any of the 'lower' sets. +struct NotableFlags { + typedef set FlagSet; + FlagSet perfect_match_flag; + FlagSet module_flags; // Found in module file + FlagSet package_flags; // Found in same directory as module file + FlagSet most_common_flags; // One of the XXX most commonly supplied flags + FlagSet subpackage_flags; // Found in subdirectories of package +}; + + +// +// Tab completion implementation - entry point +static void PrintFlagCompletionInfo(void) { + string cursor_word = FLAGS_tab_completion_word; + string canonical_token; + CompletionOptions options; + CanonicalizeCursorWordAndSearchOptions( + cursor_word, + &canonical_token, + &options); + + //VLOG(1) << "Identified canonical_token: '" << canonical_token << "'"; + + vector all_flags; + set matching_flags; + GetAllFlags(&all_flags); + //VLOG(2) << "Found " << all_flags.size() << " flags overall"; + + string longest_common_prefix; + FindMatchingFlags( + all_flags, + options, + canonical_token, + &matching_flags, + &longest_common_prefix); + //VLOG(1) << "Identified " << matching_flags.size() << " matching flags"; + //VLOG(1) << "Identified " << longest_common_prefix + // << " as longest common prefix."; + if (longest_common_prefix.size() > canonical_token.size()) { + // There's actually a shared common prefix to all matching flags, + // so may as well output that and quit quickly. + //VLOG(1) << "The common prefix '" << longest_common_prefix + // << "' was longer than the token '" << canonical_token + // << "'. Returning just this prefix for completion."; + fprintf(stdout, "--%s", longest_common_prefix.c_str()); + return; + } + if (matching_flags.empty()) { + //VLOG(1) << "There were no matching flags, returning nothing."; + return; + } + + string module; + string package_dir; + TryFindModuleAndPackageDir(all_flags, &module, &package_dir); + //VLOG(1) << "Identified module: '" << module << "'"; + //VLOG(1) << "Identified package_dir: '" << package_dir << "'"; + + NotableFlags notable_flags; + CategorizeAllMatchingFlags( + matching_flags, + canonical_token, + module, + package_dir, + ¬able_flags); + //VLOG(2) << "Categorized matching flags:"; + //VLOG(2) << " perfect_match: " << notable_flags.perfect_match_flag.size(); + //VLOG(2) << " module: " << notable_flags.module_flags.size(); + //VLOG(2) << " package: " << notable_flags.package_flags.size(); + //VLOG(2) << " most common: " << notable_flags.most_common_flags.size(); + //VLOG(2) << " subpackage: " << notable_flags.subpackage_flags.size(); + + vector completions; + FinalizeCompletionOutput( + matching_flags, + &options, + ¬able_flags, + &completions); + + if (options.force_no_update) + completions.push_back("~"); + + //VLOG(1) << "Finalized with " << completions.size() + // << " chosen completions"; + + for (vector::const_iterator it = completions.begin(); + it != completions.end(); + ++it) { + //VLOG(9) << " Completion entry: '" << *it << "'"; + fprintf(stdout, "%s\n", it->c_str()); + } +} + + +// 1) Examine search word (and helper method) +static void CanonicalizeCursorWordAndSearchOptions( + const string &cursor_word, + string *canonical_search_token, + CompletionOptions *options) { + *canonical_search_token = cursor_word; + if (canonical_search_token->empty()) return; + + // Get rid of leading quotes and dashes in the search term + if ((*canonical_search_token)[0] == '"') + *canonical_search_token = canonical_search_token->substr(1); + while ((*canonical_search_token)[0] == '-') + *canonical_search_token = canonical_search_token->substr(1); + + options->flag_name_substring_search = false; + options->flag_location_substring_search = false; + options->flag_description_substring_search = false; + options->return_all_matching_flags = false; + options->force_no_update = false; + + // Look for all search options we can deduce now. Do this by walking + // backwards through the term, looking for up to three '?' and up to + // one '+' as suffixed characters. Consume them if found, and remove + // them from the canonical search token. + int found_question_marks = 0; + int found_plusses = 0; + while (true) { + if (found_question_marks < 3 && + RemoveTrailingChar(canonical_search_token, '?')) { + ++found_question_marks; + continue; + } + if (found_plusses < 1 && + RemoveTrailingChar(canonical_search_token, '+')) { + ++found_plusses; + continue; + } + break; + } + + switch (found_question_marks) { // all fallthroughs + case 3: options->flag_description_substring_search = true; + case 2: options->flag_location_substring_search = true; + case 1: options->flag_name_substring_search = true; + }; + + options->return_all_matching_flags = (found_plusses > 0); +} + +// Returns true if a char was removed +static bool RemoveTrailingChar(string *str, char c) { + if (str->empty()) return false; + if ((*str)[str->size() - 1] == c) { + *str = str->substr(0, str->size() - 1); + return true; + } + return false; +} + + +// 2) Find all matches (and helper methods) +static void FindMatchingFlags( + const vector &all_flags, + const CompletionOptions &options, + const string &match_token, + set *all_matches, + string *longest_common_prefix) { + all_matches->clear(); + bool first_match = true; + for (vector::const_iterator it = all_flags.begin(); + it != all_flags.end(); + ++it) { + if (DoesSingleFlagMatch(*it, options, match_token)) { + all_matches->insert(&*it); + if (first_match) { + first_match = false; + *longest_common_prefix = it->name; + } else { + if (longest_common_prefix->empty() || it->name.empty()) { + longest_common_prefix->clear(); + continue; + } + string::size_type pos = 0; + while (pos < longest_common_prefix->size() && + pos < it->name.size() && + (*longest_common_prefix)[pos] == it->name[pos]) + ++pos; + longest_common_prefix->erase(pos); + } + } + } +} + +// Given the set of all flags, the parsed match options, and the +// canonical search token, produce the set of all candidate matching +// flags for subsequent analysis or filtering. +static bool DoesSingleFlagMatch( + const CommandLineFlagInfo &flag, + const CompletionOptions &options, + const string &match_token) { + // Is there a prefix match? + string::size_type pos = flag.name.find(match_token); + if (pos == 0) return true; + + // Is there a substring match if we want it? + if (options.flag_name_substring_search && + pos != string::npos) + return true; + + // Is there a location match if we want it? + if (options.flag_location_substring_search && + flag.filename.find(match_token) != string::npos) + return true; + + // TODO(daven): All searches should probably be case-insensitive + // (especially this one...) + if (options.flag_description_substring_search && + flag.description.find(match_token) != string::npos) + return true; + + return false; +} + +// 3) Categorize matches (and helper method) + +// Given a set of matching flags, categorize them by +// likely relevence to this specific binary +static void CategorizeAllMatchingFlags( + const set &all_matches, + const string &search_token, + const string &module, // empty if we couldn't find any + const string &package_dir, // empty if we couldn't find any + NotableFlags *notable_flags) { + notable_flags->perfect_match_flag.clear(); + notable_flags->module_flags.clear(); + notable_flags->package_flags.clear(); + notable_flags->most_common_flags.clear(); + notable_flags->subpackage_flags.clear(); + + for (set::const_iterator it = + all_matches.begin(); + it != all_matches.end(); + ++it) { + //VLOG(2) << "Examinging match '" << (*it)->name << "'"; + //VLOG(7) << " filename: '" << (*it)->filename << "'"; + string::size_type pos = string::npos; + if (!package_dir.empty()) + pos = (*it)->filename.find(package_dir); + string::size_type slash = string::npos; + if (pos != string::npos) // candidate for package or subpackage match + slash = (*it)->filename.find( + PATH_SEPARATOR, + pos + package_dir.size() + 1); + + if ((*it)->name == search_token) { + // Exact match on some flag's name + notable_flags->perfect_match_flag.insert(*it); + //VLOG(3) << "Result: perfect match"; + } else if (!module.empty() && (*it)->filename == module) { + // Exact match on module filename + notable_flags->module_flags.insert(*it); + //VLOG(3) << "Result: module match"; + } else if (!package_dir.empty() && + pos != string::npos && slash == string::npos) { + // In the package, since there was no slash after the package portion + notable_flags->package_flags.insert(*it); + //VLOG(3) << "Result: package match"; + } else if (false) { + // In the list of the XXX most commonly supplied flags overall + // TODO(daven): Compile this list. + //VLOG(3) << "Result: most-common match"; + } else if (!package_dir.empty() && + pos != string::npos && slash != string::npos) { + // In a subdirectory of the package + notable_flags->subpackage_flags.insert(*it); + //VLOG(3) << "Result: subpackage match"; + } + + //VLOG(3) << "Result: not special match"; + } +} + +static void TryFindModuleAndPackageDir( + const vector all_flags, + string *module, + string *package_dir) { + module->clear(); + package_dir->clear(); + + vector suffixes; + // TODO(daven): There's some inherant ambiguity here - multiple directories + // could share the same trailing folder and file structure (and even worse, + // same file names), causing us to be unsure as to which of the two is the + // actual package for this binary. In this case, we'll arbitrarily choose. + string progname(ProgramInvocationShortName()); + suffixes.push_back("/" + progname + "."); + suffixes.push_back("/" + progname + "-main."); + suffixes.push_back("/" + progname + "_main."); + // These four are new but probably merited? + suffixes.push_back("/" + progname + "_test."); + suffixes.push_back("/" + progname + "-test."); + suffixes.push_back("/" + progname + "_unittest."); + suffixes.push_back("/" + progname + "-unittest."); + + for (vector::const_iterator it = all_flags.begin(); + it != all_flags.end(); + ++it) { + for (vector::const_iterator suffix = suffixes.begin(); + suffix != suffixes.end(); + ++suffix) { + // TODO(daven): Make sure the match is near the end of the string + if (it->filename.find(*suffix) != string::npos) { + *module = it->filename; + string::size_type sep = it->filename.rfind(PATH_SEPARATOR); + *package_dir = it->filename.substr(0, (sep == string::npos) ? 0 : sep); + return; + } + } + } +} + +// Can't specialize template type on a locally defined type. Silly C++... +struct DisplayInfoGroup { + string header; + string footer; + set *group; +}; + +// 4) Finalize and trim output flag set +static void FinalizeCompletionOutput( + const set &matching_flags, + CompletionOptions *options, + NotableFlags *notable_flags, + vector *completions) { + + // We want to output lines in groups. Each group needs to be indented + // the same to keep its lines together. Unless otherwise required, + // only 99 lines should be output to prevent bash from harassing the + // user. + + // First, figure out which output groups we'll actually use. For each + // nonempty group, there will be ~3 lines of header & footer, plus all + // output lines themselves. + int max_desired_lines = // "999999 flags should be enough for anyone. -dave" + (options->return_all_matching_flags ? 999999 : 98); + int lines_so_far = 0; + + vector output_groups; + bool perfect_match_found = false; + if (lines_so_far < max_desired_lines && + !notable_flags->perfect_match_flag.empty()) { + perfect_match_found = true; + lines_so_far += notable_flags->perfect_match_flag.size() + 2; // no header + DisplayInfoGroup group = + { "", "==========", ¬able_flags->perfect_match_flag }; + output_groups.push_back(group); + } + if (lines_so_far < max_desired_lines && + !notable_flags->module_flags.empty()) { + lines_so_far += notable_flags->module_flags.size() + 3; + DisplayInfoGroup group = { + "-* Matching module flags *-", + "===========================", + ¬able_flags->module_flags }; + output_groups.push_back(group); + } + if (lines_so_far < max_desired_lines && + !notable_flags->package_flags.empty()) { + lines_so_far += notable_flags->package_flags.size() + 3; + DisplayInfoGroup group = { + "-* Matching package flags *-", + "============================", + ¬able_flags->package_flags }; + output_groups.push_back(group); + } + if (lines_so_far < max_desired_lines && + !notable_flags->most_common_flags.empty()) { + lines_so_far += notable_flags->most_common_flags.size() + 3; + DisplayInfoGroup group = { + "-* Commonly used flags *-", + "=========================", + ¬able_flags->most_common_flags }; + output_groups.push_back(group); + } + if (lines_so_far < max_desired_lines && + !notable_flags->subpackage_flags.empty()) { + lines_so_far += notable_flags->subpackage_flags.size() + 3; + DisplayInfoGroup group = { + "-* Matching sub-package flags *-", + "================================", + ¬able_flags->subpackage_flags }; + output_groups.push_back(group); + } + + set obscure_flags; // flags not notable + if (lines_so_far < max_desired_lines) { + RetrieveUnusedFlags(matching_flags, *notable_flags, &obscure_flags); + if (!obscure_flags.empty()) { + lines_so_far += obscure_flags.size() + 2; // no footer + DisplayInfoGroup group = { + "-* Other flags *-", + "", + &obscure_flags }; + output_groups.push_back(group); + } + } + + // Second, go through each of the chosen output groups and output + // as many of those flags as we can, while remaining below our limit + int remaining_lines = max_desired_lines; + int completions_output = 0; + int indent = output_groups.size() - 1; + for (vector::const_iterator it = + output_groups.begin(); + it != output_groups.end(); + ++it, --indent) { + OutputSingleGroupWithLimit( + *it->group, // group + string(indent, ' '), // line indentation + it->header, // header + it->footer, // footer + perfect_match_found, // long format + &remaining_lines, // line limit - reduces this by number printed + &completions_output, // completions (not lines) added + completions); // produced completions + perfect_match_found = false; + } + + if (completions_output != matching_flags.size()) { + options->force_no_update = false; + completions->push_back("~ (Remaining flags hidden) ~"); + } else { + options->force_no_update = true; + } +} + +static void RetrieveUnusedFlags( + const set &matching_flags, + const NotableFlags ¬able_flags, + set *unused_flags) { + // Remove from 'matching_flags' set all members of the sets of + // flags we've already printed (specifically, those in notable_flags) + for (set::const_iterator it = + matching_flags.begin(); + it != matching_flags.end(); + ++it) { + if (notable_flags.perfect_match_flag.count(*it) || + notable_flags.module_flags.count(*it) || + notable_flags.package_flags.count(*it) || + notable_flags.most_common_flags.count(*it) || + notable_flags.subpackage_flags.count(*it)) + continue; + unused_flags->insert(*it); + } +} + +// 5) Output matches (and helpfer methods) + +static void OutputSingleGroupWithLimit( + const set &group, + const string &line_indentation, + const string &header, + const string &footer, + bool long_output_format, + int *remaining_line_limit, + int *completion_elements_output, + vector *completions) { + if (group.empty()) return; + if (!header.empty()) { + if (*remaining_line_limit < 2) return; + *remaining_line_limit -= 2; + completions->push_back(line_indentation + header); + completions->push_back(line_indentation + string(header.size(), '-')); + } + for (set::const_iterator it = group.begin(); + it != group.end() && *remaining_line_limit > 0; + ++it) { + --*remaining_line_limit; + ++*completion_elements_output; + completions->push_back( + (long_output_format + ? GetLongFlagLine(line_indentation, **it) + : GetShortFlagLine(line_indentation, **it))); + } + if (!footer.empty()) { + if (*remaining_line_limit < 1) return; + --*remaining_line_limit; + completions->push_back(line_indentation + footer); + } +} + +static string GetShortFlagLine( + const string &line_indentation, + const CommandLineFlagInfo &info) { + string prefix = + line_indentation + "--" + info.name + " [" + + (info.type == "string" ? + ("'" + info.default_value + "'") : + info.default_value) + + "] "; + int remainder = FLAGS_tab_completion_columns - prefix.size(); + string suffix = ""; + if (remainder > 0) + suffix = + (info.description.size() > remainder ? + (info.description.substr(0, remainder - 3) + "...").c_str() : + info.description.c_str()); + return prefix + suffix; +} + +static string GetLongFlagLine( + const string &line_indentation, + const CommandLineFlagInfo &info) { + + string output = DescribeOneFlag(info); + + // Replace '-' with '--', and remove trailing newline before appending + // the module definition location. + string old_flagname = "-" + info.name; + output.replace( + output.find(old_flagname), + old_flagname.size(), + "-" + old_flagname); + // Stick a newline and indentation in front of the type and default + // portions of DescribeOneFlag()s description + static const char kNewlineWithIndent[] = "\n "; + output.replace(output.find(" type:"), 1, string(kNewlineWithIndent)); + output.replace(output.find(" default:"), 1, string(kNewlineWithIndent)); + output = line_indentation + " Details for '--" + info.name + "':\n" + + output + " defined: " + info.filename; + + // Eliminate any doubled newlines that crept in. Specifically, if + // DescribeOneFlag() decided to break the line just before "type" + // or "default", we don't want to introduce an extra blank line + static const string line_of_spaces(FLAGS_tab_completion_columns, ' '); + static const char kDoubledNewlines[] = "\n \n"; + for (string::size_type newlines = output.find(kDoubledNewlines); + newlines != string::npos; + newlines = output.find(kDoubledNewlines)) + // Replace each 'doubled newline' with a single newline + output.replace(newlines, sizeof(kDoubledNewlines) - 1, string("\n")); + + for (string::size_type newline = output.find('\n'); + newline != string::npos; + newline = output.find('\n')) { + int newline_pos = newline % FLAGS_tab_completion_columns; + int missing_spaces = FLAGS_tab_completion_columns - newline_pos; + output.replace(newline, 1, line_of_spaces, 1, missing_spaces); + } + return output; +} +} // anonymous + +void HandleCommandLineCompletions(void) { + if (FLAGS_tab_completion_word.empty()) return; + PrintFlagCompletionInfo(); + exit(0); +} + +_END_GOOGLE_NAMESPACE_ diff --git a/src/gflags_completions.sh b/src/gflags_completions.sh new file mode 100755 index 0000000..81c9674 --- /dev/null +++ b/src/gflags_completions.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# Copyright (c) 2008, 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: Dave Nicponski +# +# This script is invoked by bash in response to a matching compspec. When +# this happens, bash calls this script using the command shown in the -C +# block of the complete entry, but also appends 3 arguments. They are: +# - The command being used for completion +# - The word being completed +# - The word preceding the completion word. +# +# Here's an example of how you might use this script: +# $ complete -o bashdefault -o default -o nospace -C \ +# '/usr/local/bin/gflags_completions.sh --tab_completion_columns $COLUMNS' \ +# time env binary_name another_binary [...] + +# completion_word_index gets the index of the (N-1)th argument for +# this command line. completion_word gets the actual argument from +# this command line at the (N-1)th position +completion_word_index="$(($# - 1))" +completion_word="${!completion_word_index}" + +# TODO(daven): Replace this once commandlineflags_completions.cc has +# a bool parameter indicating unambiguously to hijack the process for +# completion purposes. +if [ -z "$completion_word" ]; then + # Until an empty value for the completion word stops being misunderstood + # by google3 binaries, don't actuall execute the binary or the process + # won't be hijacked! + exit 0 +fi + +# binary_index gets the index of the command being completed (which bash +# places in the (N-2)nd position. binary gets the actual command from +# this command line at that (N-2)nd position +binary_index="$(($# - 2))" +binary="${!binary_index}" + +# For completions to be universal, we may have setup the compspec to +# trigger on 'harmless pass-through' commands, like 'time' or 'env'. +# If the command being completed is one of those two, we'll need to +# identify the actual command being executed. To do this, we need +# the actual command line that the was pressed on. Bash helpfully +# places this in the $COMP_LINE variable. +if [ "$binary" == "time" ] || [ "$binary" == "env" ]; then + # we'll assume that the first 'argument' is actually the + # binary to be run, if we think it looks like a google3 + # binary + + # TODO(daven): Decide what 'looks' like a google3 binary. =) + + # TODO(daven): This is not perfect - the 'env' command, for instance, + # is allowed to have options between the 'env' and 'the command to + # be executed'. For example, consider: + # $ env FOO="bar" bin/do_something --help + # In this case, we'll mistake the FOO="bar" portion as the binary. + # Perhaps we should continuing consuming leading words until we + # either run out of words, or find a word that is a valid file + # marked as executable. I can't think of any reason this wouldn't + # work. + + # Break up the 'original command line' (not this script's command line, + # rather the one the was pressed on) and find the second word. + parts=( ${COMP_LINE} ) + binary=${parts[1]} +fi + +# Build the command line to use for completion. Basically it involves +# passing through all the arguments given to this script (except the 3 +# that bash added), and appending a '--tab_completion_word "WORD"' to +# the arguments. +params="" +for ((i=1; i<=$(($# - 3)); ++i)); do + params="$params \"${!i}\""; +done +params="$params --tab_completion_word \"$completion_word\"" + +# TODO(daven): Perhaps stash the output in a temporary file somewhere +# in /tmp, and only cat it to stdout if the command returned a success +# code, to prevent false positives + +# If we think we have a reasonable command to execute, then execute it +# and hope for the best. +if [ -f "$binary" ] && [ -x "$binary" ]; then + eval "$binary 2>/dev/null $params" +fi + diff --git a/src/gflags_reporting.cc b/src/gflags_reporting.cc index e8099fb..835036c 100644 --- a/src/gflags_reporting.cc +++ b/src/gflags_reporting.cc @@ -56,6 +56,7 @@ #include #include #include "google/gflags.h" +#include "google/gflags_completions.h" #ifndef PATH_SEPARATOR #define PATH_SEPARATOR '/' @@ -345,6 +346,8 @@ void HandleCommandLineHelpFlags() { const char* progname = ProgramInvocationShortName(); extern void (*commandlineflags_exitfunc)(int); // in gflags.cc + HandleCommandLineCompletions(); + if (FLAGS_helpshort) { // show only flags related to this binary: // E.g. for fileutil.cc, want flags containing ... "/fileutil." cc diff --git a/src/gflags_unittest.cc b/src/gflags_unittest.cc index 03219c6..b322826 100644 --- a/src/gflags_unittest.cc +++ b/src/gflags_unittest.cc @@ -53,7 +53,7 @@ using std::string; DECLARE_string(tryfromenv); // in commandlineflags.cc DEFINE_string(test_tmpdir, "/tmp/gflags_unittest", "Dir we use for temp files"); -DEFINE_string(srcdir, google::StringFromEnv("SRCDIR", "."), +DEFINE_string(srcdir, GOOGLE_NAMESPACE::StringFromEnv("SRCDIR", "."), "Source-dir root, needed to find gflags_unittest_flagfile"); @@ -91,6 +91,20 @@ 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, ""); + +static int changeable_bool_var = 8008; +DEFINE_bool(changeable_bool_var, ++changeable_bool_var == 8009, ""); + +static int changeable_string_var = 0; +static string ChangeableString() { + char r[] = {'0' + ++changeable_string_var, '\0'}; + return r; +} +DEFINE_string(changeable_string_var, ChangeableString(), ""); + // These are never used in this unittest, but can be used by // commandlineflags_unittest.sh when it needs to specify flags // that are legal for commandlineflags_unittest but don't need to @@ -106,6 +120,10 @@ DEFINE_string(unused_string, "unused", ""); DEFINE_bool(changed_bool1, false, "changed"); DEFINE_bool(changed_bool2, false, "changed"); +static bool AlwaysFail(const char* flag, bool value) { return value == false; } +DEFINE_bool(always_fail, false, "will fail to validate when you set it"); +static const bool dummy = GOOGLE_NAMESPACE::RegisterFlagValidator(&FLAGS_always_fail, AlwaysFail); + _START_GOOGLE_NAMESPACE_ // The following is some bare-bones testing infrastructure @@ -204,6 +222,31 @@ static int RUN_ALL_TESTS() { } +// Defining a variable of type CompileAssertTypesEqual will cause a +// compiler error iff T1 and T2 are different types. +template +struct CompileAssertTypesEqual; + +template +struct CompileAssertTypesEqual { +}; + + +template +void AssertIsType(Actual& x) { + CompileAssertTypesEqual(); +} + +// Verify all the flags are the right type. +TEST(FlagTypes, FlagTypes) { + AssertIsType(FLAGS_test_bool); + AssertIsType(FLAGS_test_int32); + AssertIsType(FLAGS_test_int64); + AssertIsType(FLAGS_test_uint64); + AssertIsType(FLAGS_test_double); + AssertIsType(FLAGS_test_string); +} + // Death tests for "help" options. // // The help system automatically calls exit(1) when you specify any of @@ -532,6 +575,36 @@ TEST(SetFlagValueTest, IllegalValues) { } +// Tests that we only evaluate macro args once +TEST(MacroArgs, EvaluateOnce) { + EXPECT_EQ(13, FLAGS_changeable_var); + // Make sure we don't ++ the value somehow, when evaluating the flag. + EXPECT_EQ(13, FLAGS_changeable_var); + // Make sure the macro only evaluated this var once. + EXPECT_EQ(13, changeable_var); + // Make sure the actual value and default value are the same + SetCommandLineOptionWithMode("changeable_var", "21", SET_FLAG_IF_DEFAULT); + EXPECT_EQ(21, FLAGS_changeable_var); +} + +TEST(MacroArgs, EvaluateOnceBool) { + EXPECT_EQ(true, FLAGS_changeable_bool_var); + EXPECT_EQ(true, FLAGS_changeable_bool_var); + EXPECT_EQ(8009, changeable_bool_var); + SetCommandLineOptionWithMode("changeable_bool_var", "false", + SET_FLAG_IF_DEFAULT); + EXPECT_EQ(false, FLAGS_changeable_bool_var); +} + +TEST(MacroArgs, EvaluateOnceStrings) { + EXPECT_EQ("1", FLAGS_changeable_string_var); + EXPECT_EQ("1", FLAGS_changeable_string_var); + EXPECT_EQ(1, changeable_string_var); + SetCommandLineOptionWithMode("changeable_string_var", "different", + SET_FLAG_IF_DEFAULT); + EXPECT_EQ("different", FLAGS_changeable_string_var); +} + // Tests that the FooFromEnv does the right thing TEST(FromEnvTest, LegalValues) { setenv("BOOL_VAL1", "true", 1); @@ -856,6 +929,7 @@ TEST(GetCommandLineFlagInfoTest, FlagExists) { EXPECT_EQ("-1", info.current_value); EXPECT_EQ("-1", info.default_value); EXPECT_EQ(true, info.is_default); + EXPECT_EQ(false, info.has_validator_fn); FLAGS_test_bool = true; r = GetCommandLineFlagInfo("test_bool", &info); @@ -866,6 +940,7 @@ TEST(GetCommandLineFlagInfoTest, FlagExists) { EXPECT_EQ("true", info.current_value); EXPECT_EQ("false", info.default_value); EXPECT_EQ(false, info.is_default); + EXPECT_EQ(false, info.has_validator_fn); FLAGS_test_bool = false; r = GetCommandLineFlagInfo("test_bool", &info); @@ -876,6 +951,7 @@ TEST(GetCommandLineFlagInfoTest, FlagExists) { EXPECT_EQ("false", info.current_value); EXPECT_EQ("false", info.default_value); EXPECT_EQ(false, info.is_default); // value is same, but flag *was* modified + EXPECT_EQ(false, info.has_validator_fn); } TEST(GetCommandLineFlagInfoTest, FlagDoesNotExist) { @@ -887,6 +963,7 @@ TEST(GetCommandLineFlagInfoTest, FlagDoesNotExist) { info.default_value = "def"; info.filename = "/"; info.is_default = false; + info.has_validator_fn = true; bool r = GetCommandLineFlagInfo("test_int3210", &info); EXPECT_EQ(false, r); EXPECT_EQ("name", info.name); @@ -896,6 +973,7 @@ TEST(GetCommandLineFlagInfoTest, FlagDoesNotExist) { EXPECT_EQ("def", info.default_value); EXPECT_EQ("/", info.filename); EXPECT_EQ(false, info.is_default); + EXPECT_EQ(true, info.has_validator_fn); } TEST(GetCommandLineFlagInfoOrDieTest, FlagExistsAndIsDefault) { @@ -914,6 +992,7 @@ TEST(GetCommandLineFlagInfoOrDieTest, FlagExistsAndIsDefault) { EXPECT_EQ("false", info.current_value); EXPECT_EQ("false", info.default_value); EXPECT_EQ(true, info.is_default); + EXPECT_EQ(false, info.has_validator_fn); } TEST(GetCommandLineFlagInfoOrDieTest, FlagExistsAndWasAssigned) { @@ -934,6 +1013,7 @@ TEST(GetCommandLineFlagInfoOrDieTest, FlagExistsAndWasAssigned) { EXPECT_EQ("true", info.current_value); EXPECT_EQ("false", info.default_value); EXPECT_EQ(false, info.is_default); + EXPECT_EQ(false, info.has_validator_fn); } TEST(GetCommandLineFlagInfoOrDieTest, FlagDoesNotExist) { @@ -1146,6 +1226,203 @@ TEST(ParseCommandLineFlagsAndDashArgs, OneDashArg) { EXPECT_EQ(0, ParseTestFlag(false, GET_ARRAY_SIZE(argv) - 1, argv)); } +TEST(ParseCommandLineFlagsUnknownFlag, + FlagIsCompletelyUnknown) { + const char* argv[] = { + "my_test", + "--this_flag_does_not_exist", + NULL, + }; + + EXPECT_DEATH(ParseTestFlag(true, GET_ARRAY_SIZE(argv) - 1, argv), + "unknown command line flag.*"); + EXPECT_DEATH(ParseTestFlag(false, GET_ARRAY_SIZE(argv) - 1, argv), + "unknown command line flag.*"); +} + +TEST(ParseCommandLineFlagsUnknownFlag, + BoolFlagIsCompletelyUnknown) { + const char* argv[] = { + "my_test", + "--nothis_flag_does_not_exist", + NULL, + }; + + EXPECT_DEATH(ParseTestFlag(true, GET_ARRAY_SIZE(argv) - 1, argv), + "unknown command line flag.*"); + EXPECT_DEATH(ParseTestFlag(false, GET_ARRAY_SIZE(argv) - 1, argv), + "unknown command line flag.*"); +} + +TEST(ParseCommandLineFlagsUnknownFlag, + FlagIsNotABool) { + const char* argv[] = { + "my_test", + "--notest_string", + NULL, + }; + + EXPECT_DEATH(ParseTestFlag(true, GET_ARRAY_SIZE(argv) - 1, argv), + "boolean value .* specified for .* command line flag"); + EXPECT_DEATH(ParseTestFlag(false, GET_ARRAY_SIZE(argv) - 1, argv), + "boolean value .* specified for .* command line flag"); +} + +TEST(ParseCommandLineFlagsWrongFields, + DescriptionIsInvalid) { + // These must not be automatic variables, since command line flags + // aren't unregistered and gUnit uses FlagSaver to save and restore + // command line flags' values. If these are on the stack, then when + // later tests attempt to save and restore their values, the stack + // addresses of these variables will be overwritten... Stack smash! + static bool current_storage; + static bool defvalue_storage; + FlagRegisterer fr("flag_name", "bool", 0, "filename", + ¤t_storage, &defvalue_storage); + CommandLineFlagInfo fi; + EXPECT_TRUE(GetCommandLineFlagInfo("flag_name", &fi)); + EXPECT_EQ("", fi.description); +} + +static bool ValidateTestFlagIs5(const char* flagname, int32 flagval) { + if (flagval == 5) + return true; + printf("%s isn't 5!\n", flagname); + return false; +} + +static bool ValidateTestFlagIs10(const char* flagname, int32 flagval) { + return flagval == 10; +} + + +TEST(FlagsValidator, ValidFlagViaArgv) { + const char* argv[] = { + "my_test", + "--test_flag=5", + NULL, + }; + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + EXPECT_EQ(5, ParseTestFlag(true, GET_ARRAY_SIZE(argv) - 1, argv)); + // Undo the flag validator setting + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); +} + +TEST(FlagsValidator, ValidFlagViaSetDefault) { + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + // SetCommandLineOptionWithMode returns the empty string on error. + EXPECT_NE("", SetCommandLineOptionWithMode("test_flag", "5", + SET_FLAG_IF_DEFAULT)); + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); +} + +TEST(FlagsValidator, ValidFlagViaSetValue) { + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + FLAGS_test_flag = 100; // doesn't trigger the validator + // SetCommandLineOptionWithMode returns the empty string on error. + EXPECT_NE("", SetCommandLineOptionWithMode("test_flag", "5", + SET_FLAGS_VALUE)); + EXPECT_NE("", SetCommandLineOptionWithMode("test_flag", "5", + SET_FLAGS_DEFAULT)); + EXPECT_NE("", SetCommandLineOption("test_flag", "5")); + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); +} + +TEST(FlagsValidator, InvalidFlagViaArgv) { + const char* argv[] = { + "my_test", + "--test_flag=50", + NULL, + }; + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + EXPECT_DEATH(ParseTestFlag(true, GET_ARRAY_SIZE(argv) - 1, argv), + "ERROR: failed validation of new value '50' for flag 'test_flag'"); + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); +} + +TEST(FlagsValidator, InvalidFlagViaSetDefault) { + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + // SetCommandLineOptionWithMode returns the empty string on error. + EXPECT_EQ("", SetCommandLineOptionWithMode("test_flag", "50", + SET_FLAG_IF_DEFAULT)); + EXPECT_EQ(-1, FLAGS_test_flag); // the setting-to-50 should have failed + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); +} + +TEST(FlagsValidator, InvalidFlagViaSetValue) { + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + FLAGS_test_flag = 100; // doesn't trigger the validator + // SetCommandLineOptionWithMode returns the empty string on error. + EXPECT_EQ("", SetCommandLineOptionWithMode("test_flag", "50", + SET_FLAGS_VALUE)); + EXPECT_EQ("", SetCommandLineOptionWithMode("test_flag", "50", + SET_FLAGS_DEFAULT)); + EXPECT_EQ("", SetCommandLineOption("test_flag", "50")); + EXPECT_EQ(100, FLAGS_test_flag); // the setting-to-50 should have failed + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); +} + +TEST(FlagsValidator, InvalidFlagNeverSet) { + // If a flag keeps its default value, and that default value is + // invalid, we should die at argv-parse time. + const char* argv[] = { + "my_test", + NULL, + }; + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + EXPECT_DEATH(ParseTestFlag(true, GET_ARRAY_SIZE(argv) - 1, argv), + "ERROR: --test_flag must be set on the commandline"); +} + +TEST(FlagsValidator, InvalidFlagPtr) { + int32 dummy; + EXPECT_FALSE(RegisterFlagValidator(NULL, &ValidateTestFlagIs5)); + EXPECT_FALSE(RegisterFlagValidator(&dummy, &ValidateTestFlagIs5)); +} + +TEST(FlagsValidator, RegisterValidatorTwice) { + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + EXPECT_FALSE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs10)); + EXPECT_FALSE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs10)); + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs10)); + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); +} + +TEST(FlagsValidator, CommandLineFlagInfo) { + CommandLineFlagInfo info; + info = GetCommandLineFlagInfoOrDie("test_flag"); + EXPECT_FALSE(info.has_validator_fn); + + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + info = GetCommandLineFlagInfoOrDie("test_flag"); + EXPECT_TRUE(info.has_validator_fn); + + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); + info = GetCommandLineFlagInfoOrDie("test_flag"); + EXPECT_FALSE(info.has_validator_fn); +} + +TEST(FlagsValidator, FlagSaver) { + { + FlagSaver fs; + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + EXPECT_EQ("", SetCommandLineOption("test_flag", "50")); // fails validation + } + EXPECT_NE("", SetCommandLineOption("test_flag", "50")); // validator is gone + + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, &ValidateTestFlagIs5)); + { + FlagSaver fs; + EXPECT_TRUE(RegisterFlagValidator(&FLAGS_test_flag, NULL)); + EXPECT_NE("", SetCommandLineOption("test_flag", "50")); // no validator + } + EXPECT_EQ("", SetCommandLineOption("test_flag", "50")); // validator is back +} + + static int Main(int argc, char **argv) { // We need to call SetArgv before InitGoogle, so our "test" argv will // win out over this executable's real argv. That makes running this diff --git a/src/gflags_unittest.sh b/src/gflags_unittest.sh index cb214b8..b14064c 100755 --- a/src/gflags_unittest.sh +++ b/src/gflags_unittest.sh @@ -150,8 +150,8 @@ Expect $LINENO 1 "/gflags_unittest.cc" "/gflags.cc" \ --helpon gflags_unittest # helpmatch is like helpon but takes substrings -Expect $LINENO 1 "/gflags_unittest.cc" "/gflags.cc" \ - -helpmatch _ +Expect $LINENO 1 "/gflags_reporting.cc" "/gflags_unittest.cc" \ + -helpmatch reporting Expect $LINENO 1 "/gflags_unittest.cc" "/gflags.cc" \ -helpmatch=unittest @@ -208,12 +208,15 @@ Expect $LINENO 0 "gflags_unittest" "gflags_unittest.cc" \ 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." +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 + echo "PASS" exit 0 diff --git a/src/google/gflags.h.in b/src/google/gflags.h.in index 8d3921d..b6f4e69 100644 --- a/src/google/gflags.h.in +++ b/src/google/gflags.h.in @@ -69,8 +69,8 @@ // other thread is writing to the variable or calling non-const // methods of this class. -#ifndef BASE_COMMANDLINEFLAGS_H__ -#define BASE_COMMANDLINEFLAGS_H__ +#ifndef GOOGLE_GFLAGS_H_ +#define GOOGLE_GFLAGS_H_ #include #include @@ -113,6 +113,45 @@ typedef __uint64 uint64; #endif // -------------------------------------------------------------------- +// To actually define a flag in a file, use DEFINE_bool, +// DEFINE_string, etc. at the bottom of this file. You may also find +// it useful to register a validator with the flag. This ensures that +// when the flag is parsed from the commandline, or is later set via +// SetCommandLineOption, we call the validation function. The +// validation function should return true if the flag value is valid, +// and false otherwise. +// +// This function is safe to call at global construct time (as in the +// example below). +// +// Example use: +// static bool ValidatePort(const char* flagname, int32 value) { +// if (value > 0 && value < 32768) // value is ok +// return true; +// printf("Invalid value for --%s: %d\n", flagname, (int)value); +// return false; +// } +// DEFINE_int32(port, 0, "What port to listen on"); +// static bool dummy = RegisterFlagValidator(&FLAGS_port, &ValidatePort); + +// Returns true if successfully registered, false if not (because the +// first argument doesn't point to a command-line flag, or because a +// validator is already registered for this flag). +bool RegisterFlagValidator(const bool* flag, + bool (*validate_fn)(const char*, bool)); +bool RegisterFlagValidator(const int32* flag, + bool (*validate_fn)(const char*, int32)); +bool RegisterFlagValidator(const int64* flag, + bool (*validate_fn)(const char*, int64)); +bool RegisterFlagValidator(const uint64* flag, + bool (*validate_fn)(const char*, uint64)); +bool RegisterFlagValidator(const double* flag, + bool (*validate_fn)(const char*, double)); +bool RegisterFlagValidator(const std::string* flag, + bool (*validate_fn)(const char*, const std::string&)); + + +// -------------------------------------------------------------------- // These methods are the best way to get access to info about the // list of commandline flags. Note that these routines are pretty slow. // GetAllFlags: mostly-complete info about the list, sorted by file. @@ -130,6 +169,7 @@ struct CommandLineFlagInfo { std::string current_value; // the current value, as a string std::string default_value; // the default value, as a string std::string filename; // 'cleaned' version of filename holding the flag + bool has_validator_fn; // true if RegisterFlagValidator called on flag bool is_default; // true if the flag has default value }; @@ -206,10 +246,11 @@ extern std::string SetCommandLineOptionWithMode(const char* name, const char* va // -------------------------------------------------------------------- // Saves the states (value, default value, whether the user has set -// the flag, etc) of all flags, and restores them when the FlagSaver -// is destroyed. This is very useful in tests, say, when you want to -// let your tests change the flags, but make sure that they get -// reverted to the original states when your test is complete. +// the flag, registered validators, etc) of all flags, and restores +// them when the FlagSaver is destroyed. This is very useful in +// tests, say, when you want to let your tests change the flags, but +// make sure that they get reverted to the original states when your +// test is complete. // // Example usage: // void TestFoo() { @@ -327,27 +368,6 @@ extern void AllowCommandLineReparsing(); extern uint32 ReparseCommandLineNonHelpFlags(); -// The following code is added to check if proper value types are passed to -// flags. Specially for boolean flags. Since almost anything can be implicitly -// casted to boolean many copy-paste type of errors got through and they are -// there in code now. As of now, flags_safe_cast is written such a way that -// it raises only warning for type mismatches. -// -// TODO(who?): This needs to be changed to give compilation error if type -// does not match. -extern void FlagsTypeWarn(const char *name); - -template -inline bool flags_safe_bool(From from, const char *name) { - FlagsTypeWarn(name); - return from; -} - -inline bool flags_safe_bool(bool from, const char *name) { - return from; -} - - // -------------------------------------------------------------------- // Now come the command line flag declaration/definition macros that // will actually be used. They're kind of hairy. A major reason @@ -390,16 +410,9 @@ class FlagRegisterer { FlagRegisterer(const char* name, const char* type, const char* help, const char* filename, void* current_storage, void* defvalue_storage); - private: - class CommandLineFlag* flag_; }; -// namespc should be 'std::', and type 'string', for a var of type 'std::string' -#define DECLARE_VARIABLE(namespc, type, shorttype, name) \ - namespace fL##shorttype { \ - extern namespc type& FLAGS_##name; \ - } \ - using fL##shorttype::FLAGS_##name +#ifndef SWIG // In swig, ignore the main flag declarations // If your application #defines STRIP_FLAG_HELP to a non-zero value // before #including this file, we remove the help message from the @@ -415,49 +428,92 @@ extern const char kStrippedFlagHelp[]; #define MAYBE_STRIPPED_HELP(txt) txt #endif -// Each command-line flag defines an internal array of two elements -// of the appropriate time (each element is actually a union to get -// the values to be aligned on larger-than-byte boundaries). Element -// 0 of the s_##name array holds the current value, and element 1 -// holds the default value. -#define DEFINE_VARIABLE(namespc, type, shorttype, name, value, help) \ - namespace fL##shorttype { \ - static union { void* align; char store[sizeof(namespc type)]; } \ - s_##name[2]; \ - static @ac_google_namespace@::FlagRegisterer o_##name( \ - #name, #type, MAYBE_STRIPPED_HELP(help), __FILE__, \ - new (s_##name[0].store) namespc type(value), \ - new (s_##name[1].store) namespc type(value)); \ - namespc type& FLAGS_##name = \ - *(reinterpret_cast(s_##name[0].store)); \ - char FLAGS_no##name @ac_cv___attribute__unused@; \ - } \ +// Each command-line flag has two variables associated with it: one +// with the current value, and one with the default value. However, +// we have a third variable, which is where value is assigned; it's a +// constant. This guarantees that FLAG_##value is initialized at +// static initialization time (e.g. before program-start) rather than +// than global construction time (which is after program-start but +// before main), at least when 'value' is a compile-time constant. We +// use a small trick for the "default value" variable, and call it +// FLAGS_no. This serves the second purpose of assuring a +// compile error if someone tries to define a flag named no +// which is illegal (--foo and --nofoo both affect the "foo" flag). +#define DEFINE_VARIABLE(type, shorttype, name, value, help) \ + namespace fL##shorttype { \ + static const type FLAGS_nono##name = value; \ + type FLAGS_##name = FLAGS_nono##name; \ + type FLAGS_no##name = FLAGS_nono##name; \ + static @ac_google_namespace@::FlagRegisterer o_##name( \ + #name, #type, MAYBE_STRIPPED_HELP(help), __FILE__, \ + &FLAGS_##name, &FLAGS_no##name); \ + } \ using fL##shorttype::FLAGS_##name +#define DECLARE_VARIABLE(type, shorttype, name) \ + namespace fL##shorttype { \ + extern type FLAGS_##name; \ + } \ + using fL##shorttype::FLAGS_##name -#ifndef SWIG // In swig, ignore the main flag declarations - -#define DECLARE_bool(name) DECLARE_VARIABLE(, bool, B, name) -#define DEFINE_bool(name, val, txt) \ - DEFINE_VARIABLE(, bool, B, name, @ac_google_namespace@::flags_safe_bool(val, #name), 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) - -#define DECLARE_int64(name) DECLARE_VARIABLE(@ac_google_namespace@::, int64,I64, name) -#define DEFINE_int64(name, val,txt) DEFINE_VARIABLE(@ac_google_namespace@::, int64,I64, name,val,txt) - -#define DECLARE_uint64(name) DECLARE_VARIABLE(@ac_google_namespace@::, uint64,U64, name) -#define DEFINE_uint64(name, val,txt) DEFINE_VARIABLE(@ac_google_namespace@::, uint64,U64,name,val,txt) - -#define DECLARE_double(name) DECLARE_VARIABLE(, double,D, name) -#define DEFINE_double(name, val,txt) DEFINE_VARIABLE(, double,D, name,val,txt) - -#define DECLARE_string(name) DECLARE_VARIABLE(std::, string,S, name) -#define DEFINE_string(name, val,txt) DEFINE_VARIABLE(std::, string,S, name,val,txt) +// For boolean flags, 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. +namespace fLB { +template double IsBoolFlag(const From& from); +bool IsBoolFlag(bool from); +} +extern bool FlagsTypeWarn(const char *name); + +#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 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) + +#define DECLARE_int64(name) DECLARE_VARIABLE(@ac_google_namespace@::int64,I64, name) +#define DEFINE_int64(name,val,txt) DEFINE_VARIABLE(@ac_google_namespace@::int64,I64, name, val, txt) + +#define DECLARE_uint64(name) DECLARE_VARIABLE(@ac_google_namespace@::uint64,U64, name) +#define DEFINE_uint64(name,val,txt) DEFINE_VARIABLE(@ac_google_namespace@::uint64,U64, name, val, txt) + +#define DECLARE_double(name) DECLARE_VARIABLE(double,D, name) +#define DEFINE_double(name,val,txt) DEFINE_VARIABLE(double,D, name, val, txt) + +// Strings are trickier, because they're not a POD, so we can't +// construct them at static-initialization time (instead they get +// constructed at global-constructor time, which is much later). To +// try to avoid crashes in that case, we use a char buffer to store +// the string, which we can static-initialize, and then placement-new +// into it later. It's not perfect, but the best we can do. +#define DECLARE_string(name) namespace fLS { extern string& FLAGS_##name; } \ + using fLS::FLAGS_##name + +// We need to define a var named FLAGS_no##name so people don't define +// --string and --nostring. And we need a temporary place to put val +// so we don't have to evaluate it twice. Two great needs that go +// great together! +#define DEFINE_string(name, val, txt) \ + namespace fLS { \ + static union { void* align; char s[sizeof(std::string)]; } s_##name[2]; \ + const string* const FLAGS_no##name = new (s_##name[0].s) std::string(val); \ + static @ac_google_namespace@::FlagRegisterer o_##name( \ + #name, "string", MAYBE_STRIPPED_HELP(txt), __FILE__, \ + s_##name[0].s, new (s_##name[1].s) std::string(*FLAGS_no##name)); \ + std::string& FLAGS_##name = *(reinterpret_cast(s_##name[0].s)); \ + } \ + using fLS::FLAGS_##name #endif // SWIG @ac_google_end_namespace@ -#endif // BASE_COMMANDLINEFLAGS_H__ +#endif // GOOGLE_GFLAGS_H_ diff --git a/src/google/gflags_completions.h.in b/src/google/gflags_completions.h.in new file mode 100644 index 0000000..b4ca6a8 --- /dev/null +++ b/src/google/gflags_completions.h.in @@ -0,0 +1,121 @@ +// Copyright (c) 2008, 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: Dave Nicponski +// +// Implement helpful bash-style command line flag completions +// +// ** Functional API: +// HandleCommandLineCompletions() should be called early during +// program startup, but after command line flag code has been +// initialized, such as the beginning of HandleCommandLineHelpFlags(). +// It checks the value of the flag --tab_completion_word. If this +// flag is empty, nothing happens here. If it contains a string, +// however, then HandleCommandLineCompletions() will hijack the +// process, attempting to identify the intention behind this +// completion. Regardless of the outcome of this deduction, the +// process will be terminated, similar to --helpshort flag +// handling. +// +// ** Overview of Bash completions: +// Bash can be told to programatically determine completions for the +// current 'cursor word'. It does this by (in this case) invoking a +// command with some additional arguments identifying the command +// being executed, the word being completed, and the previous word +// (if any). Bash then expects a sequence of output lines to be +// printed to stdout. If these lines all contain a common prefix +// longer than the cursor word, bash will replace the cursor word +// with that common prefix, and display nothing. If there isn't such +// a common prefix, bash will display the lines in pages using 'more'. +// +// ** Strategy taken for command line completions: +// If we can deduce either the exact flag intended, or a common flag +// prefix, we'll output exactly that. Otherwise, if information +// must be displayed to the user, we'll take the opportunity to add +// some helpful information beyond just the flag name (specifically, +// we'll include the default flag value and as much of the flag's +// description as can fit on a single terminal line width, as specified +// by the flag --tab_completion_columns). Furthermore, we'll try to +// make bash order the output such that the most useful or relevent +// flags are the most likely to be shown at the top. +// +// ** Additional features: +// To assist in finding that one really useful flag, substring matching +// was implemented. Before pressing a to get completion for the +// current word, you can append one or more '?' to the flag to do +// substring matching. Here's the semantics: +// --foo Show me all flags with names prefixed by 'foo' +// --foo? Show me all flags with 'foo' somewhere in the name +// --foo?? Same as prior case, but also search in module +// definition path for 'foo' +// --foo??? Same as prior case, but also search in flag +// descriptions for 'foo' +// Finally, we'll trim the output to a relatively small number of +// flags to keep bash quiet about the verbosity of output. If one +// really wanted to see all possible matches, appending a '+' to the +// search word will force the exhaustive list of matches to be printed. +// +// ** How to have bash accept completions from a binary: +// Bash requires that it be informed about each command that programmatic +// completion should be enabled for. Example addition to a .bashrc +// file would be (your path to gflags_completions.sh file may differ): + +/* +$ complete -o bashdefault -o default -o nospace -C \ + '/usr/local/bin/gflags_completions.sh --tab_completion_columns $COLUMNS' \ + time env binary_name another_binary [...] +*/ + +// This would allow the following to work: +// $ /path/to/binary_name --vmodule +// Or: +// $ ./bin/path/another_binary --gfs_u +// (etc) +// +// Sadly, it appears that bash gives no easy way to force this behavior for +// all commands. That's where the "time" in the above example comes in. +// If you haven't specifically added a command to the list of completion +// supported commands, you can still get completions by prefixing the +// entire command with "env". +// $ env /some/brand/new/binary --vmod +// Assuming that "binary" is a newly compiled binary, this should still +// produce the expected completion output. + + +#ifndef GOOGLE_GFLAGS_COMPLETIONS_H_ +#define GOOGLE_GFLAGS_COMPLETIONS_H_ + +@ac_google_start_namespace@ + +void HandleCommandLineCompletions(void); + +@ac_google_end_namespace@ + +#endif // GOOGLE_GFLAGS_COMPLETIONS_H_ -- 2.7.4