Commit 79a184760591a82e0e68354246e476e3f0f25c71

Authored by Henry Schreiner
Committed by GitHub
1 parent 31636306

Overflow (#84)

* Overflowing an integer conversion now results in a conversion failure

* CMake improvements for VERSION and inital packing support

* Fix for recent addition of overflow check

* Conan file now gets version from Version.hpp file too
CMakeLists.txt
1 cmake_minimum_required(VERSION 3.4 FATAL_ERROR) 1 cmake_minimum_required(VERSION 3.4 FATAL_ERROR)
2 2
3 -project(CLI11 LANGUAGES CXX) 3 +set(VERSION_REGEX "#define CLI11_VERSION[ \t]+\"(.+)\"")
4 4
5 -SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) 5 +# Read in the line containing the version
  6 +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/Version.hpp"
  7 + VERSION_STRING REGEX ${VERSION_REGEX})
6 8
  9 +# Pick out just the version
  10 +string(REGEX REPLACE ${VERSION_REGEX} "\\1" VERSION_STRING "${VERSION_STRING}")
  11 +
  12 +project(CLI11 LANGUAGES CXX VERSION ${VERSION_STRING})
  13 +
  14 +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
  15 +
  16 +# User settable
7 set(CLI_CXX_STD "11" CACHE STRING "The CMake standard to require") 17 set(CLI_CXX_STD "11" CACHE STRING "The CMake standard to require")
8 18
9 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 19 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
@@ -70,6 +80,18 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/CLI DESTINATION include) @@ -70,6 +80,18 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/CLI DESTINATION include)
70 # to Config.cmake (otherwise we'd have a custom config and would 80 # to Config.cmake (otherwise we'd have a custom config and would
71 # import Targets.cmake 81 # import Targets.cmake
72 82
  83 +# Add the version in a CMake readable way
  84 +include(CMakePackageConfigHelpers)
  85 +write_basic_package_version_file(
  86 + CLI11ConfigVersion.cmake
  87 + VERSION ${CLI11_VERSION}
  88 + COMPATIBILITY AnyNewerVersion
  89 + )
  90 +
  91 +# Make version available in the install
  92 +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/CLI11ConfigVersion.cmake"
  93 + DESTINATION lib/cmake/CLI11)
  94 +
73 # Make an export target 95 # Make an export target
74 install(TARGETS CLI11 96 install(TARGETS CLI11
75 EXPORT CLI11Targets) 97 EXPORT CLI11Targets)
@@ -129,3 +151,24 @@ if(CLI_EXAMPLES) @@ -129,3 +151,24 @@ if(CLI_EXAMPLES)
129 add_subdirectory(examples) 151 add_subdirectory(examples)
130 endif() 152 endif()
131 153
  154 +# Packaging support
  155 +set(CPACK_PACKAGE_VENDOR "github.com/CLIUtils/CLI11")
  156 +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Command line interface")
  157 +set(CPACK_PACKAGE_VERSION_MAJOR ${CLI11_VERSION_MAJOR})
  158 +set(CPACK_PACKAGE_VERSION_MINOR ${CLI11_VERSION_MINOR})
  159 +set(CPACK_PACKAGE_VERSION_PATCH ${CLI11_VERSION_PATCH})
  160 +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
  161 +set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
  162 +set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
  163 +# CPack collects *everything* except what's listed here.
  164 +set(CPACK_SOURCE_IGNORE_FILES
  165 + /.git
  166 + /dist
  167 + /.*build.*
  168 + /\\\\.DS_Store
  169 + /.*\\\\.egg-info
  170 + /var
  171 + /Pipfile.*$
  172 +)
  173 +include(CPack)
  174 +
cmake/AddGoogletest.cmake
@@ -15,7 +15,11 @@ download_project(PROJ googletest @@ -15,7 +15,11 @@ download_project(PROJ googletest
15 ) 15 )
16 16
17 set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 17 set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
  18 +
  19 +# CMake warning suppression will not be needed in version 1.9
  20 +set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "")
18 add_subdirectory(${googletest_SOURCE_DIR} ${googletest_SOURCE_DIR} EXCLUDE_FROM_ALL) 21 add_subdirectory(${googletest_SOURCE_DIR} ${googletest_SOURCE_DIR} EXCLUDE_FROM_ALL)
  22 +unset(CMAKE_SUPPRESS_DEVELOPER_WARNINGS)
19 23
20 if (CMAKE_CONFIGURATION_TYPES) 24 if (CMAKE_CONFIGURATION_TYPES)
21 add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} 25 add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
conanfile.py
1 from conans import ConanFile, CMake 1 from conans import ConanFile, CMake
  2 +from conans.tools import load
  3 +import re
  4 +
  5 +def get_version():
  6 + try:
  7 + content = load("include/CLI/Version.hpp")
  8 + version = re.search(r'#define CLI11_VERSION "(.*)"', content).group(1)
  9 + return version
  10 + except Exception:
  11 + return None
