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));
+}