1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-05-07 23:33:52 +00:00

Merge pull request #2 from henryiii/appveyor

Appveyor support
This commit is contained in:
henryiii 2017-02-19 17:02:28 -05:00 committed by GitHub
commit 23edb82bc4
11 changed files with 77 additions and 72 deletions

View File

@ -1,47 +1,25 @@
# Repo as zip
shallow_clone: true
os:
- Visual Studio 2015 Update 2
- Visual Studio 2015
environment:
CTEST_OUPTUT_ON_FAILURE: ON
configuration:
- Debug
branches:
except:
- gh-pages
install:
############################################################################
# All external dependencies are installed in C:\projects\deps
############################################################################
- mkdir C:\projects\deps
- cd C:\projects\deps
############################################################################
# Install a recent CMake
############################################################################
- set CMAKE_URL="https://cmake.org/files/v3.7/cmake-3.7.0-win32-x86.zip"
- appveyor DownloadFile %CMAKE_URL% -FileName cmake.zip
- 7z x cmake.zip -oC:\projects\deps\cmake > nul
- set PATH=C:\projects\deps\cmake\bin;%PATH%
- set PATH=C:\Python36;%PATH%
- cmake --version
before_build:
- call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
- cd %APPVEYOR_BUILD_FOLDER%
build:
verbosity: detailed
build_script:
- mkdir build
- cd build
- cmake ..
- cmake .. -DCLI_SINGLE_FILE_TESTS=ON
- cmake --build .
test_script:
- cmake --build --target test .
- ctest

View File

@ -3,6 +3,7 @@
* Added `require_subcommand` to `App`, to simplify forcing subcommands. Do not "chain" with `add_subcommand`, since that is the subcommand, not the master `App`. Untested.
* Added printout of ini file text given parsed options, skips flags.
* Support for quotes and spaces in ini files
* Support for Windows (added Appveyor) (Still use `--` syntax)
## Version 0.4

View File

