Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion MODERNIZATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ header parsing, and the top of the expensive list is exactly the
primary metric). Capture the Linux numbers on the self-hosted arm64 box
and an iOS build-only timing before the Phase 2.4 checkpoint.

## Phase 2.1 — Canary: streamr-eventemitter + streamr-json (PR pending)
## Phase 2.1 — Canary: streamr-eventemitter + streamr-json ✅ (PR #29, merged)
First real modules. Both packages carry the designed façade shape: one
`.cppm` partition per public header (header `#include`d in the global
module fragment, public names re-exported with `export using`), a primary
Expand Down Expand Up @@ -491,6 +491,54 @@ interface unit `export import`ing the partitions, INTERFACE→STATIC via
- iOS gate via the PR's `iosbuild` keyword (module lib builds; tests are
host-only). Android modules validation lands with its phase-2.5 gate.

## Phase 2.2 — streamr-logger + streamr-utils (PR pending)
folly enters the global module fragment; utils is the folly::coro canary.
- **streamr-logger**: 4 partitions (Logger, LoggerImpl, SLogger,
StreamrLogLevel) + primary unit. folly logging machinery in the GMF
compiled without incident. `detail/` headers get no partitions (internal
API); LoggerEnvTest, which tests detail machinery, keeps its detail
`#include` alongside `import streamr.logger` — mixing is the designed
property of the façade.
- **streamr-utils**: 21 partitions + primary. **The folly::coro coroutine
canary passed with zero compiler workarounds** — waitForEvent,
waitForCondition, collect, toCoroTask (coro Tasks in GMF, coroutine
code re-exported) all build and run under Clang 22. The per-header
opt-out budget went unused.
- **New mechanism discovered (now in `streamr_add_module_library()`)**:
exported module targets need `target_compile_features(… PUBLIC
cxx_std_26)`. When another build tree imports the target from its
export file, CMake synthesizes a consumer-side BMI-compiling target and
hard-errors if the standard level is not part of the exported usage
requirements. (First surfaced when streamr-logger consumed
streamr-json's export.)
- **Language rule fallout**: namespace-scope `constexpr` variables have
internal linkage and cannot be re-exported — 7 header constants across
both packages became `inline constexpr` (the correct C++17+ idiom for
header constants regardless of modules).
- 20 test files + both examples flipped to `import`; the
include-what-you-use pass stayed small (`<thread>`, `<chrono>`,
`<string>`).
- **clangd-modules root cause identified**: the 2.1 quirks (constrained
`string(string_view)` ctor, and now generic-lambda `std::visit`
exhaustiveness + plain `std::string` overload failures) share one
mechanism — clangd fails to unify std types between its own preamble
and the types reached through module BMIs. It only bites where std
types cross the module boundary in an import-using file. One test file
(`toEthereumAddressOrENSNameTest.cpp` — its whole API is std types in
Branded wrappers) needed the planned lint-exclusion fallback: first and
only use so far; the compiler still typechecks it on every build.
`bugprone-exception-escape` also surfaces on `main()` in import-using
files (clangd sees deeper through BMIs) — legitimate findings, fixed
with function-try-blocks.
- Verified: logger 63/63, utils 49/49 through import; downstream
proto-rpc/dht standalone builds unchanged; root tree 307/307; full lint
(one documented exclusion).
- **Honest bench note**: no incremental-rebuild improvement is expected
yet — the heavy consumers of SLogger/utils headers (dht,
trackerless-network) still `#include` them; the measured win arrives
when those packages flip (2.4/2.5) and their test TUs load BMIs instead
of re-parsing the header stack.

## Lint/IDE survival
- During the façade stage, headers remain the fully-linted source of truth
(`lint.sh` globs only `*.hpp/*.cpp`); `.cppm` added to clang-format only.
Expand Down
5 changes: 5 additions & 0 deletions cmake/StreamrModules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ function(streamr_add_module_library TARGET)
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/modules
FILES ${ARG_FILES})
set_target_properties(${TARGET} PROPERTIES CXX_SCAN_FOR_MODULES ON)
# PUBLIC compile feature (not just CMAKE_CXX_STANDARD): when another
# build tree imports this target from its export, CMake synthesizes a
# BMI-compiling target on the consumer side and requires the standard
# level to be part of the exported usage requirements.
target_compile_features(${TARGET} PUBLIC cxx_std_26)
endfunction()

