Skip to content

Commit 2c65b6c

Browse files
Merge branch 'main' into houliston/priority-changes
2 parents afb7483 + 733b870 commit 2c65b6c

9 files changed

Lines changed: 136 additions & 115 deletions

File tree

.github/workflows/sonarcloud.yaml

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,20 @@ jobs:
1919
sonarcloud:
2020
name: SonarCloud
2121
runs-on: ubuntu-24.04
22+
container: silkeh/clang:18
2223
env:
2324
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed
2425
steps:
2526
- uses: actions/checkout@v4
2627
with:
2728
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
2829

29-
- name: Install gcovr
30-
run: pip install gcovr==8.2
31-
32-
- name: Install sonar-scanner and build-wrapper
33-
uses: SonarSource/sonarcloud-github-c-cpp@v3
30+
- name: Install Prerequisites
31+
run: |
32+
apt-get update
33+
apt-get install -y \
34+
ccache \
35+
unzip
3436
3537
- name: Install CMake
3638
uses: lukka/get-cmake@v3.30.5
@@ -53,66 +55,54 @@ jobs:
5355
-GNinja \
5456
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
5557
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
56-
-DCMAKE_BUILD_TYPE=Debug \
58+
-DCMAKE_C_COMPILER=clang \
59+
-DCMAKE_CXX_COMPILER=clang++ \
60+
-DCMAKE_BUILD_TYPE=Release \
5761
-DBUILD_TESTS=ON \
5862
-DCI_BUILD=ON \
5963
-DENABLE_COVERAGE=ON \
6064
-DENABLE_CLANG_TIDY=OFF
6165
62-
- name: Build the code in debug mode
66+
- name: Build the code
6367
timeout-minutes: 30
64-
run: cmake --build build/ --config Debug
68+
run: cmake --build build/ --config Release
6569

6670
- name: Add capabilities for scheduler
6771
run: find build/tests -type f -executable -exec sudo setcap cap_sys_nice=eip {} \; || true
6872

6973
- name: Run tests to generate coverage statistics
7074
timeout-minutes: 10
7175
working-directory: build
72-
run: ninja run_all_tests -j1 -k 0
76+
run: ninja coverage -k 0
7377

7478
- name: Test Summary
7579
if: ${{ !cancelled() }}
7680
uses: test-summary/action@v2
7781
with:
7882
paths: "build/reports/tests/*.junit.xml"
7983

80-
- name: Collect coverage into one XML report
81-
if: ${{ !cancelled() }}
82-
run: |
83-
gcovr \
84-
--root . \
85-
--object-directory build \
86-
--force-color \
87-
--no-markers \
88-
--decisions \
89-
--calls \
90-
--exclude-noncode-lines \
91-
--gcov-ignore-parse-errors negative_hits.warn \
92-
--sonarqube "coverage.xml"
93-
9484
- name: Upload coverage report
9585
if: ${{ !cancelled() }}
9686
uses: actions/upload-artifact@v4
9787
with:
98-
name: coverage-sonar
99-
path: coverage.xml
88+
name: coverage
89+
path: build/reports/coverage.txt
10090
retention-days: 1 # This sets the artifact TTL to 1 day (minimum is 1 day)
10191
overwrite: true
10292

103-
- name: Run sonar-scanner
93+
- name: SonarQube Scan
94+
uses: SonarSource/sonarqube-scan-action@v5
10495
if: ${{ !cancelled() }}
10596
env:
106-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
10797
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
108-
run: |
109-
sonar-scanner \
110-
--define sonar.projectKey=Fastcode_NUClear \
111-
--define sonar.organization=fastcode \
112-
--define sonar.sources=src \
113-
--define sonar.tests=tests \
114-
--define sonar.cfamily.compile-commands=build/compile_commands.json \
115-
--define sonar.coverageReportPaths=coverage.xml
98+
with:
99+
args: >
100+
--define sonar.projectKey=Fastcode_NUClear
101+
--define sonar.organization=fastcode
102+
--define sonar.sources=src
103+
--define sonar.tests=tests
104+
--define sonar.cfamily.compile-commands=build/compile_commands.json
105+
--define sonar.cfamily.llvm-cov.reportPath=build/reports/coverage.txt
116106
117107
- name: Upload Traces
118108
if: ${{ !cancelled() }}

