You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by be...@apache.org on 2013/05/29 19:40:49 UTC

[11/35] git commit: Moved flags to stout.

Moved flags to stout.

Review: https://reviews.apache.org/r/11465


Project: http://git-wip-us.apache.org/repos/asf/incubator-mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mesos/commit/190a3bb8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mesos/tree/190a3bb8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mesos/diff/190a3bb8

Branch: refs/heads/master
Commit: 190a3bb80656ffdd435561d8e8f0f638bdcd7d6e
Parents: f122e61
Author: Benjamin Hindman <be...@twitter.com>
Authored: Tue May 21 10:55:30 2013 -0700
Committer: Benjamin Hindman <be...@twitter.com>
Committed: Tue May 28 14:18:39 2013 -0700

----------------------------------------------------------------------
 third_party/libprocess/third_party/Makefile.am     |    1 +
 .../libprocess/third_party/stout/Makefile.am       |    6 +
 .../third_party/stout/include/stout/flags.hpp      |   70 +++
 .../third_party/stout/include/stout/flags/flag.hpp |   26 +
 .../stout/include/stout/flags/flags.hpp            |  481 +++++++++++++++
 .../stout/include/stout/flags/loader.hpp           |  109 ++++
 .../stout/include/stout/flags/parse.hpp            |   55 ++
 .../third_party/stout/include/stout/os.hpp         |   22 +
 .../third_party/stout/tests/flags_tests.cpp        |  349 +++++++++++
 9 files changed, 1119 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-mesos/blob/190a3bb8/third_party/libprocess/third_party/Makefile.am
----------------------------------------------------------------------
diff --git a/third_party/libprocess/third_party/Makefile.am b/third_party/libprocess/third_party/Makefile.am
index 812b7c3..2f0567b 100644
--- a/third_party/libprocess/third_party/Makefile.am
+++ b/third_party/libprocess/third_party/Makefile.am
@@ -119,6 +119,7 @@ stout_tests_SOURCES =				\
   $(STOUT)/tests/bytes_tests.cpp		\
   $(STOUT)/tests/duration_tests.cpp		\
   $(STOUT)/tests/error_tests.cpp		\
+  $(STOUT)/tests/flags_tests.cpp		\
   $(STOUT)/tests/gzip_tests.cpp			\
   $(STOUT)/tests/hashset_tests.cpp		\
   $(STOUT)/tests/json_tests.cpp			\

http://git-wip-us.apache.org/repos/asf/incubator-mesos/blob/190a3bb8/third_party/libprocess/third_party/stout/Makefile.am
----------------------------------------------------------------------
diff --git a/third_party/libprocess/third_party/stout/Makefile.am b/third_party/libprocess/third_party/stout/Makefile.am
index 4af647e..fdd3482 100644
--- a/third_party/libprocess/third_party/stout/Makefile.am
+++ b/third_party/libprocess/third_party/stout/Makefile.am
@@ -11,6 +11,11 @@ EXTRA_DIST =					\
   include/stout/error.hpp			\
   include/stout/exit.hpp			\
   include/stout/fatal.hpp			\
+  include/stout/flags.hpp			\
+  include/stout/flags/flag.hpp			\
+  include/stout/flags/flags.hpp			\
+  include/stout/flags/loader.hpp		\
+  include/stout/flags/parse.hpp			\
   include/stout/foreach.hpp			\
   include/stout/format.hpp			\
   include/stout/fs.hpp				\
@@ -43,6 +48,7 @@ EXTRA_DIST =					\
   tests/bytes_tests.cpp				\
   tests/duration_tests.cpp			\
   tests/error_tests.cpp				\
+  tests/flags_tests.cpp				\
   tests/gzip_tests.cpp				\
   tests/hashset_tests.cpp			\
   tests/json_tests.cpp				\

