Commit 27812080 authored by LEFEBVREJP email's avatar LEFEBVREJP email
Browse files

Adding radixcommand packages.

Adding default initialization of DEBUG_OUTPUT.
parent 1cac0267
......@@ -21,6 +21,14 @@ IF (NOT TRIBITS_PROCESSING_PACKAGE)
SET(${PROJECT_NAME}_TRIBITS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/TriBITS/tribits" CACHE PATH "")
INCLUDE("${${PROJECT_NAME}_TRIBITS_DIR}/TriBITS.cmake")
SET(${PROJECT_NAME}_ENABLE_TESTS ON CACHE BOOL "Enable tests by default.")
# set default debug output
IF(NOT DEFINED DEBUG_OUTPUT)
SET(DEBUG_OUTPUT 0 CACHE STRING "Display debug output.")
ENDIF()
IF(DEBUG_OUTPUT)
ADD_DEFINITIONS("-DDEBUG_OUTPUT=${DEBUG_OUTPUT}")
ENDIF()
TRIBITS_PROJECT_ENABLE_ALL()
ELSE()
# This CMakeLists.txt file is being processed as the TriBITS package file.
......
TRIBITS_SUBPACKAGE(command)
SET(SOURCE
commandline.cc
)
SET(HEADERS
commandline.hh
commandline.i.hh
)
#
# Add library
TRIBITS_ADD_LIBRARY(radixcommand
SOURCES ${SOURCE}
NOINSTALLHEADERS ${HEADERS}
)
#
# Add test directory
TRIBITS_ADD_TEST_DIRECTORIES(tests)
INSTALL(FILES ${HEADERS} DESTINATION "include/radixcommand/")
TRIBITS_SUBPACKAGE_POSTPROCESS()
TRIBITS_PACKAGE_DEFINE_DEPENDENCIES(
LIB_REQUIRED_PACKAGES radixbug
LIB_OPTIONAL_PACKAGES
TEST_REQUIRED_PACKAGES testframework
TEST_OPTIONAL_PACKAGES
LIB_REQUIRED_TPLS
LIB_OPTIONAL_TPLS
TEST_REQUIRED_TPLS
TEST_OPTIONAL_TPLS
)
#include "radixcommand/commandline.hh"
namespace radix
{
CommandLine::CommandLine(int argc, char ** argv)
: mArgc(argc)
, mArgv(argv)
{
if(argc < 1) return;
mExecutable = argv[0];
for (int i = 1; i < argc; i++) {
std::string temp = std::string(argv[i]);
std::tuple<std::string, std::string, bool> data
= std::make_tuple("", "", false);
//
// Check for '-' as first character
//
if (temp.size() > 0)
{
if (temp.at(0) == '-')
{
//
// Check for flag=value
//
size_t index = temp.find('=');
if (index == std::string::npos)
{//we didn't find equal sign
//
// This is just a flag with empty value
//
std::get<0>(data) = std::string("");
mData.insert(std::make_pair(temp.substr(1), data));
} else
{
//
// We found equal sign, so we must divide into flag and value
//
std::string flag = temp.substr(1, index-1);
//
// Check to make sure empty string wasn't provided
// flag=''
if (temp.size() - 1 != index)
{
std::get<0>(data) = temp.substr(index + 1, temp.size() - 1);
}
mData.insert(std::make_pair(flag, data));
}//else
}//if = '-'
else
{
mArgs.push_back(temp);
}
}//if size > 0
}//loop
}
void CommandLine::addOption(const std::string &name
, const std::string &description
, bool required)
{
// check if option was parsed in
const auto& it = mData.find(name);
if(it == mData.end())
{
// create a new tuple with no value
mData.insert(std::make_pair(name,
std::make_tuple(std::string("")
, description
, required)));
} else
{ // update existing entry
std::get<1>(it->second) = description;
std::get<2>(it->second) = required;
}
}
void CommandLine::printParsedLine(std::ostream &out) const
{
out << "Executable: '" << mExecutable << "'" << std::endl;
if(0 < mData.size())
{
out << "Options:" << std::endl;
}
int unknown = 0;
for(const auto& it: mData)
{
// <0> is value
std::string value = std::get<0>(it.second);
// check descriptions to indicate if this is a known option
bool known = !std::get<1>(it.second).empty();
bool required = std::get<2>(it.second);
if(!known)
{
unknown++;
}
out << "\t";
if(known)
{
if(required)
{
out << "[" << it.first << "] " << value;
} else
{
out << "<" << it.first << "> " << value;
}
out << " -- " << std::get<1>(it.second) << std::endl;
} else
{
out << "*" << it.first << " " << value << std::endl;
}
}
if(0 < mArgs.size())
{
out << "Arguments:" << std::endl;
for(size_t i = 0; i < mArgs.size(); ++i)
{
out << "\t" << mArgs[i] << std::endl;
}
} else
{
out << "No arguments." << std::endl;
}
if(unknown)
{
out << "* - unknown/unrecognized input(" << unknown << ")" << std::endl;
}
}
void CommandLine::help(std::ostream &out) const
{
// dump commandline example first
out << mExecutable;
for(const auto& it: mData)
{
// <0> is value
std::string value = std::get<0>(it.second);
// check descriptions to indicate if this is a known option
bool hasDoc = !std::get<1>(it.second).empty();
bool required = std::get<2>(it.second);
if(hasDoc)
{
if(required)
{
out << " [-" << it.first << "=var]";
} else
{
out << " <-" << it.first << "<=var>>";
}
}
}
for(size_t i = 0; i < mDeclArgs.size(); ++i)
{
out << " " << mDeclArgs[i].first;
}
out << std::endl;
for(const auto& it: mData)
{
// <0> is value
std::string value = std::get<0>(it.second);
// check descriptions to indicate if this is a known option
bool hasDoc = !std::get<1>(it.second).empty();
bool required = std::get<2>(it.second);
if(hasDoc)
{
out << "\t";
if(required)
{
out << "[" << it.first << "] ";
} else
{
out << "<" << it.first << "> ";
}
out << " -- " << std::get<1>(it.second) << std::endl;
}
}
for(size_t i = mArgs.size(); i < mDeclArgs.size(); ++i)
{
out << "\t" << mDeclArgs[i].first;
if(!mDeclArgs[i].second.empty())
{
out << " -- "
<< mDeclArgs[i].second << std::endl;
} else
{
out << std::endl;
}
}
}
bool CommandLine::exists(const std::string &name) const
{
const auto& it = mData.find(name);
return (it != mData.end());
}
bool CommandLine::existsNotEmpty(const std::string &name) const
{
const auto& it = mData.find(name);
bool exists = (it != mData.end());
if(!exists) return false;
if(std::get<0>(it->second).empty()) return false;
return true;
}
const std::vector<std::string>& CommandLine::arguments() const
{
return mArgs;
}
void CommandLine::setArguments(const std::vector<std::string> &args)
{
mArgs = args;
}
std::string CommandLine::executable() const
{
return mExecutable;
}
void CommandLine::setExecutable(const std::string &executable)
{
mExecutable = executable;
}
void CommandLine::declareArgument(const std::string &id, const std::string &description)
{
mDeclArgs.push_back(std::make_pair(id, description));
}
bool CommandLine::validate(std::vector<std::string> &errors) const
{
for(const auto& it: mData)
{
// check if value is empty/no set
bool empty = std::get<0>(it.second).empty();
bool required = std::get<2>(it.second);
if(required && empty)
{
std::stringstream ss;
ss << "Required option: '" << it.first << "' is missing.";
errors.push_back(ss.str());
}
}
// ensure the number of arguments matchs the number of declared arguments
if(mArgs.size() < mDeclArgs.size())
{
std::stringstream ss;
ss << "Missing required argument";
if((mDeclArgs.size() - mArgs.size())==1)
{
ss << " '" << mDeclArgs[mArgs.size()].first << "'" << std::endl;
} else {
ss << "s:" << std::endl;
for(size_t i = mArgs.size(); i < mDeclArgs.size(); ++i)
{
ss << "\t'" << mDeclArgs[i].first;
if(!mDeclArgs[i].second.empty())
{
ss << "' -- "
<< mDeclArgs[i].second << std::endl;
} else
{
ss << "'" << std::endl;
}
}
}
errors.push_back(ss.str());
}
if(errors.size()) return false;
return true;
}
template<>
std::string CommandLine::get(const std::string& name) const
{
std::string result;
const auto& it = mData.find(name);
if(it == mData.end())
{
std::stringstream ss;
ss << "No such option: " << name;
throw std::logic_error(ss.str());
} else
{
std::string value = std::get<0>(it->second);
if(value.empty())
{
std::stringstream ss;
ss << "Illegal attempt to convert flag '" << name << "' to a value.";
throw std::logic_error(ss.str());
}
result = value;
}
return result;
}
template<>
std::string CommandLine::get(const std::string& name
, std::string defaultValue) const
{
std::string result = defaultValue;
if(existsNotEmpty(name))
{
result = get<std::string>(name);
}
return result;
}
} // namespace radix
#ifndef RADIX_RADIXCOMMAND_COMMANDLINE_HH_
#define RADIX_RADIXCOMMAND_COMMANDLINE_HH_
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <ostream>
namespace radix
{
class CommandLine
{
public:
CommandLine(int argc, char ** argv);
/**
* @brief addOption Adds official option to this commandline
* @param name key
* @param description description of option
* @param required whether it is required or not
*/
void addOption(const std::string& name
, const std::string& description
,bool required = false);
// dumps all options to \code out
void printParsedLine(std::ostream& out) const;
// dumps the optional and required options to \code out
void help(std::ostream& out) const;
// does an option exist in this commandline
bool exists(const std::string& name) const;
// does an option exist in this commandline
bool existsNotEmpty(const std::string& name) const;
const std::vector<std::string>& arguments() const;
void setArguments(const std::vector<std::string> &args);
std::string executable() const;
void setExecutable(const std::string &executable);
void declareArgument(const std::string& id
, const std::string& description = std::string());
/**
* @brief get retrieve option value as type \code return_type
* @param name key of command line option
* @return \code return_type
*/
template<typename return_type>
return_type get(const std::string& name) const;
/**
* @brief get retrieve option value as type \code return_type
* @param name key of command line option
* @param defaultValue default return value in the event name was not provided
* @return \code return_type
*/
template<typename return_type>
return_type get(const std::string& name, return_type defaultValue) const;
/**
* @brief validate Checks for existince of required options
* @param errors list of errors
* @return success/failure
*/
bool validate(std::vector<std::string>& errors) const;
private:
int mArgc;
char ** mArgv;
std::string mExecutable;
std::vector<std::string> mArgs;
std::vector<std::pair<std::string, std::string>> mDeclArgs;
std::unordered_map<std::string, std::tuple<std::string,std::string, bool>> mData;
}; // class CommandLine
} // namespace radix
#include "radixcommand/commandline.i.hh"
#endif /** RADIX_RADIXCOMMAND_COMMANDLINE_HH_ */
#include "radixcommand/commandline.hh"
#include "radixbug/bug.hh"
#include <sstream>
#include <iomanip>
#include <exception>
#include <typeinfo>
namespace radix
{
template<typename return_type>
return_type CommandLine::get(const std::string& name) const
{
return_type result;
const auto& it = mData.find(name);
if(it == mData.end())
{
std::stringstream ss;
ss << "No such option: " << name;
throw std::logic_error(ss.str());
} else
{
std::string value = std::get<0>(it->second);
if(value.empty())
{
std::stringstream ss;
ss << "Illegal attempt to convert flag '" << name << "' to a value.";
throw std::logic_error(ss.str());
}
std::istringstream ss(value);
ss >> result;
}
return result;
}
template<typename return_type>
return_type CommandLine::get(const std::string& name, return_type defaultValue) const
{
return_type result = defaultValue;
if(existsNotEmpty(name))
{
result = get<return_type>(name);
}
return result;
}
} // namespace radix
INCLUDE(GoogleTest)
ADD_GOOGLE_TEST(tstCommandLine.cc NP 1)
/*
* File: tstCommandLine.cc
* Author: Jordan P. Lefebvre, lefebvrejp@ornl.gov
*
* Created on January 11, 2017, 11:09 AM
*/
#include "gtest/gtest.h"
#include "radixbug/bug.hh"
#include "radixcommand/commandline.hh"
#include <iostream>
using namespace std;
using namespace radix;
TEST(radixcommand, CommandLine)
{
{
int fake_argc = 7;
char * fake_argv[] = {(char*)("/Users/jap/build/release/dummy"),
(char*)"-T=temp",
(char*)"-N=16",
(char*)"inputA",
(char*)"-M=machine.txt",
(char*)"-load=3.4",
(char*)"-flag",
(char*)"inputB"};
CommandLine line(fake_argc, fake_argv);
line.addOption("o"
, "Output filepath, whitespace must be escaped."
, true);
line.addOption("flag"
, "Print warnings."
, false);
line.printParsedLine(std::cout);
line.help(std::cout);
EXPECT_TRUE(line.exists("flag"));
EXPECT_EQ(16, line.get<int>("N"));
EXPECT_EQ(32, line.get("Z", 32));
EXPECT_DOUBLE_EQ(32., line.get("A", 32.));
EXPECT_ANY_THROW(line.get<int>("F"));
EXPECT_EQ("out.radix", line.get<std::string>("o", "out.radix"));
std::vector<std::string> errors;
EXPECT_FALSE(line.validate(errors));
std::cout << "Errors:" << std::endl;
for(size_t i = 0; i < errors.size(); ++i)
{
std::cout << "\t" <<errors[i] << std::endl;
}
}
{
int fake_argc = 2;
char * fake_argv[] = {(char*)("/Users/jap/build/release/dummy"),
(char*)"-o=path/to a/file"};
CommandLine line(fake_argc, fake_argv);
line.declareArgument("input", "Path to input file.");
line.addOption("o"
, "Output filepath, whitespace must be escaped."
, true);
line.printParsedLine(std::cout);
EXPECT_EQ("path/to a/file", line.get<std::string>("o", ""));
std::vector<std::string> errors;
EXPECT_FALSE(line.validate(errors));
std::cout << "Errors:" << std::endl;
for(size_t i = 0; i < errors.size(); ++i)
{
std::cout << "\t" <<errors[i] << std::endl;
}
//test multiple missing arguments
line.declareArgument("output", "Path to output file w/o extension.");
EXPECT_FALSE(line.validate(errors));
std::cout << "Errors:" << std::endl;
for(size_t i = 0; i < errors.size(); ++i)
{
std::cout << "\t" <<errors[i] << std::endl;
}
line.help(std::cout);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment