Files
cmake/Source/cmDiscoverTestsCommand.cxx
Brad King 6498b76d61 Merge topic 'reduce-realloc-clang-tidy-part3'
0aa1eaf506 Source: Reduce string allocations, part 4
1d2f0120be Source: Reduce string allocations, part 3
cb20c5bd89 Source: Reduce string allocations, part 2

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !12538
2026-09-23 13:30:55 -04:00

155 lines
4.9 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmDiscoverTestsCommand.h"
#include <cstddef>
#include <ostream>
#include <utility>
#include <vector>
#include <cm/memory>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmArgumentParser.h"
#include "cmArgumentParserTypes.h"
#include "cmExecutionStatus.h"
#include "cmGeneratorExpression.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmPolicies.h"
#include "cmScriptGenerator.h"
#include "cmStringAlgorithms.h"
#include "cmTestDiscovery.h"
#include "cmTestGenerator.h"
namespace {
struct Arguments : cmTestDiscoveryArgs
{
bool CommandExpandLists = false;
ArgumentParser::MaybeEmpty<std::vector<std::string>> Configurations;
};
class DiscoveryGenerator : public cmTestGenerator
{
public:
DiscoveryGenerator(Arguments arguments, cmListFileBacktrace backtrace)
: cmTestGenerator(nullptr, arguments.Configurations)
, Args{ std::move(arguments) }
, Backtrace{ std::move(backtrace) }
{
}
private:
void GenerateProperties(std::ostream& os, Indent indent,
std::vector<std::string> const& props,
std::string const& config, cmGeneratorExpression& ge)
{
for (std::size_t i = 0; i < props.size(); i += 2) {
os << '\n'
<< indent << Quote(props[i]) << ' '
<< Quote(ge.Parse(props[i + 1])->Evaluate(this->LG, config));
}
}
void GenerateScriptForConfig(std::ostream& os, std::string const& config,
Indent indent) override
{
// Set up generator expression evaluation context.
cmGeneratorExpression ge(*this->LG->GetMakefile()->GetCMakeInstance(),
this->Backtrace);
auto const in = indent.Next();
os << indent << "discover_tests(COMMAND ";
this->GenerateCommand(os, this->Args.Command, config,
this->Args.CommandExpandLists, ge);
os << '\n' << in << "DISCOVERY_ARGS";
for (auto const& arg : this->Args.DiscoveryArgs) {
os << ' ' << Quote(arg);
}
os << '\n' << in << "DISCOVERY_MATCH " << Quote(this->Args.DiscoveryMatch);
if (!this->Args.DiscoveryProperties.empty()) {
os << '\n' << in << "DISCOVERY_PROPERTIES";
this->GenerateProperties(os, in.Next(), this->Args.DiscoveryProperties,
config, ge);
}
os << '\n' << in << "TEST_NAME " << Quote(this->Args.TestName);
os << '\n' << in << "TEST_ARGS";
for (auto const& arg : this->Args.TestArgs) {
os << ' ' << Quote(arg);
}
os << '\n' << in << "TEST_PROPERTIES";
this->GenerateProperties(os, in.Next(), this->Args.TestProperties, config,
ge);
os << '\n' << in.Next();
this->GenerateBacktrace(os, this->Backtrace);
os << '\n' << in << ")\n";
}
Arguments Args;
cmListFileBacktrace Backtrace;
};
bool SetsFixtureRepeatMode(std::vector<std::string> const& properties)
{
for (std::size_t i = 0; i < properties.size(); i += 2) {
if (properties[i] == "FIXTURE_REPEAT_MODE"_s ||
properties[i] == "_CMAKE_DEFAULT_FIXTURE_REPEAT_MODE"_s) {
return true;
}
}
return false;
}
} // namespace
bool cmDiscoverTestsCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
static auto const parser =
cmArgumentParser<Arguments>{ cmTestDiscoveryParser<Arguments>() }
.Bind("COMMAND_EXPAND_LISTS"_s, &Arguments::CommandExpandLists)
.Bind("CONFIGURATIONS"_s, &Arguments::Configurations);
auto unparsed = std::vector<std::string>{};
Arguments arguments = parser.Parse(args, &unparsed);
if (arguments.MaybeReportError(status.GetMakefile())) {
return true;
}
if (!unparsed.empty()) {
status.SetError(
cmStrCat(" given unknown argument \"", unparsed.front(), "\"."));
return false;
}
if (arguments.DiscoveryProperties.size() % 2 != 0) {
status.SetError(" DISCOVERY_PROPERTIES must be key-value pairs.");
return false;
}
if (arguments.TestProperties.size() % 2 != 0) {
status.SetError(" TEST_PROPERTIES must be key-value pairs.");
return false;
}
cmMakefile& mf = status.GetMakefile();
// The discovered tests are created while ctest runs, too late for the
// policy to reach them. Only NEW needs carrying through. With nothing
// recorded, ctest already uses the EACH_TEST_SEPARATELY behavior of
// CMake 4.4 and below.
if (mf.GetPolicyStatus(cmPolicies::CMP0224) == cmPolicies::NEW &&
!SetsFixtureRepeatMode(arguments.TestProperties)) {
arguments.TestProperties.emplace_back(
"_CMAKE_DEFAULT_FIXTURE_REPEAT_MODE");
arguments.TestProperties.emplace_back("AROUND_EACH_REPEAT");
}
mf.AddTestGenerator(cm::make_unique<DiscoveryGenerator>(std::move(arguments),
mf.GetBacktrace()));
return true;
}