diff --git a/.clang-format b/.clang-format index 5f5881dc3..cf38aeba3 100644 --- a/.clang-format +++ b/.clang-format @@ -1,9 +1,10 @@ --- -Language: Cpp -BasedOnStyle: LLVM +Language: Cpp +BasedOnStyle: LLVM AccessModifierOffset: -4 BreakBeforeBraces: Mozilla -IndentWidth: 4 +IndentWidth: 4 ConstructorInitializerIndentWidth: 0 ReflowComments: false AlwaysBreakTemplateDeclarations: true +QualifierAlignment: Right diff --git a/.clang-tidy b/.clang-tidy index e15b32fff..12cb5d635 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: '*,-abseil-*,-altera-*,-android-cloexec-*,-bugprone-easily-swappable-parameters,-cert-err58-cpp,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-cstyle-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-vararg,-fuchsia-*,-google-readability-casting,-google-readability-todo,-hicpp-named-parameter,-hicpp-no-array-decay,-hicpp-vararg,-llvm-include-order,-llvm-header-guard,-llvmlibc-*,-misc-no-recursion,-modernize-use-nodiscard,-modernize-use-trailing-return-type,-readability-identifier-length,-readability-implicit-bool-conversion,-readability-named-parameter,-readability-magic-numbers' +Checks: '*,-abseil-*,-altera-*,-android-cloexec-*,-boost-use-ranges,-bugprone-chained-comparison,-bugprone-easily-swappable-parameters,-cert-err58-cpp,-cppcoreguidelines-avoid-do-while,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-cstyle-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-vararg,-fuchsia-*,-google-readability-casting,-google-readability-todo,-hicpp-named-parameter,-hicpp-no-array-decay,-hicpp-vararg,-llvm-include-order,-llvm-header-guard,-llvmlibc-*,-misc-include-cleaner,-misc-no-recursion,-modernize-use-nodiscard,-modernize-use-trailing-return-type,-readability-identifier-length,-readability-implicit-bool-conversion,-readability-math-missing-parentheses,-readability-named-parameter,-readability-magic-numbers' # # cppcoreguidelines-pro-type-cstyle-cast # google-build-using-namespace @@ -7,6 +7,7 @@ Checks: '*,-abseil-*,-altera-*,-android-cloexec-*,-bugprone-easily-swappable-par # llvm-header-guard # llvm-include-order # hicpp-named-parameter +# readability-math-missing-parentheses # readability-named-parameter # Differ from our style guidelines # @@ -19,6 +20,12 @@ Checks: '*,-abseil-*,-altera-*,-android-cloexec-*,-bugprone-easily-swappable-par # android-cloexec-* # O_CLOEXEC isn't available on Windows # +# boost-use-ranges +# Not applicable, we don't want to rely on more code from boost. +# +# bugprone-chained-comparison +# Triggerd by the Catch library in many places. +# # bugprone-easily-swappable-parameters # Can't always be avoided. # @@ -26,6 +33,10 @@ Checks: '*,-abseil-*,-altera-*,-android-cloexec-*,-bugprone-easily-swappable-par # There are many of these in the test code, most of them from the Catch # framework. Difficult to avoid. # +# cppcoreguidelines-avoid-do-while (new in clang-tidy-16) +# Its a good idea to avoid them when there are better alternatives, but +# sometimes they are the cleanest alternative. +# # cppcoreguidelines-avoid-magic-numbers # readability-magic-numbers # We have a lot of these and should probably at least fix some. But remove @@ -55,12 +66,16 @@ Checks: '*,-abseil-*,-altera-*,-android-cloexec-*,-bugprone-easily-swappable-par # llvmlibc-* # Not applicable # +# misc-include-cleaner (new in clang-tidy-17) +# Many instances of this warning, disabled for now. (TODO) +# # misc-no-recursion # Nothing wrong with recursion # # modernize-use-nodiscard -# We have a lot of these. Remove it for the time being because there are -# too many to fix quickly. (TODO) +# Doesn't really make the code clearer if it is all littered with +# [[nodiscard]]. Looks like this warning is more suited for a library +# than for normal code. # # modernize-use-trailing-return-type # We are not that modern... @@ -73,7 +88,7 @@ Checks: '*,-abseil-*,-altera-*,-android-cloexec-*,-bugprone-easily-swappable-par # Readability is a matter of opinion here # #WarningsAsErrors: '*' -HeaderFilterRegex: '\/src\/' +HeaderFilterRegex: '\/(src|tests)\/' CheckOptions: - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor value: true @@ -87,4 +102,30 @@ CheckOptions: value: 100 - key: readability-function-cognitive-complexity.IgnoreMacros value: true + - key: cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove + value: true + - key: bugprone-empty-catch.IgnoreCatchWithKeywords + value: "@todo;@fixme;exception ignored on purpose" + - key: readability-identifier-naming.ClassCase + value: lower_case + - key: readability-identifier-naming.ClassSuffix + value: _t + - key: readability-identifier-naming.PrivateMemberPrefix + value: m_ + - key: readability-identifier-naming.StructCase + value: lower_case + - key: readability-identifier-naming.EnumCase + value: lower_case + - key: readability-identifier-naming.FunctionCase + value: lower_case + - key: readability-identifier-naming.FunctionIgnoredRegexp + value: luaX.* + - key: readability-identifier-naming.VariableCase + value: lower_case + - key: readability-identifier-naming.ConstexprVariableCase + value: UPPER_CASE + - key: readability-identifier-naming.GlobalConstantCase + value: UPPER_CASE + - key: readability-identifier-naming.NamespaceCase + value: lower_case ... diff --git a/.github/actions/build-and-test/action.yml b/.github/actions/build-and-test/action.yml index ce5a190e7..9ab77f14f 100644 --- a/.github/actions/build-and-test/action.yml +++ b/.github/actions/build-and-test/action.yml @@ -10,33 +10,6 @@ runs: using: composite steps: - - name: create build directory - run: mkdir build - shell: bash - - - name: configure - run: | - CMAKE_OPTIONS="-LA -DBUILD_TESTS=ON" - if [ -z "${LUA_VERSION}" ]; then - CMAKE_OPTIONS="$CMAKE_OPTIONS -DWITH_LUA=OFF" - else - CMAKE_OPTIONS="$CMAKE_OPTIONS -DWITH_LUAJIT=${LUAJIT_OPTION}" - fi - if [ -n "$USE_PROJ_LIB" ]; then - CMAKE_OPTIONS="$CMAKE_OPTIONS -DUSE_PROJ_LIB=$USE_PROJ_LIB" - fi - if [ -n "$CPP_VERSION" ]; then - CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_CXX_STANDARD=$CPP_VERSION" - fi - if [ -n "$BUILD_TYPE" ]; then - CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_BUILD_TYPE=$BUILD_TYPE" - fi - cmake $CMAKE_OPTIONS .. - shell: bash - working-directory: build - env: - CXXFLAGS: -pedantic -Wextra ${{ env.EXTRA_FLAGS }} -Werror - - name: build run: make -j3 all man shell: bash diff --git a/.github/actions/clang-tidy/action.yml b/.github/actions/clang-tidy/action.yml new file mode 100644 index 000000000..c74ec7dfe --- /dev/null +++ b/.github/actions/clang-tidy/action.yml @@ -0,0 +1,9 @@ +name: Run clang-tidy + +runs: + using: composite + + steps: + - name: Run clang-tidy + run: run-clang-tidy-17 -q -p build -warnings-as-errors=\* + shell: bash diff --git a/.github/actions/linux-cmake/action.yml b/.github/actions/linux-cmake/action.yml new file mode 100644 index 000000000..58081d6e0 --- /dev/null +++ b/.github/actions/linux-cmake/action.yml @@ -0,0 +1,28 @@ +name: CMake + +runs: + using: composite + + steps: + - name: create build directory + run: mkdir build + shell: bash + + - name: configure + run: | + CMAKE_OPTIONS="-LA -DBUILD_TESTS=ON -DWITH_LUAJIT=${LUAJIT_OPTION}" + if [ -n "$WITH_PROJ" ]; then + CMAKE_OPTIONS="$CMAKE_OPTIONS -DWITH_PROJ=$WITH_PROJ" + fi + if [ -n "$CPP_VERSION" ]; then + CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_CXX_STANDARD=$CPP_VERSION" + fi + if [ -n "$BUILD_TYPE" ]; then + CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_BUILD_TYPE=$BUILD_TYPE" + fi + cmake $CMAKE_OPTIONS .. + shell: bash + working-directory: build + env: + CXXFLAGS: -pedantic -Wextra ${{ env.EXTRA_FLAGS }} -Werror + diff --git a/.github/actions/ubuntu-prerequisites/action.yml b/.github/actions/ubuntu-prerequisites/action.yml index c2b3248d2..ecc250da5 100644 --- a/.github/actions/ubuntu-prerequisites/action.yml +++ b/.github/actions/ubuntu-prerequisites/action.yml @@ -6,21 +6,18 @@ runs: steps: - name: Remove preinstalled PostgreSQL run: | - sudo apt-get remove -yq postgresql* - sudo apt-get install curl ca-certificates gnupg - curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/apt.postgresql.org.gpg >/dev/null - sudo sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' + sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y + sudo apt-get purge -yq postgresql* sudo apt-get update -qq shell: bash - name: Install software run: | sudo apt-get install -yq --no-install-suggests --no-install-recommends \ - cimg-dev \ - libboost-filesystem-dev \ - libboost-system-dev \ + libboost-dev \ libbz2-dev \ libexpat1-dev \ + libopencv-dev \ libpotrace-dev \ libpq-dev \ libproj-dev \ @@ -32,12 +29,11 @@ runs: postgresql-client postgresql-contrib-${POSTGRESQL_VERSION} \ python3-setuptools \ zlib1g-dev - pip3 install behave osmium - if [ "$CC" = clang-8 ]; then sudo apt-get install -yq --no-install-suggests --no-install-recommends clang-8; fi + pip3 install $PIP_OPTION behave osmium if [ "$PSYCOPG" = "2"]; then sudo apt-get install -yq --no-install-suggests --no-install-recommends python3-psycopg2 else - pip3 install psycopg + pip3 install $PIP_OPTION psycopg fi shell: bash diff --git a/.github/actions/win-cmake/action.yml b/.github/actions/win-cmake/action.yml index cb5aa0971..8dc05f784 100644 --- a/.github/actions/win-cmake/action.yml +++ b/.github/actions/win-cmake/action.yml @@ -9,7 +9,7 @@ runs: shell: bash - name: Configure - run: cmake -LA .. -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DBUILD_TESTS=ON -DGETOPT_INCLUDE_DIR=$GETOPT_INCLUDE_DIR -DGETOPT_LIBRARY=$GETOPT_LIBRARY -DBoost_USE_STATIC_LIBS=ON + run: cmake -LA .. -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DBUILD_TESTS=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON -DBoost_USE_STATIC_LIBS=ON shell: bash working-directory: build diff --git a/.github/actions/win-getopt/action.yml b/.github/actions/win-getopt/action.yml deleted file mode 100644 index 0eb5da8aa..000000000 --- a/.github/actions/win-getopt/action.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Windows getopt - -runs: - using: composite - - steps: - - name: Install wingetopt - env: - WINGETOPT_VER: v0.95 - run: | - git clone --quiet --depth 1 https://github.com/alex85k/wingetopt -b $WINGETOPT_VER ../wingetopt - mkdir ../wingetopt/build - shell: bash - - - name: Build wingetopt - run: | - cmake -LA .. -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake - cmake --build . --config Release --verbose - working-directory: ../wingetopt/build - shell: bash diff --git a/.github/actions/win-install/action.yml b/.github/actions/win-install/action.yml index 47eaf79cc..e00163d91 100644 --- a/.github/actions/win-install/action.yml +++ b/.github/actions/win-install/action.yml @@ -7,19 +7,15 @@ runs: - name: Install packages run: | vcpkg install \ - boost-filesystem:x64-windows \ boost-geometry:x64-windows \ - boost-property-tree:x64-windows \ - boost-system:x64-windows \ - bzip2:x64-windows \ - cimg:x64-windows \ + bzip2[core]:x64-windows \ expat:x64-windows \ libpq:x64-windows \ lua:x64-windows \ nlohmann-json:x64-windows \ - proj4:x64-windows \ + proj[core]:x64-windows \ zlib:x64-windows shell: bash - - name: Install psycopg2 and behave - run: python -m pip install psycopg2 behave osmium + - name: Install psycopg and behave + run: python -m pip install psycopg behave osmium shell: bash diff --git a/.github/actions/win-postgres/action.yml b/.github/actions/win-postgres/action.yml index c8ab4450b..122ca852c 100644 --- a/.github/actions/win-postgres/action.yml +++ b/.github/actions/win-postgres/action.yml @@ -3,11 +3,38 @@ name: Set up PostgreSQL on Windows runs: using: composite steps: + - name: Decide Postgis version (Windows) + # Borrowed from https://github.com/nyurik/action-setup-postgis/blob/main/action.yml + id: postgis-ver + shell: pwsh + run: | + echo "PowerShell version: ${PSVersionTable.PSVersion}" + $PG_VERSION = Split-Path $env:PGROOT -Leaf + $postgis_page = "https://download.osgeo.org/postgis/windows/pg$PG_VERSION" + echo "Detecting PostGIS version from $postgis_page for PostgreSQL $PG_VERSION" + $pgis_bundle = (Invoke-WebRequest -Uri $postgis_page -ErrorAction Stop).Links.Where({$_.href -match "^postgis.*zip$"}).href + if (!$pgis_bundle) { + Write-Error "Could not find latest PostGIS version in $postgis_page that would match ^postgis.*zip$ pattern" + exit 1 + } + $pgis_bundle = [IO.Path]::ChangeExtension($pgis_bundle, [NullString]::Value) + $pgis_bundle_url = "$postgis_page/$pgis_bundle.zip" + Add-Content $env:GITHUB_OUTPUT "postgis_file=$pgis_bundle" + Add-Content $env:GITHUB_OUTPUT "postgis_bundle_url=$pgis_bundle_url" + + - uses: actions/cache@v4 + with: + path: | + C:/postgis.zip + key: postgis-cache-${{ steps.postgis-ver.outputs.postgis_file }} + - name: Download postgis run: | - if (!(Test-Path "C:\postgis.zip")){(new-object net.webclient).DownloadFile("https://osm2pgsql.org/ci/winbuild/postgis-bundle-pg14-3.2.0x64.zip", "c:\postgis.zip")} + if (!(Test-Path "C:\postgis.zip")){(new-object net.webclient).DownloadFile($env:PGIS_BUNDLE_URL, "c:\postgis.zip")} 7z x c:\postgis.zip -oc:\postgis_archive shell: pwsh + env: + PGIS_BUNDLE_URL: ${{ steps.postgis-ver.outputs.postgis_bundle_url }} - name: Install postgis run: | diff --git a/.github/actions/win-test/action.yml b/.github/actions/win-test/action.yml index aca176018..5cf2dffda 100644 --- a/.github/actions/win-test/action.yml +++ b/.github/actions/win-test/action.yml @@ -8,6 +8,7 @@ runs: run: ctest --output-on-failure -C Release env: PYTHONUTF8: 1 + PROJ_DATA: c:/vcpkg/installed/x64-windows/share/proj/ # so proj finds proj.db shell: bash working-directory: build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83e6f0581..1d8d0a873 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,149 +3,90 @@ name: CI on: [ push, pull_request ] jobs: - macos: - strategy: - fail-fast: false - matrix: - os: - - "macos-11" - - "macos-12" # latest - - "macos-13" - runs-on: ${{ matrix.os }} - - env: - LUA_VERSION: 5.4 - - steps: - - uses: actions/checkout@v3 - - - name: Install prerequisites - run: | - brew install lua boost postgis pandoc cimg potrace nlohmann-json - pip3 install psycopg2 behave osmium - pg_ctl -D /usr/local/var/postgres init - pg_ctl -D /usr/local/var/postgres start - # Work around homebrew postgresql installation screw-up, see - # https://github.com/actions/runner-images/issues/6176 - ln -s $(pg_config --libdir)/* /usr/local/lib/ || true - ln -s $(pg_config --includedir)/* /usr/local/include/ - shell: bash - - - name: Setup database - run: | - psql -d postgres -c 'CREATE EXTENSION postgis' - psql -d postgres -c 'CREATE EXTENSION hstore' - psql -d postgres -c 'SELECT PostGIS_Full_Version()' - mkdir -m 700 $GITHUB_WORKSPACE/tablespacetest - psql -d postgres -c "CREATE TABLESPACE tablespacetest LOCATION '$GITHUB_WORKSPACE/tablespacetest'" - shell: bash - - uses: ./.github/actions/build-and-test - with: - test-wrapper: '' - env: - PGHOST: /tmp - - ubuntu20-pg96-gcc10-jit: - runs-on: ubuntu-20.04 + ubuntu22-pg11-gcc10-jit: + runs-on: ubuntu-22.04 env: CC: gcc-10 CXX: g++-10 - EXTRA_FLAGS: -Wno-unused-but-set-parameter # workaround for GCC bug LUA_VERSION: 5.3 LUAJIT_OPTION: ON - POSTGRESQL_VERSION: 9.6 - POSTGIS_VERSION: 2.5 + POSTGRESQL_VERSION: 11 + POSTGIS_VERSION: 3 BUILD_TYPE: Release PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu20-pg96-clang10-jit: - runs-on: ubuntu-20.04 + ubuntu22-pg11-clang13-jit: + runs-on: ubuntu-22.04 env: - CC: clang-10 - CXX: clang++-10 + CC: clang-13 + CXX: clang++-13 LUA_VERSION: 5.3 LUAJIT_OPTION: ON - POSTGRESQL_VERSION: 9.6 - POSTGIS_VERSION: 2.5 + POSTGRESQL_VERSION: 11 + POSTGIS_VERSION: 3 BUILD_TYPE: Release PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu20-pg10-gcc10: - runs-on: ubuntu-20.04 + ubuntu22-pg11-gcc10: + runs-on: ubuntu-22.04 env: CC: gcc-10 CXX: g++-10 - EXTRA_FLAGS: -Wno-unused-but-set-parameter # workaround for GCC bug LUA_VERSION: 5.3 LUAJIT_OPTION: OFF - POSTGRESQL_VERSION: 10 + POSTGRESQL_VERSION: 11 POSTGIS_VERSION: 3 BUILD_TYPE: Debug PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu20-pg11-clang10: - runs-on: ubuntu-20.04 + ubuntu22-pg11-clang13: + runs-on: ubuntu-22.04 env: - CC: clang-10 - CXX: clang++-10 + CC: clang-13 + CXX: clang++-13 LUA_VERSION: 5.3 LUAJIT_OPTION: OFF POSTGRESQL_VERSION: 11 - POSTGIS_VERSION: 2.5 - BUILD_TYPE: Debug - PSYCOPG: 2 - - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/ubuntu-prerequisites - - uses: ./.github/actions/build-and-test - - ubuntu20-pg12-gcc10-jit: - runs-on: ubuntu-20.04 - - env: - CC: gcc-10 - CXX: g++-10 - LUA_VERSION: 5.3 - LUAJIT_OPTION: ON - POSTGRESQL_VERSION: 12 - POSTGIS_VERSION: 2.5 + POSTGIS_VERSION: 3 BUILD_TYPE: Debug PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - - ubuntu20-pg13-clang10-jit: - runs-on: ubuntu-20.04 + ubuntu22-pg13-gcc11-jit: + runs-on: ubuntu-22.04 env: - CC: clang-10 - CXX: clang++-10 + CC: gcc-11 + CXX: g++-11 LUA_VERSION: 5.3 LUAJIT_OPTION: ON POSTGRESQL_VERSION: 13 @@ -154,107 +95,95 @@ jobs: PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu20-pg13-clang10-proj6: - runs-on: ubuntu-20.04 + + ubuntu22-pg12-clang14-jit: + runs-on: ubuntu-22.04 env: - CC: clang-10 - CXX: clang++-10 + CC: clang-14 + CXX: clang++-14 LUA_VERSION: 5.3 - LUAJIT_OPTION: OFF - POSTGRESQL_VERSION: 13 + LUAJIT_OPTION: ON + POSTGRESQL_VERSION: 12 POSTGIS_VERSION: 3 - USE_PROJ_LIB: 6 BUILD_TYPE: Debug PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu20-pg13-clang10-noproj: - runs-on: ubuntu-20.04 + ubuntu22-pg12-clang15-noproj: + runs-on: ubuntu-22.04 env: - CC: clang-10 - CXX: clang++-10 + CC: clang-15 + CXX: clang++-15 LUA_VERSION: 5.3 LUAJIT_OPTION: OFF - POSTGRESQL_VERSION: 13 + POSTGRESQL_VERSION: 12 POSTGIS_VERSION: 3 - USE_PROJ_LIB: off + WITH_PROJ: OFF BUILD_TYPE: Debug PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu20-pg13-clang10-cpp17: - runs-on: ubuntu-20.04 + ubuntu22-pg16-clang15: + runs-on: ubuntu-22.04 env: - CC: clang-10 - CXX: clang++-10 + CC: clang-15 + CXX: clang++-15 LUA_VERSION: 5.3 LUAJIT_OPTION: OFF - POSTGRESQL_VERSION: 14 + POSTGRESQL_VERSION: 16 POSTGIS_VERSION: 3 - USE_PROJ_LIB: 6 BUILD_TYPE: Debug PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu20-pg13-gcc10-release: - runs-on: ubuntu-20.04 + ubuntu22-pg13-gcc12-release: + runs-on: ubuntu-22.04 env: - CC: gcc-10 - CXX: g++-10 + CC: gcc-12 + CXX: g++-12 LUA_VERSION: 5.3 LUAJIT_OPTION: ON POSTGRESQL_VERSION: 13 - POSTGIS_VERSION: 2.5 - BUILD_TYPE: Release - PSYCOPG: 2 - - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/ubuntu-prerequisites - - uses: ./.github/actions/build-and-test - - ubuntu20-pg13-gcc10-release-nolua: - runs-on: ubuntu-20.04 - - env: - CC: gcc-10 - CXX: g++-10 - POSTGRESQL_VERSION: 13 - POSTGIS_VERSION: 2.5 + POSTGIS_VERSION: 3 BUILD_TYPE: Release PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu22-pg14-clang14-jit: - runs-on: ubuntu-22.04 + ubuntu24-pg14-clang16-jit: + runs-on: ubuntu-24.04 env: - CC: clang-14 - CXX: clang++-14 + CC: clang-16 + CXX: clang++-16 LUA_VERSION: 5.4 LUAJIT_OPTION: ON POSTGRESQL_VERSION: 14 @@ -263,173 +192,173 @@ jobs: PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu22-pg14-clang14-proj6: - runs-on: ubuntu-22.04 + ubuntu24-pg14-clang17-proj: + runs-on: ubuntu-24.04 env: - CC: clang-14 - CXX: clang++-14 + CC: clang-17 + CXX: clang++-17 LUA_VERSION: 5.4 LUAJIT_OPTION: OFF POSTGRESQL_VERSION: 14 POSTGIS_VERSION: 3 - USE_PROJ_LIB: 6 BUILD_TYPE: Debug PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu22-pg14-clang14-noproj: - runs-on: ubuntu-22.04 + ubuntu24-pg15-clang17-noproj: + runs-on: ubuntu-24.04 env: - CC: clang-14 - CXX: clang++-14 + CC: clang-17 + CXX: clang++-17 LUA_VERSION: 5.3 LUAJIT_OPTION: OFF - POSTGRESQL_VERSION: 14 + POSTGRESQL_VERSION: 15 POSTGIS_VERSION: 3 - USE_PROJ_LIB: off + WITH_PROJ: OFF BUILD_TYPE: Debug PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu22-pg14-clang14-cpp17: - runs-on: ubuntu-22.04 + ubuntu24-pg15-clang18: + runs-on: ubuntu-24.04 env: - CC: clang-14 - CXX: clang++-14 + CC: clang-18 + CXX: clang++-18 LUA_VERSION: 5.4 LUAJIT_OPTION: OFF - POSTGRESQL_VERSION: 14 + POSTGRESQL_VERSION: 15 POSTGIS_VERSION: 3 - USE_PROJ_LIB: 6 BUILD_TYPE: Debug PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu22-pg14-gcc12-release: - runs-on: ubuntu-22.04 + ubuntu24-pg16-gcc12-release: + runs-on: ubuntu-24.04 env: CC: gcc-12 CXX: g++-12 - EXTRA_FLAGS: -Wno-stringop-overread LUA_VERSION: 5.4 LUAJIT_OPTION: ON - POSTGRESQL_VERSION: 14 + POSTGRESQL_VERSION: 16 POSTGIS_VERSION: 3 BUILD_TYPE: Release PSYCOPG: 2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu22-pg14-gcc12-release-nolua: - runs-on: ubuntu-22.04 + ubuntu24-pg17-clang16-cpp20: + runs-on: ubuntu-24.04 env: - CC: gcc-12 - CXX: g++-12 - EXTRA_FLAGS: -Wno-stringop-overread - POSTGRESQL_VERSION: 14 + CC: clang-16 + CXX: clang++-16 + LUA_VERSION: 5.3 + LUAJIT_OPTION: OFF + POSTGRESQL_VERSION: 17 POSTGIS_VERSION: 3 - BUILD_TYPE: Release - PSYCOPG: 2 + CPP_VERSION: 20 + BUILD_TYPE: Debug + PSYCOPG: 3 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu22-pg14-clang14-cpp20: - runs-on: ubuntu-22.04 + ubuntu24-pg17-clang16-san: + runs-on: ubuntu-24.04 env: - CC: clang-14 - CXX: clang++-14 + CC: clang-16 + CXX: clang++-16 + EXTRA_FLAGS: -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer + LDFLAGS: -fsanitize=address,undefined LUA_VERSION: 5.3 LUAJIT_OPTION: OFF - POSTGRESQL_VERSION: 14 + POSTGRESQL_VERSION: 17 POSTGIS_VERSION: 3 - CPP_VERSION: 20 BUILD_TYPE: Debug PSYCOPG: 3 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test - ubuntu22-pg14-gcc12-cpp20: - runs-on: ubuntu-22.04 + ubuntu24-pg17-gcc12-cpp20: + runs-on: ubuntu-24.04 env: CC: gcc-12 CXX: g++-12 LUA_VERSION: 5.3 LUAJIT_OPTION: OFF - POSTGRESQL_VERSION: 14 + POSTGRESQL_VERSION: 17 POSTGIS_VERSION: 3 CPP_VERSION: 20 BUILD_TYPE: Debug PSYCOPG: 3 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake - uses: ./.github/actions/build-and-test windows: strategy: fail-fast: false matrix: - os: [windows-2019, windows-2022] + os: [windows-2022, windows-2025] runs-on: ${{ matrix.os }} env: - GETOPT_INCLUDE_DIR: ${{ github.workspace }}/../wingetopt/src - GETOPT_LIBRARY: ${{ github.workspace }}/../wingetopt/build/Release/wingetopt.lib VCPKG_DEFAULT_BINARY_CACHE: C:/vcpkg_binary_cache steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v6 + - uses: actions/cache@v5 with: path: | C:/vcpkg_binary_cache key: vcpkg-binary-cache-${{ matrix.os }}-${{ github.run_id }} restore-keys: | vcpkg-binary-cache-${{ matrix.os }} - - uses: actions/cache@v3 - with: - path: | - C:/postgis.zip - key: postgis-cache - name: Prepare cache run: if [ ! -d C:/vcpkg_binary_cache ]; then mkdir C:/vcpkg_binary_cache; fi shell: bash - uses: ./.github/actions/win-postgres - uses: ./.github/actions/win-install - - uses: ./.github/actions/win-getopt - uses: ./.github/actions/win-cmake - uses: ./.github/actions/win-build - uses: ./.github/actions/win-test @@ -437,32 +366,27 @@ jobs: run: | mkdir c:/artifact/ mkdir c:/artifact/osm2pgsql-bin - cp -r Release/* ../README.md ../COPYING ../*.style ../scripts ../flex-config c:/artifact/osm2pgsql-bin/ + cp -r Release/* ../README.md ../COPYING ../*.style ../scripts ../flex-config c:/vcpkg/installed/x64-windows/share/proj/proj.db c:/artifact/osm2pgsql-bin/ shell: bash working-directory: build - if: matrix.os == 'windows-2022' + if: matrix.os == 'windows-2025' - name: 'Upload Artifact' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v6 with: name: osm2pgsql-win64 path: c:/artifact - if: matrix.os == 'windows-2022' + if: matrix.os == 'windows-2025' windows-package: needs: windows - runs-on: windows-2022 + runs-on: windows-2025 env: - OSMURL: https://download.geofabrik.de/europe/monaco-latest.osm.bz2 + OSMURL: https://download.geofabrik.de/europe/monaco-latest.osm.pbf steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: | - C:/postgis.zip - key: postgis-cache - - uses: actions/download-artifact@v3 + - uses: actions/checkout@v6 + - uses: actions/download-artifact@v6 with: name: osm2pgsql-win64 - uses: ./.github/actions/win-postgres @@ -472,8 +396,9 @@ jobs: & $env:PGBIN\psql -d osm -c "CREATE EXTENSION hstore; CREATE EXTENSION postgis;" shell: pwsh - name: Get test data - run: (new-object net.webclient).DownloadFile($env:OSMURL, "testfile.osm.bz2") + run: Invoke-WebRequest -Uri $env:OSMURL -OutFile "testfile.osm.pbf" + shell: pwsh - name: Execute osm2pgsql - run: ./osm2pgsql-bin/osm2pgsql --slim -d osm testfile.osm.bz2 + run: ./osm2pgsql-bin/osm2pgsql --slim -d osm testfile.osm.pbf shell: bash diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml new file mode 100644 index 000000000..b3d2491ab --- /dev/null +++ b/.github/workflows/clang-tidy.yml @@ -0,0 +1,25 @@ +name: clang-tidy + +on: [ push, pull_request ] + +jobs: + clang-tidy: + runs-on: ubuntu-24.04 + + env: + CC: clang-18 + CXX: clang++-18 + LUA_VERSION: 5.4 + LUAJIT_OPTION: OFF + POSTGRESQL_VERSION: 16 + POSTGIS_VERSION: 3 + BUILD_TYPE: Debug + PSYCOPG: 3 + PIP_OPTION: --break-system-packages + + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/ubuntu-prerequisites + - uses: ./.github/actions/linux-cmake + - uses: ./.github/actions/clang-tidy + diff --git a/.github/workflows/luacheck.yml b/.github/workflows/luacheck.yml index 3162c9ae9..26a6eae47 100644 --- a/.github/workflows/luacheck.yml +++ b/.github/workflows/luacheck.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Install prerequisites run: | @@ -15,5 +15,5 @@ jobs: sudo apt-get install -yq --no-install-suggests --no-install-recommends lua-check - name: Run luacheck - run: luacheck flex-config/*.lua flex-config/gen/*.lua tests/data/*.lua tests/lua/tests.lua + run: luacheck flex-config/*.lua flex-config/*/*.lua tests/data/*.lua tests/lua/tests.lua diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 9c91f8ce2..4b28eb924 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -4,7 +4,7 @@ on: [ push, pull_request ] jobs: ubuntu-test-install: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: @@ -19,16 +19,16 @@ jobs: env: LUA_VERSION: 5.3 - POSTGRESQL_VERSION: 14 + POSTGRESQL_VERSION: 16 POSTGIS_VERSION: 3 BUILD_TYPE: Release - CXXFLAGS: -pedantic -Wextra -Wno-stringop-overread -Werror + CXXFLAGS: -pedantic -Wextra -Werror PREFIX: /usr/local OSMURL: https://download.geofabrik.de/europe/monaco-latest.osm.pbf OSMFILE: monaco-latest.osm.pbf steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Show installed PostgreSQL packages run: apt-cache search postgresql | sort @@ -38,13 +38,14 @@ jobs: sudo apt-get purge -yq postgresql* sudo apt-get update -qq sudo apt-get install -yq --no-install-suggests --no-install-recommends \ - cimg-dev \ - libboost-filesystem-dev \ - libboost-system-dev \ + libboost-dev \ libbz2-dev \ libexpat1-dev \ liblua${LUA_VERSION}-dev \ libluajit-5.1-dev \ + libopencv-core-dev \ + libopencv-imgcodecs-dev \ + libopencv-imgproc-dev \ libpotrace-dev \ libpq-dev \ libproj-dev \ diff --git a/.luacheckrc b/.luacheckrc index 659458e07..80507e2a6 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -20,12 +20,39 @@ stds.osm2pgsql = { process_relation = { read_only = false }, + process_untagged_node = { + read_only = false + }, + process_untagged_way = { + read_only = false + }, + process_untagged_relation = { + read_only = false + }, + process_deleted_node = { + read_only = false + }, + process_deleted_way = { + read_only = false + }, + process_deleted_relation = { + read_only = false + }, select_relation_members = { read_only = false }, process_gen = { read_only = false }, + after_nodes = { + read_only = false + }, + after_ways = { + read_only = false + }, + after_relations = { + read_only = false + }, }, other_fields = true, } diff --git a/AUTHORS b/AUTHORS index f497783c1..c679eb7f3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,3 @@ -osm2pgsql was written by Jon Burgess, Artem Pavlenko, Martijn van Oosterhout +osm2pgsql was written by Jon Burgess, Artem Pavlenko, Martijn van Oosterhout, Sarah Hoffmann, Kai Krueger, Frederik Ramm, Brian Quinion, Matt Amos, Kevin Kreiser, Paul Norman, Jochen Topf and other OpenStreetMap project members. diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f6b51798..e96d9c49d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ -cmake_minimum_required(VERSION 3.8.0) +cmake_minimum_required(VERSION 3.10.0) -project(osm2pgsql VERSION 1.9.1 LANGUAGES CXX C) +project(osm2pgsql VERSION 2.2.0 LANGUAGES CXX C) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -16,6 +16,10 @@ else() set(ENABLE_INSTALL OFF) endif() +if (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(FATAL_ERROR "osm2pgsql needs a 64 bit architecture") +endif() + if (WIN32) set(DEFAULT_STYLE "default.style" CACHE STRING "Default style used unless one is given on the command line") else() @@ -24,8 +28,8 @@ endif() option(BUILD_TESTS "Build test suite" OFF) option(BUILD_COVERAGE "Build with coverage" OFF) -option(WITH_LUA "Build with Lua support" ON) option(WITH_LUAJIT "Build with LuaJIT support" OFF) +option(WITH_PROJ "Build with Projection support" ON) if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) message(FATAL_ERROR "In-source builds are not allowed, please use a separate build directory like `mkdir build && cd build && cmake ..`") @@ -42,17 +46,20 @@ set(CMAKE_CXX_EXTENSIONS OFF) if (MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS -DNOMINMAX) - add_compile_options(-wd4996) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099 /STACK:30000000") + add_compile_options(-wd4996 -utf-8) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099") else() add_compile_options(-Wall) endif() +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + add_definitions(-Wno-stringop-overread -Wno-maybe-uninitialized) +endif() + option(EXTERNAL_LIBOSMIUM "Do not use the bundled libosmium" OFF) option(EXTERNAL_PROTOZERO "Do not use the bundled protozero" OFF) option(EXTERNAL_FMT "Do not use the bundled fmt" OFF) - -set(USE_PROJ_LIB "auto" CACHE STRING "Which version of PROJ API to use: ('4', '6', 'off', or 'auto')") +option(EXTERNAL_CLI11 "Do not use the bundled CLI11" OFF) if (NOT WIN32 AND NOT APPLE) # No need for this path, just a workaround to make cmake work on all systems. @@ -61,10 +68,10 @@ if (NOT WIN32 AND NOT APPLE) set(PostgreSQL_TYPE_INCLUDE_DIR /usr/include) endif() -set(MINIMUM_POSTGRESQL_SERVER_VERSION "9.6") -set(MINIMUM_POSTGRESQL_SERVER_VERSION_NUM "90600") +set(MINIMUM_POSTGRESQL_SERVER_VERSION "11") +set(MINIMUM_POSTGRESQL_SERVER_VERSION_NUM "110000") -set(PostgreSQL_ADDITIONAL_VERSIONS "15" "14" "13" "12" "11" "10" "9.6") +set(PostgreSQL_ADDITIONAL_VERSIONS "19" "18" "17" "16" "15" "14" "13" "12" "11") ############################################################# # Version @@ -171,29 +178,29 @@ if (NOT EXTERNAL_FMT) set(FMT_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/contrib/fmt/include") endif() +if (NOT EXTERNAL_CLI11) + set(CLI11_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/contrib/CLI11/include") +endif() + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}) find_package(Osmium 2.17.3 REQUIRED COMPONENTS io) -include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS} ${PROTOZERO_INCLUDE_DIR} ${FMT_INCLUDE_DIR}) - -if (WITH_LUA) - if (WITH_LUAJIT) - message(STATUS "Building with LuaJIT support") - find_package(LuaJIT REQUIRED) - include_directories(SYSTEM ${LUAJIT_INCLUDE_DIR}) - add_definitions(-DHAVE_LUAJIT=1) - else() - message(STATUS "Building with Lua support") - find_package(Lua REQUIRED) - include_directories(SYSTEM ${LUA_INCLUDE_DIR}) - endif() - add_definitions(-DHAVE_LUA=1) - find_program(LUA_EXE NAMES lua lua5.4 lua5.3 lua5.2 lua5.1) +include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS} ${PROTOZERO_INCLUDE_DIR} ${FMT_INCLUDE_DIR} ${CLI11_INCLUDE_DIR}) + +if (WITH_LUAJIT) + message(STATUS "Building with LuaJIT support") + find_package(LuaJIT REQUIRED) + include_directories(SYSTEM ${LUAJIT_INCLUDE_DIR}) + add_definitions(-DHAVE_LUAJIT=1) else() - message(STATUS "Building without Lua support") + message(STATUS "Building with Lua (but not LuaJIT) support") + find_package(Lua REQUIRED) + include_directories(SYSTEM ${LUA_INCLUDE_DIR}) endif() -find_package(Boost 1.50 REQUIRED COMPONENTS system filesystem) +find_program(LUA_EXE NAMES lua lua5.4 lua5.3 lua5.2 lua5.1) + +find_package(Boost CONFIG 1.50 REQUIRED) include_directories(SYSTEM ${Boost_INCLUDE_DIR}) find_package(PostgreSQL REQUIRED) @@ -207,66 +214,32 @@ include_directories(SYSTEM ${NLOHMANN_INCLUDE_DIR}) find_path(POTRACE_INCLUDE_DIR potracelib.h) find_library(POTRACE_LIBRARY NAMES potrace) -find_path(CIMG_INCLUDE_DIR CImg.h) +find_package(OpenCV QUIET OPTIONAL_COMPONENTS core imgcodecs imgproc) ############### Libraries are found now ######################## set(LIBS ${Boost_LIBRARIES} ${PostgreSQL_LIBRARY} ${OSMIUM_LIBRARIES}) -if (USE_PROJ_LIB STREQUAL "off") - message(STATUS "Proj library disabled (because USE_PROJ_LIB is set to 'off').") -else() - find_path(PROJ4_INCLUDE_DIR proj_api.h) - if (PROJ4_INCLUDE_DIR AND NOT USE_PROJ_LIB STREQUAL "6") - message(STATUS "Found proj_api.h") - find_library(PROJ_LIBRARY NAMES proj) - message(STATUS "Found Proj [API 4] ${PROJ_LIBRARY}") - add_definitions(-DHAVE_GENERIC_PROJ=4) - set(HAVE_PROJ4 1) +if (WITH_PROJ) + find_path(PROJ6_INCLUDE_DIR proj.h) + find_library(PROJ_LIBRARY NAMES proj) + if (PROJ_LIBRARY) + message(STATUS "Found Proj ${PROJ_LIBRARY}") + add_definitions(-DHAVE_GENERIC_PROJ=6) + set(HAVE_PROJ6 1) list(APPEND LIBS ${PROJ_LIBRARY}) - include_directories(SYSTEM ${PROJ4_INCLUDE_DIR}) - elseif (NOT USE_PROJ_LIB STREQUAL "4") - find_path(PROJ6_INCLUDE_DIR proj.h) - find_library(PROJ_LIBRARY NAMES proj) - if (PROJ_LIBRARY) - message(STATUS "Found Proj [API 6] ${PROJ_LIBRARY}") - add_definitions(-DHAVE_GENERIC_PROJ=6) - set(HAVE_PROJ6 1) - list(APPEND LIBS ${PROJ_LIBRARY}) - include_directories(SYSTEM ${PROJ6_INCLUDE_DIR}) - else() - message(STATUS "Proj library not found.") - message(STATUS " Only Mercartor and WGS84 projections will be available.") - endif() + include_directories(SYSTEM ${PROJ6_INCLUDE_DIR}) endif() endif() -if (USE_PROJ_LIB STREQUAL "4" AND NOT HAVE_PROJ4) - message(FATAL_ERROR "USE_PROJ_LIB was set to '4', but PROJ version 4 API not found") -endif() - -if (USE_PROJ_LIB STREQUAL "6" AND NOT HAVE_PROJ6) - message(FATAL_ERROR "USE_PROJ_LIB was set to '6', but PROJ version 6 API not found") -endif() - if (LUAJIT_FOUND) list(APPEND LIBS ${LUAJIT_LIBRARIES}) -elseif (LUA_FOUND) +else() list(APPEND LIBS ${LUA_LIBRARIES}) endif() if (WIN32) list(APPEND LIBS ws2_32) - if (MSVC) - find_path(GETOPT_INCLUDE_DIR getopt.h) - find_library(GETOPT_LIBRARY NAMES wingetopt getopt) - if (GETOPT_INCLUDE_DIR AND GETOPT_LIBRARY) - include_directories(SYSTEM ${GETOPT_INCLUDE_DIR}) - list(APPEND LIBS ${GETOPT_LIBRARY}) - else() - message(FATAL_ERROR "Can not find getopt library for Windows. Please get it from https://github.com/alex85k/wingetopt or alternative source.") - endif() - endif() endif() message(STATUS "Libraries used to build: ${LIBS}") @@ -280,40 +253,38 @@ add_subdirectory(src) add_executable(osm2pgsql src/osm2pgsql.cpp) target_link_libraries(osm2pgsql osm2pgsql_lib ${LIBS}) -if (${POTRACE_LIBRARY} STREQUAL "POTRACE_LIBRARY-NOTFOUND" OR ${CIMG_INCLUDE_DIR} STREQUAL "CIMG_INCLUDE_DIR-NOTFOUND") - message(STATUS "Did not find cimg and/or potrace library. Not building osm2pgsql-gen.") +if (${POTRACE_LIBRARY} STREQUAL "POTRACE_LIBRARY-NOTFOUND" OR NOT OPENCV_CORE_FOUND) + message(STATUS "Did not find opencv and/or potrace library. Not building osm2pgsql-gen.") else() - if (WITH_LUA) - message(STATUS "Found cimg and potrace library. Building osm2pgsql-gen.") - set(BUILD_GEN 1) - include_directories(SYSTEM ${CIMG_INCLUDE_DIR}) - include_directories(SYSTEM ${POTRACE_INCLUDE_DIR}) - add_executable(osm2pgsql-gen src/gen/osm2pgsql-gen.cpp - src/gen/canvas.cpp - src/gen/gen-base.cpp - src/gen/gen-create.cpp - src/gen/gen-discrete-isolation.cpp - src/gen/gen-rivers.cpp - src/gen/gen-tile-builtup.cpp - src/gen/gen-tile-raster.cpp - src/gen/gen-tile-vector.cpp - src/gen/gen-tile.cpp - src/gen/params.cpp - src/gen/raster.cpp - src/gen/tracer.cpp) - target_link_libraries(osm2pgsql-gen osm2pgsql_lib ${LIBS} ${POTRACE_LIBRARY}) - else() - message(STATUS "No Lua. Not building osm2pgsql-gen.") - endif() + message(STATUS "Found opencv and potrace library. Building osm2pgsql-gen.") + set(BUILD_GEN 1) + include_directories(SYSTEM ${POTRACE_INCLUDE_DIR}) + add_executable(osm2pgsql-gen src/gen/osm2pgsql-gen.cpp + src/gen/canvas.cpp + src/gen/gen-base.cpp + src/gen/gen-create.cpp + src/gen/gen-discrete-isolation.cpp + src/gen/gen-rivers.cpp + src/gen/gen-tile-builtup.cpp + src/gen/gen-tile-raster.cpp + src/gen/gen-tile-sql.cpp + src/gen/gen-tile-vector.cpp + src/gen/gen-tile.cpp + src/gen/raster.cpp + src/gen/tracer.cpp) + target_link_libraries(osm2pgsql-gen osm2pgsql_lib ${LIBS} ${POTRACE_LIBRARY} ${OpenCV_LIBS}) endif() +add_executable(osm2pgsql-expire src/osm2pgsql-expire.cpp) +target_link_libraries(osm2pgsql-expire osm2pgsql_lib ${LIBS}) + ############################################################# # Optional "clang-tidy" target ############################################################# message(STATUS "Looking for clang-tidy") find_program(CLANG_TIDY - NAMES clang-tidy clang-tidy-16 clang-tidy-15 clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11) + NAMES clang-tidy clang-tidy-19 clang-tidy-18 clang-tidy-17 clang-tidy-16 clang-tidy-15) if (CLANG_TIDY) message(STATUS "Looking for clang-tidy - found ${CLANG_TIDY}") @@ -322,6 +293,7 @@ if (CLANG_TIDY) add_custom_target(clang-tidy ${CLANG_TIDY} + --config-file ${CMAKE_SOURCE_DIR}/.clang-tidy -p ${CMAKE_BINARY_DIR} ${CT_CHECK_FILES} ) @@ -340,10 +312,6 @@ if (BUILD_TESTS) set(TESTING_TIMEOUT 1200) endif() add_subdirectory(tests) - configure_file( - ${PROJECT_SOURCE_DIR}/tests/run-behave.in - ${PROJECT_BINARY_DIR}/run-behave - ) else() message(STATUS "Tests disabled. Set BUILD_TESTS=ON to enable tests.") endif() @@ -352,7 +320,7 @@ endif() # Man page ############################################################# -add_subdirectory(docs) +add_subdirectory(man) ############################################################# # Install @@ -362,10 +330,10 @@ include(GNUInstallDirs) if (ENABLE_INSTALL) install(TARGETS osm2pgsql DESTINATION bin) + install(TARGETS osm2pgsql-expire DESTINATION bin) install(FILES default.style empty.style DESTINATION share/osm2pgsql) install(PROGRAMS scripts/osm2pgsql-replication DESTINATION bin) if (BUILD_GEN) - install(TARGETS osm2pgsql-gen COMPONENT gen EXCLUDE_FROM_ALL DESTINATION bin) - add_custom_target(install-gen cmake --install ${CMAKE_BINARY_DIR} --component gen) + install(TARGETS osm2pgsql-gen DESTINATION bin) endif() endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 496591089..f830a4ceb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,49 +1,61 @@ -# Osm2pgsql contribution guidelines +# osm2pgsql contribution guidelines -## Workflow - -We operate the "Fork & Pull" model explained at - -https://help.github.com/articles/using-pull-requests - -You should fork the project into your own repo, create a topic branch -there and then make one or more pull requests back to the OpenStreetMap repository. -Your pull requests will then be reviewed and discussed. - -## History +The following section describes our work flow, coding style and give some +hints on developer setup. +For more information about what to contribute and an overview +of the general roadmap, visit the [Contribution guide](https://osm2pgsql.org/contribute/) +on the osm2pgsql website. -To understand the osm2pgsql code, it helps to know some history on it. Osm2pgsql -was written in C in 2007 as a port of an older Python utility. In 2014 it was -ported to C++ by MapQuest and the last C version was released as 0.86.0. In it's -time, it has had varying contribution activity, including times with no -maintainer or active developers. - -Parts of the codebase still clearly show their C origin and could use rewriting -in modern C++, making use of data structures in the standard library. - -## Versioning - -Osm2pgsql uses [semantic versioning](https://semver.org/). - -Bugs and known issues are fixed on the main branch only. Exceptions may be made -for severe bugs. +## Workflow -## Code style +We operate with the +["Fork & Pull"](https://help.github.com/articles/using-pull-requests) model +and try to stick to a four-eyes review mode, meaning that PRs should be merged +by a different person than the author. + +Here are a few simple rules you should follow with your code and pull request (PR). +They will maximize your chances that a PR gets reviewed and merged. + +* Split your PR into functionally sensible commits. Describe each commit with + a relevant commit message. If you need to do fix-up commits, please, merge + them into the functional commits. Interactive rebasing (`git rebase -i`) is + very useful for this. Then force-push to your PR branch. +* Avoid merge commits. If you have to catch up with changes from master, + rather use rebasing. +* Split up larger PRs into smaller units if possible. Never mix two different + topics or fixes in a single PR. +* Decorate your PR with an informative but succinct description. Do not post + AI-generated PR descriptions without having reviewed (and preferably heavily + shortened) the text. +* Try to follow the style of existing code as close as possible. Use + clang-format to follow the formal coding style (see below). + +> [!IMPORTANT] +> Any use of generative AI for writing code, documentation or PR descriptions +> must be disclosed. You must further be able to show that you have understood +> the generated parts. Your code, your responsibility. + +## Coding style Code must be written in the [K&R 1TBS style](https://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS) with 4 spaces indentation. Tabs should never be used in the C++ code. Braces must always be used for code blocks, even one-liners. -Names should use underscores, not camel case, with class/struct names ending in `_t`. -Template parameters must use all upper case. +Names should use underscores, not camel case, with class/struct names ending in +`_t`. Constants and template parameters must use all upper case. -Headers should be included in the order C++ standard library headers, -C library headers, Boost headers, and last osm2pgsql files. +Header files should be included in the following order, each group in their own +block: -There is a .clang-format configuration available and all code must be run through -clang-format before submitting. You can use git-clang-format after staging all -your changes: +* The corresponding .hpp file (in .cpp files only) +* Other osm2pgsql header files +* Header files from external libraries, each in their own block +* C++/C C++ standard library header files + +There is a .clang-format configuration available and all code must be run +through clang-format before submitting. You can use git-clang-format after +staging all your changes: git-clang-format src/*pp tests/*pp @@ -55,12 +67,12 @@ convention](https://www.doxygen.nl/manual/docblocks.html) using backslashes ## Documentation -User documentation is available on [the website](https://osm2pgsql.org/), some -is stored in `docs/`. Pages on the OpenStreetMap wiki are known to be -unreliable and outdated. +User documentation is available on [the website](https://osm2pgsql.org/). The +source of the web page is in its own repository +(https://github.com/openstreetmap/osm2pgsql-website). -The man pages for [osm2pgsql](docs/osm2pgsql.1) and -[osm2pgsql-replication](docs/osm2pgsql-replication.1) can be built from source +The man pages for [osm2pgsql](man/osm2pgsql.1) and +[osm2pgsql-replication](man/osm2pgsql-replication.1) can be built from source with `make man`. They need pandoc and argparse-manpage for the conversion. These tools can be @@ -70,16 +82,13 @@ installed with: sudo apt-get install pandoc python3-argparse-manpage ``` -Results should be checked into the repository. - -## Platforms targeted - -Ideally osm2pgsql should compile on Linux, OS X, FreeBSD and Windows. It is -actively tested on Debian, Ubuntu and FreeBSD by the maintainers. +The manpages are rebuilt and checked into the repository as part of the +release process. ## Testing -osm2pgsql is tested with two types of tests: Classic tests written in C++ and BDD (Behavior Driven Development) tests written in Python. +osm2pgsql is tested with two types of tests: Classic tests written in C++ and +BDD (Behavior Driven Development) tests written in Python. ### Classic Tests @@ -96,7 +105,7 @@ against it. This is most easily done using `pg_virtualenv`. Just run pg_virtualenv ctest ``` -`pg_virtualenv` creates a separate postgres server instance. The test databases +`pg_virtualenv` creates a separate PostgreSQL server instance. The test databases are created in this instance and the complete server is destroyed after the tests are finished. ctest also calls appropriate fixtures that create the separate tablespace required for some tests. @@ -128,54 +137,54 @@ database created by `pg_virtualenv`. Tests in the `tests/bdd` directory use [behave](https://github.com/behave/behave), a Python implementation of a behaviour-driven test framework. To run the -BDD tests you need to have behave and psycopg2 installed. On Ubuntu run: +BDD tests you need to have behave and psycopg installed. On Ubuntu run: ```sh -sudo apt-get install python3-psycopg2 python3-behave +sudo apt-get install python3-psycopg python3-behave ``` +The BDD tests are run through the osm2pgsql-test-style tester. See the +[section on testing](https://osm2pgsql.org/doc/manual.html#style-testing) +in the manual for details. + There are ctest directives to run the tests. If you want to run the tests manually, for example to run single tests during development, you can -switch to the bdd test directory and run behave directly from there: +use the `run-bdd-tests` script in the build directory. It is a thin +wrapper around osm2pgsql-test-style which properly sets up the paths +for the osm2pgsql binary and test data paths: ```sh -cd osm2pgsql/tests/bdd -behave -DBINARY=/osm2pgsql +cd build +./run-bdd-tests ../tests/bdd/regression/ ``` -Per default, behave assumes that the build directory is under `osm2pgsql/build`. -If your setup works like that, you can leave out the `-D` parameter. - -To make this a bit easier a shell script `run-behave` is provided in your -build directory which sets those correct paths and calls `behave`. If run -with `-p` as first option it will wrap the call to `behave` in a call to -`pg_virtualenv` for your convenience. All other command line parameters of -`run-behave` will be passed through to behave. - To run a single test, simply add the name of the test file, followed by a column and the line number of the test: ```sh -behave flex/area.feature:71 +./run-bdd-tests ../tests/bdd/flex/area.feature:71 ``` -If you need to inspect the database that a test produces, you can add -`-DKEEP_TEST_DB` and behave won't remove the database after the test is -finished. This makes of course only sense, when running a single test. +You can pass any additional parameters to the script that osm2pgsql-test-style +would take. If you need to inspect the database that a test produces, you add +`--keep-test-db` and behave won't remove the database after the test is +finished. This makes only sense, when running a single test. When running under pg_virtualenv, don't forget to keep the virtual environment as well. You can use the handy `-s` switch: ```sh -pg_virtualenv -s behave -DKEEP_TEST_DB flex/area.feature:71 +pg_virtualenv -s ./run-bdd-tests ../tests/bdd/flex/area.feature:71 ``` It drops you into a shell when the behave test fails, where you can use psql to look at the database. Or start a shell in the virtual environment with `pg_virtualenv bash` and run behave from there. -The BDDs automatically detect if osm2pgsql was compiled with Lua and +The BDDs automatically detect if osm2pgsql was compiled with proj support and skip tests accordingly. They also check for the test -tablespace `tablespacetest` for tests that need tablespaces. +tablespace `tablespacetest` for tests that need tablespaces. To force +running the proj and tablespace tests use `--test-proj yes` and +`--test-tablespace yes` respectively. BDD tests hide print statements by default. For development purposes they can be shown by adding these lines to `tests/bdd/.behaverc`: @@ -187,11 +196,6 @@ stderr_capture=False log_capture=False ``` -### Performance testing - -If performance testing with a full planet import is required, indicate what -needs testing in a pull request. - ## Coverage reports To create coverage reports, set `BUILD_COVERAGE` in the CMake config to `ON`, @@ -207,14 +211,11 @@ the report. * Decide on a new version. (See [semantic versioning](https://semver.org/).) * Update version in [CMakeLists.txt](CMakeLists.txt), look for `project` function. -* Build man page (`make man`) and copy it to `docs/osm2pgsql.1`. +* Update man pages + * Build man page: `make man` + * Copy to source: `cp man/*1 ../man/` * Tag release with release notes in commit message and upload the tag to Github. * Fill out release notes on Github. * Copy Windows binaries and source tarball to osm2pgsql.org. * Add release info to osm2pgsql.org. * Publish release notes as News article on osm2pgsql.org. - -## Maintainers - -The current maintainers of osm2pgsql are [Sarah Hoffmann](https://github.com/lonvia/) -and [Paul Norman](https://github.com/pnorman/). diff --git a/README.md b/README.md index 06e9d68fc..c1c7dd84a 100644 --- a/README.md +++ b/README.md @@ -9,25 +9,29 @@ Nominatim, or general analysis. See the [documentation](https://osm2pgsql.org/doc/) for instructions on how to install and run osm2pgsql. -[![Github Actions Build Status](https://github.com/openstreetmap/osm2pgsql/workflows/CI/badge.svg?branch=master)](https://github.com/openstreetmap/osm2pgsql/actions) +[![Github Actions Build Status](https://github.com/osm2pgsql-dev/osm2pgsql/workflows/CI/badge.svg)](https://github.com/osm2pgsql-dev/osm2pgsql/actions) [![Packaging Status](https://repology.org/badge/tiny-repos/osm2pgsql.svg)](https://repology.org/project/osm2pgsql/versions) ## Features -* Converts OSM files to a PostgreSQL DB -* Conversion of tags to columns is configurable in the style file -* Able to read .gz, .bz2, .pbf and .o5m files directly -* Can apply diffs to keep the database up to date -* Support the choice of output projection -* Configurable table names -* Support for hstore field type to store the complete set of tags in one database - field if desired +* freely configurable table layout and content using the LUA scripting language +* support for building geometries in simple feature format and for generalization +* read OSM data from OSM, PBF and O5M formats +* update the database from OSM change files and replication diffs +* usable for importing a small geographic area on a tiny laptop up to + importing the whole world on a large server machine +* runs on Linux, Windows and MacOS +* [and more](https://osm2pgsql.org/about/features/) ## Installing -Most Linux distributions include osm2pgsql. It is available on macOS with -[Homebrew](https://brew.sh/) and Windows builds are also available. See -https://osm2pgsql.org/doc/install.html for details. +* __Linux__: most distributions include osm2pgsql. Debian always offers + the latest version via backports. +* __macOS__: osm2pgsql is available through [Homebrew](https://brew.sh/) +* __Windows__: use our [binary builds](https://osm2pgsql.org/download/windows/) + +For detailed instructions, visit +the [Installation page](https://osm2pgsql.org/doc/install.html). ## Building @@ -35,46 +39,45 @@ The latest source code is available in the osm2pgsql git repository on GitHub and can be downloaded as follows: ```sh -git clone https://github.com/openstreetmap/osm2pgsql.git +git clone https://github.com/osm2pgsql-dev/osm2pgsql.git ``` -Osm2pgsql uses the cross-platform [CMake build system](https://cmake.org/) +osm2pgsql uses the cross-platform [CMake build system](https://cmake.org/) to configure and build itself. Required libraries are +* [CLI11](https://github.com/CLIUtils/CLI11) * [expat](https://libexpat.github.io/) * [proj](https://proj.org/) * [bzip2](http://www.bzip.org/) * [zlib](https://www.zlib.net/) -* [Boost libraries](https://www.boost.org/), including geometry, system and - filesystem +* [Boost libraries](https://www.boost.org/) (for boost geometry) * [nlohmann/json](https://json.nlohmann.me/) -* [CImg](https://cimg.eu/) (Optional, for generalization only) -* [potrace](https://potrace.sourceforge.net/) (Optional, for generalization only) -* [PostgreSQL](https://www.postgresql.org/) client libraries -* [Lua](https://www.lua.org/) (Optional, used for Lua tag transforms - and the flex output) +* [PostgreSQL](https://www.postgresql.org/) client library +* [Lua](https://www.lua.org/) +* [OpenCV](https://opencv.org/) (optional, for generalization only) +* [potrace](https://potrace.sourceforge.net/) (optional, for generalization only) * [Python](https://python.org/) (only for running tests) * [Psycopg](https://www.psycopg.org/) (only for running tests) The following libraries are included in the `contrib` directory. You can build -with other versions of those libraries (set the `EXTERNAL_*libname*` option to -`ON`) but make sure you are using a compatible version: +with other versions of those libraries (set the `EXTERNAL_*libname*` CMake +option to `ON`) but make sure you are using a compatible version: * [fmt](https://fmt.dev/) (>= 7.1.3) * [libosmium](https://osmcode.org/libosmium/) (>= 2.17.0) * [protozero](https://github.com/mapbox/protozero) (>= 1.6.3) It also requires access to a database server running -[PostgreSQL](https://www.postgresql.org/) 9.6+ and -[PostGIS](https://www.postgis.net/) 2.2+. +[PostgreSQL](https://www.postgresql.org/) (version 11+ works, 13+ strongly +recommended) and [PostGIS](https://www.postgis.net/) (version 3.0+). Make sure you have installed the development packages for the libraries mentioned in the requirements section and a C++ compiler which supports C++17. -We officially support gcc >= 7.0 and clang >= 8. +We officially support gcc >= 10.0 and clang >= 13. -To rebuild the included man page you'll need the [pandoc](https://pandoc.org/) +To rebuild the included man page you need the [pandoc](https://pandoc.org/) tool. First install the dependencies. @@ -82,18 +85,19 @@ First install the dependencies. On a Debian or Ubuntu system, this can be done with: ```sh -sudo apt-get install make cmake g++ libboost-dev libboost-system-dev \ - libboost-filesystem-dev libexpat1-dev zlib1g-dev libpotrace-dev cimg-dev \ - libbz2-dev libpq-dev libproj-dev lua5.3 liblua5.3-dev pandoc \ - nlohmann-json3-dev pyosmium +sudo apt-get install make cmake g++ libboost-dev \ + libexpat1-dev zlib1g-dev libpotrace-dev \ + libopencv-dev libbz2-dev libpq-dev libproj-dev lua5.3 liblua5.3-dev \ + pandoc nlohmann-json3-dev pyosmium ``` On a Fedora system, use ```sh -sudo dnf install cmake make gcc-c++ boost-devel expat-devel zlib-devel \ - potrace-devel cimg-devel json-devel python3-osmium \ - bzip2-devel postgresql-devel proj-devel proj-epsg lua-devel pandoc +sudo dnf install cmake make gcc-c++ libtool boost-devel bzip2-devel \ + expat-devel fmt-devel json-devel libpq-devel lua-devel zlib-devel \ + potrace-devel opencv-devel python3-osmium \ + postgresql-devel proj-devel proj-epsg pandoc ``` On RedHat / CentOS first run `sudo yum install epel-release` then install @@ -101,7 +105,7 @@ dependencies with: ```sh sudo yum install cmake make gcc-c++ boost-devel expat-devel zlib-devel \ - potrace-devel cimg-devel json-devel python3-osmium \ + potrace-devel opencv-devel json-devel python3-osmium \ bzip2-devel postgresql-devel proj-devel proj-epsg lua-devel pandoc ``` @@ -115,8 +119,9 @@ pkg install devel/cmake devel/boost-libs textproc/expat2 \ On Alpine, use ```sh -apk --update-cache add cmake make g++ boost-dev expat-dev \ - bzip2-dev zlib-dev libpq proj-dev lua5.3-dev postgresql-dev +apk --update-cache add cmake make g++ nlohmann-json \ + postgresql-dev boost-dev expat-dev bzip2-dev zlib-dev \ + libpq proj-dev lua5.3-dev luajit-dev ``` Once dependencies are installed, use CMake to build the Makefiles in a separate @@ -149,14 +154,8 @@ The compiled files can be installed with sudo make install ``` -To install the experimental `osm2pgsql-gen` binary use - -```sh -sudo make install-gen -``` - -By default, the Release build with debug info is created and no tests are -compiled. You can change that behavior by using additional options like +By default, a Release build with debug info is created and tests are +disabled. You can change that behavior by using additional options like following: ```sh @@ -168,24 +167,13 @@ Note that `Debug` builds will be much slower than release build. For production ### Using the PROJ library -Osm2pgsql has builtin support for the Latlong (WGS84, EPSG:4326) and the -WebMercator (EPSG:3857) projection. If you need other projections you have to -compile with the PROJ library. - -Both the older API (PROJ version 4) and the newer API (PROJ version 6.1 and -above) are supported. Usually the CMake configuration will find a suitable -version and use it automatically, but you can set the `USE_PROJ_LIB` CMake -cache variable to choose between the following behaviours: - -* `4`: Look for PROJ library with API version 4. If it is not found, stop with - error. -* `6`: Look for PROJ library with API version 6. If it is not found, stop with - error. -* `off`: Build without PROJ library. -* `auto`: Choose API 4 if available, otherwise API 6. If both are not available - build without PROJ library. (This is the default.) +osm2pgsql has builtin support for the Latlong (WGS84, EPSG:4326) and the +WebMercator (EPSG:3857) projection. Other projections are supported through +the [Proj library](https://proj.org/). Support will be automatically compiled +in, when CMake can find the library in the system. Set the CMake +option `WITH_PROJ` to `OFF` to disable use of that library. -## Using LuaJIT +### Using LuaJIT To speed up Lua tag transformations, [LuaJIT](https://luajit.org/) can be optionally enabled on supported platforms. This can speed up processing @@ -233,6 +221,12 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +## Maintainers + +The current maintainers of osm2pgsql are [Sarah Hoffmann](https://github.com/lonvia/), +[Paul Norman](https://github.com/pnorman/) and +[Jochen Topf](https://github.com/joto). + ## Contributing We welcome contributions to osm2pgsql. See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/cmake/FindOsmium.cmake b/cmake/FindOsmium.cmake index 651c09a72..17be19666 100644 --- a/cmake/FindOsmium.cmake +++ b/cmake/FindOsmium.cmake @@ -32,8 +32,6 @@ # io - include libraries needed for any type of input/output # geos - include if you want to use any of the GEOS functions # gdal - include if you want to use any of the OGR functions -# proj - include if you want to use any of the Proj.4 functions -# sparsehash - include if you use the sparsehash index (deprecated!) # lz4 - include support for LZ4 compression of PBF files # # You can check for success with something like this: @@ -205,57 +203,6 @@ if(Osmium_USE_GDAL) endif() endif() -#---------------------------------------------------------------------- -# Component 'proj' -if(Osmium_USE_PROJ) - find_path(PROJ_INCLUDE_DIR proj_api.h) - find_library(PROJ_LIBRARY NAMES proj) - - list(APPEND OSMIUM_EXTRA_FIND_VARS PROJ_INCLUDE_DIR PROJ_LIBRARY) - if(PROJ_INCLUDE_DIR AND PROJ_LIBRARY) - set(PROJ_FOUND 1) - list(APPEND OSMIUM_LIBRARIES ${PROJ_LIBRARY}) - list(APPEND OSMIUM_INCLUDE_DIRS ${PROJ_INCLUDE_DIR}) - else() - message(WARNING "Osmium: PROJ.4 library is required but not found, please install it or configure the paths.") - endif() -endif() - -#---------------------------------------------------------------------- -# Component 'sparsehash' -if(Osmium_USE_SPARSEHASH) - message(WARNING "Osmium: Use of Google SparseHash is deprecated. Please switch to a different index type.") - find_path(SPARSEHASH_INCLUDE_DIR google/sparsetable) - - list(APPEND OSMIUM_EXTRA_FIND_VARS SPARSEHASH_INCLUDE_DIR) - if(SPARSEHASH_INCLUDE_DIR) - # Find size of sparsetable::size_type. This does not work on older - # CMake versions because they can do this check only in C, not in C++. - if(NOT CMAKE_VERSION VERSION_LESS 3.0) - include(CheckTypeSize) - set(CMAKE_REQUIRED_INCLUDES ${SPARSEHASH_INCLUDE_DIR}) - set(CMAKE_EXTRA_INCLUDE_FILES "google/sparsetable") - check_type_size("google::sparsetable::size_type" SPARSETABLE_SIZE_TYPE LANGUAGE CXX) - set(CMAKE_EXTRA_INCLUDE_FILES) - set(CMAKE_REQUIRED_INCLUDES) - else() - set(SPARSETABLE_SIZE_TYPE ${CMAKE_SIZEOF_VOID_P}) - endif() - - # Sparsetable::size_type must be at least 8 bytes (64bit), otherwise - # OSM object IDs will not fit. - if(SPARSETABLE_SIZE_TYPE GREATER 7) - set(SPARSEHASH_FOUND 1) - add_definitions(-DOSMIUM_WITH_SPARSEHASH=${SPARSEHASH_FOUND}) - list(APPEND OSMIUM_INCLUDE_DIRS ${SPARSEHASH_INCLUDE_DIR}) - else() - message(WARNING "Osmium: Disabled Google SparseHash library on 32bit system (size_type=${SPARSETABLE_SIZE_TYPE}).") - endif() - else() - message(WARNING "Osmium: Google SparseHash library is required but not found, please install it or configure the paths.") - endif() -endif() - #---------------------------------------------------------------------- list(REMOVE_DUPLICATES OSMIUM_INCLUDE_DIRS) diff --git a/contrib/CLI11/LICENSE b/contrib/CLI11/LICENSE new file mode 100644 index 000000000..715be0bb4 --- /dev/null +++ b/contrib/CLI11/LICENSE @@ -0,0 +1,25 @@ +CLI11 2.2 Copyright (c) 2017-2024 University of Cincinnati, developed by Henry +Schreiner under NSF AWARD 1414736. All rights reserved. + +Redistribution and use in source and binary forms of CLI11, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/contrib/CLI11/README.contrib b/contrib/CLI11/README.contrib new file mode 100644 index 000000000..304172b0d --- /dev/null +++ b/contrib/CLI11/README.contrib @@ -0,0 +1,2 @@ +Source: https://github.com/CLIUtils/CLI11 +Revision: v2.4.2 diff --git a/contrib/CLI11/README.md b/contrib/CLI11/README.md new file mode 100644 index 000000000..0f3229913 --- /dev/null +++ b/contrib/CLI11/README.md @@ -0,0 +1,1857 @@ +# CLI11: Command line parser for C++11 + +![CLI11 Logo](./docs/CLI11_300.png) + +[![Build Status Azure][azure-badge]][azure] +[![Actions Status][actions-badge]][actions-link] +[![Code Coverage][codecov-badge]][codecov] +[![Codacy Badge][codacy-badge]][codacy-link] +[![License: BSD][license-badge]](./LICENSE) [![DOI][doi-badge]][doi-link] + +[![Gitter chat][gitter-badge]][gitter] +[![Latest GHA release][releases-badge]][github releases] +[![Latest release][repology-badge]][repology] +[![Conan.io][conan-badge]][conan-link] +[![Conda Version][conda-badge]][conda-link] +[![Try CLI11 2.4 online][wandbox-badge]][wandbox-link] + +[What's new](./CHANGELOG.md) • [Documentation][gitbook] • [API +Reference][api-docs] + +CLI11 is a command line parser for C++11 and beyond that provides a rich feature +set with a simple and intuitive interface. + +## Table of Contents + +- [CLI11: Command line parser for C++11](#cli11-command-line-parser-for-c11) + - [Table of Contents](#table-of-contents) + - [Background](#background) + - [Introduction](#introduction) + - [Why write another CLI parser?](#why-write-another-cli-parser) + - [Other parsers](#other-parsers) + - [Features not supported by this library](#features-not-supported-by-this-library) + - [Install](#install) + - [Usage](#usage) + - [Adding options](#adding-options) + - [Option types](#option-types) + - [Example](#example) + - [Option options](#option-options) + - [Validators](#validators) + - [Transforming Validators](#transforming-validators) + - [Validator operations](#validator-operations) + - [Custom Validators](#custom-validators) + - [Querying Validators](#querying-validators) + - [Getting results](#getting-results) + - [Subcommands](#subcommands) + - [Subcommand options](#subcommand-options) + - [Callbacks](#callbacks) + - [Option groups](#option-groups) + - [Configuration file](#configuration-file) + - [Inheriting defaults](#inheriting-defaults) + - [Formatting](#formatting) + - [Subclassing](#subclassing) + - [How it works](#how-it-works) + - [Unicode support](#unicode-support) + - [Note on using Unicode paths](#note-on-using-unicode-paths) + - [Utilities](#utilities) + - [Other libraries](#other-libraries) + - [API](#api) + - [Examples](#examples) + - [Contribute](#contribute) + - [License](#license) + +Features that were added in the last released minor version are marked with +"🆕". Features only available in main are marked with "🚧". + +## Background + +### Introduction + +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 [Azure][] and +[GitHub Actions][actions-link], and was originally used by 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 in-depth +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. Also see the [Version 1.0 post][], [Version 1.3 +post][], [Version 1.6 post][], or [Version 2.0 post][] for more information. + +You can be notified when new releases are made by subscribing to + on an RSS reader, like Feedly, +or use the releases mode of the GitHub watching tool. + +### Why write another CLI parser? + +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**). +- Short, simple syntax: This is one of the main reasons to use 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.8+ (default on CentOS/RHEL 7), Clang + 3.4+, AppleClang 7+, NVCC 7.0+, or MSVC 2015+. +- Work on Linux, macOS, and Windows. +- Well tested on all common platforms and compilers. "Well" is defined as having + good coverage measured by [CodeCov][]. +- Clear help printing. +- Nice error messages. +- 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. +- Easy to extend as part of a framework that provides "applications" to users. +- Usable subcommand syntax, with support for multiple subcommands, nested + subcommands, option groups, and optional fallthrough (explained later). +- Ability to add a configuration file (`TOML`, `INI`, or custom format), and + produce it as well. +- Produce real values that can be used directly in code, not something you have + pay compute time to look up, for HPC applications. +- Work with common types, simple custom types, and extensible to exotic types. +- Permissively licensed. + +### Other parsers + +
The major CLI parsers for C++ include, with my biased opinions: (click to expand)

