You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2014/06/25 23:37:45 UTC

[3/7] git commit: Introduced a Subcommand abstraction in stout.

Introduced a Subcommand abstraction in stout.

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


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

Branch: refs/heads/master
Commit: 3279c406c7ff12f869189f7b20dbef17310bce49
Parents: 7f1774b
Author: Jie Yu <yu...@gmail.com>
Authored: Wed Jun 25 14:33:16 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Jun 25 14:33:21 2014 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/3rdparty/stout/Makefile.am  |   2 +
 .../3rdparty/stout/include/stout/subcommand.hpp | 191 +++++++++++++++++++
 .../3rdparty/stout/tests/subcommand_tests.cpp   | 181 ++++++++++++++++++
 3 files changed, 374 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/3279c406/3rdparty/libprocess/3rdparty/stout/Makefile.am
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/Makefile.am b/3rdparty/libprocess/3rdparty/stout/Makefile.am
index eac7ab5..b562e87 100644
--- a/3rdparty/libprocess/3rdparty/stout/Makefile.am
+++ b/3rdparty/libprocess/3rdparty/stout/Makefile.am
@@ -68,6 +68,7 @@ EXTRA_DIST =					\
   include/stout/stopwatch.hpp			\
   include/stout/stringify.hpp			\
   include/stout/strings.hpp			\
+  include/stout/subcommand.hpp			\
   include/stout/tests/utils.hpp			\
   include/stout/thread.hpp			\
   include/stout/try.hpp				\
@@ -105,5 +106,6 @@ EXTRA_DIST =					\
   tests/set_tests.cpp				\
   tests/some_tests.cpp				\
   tests/strings_tests.cpp			\
+  tests/subcommand_tests.cpp			\
   tests/thread_tests.cpp			\
   tests/uuid_tests.cpp