@ -14,8 +14,12 @@ else()
endif()
# Be moderately paranoid with flags
# But only globally, don't inherit
add_compile_options(-pedantic -Wall -Wextra -O0)
if(CMAKE_COMPILER_IS_GNUCC)
add_definitions("-Wall -Wextra -pedantic")
endif()
if(MSVC)
add_definitions("/W4")
endif()
add_library(CLI INTERFACE)
target_include_directories(CLI INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")

View File

@ -1,4 +1,5 @@
[![Build Status](https://travis-ci.org/henryiii/CLI11.svg?branch=master)](https://travis-ci.org/henryiii/CLI11)
[![Build Status Windows](https://ci.appveyor.com/api/projects/status/github/henryiii/CLI11?branch=master&svg=true)](https://ci.appveyor.com/project/HenrySchreiner/cli11)
[![Join the chat at https://gitter.im/CLI11gitter/Lobby](https://badges.gitter.im/CLI11gitter/Lobby.svg)](https://gitter.im/CLI11gitter/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![License: LGPL v2.1](https://img.shields.io/badge/License-LGPL%20v2.1-blue.svg)](./LICENSE)
@ -13,9 +14,9 @@ An acceptable CLI parser library should be all of the following:
* Easy to include (i.e., header only, one file if possible, no external requirements): While many programs depend on Boost, that should not be a requirement if all you want is CLI parsing.
* Short Syntax: This is one of the main points of a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability.
* C++11 or better: Should work with GCC 4.7+ (such as GCC 4.8 on CentOS 7) or above, or Clang 3.5+. (Note: for CLI11, Clang 3.4 only fails because of tests, googlemock does not support it.)
* Work at least on Linux and MacOS.
* Well tested using Travis.
* C++11 or better: Should work with GCC 4.7+ (such as GCC 4.8 on CentOS 7) or above, or Clang 3.5+, or MSVC 2015+. (Note: for CLI11, Clang 3.4 only fails because of tests, googlemock does not support it.)
* Work on Linux, MacOS, and Windows.
* Well tested using Travis (Linux and Mac) and [Appveyor](https://ci.appveyor.com/project/HenrySchreiner/cli11) (Windows).
* Clear help printing.
* Standard shell idioms supported naturally, like grouping flags, a positional separator, etc.
* Easy to execute, with help, parse errors, etc. providing correct exit and details.

View File

@ -4,6 +4,7 @@
# gives output on failed tests without having to set an environment variable.
#
#
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
include(DownloadProject)
download_project(PROJ googletest
@ -13,6 +14,7 @@ download_project(PROJ googletest
QUIET
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_SOURCE_DIR})

View File

@ -112,9 +112,9 @@ public:
}
/// Add a subcommand. Like the constructor, you can override the help message addition by setting help=false
App* add_subcommand(std::string name, std::string description="", bool help=true) {
App* add_subcommand(std::string name_, std::string description="", bool help=true) {
subcommands.emplace_back(new App(description, help));
subcommands.back()->name = name;
subcommands.back()->name = name_;
return subcommands.back().get();
}
@ -132,17 +132,17 @@ public:
* program.add_option("filename", filename, "description of filename");
*/
Option* add_option(
std::string name,
std::string name_,
callback_t callback,
std::string description="",
bool defaulted=false
) {
Option myopt{name, description, callback, defaulted};
Option myopt{name_, description, callback, defaulted};
if(std::find_if(std::begin(options), std::end(options),
[&myopt](const Option_p &v){return *v == myopt;}) == std::end(options)) {
options.emplace_back();
Option_p& option = options.back();
option.reset(new Option(name, description, callback, defaulted));
option.reset(new Option(name_, description, callback, defaulted));
return option.get();
} else
throw OptionAlreadyAdded(myopt.get_name());
@ -152,7 +152,7 @@ public:
/// Add option for string
template<typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
Option* add_option(
std::string name,
std::string name_,
T &variable, ///< The variable to set
std::string description="",
bool defaulted=false
@ -169,7 +169,7 @@ public:
return detail::lexical_cast(res[0][0], variable);
};
Option* retval = add_option(name, fun, description, defaulted);
Option* retval = add_option(name_, fun, description, defaulted);
retval->typeval = detail::type_name<T>();
if(defaulted) {
std::stringstream out;
@ -182,7 +182,7 @@ public:
/// Add option for vector of results
template<typename T>
Option* add_option(
std::string name,
std::string name_,
std::vector<T> &variable, ///< The variable vector to set
std::string description="",
bool defaulted=false
@ -199,7 +199,7 @@ public:
return variable.size() > 0 && retval;
};
Option* retval = add_option(name, fun, description, defaulted);
Option* retval = add_option(name_, fun, description, defaulted);
retval->allow_vector = true;
retval->_expected = -1;
retval->typeval = detail::type_name<T>();
@ -211,14 +211,14 @@ public:
/// Add option for flag
Option* add_flag(
std::string name,
std::string name_,
std::string description=""
) {
CLI::callback_t fun = [](CLI::results_t){
return true;
};
Option* opt = add_option(name, fun, description, false);
Option* opt = add_option(name_, fun, description, false);
if(opt->get_positional())
throw IncorrectConstruction("Flags cannot be positional");
opt->_expected = 0;
@ -229,7 +229,7 @@ public:
template<typename T,
enable_if_t<std::is_integral<T>::value && !is_bool<T>::value, detail::enabler> = detail::dummy>
Option* add_flag(
std::string name,
std::string name_,
T &count, ///< A varaible holding the count
std::string description=""
) {
@ -240,7 +240,7 @@ public:
return true;
};
Option* opt = add_option(name, fun, description, false);
Option* opt = add_option(name_, fun, description, false);
if(opt->get_positional())
throw IncorrectConstruction("Flags cannot be positional");
opt->_expected = 0;
@ -251,7 +251,7 @@ public:
template<typename T,
enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy>
Option* add_flag(
std::string name,
std::string name_,
T &count, ///< A varaible holding true if passed
std::string description=""
) {
@ -262,7 +262,7 @@ public:
return res.size() == 1;
};
Option* opt = add_option(name, fun, description, false);
Option* opt = add_option(name_, fun, description, false);
if(opt->get_positional())
throw IncorrectConstruction("Flags cannot be positional");
opt->_expected = 0;
@ -273,7 +273,7 @@ public:
/// Add set of options
template<typename T>
Option* add_set(
std::string name,
std::string name_,
T &member, ///< The selected member of the set
std::set<T> _options, ///< The set of posibilities
std::string description="",
@ -293,7 +293,7 @@ public:
return std::find(std::begin(_options), std::end(_options), member) != std::end(_options);
};
Option* retval = add_option(name, fun, description, defaulted);
Option* retval = add_option(name_, fun, description, defaulted);
retval->typeval = detail::type_name<T>();
retval->typeval += " in {" + detail::join(_options) + "}";
if(defaulted) {
@ -306,7 +306,7 @@ public:
/// Add a configuration ini file option
Option* add_config(std::string name="--config",
Option* add_config(std::string name_="--config",
std::string default_filename="",
std::string help="Read an ini file",
bool required=false) {
@ -316,7 +316,7 @@ public:
remove_option(ini_setting);
ini_file = default_filename;
ini_required = required;
ini_setting = add_option(name, ini_file, help, default_filename!="");
ini_setting = add_option(name_, ini_file, help, default_filename!="");
return ini_setting;
}
@ -366,13 +366,13 @@ public:
}
/// Counts the number of times the given option was passed.
int count(std::string name) const {
int count(std::string name_) const {
for(const Option_p &opt : options) {
if(opt->check_name(name)) {
if(opt->check_name(name_)) {
return opt->count();
}
}
throw OptionNotFound(name);
throw OptionNotFound(name_);
}
/// Makes a help message, with a column `wid` for column 1
@ -547,7 +547,7 @@ protected:
_parse_long(values, false);
}
} catch (const FileError &e) {
} catch (const FileError &) {
if(ini_required)
throw;
}
@ -615,16 +615,16 @@ protected:
void _parse_short(std::vector<std::string> &args) {
std::string current = args.back();
std::string name;
std::string name_;
std::string rest;
if(!detail::split_short(current, name, rest))
if(!detail::split_short(current, name_, rest))
throw HorribleError("Short");
args.pop_back();
auto op_ptr = std::find_if(std::begin(options), std::end(options), [name](const Option_p &opt){return opt->check_sname(name);});
auto op_ptr = std::find_if(std::begin(options), std::end(options), [name_](const Option_p &opt){return opt->check_sname(name_);});
if(op_ptr == std::end(options)) {
missing_options.push_back("-" + name);
missing_options.push_back("-" + name_);
return;
}
@ -651,9 +651,9 @@ protected:
}
} else while(num>0 && args.size() > 0) {
num--;
std::string current = args.back();
std::string current_ = args.back();
args.pop_back();
op->add_result(vnum,current);
op->add_result(vnum, current_);
}
if(rest != "") {
@ -666,16 +666,16 @@ protected:
void _parse_long(std::vector<std::string> &args, bool overwrite=true) {
std::string current = args.back();
std::string name;
std::string name_;
std::string value;
if(!detail::split_long(current, name, value))
if(!detail::split_long(current, name_, value))
throw HorribleError("Long");
args.pop_back();
auto op_ptr = std::find_if(std::begin(options), std::end(options), [name](const Option_p &v){return v->check_lname(name);});
auto op_ptr = std::find_if(std::begin(options), std::end(options), [name_](const Option_p &v){return v->check_lname(name_);});
if(op_ptr == std::end(options)) {
missing_options.push_back("--" + name);
missing_options.push_back("--" + name_);
return;
}

View File

@ -94,13 +94,13 @@ void format_help(std::stringstream &out, std::string name, std::string descripti
/// Verify the first character of an option
template<typename T>
bool valid_first_char(T c) {
return std::isalpha(c) || c=='_';
return std::isalpha(c, std::locale::classic()) || c=='_';
}
/// Verify following characters of an option
template<typename T>
bool valid_later_char(T c) {
return std::isalnum(c) || c=='_' || c=='.' || c=='-';
return std::isalnum(c, std::locale::classic()) || c=='_' || c=='.' || c=='-';
}
/// Verify an option name

View File

@ -27,7 +27,7 @@ namespace CLI {
bool ExistingFile(std::string filename) {
struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0;
bool is_dir = buffer.st_mode & S_IFDIR;
bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
if(!exist) {
std::cerr << "File does not exist: " << filename << std::endl;
return false;
@ -43,7 +43,7 @@ bool ExistingFile(std::string filename) {
bool ExistingDirectory(std::string filename) {
struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0;
bool is_dir = buffer.st_mode & S_IFDIR;
bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
if(!exist) {
std::cerr << "Directory does not exist: " << filename << std::endl;
return false;

View File

@ -15,6 +15,8 @@ includes_system = re.compile(r"""^#include \<(.*)\>$""", re.MULTILINE)
DIR = Path(__file__).resolve().parent
BDIR = DIR.parent / 'include'
print("Git directory:", DIR)
TAG = check_output(['git', 'describe', '--tags', '--always'], cwd=str(DIR)).decode("utf-8")
def MakeHeader(out):

View File

@ -250,7 +250,7 @@ TEST_F(TApp, Reset) {
EXPECT_EQ(1, app.count("--simple"));
EXPECT_EQ(1, app.count("-d"));
EXPECT_FLOAT_EQ(1.2, doub);
EXPECT_DOUBLE_EQ(1.2, doub);
app.reset();
@ -261,7 +261,7 @@ TEST_F(TApp, Reset) {
EXPECT_EQ(1, app.count("--simple"));
EXPECT_EQ(1, app.count("-d"));
EXPECT_FLOAT_EQ(1.2, doub);
EXPECT_DOUBLE_EQ(1.2, doub);
}
@ -489,7 +489,7 @@ TEST_F(TApp, RequiresChainedFlags) {
TEST_F(TApp, Env) {
setenv("CLI11_TEST_ENV_TMP", "2", true);
put_env("CLI11_TEST_ENV_TMP", "2");
int val=1;
CLI::Option* vopt = app.add_option("--tmp", val)->envname("CLI11_TEST_ENV_TMP");
@ -504,7 +504,7 @@ TEST_F(TApp, Env) {
EXPECT_NO_THROW(run());
app.reset();
unsetenv("CLI11_TEST_ENV_TMP");
unset_env("CLI11_TEST_ENV_TMP");
EXPECT_THROW(run(), CLI::RequiredError);
}

View File

@ -42,3 +42,20 @@ public:
operator const std::string& () const {return _name;}
const char* c_str() const {return _name.c_str();}
};
inline void put_env(std::string name, std::string value) {
#ifdef _MSC_VER
_putenv_s(name.c_str(), value.c_str());
#else
setenv(name.c_str(), value.c_str(), 1);
#endif
}
inline void unset_env(std::string name) {
#ifdef _MSC_VER
_putenv_s(name.c_str(), "");
#else
unsetenv(name.c_str());
#endif
}