From c9455adcc989883888fe7aca0314d0f8e2b01665 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 20 Aug 2026 22:22:49 +0900 Subject: [PATCH 1/2] Issue Fixes --- CHANGELOG.md | 8 ++++++ CMakeLists.txt | 51 +++++++++++++++++++++++++++-------- include/countly.hpp | 9 ++++--- include/countly/constants.hpp | 11 ++++++++ include/countly/event.hpp | 3 +++ src/countly.cpp | 12 ++++----- src/event.cpp | 5 ++-- src/views_module.cpp | 11 ++++---- 8 files changed, 83 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b6e36f..a232cd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## Next release +- Static library builds on non-Windows platforms no longer require a custom HTTP client; the built-in libcurl client can now be used there as well. + +- Added the `COUNTLY_USE_SYSTEM_JSON` CMake option to compile the SDK against the application's own nlohmann/json (3.11 or newer) instead of the bundled copy, for applications that already use the library. The SDK headers now reject unsupported nlohmann/json versions at compile time with a clear error. + +- Fixed session, view and timed-event durations being distorted when the system clock changed while the application was running; elapsed time is now measured with a monotonic clock. +- Fixed `cmake --install` producing an unusable include layout: `countly.hpp` is now installed into the include root, all public headers (several were missing, including `countly_configuration.hpp`) are installed under `include/countly`, and the bundled nlohmann/json headers are installed alongside them, so the installed tree compiles on its own. + ## 26.8.0 - ! Minor breaking change ! The SDK now enforces internal limits on recorded data (key length, value size, segmentation entry count, breadcrumb count, stack trace lines per thread and line length) across events, views, crashes, and user properties. Defaults can be overridden by server-side SDK Behavior Settings, or during init via `setMaxKeyLength`, `setMaxValueSize`, `setMaxSegmentationValues`, `setMaxBreadcrumbCount`, `setMaxStackTraceLinesPerThread` and `setMaxStackTraceLineLength`. - ! Minor breaking change ! Updated the bundled nlohmann/json from v3.7.0 to v3.12.0. JSON types are part of the public API, so applications must be recompiled against the new headers; binaries built against the old headers will not link against a library built with the new ones. diff --git a/CMakeLists.txt b/CMakeLists.txt index b90cc77..162efa2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ option(COUNTLY_USE_CUSTOM_HTTP "Use a custom HTTP library" OFF) option(COUNTLY_USE_CUSTOM_SHA256 "Use a custom SHA 256 library" OFF) option(COUNTLY_USE_SQLITE "Use SQLite" OFF) option(COUNTLY_USE_SYSTEM_SQLITE "Link the system SQLite instead of compiling the vendored amalgamation" OFF) +option(COUNTLY_USE_SYSTEM_JSON "Compile against the system nlohmann_json instead of the vendored copy" OFF) option(COUNTLY_BUILD_TESTS "Build test programs" OFF) option(COUNTLY_BUILD_SAMPLE "Build Sample programs" OFF) @@ -23,25 +24,30 @@ message("Use a custom HTTP library:" ${COUNTLY_USE_CUSTOM_HTTP}) message("Use a custom SHA 256 library:" ${COUNTLY_USE_CUSTOM_SHA256}) message("Use SQLite:" ${COUNTLY_USE_SQLITE}) message("Use system SQLite:" ${COUNTLY_USE_SYSTEM_SQLITE}) +message("Use system nlohmann_json:" ${COUNTLY_USE_SYSTEM_JSON}) message("Build test programs:" ${COUNTLY_BUILD_TESTS}) message("Build Sample programs:" ${COUNTLY_BUILD_SAMPLE}) -if (NOT WIN32 AND NOT BUILD_SHARED_LIBS AND NOT COUNTLY_USE_CUSTOM_HTTP) - message(FATAL_ERROR "You must provide a custom HTTP function when compiling statically.") -endif() - -# Compile countly public headers +# Compile countly public headers: everything countly.hpp includes, so an +# installed copy of the SDK is self-contained. countly.hpp itself is installed +# separately into the include root (see the install rules at the bottom). set(COUNTLY_PUBLIC_HEADERS - ${CMAKE_CURRENT_SOURCE_DIR}/include/countly.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/configuration_module.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/configuration_provider.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/constants.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/countly_configuration.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/crash_module.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/event.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/logger_module.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/crash_module.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/remote_config_store.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/request_builder.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/request_module.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/storage_module_base.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/views_module.hpp) add_library(countly ${COUNTLY_PUBLIC_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/include/countly.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/countly.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/views_module.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/logger_module.cpp @@ -66,7 +72,21 @@ set_target_properties(countly PROPERTIES find_package(Threads) target_link_libraries(countly Threads::Threads) -target_include_directories(countly PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/vendor/json/include) +# nlohmann/json is part of the public API, so its headers are a usage +# requirement of the countly target. By default the vendored copy is used and +# later installed next to the SDK headers, so the installed tree is +# self-contained. An application that already uses nlohmann/json should set +# COUNTLY_USE_SYSTEM_JSON to compile the SDK against that same copy instead, +# so only one version of the library exists in the build (the SDK headers +# reject unsupported versions at compile time). +if(COUNTLY_USE_SYSTEM_JSON) + find_package(nlohmann_json 3.11 REQUIRED) + target_link_libraries(countly nlohmann_json::nlohmann_json) +else() + target_include_directories(countly PUBLIC + $ + $) +endif() if (COUNTLY_BUILD_TESTS) message("Compiling definitions for tests") target_compile_definitions(countly PRIVATE COUNTLY_BUILD_TESTS) @@ -154,7 +174,7 @@ if(COUNTLY_BUILD_TESTS) target_compile_definitions(countly-tests PRIVATE COUNTLY_USE_CUSTOM_SHA256) endif() target_include_directories(countly-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/doctest/doctest) - target_include_directories(countly-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/vendor/json/include) + # nlohmann/json comes in as a usage requirement of the countly target. target_link_libraries(countly-tests countly) set_target_properties(countly-tests PROPERTIES CXX_STANDARD 14 @@ -177,7 +197,7 @@ if(COUNTLY_USE_SQLITE) endif() endif() target_include_directories(countly-sample PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/doctest/doctest) - target_include_directories(countly-sample PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/vendor/json/include) + # nlohmann/json comes in as a usage requirement of the countly target. target_link_libraries(countly-sample countly) set_target_properties(countly-sample PROPERTIES CXX_STANDARD 14 @@ -190,3 +210,12 @@ install(TARGETS countly LIBRARY DESTINATION lib RUNTIME DESTINATION bin PUBLIC_HEADER DESTINATION include/countly) +# countly.hpp lives in the include root, so `#include "countly.hpp"` works +# against the installed tree just like against the source tree. +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/countly.hpp DESTINATION include) +# The SDK headers include "nlohmann/json.hpp", so ship the vendored copy with +# them; without it an installed tree does not compile on its own. Skipped when +# building against the system copy, which the application provides itself. +if(NOT COUNTLY_USE_SYSTEM_JSON) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/vendor/json/include/nlohmann DESTINATION include) +endif() diff --git a/include/countly.hpp b/include/countly.hpp index 335e077..1ab79e3 100644 --- a/include/countly.hpp +++ b/include/countly.hpp @@ -464,9 +464,9 @@ class Countly : public cly::CountlyDelegates { void _changeDeviceIdWithoutMerge(const std::string &value); - std::chrono::system_clock::duration getSessionDuration(std::chrono::system_clock::time_point now); + std::chrono::steady_clock::duration getSessionDuration(std::chrono::steady_clock::time_point now); - std::chrono::system_clock::duration getSessionDuration(); + std::chrono::steady_clock::duration getSessionDuration(); void updateLoop(); void packEvents(); @@ -474,7 +474,10 @@ class Countly : public cly::CountlyDelegates { std::atomic is_being_disposed{false}; std::atomic is_sdk_initialized{false}; - std::chrono::system_clock::time_point last_sent_session_request; + // Monotonic, not system_clock: session duration is elapsed time, and a + // system clock change mid-session must not shrink it or turn it negative + // (issue #100). Wall-clock timestamps in requests still use system_clock. + std::chrono::steady_clock::time_point last_sent_session_request; nlohmann::json session_params; std::unique_ptr thread; diff --git a/include/countly/constants.hpp b/include/countly/constants.hpp index dccde59..3163203 100644 --- a/include/countly/constants.hpp +++ b/include/countly/constants.hpp @@ -2,6 +2,17 @@ #define COUNTLY_CONSTANTS_HPP_ #include "nlohmann/json.hpp" + +// nlohmann/json is part of the SDK's public API, so the copy these headers are +// compiled against must be the one the library was built with. If another copy +// of the library wins the include-path race (both use the same include guard, +// so only the first one found is ever seen), fail here with a clear message +// instead of failing at link time. Versions from 3.11 on carry an ABI-tagged +// inline namespace, which is what makes a mismatch a linker error rather than +// silent memory corruption. To build against your application's own copy, use +// the COUNTLY_USE_SYSTEM_JSON CMake option. +static_assert(NLOHMANN_JSON_VERSION_MAJOR == 3 && NLOHMANN_JSON_VERSION_MINOR >= 11, "Countly SDK requires nlohmann/json 3.11 or newer (3.x). An incompatible copy of nlohmann/json was found first on the include path."); + #include #include #include diff --git a/include/countly/event.hpp b/include/countly/event.hpp index b09f60c..5a5e58c 100644 --- a/include/countly/event.hpp +++ b/include/countly/event.hpp @@ -42,6 +42,9 @@ class Event { nlohmann::json object; bool timer_running; std::chrono::system_clock::time_point timestamp; + // Durations are measured on the monotonic clock so a system clock change + // while a timed event runs cannot produce a wrong or negative duration. + std::chrono::steady_clock::time_point timer_start; }; } // namespace cly #endif diff --git a/src/countly.cpp b/src/countly.cpp index 1ed69b8..45c0a5a 100644 --- a/src/countly.cpp +++ b/src/countly.cpp @@ -1389,7 +1389,7 @@ bool Countly::beginSession() { requestModule->addRequestToQueue(data); session_params.erase("user_details"); - last_sent_session_request = Countly::getTimestamp(); + last_sent_session_request = std::chrono::steady_clock::now(); began_session = true; // snapshot guarded state before releasing the lock bool shouldUpdateRemoteConfig = remote_config_enabled; @@ -1568,8 +1568,8 @@ bool Countly::endSession() { log(LogLevel::ERROR, "[Countly] endSession, Session tracking is disabled in server configuration, can not end session."); return false; } - const std::chrono::system_clock::time_point now = Countly::getTimestamp(); - const auto timestamp = std::chrono::duration_cast(now.time_since_epoch()); + const auto timestamp = std::chrono::duration_cast(Countly::getTimestamp().time_since_epoch()); + const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); // lock_guard so the mutex is released on scope exit, including the early // returns below and any exception (e.g. a json type_error from a session_params @@ -1933,13 +1933,13 @@ std::string Countly::calculateChecksum(const std::string &salt, const std::strin #endif } -std::chrono::system_clock::duration Countly::getSessionDuration(std::chrono::system_clock::time_point now) { +std::chrono::steady_clock::duration Countly::getSessionDuration(std::chrono::steady_clock::time_point now) { std::lock_guard lk(*mutex); - std::chrono::system_clock::duration duration = now - last_sent_session_request; + std::chrono::steady_clock::duration duration = now - last_sent_session_request; return duration; } -std::chrono::system_clock::duration Countly::getSessionDuration() { return Countly::getSessionDuration(Countly::getTimestamp()); } +std::chrono::steady_clock::duration Countly::getSessionDuration() { return Countly::getSessionDuration(std::chrono::steady_clock::now()); } void Countly::updateLoop() { log(LogLevel::DEBUG, "[Countly][updateLoop]"); diff --git a/src/event.cpp b/src/event.cpp index 5f87be0..c9e6c6e 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -28,6 +28,7 @@ Event::Event(const std::string &key, size_t count, double sum, double duration) void Event::setTimestamp() { timestamp = std::chrono::system_clock::now(); + timer_start = std::chrono::steady_clock::now(); object["timestamp"] = std::chrono::duration_cast(timestamp.time_since_epoch()).count(); std::time_t time = std::chrono::system_clock::to_time_t(timestamp); @@ -45,8 +46,8 @@ void Event::startTimer() { void Event::stopTimer() { if (timer_running) { - auto now = std::chrono::system_clock::now(); - object["dur"] = std::chrono::duration_cast(now - timestamp).count(); + auto now = std::chrono::steady_clock::now(); + object["dur"] = std::chrono::duration_cast(now - timer_start).count(); timer_running = false; } } diff --git a/src/views_module.cpp b/src/views_module.cpp index 9302bc9..bcfcfba 100644 --- a/src/views_module.cpp +++ b/src/views_module.cpp @@ -13,7 +13,9 @@ class ViewsModule::ViewModuleImpl { public: std::string name; std::string viewId; - std::chrono::seconds startTime; + // Monotonic, not system_clock: the view duration is elapsed time, and a + // system clock change while the view is open must not distort it (issue #100). + std::chrono::steady_clock::time_point startTime; }; private: @@ -67,9 +69,8 @@ class ViewsModule::ViewModuleImpl { } } else { - const std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - const auto timestamp = std::chrono::duration_cast(now.time_since_epoch()); - std::chrono::seconds dur = timestamp - v->startTime; + const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + const auto dur = std::chrono::duration_cast(now - v->startTime); duration = dur.count(); } @@ -107,7 +108,7 @@ class ViewsModule::ViewModuleImpl { ViewModuleImpl::ViewInfo *v = new ViewModuleImpl::ViewInfo(); v->name = limitedName; v->viewId = cly::utils::generateEventID(); - v->startTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + v->startTime = std::chrono::steady_clock::now(); std::shared_ptr ptr(v); From 59c6ce7d006929318d890dda81b5dc56325e0281 Mon Sep 17 00:00:00 2001 From: turtledreams <62231246+turtledreams@users.noreply.github.com> Date: Sat, 17 Oct 2026 15:37:13 +0900 Subject: [PATCH 2/2] Tidy changelog and comments, add CI for install, static and system JSON builds - CHANGELOG section as XX.XX.XX with one line per entry, and a minor breaking line for the nlohmann/json version check - comments cut to single-line notes - the version check's message names COUNTLY_USE_SYSTEM_JSON - CI: static-builtin-http and system-json flavors, and an install job that compiles and runs an app against the installed SDK only Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_017YYaqXRqRFW3PxHyzgwNa9 --- .github/workflows/tests.yml | 61 +++++++++++++++++++++++++++++++++++ CHANGELOG.md | 11 ++++--- CMakeLists.txt | 20 ++---------- include/countly.hpp | 4 +-- include/countly/constants.hpp | 11 ++----- include/countly/event.hpp | 3 +- src/views_module.cpp | 3 +- 7 files changed, 75 insertions(+), 38 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 44cd7bc..eec3317 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -83,6 +83,11 @@ jobs: experimental: true - name: static-custom-http flags: -DCOUNTLY_USE_SQLITE=ON -DBUILD_SHARED_LIBS=OFF -DCOUNTLY_USE_CUSTOM_HTTP=ON + - name: static-builtin-http + flags: -DCOUNTLY_USE_SQLITE=ON -DBUILD_SHARED_LIBS=OFF + - name: system-json + flags: -DCOUNTLY_USE_SQLITE=ON -DCOUNTLY_USE_SYSTEM_JSON=ON + apt: nlohmann-json3-dev - name: custom-sha256 flags: -DCOUNTLY_USE_SQLITE=ON -DCOUNTLY_USE_CUSTOM_SHA256=ON - name: system-sqlite @@ -157,6 +162,62 @@ jobs: - name: Run tests run: ./build/countly-tests + # ────────────────────────────────────────────── + # Installed SDK: an app that sees only the install + # prefix must compile, link and run against it. + # ────────────────────────────────────────────── + install: + name: install-${{ matrix.libs }} + runs-on: ubuntu-24.04 + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - libs: shared + shared: ON + - libs: static + shared: OFF + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: "recursive" + + - name: Install dependencies + run: | + sudo apt-get $APT_OPTS update + sudo apt-get $APT_OPTS install -y --no-install-recommends \ + libcurl4-openssl-dev libssl-dev + + - name: Build and install + run: | + cmake -DBUILD_SHARED_LIBS=${{ matrix.shared }} -DCMAKE_BUILD_TYPE=Release -B build . + cmake --build build -j 4 + cmake --install build --prefix "$RUNNER_TEMP/countly" + + - name: Compile and run an app against the installed SDK + working-directory: ${{ runner.temp }} + run: | + cat > app.cpp << 'EOF' + #include "countly.hpp" + #include + + int main() { + (void)cly::Countly::getInstance(); + cly::Event event("install_check", 1); + event.addSegmentation("source", "installed tree"); + std::cout << event.serialize() << std::endl; + return 0; + } + EOF + if [ "${{ matrix.libs }}" = "static" ]; then + g++ -std=c++14 -I countly/include app.cpp countly/lib/libcountly.a -lcurl -lssl -lcrypto -lpthread -o app + else + g++ -std=c++14 -I countly/include app.cpp -L countly/lib -lcountly -Wl,-rpath,"$PWD/countly/lib" -o app + fi + ./app + # ────────────────────────────────────────────── # Release tooling smoke test: the SBOM generator must # not be discovered broken on release day. diff --git a/CHANGELOG.md b/CHANGELOG.md index a232cd6..e5583ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ -## Next release -- Static library builds on non-Windows platforms no longer require a custom HTTP client; the built-in libcurl client can now be used there as well. +## XX.XX.XX +- ! Minor breaking change ! Builds now stop with a clear error when the SDK headers find a nlohmann/json older than 3.11 first on the include path, instead of mixing two versions of the library. -- Added the `COUNTLY_USE_SYSTEM_JSON` CMake option to compile the SDK against the application's own nlohmann/json (3.11 or newer) instead of the bundled copy, for applications that already use the library. The SDK headers now reject unsupported nlohmann/json versions at compile time with a clear error. +- Added the `COUNTLY_USE_SYSTEM_JSON` CMake option for building against the application's own nlohmann/json (3.11 or newer) instead of the bundled copy (default `OFF`). +- Added support for static library builds with the built-in HTTP client (libcurl) on Linux and macOS. -- Fixed session, view and timed-event durations being distorted when the system clock changed while the application was running; elapsed time is now measured with a monotonic clock. -- Fixed `cmake --install` producing an unusable include layout: `countly.hpp` is now installed into the include root, all public headers (several were missing, including `countly_configuration.hpp`) are installed under `include/countly`, and the bundled nlohmann/json headers are installed alongside them, so the installed tree compiles on its own. +- Fixed session, view and timed event durations changing when the system clock was adjusted while the app was running. +- Fixed `cmake --install` producing an include folder that could not be compiled against. ## 26.8.0 - ! Minor breaking change ! The SDK now enforces internal limits on recorded data (key length, value size, segmentation entry count, breadcrumb count, stack trace lines per thread and line length) across events, views, crashes, and user properties. Defaults can be overridden by server-side SDK Behavior Settings, or during init via `setMaxKeyLength`, `setMaxValueSize`, `setMaxSegmentationValues`, `setMaxBreadcrumbCount`, `setMaxStackTraceLinesPerThread` and `setMaxStackTraceLineLength`. diff --git a/CMakeLists.txt b/CMakeLists.txt index 162efa2..4332523 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,9 +28,7 @@ message("Use system nlohmann_json:" ${COUNTLY_USE_SYSTEM_JSON}) message("Build test programs:" ${COUNTLY_BUILD_TESTS}) message("Build Sample programs:" ${COUNTLY_BUILD_SAMPLE}) -# Compile countly public headers: everything countly.hpp includes, so an -# installed copy of the SDK is self-contained. countly.hpp itself is installed -# separately into the include root (see the install rules at the bottom). +# Public headers installed under include/countly; countly.hpp goes to the include root, see the install rules set(COUNTLY_PUBLIC_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/configuration_module.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/countly/configuration_provider.hpp @@ -72,13 +70,7 @@ set_target_properties(countly PROPERTIES find_package(Threads) target_link_libraries(countly Threads::Threads) -# nlohmann/json is part of the public API, so its headers are a usage -# requirement of the countly target. By default the vendored copy is used and -# later installed next to the SDK headers, so the installed tree is -# self-contained. An application that already uses nlohmann/json should set -# COUNTLY_USE_SYSTEM_JSON to compile the SDK against that same copy instead, -# so only one version of the library exists in the build (the SDK headers -# reject unsupported versions at compile time). +# nlohmann/json is part of the public API, so it is a usage requirement of the countly target if(COUNTLY_USE_SYSTEM_JSON) find_package(nlohmann_json 3.11 REQUIRED) target_link_libraries(countly nlohmann_json::nlohmann_json) @@ -174,7 +166,6 @@ if(COUNTLY_BUILD_TESTS) target_compile_definitions(countly-tests PRIVATE COUNTLY_USE_CUSTOM_SHA256) endif() target_include_directories(countly-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/doctest/doctest) - # nlohmann/json comes in as a usage requirement of the countly target. target_link_libraries(countly-tests countly) set_target_properties(countly-tests PROPERTIES CXX_STANDARD 14 @@ -197,7 +188,6 @@ if(COUNTLY_USE_SQLITE) endif() endif() target_include_directories(countly-sample PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/doctest/doctest) - # nlohmann/json comes in as a usage requirement of the countly target. target_link_libraries(countly-sample countly) set_target_properties(countly-sample PROPERTIES CXX_STANDARD 14 @@ -210,12 +200,8 @@ install(TARGETS countly LIBRARY DESTINATION lib RUNTIME DESTINATION bin PUBLIC_HEADER DESTINATION include/countly) -# countly.hpp lives in the include root, so `#include "countly.hpp"` works -# against the installed tree just like against the source tree. install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/countly.hpp DESTINATION include) -# The SDK headers include "nlohmann/json.hpp", so ship the vendored copy with -# them; without it an installed tree does not compile on its own. Skipped when -# building against the system copy, which the application provides itself. +# The installed headers include nlohmann/json.hpp, so the bundled copy ships with them if(NOT COUNTLY_USE_SYSTEM_JSON) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/vendor/json/include/nlohmann DESTINATION include) endif() diff --git a/include/countly.hpp b/include/countly.hpp index 1ab79e3..64d285b 100644 --- a/include/countly.hpp +++ b/include/countly.hpp @@ -474,9 +474,7 @@ class Countly : public cly::CountlyDelegates { std::atomic is_being_disposed{false}; std::atomic is_sdk_initialized{false}; - // Monotonic, not system_clock: session duration is elapsed time, and a - // system clock change mid-session must not shrink it or turn it negative - // (issue #100). Wall-clock timestamps in requests still use system_clock. + // steady_clock, so a system clock change can't distort the session duration std::chrono::steady_clock::time_point last_sent_session_request; nlohmann::json session_params; diff --git a/include/countly/constants.hpp b/include/countly/constants.hpp index 3163203..bf06f79 100644 --- a/include/countly/constants.hpp +++ b/include/countly/constants.hpp @@ -3,15 +3,8 @@ #include "nlohmann/json.hpp" -// nlohmann/json is part of the SDK's public API, so the copy these headers are -// compiled against must be the one the library was built with. If another copy -// of the library wins the include-path race (both use the same include guard, -// so only the first one found is ever seen), fail here with a clear message -// instead of failing at link time. Versions from 3.11 on carry an ABI-tagged -// inline namespace, which is what makes a mismatch a linker error rather than -// silent memory corruption. To build against your application's own copy, use -// the COUNTLY_USE_SYSTEM_JSON CMake option. -static_assert(NLOHMANN_JSON_VERSION_MAJOR == 3 && NLOHMANN_JSON_VERSION_MINOR >= 11, "Countly SDK requires nlohmann/json 3.11 or newer (3.x). An incompatible copy of nlohmann/json was found first on the include path."); +// From 3.11 on nlohmann/json tags its namespace with its version, so a copy other than the library's fails to link instead of corrupting memory +static_assert(NLOHMANN_JSON_VERSION_MAJOR == 3 && NLOHMANN_JSON_VERSION_MINOR >= 11, "Countly SDK requires nlohmann/json 3.11 or newer (3.x). An incompatible copy of nlohmann/json was found first on the include path. To use your own copy, build the SDK with COUNTLY_USE_SYSTEM_JSON."); #include #include diff --git a/include/countly/event.hpp b/include/countly/event.hpp index 5a5e58c..7495c74 100644 --- a/include/countly/event.hpp +++ b/include/countly/event.hpp @@ -42,8 +42,7 @@ class Event { nlohmann::json object; bool timer_running; std::chrono::system_clock::time_point timestamp; - // Durations are measured on the monotonic clock so a system clock change - // while a timed event runs cannot produce a wrong or negative duration. + // steady_clock, so a system clock change can't distort the timed duration std::chrono::steady_clock::time_point timer_start; }; } // namespace cly diff --git a/src/views_module.cpp b/src/views_module.cpp index bcfcfba..a06f5b8 100644 --- a/src/views_module.cpp +++ b/src/views_module.cpp @@ -13,8 +13,7 @@ class ViewsModule::ViewModuleImpl { public: std::string name; std::string viewId; - // Monotonic, not system_clock: the view duration is elapsed time, and a - // system clock change while the view is open must not distort it (issue #100). + // steady_clock, so a system clock change can't distort the view duration std::chrono::steady_clock::time_point startTime; };