http://git-wip-us.apache.org/repos/asf/incubator-mesos/blob/190a3bb8/third_party/libprocess/third_party/stout/include/stout/flags.hpp
----------------------------------------------------------------------
diff --git a/third_party/libprocess/third_party/stout/include/stout/flags.hpp b/third_party/libprocess/third_party/stout/include/stout/flags.hpp
new file mode 100644
index 0000000..0efd079
--- /dev/null
+++ b/third_party/libprocess/third_party/stout/include/stout/flags.hpp
@@ -0,0 +1,70 @@
+#ifndef __STOUT_FLAGS_HPP__
+#define __STOUT_FLAGS_HPP__
+
+#include <stout/flags/flags.hpp>
+
+// An abstraction for application/library "flags". An example is
+// probably best:
+//  -------------------------------------------------------------
+// class MyFlags : public virtual FlagsBase // Use 'virtual' for composition!
+// {
+// public:
+//   Flags()
+//   {
+//     add(&debug,
+//         "debug",
+//         "Help string for debug",
+//         false);
+//
+//     add(&name,
+//         "name",
+//         "Help string for name");
+//   }
+
+//   bool debug;
+//   Option<string> name;
+// };
+//
+// ...
+//
+// map<string, Option<string> > values;
+// values["no-debug"] = None();                       // --no-debug
+// values["debug"] = None();                          // --debug
+// values["debug"] = Option<string>::some("true");    // --debug=true
+// values["debug"] = Option<string>::some("false");   // --debug=false
+// values["name"] = Option<string>::some("frank");    // --name=frank
+//
+// MyFlags flags;
+// flags.load(values);
+// flags.name.isSome() ...
+// flags.debug ...
+//  -------------------------------------------------------------
+//
+// You can also compose flags provided that each has used "virtual
+// inheritance":
+//  -------------------------------------------------------------
+// Flags<MyFlags1, MyFlags2> flags;
+// flags.add(...); // Any other flags you want to throw in there.
+// flags.load(values);
+// flags.flag_from_myflags1 ...
+// flags.flag_from_myflags2 ...
+//  -------------------------------------------------------------
+//
+// "Fail early, fail often":
+//
+// You can not add duplicate flags, this is checked for you at compile
+// time for composite flags (e.g., Flag<MyFlags1, MyFlags2>) and also
+// checked at runtime for any other flags added via inheritance or
+// Flags::add(...).
+//
+// Flags that can not be loaded (e.g., attempting to use the 'no-'
+// prefix for a flag that is not boolean) will print a message to
+// standard error and abort the process.
+
+// TODO(benh): Provide a boolean which specifies whether or not to
+// abort on duplicates or load errors.
+
+// TODO(benh): Make prefix for environment variables configurable
+// (e.g., "MESOS_").
+
+#endif // __STOUT_FLAGS_HPP__

http://git-wip-us.apache.org/repos/asf/incubator-mesos/blob/190a3bb8/third_party/libprocess/third_party/stout/include/stout/flags/flag.hpp
----------------------------------------------------------------------
diff --git a/third_party/libprocess/third_party/stout/include/stout/flags/flag.hpp b/third_party/libprocess/third_party/stout/include/stout/flags/flag.hpp
new file mode 100644
index 0000000..d31c984
--- /dev/null
+++ b/third_party/libprocess/third_party/stout/include/stout/flags/flag.hpp
@@ -0,0 +1,26 @@
+#ifndef __STOUT_FLAGS_FLAG_HPP__
+#define __STOUT_FLAGS_FLAG_HPP__
+
+#include <string>
+
+#include <tr1/functional>
+
+#include <stout/nothing.hpp>
+#include <stout/try.hpp>
+
+namespace flags {
+
+// Forward declaration.
+class FlagsBase;
+
+struct Flag
+{
+  std::string name;
+  std::string help;
+  bool boolean;
+  std::tr1::function<Try<Nothing>(FlagsBase*, const std::string&)> loader;
+};
+
+} // namespace flags {
+
+#endif // __STOUT_FLAGS_FLAG_HPP__

