mirror of
https://github.com/Kitware/CMake.git
synced 2026-08-03 14:20:27 +00:00
Makefile: Generate test_prep targets for CMAKE_TEST_BUILD_DEPENDS
Extend the test_prep/<test> and test_prep/all build targets, generated from add_test(BUILD_DEPENDS) when CMAKE_TEST_BUILD_DEPENDS is enabled, to the Makefile generators. Previously only the Ninja and FASTBuild generators provided them. Resolve each test's dependencies in a shared layer so all command-line generators consume one result. cmTestGenerator now filters target dependencies to build-system targets and records, for each file dependency, the single target that produces it as a primary custom-command output (cmGlobalGenerator::FindOutputOwningTarget). The Makefile generators emit phony rules in CMakeFiles/Makefile2 depending on <target>.dir/all, with top-level forwarding rules. Sharing the resolution also stops Ninja and FASTBuild from emitting a dead test_prep edge for a non-buildable (e.g. INTERFACE) BUILD_DEPENDS target. On the Makefile generators a file dependency that no single target produces, and a test whose name contains ':', cannot be expressed as a recursive-make rule and are reported with a warning and skipped. Fixes: #27879
This commit is contained in:
@@ -82,7 +82,12 @@ directory the test is created in.
|
||||
|
||||
.. versionchanged:: 4.5
|
||||
|
||||
:generator:`FASTBuild` gained support for ``test_prep/<name>`` targets.
|
||||
:generator:`FASTBuild` and the :ref:`Makefile Generators` gained support
|
||||
for ``test_prep/<name>`` targets. With the Makefile generators a
|
||||
``BUILD_DEPENDS`` file is built by building the target whose build
|
||||
produces it; a file that is not the unique output of a single target is
|
||||
reported with a warning, and a test whose name contains a ``:`` character
|
||||
is excluded.
|
||||
|
||||
The command may be specified using
|
||||
:manual:`generator expressions <cmake-generator-expressions(7)>`.
|
||||
@@ -101,9 +106,10 @@ directory the test is created in.
|
||||
|
||||
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` or :generator:`FASTBuild`. The test name must be
|
||||
a valid target name in order to list build dependencies with this keyword.
|
||||
described above when :variable:`CMAKE_TEST_BUILD_DEPENDS` is enabled with the
|
||||
:ref:`Ninja Generators`, :generator:`FASTBuild`, or :ref:`Makefile
|
||||
Generators`. The test name must be a valid target name in order to list
|
||||
build dependencies with this keyword.
|
||||
|
||||
``COMMAND_EXPAND_LISTS``
|
||||
.. versionadded:: 3.16
|
||||
|
||||
@@ -29,3 +29,23 @@ be created, containing the following targets:
|
||||
|
||||
``package``
|
||||
Runs the package step in the subdirectory, if any.
|
||||
|
||||
.. versionadded:: 4.5
|
||||
|
||||
When the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is enabled, the
|
||||
top-level makefile additionally 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. A ``BUILD_DEPENDS`` file is built
|
||||
by building the target whose build produces it.
|
||||
|
||||
Tests whose names are not valid target names, and tests whose names
|
||||
contain a ``:`` character, 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``
|
||||
Depends on every generated ``test_prep/<test-name>`` target.
|
||||
|
||||
9
Help/release/dev/test-prep-makefile-generators.rst
Normal file
9
Help/release/dev/test-prep-makefile-generators.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
test-prep-makefile-generators
|
||||
-----------------------------
|
||||
|
||||
* The :generator:`FASTBuild` generator and the :ref:`Makefile Generators`
|
||||
now generate ``test_prep/<test-name>`` and ``test_prep/all`` convenience
|
||||
build targets when the :variable:`CMAKE_TEST_BUILD_DEPENDS` variable is
|
||||
enabled, matching the behavior of the :ref:`Ninja Generators`. These
|
||||
targets build the dependencies of tests added by :command:`add_test`,
|
||||
including those listed with the ``BUILD_DEPENDS`` keyword.
|
||||
@@ -5,11 +5,12 @@ CMAKE_TEST_BUILD_DEPENDS
|
||||
|
||||
.. versionchanged:: 4.5
|
||||
|
||||
:generator:`FASTBuild` gained support for ``test_prep/<name>`` targets.
|
||||
:generator:`FASTBuild` and the :ref:`Makefile Generators` gained support
|
||||
for ``test_prep/<name>`` targets.
|
||||
|
||||
Enable ``test_prep/<name>`` build targets for tests added by
|
||||
:command:`add_test` when using the :ref:`Ninja Generators` or
|
||||
:generator:`FASTBuild`.
|
||||
:command:`add_test` when using the :ref:`Ninja Generators`,
|
||||
:generator:`FASTBuild`, or :ref:`Makefile 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``
|
||||
@@ -20,3 +21,9 @@ targets referenced by test command generator expressions, and explicit
|
||||
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.
|
||||
|
||||
With the :ref:`Makefile Generators`, a ``BUILD_DEPENDS`` file is built by
|
||||
building the target whose build produces it. A generated file that is not the
|
||||
unique output of a single target is reported with a warning, and a test whose
|
||||
name contains a ``:`` character is excluded because it cannot be expressed as
|
||||
a Makefile target.
|
||||
|
||||
@@ -1614,8 +1614,9 @@ void cmGlobalFastbuildGenerator::WriteTestPrepTargets()
|
||||
for (cmGeneratorTarget* depTarget : testDeps.Targets) {
|
||||
testPrepTarget.Dependencies.emplace(depTarget->GetName());
|
||||
}
|
||||
for (std::string const& depFile : testDeps.Files) {
|
||||
testPrepTarget.Dependencies.emplace(depFile);
|
||||
for (cmTestGenerator::BuildDependencies::FileDependency const& depFile :
|
||||
testDeps.Files) {
|
||||
testPrepTarget.Dependencies.emplace(depFile.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2232,10 +2232,51 @@ void cmGlobalGenerator::CreateGeneratorTargets(TargetTypes targetTypes)
|
||||
}
|
||||
}
|
||||
|
||||
void cmGlobalGenerator::ComputeOutputOwnerIndex()
|
||||
{
|
||||
this->OutputOwnerIndexComputed = true;
|
||||
for (auto const& lg : this->LocalGenerators) {
|
||||
for (auto const& gt : lg->GetGeneratorTargets()) {
|
||||
if (!gt->IsInBuildSystem()) {
|
||||
continue;
|
||||
}
|
||||
for (cmGeneratorTarget::AllConfigSource const& acs :
|
||||
gt->GetAllConfigSources(
|
||||
cmGeneratorTarget::SourceKindCustomCommand)) {
|
||||
cmCustomCommand const* cc = acs.Source->GetCustomCommand();
|
||||
if (!cc) {
|
||||
continue;
|
||||
}
|
||||
for (std::string const& out : cc->GetOutputs()) {
|
||||
this->OutputOwnerIndex[cmSystemTools::CollapseFullPath(out)]
|
||||
.push_back(gt.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmGeneratorTarget* cmGlobalGenerator::FindOutputOwningTarget(
|
||||
std::string const& output)
|
||||
{
|
||||
if (!this->OutputOwnerIndexComputed) {
|
||||
this->ComputeOutputOwnerIndex();
|
||||
}
|
||||
auto it =
|
||||
this->OutputOwnerIndex.find(cmSystemTools::CollapseFullPath(output));
|
||||
if (it != this->OutputOwnerIndex.end() && it->second.size() == 1) {
|
||||
return it->second.front();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void cmGlobalGenerator::ClearGeneratorMembers()
|
||||
{
|
||||
this->BuildExportSets.clear();
|
||||
|
||||
this->OutputOwnerIndex.clear();
|
||||
this->OutputOwnerIndexComputed = false;
|
||||
|
||||
this->Makefiles.clear();
|
||||
|
||||
this->LocalGenerators.clear();
|
||||
|
||||
@@ -313,6 +313,11 @@ public:
|
||||
std::vector<cmGeneratorTarget*> GetLocalGeneratorTargetsInOrder(
|
||||
cmLocalGenerator* lg) const;
|
||||
|
||||
// Find the single build-system target that produces the given path as a
|
||||
// primary custom-command output, or nullptr if there is none or more than
|
||||
// one. Used to resolve file-level test build dependencies.
|
||||
cmGeneratorTarget* FindOutputOwningTarget(std::string const& output);
|
||||
|
||||
cmMakefile* GetCurrentMakefile() const
|
||||
{
|
||||
return this->CurrentConfigureMakefile;
|
||||
@@ -978,6 +983,13 @@ private:
|
||||
using TargetDependMap = std::map<cmGeneratorTarget const*, TargetDependSet>;
|
||||
TargetDependMap TargetDependencies;
|
||||
|
||||
// Map from a custom-command primary output (collapsed full path) to the
|
||||
// build-system target(s) that produce it. Built lazily on first use and
|
||||
// cleared with the other generator members.
|
||||
std::map<std::string, std::vector<cmGeneratorTarget*>> OutputOwnerIndex;
|
||||
bool OutputOwnerIndexComputed = false;
|
||||
void ComputeOutputOwnerIndex();
|
||||
|
||||
friend class cmake;
|
||||
void CreateGeneratorTargets(
|
||||
TargetTypes targetTypes, cmMakefile* mf, cmLocalGenerator* lg,
|
||||
|
||||
@@ -1326,9 +1326,11 @@ void cmGlobalNinjaGenerator::WriteTestPrepTargets()
|
||||
this->AppendTargetOutputs(depTarget, testPrepTarget.ExplicitDeps,
|
||||
config, DependOnTargetArtifact);
|
||||
}
|
||||
std::transform(testDeps.Files.begin(), testDeps.Files.end(),
|
||||
std::back_inserter(testPrepTarget.ExplicitDeps),
|
||||
this->MapToNinjaPath());
|
||||
for (cmTestGenerator::BuildDependencies::FileDependency const& file :
|
||||
testDeps.Files) {
|
||||
testPrepTarget.ExplicitDeps.push_back(
|
||||
this->ConvertToNinjaPath(file.Path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "cmLocalUnixMakefileGenerator3.h"
|
||||
#include "cmMakefile.h"
|
||||
#include "cmMakefileTargetGenerator.h"
|
||||
#include "cmMessageType.h"
|
||||
#include "cmOutputConverter.h"
|
||||
#include "cmState.h"
|
||||
#include "cmStateTypes.h"
|
||||
@@ -25,6 +26,8 @@
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmTarget.h"
|
||||
#include "cmTargetDepend.h"
|
||||
#include "cmTest.h"
|
||||
#include "cmTestGenerator.h"
|
||||
#include "cmValue.h"
|
||||
#include "cmake.h"
|
||||
|
||||
@@ -109,6 +112,10 @@ void cmGlobalUnixMakefileGenerator3::Generate()
|
||||
this->ClangTidyExportFixesDirs.clear();
|
||||
this->ClangTidyExportFixesFiles.clear();
|
||||
|
||||
// Compute the "test_prep/" targets before generating the local makefiles
|
||||
// so their convenience rules can be written into the top-level Makefile.
|
||||
this->ComputeTestPrepTargets();
|
||||
|
||||
// first do superclass method
|
||||
this->cmGlobalGenerator::Generate();
|
||||
|
||||
@@ -233,6 +240,9 @@ void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2()
|
||||
cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(localGen));
|
||||
}
|
||||
|
||||
// Write the internal test_prep/ rules.
|
||||
this->WriteTestPrepRules(makefileStream, rootLG);
|
||||
|
||||
// Write special bottom targets
|
||||
rootLG.WriteSpecialTargetsBottom(makefileStream);
|
||||
}
|
||||
@@ -648,6 +658,9 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Forward the test_prep/ convenience targets to CMakeFiles/Makefile2.
|
||||
this->WriteTestPrepConvenienceRules(ruleFileStream);
|
||||
}
|
||||
|
||||
void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2(
|
||||
@@ -932,6 +945,159 @@ void cmGlobalUnixMakefileGenerator3::AppendCodegenTargetDepends(
|
||||
}
|
||||
}
|
||||
|
||||
void cmGlobalUnixMakefileGenerator3::ComputeTestPrepTargets()
|
||||
{
|
||||
this->TestPrepTargets.clear();
|
||||
this->TestPrepEnabled = false;
|
||||
if (this->Makefiles.empty() ||
|
||||
!this->Makefiles.front()->IsOn("CMAKE_TEST_BUILD_DEPENDS")) {
|
||||
return;
|
||||
}
|
||||
this->TestPrepEnabled = true;
|
||||
|
||||
// Map a generator target to its "<target>.dir/all" recursive rule.
|
||||
auto targetAllRule = [](cmGeneratorTarget* gt) -> std::string {
|
||||
auto* lg3 =
|
||||
static_cast<cmLocalUnixMakefileGenerator3*>(gt->GetLocalGenerator());
|
||||
return cmStrCat(lg3->GetRelativeTargetDirectory(gt), "/all");
|
||||
};
|
||||
|
||||
// Collect the dependencies of each test, merging tests that share a name
|
||||
// across directories (as the Ninja generator does).
|
||||
for (auto const& lg : this->LocalGenerators) {
|
||||
for (auto const& tester : lg->GetMakefile()->GetTestGenerators()) {
|
||||
cmTestGenerator::BuildDependencies deps;
|
||||
if (!tester->GetBuildDependencies(lg.get(), deps)) {
|
||||
continue;
|
||||
}
|
||||
cmTest* test = tester->GetTest();
|
||||
std::string const& testName = test->GetName();
|
||||
|
||||
// A Makefile target name cannot contain ':'. Such a name is valid for
|
||||
// add_test() (e.g. a namespaced name) and works with the Ninja
|
||||
// generator, but cannot be expressed as a Makefile rule.
|
||||
if (testName.find(':') != std::string::npos) {
|
||||
test->GetMakefile()->IssueMessage(
|
||||
MessageType::WARNING,
|
||||
cmStrCat("Test \"", testName,
|
||||
"\" 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 \"test_prep/\" "
|
||||
"target."),
|
||||
test->GetBacktrace());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string>& rules =
|
||||
this->TestPrepTargets[cmStrCat("test_prep/", testName)];
|
||||
|
||||
// Target dependencies are filtered to build-system targets by
|
||||
// GetBuildDependencies.
|
||||
for (cmGeneratorTarget* dep : deps.Targets) {
|
||||
rules.push_back(targetAllRule(dep));
|
||||
}
|
||||
|
||||
for (cmTestGenerator::BuildDependencies::FileDependency const& file :
|
||||
deps.Files) {
|
||||
if (file.Owner) {
|
||||
// The file is the primary output of one build-system target; build
|
||||
// that target to produce the file.
|
||||
rules.push_back(targetAllRule(file.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
|
||||
// recursive Makefile graph cannot build it from a top-level rule.
|
||||
test->GetMakefile()->IssueMessage(
|
||||
MessageType::WARNING,
|
||||
cmStrCat("Test \"", testName, "\" BUILD_DEPENDS file\n ",
|
||||
file.Path,
|
||||
"\nis generated but is not the unique output of a build "
|
||||
"target, so the \"test_prep/",
|
||||
testName,
|
||||
"\" target cannot build it with this generator. Depend "
|
||||
"on the target that produces it (for example one created "
|
||||
"with add_custom_target) instead."),
|
||||
test->GetBacktrace());
|
||||
}
|
||||
// Otherwise the file is not generated by the build (e.g. a source
|
||||
// file that already exists) and needs no build rule.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and de-duplicate each rule list (as the Ninja generator does).
|
||||
for (auto& entry : this->TestPrepTargets) {
|
||||
std::vector<std::string>& rules = entry.second;
|
||||
std::sort(rules.begin(), rules.end());
|
||||
rules.erase(std::unique(rules.begin(), rules.end()), rules.end());
|
||||
}
|
||||
}
|
||||
|
||||
void cmGlobalUnixMakefileGenerator3::WriteTestPrepRules(
|
||||
std::ostream& makefileStream, cmLocalUnixMakefileGenerator3& rootLG)
|
||||
{
|
||||
if (!this->TestPrepEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
rootLG.WriteDivider(makefileStream);
|
||||
makefileStream << "# Targets to build the dependencies of tests.\n\n";
|
||||
|
||||
std::vector<std::string> no_commands;
|
||||
std::vector<std::string> allDeps;
|
||||
for (auto const& entry : this->TestPrepTargets) {
|
||||
std::vector<std::string> depends = entry.second;
|
||||
if (depends.empty() && !this->EmptyRuleHackDepends.empty()) {
|
||||
depends.push_back(this->EmptyRuleHackDepends);
|
||||
}
|
||||
rootLG.WriteMakeRule(makefileStream, "Build the dependencies of a test.",
|
||||
entry.first, depends, no_commands, true);
|
||||
allDeps.push_back(entry.first);
|
||||
}
|
||||
|
||||
if (allDeps.empty() && !this->EmptyRuleHackDepends.empty()) {
|
||||
allDeps.push_back(this->EmptyRuleHackDepends);
|
||||
}
|
||||
rootLG.WriteMakeRule(makefileStream, "Build the dependencies of all tests.",
|
||||
"test_prep/all", allDeps, no_commands, true);
|
||||
}
|
||||
|
||||
void cmGlobalUnixMakefileGenerator3::WriteTestPrepConvenienceRules(
|
||||
std::ostream& ruleFileStream)
|
||||
{
|
||||
if (!this->TestPrepEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& lg = cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(
|
||||
this->LocalGenerators[0]);
|
||||
|
||||
bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION");
|
||||
std::string const makefile2 = "CMakeFiles/Makefile2";
|
||||
|
||||
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);
|
||||
}
|
||||
writeForward("test_prep/all");
|
||||
}
|
||||
|
||||
void cmGlobalUnixMakefileGenerator3::WriteHelpRule(
|
||||
std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3* lg)
|
||||
{
|
||||
|
||||
@@ -226,6 +226,21 @@ protected:
|
||||
void AppendCodegenTargetDepends(std::vector<std::string>& depends,
|
||||
cmGeneratorTarget* target);
|
||||
|
||||
// Compute the "test_prep/<name>" build targets requested via the
|
||||
// CMAKE_TEST_BUILD_DEPENDS variable. Each test's dependencies are
|
||||
// resolved to the build-system targets that produce them so that the
|
||||
// recursive Makefile graph can build them.
|
||||
void ComputeTestPrepTargets();
|
||||
|
||||
// Write the internal "test_prep/<name>" and "test_prep/all" rules into
|
||||
// CMakeFiles/Makefile2.
|
||||
void WriteTestPrepRules(std::ostream& makefileStream,
|
||||
cmLocalUnixMakefileGenerator3& rootLG);
|
||||
|
||||
// Write the top-level "test_prep/<name>" convenience rules that forward
|
||||
// into CMakeFiles/Makefile2.
|
||||
void WriteTestPrepConvenienceRules(std::ostream& ruleFileStream);
|
||||
|
||||
// Target name hooks for superclass.
|
||||
char const* GetAllTargetName() const override { return "all"; }
|
||||
char const* GetInstallTargetName() const override { return "install"; }
|
||||
@@ -298,4 +313,10 @@ private:
|
||||
cmStateSnapshot::StrictWeakOrder>
|
||||
DirectoryTargetsMap;
|
||||
void InitializeProgressMarks() override;
|
||||
|
||||
// Ordered map of "test_prep/<name>" -> the "<target>.dir/all" recursive
|
||||
// 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;
|
||||
bool TestPrepEnabled = false;
|
||||
};
|
||||
|
||||
@@ -64,6 +64,27 @@ std::string TestName(cmTest* test)
|
||||
return name;
|
||||
}
|
||||
|
||||
// Whether a path is produced by the build (a custom-command output or
|
||||
// byproduct) rather than a pre-existing file. The output-to-source map
|
||||
// records every generated path regardless of which target, if any, builds it.
|
||||
bool fileIsGenerated(cmGlobalGenerator* gg, std::string const& file)
|
||||
{
|
||||
std::string const collapsed = cmSystemTools::CollapseFullPath(file);
|
||||
for (auto const& lg : gg->GetLocalGenerators()) {
|
||||
cmSourcesWithOutput so = lg->GetSourcesWithOutput(collapsed);
|
||||
if (so.Source || so.Target) {
|
||||
return true;
|
||||
}
|
||||
if (file != collapsed) {
|
||||
so = lg->GetSourcesWithOutput(file);
|
||||
if (so.Source || so.Target) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End: anonymous namespace
|
||||
|
||||
cmTestGenerator::cmTestGenerator(
|
||||
@@ -135,7 +156,12 @@ bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg,
|
||||
}
|
||||
cmGeneratorTarget* depTarget = lg->FindGeneratorTargetToUse(depName);
|
||||
if (!depTarget) {
|
||||
info.Files.push_back(depName);
|
||||
cmGlobalGenerator* gg = lg->GetGlobalGenerator();
|
||||
BuildDependencies::FileDependency file;
|
||||
file.Path = depName;
|
||||
file.Owner = gg->FindOutputOwningTarget(depName);
|
||||
file.Generated = fileIsGenerated(gg, depName);
|
||||
info.Files.push_back(std::move(file));
|
||||
continue;
|
||||
}
|
||||
if (depTarget->IsImported()) {
|
||||
@@ -149,8 +175,11 @@ bool cmTestGenerator::GetBuildDependencies(cmLocalGenerator* lg,
|
||||
dependencies.insert(depTarget);
|
||||
}
|
||||
|
||||
info.Targets.insert(info.Targets.end(), dependencies.begin(),
|
||||
dependencies.end());
|
||||
for (cmGeneratorTarget* gt : dependencies) {
|
||||
if (gt->IsInBuildSystem()) {
|
||||
info.Targets.push_back(gt);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,21 @@ class cmTestGenerator : public cmScriptGenerator
|
||||
public:
|
||||
struct BuildDependencies
|
||||
{
|
||||
// A file dependency together with how the build produces it.
|
||||
struct FileDependency
|
||||
{
|
||||
std::string Path;
|
||||
// The single build-system target that produces Path as a primary
|
||||
// custom-command output, or nullptr if there is no such unique target.
|
||||
cmGeneratorTarget* Owner = nullptr;
|
||||
// Whether Path is produced by the build at all (output or byproduct).
|
||||
bool Generated = false;
|
||||
};
|
||||
// Build-system targets the test depends on (filtered to targets that are
|
||||
// part of the build system).
|
||||
std::vector<cmGeneratorTarget*> Targets;
|
||||
std::vector<std::string> Files;
|
||||
// BUILD_DEPENDS entries that did not name a target.
|
||||
std::vector<FileDependency> Files;
|
||||
};
|
||||
|
||||
cmTestGenerator(cmTest* test,
|
||||
|
||||
@@ -71,24 +71,57 @@ function(run_testdependency_case CASE_NAME EXPECT_PRESENT)
|
||||
unset(RunCMake_TEST_NO_CLEAN)
|
||||
endfunction()
|
||||
|
||||
if(RunCMake_GENERATOR MATCHES "Ninja|FASTBuild")
|
||||
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_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
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
CMake Warning at TestDependency-ON-byproduct.cmake:[0-9]+ \(add_test\):
|
||||
Test "ByproductTest" BUILD_DEPENDS file
|
||||
|
||||
.*/TestDependencyByproduct-built.txt
|
||||
|
||||
is generated but is not the unique output of a build target, so the
|
||||
"test_prep/ByproductTest" target cannot build it with this generator.
|
||||
Depend on the target that produces it \(for example one created with
|
||||
add_custom_target\) instead.
|
||||
20
Tests/RunCMake/add_test/TestDependency-ON-byproduct.cmake
Normal file
20
Tests/RunCMake/add_test/TestDependency-ON-byproduct.cmake
Normal file
@@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 4.3)
|
||||
set(CMAKE_TEST_BUILD_DEPENDS ON)
|
||||
|
||||
project(TestDependencyByproduct C)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_executable(TestDependencyByproductExe main.c)
|
||||
add_custom_command(TARGET TestDependencyByproductExe POST_BUILD
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -E touch
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyByproduct-built.txt"
|
||||
BYPRODUCTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyByproduct-built.txt")
|
||||
|
||||
add_test(NAME ByproductTest
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -E true
|
||||
BUILD_DEPENDS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyByproduct-built.txt")
|
||||
@@ -0,0 +1,5 @@
|
||||
CMake Warning at TestDependency-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
|
||||
"test_prep/" target.
|
||||
20
Tests/RunCMake/add_test/TestDependency-ON-colon-name.cmake
Normal file
20
Tests/RunCMake/add_test/TestDependency-ON-colon-name.cmake
Normal file
@@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 4.3)
|
||||
set(CMAKE_TEST_BUILD_DEPENDS ON)
|
||||
|
||||
project(TestDependencyColonName 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 "Foo::Bar"
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -E true
|
||||
BUILD_DEPENDS
|
||||
TestDependencyPrereq)
|
||||
19
Tests/RunCMake/add_test/TestDependency-ON-interface.cmake
Normal file
19
Tests/RunCMake/add_test/TestDependency-ON-interface.cmake
Normal file
@@ -0,0 +1,19 @@
|
||||
cmake_minimum_required(VERSION 4.3)
|
||||
set(CMAKE_TEST_BUILD_DEPENDS ON)
|
||||
|
||||
project(TestDependencyInterface C)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_executable(TestDependencyInterfaceExe 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
|
||||
# "TestDependencyIface.dir/all" prerequisite is generated.
|
||||
add_library(TestDependencyIface INTERFACE)
|
||||
|
||||
add_test(NAME InterfaceTest
|
||||
COMMAND
|
||||
TestDependencyInterfaceExe
|
||||
BUILD_DEPENDS
|
||||
TestDependencyIface)
|
||||
@@ -0,0 +1,9 @@
|
||||
CMake Warning at TestDependency-ON-orphan.cmake:[0-9]+ \(add_test\):
|
||||
Test "OrphanTest" BUILD_DEPENDS file
|
||||
|
||||
.*/TestDependencyOrphan-built.txt
|
||||
|
||||
is generated but is not the unique output of a build target, so the
|
||||
"test_prep/OrphanTest" target cannot build it with this generator. Depend
|
||||
on the target that produces it \(for example one created with
|
||||
add_custom_target\) instead.
|
||||
20
Tests/RunCMake/add_test/TestDependency-ON-orphan.cmake
Normal file
20
Tests/RunCMake/add_test/TestDependency-ON-orphan.cmake
Normal file
@@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 4.3)
|
||||
set(CMAKE_TEST_BUILD_DEPENDS ON)
|
||||
|
||||
project(TestDependencyOrphan C)
|
||||
|
||||
enable_testing()
|
||||
|
||||
# A custom-command output that no target builds.
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TestDependencyOrphan-built.txt"
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -E touch
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyOrphan-built.txt"
|
||||
VERBATIM)
|
||||
|
||||
add_test(NAME OrphanTest
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -E true
|
||||
BUILD_DEPENDS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/TestDependencyOrphan-built.txt")
|
||||
@@ -6,14 +6,16 @@ if(NOT DEFINED expect_present)
|
||||
message(FATAL_ERROR "expect_present not set")
|
||||
endif()
|
||||
|
||||
file(GLOB ninja_files LIST_DIRECTORIES false
|
||||
file(GLOB build_files LIST_DIRECTORIES false
|
||||
"${RunCMake_TEST_BINARY_DIR}/*.ninja"
|
||||
"${RunCMake_TEST_BINARY_DIR}/*.ninja.in"
|
||||
"${RunCMake_TEST_BINARY_DIR}/fbuild.bff")
|
||||
"${RunCMake_TEST_BINARY_DIR}/fbuild.bff"
|
||||
"${RunCMake_TEST_BINARY_DIR}/Makefile"
|
||||
"${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Makefile2")
|
||||
|
||||
set(found FALSE)
|
||||
foreach(ninja_file IN LISTS ninja_files)
|
||||
file(READ "${ninja_file}" content)
|
||||
foreach(build_file IN LISTS build_files)
|
||||
file(READ "${build_file}" content)
|
||||
if(content MATCHES "test_prep.all|test_prep.TargetBuildTest")
|
||||
set(found TRUE)
|
||||
break()
|
||||
@@ -21,9 +23,9 @@ foreach(ninja_file IN LISTS ninja_files)
|
||||
endforeach()
|
||||
|
||||
if(expect_present AND NOT found)
|
||||
message(FATAL_ERROR "Expected test_prep targets to be present in ninja files.")
|
||||
message(FATAL_ERROR "Expected test_prep targets to be present in build files.")
|
||||
endif()
|
||||
|
||||
if(NOT expect_present AND found)
|
||||
message(FATAL_ERROR "Expected test_prep targets to be absent from ninja files.")
|
||||
message(FATAL_ERROR "Expected test_prep targets to be absent from build files.")
|
||||
endif()
|
||||
|
||||
Reference in New Issue
Block a user