Skip to content

Commit 9e4ba86

Browse files
authored
ci(boost): per-job Conan home + committed profile + Boost sentinel on Linux lane (#700)
Root-fix (not a downgrade) for the recurring Boost 1.90.0 / GCC-13 CI flakes, which were the mechanical signature of a torn/interleaved header set from a shared-cache race, not a real 1.90.0<->GCC-13 incompatibility. - ci/conan/linux-gcc13.profile: committed, exact-pinned settings (cppstd=20 strict, libcxx=libstdc++11) so the lane always resolves the same package_id and cannot fall back to a wrong-config prebuilt. - build.yml linux lane: private per-job CONAN_HOME (runner.temp) so concurrent jobs build Boost into isolated caches and can never tear each other's ~/.conan2 (the actual race fix); install resolves via the committed profile instead of detect+sed drift. - ci/sentinel/boost_sentinel.cpp: one TU that static_asserts BOOST_VERSION==1.90.0 (catches apt-Boost fallback) and exercises the four failing paths (asio cancel_after, severity_logger, signals2 track_foreign, spirit.x3 sequence); gated right after configure. Also runs the ci-boost-stream.md A.5 falsification test: a green sentinel from a pristine per-job home confirms version-agnostic; if spirit.x3 still fires there, that is the only genuine-regression signal. Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
1 parent 7a7f326 commit 9e4ba86

5 files changed

Lines changed: 112 additions & 7 deletions

File tree

.github/workflows/build.yml

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ jobs:
4040
linux:
4141
name: Linux x86_64
4242
runs-on: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && fromJSON('["self-hosted","Linux","X64","c2pool-build"]') || 'ubuntu-24.04' }}
43+
# Private per-job Conan home: each job builds Boost into an ISOLATED cache,
44+
# so concurrent jobs can never tear each other's ~/.conan2 (root cause of the
45+
# 1.90.0 torn-header flakes). See ci-boost-stream.md.
46+
env:
47+
CONAN_HOME: ${{ runner.temp }}/conan2
4348
steps:
4449
- uses: actions/checkout@v6
4550

@@ -64,16 +69,14 @@ jobs:
6469
conan --version # pre-provisioned at /usr/local/bin on self-hosted
6570
fi
6671
67-
- name: Detect Conan profile
68-
run: |
69-
conan profile detect --force
70-
sed -i 's/compiler.cppstd=.*/compiler.cppstd=20/' "$(conan profile path default)"
72+
- name: Show committed Conan profile
73+
run: conan profile show -pr:a=ci/conan/linux-gcc13.profile
7174

7275
- name: Restore Conan cache
7376
uses: actions/cache@v5
7477
with:
75-
path: ~/.conan2
76-
key: conan2-ubuntu24-gcc13-${{ hashFiles('conanfile.txt') }}
78+
path: ${{ runner.temp }}/conan2
79+
key: conan2-ubuntu24-gcc13-${{ hashFiles('conanfile.txt', 'ci/conan/linux-gcc13.profile') }}
7780

7881
# Self-hosted runners reuse the workspace across runs; a stale build_ci/
7982
# leaves gtest_discover_tests POST_BUILD artifacts that ctest then
@@ -85,11 +88,18 @@ jobs:
8588
run: rm -rf build_ci
8689

8790
- name: Conan install
88-
run: conan install . --build=missing --output-folder=build_ci --settings=build_type=Release
91+
run: conan install . -pr:a=ci/conan/linux-gcc13.profile --build=missing --output-folder=build_ci
8992

9093
- name: Configure
9194
run: cmake -S . -B build_ci -DCMAKE_TOOLCHAIN_FILE=build_ci/conan_toolchain.cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Release -DCOIN_DGB=ON
9295

96+
# Boost header-integrity + version gate: a torn/mixed Boost tree or apt-Boost
97+
# fallback fails HERE (fast, one TU), not deep in the coin build.
98+
- name: Boost sentinel
99+
run: |
100+
cmake --build build_ci --target boost_sentinel -j8
101+
ctest --test-dir build_ci -R '^boost_sentinel$' --output-on-failure
102+
93103
- name: Build
94104
run: cmake --build build_ci --target c2pool -j8
95105

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,9 @@ endif()
9999

100100
add_subdirectory(src)
101101
add_subdirectory(test)
102+
# Boost header-integrity + version sentinel (CI compile-gate; testing-only).
103+
if(GTest_FOUND OR GTEST_FOUND)
104+
add_subdirectory(ci/sentinel)
105+
endif()
102106
# add_subdirectory(benchmark)
103107
# add_subdirectory(temp)