cmake/TestRunner.cmake

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,68 @@ get_all_catch_test_targets(all_targets ${PROJECT_SOURCE_DIR})
3636

3737
# Create a custom command for each test target to run it
3838
# Make sure that coverage data is written with paths relative to the source directory
39-
unset(reports)
39+
unset(report_outputs)
40+
unset(coverage_reports)
4041
foreach(target ${all_targets})
4142

42-
set(sonarqube_report_file "${PROJECT_BINARY_DIR}/reports/tests/${target}.sonarqube.xml")
43+
unset(command_prefix)
44+
if(ENABLE_COVERAGE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
45+
# Extra output files for the profile data
46+
set(raw_coverage "${PROJECT_BINARY_DIR}/reports/tests/${target}.profraw")
47+
set(indexed_coverage "${PROJECT_BINARY_DIR}/reports/tests/${target}.profdata")
48+
set(coverage_report "${PROJECT_BINARY_DIR}/reports/tests/${target}.txt")
49+
set(command_prefix "cmake" "-E" "env" "LLVM_PROFILE_FILE=${raw_coverage}")
50+
list(APPEND coverage_reports ${coverage_report})
51+
endif()
52+
4353
set(junit_report_file "${PROJECT_BINARY_DIR}/reports/tests/${target}.junit.xml")
44-
list(APPEND reports ${sonarqube_report_file} ${junit_report_file})
54+
list(APPEND report_outputs ${junit_report_file})
4555
add_custom_command(
46-
OUTPUT ${sonarqube_report_file} ${junit_report_file}
47-
COMMAND $<TARGET_FILE:${target}> --reporter console --reporter SonarQube::out=${sonarqube_report_file} --reporter
48-
JUnit::out=${junit_report_file}
56+
OUTPUT ${junit_report_file} ${raw_coverage}
57+
COMMAND ${command_prefix} $<TARGET_FILE:${target}> --reporter console --reporter JUnit::out=${junit_report_file}
4958
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
59+
DEPENDS ${target}
5060
USES_TERMINAL
5161
COMMENT "Running test ${target}"
5262
)
63+
64+
if(ENABLE_COVERAGE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
65+
add_custom_command(
66+
OUTPUT ${indexed_coverage}
67+
COMMAND llvm-profdata merge -sparse ${raw_coverage} -o ${indexed_coverage}
68+
DEPENDS ${raw_coverage}
69+
)
70+
71+
add_custom_command(
72+
OUTPUT ${coverage_report}
73+
COMMAND llvm-cov show --show-branches=count -Xdemangler c++filt -instr-profile=${indexed_coverage}
74+
$<TARGET_FILE:${target}> > ${coverage_report}
75+
DEPENDS ${indexed_coverage}
76+
)
77+
endif()
78+
5379
endforeach()
5480

5581
# Create a custom target that depends on all test targets
5682
add_custom_target(
5783
run_all_tests
58-
DEPENDS ${reports}
84+
DEPENDS ${report_outputs} ${all_targets}
5985
COMMENT "Running all Catch2 tests"
6086
)
87+
88+
# Concatenate all the coverage reports together
89+
if(ENABLE_COVERAGE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
90+
91+
add_custom_command(
92+
OUTPUT ${PROJECT_BINARY_DIR}/reports/coverage.txt
93+
COMMAND cat ${coverage_reports} > ${PROJECT_BINARY_DIR}/reports/coverage.txt
94+
DEPENDS ${coverage_reports}
95+
)
96+
97+
add_custom_target(
98+
coverage
99+
DEPENDS ${PROJECT_BINARY_DIR}/reports/coverage.txt
100+
COMMENT "Running all Catch2 tests with coverage"
101+
)
102+
103+
endif()

src/CMakeLists.txt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,18 @@ target_compile_features(nuclear PUBLIC cxx_std_14)
3939

4040
option(ENABLE_COVERAGE "Compile with coverage support enabled.")
4141
if(ENABLE_COVERAGE)
42-
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
43-
message(WARNING "Coverage is enabled but not build in debug mode. Coverage results may be misleading.")
42+
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
43+
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
44+
message(WARNING "Coverage is enabled but not build in debug mode. Coverage results may be misleading.")
45+
endif()
46+
target_compile_options(nuclear PUBLIC -fprofile-arcs -ftest-coverage -fprofile-abs-path -fprofile-update=atomic)
47+
target_link_options(nuclear PUBLIC -fprofile-arcs)
48+
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
49+
target_compile_options(nuclear PUBLIC -fprofile-instr-generate -fcoverage-mapping)
50+
target_link_options(nuclear PUBLIC -fprofile-instr-generate)
51+
else()
52+
message(FATAL_ERROR "Coverage is not supported for the current compiler.")
4453
endif()
45-
target_compile_options(nuclear PUBLIC -fprofile-arcs -ftest-coverage -fprofile-abs-path -fprofile-update=atomic)
46-
target_link_options(nuclear PUBLIC -fprofile-arcs)
4754
endif(ENABLE_COVERAGE)
4855

4956
# Enable warnings, and all warnings are errors

src/dsl/word/Network.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ namespace dsl {
9191
template <typename DSL>
9292
static std::tuple<std::shared_ptr<NetworkSource>, NetworkData<T>> get(threading::ReactionTask& /*task*/) {
9393

94-
auto* data = store::ThreadStore<std::vector<uint8_t>>::value;
95-
auto* source = store::ThreadStore<NetworkSource>::value;
94+
const auto* data = store::ThreadStore<const std::vector<uint8_t>>::value;
95+
const auto* source = store::ThreadStore<const NetworkSource>::value;
9696

9797
if (data && source) {
9898

src/dsl/word/TCP.hpp

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,10 @@ namespace dsl {
6666

6767
struct Connection {
6868

69-
struct Target {
70-
/// The address of the connection
71-
std::string address;
72-
/// The port of the connection
73-
uint16_t port;
74-
};
75-
7669
/// The local address of the connection
77-
Target local;
70+
util::network::sock_t local;
7871
/// The remote address of the connection
79-
Target remote;
72+
util::network::sock_t remote;
8073

8174
/// The file descriptor for the connection
8275
fd_t fd;
@@ -187,10 +180,7 @@ namespace dsl {
187180
return Connection{};
188181
}
189182

190-
auto local_s = local.address();
191-
auto remote_s = remote.address();
192-
193-
return Connection{{local_s.first, local_s.second}, {remote_s.first, remote_s.second}, fd.release()};
183+
return Connection{local, remote, fd.release()};
194184
}
195185
};
196186

src/dsl/word/UDP.hpp

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,10 @@ namespace dsl {
102102
/// If the packet is valid (it contains data)
103103
bool valid{false};
104104

105-
struct Target {
106-
Target() = default;
107-
Target(std::string address, const uint16_t& port) : address(std::move(address)), port(port) {}
108-
109-
/// The address of the target
110-
std::string address;
111-
/// The port of the target
112-
uint16_t port{0};
113-
};
114-
115105
/// The information about this packets destination
116-
Target local;
106+
util::network::sock_t local{};
117107
/// The information about this packets source
118-
Target remote;
108+
util::network::sock_t remote{};
119109

120110
/// The data to be sent in the packet
121111
std::vector<uint8_t> payload;
@@ -427,12 +417,10 @@ namespace dsl {
427417
RecvResult result = read<DSL>(task);
428418

429419
Packet p{};
430-
p.valid = result.valid;
431-
p.payload = std::move(result.payload);
432-
auto local_s = result.local.address();
433-
auto remote_s = result.remote.address();
434-
p.local = Packet::Target{local_s.first, local_s.second};
435-
p.remote = Packet::Target{remote_s.first, remote_s.second};
420+
p.valid = result.valid;
421+
p.payload = std::move(result.payload);
422+
p.local = result.local;
423+
p.remote = result.remote;
436424

437425
// Confirm that this packet was sent to one of our local addresses
438426
for (const auto& iface : util::network::get_interfaces()) {
@@ -475,12 +463,10 @@ namespace dsl {
475463
if (result.local.sock.sa_family == AF_INET) {
476464

477465
Packet p{};
478-
p.valid = result.valid;
479-
p.payload = std::move(result.payload);
480-
auto local_s = result.local.address();
481-
auto remote_s = result.remote.address();
482-
p.local = Packet::Target{local_s.first, local_s.second};
483-
p.remote = Packet::Target{remote_s.first, remote_s.second};
466+
p.valid = result.valid;
467+
p.payload = std::move(result.payload);
468+
p.local = result.local;
469+
p.remote = result.remote;
484470

485471
// 255.255.255.255 is always a valid broadcast address
486472
if (result.local.ipv4.sin_addr.s_addr == htonl(INADDR_BROADCAST)) {
@@ -526,12 +512,10 @@ namespace dsl {
526512
// Only return multicast packets
527513
if (multicast) {
528514
Packet p{};
529-
p.valid = result.valid;
530-
p.payload = std::move(result.payload);
531-
auto local_s = result.local.address();
532-
auto remote_s = result.remote.address();
533-
p.local = Packet::Target{local_s.first, local_s.second};
534-
p.remote = Packet::Target{remote_s.first, remote_s.second};
515+
p.valid = result.valid;
516+
p.payload = std::move(result.payload);
517+
p.local = result.local;
518+
p.remote = result.remote;
535519
return p;
536520
}
537521

src/extension/NetworkController.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,10 @@ namespace extension {
5757
const bool& reliable,
5858
std::vector<uint8_t>&& payload) {
5959
// Construct our NetworkSource information
60-
dsl::word::NetworkSource src{remote.name, remote.target, reliable};
60+
const dsl::word::NetworkSource src{remote.name, remote.target, reliable};
6161

6262
// Move the payload in as we are stealing it
63-
std::vector<uint8_t> p(std::move(payload));
64-
65-
// Store in our thread local cache
66-
dsl::store::ThreadStore<std::vector<uint8_t>>::value = &p;
67-
dsl::store::ThreadStore<dsl::word::NetworkSource>::value = &src;
63+
const std::vector<uint8_t> p(std::move(payload));
6864

6965
/* Mutex Scope */ {
7066
// Lock our reaction mutex
@@ -75,13 +71,17 @@ namespace extension {
7571

7672
// Execute on our interested reactions
7773
for (auto it = rs.first; it != rs.second; ++it) {
74+
// Store in our thread local cache
75+
dsl::store::ThreadStore<const std::vector<uint8_t>>::value = &p;
76+
dsl::store::ThreadStore<const dsl::word::NetworkSource>::value = &src;
77+
7878
powerplant.submit(it->second->get_task());
7979
}
80-
}
8180

82-
// Clear our cache
83-
dsl::store::ThreadStore<std::vector<uint8_t>>::value = nullptr;
84-
dsl::store::ThreadStore<dsl::word::NetworkSource>::value = nullptr;
81+
// Clear our cache
82+
dsl::store::ThreadStore<const std::vector<uint8_t>>::value = nullptr;
83+
dsl::store::ThreadStore<const dsl::word::NetworkSource>::value = nullptr;
84+
}
8585
});
8686

8787
// Set our join callback

0 commit comments

Comments
 (0)