ctest: Support Test Dependencies for discover_test modules

- Update discover_tests to work with test_prep targets and ctest --out-of-date
- Extend behavior to gtest_discover_tests
- Don't treat non-built targets as always out of date
- Support imported targets in BUILD_DEPENDS

Issue: #28052, #28070
This commit is contained in:
Martin Duffy authored and Brad King committed 2026-09-30 09:26:58 -04:00
1 parent d291927f5d
commit 463517fddd
69 files changed
+1146 -160

No files matched your search

+2
View File
@@ -117,6 +117,8 @@ directory the test is created in.
``COMMAND`` also enable the :option:`--out-of-date <ctest --out-of-date>`
behavior of :manual:`ctest(1)`.
Tests with missing build dependencies will not be run.
``COMMAND_EXPAND_LISTS``
.. versionadded:: 3.16
+20
View File
@@ -16,6 +16,7 @@ Register tests with names and properties discovered at test time by
TEST_NAME <replacement>
TEST_ARGS <replacement>...
[TEST_PROPERTIES <key> <replacement> [<key> <replacement>]...]
[BUILD_DEPENDS <dependencies>...]
)
This command configures test discovery rather than defining a single test at
@@ -94,6 +95,25 @@ the provided regular expression and replacement strings.
they do not set :prop_test:`FIXTURE_REPEAT_MODE`, policy :policy:`CMP0224`
determines the behavior when the :ctest-option:`--repeat` option is used.
``BUILD_DEPENDS``
Specify a list of targets or files that must be built before the test
discovery can run.
When the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is enabled with the
:ref:`Ninja Generators`, :generator:`FASTBuild`, or :ref:`Makefile
Generators`, these dependencies are built by the ``test_prep/all`` target.
If ``COMMAND`` names an executable target created by
:command:`add_executable`, that target is also added automatically.
Individual ``test_prep/<test-name>`` targets are not created for discovered
tests because their names are not known at generation time.
Build dependencies added by this argument, and an executable target
named by ``COMMAND``, also enable the
:ctest-option:`--out-of-date` behavior of :manual:`ctest(1)`.
Tests with missing build dependencies will not be run.
CTest executes the discovery step to obtain the list of tests and then runs
each discovered test using the command-line produced by ``COMMAND`` together
with ``TEST_ARGS``. The pass/fail behavior of each discovered test follows
+27
View File
@@ -29,6 +29,32 @@ This writes a FASTBuild configuration file named ``fbuild.bff`` into
These IDE files can be generated by building ``xcode`` or ``solution`` targets
and will build using FASTBuild as the backend.
Built-in Targets
----------------
.. versionadded:: 4.5
When the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is enabled, the
generated build file provides the following targets:
``test_prep/<test-name>``
Builds all known build dependencies for the test named ``<test-name>``
added by :command:`add_test`, including the executable target invoked by
the test, targets referenced by generator expressions in the test command,
and explicit ``BUILD_DEPENDS`` entries.
Tests whose names are not valid target names are excluded. If multiple
tests in different directories share the same name, their dependencies are
merged into one ``test_prep/<test-name>`` target.
Individual ``test_prep/<test-name>`` targets are not created for tests
discovered at test time, because their names are not known during generation.
``test_prep/all``
Depends on every generated ``test_prep/<test-name>`` target. Also builds
dependencies recorded by the :prop_dir:`CMAKE_TEST_BUILD_DEPENDS` directory
property.
Caching
-------
@@ -70,6 +96,7 @@ The following variables can be used to configure this generator:
* :variable:`CMAKE_FASTBUILD_IDE_ARGS`
* :variable:`CMAKE_FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT`
* :variable:`CMAKE_FASTBUILD_VERBOSE_GENERATOR`
* :variable:`CMAKE_TEST_BUILD_DEPENDS`
Target Properties
-----------------
+1
View File
@@ -72,6 +72,7 @@ Properties on Directories
/prop_dir/CACHE_VARIABLES
/prop_dir/CLEAN_NO_CUSTOM
/prop_dir/CMAKE_CONFIGURE_DEPENDS
/prop_dir/CMAKE_TEST_BUILD_DEPENDS
/prop_dir/COMPILE_DEFINITIONS
/prop_dir/COMPILE_OPTIONS
/prop_dir/DEFINITIONS
+8 -5
View File
@@ -616,13 +616,16 @@ The options for running tests are:
respect to their recorded build dependencies. Build dependencies include
executables and targets in generator expressions as part of the test
``COMMAND``, as well as the outputs of targets or files added as explicit
dependencies with the ``BUILD_DEPENDS`` argument of :command:`add_test`.
dependencies with the ``BUILD_DEPENDS`` argument of :command:`add_test` or
:command:`discover_tests`. Tests discovered by
:command:`gtest_discover_tests` also record their test executable as a build
dependency.
A test is selected to run when any of its recorded build dependencies are
newer than the test's last-run timestamp, or when the test has not been
run before. Tests without any known build dependencies, including any tests
not added by the :command:`add_test` command, are excluded when this argument
is provided.
newer than the test's last-run timestamp, when any dependency is missing, or
when the test has not been run before. Selected tests with missing build
dependencies are reported as ``Not Run``. Tests without any known build
dependencies are excluded when this argument is provided.
View Help
=========
@@ -0,0 +1,15 @@
CMAKE_TEST_BUILD_DEPENDS
------------------------
.. versionadded:: 4.5
List of directory-scoped build time dependencies for tests.
This property holds a
:ref:`semicolon-separated list <CMake Language Lists>` of targets and/or files
that should be built by ``test_prep/all`` when
:variable:`CMAKE_TEST_BUILD_DEPENDS` is enabled.
Projects may append to this property directly with :command:`set_property` or
:command:`set_directory_properties` to extend ``test_prep/all`` with
directory-scoped build dependencies.
@@ -18,6 +18,9 @@ target for all such tests. Building these targets ensures the test executable,
targets referenced by test command generator expressions, and explicit
``BUILD_DEPENDS`` entries are up-to-date before the test runs.
Modules may also extend ``test_prep/all`` with directory-scoped dependencies
via the :prop_dir:`CMAKE_TEST_BUILD_DEPENDS` directory property.
Tests whose names are not valid target names are excluded from this behavior.
If multiple tests in different directories share the same name, their
dependencies are merged into a single ``test_prep/<name>`` target.
+4
View File
@@ -740,6 +740,10 @@ function(gtest_discover_tests target)
)
file(GENERATE OUTPUT "${discovery_file}" CONTENT "${discovery_content}")
set_property(DIRECTORY APPEND PROPERTY CMAKE_TEST_BUILD_DEPENDS
"${target}"
)
set(ctest_include_content)
if(arg_DISCOVERY_MODE STREQUAL "POST_BUILD")
# Make sure that TEST_LAUNCHER and CROSSCOMPILING_EMULATOR appear on the
+1
View File
@@ -97,6 +97,7 @@ macro(write_test_to_file)
"set_tests_properties(${guarded_testname}\n"
" PROPERTIES\n"
" ${maybe_DISABLED}\n"
" _CMAKE_TEST_BUILD_DEPENDS [==[${arg_TEST_EXECUTABLE}]==]\n"
" ${maybe_LOCATION}\n"
" WORKING_DIRECTORY [==[${arg_TEST_WORKING_DIR}]==]\n"
" SKIP_REGULAR_EXPRESSION [==[\\[ SKIPPED \\]]==]\n"
+6 -1
View File
@@ -161,11 +161,16 @@ bool cmCTestDiscoverTests(cmTestDiscoveryArgs const& args,
}
auto testProperties = std::vector<std::string>{ testName, "PROPERTIES" };
testProperties.reserve(2 + args.TestProperties.size());
testProperties.reserve(2 + args.TestProperties.size() +
(args.BuildDepends.empty() ? 0 : 2));
for (std::size_t i = 0; i < args.TestProperties.size(); i += 2) {
testProperties.push_back(args.TestProperties[i]);
testProperties.push_back(replace(args.TestProperties[i + 1]));
}
if (!args.BuildDepends.empty()) {
testProperties.push_back("_CMAKE_TEST_BUILD_DEPENDS");
testProperties.push_back(cmList::to_string(args.BuildDepends));
}
if (!handler->SetTestsProperties(testProperties)) {
return false;
}
+15
View File
@@ -30,6 +30,7 @@
#include "cmInstrumentation.h"
#include "cmInstrumentationQuery.h"
#include "cmJSONState.h"
#include "cmList.h"
#include "cmProcess.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@@ -668,6 +669,20 @@ bool cmCTestRunTest::StartTest(size_t completed, size_t total)
this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
return false;
}
// log and return if we did not find any other test dependencies
for (std::string const& dep : cmList{ this->TestProperties->BuildDepends }) {
if (!dep.empty() && !cmSystemTools::FileExists(dep)) {
std::string const msg = "Unable to find build dependency: " + dep;
*this->TestHandler->LogFile << msg << std::endl;
cmCTestLog(this->CTest, ERROR_MESSAGE, msg, '\n');
this->TestResult.Output = msg;
this->TestResult.FullCommandLine.clear();
this->TestResult.Environment.clear();
this->TestResult.CompletionStatus = "Build Dependencies Missing";
this->TestResult.Status = cmCTestTestHandler::NOT_RUN;
return false;
}
}
this->StartTime = this->CTest->CurrentTime();
if (this->CTest->GetInstrumentation().HasQuery()) {
this->CTest->GetInstrumentation().GetPreTestStats();
+8 -8
View File
@@ -995,6 +995,10 @@ bool cmCTestTestHandler::ComputeTestList()
}
}
if (this->TestOptions.OutOfDateOnly && !tp.IsOutOfDate) {
continue;
}
if (this->TestsToRunByName) {
if (this->TestsToRunByName->find(tp.Name) ==
this->TestsToRunByName->end()) {
@@ -1057,11 +1061,11 @@ bool cmCTestTestHandler::ComputeTestListForRerunFailed()
void cmCTestTestHandler::ComputeOutOfDateTests()
{
ListOfTests finalList;
std::string const stampDir = this->CTest->GetStampDir();
cmSystemTools::MakeDirectory(stampDir);
for (cmCTestTestProperties& tp : this->TestList) {
tp.IsOutOfDate = false;
if (tp.BuildDepends.empty()) {
continue;
}
@@ -1069,7 +1073,7 @@ void cmCTestTestHandler::ComputeOutOfDateTests()
std::string const stampFile = cmStrCat(stampDir, '/', tp.GetStampFile());
if (!cmSystemTools::FileExists(stampFile)) {
finalList.push_back(tp);
tp.IsOutOfDate = true;
continue;
}
@@ -1080,8 +1084,7 @@ void cmCTestTestHandler::ComputeOutOfDateTests()
continue;
}
if (!cmSystemTools::FileExists(dep)) {
// If any dependencies don't exist, skip the test
outOfDate = false;
outOfDate = true;
break;
}
int result = 0;
@@ -1091,11 +1094,8 @@ void cmCTestTestHandler::ComputeOutOfDateTests()
outOfDate = true;
}
}
if (outOfDate) {
finalList.push_back(tp);
}
tp.IsOutOfDate = outOfDate;
}
this->TestList = finalList;
}
void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const
+1
View File
@@ -191,6 +191,7 @@ public:
std::map<std::string, std::string> CustomProperties;
std::unordered_map<std::string, std::string> RawProperties;
bool IsInBasedOnREOptions = true;
bool IsOutOfDate = true;
bool WillFail = false;
std::string WillFailRaw;
bool Disabled = false;
+51
View File
@@ -3,6 +3,7 @@
#include "cmDiscoverTestsCommand.h"
#include <cstddef>
#include <map>
#include <ostream>
#include <utility>
#include <vector>
@@ -15,6 +16,8 @@
#include "cmArgumentParserTypes.h"
#include "cmExecutionStatus.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
@@ -42,6 +45,42 @@ public:
{
}
void Compute(cmLocalGenerator* lg) override
{
this->cmTestGenerator::Compute(lg);
this->BuildDependenciesByConfig.clear();
if (!lg->GetMakefile()->IsOn("CMAKE_TESTING_ENABLED")) {
return;
}
bool const filterConfigs =
!lg->GetMakefile()
->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig)
.empty();
for (std::string const& config : lg->GetMakefile()->GetGeneratorConfigs(
cmMakefile::IncludeEmptyConfig)) {
if (filterConfigs && !this->GeneratesForConfig(config)) {
continue;
}
cmTestGenerator::BuildDependencies& deps =
this->BuildDependenciesByConfig[config];
if (!cmTestGenerator::EvaluateBuildDependencies(
lg, config, this->Backtrace, "discover_tests", this->Args.Command,
this->Args.BuildDepends, deps, lg->GetMakefile())) {
deps = {};
continue;
}
for (cmGeneratorTarget* target : deps.Targets) {
lg->AddDirectoryTestPrepDependency(config,
{ target->GetName(), target });
}
for (cmTestGenerator::BuildDependencies::FileDependency const& file :
deps.Files) {
lg->AddDirectoryTestPrepDependency(
config, { file.Path, nullptr, file.Owner, file.Generated });
}
}
}
private:
void GenerateProperties(std::ostream& os, Indent indent,
std::vector<std::string> const& props,
@@ -61,6 +100,10 @@ private:
cmGeneratorExpression ge(*this->LG->GetMakefile()->GetCMakeInstance(),
this->Backtrace);
std::vector<std::string> const buildDepends =
this->LG->GetGlobalGenerator()->GetTestBuildDependencyPaths(
config, this->BuildDependenciesByConfig.at(config));
auto const in = indent.Next();
os << indent << "discover_tests(COMMAND ";
this->GenerateCommand(os, this->Args.Command, config,
@@ -85,11 +128,19 @@ private:
ge);
os << '\n' << in.Next();
this->GenerateBacktrace(os, this->Backtrace);
if (!buildDepends.empty()) {
os << '\n' << in << "BUILD_DEPENDS";
for (std::string const& dep : buildDepends) {
os << ' ' << Quote(dep);
}
}
os << '\n' << in << ")\n";
}
Arguments Args;
cmListFileBacktrace Backtrace;
std::map<std::string, cmTestGenerator::BuildDependencies>
BuildDependenciesByConfig;
};
bool SetsFixtureRepeatMode(std::vector<std::string> const& properties)
+16
View File
@@ -1625,6 +1625,22 @@ void cmGlobalFastbuildGenerator::WriteTestPrepTargets()
testPrepTarget.Dependencies.emplace(depFile.Path);
}
}
cmLocalGenerator::DirectoryTestPrepTarget directoryTarget;
if (localGen->GetDirectoryTestPrepTarget(
directoryTarget,
localGen->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE"))) {
TestPrepTarget& testPrepTarget = testPrepTargets[directoryTarget.Name];
testPrepTarget.Comment = std::move(directoryTarget.Comment);
for (cmLocalGenerator::DirectoryTestPrepDependency const& dep :
directoryTarget.Dependencies) {
if (dep.Target) {
testPrepTarget.Dependencies.emplace(dep.Target->GetName());
} else {
testPrepTarget.Dependencies.emplace(dep.Raw);
}
}
}
}
FastbuildAliasNode allAliasNode;
+16 -1
View File
@@ -71,6 +71,7 @@
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmTargetExport.h"
#include "cmTestGenerator.h"
#include "cmUnreachable.h"
#include "cmValue.h"
#include "cmVersion.h"
@@ -1870,6 +1871,14 @@ bool cmGlobalGenerator::Compute()
localGen->ComputeTargetManifest();
}
// Register test preparation dependencies after custom-command outputs are
// known, but before generators compute their build graphs.
for (auto const& localGen : this->LocalGenerators) {
for (auto const& tester : localGen->GetMakefile()->GetTestGenerators()) {
tester->Compute(localGen.get());
}
}
// Compute the inter-target dependencies.
if (!this->ComputeTargetDepends()) {
return false;
@@ -3248,11 +3257,17 @@ std::vector<std::string> cmGlobalGenerator::GetTestBuildDependencyPaths(
}
continue;
}
uniqueDeps.insert(target->GetFullPath(config));
uniqueDeps.insert(this->GetTestBuildDependencyPath(target, config));
}
return { uniqueDeps.begin(), uniqueDeps.end() };
}
std::string cmGlobalGenerator::GetTestBuildDependencyPath(
cmGeneratorTarget const* target, std::string const& config) const
{
return target->GetFullPath(config);
}
// If the file has no extension it's either a raw executable or might
// be a direct reference to a binary within a framework (bad practice!).
// This is where we change the path to point to the framework directory.
+3
View File
@@ -796,6 +796,9 @@ public:
cmXcFrameworkPlist const& content);
protected:
virtual std::string GetTestBuildDependencyPath(
cmGeneratorTarget const* target, std::string const& config) const;
/** Get all targets produced under the given root, plus the transitive
closure of targets on which they depend, possibly from other dirs. */
TargetDependSet GetTargetsForProject(
+24 -2
View File
@@ -1308,7 +1308,7 @@ void cmGlobalNinjaGenerator::WriteTestPrepTargets()
std::map<std::string, TestPrepTarget> testPrepTargets;
for (auto const& localGen : this->LocalGenerators) {
auto* lg = static_cast<cmLocalNinjaGenerator*>(localGen.get());
cmLocalGenerator* lg = localGen.get();
auto const& testGenerators = lg->GetMakefile()->GetTestGenerators();
for (auto const& tester : testGenerators) {
cmTestGenerator::BuildDependencies testDeps;
@@ -1332,6 +1332,27 @@ void cmGlobalNinjaGenerator::WriteTestPrepTargets()
this->ConvertToNinjaPath(file.Path));
}
}
cmLocalGenerator::DirectoryTestPrepTarget directoryTarget;
if (!lg->GetDirectoryTestPrepTarget(directoryTarget, config)) {
continue;
}
std::string const depName =
this->ConvertToNinjaPath(directoryTarget.Name);
TestPrepTarget& testPrepTarget = testPrepTargets[depName];
testPrepTarget.Comment = std::move(directoryTarget.Comment);
for (cmLocalGenerator::DirectoryTestPrepDependency const& dep :
directoryTarget.Dependencies) {
if (dep.Target) {
this->AppendTargetOutputs(dep.Target, testPrepTarget.ExplicitDeps,
config, DependOnTargetArtifact);
} else {
testPrepTarget.ExplicitDeps.push_back(
this->ConvertToNinjaPath(dep.Raw));
}
}
}
std::vector<std::string> allDeps;
@@ -1369,7 +1390,8 @@ void cmGlobalNinjaGenerator::WriteTestPrepTargets()
}
}
} else {
writeConfig(std::string(), *this->GetCommonFileStream());
writeConfig(this->Makefiles.front()->GetSafeDefinition("CMAKE_BUILD_TYPE"),
*this->GetCommonFileStream());
}
}
+66 -15
View File
@@ -115,8 +115,11 @@ void cmGlobalUnixMakefileGenerator3::Generate()
this->ClangTidyExportFixesDirs.clear();
this->ClangTidyExportFixesFiles.clear();
this->TestPrepTargets.clear();
this->TestPrepEnabled = false;
// Compute the "test_prep/" targets before generating the local makefiles
// so their convenience rules can be written into the top-level Makefile.
// so they can be emitted in a single pass.
this->ComputeTestPrepTargets();
// first do superclass method
@@ -1064,10 +1067,12 @@ void cmGlobalUnixMakefileGenerator3::ComputeTestPrepTargets()
for (cmTestGenerator::BuildDependencies::FileDependency const& file :
deps.Files) {
if (file.Owner) {
if (cmGeneratorTarget* owner = file.Owner
? file.Owner
: this->FindOutputOwningTarget(file.Path)) {
// The file is the primary output of one build-system target; build
// that target to produce the file.
rules.push_back(targetAllRule(file.Owner));
rules.push_back(targetAllRule(owner));
} else if (file.Generated) {
// The file is generated but cannot be attributed to a single owning
// target (e.g. a byproduct or an ambiguous/shared output), so the
@@ -1088,6 +1093,8 @@ void cmGlobalUnixMakefileGenerator3::ComputeTestPrepTargets()
// file that already exists) and needs no build rule.
}
}
this->AddDirectoryTestPrepTargets(lg.get(), this->TestPrepTargets);
}
// Sort and de-duplicate each rule list (as the Ninja generator does).
@@ -1098,6 +1105,45 @@ void cmGlobalUnixMakefileGenerator3::ComputeTestPrepTargets()
}
}
void cmGlobalUnixMakefileGenerator3::AddDirectoryTestPrepTargets(
cmLocalGenerator* lg,
std::map<std::string, std::vector<std::string>>& testPrepTargets)
{
cmLocalGenerator::DirectoryTestPrepTarget directoryTarget;
if (!lg->GetDirectoryTestPrepTarget(
directoryTarget,
lg->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE"))) {
return;
}
std::vector<std::string>& rules = testPrepTargets[directoryTarget.Name];
for (cmLocalGenerator::DirectoryTestPrepDependency const& dep :
directoryTarget.Dependencies) {
cmGeneratorTarget* target = dep.Target ? dep.Target : dep.Owner;
if (!target) {
target = this->FindOutputOwningTarget(dep.Raw);
}
if (target) {
auto* lg3 = static_cast<cmLocalUnixMakefileGenerator3*>(
target->GetLocalGenerator());
rules.push_back(
cmStrCat(lg3->GetRelativeTargetDirectory(target), "/all"));
} else if (dep.Generated) {
lg->GetMakefile()->IssueMessage(
MessageType::WARNING,
cmStrCat("Directory test prep dependency\n ", dep.Raw,
"\nis generated but is not the unique output of a build "
"target, so the \"",
directoryTarget.Name,
"\" target cannot build it with this generator. Depend "
"on the target that produces it (for example one created "
"with add_custom_target) instead."),
lg->GetMakefile()->GetBacktrace());
}
}
}
void cmGlobalUnixMakefileGenerator3::WriteTestPrepRules(
std::ostream& makefileStream, cmLocalUnixMakefileGenerator3& rootLG)
{
@@ -1142,24 +1188,29 @@ void cmGlobalUnixMakefileGenerator3::WriteTestPrepConvenienceRules(
std::vector<std::string> depends;
std::vector<std::string> commands;
auto writeForward = [&](std::string const& prepName) {
depends.clear();
if (regenerate) {
depends.emplace_back("cmake_check_build_system");
}
commands.clear();
commands.push_back(lg.GetRecursiveMakeCall(makefile2, prepName));
lg.WriteMakeRule(ruleFileStream, "Build the dependencies of a test.",
prepName, depends, commands, true);
};
lg.WriteDivider(ruleFileStream);
ruleFileStream << "# Convenience rules to build test dependencies.\n\n";
for (auto const& entry : this->TestPrepTargets) {
writeForward(entry.first);
depends.clear();
if (regenerate) {
depends.emplace_back("cmake_check_build_system");
}
commands.clear();
commands.push_back(lg.GetRecursiveMakeCall(makefile2, entry.first));
lg.WriteMakeRule(ruleFileStream, "Build the dependencies of a test.",
entry.first, depends, commands, true);
}
writeForward("test_prep/all");
depends.clear();
if (regenerate) {
depends.emplace_back("cmake_check_build_system");
}
commands.clear();
commands.push_back(lg.GetRecursiveMakeCall(makefile2, "test_prep/all"));
lg.WriteMakeRule(ruleFileStream, "Build the dependencies of a test.",
"test_prep/all", depends, commands, true);
}
void cmGlobalUnixMakefileGenerator3::WriteHelpRule(
+3
View File
@@ -330,5 +330,8 @@ private:
// rules that must be built to prepare the named test. Populated by
// ComputeTestPrepTargets when CMAKE_TEST_BUILD_DEPENDS is enabled.
std::map<std::string, std::vector<std::string>> TestPrepTargets;
void AddDirectoryTestPrepTargets(
cmLocalGenerator* lg,
std::map<std::string, std::vector<std::string>>& testPrepTargets);
bool TestPrepEnabled = false;
};
+24
View File
@@ -5248,6 +5248,30 @@ void cmGlobalXCodeGenerator::OutputXCodeProject(
root->GetBinaryDirectory());
}
std::string cmGlobalXCodeGenerator::GetTestBuildDependencyPath(
cmGeneratorTarget const* target, std::string const& config) const
{
// Replicates the CMake-language logic that computes _test_bundle_dir in
// xctest_add_test() in Modules/FindXCTest.cmake.
if (!target->IsImported() && target->IsXCTestOnApple() &&
this->XcodeVersion >= 73) {
cmValue testeeName = target->GetProperty("XCTEST_TESTEE");
cmGeneratorTarget const* testee = testeeName
? target->GetLocalGenerator()->FindGeneratorTargetToUse(*testeeName)
: nullptr;
if (testee && testee->IsAppBundleOnApple()) {
std::string path = testee->GetMacContentDirectory(
config, cmStateEnums::RuntimeBinaryArtifact);
if (!(this->XcodeBuildSystem == BuildSystem::Twelve &&
this->XcodeVersion < 125 && this->SystemName != "Darwin"_s)) {
path += "/PlugIns";
}
return cmStrCat(path, '/', target->GetFullName(config));
}
}
return this->cmGlobalGenerator::GetTestBuildDependencyPath(target, config);
}
bool cmGlobalXCodeGenerator::OutputXCodeSharedSchemes(
std::string const& xcProjDir, cmLocalGenerator* root)
{
+3
View File
@@ -143,6 +143,9 @@ public:
};
protected:
std::string GetTestBuildDependencyPath(
cmGeneratorTarget const* target, std::string const& config) const override;
void AddExtraIDETargets() override;
void Generate() override;
+47 -1
View File
@@ -243,6 +243,53 @@ cmLocalGenerator::CreateRulePlaceholderExpander(cmBuildStep buildStep) const
: cmRulePlaceholderExpander::UseShortPaths::No);
}
void cmLocalGenerator::AddDirectoryTestPrepDependency(
std::string const& config, DirectoryTestPrepDependency dependency)
{
this->DiscoveryTestPrepDependencies[config].push_back(std::move(dependency));
}
bool cmLocalGenerator::GetDirectoryTestPrepTarget(DirectoryTestPrepTarget& out,
std::string const& config)
{
cmValue const directoryTestPrepDependencies =
this->GetStateSnapshot().GetDirectory().GetProperty(
"CMAKE_TEST_BUILD_DEPENDS");
std::string const directoryId =
cmCryptoHash(cmCryptoHash::AlgoMD5)
.HashString(this->GetCurrentBinaryDirectory())
.substr(0, 8);
out.Name = cmStrCat("test_prep/directory_", directoryId);
out.Comment =
cmStrCat("Build extra dependencies for test prep in directory ",
this->GetCurrentBinaryDirectory());
out.Dependencies.clear();
auto const discovery = this->DiscoveryTestPrepDependencies.find(config);
if (discovery != this->DiscoveryTestPrepDependencies.end()) {
out.Dependencies = discovery->second;
}
cmList buildDepends;
if (directoryTestPrepDependencies) {
buildDepends.assign(
cmList{ *directoryTestPrepDependencies, cmList::EmptyElements::Yes });
}
cmTestGenerator::BuildDependencies deps;
cmTestGenerator::EvaluateBuildDependencies(
this, config, this->Makefile->GetBacktrace(), "directory_test_prep", {},
std::vector<std::string>(buildDepends.begin(), buildDepends.end()), deps,
this->Makefile);
for (cmGeneratorTarget* target : deps.Targets) {
out.Dependencies.push_back({ target->GetName(), target });
}
for (cmTestGenerator::BuildDependencies::FileDependency const& file :
deps.Files) {
out.Dependencies.push_back(
{ file.Path, nullptr, file.Owner, file.Generated });
}
return !out.Dependencies.empty();
}
cmLocalGenerator::~cmLocalGenerator() = default;
void cmLocalGenerator::IssueMessage(MessageType type, std::string const& text,
@@ -485,7 +532,6 @@ void cmLocalGenerator::GenerateTestFiles()
// Ask each test generator to write its code.
for (auto const& tester : this->Makefile->GetTestGenerators()) {
tester->Compute(this);
tester->Generate(fout, config, configurationTypes);
}
using vec_t = std::vector<cmStateSnapshot>;
+32
View File
@@ -78,6 +78,31 @@ struct cmSourcesWithOutput
class cmLocalGenerator : public cmOutputConverter
{
public:
struct DirectoryTestPrepDependency
{
DirectoryTestPrepDependency(std::string raw, cmGeneratorTarget* target,
cmGeneratorTarget* owner = nullptr,
bool generated = false)
: Raw(std::move(raw))
, Target(target)
, Owner(owner)
, Generated(generated)
{
}
std::string Raw;
cmGeneratorTarget* Target;
cmGeneratorTarget* Owner;
bool Generated;
};
struct DirectoryTestPrepTarget
{
std::string Name;
std::string Comment;
std::vector<DirectoryTestPrepDependency> Dependencies;
};
cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile);
~cmLocalGenerator() override;
@@ -135,6 +160,11 @@ public:
CreateRulePlaceholderExpander(
cmBuildStep buildStep = cmBuildStep::Compile) const;
void AddDirectoryTestPrepDependency(std::string const& config,
DirectoryTestPrepDependency dependency);
bool GetDirectoryTestPrepTarget(DirectoryTestPrepTarget& out,
std::string const& config);
std::string GetExeExportFlags(std::string const& linkLanguage,
cmGeneratorTarget& tgt) const;
@@ -708,6 +738,8 @@ protected:
#endif
private:
std::map<std::string, std::vector<DirectoryTestPrepDependency>>
DiscoveryTestPrepDependencies;
/**
* See LinearGetSourceFileWithOutput for background information
*/
+2
View File
@@ -12,6 +12,7 @@ struct cmTestDiscoveryArgs : ArgumentParser::ParseResult
ArgumentParser::NonEmpty<std::vector<std::string>> DiscoveryArgs;
ArgumentParser::NonEmpty<std::string> DiscoveryMatch;
ArgumentParser::MaybeEmpty<std::vector<std::string>> DiscoveryProperties;
ArgumentParser::MaybeEmpty<std::vector<std::string>> BuildDepends;
ArgumentParser::NonEmpty<std::string> TestName;
ArgumentParser::NonEmpty<std::vector<std::string>> TestArgs;
ArgumentParser::MaybeEmpty<std::vector<std::string>> TestProperties;
@@ -26,6 +27,7 @@ auto cmTestDiscoveryParser() -> cmArgumentParser<Args>
.Bind("DISCOVERY_ARGS"_s, &Args::DiscoveryArgs)
.Bind("DISCOVERY_MATCH"_s, &Args::DiscoveryMatch)
.Bind("DISCOVERY_PROPERTIES"_s, &Args::DiscoveryProperties)
.Bind("BUILD_DEPENDS"_s, &Args::BuildDepends)
.Bind("TEST_NAME"_s, &Args::TestName)
.Bind("TEST_ARGS"_s, &Args::TestArgs)
.Bind("TEST_PROPERTIES"_s, &Args::TestProperties);
+30 -22
View File
@@ -17,7 +17,6 @@
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmPropertyMap.h"
#include "cmRange.h"
@@ -114,24 +113,29 @@ cmTest* cmTestGenerator::GetTest() const
return this->Test;
}
bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg,
std::string const& config,
BuildDependencies& info)
bool cmTestGenerator::EvaluateBuildDependencies(
cmLocalGenerator* lg, std::string const& config,
cmListFileBacktrace const& backtrace, std::string const& testName,
std::vector<std::string> const& command,
std::vector<std::string> const& buildDepends, BuildDependencies& info,
cmMakefile* mf)
{
if (this->Test == nullptr ||
!cmGeneratorExpression::IsValidTargetName(this->Test->GetName()) ||
cmGlobalGenerator::IsReservedTarget(this->Test->GetName())) {
if (!cmGeneratorExpression::IsValidTargetName(testName) ||
cmGlobalGenerator::IsReservedTarget(testName)) {
return false;
}
std::set<cmGeneratorTarget*> dependencies;
// Get dependencies from generator expressions
cmGeneratorExpression ge(*this->Test->GetMakefile()->GetCMakeInstance(),
this->Test->GetBacktrace());
for (std::string const& arg : this->Test->GetCommand()) {
cmGeneratorExpression ge(*mf->GetCMakeInstance(), backtrace);
std::string exe;
for (std::string const& arg : command) {
auto parsed = ge.Parse(arg);
parsed->Evaluate(lg, config);
std::string const& evaluated = parsed->Evaluate(lg, config);
if (exe.empty()) {
exe = evaluated;
}
for (cmGeneratorTarget* dep : parsed->GetTargets()) {
if (dep && !dep->IsImported()) {
dependencies.insert(dep);
@@ -140,8 +144,7 @@ bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg,
}
// Add target executed by test
if (!this->Test->GetCommand().empty()) {
std::string exe = this->Test->GetCommand().front();
if (!exe.empty()) {
cmGeneratorTarget* target = lg->FindGeneratorTargetToUse(exe);
if (target && target->GetType() == cm::TargetType::EXECUTABLE &&
!target->IsImported()) {
@@ -150,7 +153,7 @@ bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg,
}
// Add dependencies from BUILD_DEPENDS keyword
for (auto const& depName : this->Test->GetDependencies()) {
for (std::string const& depName : buildDepends) {
if (depName.empty()) {
continue;
}
@@ -164,14 +167,6 @@ bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg,
info.Files.push_back(std::move(file));
continue;
}
if (depTarget->IsImported()) {
lg->GetMakefile()->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Test \"", this->Test->GetName(), "\" DEPENDS target \"",
depName, "\" which is imported and cannot be built."),
this->Test->GetBacktrace());
return false;
}
dependencies.insert(depTarget);
}
@@ -183,6 +178,19 @@ bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg,
return true;
}
bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg,
std::string const& config,
BuildDependencies& info)
{
if (this->Test == nullptr) {
return false;
}
return EvaluateBuildDependencies(
lg, config, this->Test->GetBacktrace(), this->Test->GetName(),
this->Test->GetCommand(), this->Test->GetDependencies(), info,
this->Test->GetMakefile());
}
void cmTestGenerator::GenerateScriptActions(std::ostream& os, Indent indent)
{
if (this->ActionsPerConfig) {
+9 -1
View File
@@ -15,6 +15,7 @@ class cmListFileBacktrace;
class cmGeneratorExpression;
class cmGeneratorTarget;
class cmLocalGenerator;
class cmMakefile;
class cmTest;
/** \class cmTestGenerator
@@ -43,6 +44,13 @@ public:
std::vector<FileDependency> Files;
};
static bool EvaluateBuildDependencies(
cmLocalGenerator* lg, std::string const& config,
cmListFileBacktrace const& backtrace, std::string const& testName,
std::vector<std::string> const& command,
std::vector<std::string> const& buildDepends, BuildDependencies& info,
cmMakefile* mf);
cmTestGenerator(cmTest* test,
std::vector<std::string> const& configurations =
std::vector<std::string>());
@@ -51,7 +59,7 @@ public:
cmTestGenerator(cmTestGenerator const&) = delete;
cmTestGenerator& operator=(cmTestGenerator const&) = delete;
void Compute(cmLocalGenerator* lg);
virtual void Compute(cmLocalGenerator* lg);
bool GetBuildDependencies(cmLocalGenerator* lg, std::string const& config,
BuildDependencies& deps);
+1
View File
@@ -865,6 +865,7 @@ add_RunCMake_test(string
-DCMake_TEST_NO_64BIT_TIME=${CMake_TEST_NO_64BIT_TIME}
)
add_RunCMake_test(test_include_dirs)
add_RunCMake_test(TestDependency)
add_RunCMake_test(BundleUtilities)
if(APPLE)
add_RunCMake_test(INSTALL_NAME_DIR)
@@ -216,8 +216,15 @@ block()
set(RunCMake_TEST_BINARY_DIR "${out_of_date_build_dir}")
run_cmake(out-of-date-configure)
run_cmake_command(out-of-date-build "${CMAKE_COMMAND}" --build "${out_of_date_build_dir}" ${out_of_date_build_config_args})
# file_gen is created below, so file-test is initially Not Run.
set(RunCMake_TEST_EXPECT_RESULT 8)
set(RunCMake_TEST_EXPECT_stdout "file-test.*Not Run")
set(RunCMake_TEST_EXPECT_stderr "Unable to find build dependency: [^\n]*file_gen")
run_cmake_command(out-of-date-initial-test
"${CMAKE_CTEST_COMMAND}" ${out_of_date_ctest_config_args} -V --test-dir "${out_of_date_build_dir}")
unset(RunCMake_TEST_EXPECT_RESULT)
unset(RunCMake_TEST_EXPECT_stdout)
unset(RunCMake_TEST_EXPECT_stderr)
# Sleep for timestamp compare
execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1.125)
@@ -4,4 +4,4 @@ Test project .*
.*Start 3: shared-test.*
.*Start 4: module-test.*
.*Start 5: object-test.*
.*Start 6: file-test.*
.*Start 8: file-test.*
@@ -0,0 +1,16 @@
enable_language(CXX)
include(GoogleTest)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(fake_gtest fake_gtest.cpp)
gtest_discover_tests(
fake_gtest
TEST_PREFIX PREP:
TEST_FILTER basic*
EXTRA_ARGS how now "\"brown\" cow"
DISCOVERY_MODE POST_BUILD
)
@@ -492,6 +492,32 @@ function(run_GoogleTest_discovery_duplicate)
unset(RunCMake_TEST_OUTPUT_MERGE)
endfunction()
function(run_GoogleTest_build_depends)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-build-depends-build)
set(RunCMake_TEST_NO_CLEAN 1)
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
endif()
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
run_cmake(GoogleTestBuildDepends)
run_cmake_command(GoogleTest-build-depends-build
${CMAKE_COMMAND}
--build .
--config Debug
--target test_prep/all
)
run_cmake_command(GoogleTest-build-depends-test
${CMAKE_CTEST_COMMAND}
-C Debug
-R "^PREP:basic\\.case_(foo|bar)$"
--no-label-summary
)
endfunction()
foreach(DISCOVERY_MODE POST_BUILD PRE_TEST)
message(STATUS "Testing ${DISCOVERY_MODE} discovery mode via CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE global override...")
run_GoogleTest(${DISCOVERY_MODE})
@@ -546,3 +572,7 @@ if (NOT RunCMake_GENERATOR MATCHES "(Borland|NMake|Watcom)")
endif()
run_GoogleTest_discovery_duplicate()
if(RunCMake_GENERATOR MATCHES "Ninja|FASTBuild|Makefiles")
run_GoogleTest_build_depends()
endif()
@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 4.3)
project(TestDependencyAddTest C)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(fake_discovery fake_discovery.c)
# Ensure the timestamp check uses the artifact path, not the target name.
set_target_properties(fake_discovery PROPERTIES OUTPUT_NAME renamed_discovery)
add_test(NAME ADD_TEST.command_genex
COMMAND $<TARGET_FILE:fake_discovery> fake_discovery case_foo)
set_tests_properties(ADD_TEST.command_genex PROPERTIES
LABELS label_one)
add_test(NAME ADD_TEST.command_target
COMMAND "$<1:fake_discovery>" case_bar)
set_tests_properties(ADD_TEST.command_target PROPERTIES
LABELS label_two)
add_test(NAME ADD_TEST.build_depends
COMMAND ${CMAKE_COMMAND} -E true
BUILD_DEPENDS fake_discovery)
set_tests_properties(ADD_TEST.build_depends PROPERTIES
LABELS label_three)
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 4.3)
project(TestDependencyRunCMake NONE)
if(NOT DEFINED RunCMake_TEST)
message(FATAL_ERROR "RunCMake_TEST not set")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/${RunCMake_TEST}.cmake")
foreach(target IN ITEMS fake_discovery fake_gtest)
if(TARGET ${target})
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${target}-$<CONFIG>.txt"
CONTENT "$<TARGET_FILE:${target}>")
endif()
endforeach()
@@ -0,0 +1,13 @@
project(TestDependencyDirectoryGeneratedFile NONE)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
set(output "${CMAKE_CURRENT_BINARY_DIR}/directory-generated.txt")
add_custom_command(OUTPUT "${output}"
COMMAND "${CMAKE_COMMAND}" -E touch "${output}"
VERBATIM)
add_custom_target(directory_generated_owner DEPENDS "${output}")
set_property(DIRECTORY PROPERTY CMAKE_TEST_BUILD_DEPENDS "${output}")
add_test(NAME DirectoryGeneratedFileBuilt
COMMAND "${CMAKE_COMMAND}" -E compare_files "${output}" "${output}")
@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 4.3)
project(TestDependencyDiscoverGeneric C)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(fake_discovery fake_discovery.c)
discover_tests(
COMMAND fake_discovery
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^([^,]+),([^,]+)$"
TEST_NAME "DISCOVER.\\1"
TEST_ARGS "\\1"
TEST_PROPERTIES
LABELS "\\2"
)
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 4.3)
project(TestDependencyDiscoverGenericBuildDepends C)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(fake_discovery fake_discovery.c)
discover_tests(
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/shared/fake_discovery_wrapper.cmake
"$<TARGET_FILE:fake_discovery>"
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^([^,]+),([^,]+)$"
TEST_NAME "DISCOVER.\\1"
TEST_ARGS "\\1"
TEST_PROPERTIES
LABELS "\\2"
BUILD_DEPENDS
fake_discovery
)
@@ -0,0 +1,32 @@
project(TestDependencyDiscoverCommands C)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(command_genex EXCLUDE_FROM_ALL fake_discovery.c)
add_executable(command_wrapper EXCLUDE_FROM_ALL fake_discovery.c)
add_executable(command_name EXCLUDE_FROM_ALL fake_discovery.c)
discover_tests(COMMAND "$<TARGET_FILE:command_genex>"
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^([^,]+),([^,]+)$"
TEST_NAME "GENEX.\\1"
TEST_ARGS "\\1")
discover_tests(COMMAND "${CMAKE_COMMAND}" -P
"${CMAKE_CURRENT_SOURCE_DIR}/shared/fake_discovery_wrapper.cmake"
"$<TARGET_FILE:command_wrapper>"
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^([^,]+),([^,]+)$"
TEST_NAME "WRAPPER.\\1"
TEST_ARGS "\\1")
discover_tests(COMMAND command_forward
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^([^,]+),([^,]+)$"
TEST_NAME "FORWARD.\\1"
TEST_ARGS "\\1")
discover_tests(COMMAND "$<1:command_name>"
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^([^,]+),([^,]+)$"
TEST_NAME "NAME.\\1"
TEST_ARGS "\\1")
add_executable(command_forward EXCLUDE_FROM_ALL fake_discovery.c)
@@ -0,0 +1,19 @@
project(TestDependencyDiscoverGeneratedFile C)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(fake_discovery EXCLUDE_FROM_ALL fake_discovery.c)
set(output "${CMAKE_CURRENT_BINARY_DIR}/generated.txt")
add_custom_command(OUTPUT "${output}"
COMMAND "${CMAKE_COMMAND}" -E touch "${output}"
VERBATIM)
add_custom_target(generated_owner DEPENDS "${output}")
discover_tests(COMMAND fake_discovery
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^([^,]+),([^,]+)$"
TEST_NAME "GENERATED.\\1"
TEST_ARGS "\\1"
BUILD_DEPENDS "${output}")
add_test(NAME GeneratedFileBuilt
COMMAND "${CMAKE_COMMAND}" -E compare_files "${output}" "${output}")
@@ -0,0 +1,19 @@
project(TestDependencyDiscoverUtility C)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(fake_discovery EXCLUDE_FROM_ALL fake_discovery.c)
add_custom_target(discovery_utility
COMMAND "${CMAKE_COMMAND}" -E touch "${CMAKE_CURRENT_BINARY_DIR}/utility-built.txt"
VERBATIM)
discover_tests(COMMAND fake_discovery
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^([^,]+),([^,]+)$"
TEST_NAME "UTILITY.\\1"
TEST_ARGS "\\1"
BUILD_DEPENDS discovery_utility)
add_test(NAME UtilityBuilt
COMMAND "${CMAKE_COMMAND}" -E compare_files
"${CMAKE_CURRENT_BINARY_DIR}/utility-built.txt"
"${CMAKE_CURRENT_BINARY_DIR}/utility-built.txt")
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 4.3)
project(TestDependencyDiscoverPostBuild CXX)
include(GoogleTest)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(fake_gtest fake_gtest_post_build.cpp)
gtest_discover_tests(
fake_gtest
TEST_PREFIX POST:
TEST_FILTER basic*
DISCOVERY_MODE POST_BUILD
)
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 4.3)
project(TestDependencyGoogleTestPreTest CXX)
include(GoogleTest)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(fake_gtest fake_gtest_post_build.cpp)
gtest_discover_tests(
fake_gtest
TEST_PREFIX PRE:
TEST_FILTER basic*
DISCOVERY_MODE PRE_TEST
)
@@ -0,0 +1,13 @@
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_test(NAME Direct
COMMAND "${CMAKE_COMMAND}" -E true
BUILD_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dependency.txt")
discover_tests(
COMMAND "${CMAKE_COMMAND}" -E echo
DISCOVERY_ARGS Discovered
DISCOVERY_MATCH "Discovered"
TEST_NAME Discovered
TEST_ARGS Discovered
BUILD_DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/dependency.txt")
@@ -1,4 +1,4 @@
CMake Warning at TestDependency-ON-byproduct\.cmake:[0-9]+ \(add_test\):
CMake Warning at ON-byproduct\.cmake:[0-9]+ \(add_test\):
Test "ByproductTest" BUILD_DEPENDS file
.*/TestDependencyByproduct-built\.txt
@@ -5,7 +5,7 @@ project(TestDependencyByproduct C)
enable_testing()
add_executable(TestDependencyByproductExe main.c)
add_executable(TestDependencyByproductExe ../add_test/main.c)
add_custom_command(TARGET TestDependencyByproductExe POST_BUILD
COMMAND
"${CMAKE_COMMAND}" -E touch
@@ -1,4 +1,4 @@
CMake Warning at TestDependency-ON-colon-name\.cmake:[0-9]+ \(add_test\):
CMake Warning at ON-colon-name\.cmake:[0-9]+ \(add_test\):
Test "Foo::Bar" has a name containing ':', which cannot be used as a
Makefile build target\. No "test_prep/" target will be generated for it\.
Use the Ninja generator or rename the test to build its dependencies with a
@@ -5,7 +5,7 @@ project(TestDependencyFileGenerateConfig C)
enable_testing()
add_executable(TestDependencyGenexFileGenerate main.c)
add_executable(TestDependencyGenexFileGenerate ../add_test/main.c)
set(rc_file
"${CMAKE_CURRENT_BINARY_DIR}/gen/TestDependencyGenexFileGenerate/$<CONFIG>/version.rc")
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 4.3)
set(CMAKE_TEST_BUILD_DEPENDS ON)
project(TestDependencyImported C)
enable_testing()
add_executable(TestDependencyImportedExe ../add_test/main.c)
add_executable(TestDependencyImportedTool IMPORTED)
set_target_properties(TestDependencyImportedTool PROPERTIES
IMPORTED_LOCATION "${CMAKE_COMMAND}")
add_test(NAME ImportedTest
COMMAND TestDependencyImportedExe
BUILD_DEPENDS TestDependencyImportedTool)
@@ -5,7 +5,7 @@ project(TestDependencyInterface C)
enable_testing()
add_executable(TestDependencyInterfaceExe main.c)
add_executable(TestDependencyInterfaceExe ../add_test/main.c)
# Header-only INTERFACE library is not part of the build system and must be
# filtered out of the test_prep dependencies so that no dead
@@ -1,5 +1,5 @@
CMake Error at TestDependency-ON-invalid-test-name\.cmake:[0-9]+ \(add_test\):
CMake Error at ON-invalid-test-name\.cmake:[0-9]+ \(add_test\):
add_test Cannot set build dependencies for a test with NAME "Target Build
Test Invalid Name" which is not a valid target name\.
Call Stack \(most recent call first\):
CMakeLists\.txt:3 \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
@@ -1,4 +1,4 @@
CMake Warning at TestDependency-ON-orphan\.cmake:[0-9]+ \(add_test\):
CMake Warning at ON-orphan\.cmake:[0-9]+ \(add_test\):
Test "OrphanTest" BUILD_DEPENDS file
.*/TestDependencyOrphan-built\.txt
@@ -0,0 +1,18 @@
project(TestDependencyObjectLibrary C)
enable_testing()
set(CMAKE_TEST_BUILD_DEPENDS ON)
add_executable(fake_discovery EXCLUDE_FROM_ALL fake_discovery.c)
add_library(dependency_objects OBJECT EXCLUDE_FROM_ALL fake_discovery.c)
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/dependency_objects-$<CONFIG>.txt"
CONTENT "$<TARGET_OBJECTS:dependency_objects>")
add_test(NAME OBJECT.direct COMMAND "${CMAKE_COMMAND}" -E true
BUILD_DEPENDS dependency_objects)
discover_tests(COMMAND fake_discovery
DISCOVERY_ARGS --list_tests
DISCOVERY_MATCH "^([^,]+),([^,]+)$"
TEST_NAME "OBJECT.\\1"
TEST_ARGS "\\1"
BUILD_DEPENDS dependency_objects)
@@ -0,0 +1,289 @@
include(RunCMake)
# Verify test_prep behavior
function(run_testdependency_case CASE_NAME EXPECT_PRESENT)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE_NAME}-build)
# 1. Configure
run_cmake(${CASE_NAME})
set(RunCMake_TEST_NO_CLEAN 1)
# 2. Verify build targets exist
run_cmake_command(${CASE_NAME}-check
${CMAKE_COMMAND}
-DRunCMake_TEST_BINARY_DIR=${RunCMake_TEST_BINARY_DIR}
-Dexpect_present=${EXPECT_PRESENT}
-P ${RunCMake_SOURCE_DIR}/check-targets.cmake)
# If build targets enabled...
if (EXPECT_PRESENT)
# Build test_prep target
run_cmake_command(${CASE_NAME}-build
${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG}
--target test_prep/TargetBuildTest)
# Verify building test_prep target built test dependencies
run_cmake_command(${CASE_NAME}-build-check
${CMAKE_COMMAND}
-DRunCMake_TEST_BINARY_DIR=${RunCMake_TEST_BINARY_DIR}
-P ${RunCMake_SOURCE_DIR}/build-check.cmake)
endif()
unset(RunCMake_TEST_NO_CLEAN)
endfunction()
# Ensure generated test file includes BUILD_DEPENDs
function(check_discovered_build_depends CASE_NAME TESTS_FILE)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(${CASE_NAME}-build-depends
${CMAKE_COMMAND}
-Dtests_file=${TESTS_FILE}
-Ddependency_manifest=${TestDependency_DEPENDENCY_MANIFEST}
-P ${RunCMake_SOURCE_DIR}/check-discovered-build-depends.cmake)
unset(RunCMake_TEST_NO_CLEAN)
endfunction()
# Find discovered test file for GTest
function(find_discovered_tests_file OUT_VAR BINARY_DIR TARGET_NAME)
file(GLOB discovered_tests_files
"${BINARY_DIR}/${TARGET_NAME}*_tests.cmake")
list(FILTER discovered_tests_files EXCLUDE REGEX [[.*_include\.cmake$]])
list(LENGTH discovered_tests_files discovered_tests_files_length)
if(NOT discovered_tests_files_length EQUAL 1)
message(FATAL_ERROR
"Expected exactly one discovered tests file for ${TARGET_NAME}, got: ${discovered_tests_files}")
endif()
set(${OUT_VAR} "${discovered_tests_files}" PARENT_SCOPE)
endfunction()
# Verify ctest --out-of-date behavior
function(run_discover_out_of_date_case CASE_NAME TARGET_NAME EXPECTED_TESTS_REGEX)
cmake_parse_arguments(arg
"ADD_TEST;GTEST"
""
""
${ARGN})
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${CASE_NAME}-build)
# 1. Configure
run_cmake(${CASE_NAME})
set(RunCMake_TEST_NO_CLEAN 1)
# 2. Build dependencies needed for the tests.
run_cmake_command(${CASE_NAME}-build
${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG}
--target test_prep/all)
# 3. Run ctest once, including any PRE_TEST discovery.
# Ensure test stamps are newer than build dependencies even on filesystems
# with one-second timestamp resolution. Equal timestamps are out of date.
run_cmake_command(${CASE_NAME}-build-sleep
${CMAKE_COMMAND} -E sleep 1.1)
set(RunCMake_TEST_EXPECT_stderr ".*")
run_cmake_command(${CASE_NAME}-initial
${CMAKE_CTEST_COMMAND} ${TestDependency_CTEST_CONFIG_ARG})
# 4. Check that generated files include dependencies.
if(arg_GTEST)
find_discovered_tests_file(discovered_tests_file
${RunCMake_TEST_BINARY_DIR} ${TARGET_NAME})
check_discovered_build_depends(${CASE_NAME} ${discovered_tests_file})
else()
set(discovered_tests_file
"${RunCMake_TEST_BINARY_DIR}/CTestTestfile.cmake")
check_discovered_build_depends(${CASE_NAME} ${discovered_tests_file})
endif()
if(TestDependency_DEPENDENCY_MANIFEST)
set(RunCMake_TEST_EXPECT_stdout "Total Tests: 0")
run_cmake_command(${CASE_NAME}-up-to-date
${CMAKE_CTEST_COMMAND} ${TestDependency_CTEST_CONFIG_ARG} --out-of-date -N)
unset(RunCMake_TEST_EXPECT_stdout)
endif()
# 5. Touch the actual generated artifact, after the last test timestamp.
file(READ "${RunCMake_TEST_BINARY_DIR}/${TARGET_NAME}-${TestDependency_CONFIG}.txt"
dependent_files)
foreach(dependent_file IN LISTS dependent_files)
if(NOT EXISTS "${dependent_file}")
message(FATAL_ERROR "Dependency was not built: ${dependent_file}")
endif()
endforeach()
run_cmake_command(${CASE_NAME}-sleep
${CMAKE_COMMAND} -E sleep 1.1)
run_cmake_command(${CASE_NAME}-touch
${CMAKE_COMMAND} -E touch ${dependent_files})
# 6. Run ctest --out-of-date and ensure test reruns
set(RunCMake_TEST_EXPECT_stdout
"${EXPECTED_TESTS_REGEX}")
run_cmake_command(${CASE_NAME}-out-of-date
${CMAKE_CTEST_COMMAND} ${TestDependency_CTEST_CONFIG_ARG} --out-of-date)
unset(RunCMake_TEST_NO_CLEAN)
unset(RunCMake_TEST_EXPECT_stderr)
unset(RunCMake_TEST_EXPECT_stdout)
endfunction()
if(RunCMake_GENERATOR MATCHES "Ninja|FASTBuild|Makefiles")
block()
# Use the same configuration for generated manifests, builds, and CTest,
# including single-config generators whose default build type varies.
set(TestDependency_CONFIG Debug)
set(TestDependency_BUILD_CONFIG_ARG --config ${TestDependency_CONFIG})
set(TestDependency_CTEST_CONFIG_ARG -C ${TestDependency_CONFIG})
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=${TestDependency_CONFIG})
endif()
# Don't run tests with missing dependencies
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/MissingBuildDependency-build)
run_cmake(MissingBuildDependency)
set(RunCMake_TEST_NO_CLEAN 1)
block()
set(RunCMake_TEST_EXPECT_RESULT 8)
set(RunCMake_TEST_EXPECT_stdout "Direct[^\n]*Not Run.*Discovered[^\n]*Not Run")
set(RunCMake_TEST_EXPECT_stderr "Unable to find build dependency: [^\n]*dependency.txt")
run_cmake_command(MissingBuildDependency-test
${CMAKE_CTEST_COMMAND} ${TestDependency_CTEST_CONFIG_ARG})
run_cmake_command(MissingBuildDependency-out-of-date
${CMAKE_CTEST_COMMAND} ${TestDependency_CTEST_CONFIG_ARG} --out-of-date)
endblock()
file(WRITE "${RunCMake_TEST_BINARY_DIR}/dependency.txt" "ready\n")
set(RunCMake_TEST_EXPECT_stdout "100% tests passed out of 2")
run_cmake_command(MissingBuildDependency-available
${CMAKE_CTEST_COMMAND} ${TestDependency_CTEST_CONFIG_ARG} --out-of-date)
# Missing dependencies make tests out of date even with existing stamps.
file(REMOVE "${RunCMake_TEST_BINARY_DIR}/dependency.txt")
set(RunCMake_TEST_EXPECT_RESULT 8)
set(RunCMake_TEST_EXPECT_stdout "Direct[^\n]*Not Run.*Discovered[^\n]*Not Run")
set(RunCMake_TEST_EXPECT_stderr "Unable to find build dependency: [^\n]*dependency.txt")
run_cmake_command(MissingBuildDependency-removed
${CMAKE_CTEST_COMMAND} ${TestDependency_CTEST_CONFIG_ARG} --out-of-date)
endblock()
# "Failure" cases, do not generate test dependencies
block()
run_testdependency_case(DEFAULT FALSE)
run_testdependency_case(OFF FALSE)
run_cmake(ON-invalid-test-name)
endblock()
# A header-only INTERFACE library dependency is filtered out by the
# generator-independent dependency resolution, so building the
# test_prep target must not fail on a missing rule.
block()
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/ON-interface-build)
run_cmake(ON-interface)
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_TEST_OUTPUT_MERGE 1)
run_cmake_command(ON-interface-build
${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG}
--target test_prep/InterfaceTest)
unset(RunCMake_TEST_OUTPUT_MERGE)
endblock()
# Imported dependencies are accepted without requiring a build rule.
block()
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/ON-imported-build)
run_cmake(ON-imported)
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_TEST_OUTPUT_MERGE 1)
run_cmake_command(ON-imported-build
${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG}
--target test_prep/ImportedTest)
unset(RunCMake_TEST_OUTPUT_MERGE)
run_cmake_command(ON-imported-test
${CMAKE_CTEST_COMMAND} ${TestDependency_CTEST_CONFIG_ARG}
-R "^ImportedTest$")
endblock()
# Exceptions for Makefile generator
if(RunCMake_GENERATOR MATCHES Makefiles)
# Diagnostics specific to the Makefile generators.
block()
# A ':' in a test name cannot be expressed as a Makefile target.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/ON-colon-name-build)
run_cmake(ON-colon-name)
endblock()
block()
# A byproduct file cannot be built through a single owning target.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/ON-byproduct-build)
run_cmake(ON-byproduct)
endblock()
block()
# A custom-command output owned by no target cannot be built.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/ON-orphan-build)
run_cmake(ON-orphan)
endblock()
endif()
# Verify successful behavior
block()
foreach(case IN ITEMS DiscoverTestsUtility DiscoverTestsCommands
DiscoverTestsGeneratedFile DirectoryGeneratedFile)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
run_cmake(${case})
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(${case}-build
${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG}
--target test_prep/all)
set(RunCMake_TEST_EXPECT_stdout "100% tests passed")
run_cmake_command(${case}-test
${CMAKE_CTEST_COMMAND} ${TestDependency_CTEST_CONFIG_ARG})
unset(RunCMake_TEST_EXPECT_stdout)
unset(RunCMake_TEST_NO_CLEAN)
endforeach()
if(RunCMake_GENERATOR STREQUAL "Ninja Multi-Config")
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/ON-file-generate-config-build)
run_cmake(ON-file-generate-config)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(ON-file-generate-config-build
${CMAKE_COMMAND} --build . --config Debug
--target test_prep/FileGenerateConfigTest)
run_cmake_command(ON-file-generate-config-test
${CMAKE_CTEST_COMMAND} -C Debug -R "^FileGenerateConfigTest$")
unset(RunCMake_TEST_NO_CLEAN)
endif()
block()
set(TestDependency_DEPENDENCY_MANIFEST
"${RunCMake_BINARY_DIR}/ObjectLibrary-build/dependency_objects-${TestDependency_CONFIG}.txt")
run_discover_out_of_date_case(ObjectLibrary dependency_objects
"OBJECT\\.direct.*OBJECT\\.case_foo.*OBJECT\\.case_bar")
endblock()
# Verify test_depends
run_testdependency_case(ON TRUE)
# Verify test_depends + --out-of-date for add_test
run_discover_out_of_date_case(AddTest fake_discovery
"ADD_TEST\\.command_genex.*ADD_TEST\\.command_target.*ADD_TEST\\.build_depends"
ADD_TEST)
# Verify test_depends + --out-of-date for discover_tests
run_discover_out_of_date_case(DiscoverTests fake_discovery
"DISCOVER\\.case_foo.*DISCOVER\\.case_bar")
# Verify explicit BUILD_DEPENDS for discover_tests
run_discover_out_of_date_case(DiscoverTestsBuildDepends fake_discovery
"DISCOVER\\.case_foo.*DISCOVER\\.case_bar")
# Verify test_depends + --out-of-date for gtest_discover_tests
run_discover_out_of_date_case(GoogleTest-PRE_TEST fake_gtest
"PRE:basic\\.case_foo.*PRE:basic\\.case_bar"
GTEST)
run_discover_out_of_date_case(GoogleTest-POST_BUILD fake_gtest
"POST:basic\\.case_foo.*POST:basic\\.case_bar"
GTEST)
endblock()
endblock()
endif()
@@ -2,7 +2,7 @@ project(TestDependency C)
enable_testing()
add_executable(TestDependencyExe main.c)
add_executable(TestDependencyExe ../add_test/main.c)
add_custom_command(TARGET TestDependencyExe POST_BUILD
COMMAND
"${CMAKE_COMMAND}" -E touch
@@ -10,7 +10,7 @@ add_custom_command(TARGET TestDependencyExe POST_BUILD
BYPRODUCTS
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyExe-built.txt")
add_executable(TestDependencyGenex main.c)
add_executable(TestDependencyGenex ../add_test/main.c)
add_custom_command(TARGET TestDependencyGenex POST_BUILD
COMMAND
"${CMAKE_COMMAND}" -E touch
@@ -0,0 +1,33 @@
if(NOT DEFINED tests_file)
message(FATAL_ERROR "tests_file not set")
endif()
if(NOT EXISTS "${tests_file}")
message(FATAL_ERROR "Discovered tests file not found: ${tests_file}")
endif()
file(READ "${tests_file}" content)
if(NOT content MATCHES "_CMAKE_TEST_BUILD_DEPENDS"
AND NOT content MATCHES "BUILD_DEPENDS")
message(FATAL_ERROR
"Expected discovered tests to contain BUILD_DEPENDS metadata.")
endif()
if(dependency_manifest)
file(READ "${dependency_manifest}" dependencies)
file(STRINGS "${tests_file}" metadata REGEX "BUILD_DEPENDS")
foreach(dependency IN LISTS dependencies)
set(count 0)
foreach(line IN LISTS metadata)
string(FIND "${line}" "${dependency}" pos)
if(NOT pos EQUAL -1)
math(EXPR count "${count} + 1")
endif()
endforeach()
# Both add_test and discover_tests must record the object, not its target name.
if(count LESS 2)
message(FATAL_ERROR "Missing object file dependency metadata: ${dependency}")
endif()
endforeach()
endif()
@@ -16,7 +16,7 @@ file(GLOB build_files LIST_DIRECTORIES false
set(found FALSE)
foreach(build_file IN LISTS build_files)
file(READ "${build_file}" content)
if(content MATCHES "test_prep.all|test_prep.TargetBuildTest")
if(content MATCHES "test_prep.all|test_prep.TargetBuildTest|test_prep.directory_")
set(found TRUE)
break()
endif()
@@ -0,0 +1,13 @@
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
if (argc >= 2 && strcmp(argv[1], "--list_tests") == 0) {
puts("case_foo,label_one");
puts("case_bar,label_two");
return 0;
}
return 0;
}
@@ -0,0 +1,58 @@
#include <fstream>
#include <iostream>
#include <string>
int main(int argc, char** argv)
{
if (argc >= 2 && std::string(argv[1]) == "--gtest_list_tests") {
bool saw_json = false;
bool saw_basic_filter = false;
for (int i = 2; i < argc; ++i) {
std::string const arg = argv[i];
if (arg.find("--gtest_output=json:") == 0) {
saw_json = true;
} else if (arg == "--gtest_filter=basic*") {
saw_basic_filter = true;
} else {
return 1;
}
}
if (saw_json && argc >= 3) {
for (int i = 2; i < argc; ++i) {
std::string const arg = argv[i];
if (arg.find("--gtest_output=json:") == 0) {
std::string const json_path = arg.substr(20);
std::ofstream json(json_path.c_str());
json << "{\n"
" \"name\": \"AllTests\",\n"
" \"testsuites\": [\n"
" {\n"
" \"name\": \"basic\",\n"
" \"tests\": 2,\n"
" \"testsuite\": [\n"
" { \"name\": \"case_foo\", \"file\": \"file1.cpp\", "
"\"line\": 1 },\n"
" { \"name\": \"case_bar\", \"file\": \"file1.cpp\", "
"\"line\": 2 }\n"
" ]\n"
" }\n"
" ]\n"
"}\n";
break;
}
}
}
if (!saw_basic_filter && argc >= 3) {
return 1;
}
std::cout << "basic.\n";
std::cout << " case_foo\n";
std::cout << " case_bar\n";
return 0;
}
return 0;
}
@@ -0,0 +1,10 @@
set(args)
math(EXPR last_arg_index "${CMAKE_ARGC} - 1")
foreach(arg_index RANGE 4 ${last_arg_index})
list(APPEND args "${CMAKE_ARGV${arg_index}}")
endforeach()
execute_process(
COMMAND "${CMAKE_ARGV3}" ${args}
COMMAND_ERROR_IS_FATAL ANY
)
@@ -61,95 +61,3 @@ block()
run_cmake_command(TestLauncher-test ${CMAKE_CTEST_COMMAND} -C Debug -V)
endblock()
unset(RunCMake_TEST_OPTIONS)
function(run_testdependency_case CASE_NAME EXPECT_PRESENT)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TestDependency-${CASE_NAME}-build)
run_cmake(TestDependency-${CASE_NAME})
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(TestDependency-${CASE_NAME}-check
${CMAKE_COMMAND}
-DRunCMake_TEST_BINARY_DIR=${RunCMake_TEST_BINARY_DIR}
-Dexpect_present=${EXPECT_PRESENT}
-P ${RunCMake_SOURCE_DIR}/TestDependency-check-targets.cmake)
unset(RunCMake_TEST_NO_CLEAN)
endfunction()
if(RunCMake_GENERATOR MATCHES "Ninja|FASTBuild|Makefiles")
block()
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(TestDependency_BUILD_CONFIG_ARG --config Debug)
else()
set(TestDependency_BUILD_CONFIG_ARG)
endif()
run_testdependency_case(DEFAULT FALSE)
run_testdependency_case(OFF FALSE)
run_cmake(TestDependency-ON-invalid-test-name)
block()
# A header-only INTERFACE library dependency is filtered out by the
# generator-independent dependency resolution, so building the
# test_prep target must not fail on a missing rule.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/TestDependency-ON-interface-build)
run_cmake(TestDependency-ON-interface)
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_TEST_OUTPUT_MERGE 1)
run_cmake_command(TestDependency-ON-interface-build
${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG}
--target test_prep/InterfaceTest)
endblock()
if(RunCMake_GENERATOR MATCHES Makefiles)
# Diagnostics specific to the Makefile generators.
block()
# A ':' in a test name cannot be expressed as a Makefile target.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/TestDependency-ON-colon-name-build)
run_cmake(TestDependency-ON-colon-name)
endblock()
block()
# A byproduct file cannot be built through a single owning target.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/TestDependency-ON-byproduct-build)
run_cmake(TestDependency-ON-byproduct)
endblock()
block()
# A custom-command output owned by no target cannot be built.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/TestDependency-ON-orphan-build)
run_cmake(TestDependency-ON-orphan)
endblock()
endif()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TestDependency-ON-build)
run_testdependency_case(ON TRUE)
if(RunCMake_GENERATOR MATCHES "Ninja" AND RunCMake_GENERATOR_IS_MULTI_CONFIG)
block()
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/TestDependency-ON-file-generate-config-build)
run_cmake(TestDependency-ON-file-generate-config)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(TestDependency-ON-file-generate-config-build
${CMAKE_COMMAND} --build . --config Debug
--target test_prep/FileGenerateConfigTest)
endblock()
endif()
set(RunCMake_TEST_OUTPUT_MERGE 1)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(TestDependency-ON-all
${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG}
--target test_prep/all)
run_cmake_command(TestDependency-ON-build
${CMAKE_COMMAND} --build . ${TestDependency_BUILD_CONFIG_ARG}
--target test_prep/TargetBuildTest)
run_cmake_command(TestDependency-ON-build-check
${CMAKE_COMMAND}
-DRunCMake_TEST_BINARY_DIR=${RunCMake_TEST_BINARY_DIR}
-P ${RunCMake_SOURCE_DIR}/TestDependency-build-check.cmake)
unset(RunCMake_TEST_OUTPUT_MERGE)
unset(RunCMake_TEST_NO_CLEAN)
endblock()
endif()