http://git-wip-us.apache.org/repos/asf/incubator-mesos/blob/190a3bb8/third_party/libprocess/third_party/stout/include/stout/flags/flags.hpp
----------------------------------------------------------------------
diff --git a/third_party/libprocess/third_party/stout/include/stout/flags/flags.hpp b/third_party/libprocess/third_party/stout/include/stout/flags/flags.hpp
new file mode 100644
index 0000000..77d36e6
--- /dev/null
+++ b/third_party/libprocess/third_party/stout/include/stout/flags/flags.hpp
@@ -0,0 +1,481 @@
+#ifndef __STOUT_FLAGS_FLAGS_HPP__
+#define __STOUT_FLAGS_FLAGS_HPP__
+
+#include <stdlib.h> // For abort.
+
+#include <map>
+#include <string>
+#include <typeinfo> // For typeid.
+
+#include <tr1/functional>
+
+#include <stout/error.hpp>
+#include <stout/exit.hpp>
+#include <stout/foreach.hpp>
+#include <stout/none.hpp>
+#include <stout/nothing.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/stringify.hpp>
+#include <stout/strings.hpp>
+#include <stout/try.hpp>
+
+#include <stout/flags/flag.hpp>
+#include <stout/flags/loader.hpp>
+#include <stout/flags/parse.hpp>
+
+namespace flags {
+
+class FlagsBase
+{
+public:
+  virtual ~FlagsBase() {}
+
+  // Load any flags from the environment given the variable prefix,
+  // i.e., given prefix 'STOUT_' will load a flag named 'foo' via
+  // environment variables 'STOUT_foo' or 'STOUT_FOO'.
+  virtual Try<Nothing> load(
+      const std::string& prefix,
+      bool unknowns = false);
+
+  // Load any flags from the environment given the variable prefix
+  // (see above) followed by loading from the command line (via 'argc'
+  // and 'argv'). If 'unknowns' is true then we'll ignore unknown
+  // flags we see while loading. If 'duplicates' is true then we'll
+  // ignore any duplicates we see while loading.
+  virtual Try<Nothing> load(
+      const Option<std::string>& prefix,
+      int argc,
+      char** argv,
+      bool unknowns = false,
+      bool duplicates = false);
+
+  Try<Nothing> load(
+      const std::string& prefix,
+      int argc,
+      char** argv,
+      bool unknowns = false,
+      bool duplicates = false);
+
+  virtual Try<Nothing> load(
+      const std::map<std::string, Option<std::string> >& values,
+      bool unknowns = false);
+
+  virtual Try<Nothing> load(
+      const std::map<std::string, std::string>& values,
+      bool unknowns = false);
+
+  // Returns a string describing the flags.
+  std::string usage() const;
+
+  typedef std::map<std::string, Flag>::const_iterator const_iterator;
+
+  const_iterator begin() const { return flags.begin(); }
+  const_iterator end() const { return flags.end(); }
+
+  template <typename T1, typename T2>
+  void add(T1* t1,
+           const std::string& name,
+           const std::string& help,
+           const T2& t2);
+
+  template <typename T>
+  void add(Option<T>* option,
+           const std::string& name,
+           const std::string& help);
+
+protected:
+  template <typename Flags, typename T1, typename T2>
+  void add(T1 Flags::*t1,
+           const std::string& name,
+           const std::string& help,
+           const T2& t2);
+
+  template <typename Flags, typename T>
+  void add(Option<T> Flags::*option,
+           const std::string& name,
+           const std::string& help);
+
+  void add(const Flag& flag);
+
+private:
+  std::map<std::string, Flag> flags;
+};
+
+
+// Need to declare/define some explicit subclasses of FlagsBase so
+// that we can overload the 'Flags::operator FlagsN () const'
+// functions for each possible type.
+class _Flags1 : public virtual FlagsBase {};
+class _Flags2 : public virtual FlagsBase {};
+class _Flags3 : public virtual FlagsBase {};
+class _Flags4 : public virtual FlagsBase {};
+class _Flags5 : public virtual FlagsBase {};
+
+
+// TODO(benh): Add some "type constraints" for template paramters to
+// make sure they are all of type FlagsBase.
+template <typename Flags1 = _Flags1,
+          typename Flags2 = _Flags2,
+          typename Flags3 = _Flags3,
+          typename Flags4 = _Flags4,
+          typename Flags5 = _Flags5>
+class Flags : public virtual Flags1,
+              public virtual Flags2,
+              public virtual Flags3,
+              public virtual Flags4,
+              public virtual Flags5 {};
+
+
+template <typename T1, typename T2>
+void FlagsBase::add(
+    T1* t1,
+    const std::string& name,
+    const std::string& help,
+    const T2& t2)
+{
+  *t1 = t2; // Set the default.
+
+  Flag flag;
+  flag.name = name;
+  flag.help = help;
+  flag.boolean = typeid(T1) == typeid(bool);
+  flag.loader = std::tr1::bind(
+      &Loader<T1>::load,
+      t1,
+      std::tr1::function<Try<T1>(const std::string&)>(
+          std::tr1::bind(&parse<T1>, std::tr1::placeholders::_1)),
+      name,
+      std::tr1::placeholders::_2); // Use _2 because ignore FlagsBase*.
+
+  // Update the help string to include the default value.
+  flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1
+    ? " (default: " // On same line, add space.
+    : "(default: "; // On newline.
+  flag.help += stringify(t2);
+  flag.help += ")";
+
+  FlagsBase::add(flag);
+}
+
+
+template <typename T>
+void FlagsBase::add(
+    Option<T>* option,
+    const std::string& name,
+    const std::string& help)
+{
+  Flag flag;
+  flag.name = name;
+  flag.help = help;
+  flag.boolean = typeid(T) == typeid(bool);
+  flag.loader = std::tr1::bind(
+      &OptionLoader<T>::load,
+      option,
+      std::tr1::function<Try<T>(const std::string&)>(
+          std::tr1::bind(&parse<T>, std::tr1::placeholders::_1)),
+      name,
+      std::tr1::placeholders::_2); // Use _2 because ignore FlagsBase*.
+
+  FlagsBase::add(flag);
+}
+
+
+template <typename Flags, typename T1, typename T2>
+void FlagsBase::add(
+    T1 Flags::*t1,
+    const std::string& name,
+    const std::string& help,
+    const T2& t2)
+{
+  Flags* flags = dynamic_cast<Flags*>(this);
+  if (flags == NULL) {
+    std::cerr << "Attempted to add flag '" << name
+              << "' with incompatible type" << std::endl;
+    abort();
+  } else {
+    flags->*t1 = t2; // Set the default.
+  }
+
+  Flag flag;
+  flag.name = name;
+  flag.help = help;
+  flag.boolean = typeid(T1) == typeid(bool);
+  flag.loader = std::tr1::bind(
+      &MemberLoader<Flags, T1>::load,
+      std::tr1::placeholders::_1,
+      t1,
+      std::tr1::function<Try<T1>(const std::string&)>(
+          std::tr1::bind(&parse<T1>, std::tr1::placeholders::_1)),
+      name,
+      std::tr1::placeholders::_2);
+
+  // Update the help string to include the default value.
+  flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1
+    ? " (default: " // On same line, add space.
+    : "(default: "; // On newline.
+  flag.help += stringify(t2);
+  flag.help += ")";
+
+  add(flag);
+}
+
+
+template <typename Flags, typename T>
+void FlagsBase::add(
+    Option<T> Flags::*option,
+    const std::string& name,
+    const std::string& help)
+{
+  Flags* flags = dynamic_cast<Flags*>(this);
+  if (flags == NULL) {
+    std::cerr << "Attempted to add flag '" << name
+              << "' with incompatible type" << std::endl;
+    abort();
+  }
+
+  Flag flag;
+  flag.name = name;
+  flag.help = help;
+  flag.boolean = typeid(T) == typeid(bool);
+  flag.loader = std::tr1::bind(
+      &OptionMemberLoader<Flags, T>::load,
+      std::tr1::placeholders::_1,
+      option,
+      std::tr1::function<Try<T>(const std::string&)>(
+          std::tr1::bind(&parse<T>, std::tr1::placeholders::_1)),
+      name,
+      std::tr1::placeholders::_2);
+
+  add(flag);
+}
+
+
+inline void FlagsBase::add(const Flag& flag)
+{
+  if (flags.count(flag.name) > 0) {
+    EXIT(1) << "Attempted to add duplicate flag '" << flag.name << "'";
+  } else if (flag.name.find("no-") == 0) {
+    EXIT(1) << "Attempted to add flag '" << flag.name
+            << "' that starts with the reserved 'no-' prefix";
+  }
+
+  flags[flag.name] = flag;
+}
+
+
+// Extract environment variable "flags" with the specified prefix.
+inline std::map<std::string, Option<std::string> > extract(
+    const std::string& prefix)
+{
+  char** environ = os::environ();
+
+  std::map<std::string, Option<std::string> > values;
+
+  for (int i = 0; environ[i] != NULL; i++) {
+    std::string variable = environ[i];
+    if (variable.find(prefix) == 0) {
+      size_t eq = variable.find_first_of("=");
+      if (eq == std::string::npos) {
+        continue; // Not expecting a missing '=', but ignore anyway.
+      }
+      std::string name = variable.substr(prefix.size(), eq - prefix.size());
+      name = strings::lower(name); // Allow PREFIX_NAME or PREFIX_name.
+      std::string value = variable.substr(eq + 1);
+      values[name] = Option<std::string>::some(value);
+    }
+  }
+
+  return values;
+}
+
+
+inline Try<Nothing> FlagsBase::load(
+    const std::string& prefix,
+    bool unknowns)
+{
+  return load(extract(prefix), unknowns);
+}
+
+
+inline Try<Nothing> FlagsBase::load(
+    const Option<std::string>& prefix,
+    int argc,
+    char** argv,
+    bool unknowns,
+    bool duplicates)
+{
+  std::map<std::string, Option<std::string> > values;
+
+  if (prefix.isSome()) {
+    values = extract(prefix.get());
+  }
+
+  // Read flags from the command line.
+  for (int i = 1; i < argc; i++) {
+    const std::string arg(argv[i]);
+
+    std::string name;
+    Option<std::string> value = None();
+    if (arg.find("--") == 0) {
+      size_t eq = arg.find_first_of("=");
+      if (eq == std::string::npos && arg.find("--no-") == 0) { // --no-name
+        name = arg.substr(2);
+      } else if (eq == std::string::npos) {                    // --name
+        name = arg.substr(2);
+      } else {                                                 // --name=value
+        name = arg.substr(2, eq - 2);
+        value = arg.substr(eq + 1);
+      }
+    }
+    name = strings::lower(name);
+
+    if (!duplicates) {
+      if (values.count(name) > 0 ||
+          (name.find("no-") == 0 && values.count(name.substr(3)) > 0)) {
+        return Error("Duplicate flag '" + name + "' on command line");
+      }
+    }
+
+    values[name] = value;
+  }
+
+  return load(values, unknowns);
+}
+
+
+inline Try<Nothing> FlagsBase::load(
+    const std::string& prefix,
+    int argc,
+    char** argv,
+    bool unknowns,
+    bool duplicates)
+{
+  return load(Option<std::string>::some(prefix),
+              argc,
+              argv,
+              unknowns,
+              duplicates);
+}
+
+
+inline Try<Nothing> FlagsBase::load(
+    const std::map<std::string, Option<std::string> >& values,
+    bool unknowns)
+{
+  std::map<std::string, Option<std::string> >::const_iterator iterator;
+
+  for (iterator = values.begin(); iterator != values.end(); ++iterator) {
+    const std::string& name = iterator->first;
+    const Option<std::string>& value = iterator->second;
+
+    if (flags.count(name) > 0) {
+      if (value.isSome()) {                        // --name=value
+        if (flags[name].boolean && value.get() == "") {
+          flags[name].loader(this, "true"); // Should never fail.
+        } else {
+          Try<Nothing> loader = flags[name].loader(this, value.get());
+          if (loader.isError()) {
+            return Error(
+                "Failed to load flag '" + name + "': " + loader.error());
+          }
+        }
+      } else {                                     // --name
+        if (flags[name].boolean) {
+          flags[name].loader(this, "true"); // Should never fail.
+        } else {
+          return Error(
+              "Failed to load non-boolean flag '" + name + "': Missing value");
+        }
+      }
+    } else if (name.find("no-") == 0) {
+      if (flags.count(name.substr(3)) > 0) {       // --no-name
+        if (flags[name.substr(3)].boolean) {
+          if (value.isNone() || value.get() == "") {
+            flags[name.substr(3)].loader(this, "false"); // Should never fail.
+          } else {
+            return Error(
+                "Failed to load boolean flag '" + name.substr(3) +
+                "' via '" + name + "' with value '" + value.get() + "'");
+          }
+        } else {
+          return Error(
+              "Failed to load non-boolean flag '" + name.substr(3) +
+              "' via '" + name + "'");
+        }
+      } else {
+        return Error(
+            "Failed to load unknown flag '" + name.substr(3) +
+            "' via '" + name + "'");
+      }
+    } else if (!unknowns) {
+      return Error("Failed to load unknown flag '" + name + "'");
+    }
+  }
+
+  return Nothing();
+}
+
+
+inline Try<Nothing> FlagsBase::load(
+    const std::map<std::string, std::string>& _values,
+    bool unknowns)
+{
+  std::map<std::string, Option<std::string> > values;
+  std::map<std::string, std::string>::const_iterator iterator;
+  for (iterator = _values.begin(); iterator != _values.end(); ++iterator) {
+    const std::string& name = iterator->first;
+    const std::string& value = iterator->second;
+    values[name] = Option<std::string>::some(value);
+  }
+  return load(values, unknowns);
+}
+
+
+inline std::string FlagsBase::usage() const
+{
+  const int PAD = 5;
+
+  std::string usage;
+
+  std::map<std::string, std::string> col1; // key -> col 1 string
+
+  // Construct string for the first column and store width of column.
+  size_t width = 0;
+
+  foreachvalue (const flags::Flag& flag, *this) {
+    if (flag.boolean) {
+      col1[flag.name] = "  --[no-]" + flag.name;
+    } else {
+      col1[flag.name] = "  --" + flag.name + "=VALUE";
+    }
+    width = std::max(width, col1[flag.name].size());
+  }
+
+  foreachvalue (const flags::Flag& flag, *this) {
+    std::string line = col1[flag.name];
+
+    std::string pad(PAD + width - line.size(), ' ');
+    line += pad;
+
+    size_t pos1 = 0, pos2 = 0;
+    pos2 = flag.help.find_first_of("\n\r", pos1);
+    line += flag.help.substr(pos1, pos2 - pos1) + "\n";
+    usage += line;
+
+    while (pos2 != std::string::npos) {  // Handle multi-line help strings.
+      line = "";
+      pos1 = pos2 + 1;
+      std::string pad2(PAD + width, ' ');
+      line += pad2;
+      pos2 = flag.help.find_first_of("\n\r", pos1);
+      line += flag.help.substr(pos1, pos2 - pos1) + "\n";
+      usage += line;
+    }
+  }
+  return usage;
+}
+
+} // namespace flags {
+
+#endif // __STOUT_FLAGS_FLAGS_HPP__

