diff --git a/CHANGELOG.md b/CHANGELOG.md index f9632156..d5dbb631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ ## Version 1.1 (in progress) -* Added support for basic enumerations [#12](https://github.com/CLIUtils/CLI11/issues/12) -* Added `app.parse_order()` with original parse order -* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns. + +* Added simple support for enumerations, allow non-printable objects [#12](https://github.com/CLIUtils/CLI11/issues/12) +* Added `app.parse_order()` with original parse order ([#13](https://github.com/CLIUtils/CLI11/issues/13), [#16](https://github.com/CLIUtils/CLI11/pull/16)) +* Added `prefix_command()`, which is like `allow_extras` but instantly stops and returns. ([#8](https://github.com/CLIUtils/CLI11/issues/8), [#17](https://github.com/CLIUtils/CLI11/pull/17)) +* Removed Windows error ([#10](https://github.com/CLIUtils/CLI11/issues/10), [#20](https://github.com/CLIUtils/CLI11/pull/20)) +* Some improvements to CMake, detect Python and no dependencies on Python 2 (like Python 3) ([#18](https://github.com/CLIUtils/CLI11/issues/18), [#21](https://github.com/CLIUtils/CLI11/pull/21)) ## Version 1.0 * Cleanup using `clang-tidy` and `clang-format` diff --git a/CMakeLists.txt b/CMakeLists.txt index a5666a7e..f8b8e0b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,6 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) # Be moderately paranoid with flags if(MSVC) add_definitions("/W4") - add_definitions(-D_CRT_SECURE_NO_WARNINGS) else() add_definitions("-Wall -Wextra -pedantic") endif() @@ -54,11 +53,18 @@ add_library(CLI11 INTERFACE) target_include_directories(CLI11 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") # Single file test -option(CLI_SINGLE_FILE "Generate a single header file (and test)" ${CUR_PROJ}) +find_package(PythonInterp) +if(CUR_PROJ AND PYTHONINTERP_FOUND) + set(CLI_SINGLE_FILE_DEFAULT ON) +else() + set(CLI_SINGLE_FILE_DEFAULT OFF) +endif() +option(CLI_SINGLE_FILE "Generate a single header file (and test)" ${CLI_SINGLE_FILE_DEFAULT}) if(CLI_SINGLE_FILE) + find_package(PythonInterp REQUIRED) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include") add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" - COMMAND python "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py" "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" + COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py" "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp" ${CLI_headers} ) add_custom_target(generate_cli_single_file ALL diff --git a/README.md b/README.md index 42a32f5d..8ce93cac 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ CLI11 provides all the features you expect in a powerful command line parser, with a beautiful, minimal syntax and no dependencies beyond C++11. It is header only, and comes in a single file form for easy inclusion in projects. It is easy to use for small projects, but powerful enough for complex command line projects, and can be customized for frameworks. It is tested on [Travis] and [AppVeyor], and is being included in the [GooFit GPU fitting framework][GooFit]. It was inspired by [`plumbum.cli`][Plumbum] for Python. CLI11 has a user friendly introduction in this README, a more indepth tutorial [GitBook], as well as [API documentation][api-docs] generated by Travis. -See the [changelog](./CHANGELOG.md) or [GitHub Releases] for details for current and past releases. The 1.0 announcement post can be found [here](http://iscinumpy.blogspot.com/2017/06/announcing-cli11-version-10.html). +See the [changelog](./CHANGELOG.md) or [GitHub Releases] for details for current and past releases. The version 1.0 announcement post can be found [here](http://iscinumpy.blogspot.com/2017/06/announcing-cli11-version-10.html). ### Why write another CLI parser? @@ -126,7 +126,7 @@ app.add_set_ignore_case(... // String only App* subcom = app.add_subcommand(name, discription); ``` -An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option` or `add_set`. +An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option` or `add_set`. The set options allow your users to pick from a set of predefined options. ### Example diff --git a/cmake/AddGoogletest.cmake b/cmake/AddGoogletest.cmake index e18ce48a..5e026cca 100644 --- a/cmake/AddGoogletest.cmake +++ b/cmake/AddGoogletest.cmake @@ -46,6 +46,8 @@ gtest_build_tests gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols +BUILD_GMOCK +BUILD_GTEST ) set_target_properties(gtest gtest_main gmock gmock_main diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index b9dc4f63..02863cf1 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -909,9 +909,25 @@ class App { // Get envname options if not yet passed for(const Option_p &opt : options_) { if(opt->count() == 0 && opt->envname_ != "") { - char *ename = std::getenv(opt->envname_.c_str()); - if(ename != nullptr) { - opt->add_result(std::string(ename)); + char *buffer = nullptr; + std::string ename_string; + + #ifdef _MSC_VER + // Windows version + size_t sz = 0; + if(_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 && buffer != nullptr) { + ename_string = std::string(buffer); + free(buffer); + } + #else + // This also works on Windows, but gives a warning + buffer = std::getenv(opt->envname_.c_str()); + if(buffer != nullptr) + ename_string = std::string(buffer); + #endif + + if(!ename_string.empty()) { + opt->add_result(ename_string); } } } diff --git a/scripts/MakeSingleHeader.py b/scripts/MakeSingleHeader.py index 15760929..23c2de3f 100755 --- a/scripts/MakeSingleHeader.py +++ b/scripts/MakeSingleHeader.py @@ -4,24 +4,24 @@ from __future__ import print_function, unicode_literals +import os import re import argparse -from pathlib import Path from subprocess import check_output includes_local = re.compile(r"""^#include "(.*)"$""", re.MULTILINE) includes_system = re.compile(r"""^#include \<(.*)\>$""", re.MULTILINE) -DIR = Path(__file__).resolve().parent -BDIR = DIR.parent / 'include' +DIR = os.path.dirname(os.path.abspath(__file__)) # Path(__file__).resolve().parent +BDIR = os.path.join(os.path.dirname(DIR), 'include') # DIR.parent / 'include' print("Git directory:", DIR) TAG = check_output(['git', 'describe', '--tags', '--always'], cwd=str(DIR)).decode("utf-8") def MakeHeader(out): - main_header = BDIR / 'CLI/CLI.hpp' - with main_header.open() as f: + main_header = os.path.join(BDIR, 'CLI', 'CLI.hpp') + with open(main_header) as f: header = f.read() include_files = includes_local.findall(header) @@ -29,7 +29,7 @@ def MakeHeader(out): headers = set() output = '' for inc in include_files: - with (BDIR / inc).open() as f: + with open(os.path.join(BDIR, inc)) as f: inner = f.read() headers |= set(includes_system.findall(inner)) output += '\n// From {inc}\n\n'.format(inc=inc) @@ -50,7 +50,7 @@ def MakeHeader(out): {header_list} {output}'''.format(header_list=header_list, output=output, tag=TAG) - with Path(out).open('w') as f: + with open(out, 'w') as f: f.write(output) print("Created {out}".format(out=out)) @@ -58,6 +58,6 @@ def MakeHeader(out): if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument("output", nargs='?', default=BDIR / 'CLI11.hpp') + parser.add_argument("output", nargs='?', default=os.path.join(BDIR, 'CLI11.hpp')) args = parser.parse_args() MakeHeader(args.output)