+ +| Library | My biased opinion | +| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Boost Program Options][] | A great library if you already depend on Boost, but its pre-C++11 syntax is really odd and setting up the correct call in the main function is poorly documented (and is nearly a page of code). A simple wrapper for the Boost library was originally developed, but was discarded as CLI11 became more powerful. The idea of capturing a value and setting it originated with Boost PO. [See this comparison.][cli11-po-compare] | +| [The Lean Mean C++ Option Parser][] | One header file is great, but the syntax is atrocious, in my opinion. It was quite impractical to wrap the syntax or to use in a complex project. It seems to handle standard parsing quite well. | +| [TCLAP][] | The not-quite-standard command line parsing causes common shortcuts to fail. It also seems to be poorly supported, with only minimal bugfixes accepted. Header only, but in quite a few files. Has not managed to get enough support to move to GitHub yet. No subcommands. Produces wrapped values. | +| [Cxxopts][] | C++11, single file, and nice CMake support, but requires regex, therefore GCC 4.8 (CentOS 7 default) does not work. Syntax closely based on Boost PO, so not ideal but familiar. | +| [DocOpt][] | Completely different approach to program options in C++11, you write the docs and the interface is generated. Too fragile and specialized. | + +After I wrote this, I also found the following libraries: + +| Library | My biased opinion | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [GFlags][] | The Google Commandline Flags library. Uses macros heavily, and is limited in scope, missing things like subcommands. It provides a simple syntax and supports config files/env vars. | +| [GetOpt][] | Very limited C solution with long, convoluted syntax. Does not support much of anything, like help generation. Always available on UNIX, though (but in different flavors). | +| [ProgramOptions.hxx][] | Interesting library, less powerful and no subcommands. Nice callback system. | +| [Args][] | Also interesting, and supports subcommands. I like the optional-like design, but CLI11 is cleaner and provides direct value access, and is less verbose. | +| [Argument Aggregator][] | I'm a big fan of the [fmt][] library, and the try-catch statement looks familiar. :thumbsup: Doesn't seem to support subcommands. | +| [Clara][] | Simple library built for the excellent [Catch][] testing framework. Unique syntax, limited scope. | +| [Argh!][] | Very minimalistic C++11 parser, single header. Don't have many features. No help generation?!?! At least it's exception-free. | +| [CLI][] | Custom language and parser. Huge build-system overkill for very little benefit. Last release in 2009, but still occasionally active. | +| [argparse][] | C++17 single file argument parser. Design seems similar to CLI11 in some ways. The author has several other interesting projects. | +| [lyra][] | a simple header only parser with composable options. Might work well for simple standardized parsing | + +See [Awesome C++][] for a less-biased list of parsers. You can also find other +single file libraries at [Single file libs][]. + +

