diff --git a/.github/workflows/modules-ci.yml b/.github/workflows/modules-ci.yml new file mode 100644 index 000000000..dd11346e5 --- /dev/null +++ b/.github/workflows/modules-ci.yml @@ -0,0 +1,177 @@ +name: C++20 Modules CI +on: [push, workflow_dispatch, pull_request] + +env: + # Enable verbose output for CMake and tests + VERBOSE: 1 + CTEST_OUTPUT_ON_FAILURE: 1 + +jobs: + ubuntu-clang-modules: + strategy: + matrix: + buildType: [Debug, Release] + runs-on: ubuntu-latest + steps: + - name: Update package list + run: sudo apt update + - name: Install Dependencies + run: | + sudo apt install -y git libssl-dev build-essential libcurl4-openssl-dev libpsl-dev meson libunistring-dev ninja-build wget + # Install Clang 21+ + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 21 + sudo apt install -y libc++-21-dev libc++abi-21-dev + env: + DEBIAN_FRONTEND: noninteractive + - name: Install CMake 3.28+ + run: | + wget -O cmake.sh https://github.com/Kitware/CMake/releases/download/v3.28.3/cmake-3.28.3-linux-x86_64.sh + sudo sh cmake.sh --prefix=/usr/local --skip-license + cmake --version + - name: Checkout + uses: actions/checkout@v5 + - name: Configure + run: | + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=${{ matrix.buildType }} \ + -DCMAKE_CXX_COMPILER=clang++-21 \ + -DCMAKE_C_COMPILER=clang-21 \ + -DCPR_BUILD_MODULES=ON \ + -DCPR_BUILD_TESTS=ON \ + -DCPR_BUILD_TESTS_SSL=ON \ + -DCPR_FORCE_OPENSSL_BACKEND=ON \ + -DCPR_USE_SYSTEM_CURL=OFF \ + -G Ninja + - name: Build + run: cmake --build build --verbose + - name: Test + run: ctest --test-dir build --output-on-failure --repeat until-pass:5 + - name: Verify Module Build + run: | + echo "Contents of build/modules:" + ls -la build/modules/ + if [ -f build/modules/libcpr_module.a ] || [ -f build/modules/libcpr_module.so ] || [ -f build/modules/cpr_module.a ] || find build/modules -name "*cpr_module*" -type f | grep -q .; then + echo "Module library built successfully" + else + echo "Error: Module library not found" + exit 1 + fi + + fedora-gcc-modules: + runs-on: ubuntu-latest + container: "fedora:latest" + steps: + - name: Update package list + run: dnf update -y + - name: Install Dependencies + run: dnf install -y gcc g++ git make openssl-devel libcurl-devel cmake libpsl-devel libunistring-devel meson ninja-build + - name: Checkout + uses: actions/checkout@v5 + - name: Configure + run: | + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCPR_BUILD_MODULES=ON \ + -DCPR_BUILD_TESTS=ON \ + -DCPR_BUILD_TESTS_SSL=ON \ + -DCPR_FORCE_OPENSSL_BACKEND=ON \ + -DCPR_USE_SYSTEM_CURL=OFF \ + -G Ninja + - name: Build + run: cmake --build build --verbose + - name: Test + run: ctest --test-dir build --output-on-failure --repeat until-pass:5 + - name: Verify Module Build + run: | + echo "Searching for module build artifacts:" + find build -name "*cpr_module*" -type f || true + find build -name "cpr.gcm" -type f || true + # GCC stores the compiled module interface (BMI) as cpr.gcm rather than + # a traditional archive when all sources are in FILE_SET CXX_MODULES. + # Also check build/lib/ in case LIBRARY_OUTPUT_PATH redirected the archive. + if find build \( -name "*cpr_module*" -o -name "cpr.gcm" \) -type f | grep -q .; then + echo "Module build artifacts found successfully" + else + echo "Error: No module build artifacts found" + exit 1 + fi + + windows-msvc-modules: + runs-on: windows-latest + steps: + - uses: actions/setup-python@v6 + - name: Install meson + run: pip install meson + - name: Setup MSVC environment + uses: ilammy/msvc-dev-cmd@v1 + - name: Checkout + uses: actions/checkout@v5 + - name: Install dependencies via vcpkg + run: | + vcpkg install curl zlib openssl --triplet x64-windows + shell: pwsh + - name: Configure + run: | + cmake -S . -B build ` + -DCMAKE_BUILD_TYPE=Release ` + -DCMAKE_TOOLCHAIN_FILE="C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ` + -DVCPKG_TARGET_TRIPLET=x64-windows ` + -DCPR_BUILD_MODULES=ON ` + -DCPR_BUILD_TESTS=ON ` + -DCPR_BUILD_TESTS_SSL=OFF ` + -DCPR_USE_SYSTEM_CURL=ON ` + -G "Visual Studio 17 2022" + shell: pwsh + - name: Build + run: cmake --build build --config Release --verbose + - name: Test + run: ctest --test-dir build -C Release --output-on-failure --repeat until-pass:5 + - name: Verify Module Build + run: | + Write-Host "Contents of build/modules/Release:" + Get-ChildItem -Path build/modules/Release -Force + if (!(Test-Path "build/modules/Release/cpr_module.lib") -and !(Get-ChildItem -Path build/modules -Recurse -Filter "*cpr_module*")) { + throw "Module library not found" + } + Write-Host "Module library built successfully" + shell: pwsh + + macos-clang-modules: + runs-on: macos-latest + # Homebrew's clang++ still uses the macOS system libc++, which ships no + # 'libc++.modules.json', so 'import std' cannot be resolved there yet. + # Keep the job visible without blocking the branch until that is sorted out. + continue-on-error: true + steps: + - name: Install Dependencies + run: | + brew install llvm libpsl ninja + - name: Checkout + uses: actions/checkout@v5 + - name: Configure + run: | + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_COMPILER=$(brew --prefix llvm)/bin/clang++ \ + -DCMAKE_C_COMPILER=$(brew --prefix llvm)/bin/clang \ + -DCPR_BUILD_MODULES=ON \ + -DCPR_BUILD_TESTS=ON \ + -DCPR_BUILD_TESTS_SSL=OFF \ + -DCPR_USE_SYSTEM_LIB_PSL=ON \ + -G Ninja + - name: Build + run: cmake --build build --verbose + - name: Test + run: ctest --test-dir build --output-on-failure --repeat until-pass:5 + - name: Verify Module Build + run: | + echo "Contents of build/modules:" + ls -la build/modules/ + if [ -f build/modules/libcpr_module.a ] || [ -f build/modules/libcpr_module.dylib ] || [ -f build/modules/cpr_module.a ] || find build/modules -name "*cpr_module*" -type f | grep -q .; then + echo "Module library built successfully" + else + echo "Error: Module library not found" + exit 1 + fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 360818a0c..40f88bf53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,51 +1,17 @@ -cmake_minimum_required(VERSION 3.18) -project(cpr VERSION 1.15.0 LANGUAGES CXX) - -math(EXPR cpr_VERSION_NUM "${cpr_VERSION_MAJOR} * 0x10000 + ${cpr_VERSION_MINOR} * 0x100 + ${cpr_VERSION_PATCH}" OUTPUT_FORMAT HEXADECIMAL) -configure_file("${cpr_SOURCE_DIR}/cmake/cprver.h.in" "${cpr_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h") - -# Only change the folder behavior if cpr is not a subproject -if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) - set_property(GLOBAL PROPERTY USE_FOLDERS ON) - set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake") - set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) - set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) - set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # generate compile_commands.json to be used by other tools (e.g. vs code) -else() - # Check required c++ standard of parent project - if(CMAKE_CXX_STANDARD) - set(PARENT_CXX_STANDARD ${CMAKE_CXX_STANDARD}) - message(STATUS "CXX standard of parent project: ${PARENT_CXX_STANDARD}") - endif() -endif() - -# Avoid the dll boilerplate code for windows -set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - -if (PARENT_CXX_STANDARD) - # Don't set CMAKE_CXX_STANDARD if it is already set by parent project - if (PARENT_CXX_STANDARD LESS 17) - message(FATAL_ERROR "cpr ${cpr_VERSION} does not support ${PARENT_CXX_STANDARD}. Please use cpr <= 1.9.x") - endif() -else() - # Set standard version if not already set by potential parent project - set(CMAKE_CXX_STANDARD 17) -endif() - -message(STATUS "CXX standard: ${CMAKE_CXX_STANDARD}") -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -set(CPR_LIBRARIES cpr CACHE INTERNAL "") +cmake_minimum_required(VERSION 3.18...4.3) macro(cpr_option OPTION_NAME OPTION_TEXT OPTION_DEFAULT) option(${OPTION_NAME} ${OPTION_TEXT} ${OPTION_DEFAULT}) + if(DEFINED ENV{${OPTION_NAME}}) # Allow overriding the option through an environment variable set(${OPTION_NAME} $ENV{${OPTION_NAME}}) endif() + if(${OPTION_NAME}) add_definitions(-D${OPTION_NAME}) endif() + message(STATUS " ${OPTION_NAME}: ${${OPTION_NAME}}") endmacro() @@ -68,6 +34,7 @@ cpr_option(CPR_FORCE_DARWINSSL_BACKEND "Force to use the DarwinSSL backend. If C cpr_option(CPR_FORCE_MBEDTLS_BACKEND "Force to use the Mbed TLS backend. If CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, and CPR_FORCE_WINSSL_BACKEND are set to to OFF, cpr will try to automatically detect the best available SSL backend (WinSSL - Windows, OpenSSL - Linux, DarwinSSL - Mac ...)." OFF) cpr_option(CPR_ENABLE_LINTING "Set to ON to enable clang linting." OFF) cpr_option(CPR_ENABLE_CPPCHECK "Set to ON to enable Cppcheck static analysis. Requires CPR_BUILD_TESTS and CPR_BUILD_TESTS_SSL to be OFF to prevent checking google tests source code." OFF) +cpr_option(CPR_BUILD_MODULES "Set to ON to build cpr as a C++ module." OFF) cpr_option(CPR_BUILD_TESTS "Set to ON to build cpr tests." OFF) cpr_option(CPR_BUILD_TESTS_SSL "Set to ON to build cpr ssl tests" ${CPR_BUILD_TESTS}) cpr_option(CPR_BUILD_TESTS_PROXY "Set to ON to build proxy tests. They fail in case there is no valid proxy server available in proxy_tests.cpp" OFF) @@ -81,8 +48,77 @@ cpr_option(CPR_DEBUG_SANITIZER_FLAG_UB "Enables the UndefinedBehaviorSanitizer f cpr_option(CPR_DEBUG_SANITIZER_FLAG_ALL "Enables all sanitizers for debug builds except the ThreadSanitizer since it is incompatible with the other sanitizers." OFF) message(STATUS "=======================================================") -if (MSVC) - if (BUILD_SHARED_LIBS) +# If we build cpr as C++20 module, we use `import std;`. This block enables it for different CMake versions. +if(CPR_BUILD_MODULES AND NOT CMAKE_EXPERIMENTAL_CXX_IMPORT_STD AND NOT CMAKE_CXX_MODULE_STD) + if(CMAKE_VERSION VERSION_LESS "3.30") + message(FATAL_ERROR "CPR_BUILD_MODULES requires CMake 3.30 or newer for `import std` support (found ${CMAKE_VERSION}).") + elseif(CMAKE_VERSION VERSION_LESS_EQUAL "3.31.7") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "0e5b6991-d74f-4b3d-a41c-cf096e0b2508") + elseif(CMAKE_VERSION VERSION_LESS_EQUAL "4.2.3") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") + elseif(CMAKE_VERSION VERSION_LESS_EQUAL "4.3.4") + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "451f2fe2-a8a2-47c3-bc32-94786d8fc91b") + else() + set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "f35a9ac6-8463-4d38-8eec-5d6008153e7d") + endif() + # Deliberately NOT setting CMAKE_CXX_MODULE_STD here: as a directory-scope variable it + # initialises the CXX_MODULE_STD property on *every* target created afterwards, including + # FetchContent dependencies (zlib, curl, googletest), which then demand C++ standard library + # module metadata they neither use nor ship. `cpr_module` opts in via its own target property. +endif() + +project(cpr VERSION 1.15.0 LANGUAGES CXX) + +math(EXPR cpr_VERSION_NUM "${cpr_VERSION_MAJOR} * 0x10000 + ${cpr_VERSION_MINOR} * 0x100 + ${cpr_VERSION_PATCH}" OUTPUT_FORMAT HEXADECIMAL) +configure_file("${cpr_SOURCE_DIR}/cmake/cprver.h.in" "${cpr_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h") + +# Only change the folder behavior if cpr is not a subproject +if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake") + set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) + set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # generate compile_commands.json to be used by other tools (e.g. vs code) +else() + # Check required c++ standard of parent project + if(CMAKE_CXX_STANDARD) + set(PARENT_CXX_STANDARD ${CMAKE_CXX_STANDARD}) + message(STATUS "CXX standard of parent project: ${PARENT_CXX_STANDARD}") + endif() +endif() + +# Avoid the dll boilerplate code for windows +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +if(PARENT_CXX_STANDARD) + # Don't set CMAKE_CXX_STANDARD if it is already set by parent project + if(PARENT_CXX_STANDARD LESS 17) + message(FATAL_ERROR "cpr ${cpr_VERSION} does not support ${PARENT_CXX_STANDARD}. Please use cpr <= 1.9.x") + endif() + # `import std;` requires C++23, so we cannot honour a lower standard from the parent project here. + if(CPR_BUILD_MODULES AND PARENT_CXX_STANDARD LESS 23) + message(FATAL_ERROR "CPR_BUILD_MODULES requires C++23 or newer, but the parent project requests C++${PARENT_CXX_STANDARD}.") + endif() +else() + # Set standard version if not already set by potential parent project. + # `import std;` requires C++23, so raise the default for module builds without + # downgrading an explicitly requested higher standard. + if(CPR_BUILD_MODULES) + if(NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 23) + set(CMAKE_CXX_STANDARD 23) + endif() + else() + set(CMAKE_CXX_STANDARD 17) + endif() +endif() + +message(STATUS "CXX standard: ${CMAKE_CXX_STANDARD}") +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CPR_LIBRARIES cpr CACHE INTERNAL "") + +if(MSVC) + if(BUILD_SHARED_LIBS) message(STATUS "Build windows dynamic libs.") else() # Add this to build windows pure static library. @@ -97,7 +133,7 @@ if(CPR_BUILD_VERSION_OUTPUT_ONLY) return() endif() -if (CPR_FORCE_USE_SYSTEM_CURL) +if(CPR_FORCE_USE_SYSTEM_CURL) message(WARNING "The variable CPR_FORCE_USE_SYSTEM_CURL is deprecated, please use CPR_USE_SYSTEM_CURL instead") set(CPR_USE_SYSTEM_CURL ${CPR_FORCE_USE_SYSTEM_CURL}) endif() @@ -121,6 +157,7 @@ if(CPR_ENABLE_CPPCHECK) if(CPR_BUILD_TESTS OR CPR_BUILD_TESTS_SSL) message(FATAL_ERROR "Cppcheck is incompatible with building tests. Make sure to disable CPR_ENABLE_CPPCHECK or disable tests by setting CPR_BUILD_TESTS and CPR_BUILD_TESTS_SSL to OFF. This is because Cppcheck would try to check the google tests source code and then fail. ") endif() + include(cmake/cppcheck.cmake) endif() @@ -134,12 +171,13 @@ if(CPR_ENABLE_SSL) set(DETECT_SSL_BACKEND ON CACHE INTERNAL "" FORCE) endif() - if(CPR_FORCE_WINSSL_BACKEND AND (NOT WIN32)) + if(CPR_FORCE_WINSSL_BACKEND AND(NOT WIN32)) message(FATAL_ERROR "WinSSL is only available on Windows! Use either OpenSSL (CPR_FORCE_OPENSSL_BACKEND) or DarwinSSL (CPR_FORCE_DARWINSSL_BACKEND) instead.") endif() if(DETECT_SSL_BACKEND) message(STATUS "Detecting SSL backend...") + if(WIN32) message(STATUS "SSL auto detect: Using WinSSL.") set(SSL_BACKEND_USED "WinSSL") @@ -149,11 +187,13 @@ if(CPR_ENABLE_SSL) set(SSL_BACKEND_USED "DarwinSSL") else() find_package(OpenSSL) + if(OPENSSL_FOUND) message(STATUS "SSL auto detect: Using OpenSSL.") set(SSL_BACKEND_USED "OpenSSL") else() find_package(MbedTLS) + if(MBEDTLS_FOUND) set(SSL_BACKEND_USED "MbedTLS") else() @@ -164,6 +204,7 @@ if(CPR_ENABLE_SSL) else() if(CPR_FORCE_OPENSSL_BACKEND) find_package(OpenSSL) + if(OPENSSL_FOUND) message(STATUS "Using OpenSSL.") set(SSL_BACKEND_USED "OpenSSL") @@ -186,8 +227,8 @@ if(CPR_ENABLE_SSL) endif() if(SSL_BACKEND_USED STREQUAL "OpenSSL") -# Fix missing OpenSSL includes for Windows since in 'ssl_ctx.cpp' we include OpenSSL directly -find_package(OpenSSL REQUIRED) + # Fix missing OpenSSL includes for Windows since in 'ssl_ctx.cpp' we include OpenSSL directly + find_package(OpenSSL REQUIRED) add_compile_definitions(OPENSSL_BACKEND_USED) endif() @@ -197,15 +238,17 @@ if(CPR_USE_EXISTING_CURL_TARGET) elseif(CPR_USE_SYSTEM_CURL) if(CPR_ENABLE_SSL) find_package(CURL COMPONENTS HTTP HTTPS) + if(CURL_FOUND) message(STATUS "Curl ${CURL_VERSION_STRING} found on this system.") # To be able to load certificates under Windows when using OpenSSL: - if(CMAKE_USE_OPENSSL AND WIN32 AND (NOT (CURL_VERSION_STRING VERSION_GREATER_EQUAL "7.71.0"))) + if(CMAKE_USE_OPENSSL AND WIN32 AND(NOT(CURL_VERSION_STRING VERSION_GREATER_EQUAL "7.71.0"))) message(FATAL_ERROR "Your system curl version (${CURL_VERSION_STRING}) is too old to support OpenSSL on Windows which requires curl >= 7.71.0. Update your curl version, use WinSSL, disable SSL or use the built-in version of curl.") endif() else() find_package(CURL COMPONENTS HTTP) + if(CURL_FOUND) message(FATAL_ERROR "Curl found on this system but WITHOUT HTTPS/SSL support. Either disable SSL by setting CPR_ENABLE_SSL to OFF or use the built-in version of curl by setting CPR_USE_SYSTEM_CURL to OFF.") else() @@ -214,6 +257,7 @@ elseif(CPR_USE_SYSTEM_CURL) endif() else() find_package(CURL COMPONENTS HTTP) + if(CURL_FOUND) message(STATUS "Curl found on this system.") else() @@ -221,8 +265,8 @@ elseif(CPR_USE_SYSTEM_CURL) endif() endif() - # Check for the minimum supported curl version - if(NOT (CURL_VERSION_STRING VERSION_GREATER_EQUAL "7.64.0")) + # Check for the minimum supported curl version + if(NOT(CURL_VERSION_STRING VERSION_GREATER_EQUAL "7.64.0")) message(FATAL_ERROR "Your system curl version (${CURL_VERSION_STRING}) is too old! curl >= 7.64.0 is required. Update your curl version, or use the build in curl version e.g. via `cmake .. -DCPR_USE_SYSTEM_CURL=OFF` during CMake configure.") endif() else() @@ -231,26 +275,28 @@ else() # ZLIB is optional for curl # to disable it: # * from command line: - # -DCURL_ZLIB=OFF + # -DCURL_ZLIB=OFF # * from CMake script: - if (CURL_ZLIB OR CURL_ZLIB STREQUAL AUTO OR NOT DEFINED CACHE{CURL_ZLIB}) + if(CURL_ZLIB OR CURL_ZLIB STREQUAL AUTO OR NOT DEFINED CACHE{CURL_ZLIB}) include(cmake/zlib_external.cmake) endif() - if (CPR_ENABLE_CURL_HTTP_ONLY) + if(CPR_ENABLE_CURL_HTTP_ONLY) # We only need HTTP (and HTTPS) support: set(HTTP_ONLY ON CACHE INTERNAL "" FORCE) endif() + set(BUILD_CURL_EXE OFF CACHE INTERNAL "" FORCE) set(BUILD_TESTING OFF) - if (CURL_VERBOSE_LOGGING) + if(CURL_VERBOSE_LOGGING) message(STATUS "Enabled curl debug features") set(ENABLE_DEBUG ON CACHE INTERNAL "" FORCE) endif() - if (CPR_ENABLE_SSL) + if(CPR_ENABLE_SSL) set(CURL_ENABLE_SSL ON CACHE INTERNAL "" FORCE) + if(ANDROID) set(CURL_CA_PATH "/system/etc/security/cacerts" CACHE INTERNAL "") elseif(CPR_SKIP_CA_BUNDLE_SEARCH) @@ -294,21 +340,23 @@ else() set(CURL_USE_MBEDTLS OFF CACHE INTERNAL "" FORCE) message(STATUS "Disabled curl SSL") endif() + # Disable linting for curl clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) - if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") cmake_policy(SET CMP0135 NEW) endif() # Since curl 8.13, curl depends on lib psl set(CURL_USE_LIBPSL ${CPR_CURL_USE_LIBPSL} CACHE INTERNAL "" FORCE) + if(CPR_CURL_USE_LIBPSL AND NOT CPR_USE_SYSTEM_LIB_PSL) include(libpsl) endif() FetchContent_Declare(curl URL https://github.com/curl/curl/releases/download/curl-8_13_0/curl-8.13.0.tar.xz - URL_HASH SHA256=4a093979a3c2d02de2fbc00549a32771007f2e78032c6faa5ecd2f7a9e152025) # the file hash for curl-8.13.0.tar.xz + URL_HASH SHA256=4a093979a3c2d02de2fbc00549a32771007f2e78032c6faa5ecd2f7a9e152025) # the file hash for curl-8.13.0.tar.xz FetchContent_MakeAvailable(curl) restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) @@ -328,8 +376,10 @@ if(CPR_BUILD_TESTS) if(CPR_USE_SYSTEM_GTEST) find_package(GTest) endif() + if(NOT CPR_USE_SYSTEM_GTEST OR NOT GTEST_FOUND) message(STATUS "Not using system gtest, using built-in googletest project instead.") + if(MSVC) # By default, GTest compiles on Windows in CRT static linkage mode. We use this # variable to force it into using the CRT in dynamic linkage (DLL), just as CPR @@ -341,26 +391,25 @@ if(CPR_BUILD_TESTS) clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) FetchContent_Declare(googletest - URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz - URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 # the file hash for release-1.14.0.tar.gz - USES_TERMINAL_DOWNLOAD TRUE) # <---- This is needed only for Ninja to show download progress + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz + URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 # the file hash for release-1.14.0.tar.gz + USES_TERMINAL_DOWNLOAD TRUE) # <---- This is needed only for Ninja to show download progress FetchContent_MakeAvailable(googletest) restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) - + add_library(gtest_int INTERFACE) target_link_libraries(gtest_int INTERFACE gtest) target_include_directories(gtest_int INTERFACE ${googletest_SOURCE_DIR}/include) add_library(GTest::GTest ALIAS gtest_int) - + # Group under the "tests/gtest" project folder in IDEs such as Visual Studio. - set_property(TARGET gtest PROPERTY FOLDER "tests/gtest") - set_property(TARGET gtest_main PROPERTY FOLDER "tests/gtest") + set_property(TARGET gtest PROPERTY FOLDER "tests/gtest") + set_property(TARGET gtest_main PROPERTY FOLDER "tests/gtest") endif() endif() - # Mongoose configuration if(CPR_BUILD_TESTS) message(STATUS "Building mongoose project for test support.") @@ -382,28 +431,30 @@ if(CPR_BUILD_TESTS) # Disable linting for mongoose clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) - FetchContent_Declare(mongoose - URL https://github.com/cesanta/mongoose/archive/7.7.tar.gz - URL_HASH SHA256=4e5733dae31c3a81156af63ca9aa3a6b9b736547f21f23c3ab2f8e3f1ecc16c0 # the hash for 7.7.tar.gz - USES_TERMINAL_DOWNLOAD TRUE # This is needed only for Ninja to show download progress - SOURCE_SUBDIR "?") # Nonexistent directory to prevent FetchContent_MakeAvailable from calling add_subdirectory and duplicating the mongoose target - if (NOT mongoose_POPULATED) + FetchContent_Declare(mongoose + URL https://github.com/cesanta/mongoose/archive/7.7.tar.gz + URL_HASH SHA256=4e5733dae31c3a81156af63ca9aa3a6b9b736547f21f23c3ab2f8e3f1ecc16c0 # the hash for 7.7.tar.gz + USES_TERMINAL_DOWNLOAD TRUE # This is needed only for Ninja to show download progress + SOURCE_SUBDIR "?") # Nonexistent directory to prevent FetchContent_MakeAvailable from calling add_subdirectory and duplicating the mongoose target + + if(NOT mongoose_POPULATED) FetchContent_MakeAvailable(mongoose) file(INSTALL cmake/mongoose.CMakeLists.txt DESTINATION ${mongoose_SOURCE_DIR}) file(RENAME ${mongoose_SOURCE_DIR}/mongoose.CMakeLists.txt ${mongoose_SOURCE_DIR}/CMakeLists.txt) add_subdirectory(${mongoose_SOURCE_DIR} ${mongoose_BINARY_DIR}) - endif() + # Group under the "external" project folder in IDEs such as Visual Studio. set_property(TARGET mongoose PROPERTY FOLDER "external") restore_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) endif() -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Werror") - if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # Disable C++98 compatibility support in clang: https://github.com/libcpr/cpr/issues/927 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-nonportable-system-include-path -Wno-exit-time-destructors -Wno-undef -Wno-global-constructors -Wno-switch-enum -Wno-old-style-cast -Wno-covered-switch-default -Wno-undefined-func-template") endif() @@ -412,6 +463,10 @@ endif() add_subdirectory(cpr) add_subdirectory(include) +if(CPR_BUILD_MODULES) + add_subdirectory(modules) +endif() + if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND CPR_BUILD_TESTS) # Disable linting for tests since they are currently not up to the standard clear_variable(DESTINATION CMAKE_CXX_CLANG_TIDY BACKUP CMAKE_CXX_CLANG_TIDY_BKP) diff --git a/README.md b/README.md index 5749cad9c..343c9f476 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,7 @@ The only explicit requirements are: * A `C++17` compatible compiler such as Clang or GCC. The minimum required version of GCC is unknown, so if anyone has trouble building this library with a specific version of GCC, do let us know. * In case you only have a `C++11` compatible compiler available, all versions below cpr 1.9.x are for you. The 1.10.0 release of cpr switches to `C++17` as a requirement. +* If you would like to use cpr as a C++20 module, you must have CMake 3.28 enabled. Enable `CPR_BUILD_MODULES` to activate the feature. * If you would like to perform https requests `OpenSSL` and its development libraries are required. * If you do not use the built-in version of [curl](https://github.com/curl/curl) but instead use your systems version, make sure you use a version `>= 7.71.0`. Lower versions are not supported. This means you need Debian `>= 11` or Ubuntu `>= 22.04 LTS`. * [`The Meson Build System`](https://mesonbuild.com/) is required build PSL from source ([PSL support for curl](https://everything.curl.dev/build/deps.html#libpsl)). For more information take a look at the `CPR_CURL_USE_LIBPSL` and `CPR_USE_SYSTEM_LIB_PSL` CMake options. diff --git a/cmake/cprver.h.in b/cmake/cprver.h.in index e35332497..8ef539f90 100644 --- a/cmake/cprver.h.in +++ b/cmake/cprver.h.in @@ -1,17 +1,28 @@ #ifndef CPR_CPRVER_H #define CPR_CPRVER_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD +#include +#include +#endif + /** * CPR version as a string. **/ -#define CPR_VERSION "${cpr_VERSION}" +EXPORT_CPR inline constexpr std::string_view CPR_VERSION{"${cpr_VERSION}"}; /** * CPR version split up into parts. **/ -#define CPR_VERSION_MAJOR ${cpr_VERSION_MAJOR} -#define CPR_VERSION_MINOR ${cpr_VERSION_MINOR} -#define CPR_VERSION_PATCH ${cpr_VERSION_PATCH} +EXPORT_CPR inline constexpr uint8_t CPR_VERSION_MAJOR{${cpr_VERSION_MAJOR}}; +EXPORT_CPR inline constexpr uint8_t CPR_VERSION_MINOR{${cpr_VERSION_MINOR}}; +EXPORT_CPR inline constexpr uint8_t CPR_VERSION_PATCH{${cpr_VERSION_PATCH}}; /** * CPR version as a single hex digit. @@ -25,6 +36,6 @@ * '0x010702' -> 01.07.02 -> CPR_VERSION: 1.7.2 * '0xA13722' -> A1.37.22 -> CPR_VERSION: 161.55.34 **/ -#define CPR_VERSION_NUM ${cpr_VERSION_NUM} +EXPORT_CPR inline constexpr uint64_t CPR_VERSION_NUM{${cpr_VERSION_NUM}}; #endif diff --git a/cpr/CMakeLists.txt b/cpr/CMakeLists.txt index 9f1b303a4..c3a1d04db 100644 --- a/cpr/CMakeLists.txt +++ b/cpr/CMakeLists.txt @@ -69,13 +69,13 @@ if(CPR_USE_SYSTEM_CURL) COMPATIBILITY ExactVersion) if(SSL_BACKEND_USED STREQUAL "OpenSSL") - configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig-ssl.cmake.in - "${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake" - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr) + configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig-ssl.cmake.in + "${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr) else() - configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig.cmake.in - "${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake" - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr) + configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/cprConfig.cmake.in + "${PROJECT_BINARY_DIR}/cpr/cprConfig.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cpr) endif() if (CPR_ENABLE_INSTALL) diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 8bc83fba3..6bb38499c 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources(cpr PRIVATE cpr/curlholder.h cpr/curlholder.h cpr/error.h + cpr/export.h cpr/file.h cpr/limit_rate.h cpr/local_port.h diff --git a/include/cpr/accept_encoding.h b/include/cpr/accept_encoding.h index 65a08e058..fcd9059f8 100644 --- a/include/cpr/accept_encoding.h +++ b/include/cpr/accept_encoding.h @@ -1,17 +1,26 @@ #ifndef CPR_ACCEPT_ENCODING_H #define CPR_ACCEPT_ENCODING_H -#include -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include -#include #include +#endif + +#include +#include +#include namespace cpr { -enum class AcceptEncodingMethods : uint8_t { +EXPORT_CPR enum class AcceptEncodingMethods : uint8_t { identity, deflate, zlib, @@ -20,9 +29,9 @@ enum class AcceptEncodingMethods : uint8_t { }; // NOLINTNEXTLINE(cert-err58-cpp) -static const std::map AcceptEncodingMethodsStringMap{{AcceptEncodingMethods::identity, "identity"}, {AcceptEncodingMethods::deflate, "deflate"}, {AcceptEncodingMethods::zlib, "zlib"}, {AcceptEncodingMethods::gzip, "gzip"}, {AcceptEncodingMethods::disabled, "disabled"}}; +EXPORT_CPR inline const std::map AcceptEncodingMethodsStringMap{{AcceptEncodingMethods::identity, "identity"}, {AcceptEncodingMethods::deflate, "deflate"}, {AcceptEncodingMethods::zlib, "zlib"}, {AcceptEncodingMethods::gzip, "gzip"}, {AcceptEncodingMethods::disabled, "disabled"}}; -class AcceptEncoding { +EXPORT_CPR class AcceptEncoding { public: AcceptEncoding() = default; AcceptEncoding(const std::initializer_list& methods); diff --git a/include/cpr/api.h b/include/cpr/api.h index fa700bed1..01b159dae 100644 --- a/include/cpr/api.h +++ b/include/cpr/api.h @@ -1,27 +1,29 @@ #ifndef CPR_API_H #define CPR_API_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include -#include #include +#endif #include "cpr/async.h" #include "cpr/async_wrapper.h" -#include "cpr/auth.h" -#include "cpr/bearer.h" #include "cpr/cprtypes.h" -#include "cpr/filesystem.h" -#include "cpr/multipart.h" #include "cpr/multiperform.h" -#include "cpr/payload.h" #include "cpr/response.h" #include "cpr/session.h" namespace cpr { -using AsyncResponse = AsyncWrapper; +EXPORT_CPR using AsyncResponse = AsyncWrapper; namespace priv { @@ -114,7 +116,7 @@ void setup_multiasync(std::vector>& responses, T&& } // namespace priv // Get methods -template +EXPORT_CPR template Response Get(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); @@ -122,20 +124,20 @@ Response Get(Ts&&... ts) { } // Get async methods -template +EXPORT_CPR template AsyncResponse GetAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Get(std::move(ts_inner)...); }, std::move(ts)...); } // Get callback methods -template +EXPORT_CPR template // NOLINTNEXTLINE(fuchsia-trailing-return) auto GetCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Get(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Post methods -template +EXPORT_CPR template Response Post(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); @@ -143,20 +145,20 @@ Response Post(Ts&&... ts) { } // Post async methods -template +EXPORT_CPR template AsyncResponse PostAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Post(std::move(ts_inner)...); }, std::move(ts)...); } // Post callback methods -template +EXPORT_CPR template // NOLINTNEXTLINE(fuchsia-trailing-return) auto PostCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Post(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Put methods -template +EXPORT_CPR template Response Put(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); @@ -164,20 +166,20 @@ Response Put(Ts&&... ts) { } // Put async methods -template +EXPORT_CPR template AsyncResponse PutAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Put(std::move(ts_inner)...); }, std::move(ts)...); } // Put callback methods -template +EXPORT_CPR template // NOLINTNEXTLINE(fuchsia-trailing-return) auto PutCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Put(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Head methods -template +EXPORT_CPR template Response Head(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); @@ -185,20 +187,20 @@ Response Head(Ts&&... ts) { } // Head async methods -template +EXPORT_CPR template AsyncResponse HeadAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Head(std::move(ts_inner)...); }, std::move(ts)...); } // Head callback methods -template +EXPORT_CPR template // NOLINTNEXTLINE(fuchsia-trailing-return) auto HeadCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Head(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Delete methods -template +EXPORT_CPR template Response Delete(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); @@ -206,20 +208,20 @@ Response Delete(Ts&&... ts) { } // Delete async methods -template +EXPORT_CPR template AsyncResponse DeleteAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Delete(std::move(ts_inner)...); }, std::move(ts)...); } // Delete callback methods -template +EXPORT_CPR template // NOLINTNEXTLINE(fuchsia-trailing-return) auto DeleteCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Delete(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Options methods -template +EXPORT_CPR template Response Options(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); @@ -227,20 +229,20 @@ Response Options(Ts&&... ts) { } // Options async methods -template +EXPORT_CPR template AsyncResponse OptionsAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Options(std::move(ts_inner)...); }, std::move(ts)...); } // Options callback methods -template +EXPORT_CPR template // NOLINTNEXTLINE(fuchsia-trailing-return) auto OptionsCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Options(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Patch methods -template +EXPORT_CPR template Response Patch(Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); @@ -248,20 +250,20 @@ Response Patch(Ts&&... ts) { } // Patch async methods -template +EXPORT_CPR template AsyncResponse PatchAsync(Ts... ts) { return cpr::async([](Ts... ts_inner) { return Patch(std::move(ts_inner)...); }, std::move(ts)...); } // Patch callback methods -template +EXPORT_CPR template // NOLINTNEXTLINE(fuchsia-trailing-return) auto PatchCallback(Then then, Ts... ts) { return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Patch(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...); } // Download methods -template +EXPORT_CPR template Response Download(std::ofstream& file, Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); @@ -269,7 +271,7 @@ Response Download(std::ofstream& file, Ts&&... ts) { } // Download async method -template +EXPORT_CPR template AsyncResponse DownloadAsync(fs::path local_path, Ts... ts) { return AsyncWrapper{std::async( std::launch::async, @@ -281,7 +283,7 @@ AsyncResponse DownloadAsync(fs::path local_path, Ts... ts) { } // Download with user callback -template +EXPORT_CPR template Response Download(const WriteCallback& write, Ts&&... ts) { Session session; priv::set_option(session, std::forward(ts)...); @@ -289,97 +291,97 @@ Response Download(const WriteCallback& write, Ts&&... ts) { } // Multi requests -template +EXPORT_CPR template std::vector MultiGet(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Get(); } -template +EXPORT_CPR template std::vector MultiDelete(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Delete(); } -template +EXPORT_CPR template std::vector MultiPut(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Put(); } -template +EXPORT_CPR template std::vector MultiHead(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Head(); } -template +EXPORT_CPR template std::vector MultiOptions(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Options(); } -template +EXPORT_CPR template std::vector MultiPatch(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Patch(); } -template +EXPORT_CPR template std::vector MultiPost(Ts&&... ts) { MultiPerform multiperform; priv::setup_multiperform(multiperform, std::forward(ts)...); return multiperform.Post(); } -template +EXPORT_CPR template std::vector> MultiGetAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Get>(ret, std::forward(ts)...); return ret; } -template +EXPORT_CPR template std::vector> MultiDeleteAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Delete>(ret, std::forward(ts)...); return ret; } -template +EXPORT_CPR template std::vector> MultiHeadAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Head>(ret, std::forward(ts)...); return ret; } -template +EXPORT_CPR template std::vector> MultiOptionsAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Options>(ret, std::forward(ts)...); return ret; } -template +EXPORT_CPR template std::vector> MultiPatchAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Patch>(ret, std::forward(ts)...); return ret; } -template +EXPORT_CPR template std::vector> MultiPostAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Post>(ret, std::forward(ts)...); return ret; } -template +EXPORT_CPR template std::vector> MultiPutAsync(Ts&&... ts) { std::vector> ret{}; priv::setup_multiasync<&cpr::Session::Put>(ret, std::forward(ts)...); diff --git a/include/cpr/async.h b/include/cpr/async.h index 01c29454a..43b4844fa 100644 --- a/include/cpr/async.h +++ b/include/cpr/async.h @@ -1,13 +1,15 @@ #ifndef CPR_ASYNC_H #define CPR_ASYNC_H +#include "cpr/export.h" + #include "async_wrapper.h" #include "singleton.h" #include "threadpool.h" namespace cpr { -class GlobalThreadPool : public ThreadPool { +EXPORT_CPR class GlobalThreadPool : public ThreadPool { CPR_SINGLETON_DECL(GlobalThreadPool) protected: GlobalThreadPool() = default; @@ -22,7 +24,7 @@ class GlobalThreadPool : public ThreadPool { * async(std::bind(&Class::mem_fn, &obj)) * async(std::mem_fn(&Class::mem_fn, &obj)) **/ -template +EXPORT_CPR template auto async(Fn&& fn, Args&&... args) { std::future future = GlobalThreadPool::GetInstance()->Submit(std::forward(fn), std::forward(args)...); using async_wrapper_t = AsyncWrapper; @@ -33,7 +35,7 @@ auto async(Fn&& fn, Args&&... args) { } } -class async { +EXPORT_CPR class async { public: static void startup(size_t min_threads = CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM, size_t max_threads = CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM, std::chrono::milliseconds max_idle_ms = CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME) { GlobalThreadPool* gtp = GlobalThreadPool::GetInstance(); diff --git a/include/cpr/async_wrapper.h b/include/cpr/async_wrapper.h index c90ea06c6..14e364b5a 100644 --- a/include/cpr/async_wrapper.h +++ b/include/cpr/async_wrapper.h @@ -1,12 +1,20 @@ #ifndef CPR_ASYNC_WRAPPER_H #define CPR_ASYNC_WRAPPER_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include +#endif namespace cpr { -enum class [[nodiscard]] CancellationResult : uint8_t { failure, success, invalid_operation }; +EXPORT_CPR enum class [[nodiscard]] CancellationResult : uint8_t { failure, success, invalid_operation }; /** * A class template intended to wrap results of async operations (instances of std::future) @@ -14,7 +22,7 @@ enum class [[nodiscard]] CancellationResult : uint8_t { failure, success, invali * * The RAII semantics are the same as std::future - moveable, not copyable. */ -template +EXPORT_CPR template class AsyncWrapper; template @@ -149,10 +157,10 @@ class AsyncWrapper : public AsyncWrapper { }; // Deduction guides -template +EXPORT_CPR template AsyncWrapper(std::future&&) -> AsyncWrapper; -template +EXPORT_CPR template AsyncWrapper(std::future&&, std::shared_ptr&&) -> AsyncWrapper; } // namespace cpr diff --git a/include/cpr/auth.h b/include/cpr/auth.h index 166d1850e..fb6eb726f 100644 --- a/include/cpr/auth.h +++ b/include/cpr/auth.h @@ -1,17 +1,24 @@ #ifndef CPR_AUTH_H #define CPR_AUTH_H -#include -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include +#endif #include "cpr/util.h" +#include namespace cpr { -enum class AuthMode : uint8_t { BASIC, DIGEST, NTLM, NEGOTIATE, ANY, ANYSAFE }; +EXPORT_CPR enum class AuthMode : uint8_t { BASIC, DIGEST, NTLM, NEGOTIATE, ANY, ANYSAFE }; -class Authentication { +EXPORT_CPR class Authentication { public: Authentication(std::string_view username, std::string_view password, AuthMode auth_mode); diff --git a/include/cpr/bearer.h b/include/cpr/bearer.h index 1f448da0c..cf00db19c 100644 --- a/include/cpr/bearer.h +++ b/include/cpr/bearer.h @@ -1,19 +1,26 @@ #ifndef CPR_BEARER_H #define CPR_BEARER_H -#include -#include +#include "cpr/export.h" +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD +#include #include +#endif #include "cpr/util.h" +#include namespace cpr { // Only supported with libcurl >= 7.61.0. // As an alternative use SetHeader and add the token manually. #if LIBCURL_VERSION_NUM >= 0x073D00 -class Bearer { +EXPORT_CPR class Bearer { public: Bearer(std::string_view token) : token_string_{token} {} Bearer(const Bearer& other) = default; diff --git a/include/cpr/body.h b/include/cpr/body.h index 8febe3dcd..b88f56517 100644 --- a/include/cpr/body.h +++ b/include/cpr/body.h @@ -1,10 +1,17 @@ #ifndef CPR_BODY_H #define CPR_BODY_H -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include +#endif #include "cpr/buffer.h" #include "cpr/cprtypes.h" @@ -12,7 +19,7 @@ namespace cpr { -class Body : public StringHolder { +EXPORT_CPR class Body : public StringHolder { public: Body() = default; Body(std::string body) : StringHolder(std::move(body)) {} diff --git a/include/cpr/body_view.h b/include/cpr/body_view.h index 20bd6f366..81562bdeb 100644 --- a/include/cpr/body_view.h +++ b/include/cpr/body_view.h @@ -1,13 +1,21 @@ #ifndef CPR_BODY_VIEW_H #define CPR_BODY_VIEW_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include +#endif #include "cpr/buffer.h" namespace cpr { -class BodyView final { +EXPORT_CPR class BodyView final { public: BodyView() = default; BodyView(std::string_view body) : m_body(body) {} diff --git a/include/cpr/buffer.h b/include/cpr/buffer.h index 04a3e2bdc..4136346bb 100644 --- a/include/cpr/buffer.h +++ b/include/cpr/buffer.h @@ -1,13 +1,14 @@ #ifndef CPR_BUFFER_H #define CPR_BUFFER_H -#include +#include "cpr/export.h" #include "cpr/filesystem.h" +#include namespace cpr { -struct Buffer { +EXPORT_CPR struct Buffer { using data_t = const char*; template diff --git a/include/cpr/callback.h b/include/cpr/callback.h index 64775f0be..4f25980fc 100644 --- a/include/cpr/callback.h +++ b/include/cpr/callback.h @@ -1,18 +1,26 @@ #ifndef CPR_CALLBACK_H #define CPR_CALLBACK_H -#include "cprtypes.h" +#include "cpr/export.h" +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include -#include #include #include #include #include +#endif + +#include "cprtypes.h" +#include namespace cpr { -class ReadCallback { +EXPORT_CPR class ReadCallback { public: ReadCallback() = default; ReadCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{-1}, callback{std::move(p_callback)} {} @@ -29,7 +37,7 @@ class ReadCallback { std::function callback; }; -class HeaderCallback { +EXPORT_CPR class HeaderCallback { public: HeaderCallback() = default; HeaderCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} @@ -44,7 +52,7 @@ class HeaderCallback { std::function callback; }; -class WriteCallback { +EXPORT_CPR class WriteCallback { public: WriteCallback() = default; WriteCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} @@ -59,7 +67,7 @@ class WriteCallback { std::function callback; }; -class ProgressCallback { +EXPORT_CPR class ProgressCallback { public: ProgressCallback() = default; ProgressCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} @@ -74,7 +82,7 @@ class ProgressCallback { std::function callback; }; -class DebugCallback { +EXPORT_CPR class DebugCallback { public: enum class InfoType : uint8_t { TEXT = 0, @@ -101,7 +109,7 @@ class DebugCallback { /** * Functor class for progress functions that will be used in cancellable requests. */ -class CancellationCallback { +EXPORT_CPR class CancellationCallback { public: CancellationCallback() = default; explicit CancellationCallback(std::shared_ptr&& cs) : cancellation_state{std::move(cs)} {} diff --git a/include/cpr/cert_info.h b/include/cpr/cert_info.h index 62182a5b0..7cf62a1ea 100644 --- a/include/cpr/cert_info.h +++ b/include/cpr/cert_info.h @@ -1,13 +1,21 @@ #ifndef CPR_CERT_INFO_H #define CPR_CERT_INFO_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include +#endif namespace cpr { -class CertInfo { +EXPORT_CPR class CertInfo { private: std::vector cert_info_; diff --git a/include/cpr/connect_timeout.h b/include/cpr/connect_timeout.h index d608b98d0..a8008cc42 100644 --- a/include/cpr/connect_timeout.h +++ b/include/cpr/connect_timeout.h @@ -1,11 +1,13 @@ #ifndef CPR_CONNECT_TIMEOUT_H #define CPR_CONNECT_TIMEOUT_H +#include "cpr/export.h" + #include "cpr/timeout.h" namespace cpr { -class ConnectTimeout : public Timeout { +EXPORT_CPR class ConnectTimeout : public Timeout { public: ConnectTimeout(const std::chrono::milliseconds& duration) : Timeout{duration} {} ConnectTimeout(const std::int32_t& milliseconds) : Timeout{milliseconds} {} diff --git a/include/cpr/connection_pool.h b/include/cpr/connection_pool.h index fc75d2767..0453147fd 100644 --- a/include/cpr/connection_pool.h +++ b/include/cpr/connection_pool.h @@ -1,9 +1,18 @@ #ifndef CPR_CONNECTION_POOL_H #define CPR_CONNECTION_POOL_H -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include +#endif + +#include namespace cpr { /** @@ -28,7 +37,7 @@ namespace cpr { * auto future2 = cpr::GetAsync(cpr::Url{"http://example.com/api/more"}, pool); * ``` **/ -class ConnectionPool { +EXPORT_CPR class ConnectionPool { public: /** * Creates a new connection pool with shared connection state. diff --git a/include/cpr/cookies.h b/include/cpr/cookies.h index 68cc2cb19..3b6ebea2b 100644 --- a/include/cpr/cookies.h +++ b/include/cpr/cookies.h @@ -1,20 +1,29 @@ #ifndef CPR_COOKIES_H #define CPR_COOKIES_H -#include "cpr/curlholder.h" +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include #include +#endif + +#include "cpr/curlholder.h" namespace cpr { /** * EXPIRES_STRING_SIZE is an explicitly static and const variable that could be only accessed within the same namespace and is immutable. * To be used for "std::array", the expression must have a constant value, so EXPIRES_STRING_SIZE must be a const value. **/ -static const std::size_t EXPIRES_STRING_SIZE = 100; +EXPORT_CPR inline const std::size_t EXPIRES_STRING_SIZE = 100; -class Cookie { +EXPORT_CPR class Cookie { public: Cookie() = default; /** @@ -45,7 +54,7 @@ class Cookie { std::chrono::system_clock::time_point expires_; }; -class Cookies { +EXPORT_CPR class Cookies { public: /** * Should we URL-encode cookies when making a request. diff --git a/include/cpr/cprtypes.h b/include/cpr/cprtypes.h index 20a86ded5..0c5558224 100644 --- a/include/cpr/cprtypes.h +++ b/include/cpr/cprtypes.h @@ -1,31 +1,40 @@ #ifndef CPR_CPRTYPES_H #define CPR_CPRTYPES_H -#include -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include #include #include +#endif + +#include +#include namespace cpr { /** * Wrapper around "curl_off_t" to prevent applications from having to link against libcurl. **/ -using cpr_off_t = curl_off_t; +EXPORT_CPR using cpr_off_t = curl_off_t; /** * The argument type for progress functions, dependent on libcurl version **/ #if LIBCURL_VERSION_NUM < 0x072000 -using cpr_pf_arg_t = double; +EXPORT_CPR using cpr_pf_arg_t = double; #else -using cpr_pf_arg_t = cpr_off_t; +EXPORT_CPR using cpr_pf_arg_t = cpr_off_t; #endif -template +EXPORT_CPR template class StringHolder { public: private: @@ -139,13 +148,13 @@ class StringHolder { friend T; }; -template +EXPORT_CPR template std::ostream& operator<<(std::ostream& os, const StringHolder& s) { os << s.str(); return os; } -class Url : public StringHolder { +EXPORT_CPR class Url : public StringHolder { public: Url() = default; Url(std::string url) : StringHolder(std::move(url)) {} @@ -161,11 +170,11 @@ class Url : public StringHolder { Url& operator=(const Url& other) = default; }; -struct CaseInsensitiveCompare { +EXPORT_CPR struct CaseInsensitiveCompare { bool operator()(const std::string& a, const std::string& b) const noexcept; }; -using Header = std::map; +EXPORT_CPR using Header = std::map; } // namespace cpr diff --git a/include/cpr/curl_container.h b/include/cpr/curl_container.h index 1be9e62ae..652133e01 100644 --- a/include/cpr/curl_container.h +++ b/include/cpr/curl_container.h @@ -1,24 +1,31 @@ #ifndef CPR_CURL_CONTAINER_H #define CPR_CURL_CONTAINER_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include -#include #include #include +#endif #include "cpr/curlholder.h" namespace cpr { -struct Parameter { +EXPORT_CPR struct Parameter { Parameter(std::string p_key, std::string p_value) : key{std::move(p_key)}, value{std::move(p_value)} {} std::string key; std::string value; }; -struct Pair { +EXPORT_CPR struct Pair { Pair(std::string p_key, std::string p_value) : key(std::move(p_key)), value(std::move(p_value)) {} std::string key; @@ -26,7 +33,7 @@ struct Pair { }; -template +EXPORT_CPR template class CurlContainer { public: /** diff --git a/include/cpr/curlholder.h b/include/cpr/curlholder.h index 35a3a8490..fab4e12c9 100644 --- a/include/cpr/curlholder.h +++ b/include/cpr/curlholder.h @@ -1,15 +1,23 @@ #ifndef CPR_CURLHOLDER_H #define CPR_CURLHOLDER_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include -#include #include +#endif #include "cpr/secure_string.h" +#include namespace cpr { -struct CurlHolder { +EXPORT_CPR struct CurlHolder { private: /** * Mutex for curl_easy_init(). diff --git a/include/cpr/curlmultiholder.h b/include/cpr/curlmultiholder.h index 401df0eb6..7c3d21cce 100644 --- a/include/cpr/curlmultiholder.h +++ b/include/cpr/curlmultiholder.h @@ -1,11 +1,13 @@ #ifndef CPR_CURLMULTIHOLDER_H #define CPR_CURLMULTIHOLDER_H +#include "cpr/export.h" + #include namespace cpr { -class CurlMultiHolder { +EXPORT_CPR class CurlMultiHolder { public: CurlMultiHolder(); ~CurlMultiHolder(); diff --git a/include/cpr/error.h b/include/cpr/error.h index 5cf7e6b9e..6c7f9196c 100644 --- a/include/cpr/error.h +++ b/include/cpr/error.h @@ -1,12 +1,20 @@ #ifndef CPR_ERROR_H #define CPR_ERROR_H -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include +#include +#endif #include "cpr/cprtypes.h" -#include +#include namespace cpr { @@ -14,7 +22,7 @@ namespace cpr { * cpr error codes that match the ones found inside 'curl.h'. * These error codes only include relevant error codes meaning no support for e.g. FTP errors since cpr does only support HTTP. **/ -enum class ErrorCode : uint16_t { +EXPORT_CPR enum class ErrorCode : uint16_t { /** * Everything is good and no error occurred. **/ @@ -90,7 +98,7 @@ enum class ErrorCode : uint16_t { UNKNOWN_ERROR = 1000, }; -inline const std::unordered_map& get_error_code_to_string_mapping() { +EXPORT_CPR inline const std::unordered_map& get_error_code_to_string_mapping() { // Use a function-local static rather than inline global objects to avoid the 'double-destructor' problem in MSVC when using /MT flags. static const std::unordered_map mapping = {{ErrorCode::OK, "OK"}, {ErrorCode::UNSUPPORTED_PROTOCOL, "UNSUPPORTED_PROTOCOL"}, @@ -159,7 +167,7 @@ inline const std::unordered_map& get_error_code_to_strin return mapping; } -class Error { +EXPORT_CPR class Error { public: ErrorCode code = ErrorCode::OK; std::string message; diff --git a/include/cpr/export.h b/include/cpr/export.h new file mode 100644 index 000000000..33694838d --- /dev/null +++ b/include/cpr/export.h @@ -0,0 +1,8 @@ +#ifndef CPR_EXPORT_H +#define CPR_EXPORT_H + +#ifndef EXPORT_CPR +#define EXPORT_CPR +#endif + +#endif diff --git a/include/cpr/file.h b/include/cpr/file.h index 7eefcfdc3..66dba9560 100644 --- a/include/cpr/file.h +++ b/include/cpr/file.h @@ -1,15 +1,23 @@ #ifndef CPR_FILE_H #define CPR_FILE_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include +#endif #include "cpr/filesystem.h" namespace cpr { -struct File { +EXPORT_CPR struct File { explicit File(std::string p_filepath, const std::string& p_overriden_filename = {}) : filepath(std::move(p_filepath)), overriden_filename(p_overriden_filename) {} std::string filepath; @@ -20,7 +28,7 @@ struct File { } }; -class Files { +EXPORT_CPR class Files { public: Files() = default; Files(const File& p_file) : files{p_file} {} diff --git a/include/cpr/filesystem.h b/include/cpr/filesystem.h index 8a3bdafe0..10bfed0b6 100644 --- a/include/cpr/filesystem.h +++ b/include/cpr/filesystem.h @@ -8,14 +8,26 @@ namespace cpr { namespace fs = boost::filesystem; } +#elif defined(CPR_IMPORT_STD) +namespace cpr { +namespace fs = std::filesystem; +} // namespace cpr // cppcheck-suppress preprocessorErrorDirective #elif __has_include() +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include +#endif namespace cpr { namespace fs = std::filesystem; } // namespace cpr #elif __has_include("experimental/filesystem") +#ifndef CPR_IMPORT_STD #include +#endif namespace cpr { namespace fs = std::experimental::filesystem; } diff --git a/include/cpr/http_version.h b/include/cpr/http_version.h index 39b9512f4..ec10c985e 100644 --- a/include/cpr/http_version.h +++ b/include/cpr/http_version.h @@ -1,11 +1,13 @@ #ifndef CPR_HTTP_VERSION_H #define CPR_HTTP_VERSION_H +#include "cpr/export.h" + #include #include namespace cpr { -enum class HttpVersionCode : uint8_t { +EXPORT_CPR enum class HttpVersionCode : uint8_t { /** * Let libcurl decide which version is the best. **/ @@ -58,7 +60,7 @@ enum class HttpVersionCode : uint8_t { #endif }; -class HttpVersion { +EXPORT_CPR class HttpVersion { public: /** * The HTTP version that should be used by libcurl when initiating a HTTP(S) connection. diff --git a/include/cpr/interceptor.h b/include/cpr/interceptor.h index 4bef2f31f..55db50859 100644 --- a/include/cpr/interceptor.h +++ b/include/cpr/interceptor.h @@ -1,13 +1,22 @@ #ifndef CPR_INTERCEPTOR_H #define CPR_INTERCEPTOR_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD +#include +#endif + #include "cpr/multiperform.h" #include "cpr/response.h" #include "cpr/session.h" -#include namespace cpr { -class Interceptor { +EXPORT_CPR class Interceptor { public: enum class ProceedHttpMethod : uint8_t { GET_REQUEST = 0, @@ -38,7 +47,7 @@ class Interceptor { static Response proceed(Session& session, ProceedHttpMethod httpMethod, const WriteCallback& write); }; -class InterceptorMulti { +EXPORT_CPR class InterceptorMulti { public: enum class ProceedHttpMethod : uint8_t { GET_REQUEST = 0, diff --git a/include/cpr/interface.h b/include/cpr/interface.h index 115dc912b..cbf3b5750 100644 --- a/include/cpr/interface.h +++ b/include/cpr/interface.h @@ -1,14 +1,22 @@ #ifndef CPR_INTERFACE_H #define CPR_INTERFACE_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include +#endif #include "cpr/cprtypes.h" namespace cpr { -class Interface : public StringHolder { +EXPORT_CPR class Interface : public StringHolder { public: Interface() = default; Interface(std::string iface) : StringHolder(std::move(iface)) {} diff --git a/include/cpr/limit_rate.h b/include/cpr/limit_rate.h index ac3c049e1..3da868ab6 100644 --- a/include/cpr/limit_rate.h +++ b/include/cpr/limit_rate.h @@ -1,11 +1,13 @@ #ifndef CPR_LIMIT_RATE_H #define CPR_LIMIT_RATE_H +#include "cpr/export.h" + #include namespace cpr { -class LimitRate { +EXPORT_CPR class LimitRate { public: LimitRate(const std::int64_t p_downrate, const std::int64_t p_uprate) : downrate(p_downrate), uprate(p_uprate) {} diff --git a/include/cpr/local_port.h b/include/cpr/local_port.h index 12cee9ec5..e8a6492bb 100644 --- a/include/cpr/local_port.h +++ b/include/cpr/local_port.h @@ -1,11 +1,13 @@ #ifndef CPR_LOCAL_PORT_H #define CPR_LOCAL_PORT_H +#include "cpr/export.h" + #include namespace cpr { -class LocalPort { +EXPORT_CPR class LocalPort { public: LocalPort(const std::uint16_t p_localport) : localport_(p_localport) {} diff --git a/include/cpr/local_port_range.h b/include/cpr/local_port_range.h index 6a7fa964d..372336787 100644 --- a/include/cpr/local_port_range.h +++ b/include/cpr/local_port_range.h @@ -1,11 +1,13 @@ #ifndef CPR_LOCAL_PORT_RANGE_H #define CPR_LOCAL_PORT_RANGE_H +#include "cpr/export.h" + #include namespace cpr { -class LocalPortRange { +EXPORT_CPR class LocalPortRange { public: LocalPortRange(const std::uint16_t p_localportrange) : localportrange_(p_localportrange) {} diff --git a/include/cpr/low_speed.h b/include/cpr/low_speed.h index c74ceceb5..643f5eea3 100644 --- a/include/cpr/low_speed.h +++ b/include/cpr/low_speed.h @@ -1,12 +1,21 @@ #ifndef CPR_LOW_SPEED_H #define CPR_LOW_SPEED_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include +#endif + #include namespace cpr { -class LowSpeed { +EXPORT_CPR class LowSpeed { public: [[deprecated("Will be removed in CPR 2.x - Use the constructor with std::chrono::seconds instead of std::int32_t")]] LowSpeed(const std::int32_t p_limit, const std::int32_t p_time) diff --git a/include/cpr/multipart.h b/include/cpr/multipart.h index 991764243..4c72a1b37 100644 --- a/include/cpr/multipart.h +++ b/include/cpr/multipart.h @@ -1,18 +1,25 @@ #ifndef CPR_MULTIPART_H #define CPR_MULTIPART_H -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include -#include #include +#endif #include "buffer.h" #include "file.h" +#include namespace cpr { -struct Part { +EXPORT_CPR struct Part { Part(const std::string& p_name, const std::string& p_value, const std::string& p_content_type = {}) : name{p_name}, value{p_value}, content_type{p_content_type}, is_file{false}, is_buffer{false} {} Part(const std::string& p_name, const std::int32_t& p_value, const std::string& p_content_type = {}) : name{p_name}, value{std::to_string(p_value)}, content_type{p_content_type}, is_file{false}, is_buffer{false} {} Part(const std::string& p_name, const Files& p_files, const std::string& p_content_type = {}) : name{p_name}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{p_files} {} @@ -31,7 +38,7 @@ struct Part { Files files; }; -class Multipart { +EXPORT_CPR class Multipart { public: Multipart(const std::initializer_list& parts); explicit Multipart(const std::vector& parts); diff --git a/include/cpr/multiperform.h b/include/cpr/multiperform.h index e4ca75f92..e989503bf 100644 --- a/include/cpr/multiperform.h +++ b/include/cpr/multiperform.h @@ -1,20 +1,28 @@ #ifndef CPR_MULTIPERFORM_H #define CPR_MULTIPERFORM_H -#include "cpr/curlmultiholder.h" -#include "cpr/response.h" -#include "cpr/session.h" +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include -#include #include #include +#endif + +#include "cpr/curlmultiholder.h" +#include "cpr/response.h" +#include "cpr/session.h" namespace cpr { -class InterceptorMulti; +EXPORT_CPR class InterceptorMulti; -class MultiPerform { +EXPORT_CPR class MultiPerform { public: enum class HttpMethod : uint8_t { UNDEFINED = 0, diff --git a/include/cpr/parameters.h b/include/cpr/parameters.h index 620962778..624dae22c 100644 --- a/include/cpr/parameters.h +++ b/include/cpr/parameters.h @@ -1,13 +1,21 @@ #ifndef CPR_PARAMETERS_H #define CPR_PARAMETERS_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include +#endif #include "cpr/curl_container.h" namespace cpr { -class Parameters : public CurlContainer { +EXPORT_CPR class Parameters : public CurlContainer { public: Parameters() = default; Parameters(const std::initializer_list& parameters) : CurlContainer(parameters) {} diff --git a/include/cpr/payload.h b/include/cpr/payload.h index 0741a88fa..023048f5a 100644 --- a/include/cpr/payload.h +++ b/include/cpr/payload.h @@ -1,13 +1,21 @@ #ifndef CPR_PAYLOAD_H #define CPR_PAYLOAD_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include +#endif #include "cpr/curl_container.h" namespace cpr { -class Payload : public CurlContainer { +EXPORT_CPR class Payload : public CurlContainer { public: template Payload(const It begin, const It end) { diff --git a/include/cpr/proxies.h b/include/cpr/proxies.h index 60108af7f..9d83d1db2 100644 --- a/include/cpr/proxies.h +++ b/include/cpr/proxies.h @@ -1,12 +1,20 @@ #ifndef CPR_PROXIES_H #define CPR_PROXIES_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include +#endif namespace cpr { -class Proxies { +EXPORT_CPR class Proxies { public: Proxies() = default; Proxies(const std::initializer_list>& hosts); diff --git a/include/cpr/proxyauth.h b/include/cpr/proxyauth.h index 6a00c920e..92d8d0e6e 100644 --- a/include/cpr/proxyauth.h +++ b/include/cpr/proxyauth.h @@ -1,18 +1,26 @@ #ifndef CPR_PROXYAUTH_H #define CPR_PROXYAUTH_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include #include +#endif #include "cpr/auth.h" #include "cpr/util.h" namespace cpr { -class ProxyAuthentication; +EXPORT_CPR class ProxyAuthentication; -class EncodedAuthentication { +EXPORT_CPR class EncodedAuthentication { friend ProxyAuthentication; public: @@ -35,7 +43,7 @@ class EncodedAuthentication { util::SecureString password; }; -class ProxyAuthentication { +EXPORT_CPR class ProxyAuthentication { public: ProxyAuthentication() = default; ProxyAuthentication(const std::initializer_list>& auths) : proxyAuth_{auths} {} diff --git a/include/cpr/range.h b/include/cpr/range.h index c6ab6901a..8bea2e931 100644 --- a/include/cpr/range.h +++ b/include/cpr/range.h @@ -1,12 +1,23 @@ #ifndef CPR_RANGE_H #define CPR_RANGE_H -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include +#include +#include +#endif + +#include namespace cpr { -class Range { +EXPORT_CPR class Range { public: explicit Range(const std::optional p_resume_from = std::nullopt, const std::optional p_finish_at = std::nullopt) { resume_from = p_resume_from.value_or(0); @@ -23,7 +34,7 @@ class Range { } }; -class MultiRange { +EXPORT_CPR class MultiRange { public: MultiRange(std::initializer_list rs) : ranges{rs} {} diff --git a/include/cpr/redirect.h b/include/cpr/redirect.h index c3f24d3ad..16cdc30ff 100644 --- a/include/cpr/redirect.h +++ b/include/cpr/redirect.h @@ -1,10 +1,12 @@ #ifndef CPR_REDIRECT_H #define CPR_REDIRECT_H +#include "cpr/export.h" + #include namespace cpr { -enum class PostRedirectFlags : uint8_t { +EXPORT_CPR enum class PostRedirectFlags : uint8_t { /** * Respect RFC 7231 (section 6.4.2 to 6.4.4). * Same as CURL_REDIR_POST_301 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html). @@ -32,16 +34,16 @@ enum class PostRedirectFlags : uint8_t { NONE = 0x0 }; -PostRedirectFlags operator|(PostRedirectFlags lhs, PostRedirectFlags rhs); -PostRedirectFlags operator&(PostRedirectFlags lhs, PostRedirectFlags rhs); -PostRedirectFlags operator^(PostRedirectFlags lhs, PostRedirectFlags rhs); -PostRedirectFlags operator~(PostRedirectFlags flag); -PostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs); -PostRedirectFlags& operator&=(PostRedirectFlags& lhs, PostRedirectFlags rhs); -PostRedirectFlags& operator^=(PostRedirectFlags& lhs, PostRedirectFlags rhs); -bool any(PostRedirectFlags flag); +EXPORT_CPR PostRedirectFlags operator|(PostRedirectFlags lhs, PostRedirectFlags rhs); +EXPORT_CPR PostRedirectFlags operator&(PostRedirectFlags lhs, PostRedirectFlags rhs); +EXPORT_CPR PostRedirectFlags operator^(PostRedirectFlags lhs, PostRedirectFlags rhs); +EXPORT_CPR PostRedirectFlags operator~(PostRedirectFlags flag); +EXPORT_CPR PostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs); +EXPORT_CPR PostRedirectFlags& operator&=(PostRedirectFlags& lhs, PostRedirectFlags rhs); +EXPORT_CPR PostRedirectFlags& operator^=(PostRedirectFlags& lhs, PostRedirectFlags rhs); +EXPORT_CPR bool any(PostRedirectFlags flag); -class Redirect { +EXPORT_CPR class Redirect { public: /** * The maximum number of redirects to follow. diff --git a/include/cpr/reserve_size.h b/include/cpr/reserve_size.h index 93bb31e86..1706d542f 100644 --- a/include/cpr/reserve_size.h +++ b/include/cpr/reserve_size.h @@ -1,11 +1,13 @@ #ifndef CPR_RESERVE_SIZE_H #define CPR_RESERVE_SIZE_H +#include "cpr/export.h" + #include namespace cpr { -class ReserveSize { +EXPORT_CPR class ReserveSize { public: ReserveSize(const std::size_t _size) : size(_size) {} diff --git a/include/cpr/resolve.h b/include/cpr/resolve.h index 6f0e52c16..a30630a87 100644 --- a/include/cpr/resolve.h +++ b/include/cpr/resolve.h @@ -1,11 +1,21 @@ #ifndef CPR_RESOLVE_H #define CPR_RESOLVE_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include +#endif + +#include namespace cpr { -class Resolve { +EXPORT_CPR class Resolve { public: std::string host; std::string addr; diff --git a/include/cpr/response.h b/include/cpr/response.h index 1f85b3576..0ec1ecdda 100644 --- a/include/cpr/response.h +++ b/include/cpr/response.h @@ -1,12 +1,18 @@ #ifndef CPR_RESPONSE_H #define CPR_RESPONSE_H -#include -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include #include +#endif #include "cpr/cert_info.h" #include "cpr/cookies.h" @@ -14,12 +20,14 @@ #include "cpr/error.h" #include "cpr/ssl_options.h" #include "cpr/util.h" +#include +#include namespace cpr { -class MultiPerform; +EXPORT_CPR class MultiPerform; -class Response { +EXPORT_CPR class Response { private: friend MultiPerform; std::shared_ptr curl_{nullptr}; diff --git a/include/cpr/secure_string.h b/include/cpr/secure_string.h index 35a6cbb4b..681219c08 100644 --- a/include/cpr/secure_string.h +++ b/include/cpr/secure_string.h @@ -1,16 +1,23 @@ #ifndef CPR_SECURE_STRING_H #define CPR_SECURE_STRING_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include -#include +#endif namespace cpr::util { // This is an allocator that overwrites memory with zero values before // deallocating the memory, so as to not leave secrets in unallocated memory // sections. -template +EXPORT_CPR template struct SecureAllocator : private std::allocator { template friend struct SecureAllocator; @@ -48,16 +55,16 @@ struct SecureAllocator : private std::allocator { return static_cast&>(*this) == static_cast&>(rhs); } }; -template +EXPORT_CPR template bool operator==(const SecureAllocator& lhs, const SecureAllocator& rhs) noexcept { return lhs.IsEqual(rhs); } -template +EXPORT_CPR template bool operator!=(const SecureAllocator& lhs, const SecureAllocator& rhs) noexcept { return !lhs.IsEqual(rhs); } -using SecureString = std::basic_string, SecureAllocator>; +EXPORT_CPR using SecureString = std::basic_string, SecureAllocator>; } // namespace cpr::util diff --git a/include/cpr/session.h b/include/cpr/session.h index 0781559f9..e099c7f41 100644 --- a/include/cpr/session.h +++ b/include/cpr/session.h @@ -1,14 +1,19 @@ #ifndef CPR_SESSION_H #define CPR_SESSION_H -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include -#include -#include #include #include #include #include +#endif #include "cpr/accept_encoding.h" #include "cpr/async_wrapper.h" @@ -43,18 +48,17 @@ #include "cpr/timeout.h" #include "cpr/unix_socket.h" #include "cpr/user_agent.h" -#include "cpr/util.h" #include "cpr/verbose.h" namespace cpr { -using AsyncResponse = AsyncWrapper; -using Content = std::variant; +EXPORT_CPR using AsyncResponse = AsyncWrapper; +EXPORT_CPR using Content = std::variant; -class Interceptor; -class MultiPerform; +EXPORT_CPR class Interceptor; +EXPORT_CPR class MultiPerform; -class Session : public std::enable_shared_from_this { +EXPORT_CPR class Session : public std::enable_shared_from_this { public: Session(); Session(const Session& other) = delete; diff --git a/include/cpr/singleton.h b/include/cpr/singleton.h index ccff28483..17f39af63 100644 --- a/include/cpr/singleton.h +++ b/include/cpr/singleton.h @@ -2,7 +2,6 @@ #define CPR_SINGLETON_H #include -#include // NOLINTBEGIN(cppcoreguidelines-macro-usage, bugprone-macro-parentheses) diff --git a/include/cpr/sse.h b/include/cpr/sse.h index 54746a341..73ac32129 100644 --- a/include/cpr/sse.h +++ b/include/cpr/sse.h @@ -1,12 +1,21 @@ #ifndef CPR_SSE_H #define CPR_SSE_H -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include #include #include +#endif + +#include namespace cpr { @@ -14,7 +23,7 @@ namespace cpr { * Represents a Server-Sent Event (SSE) as defined in the HTML5 specification. * https://html.spec.whatwg.org/multipage/server-sent-events.html */ -struct ServerSentEvent { +EXPORT_CPR struct ServerSentEvent { /** * The event ID. Can be used to track the last received event and resume from there. */ @@ -42,7 +51,7 @@ struct ServerSentEvent { * Parser for Server-Sent Events (SSE) streams. * This parser handles incoming SSE data according to the HTML5 specification. */ -class ServerSentEventParser { +EXPORT_CPR class ServerSentEventParser { public: ServerSentEventParser() = default; @@ -71,7 +80,7 @@ class ServerSentEventParser { * Callback for handling Server-Sent Events. * The callback receives each parsed SSE event and can return false to abort the connection. */ -class ServerSentEventCallback { +EXPORT_CPR class ServerSentEventCallback { public: ServerSentEventCallback() = default; ServerSentEventCallback(std::function p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {} diff --git a/include/cpr/ssl_ctx.h b/include/cpr/ssl_ctx.h index b6bc81190..4dfc7fb6d 100644 --- a/include/cpr/ssl_ctx.h +++ b/include/cpr/ssl_ctx.h @@ -1,6 +1,8 @@ #ifndef CPR_SSL_CTX_H #define CPR_SSL_CTX_H +#include "cpr/export.h" + #include "cpr/ssl_options.h" #include @@ -17,7 +19,7 @@ namespace cpr { * Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html * https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html */ -CURLcode sslctx_function_load_ca_cert_from_buffer(CURL* curl, void* sslctx, void* raw_cert_buf); +EXPORT_CPR CURLcode sslctx_function_load_ca_cert_from_buffer(CURL* curl, void* sslctx, void* raw_cert_buf); } // Namespace cpr diff --git a/include/cpr/ssl_options.h b/include/cpr/ssl_options.h index 338744ea9..085f68d7c 100644 --- a/include/cpr/ssl_options.h +++ b/include/cpr/ssl_options.h @@ -1,16 +1,20 @@ #ifndef CPR_SSL_OPTIONS_H #define CPR_SSL_OPTIONS_H -#include +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include -#include +#include +#endif #include "cpr/filesystem.h" -#include - #include "cpr/util.h" -#include "util.h" -#include +#include #ifndef SUPPORT_ALPN #define SUPPORT_ALPN LIBCURL_VERSION_NUM >= 0x072400 // 7.36.0 @@ -76,7 +80,7 @@ namespace cpr { -class VerifySsl { +EXPORT_CPR class VerifySsl { public: VerifySsl() = default; VerifySsl(bool p_verify) : verify(p_verify) {} @@ -91,7 +95,7 @@ class VerifySsl { namespace ssl { // set SSL client certificate -class CertFile { +EXPORT_CPR class CertFile { public: CertFile(fs::path&& p_filename) : filename(std::move(p_filename)) {} @@ -104,9 +108,9 @@ class CertFile { } }; -using PemCert = CertFile; +EXPORT_CPR using PemCert = CertFile; -class DerCert : public CertFile { +EXPORT_CPR class DerCert : public CertFile { public: DerCert(fs::path&& p_filename) : CertFile(std::move(p_filename)) {} @@ -119,7 +123,7 @@ class DerCert : public CertFile { #if SUPPORT_CURLOPT_SSLCERT_BLOB -class CertBlob { +EXPORT_CPR class CertBlob { public: CertBlob(std::string&& p_blob) : blob(std::move(p_blob)) {} @@ -132,9 +136,9 @@ class CertBlob { } }; -using PemBlob = CertBlob; +EXPORT_CPR using PemBlob = CertBlob; -class DerBlob : public CertBlob { +EXPORT_CPR class DerBlob : public CertBlob { public: template // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) @@ -149,7 +153,7 @@ class DerBlob : public CertBlob { #endif // specify private keyfile for TLS and SSL client cert -class KeyFile { +EXPORT_CPR class KeyFile { public: KeyFile(fs::path&& p_filename) : filename(std::move(p_filename)) {} @@ -167,7 +171,7 @@ class KeyFile { }; #if SUPPORT_CURLOPT_SSLKEY_BLOB -class KeyBlob { +EXPORT_CPR class KeyBlob { public: KeyBlob(std::string&& p_blob) : blob(std::move(p_blob)) {} @@ -185,9 +189,9 @@ class KeyBlob { }; #endif -using PemKey = KeyFile; +EXPORT_CPR using PemKey = KeyFile; -class DerKey : public KeyFile { +EXPORT_CPR class DerKey : public KeyFile { public: DerKey(fs::path&& p_filename) : KeyFile(std::move(p_filename)) {} @@ -201,7 +205,7 @@ class DerKey : public KeyFile { } }; -class PinnedPublicKey { +EXPORT_CPR class PinnedPublicKey { public: PinnedPublicKey(std::string&& p_pinned_public_key) : pinned_public_key(std::move(p_pinned_public_key)) {} @@ -211,7 +215,7 @@ class PinnedPublicKey { #if SUPPORT_ALPN // This option enables/disables ALPN in the SSL handshake (if the SSL backend libcurl is built to // use supports it), which can be used to negotiate http2. -class ALPN { +EXPORT_CPR class ALPN { public: ALPN() = default; ALPN(bool p_enabled) : enabled(p_enabled) {} @@ -227,7 +231,7 @@ class ALPN { #if SUPPORT_NPN // This option enables/disables NPN in the SSL handshake (if the SSL backend libcurl is built to // use supports it), which can be used to negotiate http2. -class NPN { +EXPORT_CPR class NPN { public: NPN() = default; NPN(bool p_enabled) : enabled(p_enabled) {} @@ -242,7 +246,7 @@ class NPN { // This option determines whether libcurl verifies that the server cert is for the server it is // known as. -class VerifyHost { +EXPORT_CPR class VerifyHost { public: VerifyHost() = default; VerifyHost(bool p_enabled) : enabled(p_enabled) {} @@ -255,7 +259,7 @@ class VerifyHost { }; // This option determines whether libcurl verifies the authenticity of the peer's certificate. -class VerifyPeer { +EXPORT_CPR class VerifyPeer { public: VerifyPeer() = default; VerifyPeer(bool p_enabled) : enabled(p_enabled) {} @@ -269,7 +273,7 @@ class VerifyPeer { // This option determines whether libcurl verifies the status of the server cert using the // "Certificate Status Request" TLS extension (aka. OCSP stapling). -class VerifyStatus { +EXPORT_CPR class VerifyStatus { public: VerifyStatus(bool p_enabled) : enabled(p_enabled) {} @@ -281,55 +285,55 @@ class VerifyStatus { }; // TLS v1.0 or later -struct TLSv1 {}; +EXPORT_CPR struct TLSv1 {}; #if SUPPORT_SSLv2 // SSL v2 (but not SSLv3) -struct SSLv2 {}; +EXPORT_CPR struct SSLv2 {}; #endif #if SUPPORT_SSLv3 // SSL v3 (but not SSLv2) -struct SSLv3 {}; +EXPORT_CPR struct SSLv3 {}; #endif #if SUPPORT_TLSv1_0 // TLS v1.0 or later (Added in 7.34.0) -struct TLSv1_0 {}; +EXPORT_CPR struct TLSv1_0 {}; #endif #if SUPPORT_TLSv1_1 // TLS v1.1 or later (Added in 7.34.0) -struct TLSv1_1 {}; +EXPORT_CPR struct TLSv1_1 {}; #endif #if SUPPORT_TLSv1_2 // TLS v1.2 or later (Added in 7.34.0) -struct TLSv1_2 {}; +EXPORT_CPR struct TLSv1_2 {}; #endif #if SUPPORT_TLSv1_3 // TLS v1.3 or later (Added in 7.52.0) -struct TLSv1_3 {}; +EXPORT_CPR struct TLSv1_3 {}; #endif #if SUPPORT_MAX_TLS_VERSION // The flag defines the maximum supported TLS version by libcurl, or the default value from the SSL // library is used. -struct MaxTLSVersion {}; +EXPORT_CPR struct MaxTLSVersion {}; #endif #if SUPPORT_MAX_TLSv1_0 // The flag defines maximum supported TLS version as TLSv1.0. (Added in 7.54.0) -struct MaxTLSv1_0 {}; +EXPORT_CPR struct MaxTLSv1_0 {}; #endif #if SUPPORT_MAX_TLSv1_1 // The flag defines maximum supported TLS version as TLSv1.1. (Added in 7.54.0) -struct MaxTLSv1_1 {}; +EXPORT_CPR struct MaxTLSv1_1 {}; #endif #if SUPPORT_MAX_TLSv1_2 // The flag defines maximum supported TLS version as TLSv1.2. (Added in 7.54.0) -struct MaxTLSv1_2 {}; +EXPORT_CPR struct MaxTLSv1_2 {}; #endif #if SUPPORT_MAX_TLSv1_3 // The flag defines maximum supported TLS version as TLSv1.3. (Added in 7.54.0) -struct MaxTLSv1_3 {}; +EXPORT_CPR struct MaxTLSv1_3 {}; #endif // path to Certificate Authority (CA) bundle -class CaInfo { +EXPORT_CPR class CaInfo { public: CaInfo(fs::path&& p_filename) : filename(std::move(p_filename)) {} @@ -338,7 +342,7 @@ class CaInfo { #if SUPPORT_CURLOPT_CAINFO_BLOB // Certificate Authority (CA) bundle as blob -class CaInfoBlob { +EXPORT_CPR class CaInfoBlob { public: CaInfoBlob(std::string&& p_blob) : blob(std::move(p_blob)) {} @@ -347,7 +351,7 @@ class CaInfoBlob { #endif // specify directory holding CA certificates -class CaPath { +EXPORT_CPR class CaPath { public: CaPath(fs::path&& p_filename) : filename(std::move(p_filename)) {} @@ -355,7 +359,7 @@ class CaPath { }; #if SUPPORT_CURLOPT_SSL_CTX_FUNCTION -class CaBuffer { +EXPORT_CPR class CaBuffer { public: CaBuffer(std::string&& p_buffer) : buffer(std::move(p_buffer)) {} @@ -364,7 +368,7 @@ class CaBuffer { #endif // specify a Certificate Revocation List file -class Crl { +EXPORT_CPR class Crl { public: Crl(fs::path&& p_filename) : filename(std::move(p_filename)) {} @@ -372,7 +376,7 @@ class Crl { }; // specify ciphers to use for TLS -class Ciphers { +EXPORT_CPR class Ciphers { public: Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {} @@ -381,7 +385,7 @@ class Ciphers { #if SUPPORT_TLSv13_CIPHERS // specify ciphers suites to use for TLS 1.3 -class TLS13_Ciphers { +EXPORT_CPR class TLS13_Ciphers { public: TLS13_Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {} @@ -391,7 +395,7 @@ class TLS13_Ciphers { #if SUPPORT_SESSIONID_CACHE // enable/disable use of the SSL session-ID cache -class SessionIdCache { +EXPORT_CPR class SessionIdCache { public: SessionIdCache() = default; SessionIdCache(bool p_enabled) : enabled(p_enabled) {} @@ -405,7 +409,7 @@ class SessionIdCache { #endif #if SUPPORT_SSL_FALSESTART -class SslFastStart { +EXPORT_CPR class SslFastStart { public: SslFastStart() = default; SslFastStart(bool p_enabled) : enabled(p_enabled) {} @@ -418,7 +422,7 @@ class SslFastStart { }; #endif -class NoRevoke { +EXPORT_CPR class NoRevoke { public: NoRevoke() = default; NoRevoke(bool p_enabled) : enabled(p_enabled) {} @@ -432,7 +436,7 @@ class NoRevoke { } // namespace ssl -struct SslOptions { +EXPORT_CPR struct SslOptions { // We don't use fs::path here, as this leads to problems using windows std::string cert_file; #if SUPPORT_CURLOPT_SSLCERT_BLOB @@ -640,7 +644,7 @@ void set_ssl_option(SslOptions& opts, T&& t, Ts&&... ts) { } // namespace priv -template +EXPORT_CPR template SslOptions Ssl(Ts&&... ts) { SslOptions opts; priv::set_ssl_option(opts, std::forward(ts)...); diff --git a/include/cpr/status_codes.h b/include/cpr/status_codes.h index 53e0d251d..bd3d46d1d 100644 --- a/include/cpr/status_codes.h +++ b/include/cpr/status_codes.h @@ -1,99 +1,100 @@ #ifndef CPR_STATUS_CODES #define CPR_STATUS_CODES -namespace cpr { -namespace status { + +#include "cpr/export.h" + + +namespace cpr::status { // Information responses -constexpr long HTTP_CONTINUE = 100; -constexpr long HTTP_SWITCHING_PROTOCOL = 101; -constexpr long HTTP_PROCESSING = 102; -constexpr long HTTP_EARLY_HINTS = 103; +EXPORT_CPR inline constexpr long HTTP_CONTINUE = 100; +EXPORT_CPR inline constexpr long HTTP_SWITCHING_PROTOCOL = 101; +EXPORT_CPR inline constexpr long HTTP_PROCESSING = 102; +EXPORT_CPR inline constexpr long HTTP_EARLY_HINTS = 103; // Successful responses -constexpr long HTTP_OK = 200; -constexpr long HTTP_CREATED = 201; -constexpr long HTTP_ACCEPTED = 202; -constexpr long HTTP_NON_AUTHORITATIVE_INFORMATION = 203; -constexpr long HTTP_NO_CONTENT = 204; -constexpr long HTTP_RESET_CONTENT = 205; -constexpr long HTTP_PARTIAL_CONTENT = 206; -constexpr long HTTP_MULTI_STATUS = 207; -constexpr long HTTP_ALREADY_REPORTED = 208; -constexpr long HTTP_IM_USED = 226; +EXPORT_CPR inline constexpr long HTTP_OK = 200; +EXPORT_CPR inline constexpr long HTTP_CREATED = 201; +EXPORT_CPR inline constexpr long HTTP_ACCEPTED = 202; +EXPORT_CPR inline constexpr long HTTP_NON_AUTHORITATIVE_INFORMATION = 203; +EXPORT_CPR inline constexpr long HTTP_NO_CONTENT = 204; +EXPORT_CPR inline constexpr long HTTP_RESET_CONTENT = 205; +EXPORT_CPR inline constexpr long HTTP_PARTIAL_CONTENT = 206; +EXPORT_CPR inline constexpr long HTTP_MULTI_STATUS = 207; +EXPORT_CPR inline constexpr long HTTP_ALREADY_REPORTED = 208; +EXPORT_CPR inline constexpr long HTTP_IM_USED = 226; // Redirection messages -constexpr long HTTP_MULTIPLE_CHOICE = 300; -constexpr long HTTP_MOVED_PERMANENTLY = 301; -constexpr long HTTP_FOUND = 302; -constexpr long HTTP_SEE_OTHER = 303; -constexpr long HTTP_NOT_MODIFIED = 304; -constexpr long HTTP_USE_PROXY = 305; -constexpr long HTTP_UNUSED = 306; -constexpr long HTTP_TEMPORARY_REDIRECT = 307; -constexpr long HTTP_PERMANENT_REDIRECT = 308; +EXPORT_CPR inline constexpr long HTTP_MULTIPLE_CHOICE = 300; +EXPORT_CPR inline constexpr long HTTP_MOVED_PERMANENTLY = 301; +EXPORT_CPR inline constexpr long HTTP_FOUND = 302; +EXPORT_CPR inline constexpr long HTTP_SEE_OTHER = 303; +EXPORT_CPR inline constexpr long HTTP_NOT_MODIFIED = 304; +EXPORT_CPR inline constexpr long HTTP_USE_PROXY = 305; +EXPORT_CPR inline constexpr long HTTP_UNUSED = 306; +EXPORT_CPR inline constexpr long HTTP_TEMPORARY_REDIRECT = 307; +EXPORT_CPR inline constexpr long HTTP_PERMANENT_REDIRECT = 308; // Client error responses -constexpr long HTTP_BAD_REQUEST = 400; -constexpr long HTTP_UNAUTHORIZED = 401; -constexpr long HTTP_PAYMENT_REQUIRED = 402; -constexpr long HTTP_FORBIDDEN = 403; -constexpr long HTTP_NOT_FOUND = 404; -constexpr long HTTP_METHOD_NOT_ALLOWED = 405; -constexpr long HTTP_NOT_ACCEPTABLE = 406; -constexpr long HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; -constexpr long HTTP_REQUEST_TIMEOUT = 408; -constexpr long HTTP_CONFLICT = 409; -constexpr long HTTP_GONE = 410; -constexpr long HTTP_LENGTH_REQUIRED = 411; -constexpr long HTTP_PRECONDITION_FAILED = 412; -constexpr long HTTP_PAYLOAD_TOO_LARGE = 413; -constexpr long HTTP_URI_TOO_LONG = 414; -constexpr long HTTP_UNSUPPORTED_MEDIA_TYPE = 415; -constexpr long HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; -constexpr long HTTP_EXPECTATION_FAILED = 417; -constexpr long HTTP_IM_A_TEAPOT = 418; -constexpr long HTTP_MISDIRECTED_REQUEST = 421; -constexpr long HTTP_UNPROCESSABLE_ENTITY = 422; -constexpr long HTTP_LOCKED = 423; -constexpr long HTTP_FAILED_DEPENDENCY = 424; -constexpr long HTTP_TOO_EARLY = 425; -constexpr long HTTP_UPGRADE_REQUIRED = 426; -constexpr long HTTP_PRECONDITION_REQUIRED = 428; -constexpr long HTTP_TOO_MANY_REQUESTS = 429; -constexpr long HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; -constexpr long HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; +EXPORT_CPR inline constexpr long HTTP_BAD_REQUEST = 400; +EXPORT_CPR inline constexpr long HTTP_UNAUTHORIZED = 401; +EXPORT_CPR inline constexpr long HTTP_PAYMENT_REQUIRED = 402; +EXPORT_CPR inline constexpr long HTTP_FORBIDDEN = 403; +EXPORT_CPR inline constexpr long HTTP_NOT_FOUND = 404; +EXPORT_CPR inline constexpr long HTTP_METHOD_NOT_ALLOWED = 405; +EXPORT_CPR inline constexpr long HTTP_NOT_ACCEPTABLE = 406; +EXPORT_CPR inline constexpr long HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; +EXPORT_CPR inline constexpr long HTTP_REQUEST_TIMEOUT = 408; +EXPORT_CPR inline constexpr long HTTP_CONFLICT = 409; +EXPORT_CPR inline constexpr long HTTP_GONE = 410; +EXPORT_CPR inline constexpr long HTTP_LENGTH_REQUIRED = 411; +EXPORT_CPR inline constexpr long HTTP_PRECONDITION_FAILED = 412; +EXPORT_CPR inline constexpr long HTTP_PAYLOAD_TOO_LARGE = 413; +EXPORT_CPR inline constexpr long HTTP_URI_TOO_LONG = 414; +EXPORT_CPR inline constexpr long HTTP_UNSUPPORTED_MEDIA_TYPE = 415; +EXPORT_CPR inline constexpr long HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; +EXPORT_CPR inline constexpr long HTTP_EXPECTATION_FAILED = 417; +EXPORT_CPR inline constexpr long HTTP_IM_A_TEAPOT = 418; +EXPORT_CPR inline constexpr long HTTP_MISDIRECTED_REQUEST = 421; +EXPORT_CPR inline constexpr long HTTP_UNPROCESSABLE_ENTITY = 422; +EXPORT_CPR inline constexpr long HTTP_LOCKED = 423; +EXPORT_CPR inline constexpr long HTTP_FAILED_DEPENDENCY = 424; +EXPORT_CPR inline constexpr long HTTP_TOO_EARLY = 425; +EXPORT_CPR inline constexpr long HTTP_UPGRADE_REQUIRED = 426; +EXPORT_CPR inline constexpr long HTTP_PRECONDITION_REQUIRED = 428; +EXPORT_CPR inline constexpr long HTTP_TOO_MANY_REQUESTS = 429; +EXPORT_CPR inline constexpr long HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; +EXPORT_CPR inline constexpr long HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; // Server response errors -constexpr long HTTP_INTERNAL_SERVER_ERROR = 500; -constexpr long HTTP_NOT_IMPLEMENTED = 501; -constexpr long HTTP_BAD_GATEWAY = 502; -constexpr long HTTP_SERVICE_UNAVAILABLE = 503; -constexpr long HTTP_GATEWAY_TIMEOUT = 504; -constexpr long HTTP_HTTP_VERSION_NOT_SUPPORTED = 505; -constexpr long HTTP_VARIANT_ALSO_NEGOTIATES = 506; -constexpr long HTTP_INSUFFICIENT_STORAGE = 507; -constexpr long HTTP_LOOP_DETECTED = 508; -constexpr long HTTP_NOT_EXTENDED = 510; -constexpr long HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; +EXPORT_CPR inline constexpr long HTTP_INTERNAL_SERVER_ERROR = 500; +EXPORT_CPR inline constexpr long HTTP_NOT_IMPLEMENTED = 501; +EXPORT_CPR inline constexpr long HTTP_BAD_GATEWAY = 502; +EXPORT_CPR inline constexpr long HTTP_SERVICE_UNAVAILABLE = 503; +EXPORT_CPR inline constexpr long HTTP_GATEWAY_TIMEOUT = 504; +EXPORT_CPR inline constexpr long HTTP_HTTP_VERSION_NOT_SUPPORTED = 505; +EXPORT_CPR inline constexpr long HTTP_VARIANT_ALSO_NEGOTIATES = 506; +EXPORT_CPR inline constexpr long HTTP_INSUFFICIENT_STORAGE = 507; +EXPORT_CPR inline constexpr long HTTP_LOOP_DETECTED = 508; +EXPORT_CPR inline constexpr long HTTP_NOT_EXTENDED = 510; +EXPORT_CPR inline constexpr long HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; -constexpr long INFO_CODE_OFFSET = 100; -constexpr long SUCCESS_CODE_OFFSET = 200; -constexpr long REDIRECT_CODE_OFFSET = 300; -constexpr long CLIENT_ERROR_CODE_OFFSET = 400; -constexpr long SERVER_ERROR_CODE_OFFSET = 500; -constexpr long MISC_CODE_OFFSET = 600; +EXPORT_CPR inline constexpr long INFO_CODE_OFFSET = 100; +EXPORT_CPR inline constexpr long SUCCESS_CODE_OFFSET = 200; +EXPORT_CPR inline constexpr long REDIRECT_CODE_OFFSET = 300; +EXPORT_CPR inline constexpr long CLIENT_ERROR_CODE_OFFSET = 400; +EXPORT_CPR inline constexpr long SERVER_ERROR_CODE_OFFSET = 500; +EXPORT_CPR inline constexpr long MISC_CODE_OFFSET = 600; -constexpr bool is_informational(const long code) { +EXPORT_CPR constexpr bool is_informational(const long code) { return (code >= INFO_CODE_OFFSET && code < SUCCESS_CODE_OFFSET); } -constexpr bool is_success(const long code) { +EXPORT_CPR constexpr bool is_success(const long code) { return (code >= SUCCESS_CODE_OFFSET && code < REDIRECT_CODE_OFFSET); } -constexpr bool is_redirect(const long code) { +EXPORT_CPR constexpr bool is_redirect(const long code) { return (code >= REDIRECT_CODE_OFFSET && code < CLIENT_ERROR_CODE_OFFSET); } -constexpr bool is_client_error(const long code) { +EXPORT_CPR constexpr bool is_client_error(const long code) { return (code >= CLIENT_ERROR_CODE_OFFSET && code < SERVER_ERROR_CODE_OFFSET); } -constexpr bool is_server_error(const long code) { +EXPORT_CPR constexpr bool is_server_error(const long code) { return (code >= SERVER_ERROR_CODE_OFFSET && code < MISC_CODE_OFFSET); } - -} // namespace status -} // namespace cpr +} // namespace cpr::status #endif diff --git a/include/cpr/threadpool.h b/include/cpr/threadpool.h index 0532a5bed..ac0e18aad 100644 --- a/include/cpr/threadpool.h +++ b/include/cpr/threadpool.h @@ -1,10 +1,16 @@ #ifndef CPR_THREADPOOL_H #define CPR_THREADPOOL_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include #include -#include #include #include #include @@ -13,15 +19,18 @@ #include #include #include +#endif + +#include #define CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM std::thread::hardware_concurrency() -constexpr size_t CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM = 1; -constexpr std::chrono::milliseconds CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME{250}; +EXPORT_CPR inline constexpr size_t CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM = 1; +EXPORT_CPR inline constexpr std::chrono::milliseconds CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME{250}; namespace cpr { -class ThreadPool { +EXPORT_CPR class ThreadPool { public: using Task = std::function; diff --git a/include/cpr/timeout.h b/include/cpr/timeout.h index e2464803d..3e68193a0 100644 --- a/include/cpr/timeout.h +++ b/include/cpr/timeout.h @@ -1,12 +1,21 @@ #ifndef CPR_TIMEOUT_H #define CPR_TIMEOUT_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include +#endif + #include namespace cpr { -class Timeout { +EXPORT_CPR class Timeout { public: // Template constructor to accept any chrono duration type and convert it to milliseconds template @@ -16,7 +25,8 @@ class Timeout { // No way around since curl uses a long here. // NOLINTNEXTLINE(google-runtime-int) - [[nodiscard]] long Milliseconds() const; + [[nodiscard]] + long Milliseconds() const; std::chrono::milliseconds ms; }; diff --git a/include/cpr/unix_socket.h b/include/cpr/unix_socket.h index 4b8701634..29eed5b07 100644 --- a/include/cpr/unix_socket.h +++ b/include/cpr/unix_socket.h @@ -1,15 +1,24 @@ #ifndef CPR_UNIX_SOCKET_H #define CPR_UNIX_SOCKET_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include +#endif namespace cpr { -class UnixSocket { +EXPORT_CPR class UnixSocket { public: UnixSocket(std::string unix_socket) : unix_socket_(std::move(unix_socket)) {} - [[nodiscard]] const char* GetUnixSocketString() const noexcept; + [[nodiscard]] + const char* GetUnixSocketString() const noexcept; private: const std::string unix_socket_; diff --git a/include/cpr/user_agent.h b/include/cpr/user_agent.h index 5cb04ea7c..e6eaa9341 100644 --- a/include/cpr/user_agent.h +++ b/include/cpr/user_agent.h @@ -1,13 +1,21 @@ #ifndef CPR_USER_AGENT_H #define CPR_USER_AGENT_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include #include +#endif #include "cpr/cprtypes.h" namespace cpr { -class UserAgent : public StringHolder { +EXPORT_CPR class UserAgent : public StringHolder { public: UserAgent() = default; UserAgent(std::string useragent) : StringHolder(std::move(useragent)) {} diff --git a/include/cpr/util.h b/include/cpr/util.h index e210b19b7..2ad9cd332 100644 --- a/include/cpr/util.h +++ b/include/cpr/util.h @@ -1,10 +1,17 @@ #ifndef CPR_UTIL_H #define CPR_UTIL_H +#include "cpr/export.h" + +/** + * If we build cpr as C++20 module, we use 'import std;'. + * So skip all other imports and declare them in 'cpr.cxx'. + **/ +#ifndef CPR_IMPORT_STD #include -#include #include #include +#endif #include "cpr/callback.h" #include "cpr/cookies.h" @@ -14,16 +21,16 @@ namespace cpr::util { -Header parseHeader(const std::string& headers, std::string* status_line = nullptr, std::string* reason = nullptr); -Cookies parseCookies(curl_slist* raw_cookies); -size_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read); -size_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header); -size_t writeFunction(char* ptr, size_t size, size_t nmemb, void* data); -size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file); -size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write); -size_t writeSSEFunction(char* ptr, size_t size, size_t nmemb, ServerSentEventCallback* sse); +EXPORT_CPR Header parseHeader(const std::string& headers, std::string* status_line = nullptr, std::string* reason = nullptr); +EXPORT_CPR Cookies parseCookies(curl_slist* raw_cookies); +EXPORT_CPR size_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read); +EXPORT_CPR size_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header); +EXPORT_CPR size_t writeFunction(char* ptr, size_t size, size_t nmemb, void* data); +EXPORT_CPR size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file); +EXPORT_CPR size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write); +EXPORT_CPR size_t writeSSEFunction(char* ptr, size_t size, size_t nmemb, ServerSentEventCallback* sse); -template +EXPORT_CPR template int progressUserFunction(const T* progress, cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, cpr_pf_arg_t ultotal, cpr_pf_arg_t ulnow) { const int cancel_retval{1}; #ifdef CURL_PROGRESSFUNC_CONTINUE // Not always defined. Ref: https://github.com/libcpr/cpr/issues/932 @@ -31,18 +38,18 @@ int progressUserFunction(const T* progress, cpr_pf_arg_t dltotal, cpr_pf_arg_t d #endif // CURL_PROGRESSFUNC_CONTINUE return (*progress)(dltotal, dlnow, ultotal, ulnow) ? 0 : cancel_retval; } -int debugUserFunction(CURL* handle, curl_infotype type, char* data, size_t size, const DebugCallback* debug); -std::vector split(const std::string& to_split, char delimiter); -util::SecureString urlEncode(std::string_view s); -util::SecureString urlDecode(std::string_view s); +EXPORT_CPR int debugUserFunction(CURL* handle, curl_infotype type, char* data, size_t size, const DebugCallback* debug); +EXPORT_CPR std::vector split(const std::string& to_split, char delimiter); +EXPORT_CPR util::SecureString urlEncode(std::string_view s); +EXPORT_CPR util::SecureString urlDecode(std::string_view s); -bool isTrue(const std::string& s); +EXPORT_CPR bool isTrue(const std::string& s); /** * Parses the given std::string into time_t (unix ms). * This parsing happens time_t size agnostic since time_t does not use the same underlying type on all systems/compilers. **/ -time_t sTimestampToT(const std::string& /*st*/); +EXPORT_CPR time_t sTimestampToT(const std::string& /*st*/); } // namespace cpr::util diff --git a/include/cpr/verbose.h b/include/cpr/verbose.h index 46714200d..2de26d508 100644 --- a/include/cpr/verbose.h +++ b/include/cpr/verbose.h @@ -1,9 +1,11 @@ #ifndef CPR_VERBOSE_H_ #define CPR_VERBOSE_H_ +#include "cpr/export.h" + namespace cpr { -class Verbose { +EXPORT_CPR class Verbose { public: Verbose() = default; Verbose(const bool p_verbose) : verbose{p_verbose} {} diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt new file mode 100644 index 000000000..f05b3c6bb --- /dev/null +++ b/modules/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.28) + +# Apple Clang does not ship clang-scan-deps, which CMake requires for module +# dependency scanning. Use LLVM clang (e.g. from Homebrew) instead. +if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + message(FATAL_ERROR + "Apple Clang does not support C++20 module dependency scanning.\n" + "Use LLVM clang (e.g. 'brew install llvm') and pass " + "-DCMAKE_CXX_COMPILER=$(brew --prefix llvm)/bin/clang++ to CMake.") +endif() + +# GCC gained module scanning support in version 14. +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND + CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14") + message(FATAL_ERROR + "C++20 module scanning requires GCC 14 or later " + "(found ${CMAKE_CXX_COMPILER_VERSION}).") +endif() + +set(CMAKE_CXX_SCAN_FOR_MODULES ON) + +add_library(cpr_module) +add_library(cpr::module ALIAS cpr_module) + +target_sources(cpr_module + PUBLIC + FILE_SET CXX_MODULES FILES + cpr.cxx +) + +target_compile_features(cpr_module PUBLIC cxx_std_23) +set_target_properties(cpr_module PROPERTIES CXX_MODULE_STD ON) + +target_include_directories(cpr_module PUBLIC + $ + $ +) + +target_link_libraries(cpr_module PUBLIC cpr::cpr) + +# Installation +if(CPR_ENABLE_INSTALL) + install(TARGETS cpr_module + EXPORT ${PROJECT_NAME}Targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + FILE_SET CXX_MODULES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/module + ) +endif() diff --git a/modules/cpr.cxx b/modules/cpr.cxx new file mode 100644 index 000000000..ded60e92d --- /dev/null +++ b/modules/cpr.cxx @@ -0,0 +1,25 @@ +module; + +#include +#include +#include +#include +#include +#include +#include +#ifdef CPR_USE_BOOST_FILESYSTEM +#define BOOST_FILESYSTEM_VERSION 4 +#include +#endif + +export module cpr; + +import std; + +#define CPR_AS_MODULE 1 +#define CPR_IMPORT_STD 1 +#define EXPORT_CPR export + +extern "C++" { +#include "cpr/cpr.h" +} diff --git a/test/version_tests.cpp b/test/version_tests.cpp index 5bf7ed003..3aff950e5 100644 --- a/test/version_tests.cpp +++ b/test/version_tests.cpp @@ -5,15 +5,8 @@ #include -TEST(VersionTests, StringVersionExists) { -#ifndef CPR_VERSION - EXPECT_TRUE(false); -#endif // CPR_VERSION -} - TEST(VersionTests, StringVersionValid) { - EXPECT_TRUE(CPR_VERSION != nullptr); - std::string version = CPR_VERSION; + std::string version{CPR_VERSION}; // Check if the version string is: '\d+\.\d+\.\d+' bool digit = true; @@ -34,31 +27,6 @@ TEST(VersionTests, StringVersionValid) { EXPECT_EQ(dotCount, 2); } -TEST(VersionTests, VersionMajorExists) { -#ifndef CPR_VERSION_MAJOR - EXPECT_TRUE(false); -#endif // CPR_VERSION_MAJOR -} - -TEST(VersionTests, VersionMinorExists) { -#ifndef CPR_VERSION_MINOR - EXPECT_TRUE(false); -#endif // CPR_VERSION_MINOR -} - -TEST(VersionTests, VersionPatchExists) { -#ifndef CPR_VERSION_PATCH - EXPECT_TRUE(false); -#endif // CPR_VERSION_PATCH -} - -TEST(VersionTests, VersionNumExists) { -#ifndef CPR_VERSION_NUM - EXPECT_TRUE(false); -#endif // CPR_VERSION_NUM -} - - int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();