Replace Argument class with CLI11

CLI11 is one of the most commonly use argument parser in OpenBMC.
The Argument class will be replaced with CLI11 and the
argument.(h/c)pp file will be removed.

Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: Ibff60a19d3ff9209ca70889acc9e6aec4a8d4fca
diff --git a/argument.cpp b/argument.cpp
deleted file mode 100644
index f8e5aaf..0000000
--- a/argument.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * Copyright © 2016 IBM Corporation
- *
- * 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 "argument.hpp"
-
-#include <algorithm>
-#include <cassert>
-#include <iostream>
-#include <iterator>
-
-namespace phosphor
-{
-namespace led
-{
-ArgumentParser::ArgumentParser(int argc, char** argv)
-{
-    int option = 0;
-    while (-1 !=
-           (option = getopt_long(argc, argv, optionstr, &options[0], nullptr)))
-    {
-        switch (option)
-        {
-            case '?':
-            case 'h':
-                usage(argv);
-                exit(-1);
-                break;
-            case 'p':
-                arguments["path"] = optarg;
-                break;
-        }
-    }
-}
-
-const std::string& ArgumentParser::operator[](const std::string& opt)
-{
-    static const std::string emptyString{};
-
-    auto i = arguments.find(opt);
-    if (i == arguments.end())
-    {
-        return emptyString;
-    }
-
-    return i->second;
-}
-
-void ArgumentParser::usage(char** argv)
-{
-    // NOLINTNEXTLINE
-    std::cerr << "Usage: " << argv[0] << " [options]" << std::endl;
-    std::cerr << "Options:" << std::endl;
-    std::cerr << "    --help               Print this menu" << std::endl;
-    std::cerr << "    --path=<path>        absolute path of LED in sysfs; like";
-    std::cerr << " /sys/class/leds/<name>" << std::endl;
-}
-} // namespace led
-} // namespace phosphor
diff --git a/argument.hpp b/argument.hpp
deleted file mode 100644
index 6afb762..0000000
--- a/argument.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef ARGUMENT_H
-#define ARGUMENT_H
-#include <getopt.h>
-
-#include <map>
-#include <string>
-
-namespace phosphor
-{
-namespace led
-{
-/** @brief Class - Encapsulates parsing command line options and
- *                 populating arguments
- */
-class ArgumentParser
-{
-  public:
-    ArgumentParser() = delete;
-    ~ArgumentParser() = default;
-    ArgumentParser(const ArgumentParser&) = delete;
-    ArgumentParser& operator=(const ArgumentParser&) = delete;
-    ArgumentParser(ArgumentParser&&) = default;
-    ArgumentParser& operator=(ArgumentParser&&) = default;
-
-    /** @brief Constructs Argument object
-     *
-     *  @param argc - the main function's argc passed as is
-     *  @param argv - the main function's argv passed as is
-     *  @return Object constructed
-     */
-    ArgumentParser(int argc, char** argv);
-
-    /** @brief Given a option, returns its argument(optarg) */
-    const std::string& operator[](const std::string& opt);
-
-    /** @brief Displays usage */
-    static void usage(char** argv);
-
-  private:
-    /** @brief Option to argument mapping */
-    std::map<const std::string, std::string> arguments;
-
-    /** @brief Array of struct options as needed by getopt_long */
-    // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
-    static inline const option options[] = {
-        {"path", required_argument, nullptr, 'p'},
-        {"help", no_argument, nullptr, 'h'},
-        {nullptr, 0, nullptr, 0},
-    };
-
-    /** @brief optstring as needed by getopt_long */
-    static inline const char* const optionstr = "p:?h";
-};
-
-} // namespace led
-} // namespace phosphor
-
-#endif
diff --git a/controller.cpp b/controller.cpp
index afe476e..fd08c74 100644
--- a/controller.cpp
+++ b/controller.cpp
@@ -14,24 +14,16 @@
  * limitations under the License.
  */
 
-#include "argument.hpp"
 #include "physical.hpp"
 #include "sysfs.hpp"
 
+#include <CLI/CLI.hpp>
 #include <boost/algorithm/string.hpp>
 
 #include <algorithm>
 #include <iostream>
 #include <string>
 
-static void exitWithError(const char* err, char** argv)
-{
-    phosphor::led::ArgumentParser::usage(argv);
-    std::cerr << std::endl;
-    std::cerr << "ERROR: " << err << std::endl;
-    exit(-1);
-}
-
 struct LedDescr
 {
     std::string devicename;
@@ -50,7 +42,10 @@
 void getLedDescr(const std::string& name, LedDescr& ledDescr)
 {
     std::vector<std::string> words;
+    // FIXME: https://bugs.llvm.org/show_bug.cgi?id=41141
+    // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks)
     boost::split(words, name, boost::is_any_of(":"));
+    // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
     try
     {
         ledDescr.devicename = words.at(0);
@@ -90,19 +85,23 @@
     static constexpr auto objParent = "/xyz/openbmc_project/led/physical";
     static constexpr auto devParent = "/sys/class/leds/";
 
-    // Read arguments.
-    auto options = phosphor::led::ArgumentParser(argc, argv);
+    CLI::App app{"Control and Drive the physical LEDs"};
 
-    // FIXME: https://bugs.llvm.org/show_bug.cgi?id=41141
-    // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks)
+    // Read arguments.
+    std::string path{};
+    app.add_option("-p,--path", path,
+                   "absolute path of LED in sysfs; like /sys/class/leds/<name>")
+        ->required();
 
     // Parse out Path argument.
-    if (options["path"].empty())
+    try
     {
-        exitWithError("Path not specified.", argv);
+        app.parse(argc, argv);
     }
-
-    auto path = options["path"];
+    catch (const CLI::Error& e)
+    {
+        return app.exit(e);
+    }
 
     // If the LED has a hyphen in the name like: "one-two", then it gets passed
     // as /one/two/ as opposed to /one-two to the service file. There is a
@@ -133,7 +132,7 @@
     // Convert LED name in sysfs into DBus name
     LedDescr ledDescr;
     getLedDescr(name, ledDescr);
-    // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
+
     name = getDbusName(ledDescr);
 
     // Unique bus name representing a single LED.
diff --git a/meson.build b/meson.build
index 0ae8584..2d4dd91 100644
--- a/meson.build
+++ b/meson.build
@@ -13,7 +13,16 @@
 sdbusplus_dep = dependency('sdbusplus')
 phosphor_dbus_interfaces_dep = dependency('phosphor-dbus-interfaces')
 boost = dependency('boost', include_type: 'system')
+
+cxx = meson.get_compiler('cpp')
+if cxx.has_header('CLI/CLI.hpp')
+    cli11_dep = declare_dependency()
+else
+    cli11_dep = dependency('CLI11')
+endif
+
 deps = [
+    cli11_dep,
     sdbusplus_dep,
     phosphor_dbus_interfaces_dep,
     boost,
@@ -28,7 +37,6 @@
 )
 
 sources = [
-    'argument.cpp',
     'controller.cpp',
     'physical.cpp',
     'sysfs.cpp',
diff --git a/subprojects/CLI11.wrap b/subprojects/CLI11.wrap
new file mode 100644
index 0000000..2e5a95b
--- /dev/null
+++ b/subprojects/CLI11.wrap
@@ -0,0 +1,6 @@
+[wrap-git]
+url = https://github.com/CLIUtils/CLI11.git
+revision = HEAD
+
+[provide]
+CLI11 = CLI11_dep