http://git-wip-us.apache.org/repos/asf/incubator-mesos/blob/190a3bb8/third_party/libprocess/third_party/stout/include/stout/flags/loader.hpp
----------------------------------------------------------------------
diff --git a/third_party/libprocess/third_party/stout/include/stout/flags/loader.hpp b/third_party/libprocess/third_party/stout/include/stout/flags/loader.hpp
new file mode 100644
index 0000000..e5eaf24
--- /dev/null
+++ b/third_party/libprocess/third_party/stout/include/stout/flags/loader.hpp
@@ -0,0 +1,109 @@
+#ifndef __STOUT_FLAGS_LOADER_HPP__
+#define __STOUT_FLAGS_LOADER_HPP__
+
+#include <string>
+
+#include <tr1/functional>
+
+#include <stout/error.hpp>
+#include <stout/nothing.hpp>
+#include <stout/option.hpp>
+#include <stout/try.hpp>
+
+#include <stout/flags/parse.hpp>
+
+namespace flags {
+
+// Forward declaration.
+class FlagsBase;
+
+template <typename T>
+struct Loader
+{
+  static Try<Nothing> load(
+      T* flag,
+      const std::tr1::function<Try<T>(const std::string&)>& parse,
+      const std::string& name,
+      const std::string& value)
+  {
+    Try<T> t = parse(value);
+    if (t.isSome()) {
+      *flag = t.get();
+    } else {
+      return Error("Failed to load value '" + value + "': " + t.error());
+    }
+    return Nothing();
+  }
+};
+
+
+template <typename T>
+struct OptionLoader
+{
+  static Try<Nothing> load(
+      Option<T>* flag,
+      const std::tr1::function<Try<T>(const std::string&)>& parse,
+      const std::string& name,
+      const std::string& value)
+  {
+    Try<T> t = parse(value);
+    if (t.isSome()) {
+      *flag = Option<T>::some(t.get());
+    } else {
+      return Error("Failed to load value '" + value + "': " + t.error());
+    }
+    return Nothing();
+  }
+};
+
+
+template <typename F, typename T>
+struct MemberLoader
+{
+  static Try<Nothing> load(
+      FlagsBase* base,
+      T F::*flag,
+      const std::tr1::function<Try<T>(const std::string&)>& parse,
+      const std::string& name,
+      const std::string& value)
+  {
+    F* f = dynamic_cast<F*>(base);
+    if (f != NULL) {
+      Try<T> t = parse(value);
+      if (t.isSome()) {
+        f->*flag = t.get();
+      } else {
+        return Error("Failed to load value '" + value + "': " + t.error());
+      }
+    }
+    return Nothing();
+  }
+};
+
+
+template <typename F, typename T>
+struct OptionMemberLoader
+{
+  static Try<Nothing> load(
+      FlagsBase* base,
+      Option<T> F::*flag,
+      const std::tr1::function<Try<T>(const std::string&)>& parse,
+      const std::string& name,
+      const std::string& value)
+  {
+    F* f = dynamic_cast<F*>(base);
+    if (f != NULL) {
+      Try<T> t = parse(value);
+      if (t.isSome()) {
+        f->*flag = Option<T>::some(t.get());
+      } else {
+        return Error("Failed to load value '" + value + "': " + t.error());
+      }
+    }
+    return Nothing();
+  }
+};
+
+} // namespace flags {
+
+#endif // __STOUT_FLAGS_LOADER_HPP__

