diff --git a/.travis.yml b/.travis.yml index d3e842c..90e7c68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,18 +5,32 @@ matrix: include: - os: linux env: - - COMPILER=g++-5 STDLIB=libc++ + - COMPILER=g++-7 addons: apt: - sources: ['ubuntu-toolchain-r-test', 'george-edison55-precise-backports'] - packages: ["g++-5", "cmake-data", "cmake"] + sources: ['ubuntu-toolchain-r-test'] + packages: ["g++-7", "cmake-data", "cmake"] - os: linux env: - - COMPILER=g++-4.8 USE_BOOST_REGEX=ON + - COMPILER=g++-8 addons: apt: - sources: ['ubuntu-toolchain-r-test', 'george-edison55-precise-backports', 'boost-latest'] - packages: ["g++-4.8", "cmake-data", "cmake", "libboost-regex1.55-dev"] + sources: ['ubuntu-toolchain-r-test'] + packages: ["g++-8", "cmake-data", "cmake"] + - os: linux + env: + - COMPILER=g++-9 + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ["g++-9", "cmake-data", "cmake"] + - os: linux + env: + - COMPILER=g++-9 USE_BOOST_REGEX=ON + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ["g++-9", "cmake-data", "cmake", "libboost-regex-dev"] - os: linux env: @@ -28,41 +42,54 @@ matrix: - os: linux env: - - COMPILER=clang++-3.7 STDLIB=libc++ + - COMPILER=clang++-8 STDLIB=libc++ addons: apt: - sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7', 'george-edison55-precise-backports'] - packages: ["clang-3.7", "cmake-data", "cmake"] + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-8'] + packages: ["clang-8", "cmake-data", "cmake"] - os: osx - osx_image: xcode6.4 + osx_image: xcode9.4 env: - - COMPILER=clang++ V='Apple LLVM 6.4' - - COMPILER=clang++ V='Apple LLVM 6.4' WITH_CPP14=true + - COMPILER=clang++ V='Apple LLVM 9.1' + - COMPILER=clang++ V='Apple LLVM 9.1' WITH_CPP14=true - os: osx - osx_image: xcode7 + osx_image: xcode10.3 env: - - COMPILER=clang++ V='Apple LLVM 7.0' - - COMPILER=clang++ V='Apple LLVM 7.0' WITH_CPP14=true + - COMPILER=clang++ V='Apple LLVM 10.0' + - COMPILER=clang++ V='Apple LLVM 10.0' WITH_CPP14=true + - os: osx + osx_image: xcode11.2 + env: + - COMPILER=clang++ V='Apple LLVM 11.0' + - COMPILER=clang++ V='Apple LLVM 11.0' WITH_CPP14=true + - os: osx + osx_image: xcode11.2 + env: + - COMPILER=clang++ V='Apple LLVM 11.0' + - COMPILER=clang++ V='Apple LLVM 11.0' WITH_CPP17=true before_install: + - CMAKE_CXX_FLAGS+=" -Wall" - | - if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then - brew rm --force cmake && brew install cmake + if [[ "${WITH_CPP14}" == "true" ]]; then + CMAKE_OPTIONS+=" -DCMAKE_CXX_STANDARD=14" + fi + - | + if [[ "${WITH_CPP17}" == "true" ]]; then + CMAKE_OPTIONS+=" -DCMAKE_CXX_STANDARD=17" fi - - - CMAKE_CXX_FLAGS+=" -Wall" - - - if [[ "${WITH_CPP14}" == "true" ]]; then CMAKE_OPTIONS+=" -DCMAKE_CXX_STANDARD=14"; fi - | if [[ "${USE_BOOST_REGEX}" == "ON" ]]; then CMAKE_OPTIONS+=" -DUSE_BOOST_REGEX=ON" - CMAKE_OPTIONS+=" -DBoost_REGEX_LIBRARY_DEBUG=/usr/lib/x86_64-linux-gnu/libboost_regex.so.1.55.0" - CMAKE_OPTIONS+=" -DBoost_REGEX_LIBRARY_RELEASE=/usr/lib/x86_64-linux-gnu/libboost_regex.so.1.55.0" + CMAKE_OPTIONS+=" -DBoost_REGEX_LIBRARY_DEBUG=/usr/lib/x86_64-linux-gnu/libboost_regex.so" + CMAKE_OPTIONS+=" -DBoost_REGEX_LIBRARY_RELEASE=/usr/lib/x86_64-linux-gnu/libboost_regex.so" + fi + - | + if [[ "${STDLIB}" == "libc++" ]]; then + CMAKE_CXX_FLAGS+=" -stdlib=libc++" fi - - if [[ "${STDLIB}" == "libc++" ]]; then CMAKE_CXX_FLAGS+=" -stdlib=libc++"; fi - - ${COMPILER} --version before_script: diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e292d8..28da614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.1) -project(docopt.cpp VERSION 0.6.1) +project(docopt.cpp VERSION 0.6.2) include(GNUInstallDirs) @@ -20,11 +20,6 @@ if(NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 11) set(CMAKE_CXX_STANDARD 11) endif() -# Suppression of "unknown pragma" warning on GCC -if(CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # Code uses #pragma mark -endif() - #============================================================================ # Sources & headers #============================================================================ @@ -39,18 +34,21 @@ set(docopt_HEADERS #============================================================================ # Compile targets #============================================================================ -add_library(docopt_o OBJECT ${docopt_SOURCES} ${docopt_HEADERS}) -set_target_properties(docopt_o PROPERTIES POSITION_INDEPENDENT_CODE TRUE) - -add_library(docopt SHARED $) -add_library(docopt_s STATIC $) +add_library(docopt ${docopt_SOURCES} ${docopt_HEADERS}) +set_target_properties(docopt PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} +) target_include_directories(docopt PUBLIC $ $) -target_include_directories(docopt_s PUBLIC $ $) -if(NOT MSVC) - set_target_properties(docopt PROPERTIES OUTPUT_NAME docopt) - set_target_properties(docopt_s PROPERTIES OUTPUT_NAME docopt) +if(MSVC AND BUILD_SHARED_LIBS) + # DOCOPT_DLL: Must be specified when building *and* when using the DLL. + # That's what the "PUBLIC" means. + # DOCOPT_EXPORTS: Must use __declspec(dllexport) when building the DLL. + # "PRIVATE" means it's only defined when building the DLL. + target_compile_definitions(docopt PUBLIC DOCOPT_DLL + PRIVATE DOCOPT_EXPORTS) endif() if(USE_BOOST_REGEX) @@ -61,9 +59,6 @@ if(USE_BOOST_REGEX) find_package(Boost 1.53 REQUIRED COMPONENTS regex) include_directories(${Boost_INCLUDE_DIRS}) target_link_libraries(docopt ${Boost_LIBRARIES}) - if(WITH_STATIC) - target_link_libraries(docopt_s ${Boost_LIBRARIES}) - endif() endif() #============================================================================ @@ -96,17 +91,22 @@ endif() set(export_name "docopt-targets") # Runtime package -install(TARGETS docopt EXPORT ${export_name} DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(TARGETS docopt EXPORT ${export_name} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) # Development package -install(TARGETS docopt_s EXPORT ${export_name} DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES ${docopt_HEADERS} DESTINATION include/docopt) # CMake Package include(CMakePackageConfigHelpers) write_basic_package_version_file("${PROJECT_BINARY_DIR}/docopt-config-version.cmake" COMPATIBILITY SameMajorVersion) -install(FILES docopt-config.cmake ${PROJECT_BINARY_DIR}/docopt-config-version.cmake DESTINATION "lib/cmake/docopt") -install(EXPORT ${export_name} DESTINATION "lib/cmake/docopt") +install(FILES docopt-config.cmake ${PROJECT_BINARY_DIR}/docopt-config-version.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/docopt") +install(EXPORT ${export_name} DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/docopt") + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docopt.pc.in ${CMAKE_CURRENT_BINARY_DIR}/docopt.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/docopt.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) #============================================================================ # CPack diff --git a/README.rst b/README.rst index b7e871e..5fe8b4f 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,13 @@ ``docopt.cpp``: A C++11 Port ============================ + +Contents +-------- + +.. contents:: + :local: + :depth: 3 + docopt creates *beautiful* command-line interfaces -------------------------------------------------- @@ -58,13 +66,40 @@ and instead can write only the help message--*the way you want it*. Beat that! The option parser is generated based on the docstring above that is passed to ``docopt::docopt`` function. ``docopt`` parses the usage pattern (``"Usage: ..."``) and option descriptions (lines starting -with dash "``-``") and ensures that the program invocation matches the +with a dash "``-``") and ensures that the program invocation matches the usage pattern; it parses options, arguments and commands based on that. The basic idea is that *a good help message has all necessary information in it to make a parser*. +Getting and using +----------------- + +To get *docopt.cpp*, the simplest is to use `Conda `_:: + + conda install -c conda-forge docopt.cpp + +Alternatively manual installation is done using (unix):: + + git clone + cmake . + make install + +To link *docopt.cpp*, the simplest is to use CMake. The general structure of your +``CMakeLists.txt`` would be as follows:: + + cmake_minimum_required(VERSION 3.1) + + project(example) + + find_package(docopt COMPONENTS CXX REQUIRED) + include_directories(${DOCOPT_INCLUDE_DIRS}) + + add_executable(${PROJECT_NAME} ...) + + target_link_libraries(${PROJECT_NAME} docopt) + C++11 port details ---------------------------------------------------- +------------------ This is a port of the ``docopt.py`` module (https://github.com/docopt/docopt), and we have tried to maintain full feature parity (and code structure) as the @@ -80,7 +115,7 @@ to work with docopt: GCC-4.8 can work, but the std::regex module needs to be replaced with ``Boost.Regex``. In that case, you will need to define ``DOCTOPT_USE_BOOST_REGEX`` when compiling -docopt, and link your code with the appropriated Boost libraries. A relativley +docopt, and link your code with the appropriated Boost libraries. A relatively recent version of Boost is needed: 1.55 works, but 1.46 does not for example. This port is licensed under the MIT license, just like the original module. @@ -101,7 +136,7 @@ The differences from the Python port are: some of the regex's had to be restructured and additional loops used. API ---------------------------------------------------- +--- .. code:: c++ @@ -182,16 +217,15 @@ If any parsing error (in either the usage, or due to incorrect user inputs) is encountered, the program will exit with exit code -1. Note that there is another function that does not exit on error, and instead will -propogate an exception that you can catch and process as you like. See the docopt.h file +propagate an exception that you can catch and process as you like. See the docopt.h file for information on the exceptions and usage: .. code:: c++ docopt::docopt_parse(doc, argv, help /* =true */, version /* =true */, options_first /* =false) - Help message format ---------------------------------------------------- +------------------- Help message consists of 2 parts: @@ -210,7 +244,7 @@ Help message consists of 2 parts: Their format is described below; other text is ignored. Usage pattern format ----------------------------------------------------------------------- +-------------------- **Usage pattern** is a substring of ``doc`` that starts with ``usage:`` (case *insensitive*) and ends with a *visibly* empty line. @@ -263,7 +297,7 @@ Use the following constructs to specify patterns: - **|** (pipe) **mutually exclusive** elements. Group them using **( )** if one of the mutually exclusive elements is required: ``my_program (--clockwise | --counter-clockwise) TIME``. Group - them using **[ ]** if none of the mutually-exclusive elements are + them using **[ ]** if none of the mutually exclusive elements are required: ``my_program [--left | --right]``. - **...** (ellipsis) **one or more** elements. To specify that arbitrary number of repeating elements could be accepted, use @@ -291,7 +325,7 @@ then number of occurrences of the option will be counted. I.e. ``args['-v']`` will be ``2`` if program was invoked as ``my_program -vv``. Same works for commands. -If your usage patterns allows to match same-named option with argument +If your usage pattern allows to match same-named option with argument or positional argument several times, the matched arguments will be collected into a list:: @@ -301,9 +335,8 @@ I.e. invoked with ``my_program file1 file2 --path=./here --path=./there`` the returned dict will contain ``args[''] == ['file1', 'file2']`` and ``args['--path'] == ['./here', './there']``. - Option descriptions format ----------------------------------------------------------------------- +-------------------------- **Option descriptions** consist of a list of options that you put below your usage patterns. @@ -328,7 +361,7 @@ The rules are as follows: argument after space (or equals "``=``" sign) as shown below. Follow either or UPPER-CASE convention for options' arguments. You can use comma if you want to separate options. In - the example below, both lines are valid, however you are recommended + the example below, both lines are valid. However you are recommended to stick to a single style.:: -o FILE --output=FILE # without comma, with "=" sign @@ -352,7 +385,7 @@ The rules are as follows: - If the option is not repeatable, the value inside ``[default: ...]`` will be interpreted as string. If it *is* repeatable, it will be - splited into a list on whitespace:: + split into a list on whitespace:: Usage: my_program [--repeatable= --repeatable=] [--another-repeatable=]... @@ -368,18 +401,18 @@ The rules are as follows: --not-repeatable= [default: ./here ./there] Examples ----------------------------------------------------------------------- +-------- We have an extensive list of `examples `_ which cover every aspect of functionality of **docopt**. Try them out, read the source if in doubt. -There are also very intersting applications and ideas at that page. +There are also very interesting applications and ideas at that page. Check out the sister project for more information! Subparsers, multi-level help and *huge* applications (like git) ----------------------------------------------------------------------- +--------------------------------------------------------------- If you want to split your usage-pattern into several, implement multi-level help (with separate help-screen for each subcommand), @@ -391,7 +424,8 @@ we implemented a subset of git command-line interface as an example: `_ Compiling the example / Running the tests ----------------------------------------------------------------------- +----------------------------------------- + The original Python module includes some language-agnostic unit tests, and these can be run with this port as well. @@ -425,7 +459,7 @@ You can also compile the example shown at the start (included as example.cpp):: shoot: false Development ---------------------------------------------------- +----------- Comments and suggestions are *very* welcome! If you find issues, please file them and help improve our code! @@ -436,9 +470,10 @@ we might want to first negotiate these changes into the Python code first. However, bring it up! Let's hear it! Changelog ---------------------------------------------------- +--------- **docopt** follows `semantic versioning `_. The first release with stable API will be 1.0.0 (soon). -- 0.6.1 The initial C++ port of docopt.py +- 0.6.2 Bugfix release (still based on docopt 0.6.1) +- 0.6.1 The initial C++ port of docopt.py (based on docopt 0.6.1) diff --git a/docopt.cpp b/docopt.cpp index e875d2f..c6ee9b9 100644 --- a/docopt.cpp +++ b/docopt.cpp @@ -54,8 +54,10 @@ std::ostream& docopt::operator<<(std::ostream& os, value const& val) return os; } +#if 0 #pragma mark - #pragma mark Parsing stuff +#endif class Tokens { public: @@ -521,7 +523,7 @@ static PatternList parse_argv(Tokens tokens, std::vector