mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-04 14:50:23 +00:00
Ninja: Add Build Dependencies for Tests
Add `test_prep/<test_name>` convenience targets to the ninja generator to build dependencies of tests, and `test_prep/all` to build the dependencies of all tests. Build dependencies of a test include: - Executables directly invoked by test `COMMAND`. - Targets mentioned in generator expressionf os test `COMMAND`. - Anything explicitly listed with the new `BUILD_DEPENDS` argument of `add_test`. Issue: #27613
This commit is contained in:
@@ -8,7 +8,8 @@ Add a test to the project to be run by :manual:`ctest(1)`.
|
|||||||
add_test(NAME <name> COMMAND <command> [<arg>...]
|
add_test(NAME <name> COMMAND <command> [<arg>...]
|
||||||
[CONFIGURATIONS <config>...]
|
[CONFIGURATIONS <config>...]
|
||||||
[WORKING_DIRECTORY <dir>]
|
[WORKING_DIRECTORY <dir>]
|
||||||
[COMMAND_EXPAND_LISTS])
|
[COMMAND_EXPAND_LISTS]
|
||||||
|
[BUILD_DEPENDS <dependencies>...])
|
||||||
|
|
||||||
Adds a test called ``<name>``. The test name may contain arbitrary
|
Adds a test called ``<name>``. The test name may contain arbitrary
|
||||||
characters, expressed as a :ref:`Quoted Argument` or :ref:`Bracket Argument`
|
characters, expressed as a :ref:`Quoted Argument` or :ref:`Bracket Argument`
|
||||||
@@ -59,6 +60,25 @@ directory the test is created in.
|
|||||||
|
|
||||||
<launcher> <emulator> <command>
|
<launcher> <emulator> <command>
|
||||||
|
|
||||||
|
* .. versionadded:: 4.4
|
||||||
|
|
||||||
|
When the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is enabled,
|
||||||
|
the :ref:`Ninja Generators` generate a convenience build target named
|
||||||
|
``test_prep/<name>`` that depends on the test executable target. Building
|
||||||
|
this target ensures the executable is up-to-date before the test runs.
|
||||||
|
|
||||||
|
Additionally, targets referenced by the test command via generator
|
||||||
|
expressions are added as dependencies of the ``test_prep/<name>`` target.
|
||||||
|
|
||||||
|
If multiple tests in different directories share the same name, their
|
||||||
|
dependencies are merged into a single ``test_prep/<name>`` target.
|
||||||
|
|
||||||
|
Tests with names that are not valid target names are excluded from this
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
The ``BUILD_DEPENDS`` keyword can be used to add explicit build
|
||||||
|
dependencies.
|
||||||
|
|
||||||
The command may be specified using
|
The command may be specified using
|
||||||
:manual:`generator expressions <cmake-generator-expressions(7)>`.
|
:manual:`generator expressions <cmake-generator-expressions(7)>`.
|
||||||
|
|
||||||
@@ -71,6 +91,15 @@ directory the test is created in.
|
|||||||
:variable:`CMAKE_CURRENT_BINARY_DIR`. The working directory may be specified
|
:variable:`CMAKE_CURRENT_BINARY_DIR`. The working directory may be specified
|
||||||
using :manual:`generator expressions <cmake-generator-expressions(7)>`.
|
using :manual:`generator expressions <cmake-generator-expressions(7)>`.
|
||||||
|
|
||||||
|
``BUILD_DEPENDS``
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
|
Specify a list of targets or files that must be built before the test can
|
||||||
|
run. Each dependency is added to the ``test_prep/<name>`` build target
|
||||||
|
described above when :variable:`CMAKE_TEST_BUILD_DEPENDS` is enabled
|
||||||
|
with the :ref:`Ninja Generators`. The test name must be a valid target name
|
||||||
|
in order to list build dependencies with this keyword.
|
||||||
|
|
||||||
``COMMAND_EXPAND_LISTS``
|
``COMMAND_EXPAND_LISTS``
|
||||||
.. versionadded:: 3.16
|
.. versionadded:: 3.16
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,26 @@ Builtin Targets
|
|||||||
Created only if the :prop_gbl:`INSTALL_PARALLEL` global property is ``ON``.
|
Created only if the :prop_gbl:`INSTALL_PARALLEL` global property is ``ON``.
|
||||||
Runs the install step for each subdirectory independently and in parallel.
|
Runs the install step for each subdirectory independently and in parallel.
|
||||||
|
|
||||||
|
``test_prep/<test-name>``
|
||||||
|
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
|
Created only if the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is
|
||||||
|
enabled. Builds all known build dependencies for the named test, including
|
||||||
|
the executable target invoked by the test, targets referenced by generator
|
||||||
|
expressions in the test command, and explicit ``BUILD_DEPENDS`` entries.
|
||||||
|
|
||||||
|
Tests with names that 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.
|
||||||
|
|
||||||
|
``test_prep/all``
|
||||||
|
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
|
Created only if the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is
|
||||||
|
enabled. Depends on every generated ``test_prep/<test-name>`` target.
|
||||||
|
|
||||||
For each subdirectory ``sub/dir`` of the project, additional targets
|
For each subdirectory ``sub/dir`` of the project, additional targets
|
||||||
are generated:
|
are generated:
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ Variables that Provide Information
|
|||||||
/variable/CMAKE_Swift_MODULE_DIRECTORY
|
/variable/CMAKE_Swift_MODULE_DIRECTORY
|
||||||
/variable/CMAKE_Swift_NUM_THREADS
|
/variable/CMAKE_Swift_NUM_THREADS
|
||||||
/variable/CMAKE_Swift_SEPARATE_MODULE_EMISSION
|
/variable/CMAKE_Swift_SEPARATE_MODULE_EMISSION
|
||||||
|
/variable/CMAKE_TEST_BUILD_DEPENDS
|
||||||
/variable/CMAKE_TEST_LAUNCHER
|
/variable/CMAKE_TEST_LAUNCHER
|
||||||
/variable/CMAKE_TOOLCHAIN_FILE
|
/variable/CMAKE_TOOLCHAIN_FILE
|
||||||
/variable/CMAKE_TWEAK_VERSION
|
/variable/CMAKE_TWEAK_VERSION
|
||||||
|
|||||||
15
Help/release/dev/test-dependency-targets.rst
Normal file
15
Help/release/dev/test-dependency-targets.rst
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
test-build-dependencies
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
* The :ref:`Ninja Generators` now support generating a build target named
|
||||||
|
``test_prep/<test-name>`` for each test added by :command:`add_test`, which
|
||||||
|
builds all dependencies for that test. Build dependencies are registered for
|
||||||
|
an executable target invoked by the test, targets referenced in generator
|
||||||
|
expressions in the test command, and explicit dependencies added using the
|
||||||
|
new ``BUILD_DEPENDS`` option. This behavior is enabled by the new
|
||||||
|
:variable:`CMAKE_TEST_BUILD_DEPENDS` variable. A ``test_prep/all``
|
||||||
|
target is also generated to depend on every ``test_prep/<test-name>``
|
||||||
|
target. Tests with names that 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 one ``test_prep/<test-name>``
|
||||||
|
target.
|
||||||
17
Help/variable/CMAKE_TEST_BUILD_DEPENDS.rst
Normal file
17
Help/variable/CMAKE_TEST_BUILD_DEPENDS.rst
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
CMAKE_TEST_BUILD_DEPENDS
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
.. versionadded:: 4.4
|
||||||
|
|
||||||
|
Enable ``test_prep/<name>`` build targets for tests added by
|
||||||
|
:command:`add_test` when using the :ref:`Ninja Generators`.
|
||||||
|
|
||||||
|
When this variable is set to a true value, CMake generates a
|
||||||
|
``test_prep/<name>`` target for each eligible test and a ``test_prep/all``
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <cm/memory>
|
#include <cm/memory>
|
||||||
|
|
||||||
#include "cmExecutionStatus.h"
|
#include "cmExecutionStatus.h"
|
||||||
|
#include "cmGeneratorExpression.h"
|
||||||
#include "cmMakefile.h"
|
#include "cmMakefile.h"
|
||||||
#include "cmPolicies.h"
|
#include "cmPolicies.h"
|
||||||
#include "cmStringAlgorithms.h"
|
#include "cmStringAlgorithms.h"
|
||||||
@@ -90,6 +91,7 @@ bool cmAddTestCommandHandleNameMode(std::vector<std::string> const& args,
|
|||||||
std::vector<std::string> configurations;
|
std::vector<std::string> configurations;
|
||||||
std::string working_directory;
|
std::string working_directory;
|
||||||
std::vector<std::string> command;
|
std::vector<std::string> command;
|
||||||
|
std::vector<std::string> buildDepends;
|
||||||
bool command_expand_lists = false;
|
bool command_expand_lists = false;
|
||||||
cmPolicies::PolicyStatus cmp0178 = mf.GetPolicyStatus(cmPolicies::CMP0178);
|
cmPolicies::PolicyStatus cmp0178 = mf.GetPolicyStatus(cmPolicies::CMP0178);
|
||||||
|
|
||||||
@@ -100,6 +102,7 @@ bool cmAddTestCommandHandleNameMode(std::vector<std::string> const& args,
|
|||||||
DoingCommand,
|
DoingCommand,
|
||||||
DoingConfigs,
|
DoingConfigs,
|
||||||
DoingWorkingDirectory,
|
DoingWorkingDirectory,
|
||||||
|
DoingBuildDepends,
|
||||||
DoingCmp0178,
|
DoingCmp0178,
|
||||||
DoingNone
|
DoingNone
|
||||||
};
|
};
|
||||||
@@ -117,6 +120,8 @@ bool cmAddTestCommandHandleNameMode(std::vector<std::string> const& args,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
doing = DoingConfigs;
|
doing = DoingConfigs;
|
||||||
|
} else if (args[i] == "BUILD_DEPENDS") {
|
||||||
|
doing = DoingBuildDepends;
|
||||||
} else if (args[i] == "WORKING_DIRECTORY") {
|
} else if (args[i] == "WORKING_DIRECTORY") {
|
||||||
if (!working_directory.empty()) {
|
if (!working_directory.empty()) {
|
||||||
status.SetError(" may be given at most one WORKING_DIRECTORY.");
|
status.SetError(" may be given at most one WORKING_DIRECTORY.");
|
||||||
@@ -139,6 +144,8 @@ bool cmAddTestCommandHandleNameMode(std::vector<std::string> const& args,
|
|||||||
command.push_back(args[i]);
|
command.push_back(args[i]);
|
||||||
} else if (doing == DoingConfigs) {
|
} else if (doing == DoingConfigs) {
|
||||||
configurations.push_back(args[i]);
|
configurations.push_back(args[i]);
|
||||||
|
} else if (doing == DoingBuildDepends) {
|
||||||
|
buildDepends.push_back(args[i]);
|
||||||
} else if (doing == DoingWorkingDirectory) {
|
} else if (doing == DoingWorkingDirectory) {
|
||||||
working_directory = args[i];
|
working_directory = args[i];
|
||||||
doing = DoingNone;
|
doing = DoingNone;
|
||||||
@@ -185,6 +192,15 @@ bool cmAddTestCommandHandleNameMode(std::vector<std::string> const& args,
|
|||||||
test->SetProperty("WORKING_DIRECTORY", working_directory);
|
test->SetProperty("WORKING_DIRECTORY", working_directory);
|
||||||
}
|
}
|
||||||
test->SetCommandExpandLists(command_expand_lists);
|
test->SetCommandExpandLists(command_expand_lists);
|
||||||
|
if (!buildDepends.empty()) {
|
||||||
|
if (!cmGeneratorExpression::IsValidTargetName(name)) {
|
||||||
|
status.SetError(cmStrCat("Cannot set build dependencies for a test with"
|
||||||
|
" NAME \"",
|
||||||
|
name, "\" which is not a valid target name."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
test->SetBuildDependencies(buildDepends);
|
||||||
|
}
|
||||||
mf.AddTestGenerator(cm::make_unique<cmTestGenerator>(test, configurations));
|
mf.AddTestGenerator(cm::make_unique<cmTestGenerator>(test, configurations));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -54,6 +54,8 @@
|
|||||||
#include "cmSystemTools.h"
|
#include "cmSystemTools.h"
|
||||||
#include "cmTarget.h"
|
#include "cmTarget.h"
|
||||||
#include "cmTargetDepend.h"
|
#include "cmTargetDepend.h"
|
||||||
|
#include "cmTest.h"
|
||||||
|
#include "cmTestGenerator.h"
|
||||||
#include "cmValue.h"
|
#include "cmValue.h"
|
||||||
#include "cmVersion.h"
|
#include "cmVersion.h"
|
||||||
#include "cmake.h"
|
#include "cmake.h"
|
||||||
@@ -641,6 +643,7 @@ void cmGlobalNinjaGenerator::Generate()
|
|||||||
this->cmGlobalGenerator::Generate();
|
this->cmGlobalGenerator::Generate();
|
||||||
|
|
||||||
this->WriteAssumedSourceDependencies();
|
this->WriteAssumedSourceDependencies();
|
||||||
|
this->WriteTestPrepTargets();
|
||||||
this->WriteTargetAliases(*this->GetCommonFileStream());
|
this->WriteTargetAliases(*this->GetCommonFileStream());
|
||||||
this->WriteFolderTargets(*this->GetCommonFileStream());
|
this->WriteFolderTargets(*this->GetCommonFileStream());
|
||||||
this->WriteBuiltinTargets(*this->GetCommonFileStream());
|
this->WriteBuiltinTargets(*this->GetCommonFileStream());
|
||||||
@@ -1293,6 +1296,80 @@ void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmGlobalNinjaGenerator::WriteTestPrepTargets()
|
||||||
|
{
|
||||||
|
auto writeConfig = [this](std::string const& config, std::ostream& os) {
|
||||||
|
struct TestPrepTarget
|
||||||
|
{
|
||||||
|
std::string Comment;
|
||||||
|
cmNinjaDeps ExplicitDeps;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::string, TestPrepTarget> testPrepTargets;
|
||||||
|
for (auto const& localGen : this->LocalGenerators) {
|
||||||
|
auto* lg = static_cast<cmLocalNinjaGenerator*>(localGen.get());
|
||||||
|
auto const& testGenerators = lg->GetMakefile()->GetTestGenerators();
|
||||||
|
for (auto const& tester : testGenerators) {
|
||||||
|
cmTestGenerator::BuildDependencies testDeps;
|
||||||
|
if (!tester->GetBuildDependencies(lg, testDeps)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string const depName = this->ConvertToNinjaPath(
|
||||||
|
cmStrCat("test_prep/", tester->GetTest()->GetName()));
|
||||||
|
// Merge with existing target if multiple tests have the same name
|
||||||
|
auto& testPrepTarget = testPrepTargets[depName];
|
||||||
|
testPrepTarget.Comment = cmStrCat("Build dependencies for test ",
|
||||||
|
tester->GetTest()->GetName());
|
||||||
|
|
||||||
|
for (cmGeneratorTarget* depTarget : testDeps.Targets) {
|
||||||
|
this->AppendTargetOutputs(depTarget, testPrepTarget.ExplicitDeps,
|
||||||
|
config, DependOnTargetArtifact);
|
||||||
|
}
|
||||||
|
std::transform(testDeps.Files.begin(), testDeps.Files.end(),
|
||||||
|
std::back_inserter(testPrepTarget.ExplicitDeps),
|
||||||
|
this->MapToNinjaPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> allDeps;
|
||||||
|
for (auto& prepTarget : testPrepTargets) {
|
||||||
|
cmNinjaBuild build("phony");
|
||||||
|
build.Comment = prepTarget.second.Comment;
|
||||||
|
build.Outputs.push_back(prepTarget.first);
|
||||||
|
|
||||||
|
// Avoid duplicate dependencies when merging test_prep/ targets
|
||||||
|
auto& explicitDeps = prepTarget.second.ExplicitDeps;
|
||||||
|
std::sort(explicitDeps.begin(), explicitDeps.end());
|
||||||
|
explicitDeps.erase(std::unique(explicitDeps.begin(), explicitDeps.end()),
|
||||||
|
explicitDeps.end());
|
||||||
|
build.ExplicitDeps = std::move(explicitDeps);
|
||||||
|
|
||||||
|
allDeps.push_back(prepTarget.first);
|
||||||
|
this->WriteBuild(os, build);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmNinjaBuild build("phony");
|
||||||
|
build.Comment = "Build dependencies for all tests";
|
||||||
|
build.Outputs.push_back(this->ConvertToNinjaPath("test_prep/all"));
|
||||||
|
build.ExplicitDeps = std::move(allDeps);
|
||||||
|
this->WriteBuild(os, build);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this->Makefiles.front()->IsOn("CMAKE_TEST_BUILD_DEPENDS")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->IsMultiConfig()) {
|
||||||
|
for (std::string const& config : this->GetConfigNames()) {
|
||||||
|
if (!writeConfig(config, *this->GetConfigFileStream(config))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
writeConfig(std::string(), *this->GetCommonFileStream());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string cmGlobalNinjaGenerator::OrderDependsTargetForTarget(
|
std::string cmGlobalNinjaGenerator::OrderDependsTargetForTarget(
|
||||||
cmGeneratorTarget const* target, std::string const& /*config*/) const
|
cmGeneratorTarget const* target, std::string const& /*config*/) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -524,6 +524,7 @@ private:
|
|||||||
void WriteDisclaimer(std::ostream& os) const;
|
void WriteDisclaimer(std::ostream& os) const;
|
||||||
|
|
||||||
void WriteAssumedSourceDependencies();
|
void WriteAssumedSourceDependencies();
|
||||||
|
void WriteTestPrepTargets();
|
||||||
|
|
||||||
void WriteTargetAliases(std::ostream& os);
|
void WriteTargetAliases(std::ostream& os);
|
||||||
void WriteFolderTargets(std::ostream& os);
|
void WriteFolderTargets(std::ostream& os);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||||
#include "cmTest.h"
|
#include "cmTest.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "cmMakefile.h"
|
#include "cmMakefile.h"
|
||||||
#include "cmProperty.h"
|
#include "cmProperty.h"
|
||||||
#include "cmState.h"
|
#include "cmState.h"
|
||||||
@@ -33,6 +35,11 @@ void cmTest::SetCommand(std::vector<std::string> const& command)
|
|||||||
this->Command = command;
|
this->Command = command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmTest::SetBuildDependencies(std::vector<std::string> deps)
|
||||||
|
{
|
||||||
|
this->BuildDependencies = std::move(deps);
|
||||||
|
}
|
||||||
|
|
||||||
cmValue cmTest::GetProperty(std::string const& prop) const
|
cmValue cmTest::GetProperty(std::string const& prop) const
|
||||||
{
|
{
|
||||||
cmValue retVal = this->Properties.GetPropertyValue(prop);
|
cmValue retVal = this->Properties.GetPropertyValue(prop);
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ public:
|
|||||||
void SetCommand(std::vector<std::string> const& command);
|
void SetCommand(std::vector<std::string> const& command);
|
||||||
std::vector<std::string> const& GetCommand() const { return this->Command; }
|
std::vector<std::string> const& GetCommand() const { return this->Command; }
|
||||||
|
|
||||||
|
void SetBuildDependencies(std::vector<std::string> deps);
|
||||||
|
std::vector<std::string> const& GetDependencies() const
|
||||||
|
{
|
||||||
|
return this->BuildDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
//! Set/Get a property of this source file
|
//! Set/Get a property of this source file
|
||||||
void SetProperty(std::string const& prop, cmValue value);
|
void SetProperty(std::string const& prop, cmValue value);
|
||||||
void SetProperty(std::string const& prop, std::nullptr_t)
|
void SetProperty(std::string const& prop, std::nullptr_t)
|
||||||
@@ -85,6 +91,7 @@ private:
|
|||||||
cmPropertyMap Properties;
|
cmPropertyMap Properties;
|
||||||
std::string Name;
|
std::string Name;
|
||||||
std::vector<std::string> Command;
|
std::vector<std::string> Command;
|
||||||
|
std::vector<std::string> BuildDependencies;
|
||||||
bool CommandExpandLists = false;
|
bool CommandExpandLists = false;
|
||||||
|
|
||||||
bool OldStyle;
|
bool OldStyle;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <cstddef> // IWYU pragma: keep
|
#include <cstddef> // IWYU pragma: keep
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -12,10 +13,12 @@
|
|||||||
#include "cmDiagnostics.h"
|
#include "cmDiagnostics.h"
|
||||||
#include "cmGeneratorExpression.h"
|
#include "cmGeneratorExpression.h"
|
||||||
#include "cmGeneratorTarget.h"
|
#include "cmGeneratorTarget.h"
|
||||||
|
#include "cmGlobalGenerator.h"
|
||||||
#include "cmList.h"
|
#include "cmList.h"
|
||||||
#include "cmListFileCache.h"
|
#include "cmListFileCache.h"
|
||||||
#include "cmLocalGenerator.h"
|
#include "cmLocalGenerator.h"
|
||||||
#include "cmMakefile.h"
|
#include "cmMakefile.h"
|
||||||
|
#include "cmMessageType.h"
|
||||||
#include "cmPolicies.h"
|
#include "cmPolicies.h"
|
||||||
#include "cmPropertyMap.h"
|
#include "cmPropertyMap.h"
|
||||||
#include "cmRange.h"
|
#include "cmRange.h"
|
||||||
@@ -92,6 +95,67 @@ cmTest* cmTestGenerator::GetTest() const
|
|||||||
return this->Test;
|
return this->Test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg,
|
||||||
|
BuildDependencies& info)
|
||||||
|
{
|
||||||
|
if (this->Test == nullptr ||
|
||||||
|
!cmGeneratorExpression::IsValidTargetName(this->Test->GetName()) ||
|
||||||
|
cmGlobalGenerator::IsReservedTarget(this->Test->GetName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<cmGeneratorTarget*> dependencies;
|
||||||
|
|
||||||
|
// Get dependencies from generator expressions
|
||||||
|
cmGeneratorExpression ge(*this->Test->GetMakefile()->GetCMakeInstance(),
|
||||||
|
this->Test->GetBacktrace());
|
||||||
|
std::string const config;
|
||||||
|
for (std::string const& arg : this->Test->GetCommand()) {
|
||||||
|
auto parsed = ge.Parse(arg);
|
||||||
|
parsed->Evaluate(lg, config);
|
||||||
|
for (cmGeneratorTarget* dep : parsed->GetTargets()) {
|
||||||
|
if (dep && !dep->IsImported()) {
|
||||||
|
dependencies.insert(dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add target executed by test
|
||||||
|
if (!this->Test->GetCommand().empty()) {
|
||||||
|
std::string exe = this->Test->GetCommand().front();
|
||||||
|
cmGeneratorTarget* target = lg->FindGeneratorTargetToUse(exe);
|
||||||
|
if (target && target->GetType() == cmStateEnums::EXECUTABLE &&
|
||||||
|
!target->IsImported()) {
|
||||||
|
dependencies.insert(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add dependencies from BUILD_DEPENDS keyword
|
||||||
|
for (auto const& depName : this->Test->GetDependencies()) {
|
||||||
|
if (depName.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cmGeneratorTarget* depTarget = lg->FindGeneratorTargetToUse(depName);
|
||||||
|
if (!depTarget) {
|
||||||
|
info.Files.push_back(depName);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
info.Targets.insert(info.Targets.end(), dependencies.begin(),
|
||||||
|
dependencies.end());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void cmTestGenerator::GenerateScriptActions(std::ostream& os, Indent indent)
|
void cmTestGenerator::GenerateScriptActions(std::ostream& os, Indent indent)
|
||||||
{
|
{
|
||||||
if (this->ActionsPerConfig) {
|
if (this->ActionsPerConfig) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
class cmListFileBacktrace;
|
class cmListFileBacktrace;
|
||||||
class cmGeneratorExpression;
|
class cmGeneratorExpression;
|
||||||
|
class cmGeneratorTarget;
|
||||||
class cmLocalGenerator;
|
class cmLocalGenerator;
|
||||||
class cmTest;
|
class cmTest;
|
||||||
|
|
||||||
@@ -23,6 +24,12 @@ class cmTest;
|
|||||||
class cmTestGenerator : public cmScriptGenerator
|
class cmTestGenerator : public cmScriptGenerator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct BuildDependencies
|
||||||
|
{
|
||||||
|
std::vector<cmGeneratorTarget*> Targets;
|
||||||
|
std::vector<std::string> Files;
|
||||||
|
};
|
||||||
|
|
||||||
cmTestGenerator(cmTest* test,
|
cmTestGenerator(cmTest* test,
|
||||||
std::vector<std::string> const& configurations =
|
std::vector<std::string> const& configurations =
|
||||||
std::vector<std::string>());
|
std::vector<std::string>());
|
||||||
@@ -32,6 +39,7 @@ public:
|
|||||||
cmTestGenerator& operator=(cmTestGenerator const&) = delete;
|
cmTestGenerator& operator=(cmTestGenerator const&) = delete;
|
||||||
|
|
||||||
void Compute(cmLocalGenerator* lg);
|
void Compute(cmLocalGenerator* lg);
|
||||||
|
bool GetBuildDependencies(cmLocalGenerator* lg, BuildDependencies& deps);
|
||||||
|
|
||||||
/** Test if this generator installs the test for a given configuration. */
|
/** Test if this generator installs the test for a given configuration. */
|
||||||
bool TestsForConfig(std::string const& config);
|
bool TestsForConfig(std::string const& config);
|
||||||
|
|||||||
@@ -58,3 +58,50 @@ block()
|
|||||||
run_cmake_command(TestLauncher-test ${CMAKE_CTEST_COMMAND} -C Debug -V)
|
run_cmake_command(TestLauncher-test ${CMAKE_CTEST_COMMAND} -C Debug -V)
|
||||||
endblock()
|
endblock()
|
||||||
unset(RunCMake_TEST_OPTIONS)
|
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)
|
||||||
|
block()
|
||||||
|
run_testdependency_case(DEFAULT FALSE)
|
||||||
|
run_testdependency_case(OFF FALSE)
|
||||||
|
run_cmake(TestDependency-ON-invalid-test-name)
|
||||||
|
|
||||||
|
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TestDependency-ON-build)
|
||||||
|
run_testdependency_case(ON TRUE)
|
||||||
|
|
||||||
|
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
set(TestDependency_CONFIG Debug)
|
||||||
|
else()
|
||||||
|
set(TestDependency_CONFIG "")
|
||||||
|
endif()
|
||||||
|
set(TestDependency_BUILD_CONFIG_ARG)
|
||||||
|
if(TestDependency_CONFIG)
|
||||||
|
set(TestDependency_BUILD_CONFIG_ARG --config ${TestDependency_CONFIG})
|
||||||
|
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()
|
||||||
|
|||||||
2
Tests/RunCMake/add_test/TestDependency-DEFAULT.cmake
Normal file
2
Tests/RunCMake/add_test/TestDependency-DEFAULT.cmake
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
cmake_minimum_required(VERSION 4.3)
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/TestDependency.cmake)
|
||||||
3
Tests/RunCMake/add_test/TestDependency-OFF.cmake
Normal file
3
Tests/RunCMake/add_test/TestDependency-OFF.cmake
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
cmake_minimum_required(VERSION 4.3)
|
||||||
|
set(CMAKE_TEST_BUILD_DEPENDS OFF)
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/TestDependency.cmake)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
CMake Error at TestDependency-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\)
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
cmake_minimum_required(VERSION 4.3)
|
||||||
|
set(CMAKE_TEST_BUILD_DEPENDS ON)
|
||||||
|
|
||||||
|
project(TestDependencyInvalidTestName C)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
add_custom_target(TestDependencyPrereq
|
||||||
|
COMMAND
|
||||||
|
"${CMAKE_COMMAND}" -E touch
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyPrereq-built.txt"
|
||||||
|
BYPRODUCTS
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyPrereq-built.txt"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_test(NAME "Target Build Test Invalid Name"
|
||||||
|
COMMAND
|
||||||
|
"${CMAKE_COMMAND}" -E true
|
||||||
|
BUILD_DEPENDS
|
||||||
|
TestDependencyPrereq)
|
||||||
3
Tests/RunCMake/add_test/TestDependency-ON.cmake
Normal file
3
Tests/RunCMake/add_test/TestDependency-ON.cmake
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
cmake_minimum_required(VERSION 4.3)
|
||||||
|
set(CMAKE_TEST_BUILD_DEPENDS ON)
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/TestDependency.cmake)
|
||||||
52
Tests/RunCMake/add_test/TestDependency-build-check.cmake
Normal file
52
Tests/RunCMake/add_test/TestDependency-build-check.cmake
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
foreach(name IN ITEMS
|
||||||
|
TestDependencyExe
|
||||||
|
TestDependencyGenex
|
||||||
|
TestDependencyPrereq
|
||||||
|
TestDependencyFile
|
||||||
|
TestDependencySubdirPrereq
|
||||||
|
TestDependencySubdirFile)
|
||||||
|
set(candidates)
|
||||||
|
list(APPEND candidates
|
||||||
|
"${RunCMake_TEST_BINARY_DIR}/${name}-built.txt")
|
||||||
|
list(APPEND candidates
|
||||||
|
"${RunCMake_TEST_BINARY_DIR}/TestDependencySubdir/${name}-built.txt")
|
||||||
|
|
||||||
|
set(found FALSE)
|
||||||
|
foreach(path IN LISTS candidates)
|
||||||
|
if(EXISTS "${path}")
|
||||||
|
set(found TRUE)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT found)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Expected ${name}-built.txt not found.")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set(prereq_file "${RunCMake_TEST_BINARY_DIR}/TestDependencyPrereq-built.txt")
|
||||||
|
if(NOT EXISTS "${prereq_file}")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Expected TestDependencyPrereq-built.txt to be generated by DEPENDS target.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(file_dep_file "${RunCMake_TEST_BINARY_DIR}/TestDependencyFile-built.txt")
|
||||||
|
if(NOT EXISTS "${file_dep_file}")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Expected TestDependencyFile-built.txt to be generated by DEPENDS file.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(subdir_prereq_file
|
||||||
|
"${RunCMake_TEST_BINARY_DIR}/TestDependencySubdir/TestDependencySubdirPrereq-built.txt")
|
||||||
|
if(NOT EXISTS "${subdir_prereq_file}")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Expected TestDependencySubdirPrereq-built.txt to be generated by duplicate test DEPENDS target.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(subdir_file_dep_file
|
||||||
|
"${RunCMake_TEST_BINARY_DIR}/TestDependencySubdir/TestDependencySubdirFile-built.txt")
|
||||||
|
if(NOT EXISTS "${subdir_file_dep_file}")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Expected TestDependencySubdirFile-built.txt to be generated by duplicate test DEPENDS file.")
|
||||||
|
endif()
|
||||||
28
Tests/RunCMake/add_test/TestDependency-check-targets.cmake
Normal file
28
Tests/RunCMake/add_test/TestDependency-check-targets.cmake
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
if(NOT DEFINED RunCMake_TEST_BINARY_DIR)
|
||||||
|
message(FATAL_ERROR "RunCMake_TEST_BINARY_DIR not set")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED expect_present)
|
||||||
|
message(FATAL_ERROR "expect_present not set")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(GLOB ninja_files LIST_DIRECTORIES false
|
||||||
|
"${RunCMake_TEST_BINARY_DIR}/*.ninja"
|
||||||
|
"${RunCMake_TEST_BINARY_DIR}/*.ninja.in")
|
||||||
|
|
||||||
|
set(found FALSE)
|
||||||
|
foreach(ninja_file IN LISTS ninja_files)
|
||||||
|
file(READ "${ninja_file}" content)
|
||||||
|
if(content MATCHES "test_prep.all|test_prep.TargetBuildTest")
|
||||||
|
set(found TRUE)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(expect_present AND NOT found)
|
||||||
|
message(FATAL_ERROR "Expected test_prep targets to be present in ninja files.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT expect_present AND found)
|
||||||
|
message(FATAL_ERROR "Expected test_prep targets to be absent from ninja files.")
|
||||||
|
endif()
|
||||||
49
Tests/RunCMake/add_test/TestDependency.cmake
Normal file
49
Tests/RunCMake/add_test/TestDependency.cmake
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
project(TestDependency C)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
add_executable(TestDependencyExe main.c)
|
||||||
|
add_custom_command(TARGET TestDependencyExe POST_BUILD
|
||||||
|
COMMAND
|
||||||
|
"${CMAKE_COMMAND}" -E touch
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyExe-built.txt"
|
||||||
|
BYPRODUCTS
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyExe-built.txt")
|
||||||
|
|
||||||
|
add_executable(TestDependencyGenex main.c)
|
||||||
|
add_custom_command(TARGET TestDependencyGenex POST_BUILD
|
||||||
|
COMMAND
|
||||||
|
"${CMAKE_COMMAND}" -E touch
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyGenex-built.txt"
|
||||||
|
BYPRODUCTS
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyGenex-built.txt")
|
||||||
|
|
||||||
|
add_custom_target(TestDependencyPrereq
|
||||||
|
COMMAND
|
||||||
|
"${CMAKE_COMMAND}" -E touch
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyPrereq-built.txt"
|
||||||
|
BYPRODUCTS
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyPrereq-built.txt"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyFile-built.txt"
|
||||||
|
COMMAND
|
||||||
|
"${CMAKE_COMMAND}" -E touch
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyFile-built.txt"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_target(
|
||||||
|
FileDependsTarget
|
||||||
|
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyFile-built.txt"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(TestDependencySubdir)
|
||||||
|
|
||||||
|
add_test(NAME TargetBuildTest
|
||||||
|
COMMAND
|
||||||
|
TestDependencyExe
|
||||||
|
$<TARGET_FILE:TestDependencyGenex>
|
||||||
|
BUILD_DEPENDS
|
||||||
|
TestDependencyPrereq
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyFile-built.txt")
|
||||||
25
Tests/RunCMake/add_test/TestDependencySubdir/CMakeLists.txt
Normal file
25
Tests/RunCMake/add_test/TestDependencySubdir/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
add_custom_target(TestDependencySubdirPrereq
|
||||||
|
COMMAND
|
||||||
|
"${CMAKE_COMMAND}" -E touch
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirPrereq-built.txt"
|
||||||
|
BYPRODUCTS
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirPrereq-built.txt"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirFile-built.txt"
|
||||||
|
COMMAND
|
||||||
|
"${CMAKE_COMMAND}" -E touch
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirFile-built.txt"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_target(TestDependencySubdirFile
|
||||||
|
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirFile-built.txt")
|
||||||
|
|
||||||
|
# Duplicate test name with parent directory
|
||||||
|
add_test(NAME TargetBuildTest
|
||||||
|
COMMAND
|
||||||
|
"${CMAKE_COMMAND}" -E true
|
||||||
|
BUILD_DEPENDS
|
||||||
|
TestDependencySubdirPrereq
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencySubdirFile-built.txt")
|
||||||
Reference in New Issue
Block a user