# streamr_enable_imports(<target>)
Expand Down
5 changes: 5 additions & 0 deletions packages/streamr-dht/StreamrModules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ function(streamr_add_module_library TARGET)
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/modules
FILES ${ARG_FILES})
set_target_properties(${TARGET} PROPERTIES CXX_SCAN_FOR_MODULES ON)
# PUBLIC compile feature (not just CMAKE_CXX_STANDARD): when another
# build tree imports this target from its export, CMake synthesizes a
# BMI-compiling target on the consumer side and requires the standard
# level to be part of the exported usage requirements.
target_compile_features(${TARGET} PUBLIC cxx_std_26)
endfunction()

# streamr_enable_imports(<target>)
Expand Down
5 changes: 5 additions & 0 deletions packages/streamr-eventemitter/StreamrModules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ function(streamr_add_module_library TARGET)
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/modules
FILES ${ARG_FILES})
set_target_properties(${TARGET} PROPERTIES CXX_SCAN_FOR_MODULES ON)
# PUBLIC compile feature (not just CMAKE_CXX_STANDARD): when another
# build tree imports this target from its export, CMake synthesizes a
# BMI-compiling target on the consumer side and requires the standard
# level to be part of the exported usage requirements.
target_compile_features(${TARGET} PUBLIC cxx_std_26)
endfunction()

# streamr_enable_imports(<target>)
Expand Down
5 changes: 5 additions & 0 deletions packages/streamr-json/StreamrModules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ function(streamr_add_module_library TARGET)
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/modules
FILES ${ARG_FILES})
set_target_properties(${TARGET} PROPERTIES CXX_SCAN_FOR_MODULES ON)
# PUBLIC compile feature (not just CMAKE_CXX_STANDARD): when another
# build tree imports this target from its export, CMake synthesizes a
# BMI-compiling target on the consumer side and requires the standard
# level to be part of the exported usage requirements.
target_compile_features(${TARGET} PUBLIC cxx_std_26)
endfunction()

# streamr_enable_imports(<target>)
Expand Down
5 changes: 5 additions & 0 deletions packages/streamr-libstreamrproxyclient/StreamrModules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ function(streamr_add_module_library TARGET)
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/modules
FILES ${ARG_FILES})
set_target_properties(${TARGET} PROPERTIES CXX_SCAN_FOR_MODULES ON)
# PUBLIC compile feature (not just CMAKE_CXX_STANDARD): when another
# build tree imports this target from its export, CMake synthesizes a
# BMI-compiling target on the consumer side and requires the standard
# level to be part of the exported usage requirements.
target_compile_features(${TARGET} PUBLIC cxx_std_26)
endfunction()

# streamr_enable_imports(<target>)
Expand Down
35 changes: 29 additions & 6 deletions packages/streamr-logger/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,29 @@ find_package(folly CONFIG REQUIRED)
find_package(streamr-json CONFIG REQUIRED)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_library(streamr-logger INTERFACE)

# C++ modules support (guards, policies, helpers) — must come after
# project() because it inspects the compiler id.
include(${CMAKE_CURRENT_SOURCE_DIR}/StreamrModules.cmake)

# Module façade (MODERNIZATION.md Part 2): the package is now a STATIC
# library whose module interface units re-export the public headers.
# #include consumers are unaffected; import consumers get the BMIs.
# detail/ headers stay internal (no partitions).
streamr_add_module_library(streamr-logger
FILES
modules/streamr.logger.cppm
modules/streamr.logger-Logger.cppm
modules/streamr.logger-LoggerImpl.cppm
modules/streamr.logger-SLogger.cppm
modules/streamr.logger-StreamrLogLevel.cppm)
set_property(TARGET streamr-logger PROPERTY CXX_STANDARD 26)

add_library(streamr::streamr-logger ALIAS streamr-logger)