http://git-wip-us.apache.org/repos/asf/mesos/blob/3279c406/3rdparty/libprocess/3rdparty/stout/include/stout/subcommand.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/subcommand.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/subcommand.hpp
new file mode 100644
index 0000000..b121836
--- /dev/null
+++ b/3rdparty/libprocess/3rdparty/stout/include/stout/subcommand.hpp
@@ -0,0 +1,191 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __STOUT_SUBCOMMAND_HPP__
+#define __STOUT_SUBCOMMAND_HPP__
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <stout/flags.hpp>
+#include <stout/foreach.hpp>
+#include <stout/hashset.hpp>
+#include <stout/option.hpp>
+#include <stout/preprocessor.hpp>
+
+// Subcommand is an abstraction for creating command binaries that
+// encompass many subcommands. For example:
+//
+//  $ ./runner start --arg1=1 --arg2=2
+//  $ ./runner stop --arg3=3 --arg4=4
+//
+// Here, the 'runner' command contains two subcommand implementations:
+// StartCommand and StopCommand. Each subcommand needs to define a
+// name, implement an 'execute' function, and provide the address of a
+// flags where the command line arguments will be parsed to. To
+// simplify creating command binaries that encompass many subcommands,
+// we provide a 'dispatch' function which will look at argv[1] to
+// decide which subcommand to execute (based on its name) and then
+// parse the command line flags for you.
+class Subcommand
+{
+public:
+  // This function is supposed to be called by the main function of
+  // the command binary. A user needs to register at least one
+  // subcommand. Here is a typical example of the main function of the
+  // command binary:
+  //
+  // int main(int argc, char** argv)
+  // {
+  //   return Subcommand::dispatch(
+  //     None(),
+  //     argc,
+  //     argv,
+  //     new Subcommand1(),
+  //     new Subcommand2(),
+  //     new Subcommand3());
+  // }
+#define INSERT(z, N, _) subcommands.push_back( c ## N );
+#define TEMPLATE(Z, N, DATA)                            \
+  static int dispatch(                                  \
+      const Option<std::string>& prefix,                \
+      int argc,                                         \
+      char** argv,                                      \
+      ENUM_PARAMS(N, Subcommand* c))                    \
+  {                                                     \
+    std::vector<Subcommand*> subcommands;               \
+    REPEAT_FROM_TO(0, N, INSERT, _)                     \
+    return dispatch(prefix, argc, argv, subcommands);   \
+  }
+
+  REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args C1 -> C11.
+#undef TEMPLATE
+#undef INSERT
+
+  explicit Subcommand(const std::string& _name) : name_(_name) {}
+  virtual ~Subcommand() {}
+
+  std::string name() const { return name_; }
+
+protected:
+  // Defines the main function of this subcommand. The return value
+  // will be used as the exit code.
+  // TODO(jieyu): Consider passing in argc and argv as some users
+  // might want to access the remaining command line arguments.
+  virtual int execute() = 0;
+
+  // Returns the pointer to the flags that will be used for this
+  // subcommand. If the user does not provide an override, the default
+  // empty flags will be used.
+  virtual flags::FlagsBase* getFlags() { return &flags_; }
+
+private:
+  // Returns the usage by listing all the registered subcommands.
+  static std::string usage(
+      const std::string& argv0,
+      const std::vector<Subcommand*>& subcommands);
+
+  static int dispatch(
+    const Option<std::string>& prefix,
+    int argc,
+    char** argv,
+    const std::vector<Subcommand*>& subcommands);
+
+  // The name of this subcommand.
+  std::string name_;
+
+  // The default flags which is empty.
+  flags::FlagsBase flags_;
+};
+
+
+inline std::string Subcommand::usage(
+    const std::string& argv0,
+    const std::vector<Subcommand*>& subcommands)
+{
+  std::ostringstream stream;
+
+  stream << "Usage: " << argv0 << " <subcommand> [OPTIONS]\n\n"
+         << "Available subcommands:\n"
+         << "    help\n";
+
+  // Get a list of available subcommands.
+  foreach (Subcommand* subcommand, subcommands) {
+    stream << "    " << subcommand->name() << "\n";
+  }
+
+  return stream.str();
+}
+
+
+inline int Subcommand::dispatch(
+    const Option<std::string>& prefix,
+    int argc,
+    char** argv,
+    const std::vector<Subcommand*>& subcommands)
+{
+  if (subcommands.empty()) {
+    std::cerr << "No subcommand is found" << std::endl;
+    return 1;
+  }
+
+  // Check for duplicated subcommand names.
+  hashset<std::string> names;
+  foreach (Subcommand* subcommand, subcommands) {
+    if (names.contains(subcommand->name())) {
+      std::cerr << "Multiple subcommands have name '"
+                << subcommand->name() << "'" << std::endl;
+      return 1;
+    }
+    names.insert(subcommand->name());
+  }
+
+  if (argc < 2) {
+    std::cerr << usage(argv[0], subcommands) << std::endl;
+    return 1;
+  }
+
+  if (std::string(argv[1]) == "help") {
+    if (argc == 2) {
+      std::cout << usage(argv[0], subcommands) << std::endl;
+      return 0;
+    }
+
+    // 'argv[0] help subcommand' => 'argv[0] subcommand --help'
+    argv[1] = argv[2];
+    argv[2] = (char*) "--help";
+  }
+
+  foreach (Subcommand* subcommand, subcommands) {
+    if (subcommand->name() == argv[1]) {
+      flags::FlagsBase* flags = subcommand->getFlags();
+
+      Try<Nothing> load = flags->load(prefix, argc - 1, argv + 1);
+      if (load.isError()) {
+        std::cerr << "Failed to parse the flags: " << load.error() << std::endl;
+        return 1;
+      }
+
+      return subcommand->execute();
+    }
+  }
+
+  std::cerr << "Subcommand '" << argv[1] << "' is not available\n"
+            << usage(argv[0], subcommands) << std::endl;
+  return 1;
+}
+
+#endif // __STOUT_SUBCOMMAND_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/3279c406/3rdparty/libprocess/3rdparty/stout/tests/subcommand_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/3rdparty/stout/tests/subcommand_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/subcommand_tests.cpp
new file mode 100644
index 0000000..c40bba4
--- /dev/null
+++ b/3rdparty/libprocess/3rdparty/stout/tests/subcommand_tests.cpp
@@ -0,0 +1,181 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <stout/flags.hpp>
+#include <stout/foreach.hpp>
+#include <stout/subcommand.hpp>
+
+using std::string;
+using std::vector;
+
+
+class TestSubcommand : public Subcommand
+{
+public:
+  struct Flags : public flags::FlagsBase
+  {
+    Flags()
+    {
+      add(&b, "b", "bool");
+      add(&i, "i", "int");
+      add(&s, "s", "string");
+      add(&s2, "s2", "string with single quote");
+      add(&s3, "s3", "string with double quote");
+      add(&d, "d", "Duration");
+      add(&y, "y", "Bytes");
+      add(&j, "j", "JSON::Object");
+    }
+
+    void populate()
+    {
+      b = true;
+      i = 42;
+      s = "hello";
+      s2 = "we're";
+      s3 = "\"geek\"";
+      d = Seconds(10);
+      y = Bytes(100);
+
+      JSON::Object object;
+      object.values["strings"] = "string";
+      object.values["integer1"] = 1;
+      object.values["integer2"] = -1;
+      object.values["double1"] = 1;
+      object.values["double2"] = -1;
+      object.values["double3"] = -1.42;
+
+      JSON::Object nested;
+      nested.values["string"] = "string";
+      object.values["nested"] = nested;
+
+      JSON::Array array;
+      array.values.push_back(nested);
+      object.values["array"] = array;
+
+      j = object;
+    }
+
+    Option<bool> b;
+    Option<int> i;
+    Option<string> s;
+    Option<string> s2;
+    Option<string> s3;
+    Option<Duration> d;
+    Option<Bytes> y;
+    Option<JSON::Object> j;
+  };
+
+  explicit TestSubcommand(const string& name) : Subcommand(name) {}
+
+  Flags flags;
+
+protected:
+  virtual int execute() { return 0; }
+  virtual flags::FlagsBase* getFlags() { return &flags; }
+};
+
+
+// Generates a vector of arguments from flags.
+static vector<string> getArgv(const flags::FlagsBase& flags)
+{
+  vector<string> argv;
+  foreachpair (const string& name, const flags::Flag& flag, flags) {
+    Option<string> value = flag.stringify(flags);
+    if (value.isSome()) {
+      argv.push_back("--" + name + "=" + value.get());
+    }
+  }
+  return argv;
+}
+
+
+TEST(SubcommandTest, Flags)
+{
+  TestSubcommand::Flags flags;
+  flags.populate();
+
+  // Construct the command line arguments.
+  vector<string> _argv = getArgv(flags);
+  int argc = _argv.size() + 2;
+  char** argv = new char*[argc];
+  argv[0] = (char*) "command";
+  argv[1] = (char*) "subcommand";
+  for (int i = 2; i < argc; i++) {
+    argv[i] = ::strdup(_argv[i - 2].c_str());
+  }
+
+  TestSubcommand subcommand("subcommand");
+
+  ASSERT_EQ(0, Subcommand::dispatch(
+      None(),
+      argc,
+      argv,
+      &subcommand));
+
+  EXPECT_EQ(flags.b, subcommand.flags.b);
+  EXPECT_EQ(flags.i, subcommand.flags.i);
+  EXPECT_EQ(flags.s, subcommand.flags.s);
+  EXPECT_EQ(flags.s2, subcommand.flags.s2);
+  EXPECT_EQ(flags.s3, subcommand.flags.s3);
+  EXPECT_EQ(flags.d, subcommand.flags.d);
+  EXPECT_EQ(flags.y, subcommand.flags.y);
+  EXPECT_EQ(flags.j, subcommand.flags.j);
+
+  for (int i = 2; i < argc; i++) {
+    ::free(argv[i]);
+  }
+  delete argv;
+}
+
+
+TEST(SubcommandTest, Dispatch)
+{
+  TestSubcommand subcommand("subcommand");
+  TestSubcommand subcommand2("subcommand2");
+
+  int argc = 2;
+  char* argv[] = {
+    (char*) "command",
+    (char*) "subcommand"
+  };
+
+  EXPECT_EQ(1, Subcommand::dispatch(
+      None(),
+      argc,
+      argv,
+      &subcommand2));
+
+  // Duplicated subcommand names.
+  EXPECT_EQ(1, Subcommand::dispatch(
+      None(),
+      argc,
+      argv,
+      &subcommand,
+      &subcommand));
+
+  EXPECT_EQ(0, Subcommand::dispatch(
+      None(),
+      argc,
+      argv,
+      &subcommand,
+      &subcommand2));
+}