/src/CMake/Source/cmDiscoverTestsCommand.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmDiscoverTestsCommand.h" |
4 | | |
5 | | #include <cstddef> |
6 | | #include <map> |
7 | | #include <ostream> |
8 | | #include <utility> |
9 | | #include <vector> |
10 | | |
11 | | #include <cm/memory> |
12 | | #include <cm/string_view> |
13 | | #include <cmext/string_view> |
14 | | |
15 | | #include "cmArgumentParser.h" |
16 | | #include "cmArgumentParserTypes.h" |
17 | | #include "cmExecutionStatus.h" |
18 | | #include "cmGeneratorExpression.h" |
19 | | #include "cmGeneratorTarget.h" |
20 | | #include "cmGlobalGenerator.h" |
21 | | #include "cmListFileCache.h" |
22 | | #include "cmLocalGenerator.h" |
23 | | #include "cmMakefile.h" |
24 | | #include "cmPolicies.h" |
25 | | #include "cmScriptGenerator.h" |
26 | | #include "cmStringAlgorithms.h" |
27 | | #include "cmTestDiscovery.h" |
28 | | #include "cmTestGenerator.h" |
29 | | |
30 | | namespace { |
31 | | |
32 | | struct Arguments : cmTestDiscoveryArgs |
33 | | { |
34 | | bool CommandExpandLists = false; |
35 | | ArgumentParser::MaybeEmpty<std::vector<std::string>> Configurations; |
36 | | }; |
37 | | |
38 | | class DiscoveryGenerator : public cmTestGenerator |
39 | | { |
40 | | public: |
41 | | DiscoveryGenerator(Arguments arguments, cmListFileBacktrace backtrace) |
42 | 0 | : cmTestGenerator(nullptr, arguments.Configurations) |
43 | 0 | , Args{ std::move(arguments) } |
44 | 0 | , Backtrace{ std::move(backtrace) } |
45 | 0 | { |
46 | 0 | } |
47 | | |
48 | | void Compute(cmLocalGenerator* lg) override |
49 | 0 | { |
50 | 0 | this->cmTestGenerator::Compute(lg); |
51 | 0 | this->BuildDependenciesByConfig.clear(); |
52 | 0 | if (!lg->GetMakefile()->IsOn("CMAKE_TESTING_ENABLED")) { |
53 | 0 | return; |
54 | 0 | } |
55 | 0 | bool const filterConfigs = |
56 | 0 | !lg->GetMakefile() |
57 | 0 | ->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig) |
58 | 0 | .empty(); |
59 | 0 | for (std::string const& config : lg->GetMakefile()->GetGeneratorConfigs( |
60 | 0 | cmMakefile::IncludeEmptyConfig)) { |
61 | 0 | if (filterConfigs && !this->GeneratesForConfig(config)) { |
62 | 0 | continue; |
63 | 0 | } |
64 | 0 | cmTestGenerator::BuildDependencies& deps = |
65 | 0 | this->BuildDependenciesByConfig[config]; |
66 | 0 | if (!cmTestGenerator::EvaluateBuildDependencies( |
67 | 0 | lg, config, this->Backtrace, "discover_tests", this->Args.Command, |
68 | 0 | this->Args.BuildDepends, deps, lg->GetMakefile())) { |
69 | 0 | deps = {}; |
70 | 0 | continue; |
71 | 0 | } |
72 | 0 | for (cmGeneratorTarget* target : deps.Targets) { |
73 | 0 | lg->AddDirectoryTestPrepDependency(config, |
74 | 0 | { target->GetName(), target }); |
75 | 0 | } |
76 | 0 | for (cmTestGenerator::BuildDependencies::FileDependency const& file : |
77 | 0 | deps.Files) { |
78 | 0 | lg->AddDirectoryTestPrepDependency( |
79 | 0 | config, { file.Path, nullptr, file.Owner, file.Generated }); |
80 | 0 | } |
81 | 0 | } |
82 | 0 | } |
83 | | |
84 | | private: |
85 | | void GenerateProperties(std::ostream& os, Indent indent, |
86 | | std::vector<std::string> const& props, |
87 | | std::string const& config, cmGeneratorExpression& ge) |
88 | 0 | { |
89 | 0 | for (std::size_t i = 0; i < props.size(); i += 2) { |
90 | 0 | os << '\n' |
91 | 0 | << indent << Quote(props[i]) << ' ' |
92 | 0 | << Quote(ge.Parse(props[i + 1])->Evaluate(this->LG, config)); |
93 | 0 | } |
94 | 0 | } |
95 | | |
96 | | void GenerateScriptForConfig(std::ostream& os, std::string const& config, |
97 | | Indent indent) override |
98 | 0 | { |
99 | | // Set up generator expression evaluation context. |
100 | 0 | cmGeneratorExpression ge(*this->LG->GetMakefile()->GetCMakeInstance(), |
101 | 0 | this->Backtrace); |
102 | |
|
103 | 0 | std::vector<std::string> const buildDepends = |
104 | 0 | this->LG->GetGlobalGenerator()->GetTestBuildDependencyPaths( |
105 | 0 | config, this->BuildDependenciesByConfig.at(config)); |
106 | |
|
107 | 0 | auto const in = indent.Next(); |
108 | 0 | os << indent << "discover_tests(COMMAND "; |
109 | 0 | this->GenerateCommand(os, this->Args.Command, config, |
110 | 0 | this->Args.CommandExpandLists, ge); |
111 | 0 | os << '\n' << in << "DISCOVERY_ARGS"; |
112 | 0 | for (auto const& arg : this->Args.DiscoveryArgs) { |
113 | 0 | os << ' ' << Quote(arg); |
114 | 0 | } |
115 | 0 | os << '\n' << in << "DISCOVERY_MATCH " << Quote(this->Args.DiscoveryMatch); |
116 | 0 | if (!this->Args.DiscoveryProperties.empty()) { |
117 | 0 | os << '\n' << in << "DISCOVERY_PROPERTIES"; |
118 | 0 | this->GenerateProperties(os, in.Next(), this->Args.DiscoveryProperties, |
119 | 0 | config, ge); |
120 | 0 | } |
121 | 0 | os << '\n' << in << "TEST_NAME " << Quote(this->Args.TestName); |
122 | 0 | os << '\n' << in << "TEST_ARGS"; |
123 | 0 | for (auto const& arg : this->Args.TestArgs) { |
124 | 0 | os << ' ' << Quote(arg); |
125 | 0 | } |
126 | 0 | os << '\n' << in << "TEST_PROPERTIES"; |
127 | 0 | this->GenerateProperties(os, in.Next(), this->Args.TestProperties, config, |
128 | 0 | ge); |
129 | 0 | os << '\n' << in.Next(); |
130 | 0 | this->GenerateBacktrace(os, this->Backtrace); |
131 | 0 | if (!buildDepends.empty()) { |
132 | 0 | os << '\n' << in << "BUILD_DEPENDS"; |
133 | 0 | for (std::string const& dep : buildDepends) { |
134 | 0 | os << ' ' << Quote(dep); |
135 | 0 | } |
136 | 0 | } |
137 | 0 | os << '\n' << in << ")\n"; |
138 | 0 | } |
139 | | |
140 | | Arguments Args; |
141 | | cmListFileBacktrace Backtrace; |
142 | | std::map<std::string, cmTestGenerator::BuildDependencies> |
143 | | BuildDependenciesByConfig; |
144 | | }; |
145 | | |
146 | | bool SetsFixtureRepeatMode(std::vector<std::string> const& properties) |
147 | 0 | { |
148 | 0 | for (std::size_t i = 0; i < properties.size(); i += 2) { |
149 | 0 | if (properties[i] == "FIXTURE_REPEAT_MODE"_s || |
150 | 0 | properties[i] == "_CMAKE_DEFAULT_FIXTURE_REPEAT_MODE"_s) { |
151 | 0 | return true; |
152 | 0 | } |
153 | 0 | } |
154 | 0 | return false; |
155 | 0 | } |
156 | | |
157 | | } // namespace |
158 | | |
159 | | bool cmDiscoverTestsCommand(std::vector<std::string> const& args, |
160 | | cmExecutionStatus& status) |
161 | 0 | { |
162 | 0 | static auto const parser = |
163 | 0 | cmArgumentParser<Arguments>{ cmTestDiscoveryParser<Arguments>() } |
164 | 0 | .Bind("COMMAND_EXPAND_LISTS"_s, &Arguments::CommandExpandLists) |
165 | 0 | .Bind("CONFIGURATIONS"_s, &Arguments::Configurations); |
166 | |
|
167 | 0 | auto unparsed = std::vector<std::string>{}; |
168 | 0 | Arguments arguments = parser.Parse(args, &unparsed); |
169 | 0 | if (arguments.MaybeReportError(status.GetMakefile())) { |
170 | 0 | return true; |
171 | 0 | } |
172 | | |
173 | 0 | if (!unparsed.empty()) { |
174 | 0 | status.SetError( |
175 | 0 | cmStrCat(" given unknown argument \"", unparsed.front(), "\".")); |
176 | 0 | return false; |
177 | 0 | } |
178 | | |
179 | 0 | if (arguments.DiscoveryProperties.size() % 2 != 0) { |
180 | 0 | status.SetError(" DISCOVERY_PROPERTIES must be key-value pairs."); |
181 | 0 | return false; |
182 | 0 | } |
183 | | |
184 | 0 | if (arguments.TestProperties.size() % 2 != 0) { |
185 | 0 | status.SetError(" TEST_PROPERTIES must be key-value pairs."); |
186 | 0 | return false; |
187 | 0 | } |
188 | | |
189 | 0 | cmMakefile& mf = status.GetMakefile(); |
190 | | |
191 | | // The discovered tests are created while ctest runs, too late for the |
192 | | // policy to reach them. Only NEW needs carrying through. With nothing |
193 | | // recorded, ctest already uses the EACH_TEST_SEPARATELY behavior of |
194 | | // CMake 4.4 and below. |
195 | 0 | if (mf.GetPolicyStatus(cmPolicies::CMP0224) == cmPolicies::NEW && |
196 | 0 | !SetsFixtureRepeatMode(arguments.TestProperties)) { |
197 | 0 | arguments.TestProperties.emplace_back( |
198 | 0 | "_CMAKE_DEFAULT_FIXTURE_REPEAT_MODE"); |
199 | 0 | arguments.TestProperties.emplace_back("AROUND_EACH_REPEAT"); |
200 | 0 | } |
201 | |
|
202 | 0 | mf.AddTestGenerator(cm::make_unique<DiscoveryGenerator>(std::move(arguments), |
203 | 0 | mf.GetBacktrace())); |
204 | 0 | return true; |
205 | 0 | } |