http://git-wip-us.apache.org/repos/asf/incubator-mesos/blob/190a3bb8/third_party/libprocess/third_party/stout/include/stout/flags/parse.hpp
----------------------------------------------------------------------
diff --git a/third_party/libprocess/third_party/stout/include/stout/flags/parse.hpp b/third_party/libprocess/third_party/stout/include/stout/flags/parse.hpp
new file mode 100644
index 0000000..54eb35c
--- /dev/null
+++ b/third_party/libprocess/third_party/stout/include/stout/flags/parse.hpp
@@ -0,0 +1,55 @@
+#ifndef __STOUT_FLAGS_PARSE_HPP__
+#define __STOUT_FLAGS_PARSE_HPP__
+
+#include <sstream> // For istringstream.
+#include <string>
+
+#include <tr1/functional>
+
+#include <stout/duration.hpp>
+#include <stout/error.hpp>
+#include <stout/try.hpp>
+
+namespace flags {
+
+template <typename T>
+Try<T> parse(const std::string& value)
+{
+  T t;
+  std::istringstream in(value);
+  in >> t;
+  if (!in.good() && !in.eof()) {
+    return Error("Failed to convert into required type");
+  }
+  return t;
+}
+
+
+template <>
+inline Try<std::string> parse(const std::string& value)
+{
+  return value;
+}
+
+
+template <>
+inline Try<bool> parse(const std::string& value)
+{
+  if (value == "true" || value == "1") {
+    return true;
+  } else if (value == "false" || value == "0") {
+    return false;
+  }
+  return Error("Expecting a boolean (e.g., true or false)");
+}
+
+
+template <>
+inline Try<Duration> parse(const std::string& value)
+{
+  return Duration::parse(value);
+}
+
+} // namespace flags {
+
+#endif // __STOUT_FLAGS_PARSE_HPP__