ci/conan/linux-gcc13.profile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Committed Conan profile — the Linux/GCC-13 lane that must hold.
2+
# Pins EXACT settings so every lane resolves the SAME package_id and can never
3+
# silently fall back to a wrong-config prebuilt (root-cause of the Boost 1.90.0
4+
# torn-header flakes). See ci-boost-stream.md §B.
5+
[settings]
6+
os=Linux
7+
arch=x86_64
8+
build_type=Release
9+
compiler=gcc
10+
compiler.version=13
11+
compiler.cppstd=20
12+
compiler.libcxx=libstdc++11
13+
14+
[buildenv]
15+
CC=gcc-13
16+
CXX=g++-13

ci/sentinel/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Boost header-integrity + version sentinel. Gated into every CI lane right
2+
# after conan install + configure, before the main build: a torn/mixed Boost
3+
# tree or an apt-Boost fallback fails HERE (fast, one TU) instead of deep in
4+
# the coin build. Boost::log / Boost::log_setup are found at the root scope.
5+
add_executable(boost_sentinel boost_sentinel.cpp)
6+
target_compile_features(boost_sentinel PRIVATE cxx_std_20)
7+
target_link_libraries(boost_sentinel PRIVATE Boost::log Boost::log_setup)
8+
add_test(NAME boost_sentinel COMMAND boost_sentinel)

ci/sentinel/boost_sentinel.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Boost header-integrity + version gate.
2+
//
3+
// Compiles ONE TU that exercises the four exact subsystems whose torn-header
4+
// signatures surfaced the Boost 1.90.0 / GCC-13 CI flakes, and static_asserts
5+
// the resolved Boost is 1.90.0 (catches a stray apt Boost 1.83 filling holes
6+
// via include-path fallback). Green here == intact single-version tree, right
7+
// version resolved, compiled archives link. See ci-boost-stream.md ss.D.
8+
#include <boost/version.hpp>
9+
static_assert(BOOST_VERSION == 109000, "wrong Boost tree resolved: expected 1.90.0");
10+
11+
#include <boost/asio/cancel_after.hpp> // pulls detail/timed_cancel_op.hpp (stage-1 casualty)
12+
#include <boost/asio/io_context.hpp>
13+
#include <boost/asio/steady_timer.hpp>
14+
15+
#include <boost/log/sources/record_ostream.hpp>
16+
#include <boost/log/sources/severity_logger.hpp> // A.1 site (BOOST_PP expansion)
17+
#include <boost/log/trivial.hpp>
18+
#include <boost/log/utility/setup/console.hpp>
19+
20+
#include <boost/signals2/signal.hpp> // A.2 site (slot_template.hpp)
21+
22+
#include <boost/fusion/adapted/std_pair.hpp>
23+
#include <boost/spirit/home/x3.hpp> // A.3 site (operator/detail/sequence.hpp)
24+
25+
#include <chrono>
26+
#include <cstdio>
27+
#include <memory>
28+
#include <string>
29+
#include <utility>
30+
31+
int main()
32+
{
33+
// asio: timed cancellation machinery
34+
boost::asio::io_context io;
35+
boost::asio::steady_timer timer(io, std::chrono::milliseconds(100));
36+
bool aborted = false;
37+
timer.async_wait(boost::asio::cancel_after(
38+
std::chrono::milliseconds(10),
39+
[&](boost::system::error_code ec) { aborted = (ec == boost::asio::error::operation_aborted); }));
40+
io.run();
41+
if (!aborted) { std::puts("FAIL asio"); return 1; }
42+
43+
// log: severity_logger (header) + libboost_log / log_setup (link check)
44+
boost::log::add_console_log();
45+
boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
46+
BOOST_LOG_SEV(lg, boost::log::trivial::info) << "sentinel: log ok";
47+
48+
// signals2: std::shared_ptr tracking -> the exact foreign_void_weak_ptr push_back path
49+
using sig_t = boost::signals2::signal<int(int)>;
50+
sig_t sig;
51+
auto tracked = std::make_shared<int>(1);
52+
sig.connect(sig_t::slot_type([&](int x) { return x + *tracked; }).track_foreign(tracked));
53+
if (sig(41).value_or(0) != 42) { std::puts("FAIL signals2"); return 1; }
54+
55+
// spirit.x3: sequence operator -> partition_attribute static_asserts
56+
namespace x3 = boost::spirit::x3;
57+
std::pair<int, int> out{};
58+
std::string const in = "12,34";
59+
auto it = in.begin();
60+
if (!x3::parse(it, in.end(), x3::int_ >> ',' >> x3::int_, out)
61+
|| it != in.end() || out.first != 12 || out.second != 34) {
62+
std::puts("FAIL spirit.x3"); return 1;
63+
}
64+
65+
std::puts("boost_sentinel: asio+log+signals2+spirit.x3 OK");
66+
return 0;
67+
}

0 commit comments

Comments
 (0)