summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMoritz Haase <Moritz.Haase@bmw.de>2025-06-17 11:24:56 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2025-06-19 21:54:43 +0100
commitf6fb4c7273f767145109d3b065e6a9f55a397b8a (patch)
tree5ece8dbe780d15d5dd5ed5fe019d8e6d26d08df1
parenta2dad2ce9a9a673991e75185e76b675bfe21905c (diff)
downloadpoky-f6fb4c7273f767145109d3b065e6a9f55a397b8a.tar.gz
cmake: Correctly handle cost data of tests with arbitrary chars in name
ctest automatically optimizes the order of (parallel) test execution based on historic test case runtime via the COST property (see [0]), which can have a significant impact on overall test run times. Sadly this feature is broken in CMake < 4.0.0 for test cases that have spaces in their name (see [1]). This commit backports the upstream fix. As repeated test runs are expected to mainly take place inside the SDK, the patch is only applied to 'nativesdk' builds. [0]: https://cmake.org/cmake/help/latest/prop_test/COST.html [1]: https://gitlab.kitware.com/cmake/cmake/-/issues/26594 Reported-By: John Drouhard <john@drouhard.dev> (From OE-Core rev: dcbaf42dd74cc0bda7254856589613718ed3f057) Signed-off-by: Moritz Haase <Moritz.Haase@bmw.de> Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/recipes-devtools/cmake/cmake-native_3.31.6.bb2
-rw-r--r--meta/recipes-devtools/cmake/cmake/0001-ctest-Allow-arbitrary-characters-in-test-names-of-CT.patch202
-rw-r--r--meta/recipes-devtools/cmake/cmake_3.31.6.bb1
3 files changed, 204 insertions, 1 deletions
diff --git a/meta/recipes-devtools/cmake/cmake-native_3.31.6.bb b/meta/recipes-devtools/cmake/cmake-native_3.31.6.bb
index e285a17681..b940abb3fd 100644
--- a/meta/recipes-devtools/cmake/cmake-native_3.31.6.bb
+++ b/meta/recipes-devtools/cmake/cmake-native_3.31.6.bb
@@ -51,7 +51,7 @@ do_compile() {
51do_install() { 51do_install() {
52 oe_runmake 'DESTDIR=${D}' install 52 oe_runmake 'DESTDIR=${D}' install
53 53
54 # The following codes are here because eSDK needs to provide compatibilty 54 # The following codes are here because eSDK needs to provide compatibility
55 # for SDK. That is, eSDK could also be used like traditional SDK. 55 # for SDK. That is, eSDK could also be used like traditional SDK.
56 mkdir -p ${D}${datadir}/cmake 56 mkdir -p ${D}${datadir}/cmake
57 install -m 644 ${UNPACKDIR}/OEToolchainConfig.cmake ${D}${datadir}/cmake/ 57 install -m 644 ${UNPACKDIR}/OEToolchainConfig.cmake ${D}${datadir}/cmake/
diff --git a/meta/recipes-devtools/cmake/cmake/0001-ctest-Allow-arbitrary-characters-in-test-names-of-CT.patch b/meta/recipes-devtools/cmake/cmake/0001-ctest-Allow-arbitrary-characters-in-test-names-of-CT.patch
new file mode 100644
index 0000000000..31f6148cac
--- /dev/null
+++ b/meta/recipes-devtools/cmake/cmake/0001-ctest-Allow-arbitrary-characters-in-test-names-of-CT.patch
@@ -0,0 +1,202 @@
1From c7e8b03324883760a2d6fab86ae034beb82af651 Mon Sep 17 00:00:00 2001
2From: John Drouhard <john@drouhard.dev>
3Date: Thu, 9 Jan 2025 20:34:42 -0600
4Subject: [PATCH] ctest: Allow arbitrary characters in test names of
5 CTestCostData.txt
6
7This changes the way lines in CTestCostData.txt are parsed to allow for
8spaces in the test name.
9
10It does so by looking for space characters from the end; and once two
11have been found, assumes everything from the beginning up to that
12second-to-last-space is the test name.
13
14Additionally, parsing the file should be much more efficient since there
15is no string or vector heap allocation per line. The std::string used by
16the parse function to convert the int and float should be within most
17standard libraries' small string optimization.
18
19Fixes: #26594
20
21Upstream-Status: Backport [4.0.0, 040da7d83216ace59710407e8ce35d5fd38e1340]
22Signed-off-by: Moritz Haase <Moritz.Haase@bmw.de>
23---
24 Source/CTest/cmCTestMultiProcessHandler.cxx | 77 +++++++++++++++------
25 Source/CTest/cmCTestMultiProcessHandler.h | 3 +-
26 Tests/CTestTestScheduler/CMakeLists.txt | 4 +-
27 3 files changed, 61 insertions(+), 23 deletions(-)
28
29diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
30index 84ea32b84d40025ec333a90d30c42eeaf7adc9ef..231e7b5f39b1d8aa75f4e59a890a099b53fcdaac 100644
31--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
32+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
33@@ -20,6 +20,7 @@
34
35 #include <cm/memory>
36 #include <cm/optional>
37+#include <cm/string_view>
38 #include <cmext/algorithm>
39
40 #include <cm3p/json/value.h>
41@@ -52,6 +53,48 @@ constexpr unsigned long kParallelLevelMinimum = 2u;
42 // Under a job server, parallelism is effectively limited
43 // only by available job server tokens.
44 constexpr unsigned long kParallelLevelUnbounded = 0x10000u;
45+
46+struct CostEntry
47+{
48+ cm::string_view name;
49+ int prevRuns;
50+ float cost;
51+};
52+
53+cm::optional<CostEntry> splitCostLine(cm::string_view line)
54+{
55+ std::string part;
56+ cm::string_view::size_type pos1 = line.size();
57+ cm::string_view::size_type pos2 = line.find_last_of(' ', pos1);
58+ auto findNext = [line, &part, &pos1, &pos2]() -> bool {
59+ if (pos2 != cm::string_view::npos) {
60+ cm::string_view sub = line.substr(pos2 + 1, pos1 - pos2 - 1);
61+ part.assign(sub.begin(), sub.end());
62+ pos1 = pos2;
63+ if (pos1 > 0) {
64+ pos2 = line.find_last_of(' ', pos1 - 1);
65+ }
66+ return true;
67+ }
68+ return false;
69+ };
70+
71+ // parse the cost
72+ if (!findNext()) {
73+ return cm::nullopt;
74+ }
75+ float cost = static_cast<float>(atof(part.c_str()));
76+
77+ // parse the previous runs
78+ if (!findNext()) {
79+ return cm::nullopt;
80+ }
81+ int prev = atoi(part.c_str());
82+
83+ // from start to the last found space is the name
84+ return CostEntry{ line.substr(0, pos1), prev, cost };
85+}
86+
87 }
88
89 namespace cmsys {
90@@ -797,24 +840,21 @@ void cmCTestMultiProcessHandler::UpdateCostData()
91 if (line == "---") {
92 break;
93 }
94- std::vector<std::string> parts = cmSystemTools::SplitString(line, ' ');
95 // Format: <name> <previous_runs> <avg_cost>
96- if (parts.size() < 3) {
97+ cm::optional<CostEntry> entry = splitCostLine(line);
98+ if (!entry) {
99 break;
100 }
101
102- std::string name = parts[0];
103- int prev = atoi(parts[1].c_str());
104- float cost = static_cast<float>(atof(parts[2].c_str()));
105-
106- int index = this->SearchByName(name);
107+ int index = this->SearchByName(entry->name);
108 if (index == -1) {
109 // This test is not in memory. We just rewrite the entry
110- fout << name << " " << prev << " " << cost << "\n";
111+ fout << entry->name << " " << entry->prevRuns << " " << entry->cost
112+ << "\n";
113 } else {
114 // Update with our new average cost
115- fout << name << " " << this->Properties[index]->PreviousRuns << " "
116- << this->Properties[index]->Cost << "\n";
117+ fout << entry->name << " " << this->Properties[index]->PreviousRuns
118+ << " " << this->Properties[index]->Cost << "\n";
119 temp.erase(index);
120 }
121 }
122@@ -850,28 +890,25 @@ void cmCTestMultiProcessHandler::ReadCostData()
123 break;
124 }
125
126- std::vector<std::string> parts = cmSystemTools::SplitString(line, ' ');
127+ // Format: <name> <previous_runs> <avg_cost>
128+ cm::optional<CostEntry> entry = splitCostLine(line);
129
130 // Probably an older version of the file, will be fixed next run
131- if (parts.size() < 3) {
132+ if (!entry) {
133 fin.close();
134 return;
135 }
136
137- std::string name = parts[0];
138- int prev = atoi(parts[1].c_str());
139- float cost = static_cast<float>(atof(parts[2].c_str()));
140-
141- int index = this->SearchByName(name);
142+ int index = this->SearchByName(entry->name);
143 if (index == -1) {
144 continue;
145 }
146
147- this->Properties[index]->PreviousRuns = prev;
148+ this->Properties[index]->PreviousRuns = entry->prevRuns;
149 // When not running in parallel mode, don't use cost data
150 if (this->GetParallelLevel() > 1 && this->Properties[index] &&
151 this->Properties[index]->Cost == 0) {
152- this->Properties[index]->Cost = cost;
153+ this->Properties[index]->Cost = entry->cost;
154 }
155 }
156 // Next part of the file is the failed tests
157@@ -884,7 +921,7 @@ void cmCTestMultiProcessHandler::ReadCostData()
158 }
159 }
160
161-int cmCTestMultiProcessHandler::SearchByName(std::string const& name)
162+int cmCTestMultiProcessHandler::SearchByName(cm::string_view name)
163 {
164 int index = -1;
165
166diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h
167index fd6c17f2fac06949c20f3792dd3eae442b15850b..811be613c3387240c0181f8372b24cf09219621f 100644
168--- a/Source/CTest/cmCTestMultiProcessHandler.h
169+++ b/Source/CTest/cmCTestMultiProcessHandler.h
170@@ -13,6 +13,7 @@
171 #include <vector>
172
173 #include <cm/optional>
174+#include <cm/string_view>
175
176 #include "cmCTest.h"
177 #include "cmCTestResourceAllocator.h"
178@@ -110,7 +111,7 @@ protected:
179 void UpdateCostData();
180 void ReadCostData();
181 // Return index of a test based on its name
182- int SearchByName(std::string const& name);
183+ int SearchByName(cm::string_view name);
184
185 void CreateTestCostList();
186
187diff --git a/Tests/CTestTestScheduler/CMakeLists.txt b/Tests/CTestTestScheduler/CMakeLists.txt
188index 6f8cb4dbc0de35984540e1868788e0a02124e819..daf6ce2b23d8c048334ae1047759130b246dccef 100644
189--- a/Tests/CTestTestScheduler/CMakeLists.txt
190+++ b/Tests/CTestTestScheduler/CMakeLists.txt
191@@ -1,9 +1,9 @@
192-cmake_minimum_required(VERSION 3.10)
193+cmake_minimum_required(VERSION 3.19)
194 project (CTestTestScheduler)
195 include (CTest)
196
197 add_executable (Sleep sleep.c)
198
199 foreach (time RANGE 1 4)
200- add_test (TestSleep${time} Sleep ${time})
201+ add_test ("TestSleep ${time}" Sleep ${time})
202 endforeach ()
diff --git a/meta/recipes-devtools/cmake/cmake_3.31.6.bb b/meta/recipes-devtools/cmake/cmake_3.31.6.bb
index 7d8b8cac65..2d343d6f52 100644
--- a/meta/recipes-devtools/cmake/cmake_3.31.6.bb
+++ b/meta/recipes-devtools/cmake/cmake_3.31.6.bb
@@ -5,6 +5,7 @@ inherit cmake bash-completion
5DEPENDS += "curl expat zlib libarchive xz ncurses bzip2" 5DEPENDS += "curl expat zlib libarchive xz ncurses bzip2"
6 6
7SRC_URI:append:class-nativesdk = " \ 7SRC_URI:append:class-nativesdk = " \
8 file://0001-ctest-Allow-arbitrary-characters-in-test-names-of-CT.patch \
8 file://OEToolchainConfig.cmake \ 9 file://OEToolchainConfig.cmake \
9 file://SDKToolchainConfig.cmake.template \ 10 file://SDKToolchainConfig.cmake.template \
10 file://cmake-setup.py \ 11 file://cmake-setup.py \