+
+ +None of these libraries fulfill all the above requirements, or really even come +close. As you probably have already guessed, CLI11 does. So, this library was +designed to provide a great syntax, good compiler compatibility, and minimal +installation fuss. + +### Features not supported by this library + +There are some other possible "features" that are intentionally not supported by +this library: + +- Non-standard variations on syntax, like `-long` options. This is non-standard + and should be avoided, so that is enforced by this library. +- Completion of partial options, such as Python's `argparse` supplies for + incomplete arguments. It's better not to guess. Most third party command line + parsers for python actually reimplement command line parsing rather than using + argparse because of this perceived design flaw (recent versions do have an + option to disable it). +- Autocomplete: This might eventually be added to both Plumbum and CLI11, but it + is not supported yet. + +## Install + +To use, the most common methods are described here additional methods and +details are available at [installation][]: + +- All-in-one local header: Copy `CLI11.hpp` from the [most recent + release][github releases] into your include directory, and you are set. This + is combined from the source files for every release. This includes the entire + command parser library, but does not include separate utilities (like `Timer`, + `AutoTimer`). The utilities are completely self contained and can be copied + separately. +- All-in-one global header: Like above, but copying the file to a shared folder + location like `/opt/CLI11`. Then, the C++ include path has to be extended to + point at this folder. With CMake 3.5+, use `include_directories(/opt/CLI11)` +- For other methods including using CMake or vcpkg and some specific + instructions for GCC 8 or WASI see [installation][]. + +## Usage + +### Adding options + +To set up, add options, and run, your main function will look something like +this: + +```cpp +int main(int argc, char** argv) { + CLI::App app{"App description"}; + argv = app.ensure_utf8(argv); + + std::string filename = "default"; + app.add_option("-f,--file", filename, "A help string"); + + CLI11_PARSE(app, argc, argv); + return 0; +} +``` + +For more information about 🆕`ensure_utf8` the section on +[Unicode support](#unicode-support) below. The 🆕`ensure_utf8` function is only +available in main currently and not in a release. + +
Note: If you don't like macros, this is what that macro expands to: (click to expand)

+ +```cpp +try { + app.parse(argc, argv); +} catch (const CLI::ParseError &e) { + return app.exit(e); +} +``` + +The try/catch block ensures that `-h,--help` or a parse error will exit with the +correct return code (selected from `CLI::ExitCodes`). (The return here should be +inside `main`). You should not assume that the option values have been set +inside the catch block; for example, help flags intentionally short-circuit all +other processing for speed and to ensure required options and the like do not +interfere. + +

+
+ +The initialization is just one line, adding options is just two each. The parse +macro is just one line (or 5 for the contents of the macro). After the app runs, +the filename will be set to the correct value if it was passed, otherwise it +will be set to the default. You can check to see if this was passed on the +command line with `app.count("--file")`. + +#### Option types + +While all options internally are the same type, there are several ways to add an +option depending on what you need. The supported values are: + +```cpp +// Add options +app.add_option(option_name, help_str="") + +app.add_option(option_name, + variable_to_bind_to, // bool, char(see note), int, float, vector, enum, std::atomic, or string-like, or anything with a defined conversion from a string or that takes an int, double, or string in a constructor. Also allowed are tuples, std::array or std::pair. Also supported are complex numbers, wrapper types, and containers besides vectors of any other supported type. + help_string="") + +app.add_option_function(option_name, + function , // type can be any type supported by add_option + help_string="") + +// char as an option type is supported before 2.0 but in 2.0 it defaulted to allowing single non numerical characters in addition to the numeric values. + +// There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value. For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type +app.add_option(option_name, + T &output, // output must be assignable or constructible from a value of type XC + help_string="") + +// Add flags +app.add_flag(option_name, + help_string="") + +app.add_flag(option_name, + variable_to_bind_to, // bool, int, float, complex, containers, enum, std::atomic, or string-like, or any singular object with a defined conversion from a string like add_option + help_string="") + +app.add_flag_function(option_name, + function , + help_string="") + +app.add_flag_callback(option_name,function,help_string="") + +// Add subcommands +App* subcom = app.add_subcommand(name, description); + +Option_group *app.add_option_group(name,description); +``` + +An option name may start with any character except ('-', ' ', '\n', and '!'). +For long options, after the first character all characters are allowed except +('=',':','{',' ', '\n'). For the `add_flag*` functions '{' and '!' have special +meaning which is why they are not allowed. 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 the help line for its positional form. + +The `add_option_function(...` function will typically require the template +parameter be given unless a `std::function` object with an exact match is +passed. The type can be any type supported by the `add_option` function. The +function should throw an error (`CLI::ConversionError` or `CLI::ValidationError` +possibly) if the value is not valid. + +The two parameter template overload can be used in cases where you want to +restrict the input such as + +```cpp +double val +app.add_option("-v",val); +``` + +which would first verify the input is convertible to an `unsigned int` before +assigning it. Or using some variant type + +```cpp +using vtype=std::variant; + vtype v1; +app.add_option("--vs",v1); +app.add_option("--vi",v1); +app.add_option("--vf",v1); +``` + +otherwise the output would default to a string. The `add_option` can be used +with any integral or floating point types, enumerations, or strings. Or any type +that takes an int, double, or std\::string in an assignment operator or +constructor. If an object can take multiple varieties of those, std::string +takes precedence, then double then int. To better control which one is used or +to use another type for the underlying conversions use the two parameter +template to directly specify the conversion type. + +Types such as (std or boost) `optional`, `optional`, and +`optional` and any other wrapper types are supported directly. For +purposes of CLI11 wrapper types are those which `value_type` definition. See +[CLI11 Advanced Topics/Custom Converters][] for information on how you can add +your own converters for additional types. + +Vector types can also be used in the two parameter template overload + +```cpp +std::vector v1; +app.add_option,int>("--vs",v1); +``` + +would load a vector of doubles but ensure all values can be represented as +integers. + +Automatic direct capture of the default string is disabled when using the two +parameter template. Use `set_default_str(...)` or +`->default_function(std::string())` to set the default string or capture +function directly for these cases. + +Flag options specified through the `add_flag*` functions allow a syntax for the +option names to default particular options to a false value or any other value +if some flags are passed. For example: + +```cpp +app.add_flag("--flag,!--no-flag",result,"help for flag"); +``` + +specifies that if `--flag` is passed on the command line result will be true or +contain a value of 1. If `--no-flag` is passed `result` will contain false or -1 +if `result` is a signed integer type, or 0 if it is an unsigned type. An +alternative form of the syntax is more explicit: `"--flag,--no-flag{false}"`; +this is equivalent to the previous example. This also works for short form +options `"-f,!-n"` or `"-f,-n{false}"`. If `variable_to_bind_to` is anything but +an integer value the default behavior is to take the last value given, while if +`variable_to_bind_to` is an integer type the behavior will be to sum all the +given arguments and return the result. This can be modified if needed by +changing the `multi_option_policy` on each flag (this is not inherited). The +default value can be any value. For example if you wished to define a numerical +flag: + +```cpp +app.add_flag("-1{1},-2{2},-3{3}",result,"numerical flag") +``` + +Using any of those flags on the command line will result in the specified number +in the output. Similar things can be done for string values, and enumerations, +as long as the default value can be converted to the given type. + +On a `C++14` compiler, you can pass a callback function directly to `.add_flag`, +while in C++11 mode you'll need to use `.add_flag_function` if you want a +callback function. The function will be given the number of times the flag was +passed. You can throw a relevant `CLI::ParseError` to signal a failure. + +#### Example + +- `"one,-o,--one"`: Valid as long as not a flag, would create an option that can + be specified positionally, or with `-o` or `--one` +- `"this"` Can only be passed positionally +- `"-a,-b,-c"` No limit to the number of non-positional option names + +The add commands return a pointer to an internally stored `Option`. This option +can be used directly to check for the count (`->count()`) after parsing to avoid +a string based lookup. + +#### Option options + +Before parsing, you can set the following options: + +- `->required()`: The program will quit if this option is not present. This is + `mandatory` in Plumbum, but required options seems to be a more standard term. + For compatibility, `->mandatory()` also works. +- `->expected(N)`: Take `N` values instead of as many as possible, only for + vector args. If negative, require at least `-N`; end with `--` or another + recognized option or subcommand. +- `->expected(MIN,MAX)`: Set a range of expected values to accompany an option. + `expected(0,1)` is the equivalent of making a flag. +- `->type_name(typename)`: Set the name of an Option's type (`type_name_fn` + allows a function instead) +- `->type_size(N)`: Set the intrinsic size of an option value. The parser will + require multiples of this number if negative. Most of the time this is + detected automatically though can be modified for specific use cases. +- `->type_size(MIN,MAX)`: Set the intrinsic size of an option to a range. +- `->needs(opt)`: This option requires another option to also be present, opt is + an `Option` pointer. Options can be removed from the `needs` with + `remove_needs(opt)`. The option can also be specified with a string containing + the name of the option +- `->excludes(opt)`: This option cannot be given with `opt` present, opt is an + `Option` pointer. Can also be given as a string containing the name of the + option. Options can be removed from the excludes list with + `->remove_excludes(opt)` +- `->envname(name)`: Gets the value from the environment if present and not + passed on the command line. 🆕 The value must also pass any validators to be + used. +- `->group(name)`: The help group to put the option in. No effect for positional + options. Defaults to `"Options"`. Options given an empty string will not show + up in the help print (hidden). +- `->ignore_case()`: Ignore the case on the command line (also works on + subcommands, does not affect arguments). +- `->ignore_underscore()`: Ignore any underscores in the options names (also + works on subcommands, does not affect arguments). For example "option_one" + will match with "optionone". This does not apply to short form options since + they only have one character +- `->disable_flag_override()`: From the command line long form flag options can + be assigned a value on the command line using the `=` notation `--flag=value`. + If this behavior is not desired, the `disable_flag_override()` disables it and + will generate an exception if it is done on the command line. The `=` does not + work with short form flag options. +- `->allow_extra_args(true/false)`: If set to true the option will take an + unlimited number of arguments like a vector, if false it will limit the number + of arguments to the size of the type used in the option. Default value depends + on the nature of the type use, containers default to true, others default to + false. +- `->delimiter(char)`: Allows specification of a custom delimiter for separating + single arguments into vector arguments, for example specifying + `->delimiter(',')` on an option would result in `--opt=1,2,3` producing 3 + elements of a vector and the equivalent of --opt 1 2 3 assuming opt is a + vector value. +- `->description(str)`: Set/change the description. +- `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option + policy. Shortcuts available: `->take_last()`, `->take_first()`,`->take_all()`, + and `->join()`. This will only affect options expecting 1 argument or bool + flags (which do not inherit their default but always start with a specific + policy). `->join(delim)` can also be used to join with a specific delimiter. + This equivalent to calling `->delimiter(delim)` and `->join()`. Valid values + are `CLI::MultiOptionPolicy::Throw`, `CLI::MultiOptionPolicy::Throw`, + `CLI::MultiOptionPolicy::TakeLast`, `CLI::MultiOptionPolicy::TakeFirst`, + `CLI::MultiOptionPolicy::Join`, `CLI::MultiOptionPolicy::TakeAll`, + `CLI::MultiOptionPolicy::Sum`, and `CLI::MultiOptionPolicy::Reverse` 🆕. +- `->check(std::string(const std::string &), validator_name="",validator_description="")`: + Define a check function. The function should return a non empty string with + the error message if the check fails +- `->check(Validator)`: Use a Validator object to do the check see + [Validators](#validators) for a description of available Validators and how to + create new ones. +- `->transform(std::string(std::string &), validator_name="",validator_description=")`: + Converts the input string into the output string, in-place in the parsed + options. +- `->transform(Validator)`: Uses a Validator object to do the transformation see + [Validators](#validators) for a description of available Validators and how to + create new ones. +- `->each(void(const std::string &)>`: Run this function on each value received, + as it is received. It should throw a `ValidationError` if an error is + encountered. +- `->configurable(false)`: Disable this option from being in a configuration + file. +- `->capture_default_str()`: Store the current value attached and display it in + the help string. +- `->default_function(std::string())`: Advanced: Change the function that + `capture_default_str()` uses. +- `->always_capture_default()`: Always run `capture_default_str()` when creating + new options. Only useful on an App's `option_defaults`. +- `->default_str(string)`: Set the default string directly (NO VALIDATION OR + CALLBACKS). This string will also be used as a default value if no arguments + are passed and the value is requested. +- `->default_val(value)`: Generate the default string from a value and validate + that the value is also valid. For options that assign directly to a value type + the value in that type is also updated. Value must be convertible to a + string(one of known types or have a stream operator). The callback may be + triggered if the `run_callback_for_default` is set. +- `->run_callback_for_default()`: This will force the option callback to be + executed or the variable set when the `default_val` is set. +- `->option_text(string)`: Sets the text between the option name and + description. +- `->force_callback()`: Causes the option callback or value set to be triggered + even if the option was not present in parsing. +- `->trigger_on_parse()`: If set, causes the callback and all associated + validation checks for the option to be executed when the option value is + parsed vs. at the end of all parsing. This could cause the callback to be + executed multiple times. Also works with positional options. + +These options return the `Option` pointer, so you can chain them together, and +even skip storing the pointer entirely. The `each` function takes any function +that has the signature `void(const std::string&)`; it should throw a +`ValidationError` when validation fails. The help message will have the name of +the parent option prepended. Since `each`, `check` and `transform` use the same +underlying mechanism, you can chain as many as you want, and they will be +executed in order. Operations added through `transform` are executed first in +reverse order of addition, and `check` and `each` are run following the +transform functions in order of addition. If you just want to see the +unconverted values, use `.results()` to get the `std::vector` of +results. + +On the command line, options can be given as: + +- `-a` (flag) +- `-abc` (flags can be combined) +- `-f filename` (option) +- `-ffilename` (no space required) +- `-abcf filename` (flags and option can be combined) +- `--long` (long flag) +- `--long_flag=true` (long flag with equals to override default value) +- `--file filename` (space) +- `--file=filename` (equals) + +If `allow_windows_style_options()` is specified in the application or subcommand +options can also be given as: + +- `/a` (flag) +- `/f filename` (option) +- `/long` (long flag) +- `/file filename` (space) +- `/file:filename` (colon) +- `/long_flag:false` (long flag with : to override the default value) + - Windows style options do not allow combining short options or values not + separated from the short option like with `-` options + +Long flag options may be given with an `=` to allow specifying a false +value, or some other value to the flag. See [config files](#configuration-file) +for details on the values supported. NOTE: only the `=` or `:` for windows-style +options may be used for this, using a space will result in the argument being +interpreted as a positional argument. This syntax can override the default +values, and can be disabled by using `disable_flag_override()`. + +Extra positional arguments will cause the program to exit, so at least one +positional option with a vector is recommended if you want to allow extraneous +arguments. If you set `.allow_extras()` on the main `App`, you will not get an +error. You can access the missing options using `remaining` (if you have +subcommands, `app.remaining(true)` will get all remaining options, subcommands +included). If the remaining arguments are to processed by another `App` then the +function `remaining_for_passthrough()` can be used to get the remaining +arguments in reverse order such that `app.parse(vector)` works directly and +could even be used inside a subcommand callback. + +You can access a vector of pointers to the parsed options in the original order +using `parse_order()`. If `--` is present in the command line that does not end +an unlimited option, then everything after that is positional only. + +#### Validators + +Validators are structures to check or modify inputs, they can be used to verify +that an input meets certain criteria or transform it into another value. They +are added through the `check` or `transform` functions. The differences between +the two function are that checks do not modify the input whereas transforms can +and are executed before any Validators added through `check`. + +CLI11 has several Validators built-in that perform some common checks + +- `CLI::IsMember(...)`: Require an option be a member of a given set. See + [Transforming Validators](#transforming-validators) for more details. +- `CLI::Transformer(...)`: Modify the input using a map. See + [Transforming Validators](#transforming-validators) for more details. +- `CLI::CheckedTransformer(...)`: Modify the input using a map, and require that + the input is either in the set or already one of the outputs of the set. See + [Transforming Validators](#transforming-validators) for more details. +- `CLI::AsNumberWithUnit(...)`: Modify the ` ` pair by matching + the unit and multiplying the number by the corresponding factor. It can be + used as a base for transformers, that accept things like size values (`1 KB`) + or durations (`0.33 ms`). +- `CLI::AsSizeValue(...)`: Convert inputs like `100b`, `42 KB`, `101 Mb`, + `11 Mib` to absolute values. `KB` can be configured to be interpreted as 10^3 + or 2^10. +- `CLI::ExistingFile`: Requires that the file exists if given. +- `CLI::ExistingDirectory`: Requires that the directory exists. +- `CLI::ExistingPath`: Requires that the path (file or directory) exists. +- `CLI::NonexistentPath`: Requires that the path does not exist. +- `CLI::FileOnDefaultPath`: Best used as a transform, Will check that a file + exists either directly or in a default path and update the path appropriately. + See [Transforming Validators](#transforming-validators) for more details +- `CLI::Range(min,max)`: Requires that the option be between min and max (make + sure to use floating point if needed). Min defaults to 0. +- `CLI::Bounded(min,max)`: Modify the input such that it is always between min + and max (make sure to use floating point if needed). Min defaults to 0. Will + produce an error if conversion is not possible. +- `CLI::PositiveNumber`: Requires the number be greater than 0 +- `CLI::NonNegativeNumber`: Requires the number be greater or equal to 0 +- `CLI::Number`: Requires the input be a number. +- `CLI::ValidIPV4`: Requires that the option be a valid IPv4 string e.g. + `'255.255.255.255'`, `'10.1.1.7'`. +- `CLI::TypeValidator`:Requires that the option be convertible to the + specified type e.g. `CLI::TypeValidator()` would require that + the input be convertible to an `unsigned int` regardless of the end + conversion. + +These Validators can be used by simply passing the name into the `check` or +`transform` methods on an option + +```cpp +->check(CLI::ExistingFile); +->check(CLI::Range(0,10)); +``` + +Validators can be merged using `&` and `|` and inverted using `!`. For example: + +```cpp +->check(CLI::Range(0,10)|CLI::Range(20,30)); +``` + +will produce a check to ensure a value is between 0 and 10 or 20 and 30. + +```cpp +->check(!CLI::PositiveNumber); +``` + +will produce a check for a number less than or equal to 0. + +##### Transforming Validators + +There are a few built in Validators that let you transform values if used with +the `transform` function. If they also do some checks then they can be used +`check` but some may do nothing in that case. + +- `CLI::Bounded(min,max)` will bound values between min and max and values + outside of that range are limited to min or max, it will fail if the value + cannot be converted and produce a `ValidationError` +- The `IsMember` Validator lets you specify a set of predefined options. You can + pass any container or copyable pointer (including `std::shared_ptr`) to a + container to this Validator; the container just needs to be iterable and have + a `::value_type`. The key type should be convertible from a string, You can + use an initializer list directly if you like. If you need to modify the set + later, the pointer form lets you do that; the type message and check will + correctly refer to the current version of the set. The container passed in can + be a set, vector, or a map like structure. If used in the `transform` method + the output value will be the matching key as it could be modified by filters. + +After specifying a set of options, you can also specify "filter" functions of +the form `T(T)`, where `T` is the type of the values. The most common choices +probably will be `CLI::ignore_case` an `CLI::ignore_underscore`, and +`CLI::ignore_space`. These all work on strings but it is possible to define +functions that work on other types. Here are some examples of `IsMember`: + +- `CLI::IsMember({"choice1", "choice2"})`: Select from exact match to choices. +- `CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore)`: + Match things like `Choice_1`, too. +- `CLI::IsMember(std::set({2,3,4}))`: Most containers and types work; you + just need `std::begin`, `std::end`, and `::value_type`. +- `CLI::IsMember(std::map({{"one", 1}, {"two", 2}}))`: You + can use maps; in `->transform()` these replace the matched value with the + matched key. The value member of the map is not used in `IsMember`, so it can + be any type. +- `auto p = std::make_shared>(std::initializer_list("one", "two")); CLI::IsMember(p)`: + You can modify `p` later. +- The `Transformer` and `CheckedTransformer` Validators transform one value into + another. Any container or copyable pointer (including `std::shared_ptr`) to a + container that generates pairs of values can be passed to these `Validator's`; + the container just needs to be iterable and have a `::value_type` that + consists of pairs. The key type should be convertible from a string, and the + value type should be convertible to a string You can use an initializer list + directly if you like. If you need to modify the map later, the pointer form + lets you do that; the description message will correctly refer to the current + version of the map. `Transformer` does not do any checking so values not in + the map are ignored. `CheckedTransformer` takes an extra step of verifying + that the value is either one of the map key values, in which case it is + transformed, or one of the expected output values, and if not will generate a + `ValidationError`. A Transformer placed using `check` will not do anything. + +After specifying a map of options, you can also specify "filter" just like in +`CLI::IsMember`. Here are some examples (`Transformer` and `CheckedTransformer` +are interchangeable in the examples) of `Transformer`: + +- `CLI::Transformer({{"key1", "map1"},{"key2","map2"}})`: Select from key values + and produce map values. +- `CLI::Transformer(std::map({"two",2},{"three",3},{"four",4}}))`: + most maplike containers work, the `::value_type` needs to produce a pair of + some kind. +- `CLI::CheckedTransformer(std::map({{"one", 1}, {"two", 2}}))`: + You can use maps; in `->transform()` these replace the matched key with the + value. `CheckedTransformer` also requires that the value either match one of + the keys or match one of known outputs. +- `auto p = std::make_shared>(std::initializer_list>({"key1", "map1"},{"key2","map2"})); CLI::Transformer(p)`: + You can modify `p` later. `TransformPairs` is an alias for + `std::vector>` + +NOTES: If the container used in `IsMember`, `Transformer`, or +`CheckedTransformer` has a `find` function like `std::unordered_map` or +`std::map` then that function is used to do the searching. If it does not have a +`find` function a linear search is performed. If there are filters present, the +fast search is performed first, and if that fails a linear search with the +filters on the key values is performed. + +- `CLI::FileOnDefaultPath(default_path)`: can be used to check for files in a + default path. If used as a transform it will first check that a file exists, + if it does nothing further is done, if it does not it tries to add a default + Path to the file and search there again. If the file does not exist an error + is returned normally but this can be disabled using + `CLI::FileOnDefaultPath(default_path, false)`. This allows multiple paths to + be chained using multiple transform calls. + +- `CLI::EscapedString`: 🆕 can be used to process an escaped string. The + processing is equivalent to that used for TOML config files, see + [TOML strings](https://toml.io/en/v1.0.0#string). With 2 notable exceptions. + \` can also be used as a literal string notation, and it also allows binary + string notation see + [binary strings](https://cliutils.github.io/CLI11/book/chapters/config.html). + The escaped string processing will remove outer quotes if present, `"` will + indicate a string with potential escape sequences, `'` and \` will indicate a + literal string and the quotes removed but no escape sequences will be + processed. This is the same escape processing as used in config files. + +##### Validator operations + +Validators are copyable and have a few operations that can be performed on them +to alter settings. Most of the built in Validators have a default description +that is displayed in the help. This can be altered via +`.description(validator_description)`. The name of a Validator, which is useful +for later reference from the `get_validator(name)` method of an `Option` can be +set via `.name(validator_name)` The operation function of a Validator can be set +via `.operation(std::function)`. The `.active()` +function can activate or deactivate a Validator from the operation. A validator +can be set to apply only to a specific element of the output. For example in a +pair option `std::pair` the first element may need to be a +positive integer while the second may need to be a valid file. The +`.application_index(int)` function can specify this. It is zero based and +negative indices apply to all values. + +```cpp +opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0)); +opt->check(CLI::Validator(CLI::ExistingFile).application_index(1)); +``` + +All the validator operation functions return a Validator reference allowing them +to be chained. For example + +```cpp +opt->check(CLI::Range(10,20).description("range is limited to sensible values").active(false).name("range")); +``` + +will specify a check on an option with a name "range", but deactivate it for the +time being. The check can later be activated through + +```cpp +opt->get_validator("range")->active(); +``` + +##### Custom Validators + +A validator object with a custom function can be created via + +```cpp +CLI::Validator(std::function,validator_description,validator_name=""); +``` + +or if the operation function is set later they can be created with + +```cpp +CLI::Validator(validator_description); +``` + +It is also possible to create a subclass of `CLI::Validator`, in which case it +can also set a custom description function, and operation function. + +##### Querying Validators + +Once loaded into an Option, a pointer to a named Validator can be retrieved via + +```cpp +opt->get_validator(name); +``` + +This will retrieve a Validator with the given name or throw a +`CLI::OptionNotFound` error. If no name is given or name is empty the first +unnamed Validator will be returned or the first Validator if there is only one. + +or + +```cpp +opt->get_validator(index); +``` + +Which will return a validator in the index it is applied which isn't necessarily +the order in which was defined. The pointer can be `nullptr` if an invalid index +is given. Validators have a few functions to query the current values: + +- `get_description()`: Will return a description string +- `get_name()`: Will return the Validator name +- `get_active()`: Will return the current active state, true if the Validator is + active. +- `get_application_index()`: Will return the current application index. +- `get_modifying()`: Will return true if the Validator is allowed to modify the + input, this can be controlled via the `non_modifying()` method, though it is + recommended to let `check` and `transform` option methods manipulate it if + needed. + +#### Getting results + +In most cases, the fastest and easiest way is to return the results through a +callback or variable specified in one of the `add_*` functions. But there are +situations where this is not possible or desired. For these cases the results +may be obtained through one of the following functions. Please note that these +functions will do any type conversions and processing during the call so should +not used in performance critical code: + +- `->results()`: Retrieves a vector of strings with all the results in the order + they were given. +- `->results(variable_to_bind_to)`: Gets the results according to the + MultiOptionPolicy and converts them just like the `add_option_function` with a + variable. +- `Value=opt->as()`: Returns the result or default value directly as the + specified type if possible, can be vector to return all results, and a + non-vector to get the result according to the MultiOptionPolicy in place. + +### Subcommands + +Subcommands are keywords that invoke a new set of options and features. For +example, the `git` command has a long series of subcommands, like `add` and +`commit`. Each can have its own options and implementations. Subcommands are +supported in CLI11, and can be nested infinitely. To add a subcommand, call the +`add_subcommand` method with a name and an optional description. This gives a +pointer to an `App` that behaves just like the main app, and can take options or +further subcommands. Add `->ignore_case()` to a subcommand to allow any +variation of caps to also be accepted. `->ignore_underscore()` is similar, but +for underscores. Children inherit the current setting from the parent. You +cannot add multiple matching subcommand names at the same level (including +`ignore_case` and `ignore_underscore`). + +If you want to require that at least one subcommand is given, use +`.require_subcommand()` on the parent app. You can optionally give an exact +number of subcommands to require, as well. If you give two arguments, that sets +the min and max number allowed. 0 for the max number allowed will allow an +unlimited number of subcommands. As a handy shortcut, a single negative value N +will set "up to N" values. Limiting the maximum number allows you to keep +arguments that match a previous subcommand name from matching. + +If an `App` (main or subcommand) has been parsed on the command line, `->parsed` +will be true (or convert directly to bool). All `App`s have a +`get_subcommands()` method, which returns a list of pointers to the subcommands +passed on the command line. A `got_subcommand(App_or_name)` method is also +provided that will check to see if an `App` pointer or a string name was +collected on the command line. + +For many cases, however, using an app's callback capabilities may be easier. +Every app has a set of callbacks that can be executed at various stages of +parsing; a `C++` lambda function (with capture to get parsed values) can be used +as input to the callback definition function. If you throw `CLI::Success` or +`CLI::RuntimeError(return_value)`, you can even exit the program through the +callback. + +Multiple subcommands are allowed, to allow [`Click`][click] like series of +commands (order is preserved). The same subcommand can be triggered multiple +times but all positional arguments will take precedence over the second and +future calls of the subcommand. `->count()` on the subcommand will return the +number of times the subcommand was called. The subcommand callback will only be +triggered once unless the `.immediate_callback()` flag is set or the callback is +specified through the `parse_complete_callback()` function. The +`final_callback()` is triggered only once. In which case the callback executes +on completion of the subcommand arguments but after the arguments for that +subcommand have been parsed, and can be triggered multiple times. + +Subcommands may also have an empty name either by calling `add_subcommand` with +an empty string for the name or with no arguments. Nameless subcommands function +a similarly to groups in the main `App`. See [Option groups](#option-groups) to +see how this might work. If an option is not defined in the main App, all +nameless subcommands are checked as well. This allows for the options to be +defined in a composable group. The `add_subcommand` function has an overload for +adding a `shared_ptr` so the subcommand(s) could be defined in different +components and merged into a main `App`, or possibly multiple `Apps`. Multiple +nameless subcommands are allowed. Callbacks for nameless subcommands are only +triggered if any options from the subcommand were parsed. Subcommand names given +through the `add_subcommand` method have the same restrictions as option names. + +🆕 Options or flags in a subcommand may be directly specified using dot notation + +- `--subcommand.long=val` (long subcommand option) +- `--subcommand.long val` (long subcommand option) +- `--subcommand.f=val` (short form subcommand option) +- `--subcommand.f val` (short form subcommand option) +- `--subcommand.f` (short form subcommand flag) +- `--subcommand1.subsub.f val` (short form nested subcommand option) + +The use of dot notation in this form is equivalent `--subcommand.long ` => +`subcommand --long ++`. Nested subcommands also work `sub1.subsub` would +trigger the subsub subcommand in `sub1`. This is equivalent to "sub1 subsub". +Quotes around the subcommand names are permitted 🆕 following the TOML standard +for such specification. This includes allowing escape sequences. For example +`"subcommand".'f'` or `"subcommand.with.dots".arg1 = value`. + +#### Subcommand options + +There are several options that are supported on the main app and subcommands and +option_groups. These are: + +- `.ignore_case()`: Ignore the case of this subcommand. Inherited by added + subcommands, so is usually used on the main `App`. +- `.ignore_underscore()`: Ignore any underscores in the subcommand name. + Inherited by added subcommands, so is usually used on the main `App`. +- `.allow_windows_style_options()`: Allow command line options to be parsed in + the form of `/s /long /file:file_name.ext` This option does not change how + options are specified in the `add_option` calls or the ability to process + options in the form of `-s --long --file=file_name.ext`. +- `.fallthrough()`: Allow extra unmatched options and positionals to "fall + through" and be matched on a parent option. Subcommands always are allowed to + "fall through" as in they will first attempt to match on the current + subcommand and if they fail will progressively check parents for matching + subcommands. +- `.configurable()`: Allow the subcommand to be triggered from a configuration + file. By default subcommand options in a configuration file do not trigger a + subcommand but will just update default values. +- `.disable()`: Specify that the subcommand is disabled, if given with a bool + value it will enable or disable the subcommand or option group. +- `.disabled_by_default()`: Specify that at the start of parsing the + subcommand/option_group should be disabled. This is useful for allowing some + Subcommands to trigger others. +- `.enabled_by_default()`: Specify that at the start of each parse the + subcommand/option_group should be enabled. This is useful for allowing some + Subcommands to disable others. +- `.silent()`: Specify that the subcommand is silent meaning that if used it + won't show up in the subcommand list. This allows the use of subcommands as + modifiers +- `.validate_positionals()`: Specify that positionals should pass validation + before matching. Validation is specified through `transform`, `check`, and + `each` for an option. If an argument fails validation it is not an error and + matching proceeds to the next available positional or extra arguments. +- `.validate_optional_arguments()`: Specify that optional arguments should pass + validation before being assigned to an option. Validation is specified through + `transform`, `check`, and `each` for an option. If an argument fails + validation it is not an error and matching proceeds to the next available + positional subcommand or extra arguments. +- `.excludes(option_or_subcommand)`: If given an option pointer or pointer to + another subcommand, these subcommands cannot be given together. In the case of + options, if the option is passed the subcommand cannot be used and will + generate an error. +- `.needs(option_or_subcommand)`: If given an option pointer or pointer to + another subcommand, the subcommands will require the given option to have been + given before this subcommand is validated which occurs prior to execution of + any callback or after parsing is completed. +- `.require_option()`: Require 1 or more options or option groups be used. +- `.require_option(N)`: Require `N` options or option groups, if `N>0`, or up to + `N` if `N<0`. `N=0` resets to the default to 0 or more. +- `.require_option(min, max)`: Explicitly set min and max allowed options or + option groups. Setting `max` to 0 implies unlimited options. +- `.require_subcommand()`: Require 1 or more subcommands. +- `.require_subcommand(N)`: Require `N` subcommands if `N>0`, or up to `N` if + `N<0`. `N=0` resets to the default to 0 or more. +- `.require_subcommand(min, max)`: Explicitly set min and max allowed + subcommands. Setting `max` to 0 is unlimited. +- `.add_subcommand(name="", description="")`: Add a subcommand, returns a + pointer to the internally stored subcommand. +- `.add_subcommand(shared_ptr)`: Add a subcommand by shared_ptr, returns a + pointer to the internally stored subcommand. +- `.remove_subcommand(App)`: Remove a subcommand from the app or subcommand. +- `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on + the command line. +- `.get_subcommands(filter)`: The list of subcommands that match a particular + filter function. +- `.add_option_group(name="", description="")`: Add an + [option group](#option-groups) to an App, an option group is specialized + subcommand intended for containing groups of options or other groups for + controlling how options interact. +- `.get_parent()`: Get the parent App or `nullptr` if called on main App. +- `.get_option(name)`: Get an option pointer by option name will throw if the + specified option is not available, nameless subcommands are also searched +- `.get_option_no_throw(name)`: Get an option pointer by option name. This + function will return a `nullptr` instead of throwing if the option is not + available. +- `.get_options(filter)`: Get the list of all defined option pointers (useful + for processing the app for custom output formats). +- `.parse_order()`: Get the list of option pointers in the order they were + parsed (including duplicates). +- `.formatter(fmt)`: Set a formatter, with signature + `std::string(const App*, std::string, AppFormatMode)`. See Formatting for more + details. +- `.description(str)`: Set/change the description. +- `.get_description()`: Access the description. +- `.alias(str)`: set an alias for the subcommand, this allows subcommands to be + called by more than one name. +- `.parsed()`: True if this subcommand was given on the command line. +- `.count()`: Returns the number of times the subcommand was called. +- `.count(option_name)`: Returns the number of times a particular option was + called. +- `.count_all()`: Returns the total number of arguments a particular subcommand + processed, on the main App it returns the total number of processed commands. +- `.name(name)`: Add or change the name. +- `.callback(void() function)`: Set the callback for an app. Either sets the + `pre_parse_callback` or the `final_callback` depending on the value of + `immediate_callback`. See [Subcommand callbacks](#callbacks) for some + additional details. +- `.parse_complete_callback(void() function)`: Set the callback that runs at the + completion of parsing. For subcommands this is executed at the completion of + the single subcommand and can be executed multiple times. See + [Subcommand callbacks](#callbacks) for some additional details. +- `.final_callback(void() function)`: Set the callback that runs at the end of + all processing. This is the last thing that is executed before returning. See + [Subcommand callbacks](#callbacks) for some additional details. +- `.immediate_callback()`: Specifies whether the callback for a subcommand + should be run as a `parse_complete_callback`(true) or `final_callback`(false). + When used on the main app it will execute the main app callback prior to the + callbacks for a subcommand if they do not also have the `immediate_callback` + flag set. It is preferable to use the `parse_complete_callback` or + `final_callback` directly instead of the `callback` and `immediate_callback` + if one wishes to control the ordering and timing of callback. Though + `immediate_callback` can be used to swap them if that is needed. +- `.pre_parse_callback(void(std::size_t) function)`: Set a callback that + executes after the first argument of an application is processed. See + [Subcommand callbacks](#callbacks) for some additional details. +- `.allow_extras()`: Do not throw an error if extra arguments are left over. +- `.positionals_at_end()`: Specify that positional arguments occur as the last + arguments and throw an error if an unexpected positional is encountered. +- `.prefix_command()`: Like `allow_extras`, but stop immediately on the first + unrecognized item. It is ideal for allowing your app or subcommand to be a + "prefix" to calling another app. +- `.usage(message)`: 🆕 Replace text to appear at the start of the help string + after description. +- `.usage(std::string())`: 🆕 Set a callback to generate a string that will + appear at the start of the help string after description. +- `.footer(message)`: Set text to appear at the bottom of the help string. +- `.footer(std::string())`: Set a callback to generate a string that will appear + at the end of the help string. +- `.set_help_flag(name, message)`: Set the help flag name and message, returns a + pointer to the created option. +- `.set_version_flag(name, versionString or callback, help_message)`: Set the + version flag name and version string or callback and optional help message, + returns a pointer to the created option. +- `.set_help_all_flag(name, message)`: Set the help all flag name and message, + returns a pointer to the created option. Expands subcommands. +- `.failure_message(func)`: Set the failure message function. Two provided: + `CLI::FailureMessage::help` and `CLI::FailureMessage::simple` (the default). +- `.group(name)`: Set a group name, defaults to `"Subcommands"`. Setting an + empty string for the name will be hide the subcommand. +- `[option_name]`: retrieve a const pointer to an option given by `option_name` + for Example `app["--flag1"]` will get a pointer to the option for the + "--flag1" value, `app["--flag1"]->as()` will get the results of the + command line for a flag. The operation will throw an exception if the option + name is not valid. + +> [!NOTE] +> +> If you have a fixed number of required positional options, that will match +> before subcommand names. `{}` is an empty filter function, and any positional +> argument will match before repeated subcommand names. + +#### Callbacks + +A subcommand has three optional callbacks that are executed at different stages +of processing. The `preparse_callback` is executed once after the first argument +of a subcommand or application is processed and gives an argument for the number +of remaining arguments to process. For the main app the first argument is +considered the program name, for subcommands the first argument is the +subcommand name. For Option groups and nameless subcommands the first argument +is after the first argument or subcommand is processed from that group. The +second callback is executed after parsing. This is known as the +`parse_complete_callback`. For subcommands this is executed immediately after +parsing and can be executed multiple times if a subcommand is called multiple +times. On the main app this callback is executed after all the +`parse_complete_callback`s for the subcommands are executed but prior to any +`final_callback` calls in the subcommand or option groups. If the main app or +subcommand has a config file, no data from the config file will be reflected in +`parse_complete_callback` on named subcommands. For `option_group`s the +`parse_complete_callback` is executed prior to the `parse_complete_callback` on +the main app but after the `config_file` is loaded (if specified). The +`final_callback` is executed after all processing is complete. After the +`parse_complete_callback` is executed on the main app, the used subcommand +`final_callback` are executed followed by the "final callback" for option +groups. The last thing to execute is the `final_callback` for the `main_app`. +For example say an application was set up like + +```cpp +app.parse_complete_callback(ac1); +app.final_callback(ac2); +auto sub1=app.add_subcommand("sub1")->parse_complete_callback(c1)->preparse_callback(pc1); +auto sub2=app.add_subcommand("sub2")->final_callback(c2)->preparse_callback(pc2); +app.preparse_callback( pa); + +... A bunch of other options +``` + +Then the command line is given as + +```bash +program --opt1 opt1_val sub1 --sub1opt --sub1optb val sub2 --sub2opt sub1 --sub1opt2 sub2 --sub2opt2 val +``` + +- `pa` will be called prior to parsing any values with an argument of 13. +- `pc1` will be called immediately after processing the `sub1` command with a + value of 10. +- `c1` will be called when the `sub2` command is encountered. +- `pc2` will be called with value of 6 after the `sub2` command is encountered. +- `c1` will be called again after the second `sub2` command is encountered. +- `ac1` will be called after processing of all arguments +- `c2` will be called once after processing all arguments. +- `ac2` will be called last after completing all lower level callbacks have been + executed. + +A subcommand is considered terminated when one of the following conditions are +met. + +1. There are no more arguments to process +2. Another subcommand is encountered that would not fit in an optional slot of + the subcommand +3. The `positional_mark` (`--`) is encountered and there are no available + positional slots in the subcommand. +4. The `subcommand_terminator` mark (`++`) is encountered + +Prior to executed a `parse_complete_callback` all contained options are +processed before the callback is triggered. If a subcommand with a +`parse_complete_callback` is called again, then the contained options are reset, +and can be triggered again. + +#### Option groups + +The subcommand method + +```cpp +.add_option_group(name,description) +``` + +Will create an option group, and return a pointer to it. The argument for +`description` is optional and can be omitted. An option group allows creation of +a collection of options, similar to the groups function on options, but with +additional controls and requirements. They allow specific sets of options to be +composed and controlled as a collective. For an example see +[range example](https://github.com/CLIUtils/CLI11/blob/main/examples/ranges.cpp). +Option groups are a specialization of an App so all +[functions](#subcommand-options) that work with an App or subcommand also work +on option groups. Options can be created as part of an option group using the +add functions just like a subcommand, or previously created options can be added +through. The name given in an option group must not contain newlines or null +characters. + +```cpp +ogroup->add_option(option_pointer); +ogroup->add_options(option_pointer); +ogroup->add_options(option1,option2,option3,...); +``` + +The option pointers used in this function must be options defined in the parent +application of the option group otherwise an error will be generated. +Subcommands can also be added via + +```cpp +ogroup->add_subcommand(subcom_pointer); +``` + +This results in the subcommand being moved from its parent into the option +group. + +Options in an option group are searched for a command line match after any +options in the main app, so any positionals in the main app would be matched +first. So care must be taken to make sure of the order when using positional +arguments and option groups. Option groups work well with `excludes` and +`require_options` methods, as an application will treat an option group as a +single option for the purpose of counting and requirements, and an option group +will be considered used if any of the options or subcommands contained in it are +used. Option groups allow specifying requirements such as requiring 1 of 3 +options in one group and 1 of 3 options in a different group. Option groups can +contain other groups as well. Disabling an option group will turn off all +options within the group. + +The `CLI::TriggerOn` and `CLI::TriggerOff` methods are helper functions to allow +the use of options/subcommands from one group to trigger another group on or +off. + +```cpp +CLI::TriggerOn(group1_pointer, triggered_group); +CLI::TriggerOff(group2_pointer, disabled_group); +``` + +These functions make use of `preparse_callback`, `enabled_by_default()` and +`disabled_by_default`. The triggered group may be a vector of group pointers. +These methods should only be used once per group and will override any previous +use of the underlying functions. More complex arrangements can be accomplished +using similar methodology with a custom `preparse_callback` function that does +more. + +Additional helper functions `deprecate_option` and `retire_option` are available +to deprecate or retire options + +```cpp +CLI::deprecate_option(option *, replacement_name=""); +CLI::deprecate_option(App,option_name,replacement_name=""); +``` + +will specify that the option is deprecated which will display a message in the +help and a warning on first usage. Deprecated options function normally but will +add a message in the help and display a warning on first use. + +```cpp +CLI::retire_option(App,option *); +CLI::retire_option(App,option_name); +``` + +will create an option that does nothing by default and will display a warning on +first usage that the option is retired and has no effect. If the option exists +it is replaces with a dummy option that takes the same arguments. + +If an empty string is passed the option group name the entire group will be +hidden in the help results. For example. + +```cpp +auto hidden_group=app.add_option_group(""); +``` + +will create a group such that no options in that group are displayed in the help +string. + +### Configuration file + +```cpp +app.set_config(option_name="", + default_file_name="", + help_string="Read an ini file", + required=false) +``` + +If this is called with no arguments, it will remove the configuration file +option (like `set_help_flag`). Setting a configuration option is special. If it +is present, it will be read along with the normal command line arguments. The +file will be read if it exists, and does not throw an error unless `required` is +`true`. Configuration files are in [TOML][] format by default, though the +default reader can also accept files in INI format as well. The config reader +can read most aspects of TOML files including strings both literal 🆕 and with +potential escape sequences 🆕, digit separators 🆕, and multi-line strings 🆕, +and run them through the CLI11 parser. Other formats can be added by an adept +user, some variations are available through customization points in the default +formatter. An example of a TOML file: + +```toml +# Comments are supported, using a # +# The default section is [default], case insensitive + +value = 1 +value2 = 123_456 # a string with separators +str = "A string" +str2 = "A string\nwith new lines" +str3 = 'A literal "string"' +vector = [1,2,3] +str_vector = ["one","two","and three"] + +# Sections map to subcommands +[subcommand] +in_subcommand = Wow +sub.subcommand = true +"sub"."subcommand2" = "string_value" +``` + +or equivalently in INI format + +```ini +; Comments are supported, using a ; +; The default section is [default], case insensitive + +value = 1 +str = "A string" +vector = 1 2 3 +str_vector = "one" "two" "and three" + +; Sections map to subcommands +[subcommand] +in_subcommand = Wow +sub.subcommand = true +``` + +Spaces before and after the name and argument are ignored. Multiple arguments +are separated by spaces. One set of quotes will be removed, preserving spaces +(the same way the command line works). Boolean options can be `true`, `on`, `1`, +`yes`, `enable`; or `false`, `off`, `0`, `no`, `disable` (case insensitive). +Sections (and `.` separated names) are treated as subcommands (note: this does +not necessarily mean that subcommand was passed, it just sets the "defaults"). +You cannot set positional-only arguments. Subcommands can be triggered from +configuration files if the `configurable` flag was set on the subcommand. Then +the use of `[subcommand]` notation will trigger a subcommand and cause it to act +as if it were on the command line. + +To print a configuration file from the passed arguments, use +`.config_to_str(default_also=false, write_description=false)`, where +`default_also` will also show any defaulted arguments, and `write_description` +will include the app and option descriptions. See +[Config files](https://cliutils.github.io/CLI11/book/chapters/config.html) for +some additional details and customization points. + +If it is desired that multiple configuration be allowed. Use + +```cpp +app.set_config("--config")->expected(1, X); +``` + +Where X is some positive number and will allow up to `X` configuration files to +be specified by separate `--config` arguments. Value strings with quote +characters in it will be printed with a single quote. All other arguments will +use double quote. Empty strings will use a double quoted argument. Numerical or +boolean values are not quoted. + +For options or flags which allow 0 arguments to be passed using an empty string +in the config file, `{}`, or `[]` will convert the result to the default value +specified via `default_str` or `default_val` on the option. If no user specified +default is given the result is an empty string or the converted value of an +empty string. + +NOTE: Transforms and checks can be used with the option pointer returned from +set_config like any other option to validate the input if needed. It can also be +used with the built in transform `CLI::FileOnDefaultPath` to look in a default +path as well as the current one. For example + +```cpp +app.set_config("--config")->transform(CLI::FileOnDefaultPath("/to/default/path/")); +``` + +See [Transforming Validators](#transforming-validators) for additional details +on this validator. Multiple transforms or validators can be used either by +multiple calls or using `|` operations with the transform. + +### Inheriting defaults + +Many of the defaults for subcommands and even options are inherited from their +creators. The inherited default values for subcommands are `allow_extras`, +`prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`, +`usage`, `footer`, `immediate_callback` and maximum number of required +subcommands. The help flag existence, name, and description are inherited, as +well. + +Options have defaults for `group`, `required`, `multi_option_policy`, +`ignore_case`, `ignore_underscore`, `delimiter`, and `disable_flag_override`. To +set these defaults, you should set the `option_defaults()` object, for example: + +```cpp +app.option_defaults()->required(); +// All future options will be required +``` + +The default settings for options are inherited to subcommands, as well. + +### Formatting + +The job of formatting help printouts is delegated to a formatter callable object +on Apps and Options. You are free to replace either formatter by calling +`formatter(fmt)` on an `App`, where fmt is any copyable callable with the +correct signature. CLI11 comes with a default App formatter functional, +`Formatter`. It is customizable; you can set `label(key, value)` to replace the +default labels like `REQUIRED`, and `column_width(n)` to set the width of the +columns before you add the functional to the app or option. You can also +override almost any stage of the formatting process in a subclass of either +formatter. If you want to make a new formatter from scratch, you can do that +too; you just need to implement the correct signature. The first argument is a +const pointer to the in question. The formatter will get a `std::string` usage +name as the second option, and a `AppFormatMode` mode for the final option. It +should return a `std::string`. + +The `AppFormatMode` can be `Normal`, `All`, or `Sub`, and it indicates the +situation the help was called in. `Sub` is optional, but the default formatter +uses it to make sure expanded subcommands are called with their own formatter +since you can't access anything but the call operator once a formatter has been +set. + +### Subclassing + +The App class was designed allow toolkits to subclass it, to provide preset +default options (see above) and setup/teardown code. Subcommands remain an +unsubclassed `App`, since those are not expected to need setup and teardown. The +default `App` only adds a help flag, `-h,--help`, than can removed/replaced +using `.set_help_flag(name, help_string)`. You can also set a help-all flag with +`.set_help_all_flag(name, help_string)`; this will expand the subcommands (one +level only). You can remove options if you have pointers to them using +`.remove_option(opt)`. You can add a `pre_callback` override to customize the +after parse but before run behavior, while still giving the user freedom to +`callback` on the main app. + +The most important parse function is `parse(std::vector)`, which +takes a reversed list of arguments (so that `pop_back` processes the args in the +correct order). `get_help_ptr` and `get_config_ptr` give you access to the +help/config option pointers. The standard `parse` manually sets the name from +the first argument, so it should not be in this vector. You can also use +`parse(string, bool)` to split up and parse a single string; the optional +boolean should be set to true if you are including the program name in the +string, and false otherwise. The program name can contain spaces if it is an +existing file, otherwise can be enclosed in quotes(single quote, double quote or +backtick). Embedded quote characters can be escaped with `\`. + +Also, in a related note, the `App` you get a pointer to is stored in the parent +`App` in a `shared_ptr`s (similar to `Option`s) and are deleted when the main +`App` goes out of scope unless the object has another owner. + +### How it works + +Every `add_` option you have seen so far depends on one method that takes a +lambda function. Each of these methods is just making a different lambda +function with capture to populate the option. The function has full access to +the vector of strings, so it knows how many times an option was passed or how +many arguments it received. The lambda returns `true` if it could validate the +option strings, and `false` if it failed. + +Other values can be added as long as they support `operator>>` (and defaults can +be printed if they support `operator<<`). To add a new type, for example, +provide a custom `operator>>` with an `istream` (inside the CLI namespace is +fine if you don't want to interfere with an existing `operator>>`). + +If you wanted to extend this to support a completely new type, use a lambda or +add an overload of the `lexical_cast` function in the namespace of the type you +need to convert to. Some examples of some new parsers for `complex` that +support all of the features of a standard `add_options` call are in +[one of the tests](./tests/NewParseTest.cpp). A simpler example is shown below: + +```cpp +app.add_option("--fancy-count", [](std::vector val){ + std::cout << "This option was given " << val.size() << " times." << std::endl; + return true; + }); +``` + +### Unicode support + +CLI11 supports Unicode and wide strings as defined in the +[UTF-8 Everywhere](http://utf8everywhere.org/) manifesto. In particular: + +- The library can parse a wide version of command-line arguments on Windows, + which are converted internally to UTF-8 (more on this below); +- You can store option values in `std::wstring`, in which case they will be + converted to a correct wide string encoding on your system (UTF-16 on Windows + and UTF-32 on most other systems); +- Instead of storing wide strings, it is recommended to use provided `widen` and + `narrow` functions to convert to and from wide strings when actually necessary + (such as when calling into Windows APIs). + +When using the command line on Windows with unicode arguments, your `main` +function may already receive broken Unicode. Parsing `argv` at that point will +not give you a correct string. To fix this, you have three options; the first is +recommended for cross-platform support: + +1\. Replace `argv` with `app.ensure_utf8(argv)` before any arguments are parsed. +`ensure_utf8` will do nothing on systems where `argv` is already in UTF-8 (Such +as Linux or macOS) and return `argv` unmodified. On Windows, it will discard +`argv` and replace it with a correctly decoded array or arguments from win32 +API. + +```cpp +int main(int argc, char** argv) { + CLI::App app; + argv = app.ensure_utf8(argv); // new argv memory is held by app + // ... + CLI11_PARSE(app, argc, argv); +} +``` + +Be sure you do not modify `argv` before this function call, as the correct +values will be reconstructed using Windows APIs and produced by this call. It +has no effect on other platforms and just passes through `argv`. + +
Other options (click to expand)

+ +2\. Use the Windows-only non-standard `wmain` function, which accepts +`wchar_t *argv[]` instead of `char* argv[]`. Parsing this will allow CLI to +convert wide strings to UTF-8 without losing information. + +```cpp +int wmain(int argc, wchar_t *argv[]) { + CLI::App app; + // ... + CLI11_PARSE(app, argc, argv); +} +``` + +3\. Retrieve arguments yourself by using Windows APIs like +[`CommandLineToArgvW`](https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw) +and pass them to CLI. This is what the library is doing under the hood in +`ensure_utf8`. + +

+
+ +The library provides functions to convert between UTF-8 and wide strings: + +```cpp +namespace CLI { + std::string narrow(const std::wstring &str); + std::string narrow(const wchar_t *str); + std::string narrow(const wchar_t *str, std::size_t size); + std::string narrow(std::wstring_view str); // C++17 + + std::wstring widen(const std::string &str); + std::wstring widen(const char *str); + std::wstring widen(const char *str, std::size_t size); + std::wstring widen(std::string_view str); // C++17 +} +``` + +#### Note on using Unicode paths + +When creating a `filesystem::path` from a UTF-8 path on Windows, you need to +convert it to a wide string first. CLI11 provides a platform-independent +`to_path` function, which will convert a UTF-8 string to path, the right way: + +```cpp +std::string utf8_name = "Hello Halló Привет 你好 👩‍🚀❤️.txt"; + +std::filesystem::path p = CLI::to_path(utf8_name); +std::ifstream stream(CLI::to_path(utf8_name)); +// etc. +``` + +### Utilities + +There are a few other utilities that are often useful in CLI programming. These +are in separate headers, and do not appear in `CLI11.hpp`, but are completely +independent and can be used as needed. The `Timer`/`AutoTimer` class allows you +to easily time a block of code, with custom print output. + +```cpp +{ +CLI::AutoTimer timer {"My Long Process", CLI::Timer::Big}; +some_long_running_process(); +} +``` + +This will create a timer with a title (default: `Timer`), and will customize the +output using the predefined `Big` output (default: `Simple`). Because it is an +`AutoTimer`, it will print out the time elapsed when the timer is destroyed at +the end of the block. If you use `Timer` instead, you can use `to_string` or +`std::cout << timer << std::endl;` to print the time. The print function can be +any function that takes two strings, the title and the time, and returns a +formatted string for printing. + +### Other libraries + +If you use the excellent [Rang][] library to add color to your terminal in a +safe, multi-platform way, you can combine it with CLI11 nicely: + +```cpp +std::atexit([](){std::cout << rang::style::reset;}); +try { + app.parse(argc, argv); +} catch (const CLI::ParseError &e) { + std::cout << (e.get_exit_code()==0 ? rang::fg::blue : rang::fg::red); + return app.exit(e); +} +``` + +This will print help in blue, errors in red, and will reset before returning the +terminal to the user. + +If you are on a Unix-like system, and you'd like to handle control-c and color, +you can add: + +```cpp + #include + void signal_handler(int s) { + std::cout << std::endl << rang::style::reset << rang::fg::red << rang::fg::bold; + std::cout << "Control-C detected, exiting..." << rang::style::reset << std::endl; + std::exit(1); // will call the correct exit func, no unwinding of the stack though + } +``` + +And, in your main function: + +```cpp + // Nice Control-C + struct sigaction sigIntHandler; + sigIntHandler.sa_handler = signal_handler; + sigemptyset(&sigIntHandler.sa_mask); + sigIntHandler.sa_flags = 0; + sigaction(SIGINT, &sigIntHandler, nullptr); +``` + +## API + +The API is [documented here][api-docs]. Also see the [CLI11 tutorial +GitBook][gitbook]. + +## Examples + +Several short examples of different features are included in the repository. A +brief description of each is included here + +- [arg_capture](https://github.com/CLIUtils/CLI11/blob/main/examples/arg_capture.cpp): + Example of capturing all remaining arguments after a specific option, using + subcommand and prefix_command() with an alias. +- [callback_passthrough](https://github.com/CLIUtils/CLI11/blob/main/examples/callback_passthrough.cpp): + Example of directly passing remaining arguments through to a callback function + which generates a CLI11 application based on existing arguments. +- [custom_parse](https://github.com/CLIUtils/CLI11/blob/main/examples/custom_parse.cpp): + Based on [Issue #566](https://github.com/CLIUtils/CLI11/issues/566), example + of custom parser +- [digit_args](https://github.com/CLIUtils/CLI11/blob/main/examples/digit_args.cpp): + Based on [Issue #123](https://github.com/CLIUtils/CLI11/issues/123), uses + digit flags to pass a value +- [enum](https://github.com/CLIUtils/CLI11/blob/main/examples/enum.cpp): Using + enumerations in an option, and the use of + [CheckedTransformer](#transforming-validators) +- [enum_ostream](https://github.com/CLIUtils/CLI11/blob/main/examples/enum_ostream.cpp): + In addition to the contents of example enum.cpp, this example shows how a + custom ostream operator overrides CLI11's enum streaming. +- [formatter](https://github.com/CLIUtils/CLI11/blob/main/examples/formatter.cpp): + Illustrating usage of a custom formatter +- [groups](https://github.com/CLIUtils/CLI11/blob/main/examples/groups.cpp): + Example using groups of options for help grouping and a the timer helper class +- [inter_argument_order](https://github.com/CLIUtils/CLI11/blob/main/examples/inter_argument_order.cpp): + An app to practice mixing unlimited arguments, but still recover the original + order. +- [json](https://github.com/CLIUtils/CLI11/blob/main/examples/json.cpp): Using + JSON as a config file parser +- [modhelp](https://github.com/CLIUtils/CLI11/blob/main/examples/modhelp.cpp): + How to modify the help flag to do something other than default +- [nested](https://github.com/CLIUtils/CLI11/blob/main/examples/nested.cpp): + Nested subcommands +- [option_groups](https://github.com/CLIUtils/CLI11/blob/main/examples/option_groups.cpp): + Illustrating the use of option groups and a required number of options. Based + on [Issue #88](https://github.com/CLIUtils/CLI11/issues/88) to set interacting + groups of options +- [positional_arity](https://github.com/CLIUtils/CLI11/blob/main/examples/positional_arity.cpp): + Illustrating use of `preparse_callback` to handle situations where the number + of arguments can determine which should get parsed, Based on + [Issue #166](https://github.com/CLIUtils/CLI11/issues/166) +- [positional_validation](https://github.com/CLIUtils/CLI11/blob/main/examples/positional_validation.cpp): + Example of how positional arguments are validated using the + `validate_positional` flag, also based on + [Issue #166](https://github.com/CLIUtils/CLI11/issues/166) +- [prefix_command](https://github.com/CLIUtils/CLI11/blob/main/examples/prefix_command.cpp): + Illustrating use of the `prefix_command` flag. +- [ranges](https://github.com/CLIUtils/CLI11/blob/main/examples/ranges.cpp): App + to demonstrate exclusionary option groups based on + [Issue #88](https://github.com/CLIUtils/CLI11/issues/88) +- [shapes](https://github.com/CLIUtils/CLI11/blob/main/examples/shapes.cpp): + Illustrating how to set up repeated subcommands Based on + [gitter discussion](https://gitter.im/CLI11gitter/Lobby?at=5c7af6b965ffa019ea788cd5) +- [simple](https://github.com/CLIUtils/CLI11/blob/main/examples/simple.cpp): A + simple example of how to set up a CLI11 Application with different flags and + options +- [subcom_help](https://github.com/CLIUtils/CLI11/blob/main/examples/subcom_help.cpp): + Configuring help for subcommands +- [subcom_partitioned](https://github.com/CLIUtils/CLI11/blob/main/examples/subcom_partitioned.cpp): + Example with a timer and subcommands generated separately and added to the + main app later. +- [subcommands](https://github.com/CLIUtils/CLI11/blob/main/examples/subcommands.cpp): + Short example of subcommands +- [validators](https://github.com/CLIUtils/CLI11/blob/main/examples/validators.cpp): + Example illustrating use of validators + +## Contribute + +To contribute, open an [issue][github issues] or [pull +request][github pull requests] on GitHub, or ask a question on [gitter][]. There +is also a short note to contributors [here](./.github/CONTRIBUTING.md). This +readme roughly follows the [Standard Readme Style][] and includes a mention of +almost every feature of the library. More complex features are documented in +more detail in the [CLI11 tutorial GitBook][gitbook]. + +This project was created by [Henry Schreiner](https://github.com/henryiii) and +major features were added by [Philip Top](https://github.com/phlptp). Special +thanks to all the contributors +([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Alex Dewar
Alex Dewar

