Merge topic 'autogen-vs-reduce-target-count' into release-4.4

d963b87ab4 Autogen: Reduce the number of targets under Visual Studio
2b5ad7e395 Autogen: Register per-config timestamp file with its full path

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: buildbot <buildbot@kitware.com>
Merge-request: !12409
This commit is contained in:
Brad King
2026-08-24 12:31:25 -04:00
committed by Kitware Robot
8 changed files with 247 additions and 92 deletions
+40 -9
View File
@@ -231,31 +231,58 @@ Target dependencies may be added to the ``<ORIGIN>_autogen`` target by adding
them to the :prop_tgt:`AUTOGEN_TARGET_DEPENDS` target property.
.. note::
If Qt 5.15 or later is used and the generator is either :generator:`Ninja` or
:ref:`Makefile Generators`, see :ref:`<ORIGIN>_autogen_timestamp_deps`.
If Qt 5.15 or later is used and a depfile is used to track the ``moc``
dependencies, see :ref:`<ORIGIN>_autogen_timestamp_deps`. When using the
:ref:`Visual Studio Generators`, the ``<ORIGIN>_autogen`` target may not be
created at all, see `Visual Studio Generators`_ below.
.. _`<ORIGIN>_autogen_timestamp_deps`:
The ``<ORIGIN>_autogen_timestamp_deps`` target
==============================================
If Qt 5.15 or later is used and the generator is either :generator:`Ninja` or
:ref:`Makefile Generators`, the ``<ORIGIN>_autogen_timestamp_deps`` target is
also created in addition to the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`
If Qt 5.15 or later is used and the generator is :generator:`Ninja`, a
:ref:`Makefile <Makefile Generators>` generator, a
:ref:`Visual Studio <Visual Studio Generators>` generator or
:generator:`Xcode`, the ``moc`` dependencies are tracked with a depfile. In
that case the ``<ORIGIN>_autogen_timestamp_deps`` target is also created in
addition to the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`
target. This target does not have any sources or commands to execute, but it
has dependencies that were previously inherited by the pre-Qt 5.15
:ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target.
These dependencies will serve as a list of order-only dependencies for the
custom command, without forcing the custom command to re-execute.
.. versionadded:: 4.4
Depfile support for the :ref:`Visual Studio Generators` and
:generator:`Xcode`. Earlier versions used a depfile only for the
:generator:`Ninja` and :ref:`Makefile Generators`.
.. note::
When using the :ref:`Visual Studio Generators`, neither this target nor the
:ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target is created in the common
case, see `Visual Studio Generators`_ below.
Visual Studio Generators
========================
When using the :ref:`Visual Studio Generators`, CMake
generates a ``PRE_BUILD`` :command:`custom command <add_custom_command>`
instead of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`
When using the :ref:`Visual Studio Generators`, CMake adds the ``moc`` and
``uic`` step to the ``<ORIGIN>`` project itself instead of creating the
:ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`
:command:`custom target <add_custom_target>` (for :prop_tgt:`AUTOMOC` and
:prop_tgt:`AUTOUIC`). This isn't always possible though and an
:prop_tgt:`AUTOUIC`). With Qt 5.15 or later, the step is a
:command:`custom command <add_custom_command>` with a depfile, and therefore
runs only when one of its dependencies changed. With earlier Qt versions it is
a ``PRE_BUILD`` command that runs on every build of ``<ORIGIN>``.
.. versionchanged:: 4.4.3
With Qt 5.15 or later, the ``moc`` and ``uic`` step is a custom command in
the ``<ORIGIN>`` project. CMake 4.4.0 through 4.4.2 created the
:ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` and
:ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>`
targets instead.
This isn't always possible though and an
:ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`
:command:`custom target <add_custom_target>` is used, when either
@@ -266,6 +293,10 @@ instead of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`
- :prop_tgt:`AUTOGEN_TARGET_DEPENDS` lists a source file
- :variable:`CMAKE_GLOBAL_AUTOGEN_TARGET` is enabled
With Qt 5.15 or later, the
:ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>` target
is created along with it.
qtmain.lib on Windows
=====================
+7 -4
View File
@@ -8,10 +8,13 @@ Switch for forwarding origin target dependencies to the corresponding
.. note::
If Qt 5.15 or later is used and the generator is either :generator:`Ninja`
or :ref:`Makefile Generators`, origin target dependencies are forwarded to
the :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>`
target instead of :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`.
If the
:ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>`
target is created, origin target dependencies are forwarded to it instead
of :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>`. When using the
:ref:`Visual Studio Generators`, the ``moc`` and ``uic`` step may be part
of the ``<ORIGIN>`` target itself, in which case they need no forwarding.
See the :manual:`cmake-qt(7)` manual for details.
Targets which have their :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC` property
``ON`` have a corresponding :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target
+7 -4
View File
@@ -6,10 +6,13 @@ Additional target dependencies of the corresponding
.. note::
If Qt 5.15 or later is used and the generator is either :generator:`Ninja`
or :ref:`Makefile Generators`, additional target dependencies are added to
the :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>`
target instead of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target.
If the
:ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>`
target is created, additional target dependencies are added to it instead
of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target. When using the
:ref:`Visual Studio Generators`, the ``moc`` and ``uic`` step may be part
of the ``<ORIGIN>`` target itself, in which case the dependencies are added
to ``<ORIGIN>``. See the :manual:`cmake-qt(7)` manual for details.
Targets which have their :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC` property
@@ -8,10 +8,13 @@ Switch for forwarding origin target dependencies to the corresponding
.. note::
If Qt 5.15 or later is used and the generator is either :generator:`Ninja`
or :ref:`Makefile Generators`, additional target dependencies are added to
the :ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>`
target instead of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target.
If the
:ref:`<ORIGIN>_autogen_timestamp_deps <<ORIGIN>_autogen_timestamp_deps>`
target is created, additional target dependencies are added to it instead
of the :ref:`<ORIGIN>_autogen <<ORIGIN>_autogen>` target. When using the
:ref:`Visual Studio Generators`, the ``moc`` and ``uic`` step may be part
of the ``<ORIGIN>`` target itself, in which case the dependencies are added
to ``<ORIGIN>``. See the :manual:`cmake-qt(7)` manual for details.
This variable is used to initialize the :prop_tgt:`AUTOGEN_ORIGIN_DEPENDS`
property on all the targets. See that target property for additional
+100 -71
View File
@@ -1373,6 +1373,16 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
gen.find("Visual Studio") != std::string::npos || gen == "Xcode");
}();
// Under VS, attach the autogen custom command to the origin target instead
// of creating separate targets, to reduce the number of targets loaded into
// the IDE. The dependencies of the origin target then provide the ordering
// that '_autogen_timestamp_deps' provides otherwise. The conditions match
// those of the PRE_BUILD event below, so that the '_autogen' target keeps
// existing wherever it did before depfiles were enabled for VS.
bool const attachToOrigin = useDepfile &&
this->AutogenTarget.DependFiles.empty() &&
!this->AutogenTarget.GlobalTarget && this->GlobalGen->IsVisualStudio();
// Files provided by the autogen target
std::vector<std::string> autogenByproducts;
std::vector<std::string> timestampByproducts;
@@ -1406,6 +1416,12 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
}
}
if (attachToOrigin) {
// Without an autogen target the timestamp command provides all byproducts.
cm::append(timestampByproducts, autogenByproducts);
autogenByproducts.clear();
}
// Compose target comment
std::string autogenComment;
{
@@ -1517,8 +1533,9 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
} else {
// Add link library target dependencies to the autogen target
// dependencies
if (this->AutogenTarget.DependOrigin) {
// dependencies. Not needed when attaching to the origin target, which
// already depends on its own link libraries.
if (this->AutogenTarget.DependOrigin && !attachToOrigin) {
// add_dependencies/addUtility do not support generator expressions.
// We depend only on the libraries found in all configs therefore.
std::map<cmGeneratorTarget const*, std::size_t> targetsPartOfAllConfigs;
@@ -1555,41 +1572,43 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
if (useDepfile) {
// Create a custom command that generates a timestamp file and
// has a depfile assigned. The depfile is created by JobDepFilesMergeT.
//
// Also create an additional '_autogen_timestamp_deps' that the custom
// command will depend on. It will have no sources or commands to
// execute, but it will have dependencies that would originally be
// assigned to the pre-Qt 5.15 'autogen' target. These dependencies will
// serve as a list of order-only dependencies for the custom command,
// without forcing the custom command to re-execute.
//
// The dependency tree would then look like
// '_autogen_timestamp_deps (order-only)' <- '/timestamp' file <-
// '_autogen' target.
auto const timestampTargetName =
cmStrCat(this->GenTarget->GetName(), "_autogen_timestamp_deps");
if (!attachToOrigin) {
// Also create an additional '_autogen_timestamp_deps' that the custom
// command will depend on. It will have no sources or commands to
// execute, but it will have dependencies that would originally be
// assigned to the pre-Qt 5.15 'autogen' target. These dependencies
// will serve as a list of order-only dependencies for the custom
// command, without forcing the custom command to re-execute.
//
// The dependency tree would then look like
// '_autogen_timestamp_deps (order-only)' <- '/timestamp' file <-
// '_autogen' target.
auto const timestampTargetName =
cmStrCat(this->GenTarget->GetName(), "_autogen_timestamp_deps");
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetDepends(dependencies);
cc->SetEscapeOldStyle(false);
timestampTarget = this->LocalGen->AddUtilityCommand(timestampTargetName,
true, std::move(cc));
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetDepends(dependencies);
cc->SetEscapeOldStyle(false);
timestampTarget = this->LocalGen->AddUtilityCommand(
timestampTargetName, true, std::move(cc));
this->LocalGen->AddGeneratorTarget(
cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen));
this->LocalGen->AddGeneratorTarget(
cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen));
// Set FOLDER property on the timestamp target, so it appears in the
// appropriate folder in an IDE or in the file api.
if (!this->TargetsFolder.empty()) {
timestampTarget->SetProperty("FOLDER", this->TargetsFolder);
// Set FOLDER property on the timestamp target, so it appears in the
// appropriate folder in an IDE or in the file api.
if (!this->TargetsFolder.empty()) {
timestampTarget->SetProperty("FOLDER", this->TargetsFolder);
}
// Make '/timestamp' file depend on '_autogen_timestamp_deps'.
dependencies.clear();
dependencies.push_back(timestampTargetName);
}
// Make '/timestamp' file depend on '_autogen_timestamp_deps' and on the
// moc and uic executables (whichever are enabled).
dependencies.clear();
dependencies.push_back(timestampTargetName);
// Make '/timestamp' file depend on the moc and uic executables
// (whichever are enabled).
AddAutogenExecutableToDependencies(this->Moc, dependencies);
AddAutogenExecutableToDependencies(this->Uic, dependencies);
std::string outputFile;
@@ -1615,7 +1634,8 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
for (std::string const& config : this->ConfigsList) {
auto tempTimestampFileName = timestampFileWithoutConfig + config;
auto tempDepFile = depFileWithoutConfig + config;
outputFileWithConfig.Config[config] = tempTimestampFileName;
outputFileWithConfig.Config[config] =
cmStrCat(this->Dir.Build, '/', tempTimestampFileName);
this->AutogenTarget.DepFileRuleName.Config[config] =
cmStrCat(this->Dir.RelativeBuild, '/', tempTimestampFileName);
this->AutogenTarget.DepFile.Config[config] = tempDepFile;
@@ -1633,7 +1653,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
{ cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
this->AddGeneratedSource(outputFile, this->Moc);
}
cc = cm::make_unique<cmCustomCommand>();
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetOutputs(outputFile);
cc->SetByproducts(timestampByproducts);
cc->SetDepends(dependencies);
@@ -1650,48 +1670,57 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
autogenComment.clear();
}
// Create autogen target
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetByproducts(autogenByproducts);
cc->SetDepends(dependencies);
cc->SetCommandLines(commandLines);
cc->SetEscapeOldStyle(false);
cc->SetComment(autogenComment.c_str());
cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
this->AutogenTarget.Name, true, std::move(cc));
// Create autogen generator target
this->LocalGen->AddGeneratorTarget(
cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen));
// Order the autogen target(s) just before the original target.
cmTarget* orderTarget = timestampTarget ? timestampTarget : autogenTarget;
// Forward origin utilities to autogen target
if (this->AutogenTarget.DependOrigin) {
for (BT<std::pair<std::string, bool>> const& depName :
this->GenTarget->GetUtilities()) {
orderTarget->AddUtility(depName.Value.first, false, this->Makefile);
if (attachToOrigin) {
// Add additional autogen target dependencies to the origin target
for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) {
this->GenTarget->Target->AddUtility(depTarget->GetName(), false,
this->Makefile);
}
}
} else {
// Create autogen target
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetByproducts(autogenByproducts);
cc->SetDepends(dependencies);
cc->SetCommandLines(commandLines);
cc->SetEscapeOldStyle(false);
cc->SetComment(autogenComment.c_str());
cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
this->AutogenTarget.Name, true, std::move(cc));
// Create autogen generator target
this->LocalGen->AddGeneratorTarget(
cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen));
// Add additional autogen target dependencies to autogen target
for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) {
orderTarget->AddUtility(depTarget->GetName(), false, this->Makefile);
}
// Order the autogen target(s) just before the original target.
cmTarget* orderTarget =
timestampTarget ? timestampTarget : autogenTarget;
// Forward origin utilities to autogen target
if (this->AutogenTarget.DependOrigin) {
for (BT<std::pair<std::string, bool>> const& depName :
this->GenTarget->GetUtilities()) {
orderTarget->AddUtility(depName.Value.first, false, this->Makefile);
}
}
// Set FOLDER property in autogen target
if (!this->TargetsFolder.empty()) {
autogenTarget->SetProperty("FOLDER", this->TargetsFolder);
}
// Add additional autogen target dependencies to autogen target
for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) {
orderTarget->AddUtility(depTarget->GetName(), false, this->Makefile);
}
// Add autogen target to the origin target dependencies
this->GenTarget->Target->AddUtility(this->AutogenTarget.Name, false,
this->Makefile);
// Set FOLDER property in autogen target
if (!this->TargetsFolder.empty()) {
autogenTarget->SetProperty("FOLDER", this->TargetsFolder);
}
// Add autogen target to the global autogen target dependencies
if (this->AutogenTarget.GlobalTarget) {
this->GlobalInitializer->AddToGlobalAutoGen(this->LocalGen,
this->AutogenTarget.Name);
// Add autogen target to the origin target dependencies
this->GenTarget->Target->AddUtility(this->AutogenTarget.Name, false,
this->Makefile);
// Add autogen target to the global autogen target dependencies
if (this->AutogenTarget.GlobalTarget) {
this->GlobalInitializer->AddToGlobalAutoGen(this->LocalGen,
this->AutogenTarget.Name);
}
}
}
@@ -0,0 +1,21 @@
enable_language(CXX)
find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core)
set(CMAKE_AUTOMOC ON)
add_library(simple_lib STATIC simple_lib.cpp)
# A plain AUTOMOC target gets the autogen custom command attached to itself.
add_library(plain_lib STATIC app_qt.cpp)
target_link_libraries(plain_lib PRIVATE simple_lib Qt${with_qt_version}::Core)
# A target that depends on a GENERATED file keeps the separate autogen targets.
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/generated_dep.h
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/generated_dep.h)
add_library(gen_dep_lib STATIC app_qt.cpp
${CMAKE_CURRENT_BINARY_DIR}/generated_dep.h)
target_link_libraries(gen_dep_lib PRIVATE Qt${with_qt_version}::Core)
target_include_directories(gen_dep_lib PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
set_property(TARGET gen_dep_lib PROPERTY
AUTOGEN_TARGET_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/autogen_dep.txt)
@@ -84,4 +84,68 @@ Automatic MOC for target sub_exe_2")
endif()
endblock()
endif()
if(RunCMake_GENERATOR MATCHES "Visual Studio"
AND QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0)
block()
macro(check_project_exists target expected)
if(EXISTS "${RunCMake_TEST_BINARY_DIR}/${target}.vcxproj")
set(actual TRUE)
else()
set(actual FALSE)
endif()
if("${actual}" STREQUAL "${expected}")
set(check_result "PASSED")
set(message_type "STATUS")
else()
set(check_result "FAILED")
set(message_type "FATAL_ERROR")
endif()
message(${message_type}
"QtAutoMocVsTargets-\"${target}.vcxproj\" exists is ${actual}, expected ${expected} - ${check_result}")
endmacro()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocVsTargets-build)
run_cmake_with_options(QtAutoMocVsTargets ${RunCMake_TEST_OPTIONS})
set(RunCMake_TEST_NO_CLEAN 1)
# A plain AUTOMOC target does not get extra projects.
check_project_exists(plain_lib TRUE)
check_project_exists(plain_lib_autogen FALSE)
check_project_exists(plain_lib_autogen_timestamp_deps FALSE)
# A target with a GENERATED dependency keeps them.
check_project_exists(gen_dep_lib_autogen TRUE)
check_project_exists(gen_dep_lib_autogen_timestamp_deps TRUE)
# The first build must run AUTOMOC. Assert it, so that the checks below
# cannot pass just because the message is missing from the build output.
set(RunCMake_TEST_EXPECT_stdout "Automatic MOC for target plain_lib")
run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Debug)
unset(RunCMake_TEST_EXPECT_stdout)
set(RunCMake_TEST_NOT_EXPECT_stdout "Automatic MOC for target plain_lib|\
Automatic MOC for target gen_dep_lib")
# Assert that the depfile prevents AUTOMOC from running again.
set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC again")
run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Debug)
# Touch the source file of a library that 'plain_lib' links. This must
# not cause a rerun of AUTOMOC.
file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp")
set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC for a dependency change")
run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Debug)
# Files in AUTOGEN_TARGET_DEPENDS are order-only dependencies. Touching
# one must not cause a rerun of AUTOMOC either.
file(TOUCH "${RunCMake_SOURCE_DIR}/autogen_dep.txt")
set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC for an AUTOGEN_TARGET_DEPENDS file")
run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Debug)
unset(RunCMake_TEST_NOT_EXPECT_stdout)
# Another configuration has its own timestamp file, so it builds again.
set(RunCMake_TEST_EXPECT_stdout "Automatic MOC for target plain_lib")
set(RunCMake_TEST_VARIANT_DESCRIPTION "-Release")
run_cmake_command(QtAutoMocVsTargets-build ${CMAKE_COMMAND} --build . --config Release)
unset(RunCMake_TEST_EXPECT_stdout)
unset(RunCMake_TEST_VARIANT_DESCRIPTION)
endblock()
endif()
endif ()
+1
View File
@@ -0,0 +1 @@
A file listed in AUTOGEN_TARGET_DEPENDS.