http://git-wip-us.apache.org/repos/asf/incubator-mesos/blob/190a3bb8/third_party/libprocess/third_party/stout/include/stout/os.hpp
----------------------------------------------------------------------
diff --git a/third_party/libprocess/third_party/stout/include/stout/os.hpp b/third_party/libprocess/third_party/stout/include/stout/os.hpp
index 00b2313..28a08cc 100644
--- a/third_party/libprocess/third_party/stout/include/stout/os.hpp
+++ b/third_party/libprocess/third_party/stout/include/stout/os.hpp
@@ -1,6 +1,9 @@
 #ifndef __STOUT_OS_HPP__
 #define __STOUT_OS_HPP__
 
+#ifdef __APPLE__
+#include <crt_externs.h> // For _NSGetEnviron().
+#endif
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -55,8 +58,27 @@
   ({ (void)ret; *(result) = gethostbyname2(name, af); 0; })
 #endif // __APPLE__
 
+// Need to declare 'environ' pointer for non OS X platforms.
+#ifndef __APPLE__
+extern char** environ;
+#endif
+
 namespace os {
 
+inline char** environ()
+{
+  // Accessing the list of environment variables is platform-specific.
+  // On OS X, the 'environ' symbol isn't visible to shared libraries,
+  // so we must use the _NSGetEnviron() function (see 'man environ' on
+  // OS X). On other platforms, it's fine to access 'environ' from
+  // shared libraries.
+#ifdef __APPLE__
+  return *_NSGetEnviron();
+#endif
+  return ::environ;
+}
+
+
 // Checks if the specified key is in the environment variables.
 inline bool hasenv(const std::string& key)
 {

http://git-wip-us.apache.org/repos/asf/incubator-mesos/blob/190a3bb8/third_party/libprocess/third_party/stout/tests/flags_tests.cpp
----------------------------------------------------------------------
diff --git a/third_party/libprocess/third_party/stout/tests/flags_tests.cpp b/third_party/libprocess/third_party/stout/tests/flags_tests.cpp
new file mode 100644
index 0000000..2809280
--- /dev/null
+++ b/third_party/libprocess/third_party/stout/tests/flags_tests.cpp
@@ -0,0 +1,349 @@
+#include <gmock/gmock.h>
+
+#include <map>
+#include <string>
+
+#include <stout/duration.hpp>
+#include <stout/flags.hpp>
+#include <stout/gtest.hpp>
+#include <stout/none.hpp>
+#include <stout/nothing.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+
+
+using namespace flags;
+
+class TestFlags : public virtual FlagsBase
+{
+public:
+  TestFlags()
+  {
+    add(&TestFlags::name1,
+        "name1",
+        "Set name1",
+        "ben folds");
+
+    add(&TestFlags::name2,
+        "name2",
+        "Set name2",
+        42);
+
+    add(&TestFlags::name3,
+        "name3",
+        "Set name3",
+        false);
+
+    add(&TestFlags::name4,
+        "name4",
+        "Set name4");
+
+    add(&TestFlags::name5,
+        "name5",
+        "Set name5");
+  }
+
+  std::string name1;
+  int name2;
+  bool name3;
+  Option<bool> name4;
+  Option<bool> name5;
+};
+
+
+TEST(FlagsTest, Load)
+{
+  TestFlags flags;
+
+  std::map<std::string, Option<std::string> > values;
+
+  values["name1"] = Option<std::string>::some("billy joel");
+  values["name2"] = Option<std::string>::some("43");
+  values["name3"] = Option<std::string>::some("false");
+  values["no-name4"] = None();
+  values["name5"] = None();
+
+  flags.load(values);
+
+  EXPECT_EQ("billy joel", flags.name1);
+  EXPECT_EQ(43, flags.name2);
+  EXPECT_FALSE(flags.name3);
+  ASSERT_SOME(flags.name4);
+  EXPECT_FALSE(flags.name4.get());
+  ASSERT_SOME(flags.name5);
+  EXPECT_TRUE(flags.name5.get());
+}
+
+
+TEST(FlagsTest, Add)
+{
+  Flags<TestFlags> flags;
+
+  Option<std::string> name6;
+
+  flags.add(&name6,
+            "name6",
+            "Also set name6");
+
+  bool name7;
+
+  flags.add(&name7,
+            "name7",
+            "Also set name7",
+            true);
+
+  Option<std::string> name8;
+
+  flags.add(&name8,
+            "name8",
+            "Also set name8");
+
+  std::map<std::string, Option<std::string> > values;
+
+  values["name6"] = Option<std::string>::some("ben folds");
+  values["no-name7"] = None();
+
+  flags.load(values);
+
+  ASSERT_SOME(name6);
+  EXPECT_EQ("ben folds", name6.get());
+
+  EXPECT_FALSE(name7);
+
+  ASSERT_TRUE(name8.isNone());
+}
+
+
+TEST(FlagsTest, Flags)
+{
+  TestFlags flags;
+
+  std::map<std::string, Option<std::string> > values;
+
+  values["name1"] = Option<std::string>::some("billy joel");
+  values["name2"] = Option<std::string>::some("43");
+  values["name3"] = Option<std::string>::some("false");
+  values["no-name4"] = None();
+  values["name5"] = None();
+
+  flags.load(values);
+
+  EXPECT_EQ("billy joel", flags.name1);
+  EXPECT_EQ(43, flags.name2);
+  EXPECT_FALSE(flags.name3);
+  ASSERT_SOME(flags.name4);
+  EXPECT_FALSE(flags.name4.get());
+  ASSERT_SOME(flags.name5);
+  EXPECT_TRUE(flags.name5.get());
+}
+
+
+TEST(FlagsTest, LoadFromEnvironment)
+{
+  TestFlags flags;
+
+  os::setenv("FLAGSTEST_name1", "billy joel");
+  os::setenv("FLAGSTEST_name2", "43");
+  os::setenv("FLAGSTEST_no-name3", "");
+  os::setenv("FLAGSTEST_no-name4", "");
+  os::setenv("FLAGSTEST_name5", "");
+
+  Try<Nothing> load = flags.load("FLAGSTEST_");
+  EXPECT_SOME(load);
+
+  EXPECT_EQ("billy joel", flags.name1);
+  EXPECT_EQ(43, flags.name2);
+  EXPECT_FALSE(flags.name3);
+  ASSERT_SOME(flags.name4);
+  EXPECT_FALSE(flags.name4.get());
+  ASSERT_SOME(flags.name5);
+  EXPECT_TRUE(flags.name5.get());
+
+  os::unsetenv("FLAGSTEST_name1");
+  os::unsetenv("FLAGSTEST_name2");
+  os::unsetenv("FLAGSTEST_no-name3");
+  os::unsetenv("FLAGSTEST_no-name4");
+  os::unsetenv("FLAGSTEST_name5");
+}
+
+
+TEST(FlagsTest, LoadFromCommandLine)
+{
+  TestFlags flags;
+
+  int argc = 6;
+  char* argv[argc];
+
+  argv[0] = (char*) "/path/to/program";
+  argv[1] = (char*) "--name1=billy joel";
+  argv[2] = (char*) "--name2=43";
+  argv[3] = (char*) "--no-name3";
+  argv[4] = (char*) "--no-name4";
+  argv[5] = (char*) "--name5";
+
+  Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_SOME(load);
+
+  EXPECT_EQ("billy joel", flags.name1);
+  EXPECT_EQ(43, flags.name2);
+  EXPECT_FALSE(flags.name3);
+  ASSERT_SOME(flags.name4);
+  EXPECT_FALSE(flags.name4.get());
+  ASSERT_SOME(flags.name5);
+  EXPECT_TRUE(flags.name5.get());
+}
+
+
+TEST(FlagsTest, DuplicatesFromEnvironment)
+{
+  TestFlags flags;
+
+  os::setenv("FLAGSTEST_name1", "ben folds");
+
+  int argc = 2;
+  char* argv[argc];
+
+  argv[0] = (char*) "/path/to/program";
+  argv[1] = (char*) "--name1=billy joel";
+
+  Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_ERROR(load);
+
+  EXPECT_EQ("Duplicate flag 'name1' on command line", load.error());
+
+  os::unsetenv("FLAGSTEST_name1");
+}
+
+
+TEST(FlagsTest, DuplicatesFromCommandLine)
+{
+  TestFlags flags;
+
+  int argc = 3;
+  char* argv[argc];
+
+  argv[0] = (char*) "/path/to/program";
+  argv[1] = (char*) "--name1=billy joel";
+  argv[2] = (char*) "--name1=ben folds";
+
+  Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_ERROR(load);
+
+  EXPECT_EQ("Duplicate flag 'name1' on command line", load.error());
+}
+
+
+TEST(FlagsTest, Errors)
+{
+  TestFlags flags;
+
+  int argc = 2;
+  char* argv[argc];
+
+  argv[0] = (char*) "/path/to/program";
+
+  // Test an unknown flag.
+  argv[1] = (char*) "--foo";
+
+  Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_ERROR(load);
+
+  EXPECT_EQ("Failed to load unknown flag 'foo'", load.error());
+
+  // Now try an unknown flag with a value.
+  argv[1] = (char*) "--foo=value";
+
+  load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_ERROR(load);
+
+  EXPECT_EQ("Failed to load unknown flag 'foo'", load.error());
+
+  // Now try an unknown flag with a 'no-' prefix.
+  argv[1] = (char*) "--no-foo";
+
+  load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_ERROR(load);
+
+  EXPECT_EQ("Failed to load unknown flag 'foo' via 'no-foo'", load.error());
+
+  // Now test a boolean flag using the 'no-' prefix _and_ a value.
+  argv[1] = (char*) "--no-name3=value";
+
+  load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_ERROR(load);
+
+  EXPECT_EQ("Failed to load boolean flag 'name3' via "
+            "'no-name3' with value 'value'", load.error());
+
+  // Now test a boolean flag that couldn't be parsed.
+  argv[1] = (char*) "--name3=value";
+
+  load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_ERROR(load);
+
+  EXPECT_EQ("Failed to load flag 'name3': Failed to load value 'value': "
+            "Expecting a boolean (e.g., true or false)", load.error());
+
+  // Now test a non-boolean flag without a value.
+  argv[1] = (char*) "--name1";
+
+  load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_ERROR(load);
+
+  EXPECT_EQ("Failed to load non-boolean flag 'name1': "
+            "Missing value", load.error());
+
+  // Now test a non-boolean flag using the 'no-' prefix.
+  argv[1] = (char*) "--no-name2";
+
+  load = flags.load("FLAGSTEST_", argc, argv);
+  EXPECT_ERROR(load);
+
+  EXPECT_EQ("Failed to load non-boolean flag 'name2' "
+            "via 'no-name2'", load.error());
+}
+
+
+TEST(FlagsTest, Usage)
+{
+  TestFlags flags;
+
+  EXPECT_EQ(
+      "  --name1=VALUE     Set name1 (default: ben folds)\n"
+      "  --name2=VALUE     Set name2 (default: 42)\n"
+      "  --[no-]name3      Set name3 (default: false)\n"
+      "  --[no-]name4      Set name4\n"
+      "  --[no-]name5      Set name5\n",
+      flags.usage());
+}
+
+
+TEST(FlagsTest, Duration)
+{
+  Flags<TestFlags> flags;
+
+  Duration name6;
+
+  flags.add(&name6,
+            "name6",
+            "Amount of time",
+            Milliseconds(100));
+
+  Option<Duration> name7;
+
+  flags.add(&name7,
+            "name7",
+            "Also some amount of time");
+
+  std::map<std::string, Option<std::string> > values;
+
+  values["name6"] = Option<std::string>::some("2mins");
+  values["name7"] = Option<std::string>::some("3hrs");
+
+  flags.load(values);
+
+  EXPECT_EQ(Minutes(2), name6);
+
+  ASSERT_SOME(name7);
+  EXPECT_EQ(Hours(3), name7.get());
+}