💻
Andrew Hardin
Andrew Hardin

💻
Andrey Zhukov
Andrey Zhukov

💻
Anton
Anton

💻
Artem Trokhymchuk
Artem Trokhymchuk

💻
Benjamin Beichler
Benjamin Beichler

💻
Caleb Zulawski
Caleb Zulawski

📦
Christian Asmussen
Christian Asmussen

📖
Christoph Bachhuber
Christoph Bachhuber

💡 💻
D. Fleury
D. Fleury

💻
Dan Barowy
Dan Barowy

📖
Daniel Mensinger
Daniel Mensinger

📦
DarkWingMcQuack
DarkWingMcQuack

💻
Dominik Steinberger
Dominik Steinberger

💻
Doug Johnston
Doug Johnston

🐛 💻
Dylan Baker
Dylan Baker

📦
Eli Schwartz
Eli Schwartz

💻
Ethan Sifferman
Ethan Sifferman

💻
Fred Helmesjö
Fred Helmesjö

🐛 💻
Henry Schreiner
Henry Schreiner

🐛 📖 💻
Isabella Muerte
Isabella Muerte

📦
Izzy Muerte
Izzy Muerte

💻
Jakob Lover
Jakob Lover

💻
James Gerity
James Gerity

📖
Jesus Briales
Jesus Briales