2 12
3 class HelloConan(ConanFile): 13 class HelloConan(ConanFile):
4 name = "CLI11" 14 name = "CLI11"
5 - version = "1.3.0" 15 + version = get_version()
6 url = "https://github.com/CLIUtils/CLI11" 16 url = "https://github.com/CLIUtils/CLI11"
7 settings = "os", "compiler", "arch", "build_type" 17 settings = "os", "compiler", "arch", "build_type"
8 license = "BSD 3 clause" 18 license = "BSD 3 clause"
9 description = "Command Line Interface toolkit for C++11" 19 description = "Command Line Interface toolkit for C++11"
10 20
11 - exports_sources = "LICENCE", "include/*", "cmake/*", "CMakeLists.txt", "tests/*" 21 + exports_sources = "LICENSE", "README.md", "include/*", "cmake/*", "CMakeLists.txt", "tests/*"
12 22
13 def build(self): # this is not building a library, just tests 23 def build(self): # this is not building a library, just tests
14 cmake = CMake(self) 24 cmake = CMake(self)
include/CLI/TypeTools.hpp
@@ -79,8 +79,9 @@ template <typename T, @@ -79,8 +79,9 @@ template <typename T,
79 bool lexical_cast(std::string input, T &output) { 79 bool lexical_cast(std::string input, T &output) {
80 try { 80 try {
81 size_t n = 0; 81 size_t n = 0;
82 - output = static_cast<T>(std::stoll(input, &n, 0));  
83 - return n == input.size(); 82 + long long output_ll = std::stoll(input, &n, 0);
  83 + output = static_cast<T>(output_ll);
  84 + return n == input.size() && static_cast<long long>(output) == output_ll;
84 } catch(const std::invalid_argument &) { 85 } catch(const std::invalid_argument &) {
85 return false; 86 return false;
86 } catch(const std::out_of_range &) { 87 } catch(const std::out_of_range &) {
@@ -97,8 +98,9 @@ bool lexical_cast(std::string input, T &amp;output) { @@ -97,8 +98,9 @@ bool lexical_cast(std::string input, T &amp;output) {
97 98
98 try { 99 try {
99 size_t n = 0; 100 size_t n = 0;
100 - output = static_cast<T>(std::stoull(input, &n, 0));  
101 - return n == input.size(); 101 + unsigned long long output_ll = std::stoull(input, &n, 0);
  102 + output = static_cast<T>(output_ll);
  103 + return n == input.size() && static_cast<unsigned long long>(output) == output_ll;
102 } catch(const std::invalid_argument &) { 104 } catch(const std::invalid_argument &) {
103 return false; 105 return false;
104 } catch(const std::out_of_range &) { 106 } catch(const std::out_of_range &) {
tests/HelpersTest.cpp
@@ -348,6 +348,16 @@ TEST(Types, TypeName) { @@ -348,6 +348,16 @@ TEST(Types, TypeName) {
348 EXPECT_EQ("TEXT", text2_name); 348 EXPECT_EQ("TEXT", text2_name);
349 } 349 }
350 350
  351 +TEST(Types, OverflowSmall) {
  352 + char x;
  353 + auto strmax = std::to_string(INT8_MAX + 1);
  354 + EXPECT_FALSE(CLI::detail::lexical_cast(strmax, x));
  355 +
  356 + unsigned char y;
  357 + strmax = std::to_string(UINT8_MAX + 1);
  358 + EXPECT_FALSE(CLI::detail::lexical_cast(strmax, y));
  359 +}
  360 +
351 TEST(Types, LexicalCastInt) { 361 TEST(Types, LexicalCastInt) {
352 std::string signed_input = "-912"; 362 std::string signed_input = "-912";
353 int x_signed; 363 int x_signed;
@@ -357,7 +367,7 @@ TEST(Types, LexicalCastInt) { @@ -357,7 +367,7 @@ TEST(Types, LexicalCastInt) {
357 std::string unsigned_input = "912"; 367 std::string unsigned_input = "912";
358 unsigned int x_unsigned; 368 unsigned int x_unsigned;
359 EXPECT_TRUE(CLI::detail::lexical_cast(unsigned_input, x_unsigned)); 369 EXPECT_TRUE(CLI::detail::lexical_cast(unsigned_input, x_unsigned));
360 - EXPECT_EQ(912, x_unsigned); 370 + EXPECT_EQ((unsigned int)912, x_unsigned);
361 371
362 EXPECT_FALSE(CLI::detail::lexical_cast(signed_input, x_unsigned)); 372 EXPECT_FALSE(CLI::detail::lexical_cast(signed_input, x_unsigned));
363 373