if(NOT IOS)
add_executable(env_category_tests test/unit/LoggerEnvTest.cpp)
streamr_enable_imports(env_category_tests)
target_link_libraries(env_category_tests PRIVATE streamr-logger)
enable_testing()
add_test(
Expand Down Expand Up @@ -60,18 +76,21 @@ if(NOT IOS)
endif()

target_include_directories(
streamr-logger INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
streamr-logger PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)

target_link_libraries(streamr-logger INTERFACE Folly::folly INTERFACE streamr::streamr-json)
# PUBLIC (was INTERFACE): the module interface units compile against folly
# and streamr-json in their global module fragments.
target_link_libraries(streamr-logger PUBLIC Folly::folly PUBLIC streamr::streamr-json)
# TODO: Is there a better way to do this?
# - Apparently not: https://gitlab.kitware.com/cmake/cmake/-/issues/20511
# - At least parse the deps from the vcpkg.json file and use them to find_package() in this file and to add the
# find_package() calls to the wrapper

export(TARGETS streamr-logger
export(TARGETS streamr-logger
NAMESPACE streamr::
FILE streamr-logger-config-in.cmake)
FILE streamr-logger-config-in.cmake
CXX_MODULES_DIRECTORY streamr-logger-modules)

file(WRITE "${CMAKE_BINARY_DIR}/streamr-logger-config.cmake"
"list(APPEND CMAKE_FIND_ROOT_PATH \"${CMAKE_FIND_ROOT_PATH}\")\n"
Expand All @@ -84,8 +103,11 @@ if(NOT IOS)
find_package(GTest CONFIG REQUIRED)

add_executable(streamr-logger-test-unit test/unit/LoggerTest.cpp test/unit/StreamrLogFormatterTest.cpp)
# LoggerTest imports streamr.logger (StreamrLogFormatterTest keeps
# #include — it tests a detail/ header that has no partition).
streamr_enable_imports(streamr-logger-test-unit)

target_link_libraries(streamr-logger-test-unit
target_link_libraries(streamr-logger-test-unit
PRIVATE streamr-logger
PRIVATE GTest::gtest
PRIVATE GTest::gtest_main
Expand All @@ -98,5 +120,6 @@ if(NOT IOS)
endif()

add_executable(streamr-logger-example src/examples/LoggerExample.cpp)
streamr_enable_imports(streamr-logger-example)
target_link_libraries(streamr-logger-example PRIVATE streamr-logger)
endif()
5 changes: 5 additions & 0 deletions packages/streamr-logger/StreamrModules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ function(streamr_add_module_library TARGET)
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/modules
FILES ${ARG_FILES})
set_target_properties(${TARGET} PROPERTIES CXX_SCAN_FOR_MODULES ON)
# PUBLIC compile feature (not just CMAKE_CXX_STANDARD): when another
# build tree imports this target from its export, CMake synthesizes a
# BMI-compiling target on the consumer side and requires the standard
# level to be part of the exported usage requirements.
target_compile_features(${TARGET} PUBLIC cxx_std_26)
endfunction()

# streamr_enable_imports(<target>)
Expand Down
2 changes: 1 addition & 1 deletion packages/streamr-logger/include/streamr-logger/Logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ using streamr::json::toJson;

// const char* (not string_view): passed to getenv(), which needs a
// null-terminated string.
constexpr const char* envLogLevelName = "LOG_LEVEL";
inline constexpr const char* envLogLevelName = "LOG_LEVEL";
class Logger {
private:
std::shared_ptr<LoggerImpl> mLoggerImpl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ using StreamrLogLevel = std::variant<
streamrloglevel::Error,
streamrloglevel::Fatal>;

constexpr StreamrLogLevel systemDefaultLogLevel = streamrloglevel::Info{};
inline constexpr StreamrLogLevel systemDefaultLogLevel =
streamrloglevel::Info{};
template <typename T>
concept StreamrLogLevelConcept = std::is_same_v<T, StreamrLogLevel>;

Expand Down
9 changes: 9 additions & 0 deletions packages/streamr-logger/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ clangd-tidy -p ./build $FILES

echo "Running clang-format --dry-run on $FILES"
../../run-clang-format.py $FILES

# Module interface units: format check only. clangd-tidy is not run on
# .cppm files (headers remain the fully linted source of truth during the
# façade migration; clangd modules support is still experimental).
MODULE_FILES=$(find ./modules -type f -name "*.cppm" 2>/dev/null | xargs echo)
if [ -n "$MODULE_FILES" ]; then
echo "Running clang-format --dry-run on $MODULE_FILES"
../../run-clang-format.py $MODULE_FILES
fi
14 changes: 14 additions & 0 deletions packages/streamr-logger/modules/streamr.logger-Logger.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Façade partition over streamr-logger/Logger.hpp (see the streamr.eventemitter
// partitions for the pattern rationale).
module;

#include "streamr-logger/Logger.hpp"

export module streamr.logger:Logger;

export namespace streamr::logger {

using streamr::logger::envLogLevelName;
using streamr::logger::Logger;

} // namespace streamr::logger
13 changes: 13 additions & 0 deletions packages/streamr-logger/modules/streamr.logger-LoggerImpl.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Façade partition over streamr-logger/LoggerImpl.hpp (see the
// streamr.eventemitter partitions for the pattern rationale).
module;

#include "streamr-logger/LoggerImpl.hpp"

export module streamr.logger:LoggerImpl;

export namespace streamr::logger {

using streamr::logger::LoggerImpl;

} // namespace streamr::logger
13 changes: 13 additions & 0 deletions packages/streamr-logger/modules/streamr.logger-SLogger.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Façade partition over streamr-logger/SLogger.hpp (see the
// streamr.eventemitter partitions for the pattern rationale).
module;

#include "streamr-logger/SLogger.hpp"

export module streamr.logger:SLogger;

export namespace streamr::logger {

using streamr::logger::SLogger;

} // namespace streamr::logger
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Façade partition over streamr-logger/StreamrLogLevel.hpp (see the
// streamr.eventemitter partitions for the pattern rationale).
module;

#include "streamr-logger/StreamrLogLevel.hpp"

export module streamr.logger:StreamrLogLevel;

export namespace streamr::logger::streamrloglevel {

using streamr::logger::streamrloglevel::Debug;
using streamr::logger::streamrloglevel::Error;
using streamr::logger::streamrloglevel::Fatal;
using streamr::logger::streamrloglevel::Info;
using streamr::logger::streamrloglevel::Trace;
using streamr::logger::streamrloglevel::Warn;

} // namespace streamr::logger::streamrloglevel

export namespace streamr::logger {

using streamr::logger::getStreamrLogLevelByName;
using streamr::logger::getStreamrLogLevelValue;
using streamr::logger::LevelGetter;
using streamr::logger::StreamrLogLevel;
using streamr::logger::StreamrLogLevelConcept;
using streamr::logger::systemDefaultLogLevel;

} // namespace streamr::logger
8 changes: 8 additions & 0 deletions packages/streamr-logger/modules/streamr.logger.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Primary module interface unit of streamr.logger. Consumers write
// `import streamr.logger;` and get every partition re-exported.
export module streamr.logger;

export import :Logger;
export import :LoggerImpl;
export import :SLogger;
export import :StreamrLogLevel;
13 changes: 9 additions & 4 deletions packages/streamr-logger/src/examples/LoggerExample.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "streamr-logger/Logger.hpp"
#include "streamr-logger/SLogger.hpp"
#include "streamr-logger/StreamrLogLevel.hpp"
#include <exception>
#include <iostream>
#include <string>

import streamr.logger;

using Logger = streamr::logger::Logger;
using SLogger = streamr::logger::SLogger;
Expand Down Expand Up @@ -69,8 +71,11 @@ class LoggerExample {
}
};

int main() {
int main() try {
LoggerExample loggerExample;
loggerExample.doSomething();
return 0;
} catch (const std::exception& e) {
std::cerr << "Example failed: " << e.what() << '\n';
return 1;
}
12 changes: 9 additions & 3 deletions packages/streamr-logger/test/unit/LoggerEnvTest.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include "streamr-logger/Logger.hpp"
#include "streamr-logger/StreamrLogLevel.hpp"
// Tests detail-level machinery, so the detail header stays #included (no
// module partition exists for detail/ headers); mixing with import is safe.
#include "streamr-logger/detail/FollyLoggerImpl.hpp"

import streamr.logger;

using Logger = streamr::logger::Logger;
namespace streamrloglevel = streamr::logger::streamrloglevel;
using FollyLoggerImpl = streamr::logger::detail::FollyLoggerImpl;
Expand Down Expand Up @@ -56,7 +60,7 @@ class LoggerEnvTest {
}
};

int main(int /* argc */, char* argv[]) {
int main(int /* argc */, char* argv[]) try {
LoggerEnvTest loggerEnvTest;
if (argv[1] == std::string("1")) {
return static_cast<int>(loggerEnvTest.testInfoLogWritten());
Expand All @@ -72,4 +76,6 @@ int main(int /* argc */, char* argv[]) {
loggerEnvTest.testInfoLogWrittenWhenDefaultLogLevelIsWarn());
}
return 1;
} catch (const std::exception&) {
return 1;
}
4 changes: 2 additions & 2 deletions packages/streamr-logger/test/unit/LoggerTest.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#include "streamr-logger/Logger.hpp"
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "streamr-logger/StreamrLogLevel.hpp"
#include "streamr-logger/detail/FollyLoggerImpl.hpp"

import streamr.logger;

using streamr::logger::Logger;
using streamr::logger::StreamrLogLevel;
using FollyLoggerImpl = streamr::logger::detail::FollyLoggerImpl;
Expand Down
Loading
Loading