💻 🐛
Jonas Nilsson
Jonas Nilsson

🐛 💻
Jose Luis Rivero
Jose Luis Rivero

💻
Josh Soref
Josh Soref

🔧
KOLANICH
KOLANICH

📦
Kannan
Kannan

🐛 💻
Khem Raj
Khem Raj

💻
Lars Nielsen
Lars Nielsen

💻
Lucas Czech
Lucas Czech

🐛 💻
Mak Kolybabi
Mak Kolybabi

📖
Marcin Ropa
Marcin Ropa

💻
Marcus Brinkmann
Marcus Brinkmann

🐛 💻
Mathias Soeken
Mathias Soeken

📖
Matt McCormick
Matt McCormick

💻
Max
Max

💻
Michael Hall
Michael Hall

📖
Nathan Hourt
Nathan Hourt

🐛 💻
Nathaniel Hourt
Nathaniel Hourt

💻
Olaf Meeuwissen
Olaf Meeuwissen

💻
Ondřej Čertík
Ondřej Čertík

🐛
Paul le Roux
Paul le Roux

💻 📦
Paweł Bylica
Paweł Bylica

📦
PeteAudinate
PeteAudinate

💻
Peter Azmanov
Peter Azmanov

💻
Peter Harris
Peter Harris

💻
Peter Heywood
Peter Heywood

