diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8c6af125..2f520935 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -10,6 +10,9 @@ on: - "include/**" - "src/**" - "test/**" + - "cmake/**" + - "Makefile" + - "CMakePresets.json" - "CMakeLists.txt" - ".github/workflows/linux.yml" pull_request: @@ -18,6 +21,9 @@ on: - "include/**" - "src/**" - "test/**" + - "cmake/**" + - "Makefile" + - "CMakePresets.json" - "CMakeLists.txt" - ".github/workflows/linux.yml" @@ -28,8 +34,8 @@ jobs: fail-fast: false matrix: - # TODO: sanitizer: [debug, release, asan, usan, tsan] - sanitizer: [debug, release] + # TODO: sanitizer: [debug, release, asan, usan, tsan, lsan, msan] + preset: [debug, release] compiler: [g++-14, clang++-19] steps: @@ -42,5 +48,8 @@ jobs: chmod +x llvm.sh sudo ./llvm.sh 19 all - - name: Linux ${{ matrix.compiler }} ${{ matrix.sanitizer }} - run: CXX=${{ matrix.compiler }} make ${{ matrix.sanitizer }} + - name: Linux ${{ matrix.compiler }} ${{ matrix.preset }} + run: CXX=${{ matrix.compiler }} cmake --workflow --preset ${{ matrix.preset }} + + - name: Linux ${{ matrix.compiler }} sanitizer + run: CXX=${{ matrix.compiler }} make all diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 5d0ed779..6df6ae70 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -10,6 +10,9 @@ on: - "include/**" - "src/**" - "test/**" + - "cmake/**" + - "Makefile" + - "CMakePresets.json" - "CMakeLists.txt" - ".github/workflows/macos.yml" pull_request: @@ -18,6 +21,9 @@ on: - "include/**" - "src/**" - "test/**" + - "cmake/**" + - "Makefile" + - "CMakePresets.json" - "CMakeLists.txt" - ".github/workflows/macos.yml" @@ -28,7 +34,7 @@ jobs: fail-fast: false matrix: - sanitizer: [debug, release] + preset: [debug, release] # TODO: compiler: [g++, clang++-19] compiler: [g++, clang++-18] @@ -49,10 +55,19 @@ jobs: run: | brew install llvm@19 || echo ignored - - name: macos clang++-18 ${{ matrix.sanitizer }} + - name: macos clang++-18 ${{ matrix.preset }} if: startsWith(matrix.compiler, 'clang') - run: CXX=$(brew --prefix llvm@18)/bin/clang++ make ${{ matrix.sanitizer }} + run: CXX=$(brew --prefix llvm@18)/bin/clang++ cmake --workflow --preset ${{ matrix.preset }} - - name: macos g++ ${{ matrix.sanitizer }} + - name: macos clang++-18 sanitizer + if: startsWith(matrix.compiler, 'clang') + run: CXX=$(brew --prefix llvm@18)/bin/clang++ make all + + - name: macos g++ ${{ matrix.preset }} if: startsWith(matrix.compiler, 'g++') - run: CXX=${{ matrix.compiler }} make ${{ matrix.sanitizer }} + run: CXX=${{ matrix.compiler }} cmake --workflow --preset ${{ matrix.preset }} + + # TODO: fails with AppleClang 16.0.0 on CI! + # - name: macos g++ sanitizer + # if: startsWith(matrix.compiler, 'g++') + # run: CXX=${{ matrix.compiler }} make all diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3e003a0a..8205efdb 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -10,6 +10,8 @@ on: - "include/**" - "src/**" - "test/**" + - "cmake/**" + - "CMakePresets.json" - "CMakeLists.txt" - ".github/workflows/windows.yml" pull_request: @@ -18,6 +20,8 @@ on: - "include/**" - "src/**" - "test/**" + - "cmake/**" + - "CMakePresets.json" - "CMakeLists.txt" - ".github/workflows/windows.yml" diff --git a/Makefile b/Makefile index 37c70806..fab7c375 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,30 @@ # Makefile # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -SANITIZERS = release debug msan asan usan tsan +MAKEFLAGS+= --no-builtin-rules # Disable the built-in implicit rules. +MAKEFLAGS+= --warn-undefined-variables # Warn when an undefined variable is referenced. + +SANITIZERS = release debug usan # TODO: lsan +OS := $(shell /usr/bin/uname) +ifeq ($(OS),Darwin) + SANITIZERS += tsan +endif +ifeq ($(OS),Linux) + SANITIZERS += asan # TODO: msan +endif .PHONY: default doc run update check ce todo distclean clean codespell clang-tidy build test all format $(SANITIZERS) -COMPILER=system +SYSROOT ?= +TOOLCHAIN ?= + +COMPILER=c++ CXX_BASE=$(CXX:$(dir $(CXX))%=%) ifeq ($(CXX_BASE),g++) - COMPILER=gcc + COMPILER=g++ endif ifeq ($(CXX_BASE),clang++) - COMPILER=clang + COMPILER=clang++ endif CXX_FLAGS = -g @@ -20,11 +33,10 @@ SOURCEDIR = $(CURDIR) BUILDROOT = build BUILD = $(BUILDROOT)/$(SANITIZER) EXAMPLE = beman.execution26.examples.stop_token -CMAKE_C_COMPILER=$(COMPILER) CMAKE_CXX_COMPILER=$(COMPILER) ifeq ($(SANITIZER),release) - CXX_FLAGS = -O3 -pedantic -Wall -Wextra -Werror + CXX_FLAGS = -O3 -Wpedantic -Wall -Wextra -Wshadow # TODO: -Werror endif ifeq ($(SANITIZER),debug) CXX_FLAGS = -g @@ -62,11 +74,12 @@ $(SANITIZERS): build: @mkdir -p $(BUILD) - cd $(BUILD); CC=$(CXX) cmake $(SOURCEDIR) $(TOOLCHAIN) $(SYSROOT) -DCMAKE_CXX_COMPILER=$(CXX) -DCMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)" + cd $(BUILD); CC=$(CXX) cmake -G Ninja $(SOURCEDIR) $(TOOLCHAIN) $(SYSROOT) -DCMAKE_CXX_COMPILER=$(CXX) -DCMAKE_CXX_FLAGS="$(CXX_FLAGS) $(SAN_FLAGS)" cmake --build $(BUILD) -test: - cmake --workflow --preset $(SANITIZER) +test: build + # cmake --workflow --preset $(SANITIZER) + ctest --test-dir $(BUILD) --rerun-failed --output-on-failure ce: @mkdir -p $(BUILD) @@ -83,8 +96,9 @@ check: < $$h sed -n "/^ *# *include .*@$$from \1@p"; \ done | tsort > /dev/null +build/$(SANITIZER)/compile_commands.json: $(SANITIZER) clang-tidy: build/$(SANITIZER)/compile_commands.json - run-clang-tidy -p build/$(SANITIZER) tests + run-clang-tidy -p build/$(SANITIZER) tests examples codespell: codespell -L statics,snd,copyable,cancelled @@ -105,7 +119,7 @@ clean-doc: $(RM) -r docs/html docs/latex clean: clean-doc - $(RM) -r $(BUILD) + $(RM) -r $(BUILD) $(RM) mkerr olderr *~ distclean: clean diff --git a/examples/allocator.cpp b/examples/allocator.cpp index 27ccb852..483521c9 100644 --- a/examples/allocator.cpp +++ b/examples/allocator.cpp @@ -12,9 +12,9 @@ namespace { template struct inline_resource : std::pmr::memory_resource { const char* name; - inline_resource(const char* name) : name(name) {} - std::byte buffer[Size]; - std::byte* next{+this->buffer}; + explicit inline_resource(const char* name) : name(name) {} + std::byte buffer[Size]{}; // NOLINT(hicpp-avoid-c-arrays) + std::byte* next{+this->buffer}; // NOLINT(hicpp-no-array-decay) void* do_allocate(std::size_t size, std::size_t) override { std::cout << "allocating from=" << this->name << ", size=" << size << "\n"; @@ -29,6 +29,7 @@ struct inline_resource : std::pmr::memory_resource { bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; } }; +// NOLINTBEGIN(hicpp-special-member-functions) template struct allocator_aware_fun { using allocator_type = std::pmr::polymorphic_allocator<>; @@ -38,10 +39,11 @@ struct allocator_aware_fun { template requires std::same_as, std::remove_cvref_t> - allocator_aware_fun(F&& fun) : fun(std::forward(fun)) {} + explicit allocator_aware_fun(F&& fun) : fun(std::forward(fun)) {} allocator_aware_fun(const allocator_aware_fun& other, allocator_type allocator = {}) : fun(other.fun), allocator(allocator) {} - allocator_aware_fun(allocator_aware_fun&& other) : fun(std::move(other.fun)), allocator(other.allocator) {} + allocator_aware_fun(allocator_aware_fun&& other) noexcept + : fun(std::move(other.fun)), allocator(other.allocator) {} allocator_aware_fun(allocator_aware_fun&& other, allocator_type allocator) : fun(std::move(other.fun)), allocator(allocator) {} @@ -50,6 +52,7 @@ struct allocator_aware_fun { return this->fun(this->allocator, std::forward(args)...); } }; +// NOLINTEND(hicpp-special-member-functions) template allocator_aware_fun(Fun&& fun) -> allocator_aware_fun; @@ -60,7 +63,7 @@ struct allocator_env { } // namespace auto main() -> int { - int values[] = {1, 2, 3}; + int values[] = {1, 2, 3}; // NOLINT(hicpp-avoid-c-arrays) auto s{ex::just(std::span(values)) | ex::let_value(allocator_aware_fun([](auto alloc, std::span v) { return ex::just(std::pmr::vector(v.begin(), v.end(), alloc)); })) | diff --git a/examples/playground.cpp b/examples/playground.cpp index c1ccf5b0..eb8a343b 100644 --- a/examples/playground.cpp +++ b/examples/playground.cpp @@ -12,11 +12,8 @@ namespace ex = ::beman::execution26; int main() { - auto[result] = ex::sync_wait( - ex::when_all( - ex::just(std::string("hello, ")), - ex::just(std::string("world")) - ) | ex::then([](auto s1, auto s2){ return s1 + s2; }) - ).value_or(std::tuple(std::string("oops"))); + auto [result] = ex::sync_wait(ex::when_all(ex::just(std::string("hello, ")), ex::just(std::string("world"))) | + ex::then([](const auto& s1, const auto& s2) { return s1 + s2; })) + .value_or(std::tuple(std::string("oops"))); std::cout << "result='" << result << "'\n"; } \ No newline at end of file diff --git a/examples/sender-demo.cpp b/examples/sender-demo.cpp index 4d8b0e4b..a2894aa0 100644 --- a/examples/sender-demo.cpp +++ b/examples/sender-demo.cpp @@ -47,7 +47,7 @@ static_assert(ex::sender_in>); int main() { auto j = just_sender{std::pmr::string("value")}; - auto t = std::move(j) | ex::then([](std::pmr::string v) { return v + " then"; }); + auto t = std::move(j) | ex::then([](const std::pmr::string& v) { return v + " then"; }); auto w = ex::when_all(std::move(t)); auto e = ex::detail::write_env(std::move(w), ex::detail::make_env(ex::get_allocator, std::pmr::polymorphic_allocator<>())); diff --git a/examples/stop_token.cpp b/examples/stop_token.cpp index 1a1cf73d..aa74aa41 100644 --- a/examples/stop_token.cpp +++ b/examples/stop_token.cpp @@ -45,14 +45,15 @@ namespace exec = beman::execution26; // doesn't seem to be readily enabled on MacOS. // - std::print isn't available everywhere, yet. Let's try a simple // placeholder. -static ::std::mutex io_lock; +namespace { +::std::mutex io_lock; void print(std::string_view text, auto&&...) { - std::lock_guard guard(io_lock); + const std::lock_guard guard(io_lock); ::std::cout << text; } template -auto active(Token token) -> void { +auto active(const Token& token) -> void { auto i{0ull}; while (not token.stop_requested()) { // do work @@ -69,9 +70,9 @@ struct stop_callback_for_t { #ifdef __cpp_lib_latch template -auto inactive(Token token) -> void { +auto inactive(const Token& token) -> void { ::std::latch latch(1); - stop_callback_for_t cb(token, [&latch] { latch.count_down(); }); + const stop_callback_for_t cb(token, [&latch] { latch.count_down(); }); latch.wait(); print("inactive thread done (latch)\n"); @@ -88,6 +89,7 @@ auto inactive(Token token) -> void { print("inactive thread done (condition_variable)\n"); } #endif +} // namespace auto main() -> int { exec::stop_source source; diff --git a/examples/stopping.cpp b/examples/stopping.cpp index 39699543..d82151f5 100644 --- a/examples/stopping.cpp +++ b/examples/stopping.cpp @@ -18,10 +18,11 @@ namespace ex = beman::execution26; // ---------------------------------------------------------------------------- +namespace { struct env { ex::inplace_stop_token token; - env(ex::inplace_stop_token token) : token(token) {} + env(ex::inplace_stop_token token) : token(token) {} // NOLINT(hicpp-explicit-conversions) auto query(const ex::get_stop_token_t&) const noexcept { return this->token; } }; @@ -73,6 +74,7 @@ struct receiver { auto set_error(auto&&) noexcept -> void {} auto set_stopped() noexcept -> void {} }; +} // namespace int main() { ex::inplace_stop_source source; diff --git a/examples/when_all-cancel.cpp b/examples/when_all-cancel.cpp index abfd8546..0fcb1105 100644 --- a/examples/when_all-cancel.cpp +++ b/examples/when_all-cancel.cpp @@ -109,6 +109,7 @@ struct eager { state(R&& r, S&& s) : outer_receiver(std::forward(r)), inner_state() { inner_state.emplace(std::forward(s), receiver{this}); } + // TODO on next line: bugprone-unchecked-optional-access auto start() & noexcept -> void { ex::start((*this->inner_state).st); } }; template @@ -127,7 +128,6 @@ auto main() -> int { ex::inplace_stop_source source{}; auto op{ex::connect(s, receiver{&source})}; - (void)op; std::cout << "start\n"; ex::start(op); std::cout << "started\n";