💻
Philip Top
Philip Top

🐛 📖 💻
Rafi Wiener
Rafi Wiener

🐛 💻
RangeMachine
RangeMachine

💻
Robert Adam
Robert Adam

💻
Ryan Curtin
Ryan Curtin

📖
Ryan Sherlock
Ryan Sherlock

💻
Sam Hocevar
Sam Hocevar

💻
Sean Fisk
Sean Fisk

🐛 💻
Stéphane Del Pino
Stéphane Del Pino

💻
Viacheslav Kroilov
Viacheslav Kroilov

💻
Volker Christian
Volker Christian

💻
almikhayl
almikhayl

💻 📦
ayum
ayum

💻
captainurist
captainurist

💻
christos
christos

💻
deining
deining

📖
dherrera-fb
dherrera-fb

💻
djerius
djerius

💻
dryleev
dryleev

💻
elszon
elszon

💻
ferdymercury
ferdymercury

📖
fpeng1985
fpeng1985

💻
geir-t
geir-t

📦
gostefan
gostefan

💻
ncihnegn
ncihnegn

💻
nurelin
nurelin

💻
polistern
polistern

💻
ryan4729
ryan4729

⚠️
shameekganguly
shameekganguly

💻
+ + + + + + +This project follows the +[all-contributors](https://github.com/all-contributors/all-contributors) +specification. Contributions of any kind welcome! + +## License + +As of version 1.0, this library is available under a 3-Clause BSD license. See +the [LICENSE](./LICENSE) file for details. + +CLI11 was developed at the [University of Cincinnati][] to support of the +[GooFit][] library under [NSF Award 1414736][]. Version 0.9 was featured in a +[DIANA/HEP][] meeting at CERN ([see the slides][diana slides]). Please give it a +try! Feedback is always welcome. + +[doi-badge]: https://zenodo.org/badge/80064252.svg +[doi-link]: https://zenodo.org/badge/latestdoi/80064252 +[azure-badge]: + https://dev.azure.com/CLIUtils/CLI11/_apis/build/status/CLIUtils.CLI11?branchName=main +[azure]: https://dev.azure.com/CLIUtils/CLI11 +[actions-link]: https://github.com/CLIUtils/CLI11/actions +[actions-badge]: + https://github.com/CLIUtils/CLI11/actions/workflows/tests.yml/badge.svg +[repology-badge]: https://repology.org/badge/latest-versions/cli11.svg +[repology]: https://repology.org/project/cli11/versions +[codecov-badge]: + https://codecov.io/gh/CLIUtils/CLI11/branch/main/graph/badge.svg?token=2O4wfs8NJO +[codecov]: https://codecov.io/gh/CLIUtils/CLI11 +[gitter-badge]: https://badges.gitter.im/CLI11gitter/Lobby.svg +[gitter]: https://gitter.im/CLI11gitter/Lobby +[license-badge]: https://img.shields.io/badge/License-BSD-blue.svg +[conan-badge]: https://img.shields.io/badge/conan-io-blue +[conan-link]: https://conan.io/center/cli11 +[conda-badge]: https://img.shields.io/conda/vn/conda-forge/cli11.svg +[conda-link]: https://github.com/conda-forge/cli11-feedstock +[github releases]: https://github.com/CLIUtils/CLI11/releases +[github issues]: https://github.com/CLIUtils/CLI11/issues +[github pull requests]: https://github.com/CLIUtils/CLI11/pulls +[goofit]: https://GooFit.github.io +[plumbum]: https://plumbum.readthedocs.io/en/latest/ +[click]: http://click.pocoo.org +[api-docs]: https://CLIUtils.github.io/CLI11/index.html +[rang]: https://github.com/agauniyal/rang +[boost program options]: + http://www.boost.org/doc/libs/1_63_0/doc/html/program_options.html +[the lean mean c++ option parser]: http://optionparser.sourceforge.net +[tclap]: http://tclap.sourceforge.net +[cxxopts]: https://github.com/jarro2783/cxxopts +[docopt]: https://github.com/docopt/docopt.cpp +[gflags]: https://gflags.github.io/gflags +[getopt]: https://www.gnu.org/software/libc/manual/html_node/Getopt.html +[diana/hep]: http://diana-hep.org +[nsf award 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736 +[university of cincinnati]: http://www.uc.edu +[gitbook]: https://cliutils.github.io/CLI11/book/ +[cli11 advanced topics/custom converters]: + https://cliutils.gitlab.io/CLI11Tutorial/chapters/advanced-topics.html#custom-converters +[programoptions.hxx]: https://github.com/Fytch/ProgramOptions.hxx +[argument aggregator]: https://github.com/vietjtnguyen/argagg +[args]: https://github.com/Taywee/args +[argh!]: https://github.com/adishavit/argh +[fmt]: https://github.com/fmtlib/fmt +[catch]: https://github.com/philsquared/Catch +[clara]: https://github.com/philsquared/Clara +[version 1.0 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-10/ +[version 1.3 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-13/ +[version 1.6 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-16/ +[version 2.0 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-20/ +[wandbox-badge]: https://img.shields.io/badge/try_2.1-online-blue.svg +[wandbox-link]: https://wandbox.org/permlink/9eQyaD1DchlzukRv +[releases-badge]: https://img.shields.io/github/release/CLIUtils/CLI11.svg +[cli11-po-compare]: + https://iscinumpy.gitlab.io/post/comparing-cli11-and-boostpo/ +[diana slides]: + https://indico.cern.ch/event/619465/contributions/2507949/attachments/1448567/2232649/20170424-diana-2.pdf +[awesome c++]: https://github.com/fffaraz/awesome-cpp/blob/master/README.md#cli +[cli]: https://codesynthesis.com/projects/cli/ +[single file libs]: + https://github.com/nothings/single_file_libs/blob/master/README.md +[codacy-badge]: + https://app.codacy.com/project/badge/Grade/2796b969c1b54321a02ad08affec0800 +[codacy-link]: + https://www.codacy.com/gh/CLIUtils/CLI11/dashboard?utm_source=github.com&utm_medium=referral&utm_content=CLIUtils/CLI11&utm_campaign=Badge_Grade +[hunter]: https://docs.hunter.sh/en/latest/packages/pkg/CLI11.html +[standard readme style]: https://github.com/RichardLitt/standard-readme +[argparse]: https://github.com/p-ranav/argparse +[toml]: https://toml.io +[lyra]: https://github.com/bfgroup/Lyra +[installation]: https://cliutils.github.io/CLI11/book/chapters/installation.html diff --git a/contrib/CLI11/include/CLI/App.hpp b/contrib/CLI11/include/CLI/App.hpp new file mode 100644 index 000000000..940b858bd --- /dev/null +++ b/contrib/CLI11/include/CLI/App.hpp @@ -0,0 +1,1448 @@ +// Copyright (c) 2017-2024, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +// IWYU pragma: private, include "CLI/CLI.hpp" + +// [CLI11:public_includes:set] +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// [CLI11:public_includes:end] + +// CLI Library includes +#include "ConfigFwd.hpp" +#include "Error.hpp" +#include "FormatterFwd.hpp" +#include "Macros.hpp" +#include "Option.hpp" +#include "Split.hpp" +#include "StringTools.hpp" +#include "TypeTools.hpp" + +namespace CLI { +// [CLI11:app_hpp:verbatim] + +#ifndef CLI11_PARSE +#define CLI11_PARSE(app, ...) \ + try { \ + (app).parse(__VA_ARGS__); \ + } catch(const CLI::ParseError &e) { \ + return (app).exit(e); \ + } +#endif + +namespace detail { +enum class Classifier { NONE, POSITIONAL_MARK, SHORT, LONG, WINDOWS_STYLE, SUBCOMMAND, SUBCOMMAND_TERMINATOR }; +struct AppFriend; +} // namespace detail + +namespace FailureMessage { +/// Printout a clean, simple message on error (the default in CLI11 1.5+) +CLI11_INLINE std::string simple(const App *app, const Error &e); + +/// Printout the full help string on error (if this fn is set, the old default for CLI11) +CLI11_INLINE std::string help(const App *app, const Error &e); +} // namespace FailureMessage + +/// enumeration of modes of how to deal with extras in config files + +enum class config_extras_mode : char { error = 0, ignore, ignore_all, capture }; + +class App; + +using App_p = std::shared_ptr; + +namespace detail { +/// helper functions for adding in appropriate flag modifiers for add_flag + +template ::value || (sizeof(T) <= 1U), detail::enabler> = detail::dummy> +Option *default_flag_modifiers(Option *opt) { + return opt->always_capture_default(); +} + +/// summing modifiers +template ::value && (sizeof(T) > 1U), detail::enabler> = detail::dummy> +Option *default_flag_modifiers(Option *opt) { + return opt->multi_option_policy(MultiOptionPolicy::Sum)->default_str("0")->force_callback(); +} + +} // namespace detail + +class Option_group; +/// Creates a command line program, with very few defaults. +/** To use, create a new `Program()` instance with `argc`, `argv`, and a help description. The templated + * add_option methods make it easy to prepare options. Remember to call `.start` before starting your + * program, so that the options can be evaluated and the help option doesn't accidentally run your program. */ +class App { + friend Option; + friend detail::AppFriend; + + protected: + // This library follows the Google style guide for member names ending in underscores + + /// @name Basics + ///@{ + + /// Subcommand name or program name (from parser if name is empty) + std::string name_{}; + + /// Description of the current program/subcommand + std::string description_{}; + + /// If true, allow extra arguments (ie, don't throw an error). INHERITABLE + bool allow_extras_{false}; + + /// If ignore, allow extra arguments in the ini file (ie, don't throw an error). INHERITABLE + /// if error error on an extra argument, and if capture feed it to the app + config_extras_mode allow_config_extras_{config_extras_mode::ignore}; + + /// If true, return immediately on an unrecognized option (implies allow_extras) INHERITABLE + bool prefix_command_{false}; + + /// If set to true the name was automatically generated from the command line vs a user set name + bool has_automatic_name_{false}; + + /// If set to true the subcommand is required to be processed and used, ignored for main app + bool required_{false}; + + /// If set to true the subcommand is disabled and cannot be used, ignored for main app + bool disabled_{false}; + + /// Flag indicating that the pre_parse_callback has been triggered + bool pre_parse_called_{false}; + + /// Flag indicating that the callback for the subcommand should be executed immediately on parse completion which is + /// before help or ini files are processed. INHERITABLE + bool immediate_callback_{false}; + + /// This is a function that runs prior to the start of parsing + std::function pre_parse_callback_{}; + + /// This is a function that runs when parsing has finished. + std::function parse_complete_callback_{}; + + /// This is a function that runs when all processing has completed + std::function final_callback_{}; + + ///@} + /// @name Options + ///@{ + + /// The default values for options, customizable and changeable INHERITABLE + OptionDefaults option_defaults_{}; + + /// The list of options, stored locally + std::vector options_{}; + + ///@} + /// @name Help + ///@{ + + /// Usage to put after program/subcommand description in the help output INHERITABLE + std::string usage_{}; + + /// This is a function that generates a usage to put after program/subcommand description in help output + std::function usage_callback_{}; + + /// Footer to put after all options in the help output INHERITABLE + std::string footer_{}; + + /// This is a function that generates a footer to put after all other options in help output + std::function footer_callback_{}; + + /// A pointer to the help flag if there is one INHERITABLE + Option *help_ptr_{nullptr}; + + /// A pointer to the help all flag if there is one INHERITABLE + Option *help_all_ptr_{nullptr}; + + /// A pointer to a version flag if there is one + Option *version_ptr_{nullptr}; + + /// This is the formatter for help printing. Default provided. INHERITABLE (same pointer) + std::shared_ptr formatter_{new Formatter()}; + + /// The error message printing function INHERITABLE + std::function failure_message_{FailureMessage::simple}; + + ///@} + /// @name Parsing + ///@{ + + using missing_t = std::vector>; + + /// Pair of classifier, string for missing options. (extra detail is removed on returning from parse) + /// + /// This is faster and cleaner than storing just a list of strings and reparsing. This may contain the -- separator. + missing_t missing_{}; + + /// This is a list of pointers to options with the original parse order + std::vector