mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-18 17:31:57 +08:00
AUTOMOC: Add option to specify moc include directories explicitly
Add a `AUTOMOC_INCLUDE_DIRECTORIES` target property and a corresponding `CMAKE_AUTOMOC_INCLUDE_DIRECTORIES` variable to initialize it. This is useful for targets that do not need moc to search include directories from all dependencies. Closes: #26414
This commit is contained in:
@@ -145,6 +145,7 @@ Properties on Targets
|
||||
/prop_tgt/AUTOMOC_COMPILER_PREDEFINES
|
||||
/prop_tgt/AUTOMOC_DEPEND_FILTERS
|
||||
/prop_tgt/AUTOMOC_EXECUTABLE
|
||||
/prop_tgt/AUTOMOC_INCLUDE_DIRECTORIES
|
||||
/prop_tgt/AUTOMOC_MACRO_NAMES
|
||||
/prop_tgt/AUTOMOC_MOC_OPTIONS
|
||||
/prop_tgt/AUTOMOC_PATH_PREFIX
|
||||
|
@@ -427,6 +427,7 @@ Variables that Control the Build
|
||||
/variable/CMAKE_AUTOMOC
|
||||
/variable/CMAKE_AUTOMOC_COMPILER_PREDEFINES
|
||||
/variable/CMAKE_AUTOMOC_DEPEND_FILTERS
|
||||
/variable/CMAKE_AUTOMOC_INCLUDE_DIRECTORIES
|
||||
/variable/CMAKE_AUTOMOC_MACRO_NAMES
|
||||
/variable/CMAKE_AUTOMOC_MOC_OPTIONS
|
||||
/variable/CMAKE_AUTOMOC_PATH_PREFIX
|
||||
|
@@ -34,6 +34,11 @@ At configuration time, a list of header files that should be scanned by
|
||||
|
||||
and adds these to the scan list.
|
||||
|
||||
- The :prop_tgt:`AUTOMOC_INCLUDE_DIRECTORIES` target property may be set
|
||||
to explicitly tell ``moc`` what include directories to search. If not
|
||||
set, the default is to use the include directories from the target and
|
||||
its transitive closure of dependencies.
|
||||
|
||||
At build time, CMake scans each unknown or modified header file from the
|
||||
list and searches for
|
||||
|
||||
@@ -222,6 +227,10 @@ defining file name filters in this target property.
|
||||
Compiler pre definitions for ``moc`` are written to the ``moc_predefs.h`` file.
|
||||
The generation of this file can be enabled or disabled in this target property.
|
||||
|
||||
:prop_tgt:`AUTOMOC_INCLUDE_DIRECTORIES`:
|
||||
Specifies one or more include directories for ``AUTOMOC`` to pass explicitly to ``moc``
|
||||
instead of automatically discovering a target’s include directories.
|
||||
|
||||
:prop_sf:`SKIP_AUTOMOC`:
|
||||
Sources and headers can be excluded from ``AUTOMOC`` processing by
|
||||
setting this source file property.
|
||||
|
30
Help/prop_tgt/AUTOMOC_INCLUDE_DIRECTORIES.rst
Normal file
30
Help/prop_tgt/AUTOMOC_INCLUDE_DIRECTORIES.rst
Normal file
@@ -0,0 +1,30 @@
|
||||
AUTOMOC_INCLUDE_DIRECTORIES
|
||||
---------------------------
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
Specifies zero or more include directories for AUTOMOC to pass explicitly to
|
||||
the Qt Meta‑Object Compiler (``moc``) instead of automatically discovering a
|
||||
target's include directories.
|
||||
|
||||
When this property is set on a target, only the directories listed here will be
|
||||
used by :prop_tgt:`AUTOMOC`, and any other include paths will be ignored.
|
||||
|
||||
This property may contain :manual:`generator expressions <cmake-generator-expressions(7)>`.
|
||||
|
||||
All directory paths in the final evaluated result **must be absolute**. If any
|
||||
non-absolute paths are present after generator expression evaluation,
|
||||
configuration will fail with an error.
|
||||
|
||||
See also the :variable:`CMAKE_AUTOMOC_INCLUDE_DIRECTORIES` variable, which can
|
||||
be used to initialize this property on all targets.
|
||||
|
||||
Example
|
||||
^^^^^^^
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
add_library(myQtLib ...)
|
||||
set_property(TARGET myQtLib PROPERTY AUTOMOC_INCLUDE_DIRECTORIES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/myQtLib"
|
||||
)
|
7
Help/release/dev/automoc-include-directories.rst
Normal file
7
Help/release/dev/automoc-include-directories.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
automoc-include-directories
|
||||
---------------------------
|
||||
|
||||
* The :prop_tgt:`AUTOMOC_INCLUDE_DIRECTORIES` target property and associated
|
||||
:variable:`CMAKE_AUTOMOC_INCLUDE_DIRECTORIES` variable were added to
|
||||
override the automatic discovery of moc includes from a target's transitive
|
||||
include directories.
|
15
Help/variable/CMAKE_AUTOMOC_INCLUDE_DIRECTORIES.rst
Normal file
15
Help/variable/CMAKE_AUTOMOC_INCLUDE_DIRECTORIES.rst
Normal file
@@ -0,0 +1,15 @@
|
||||
CMAKE_AUTOMOC_INCLUDE_DIRECTORIES
|
||||
---------------------------------
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
Specifies zero or more include directories for AUTOMOC to pass explicitly to
|
||||
the Qt Meta‑Object Compiler (``moc``) instead of automatically discovering
|
||||
each target's include directories.
|
||||
|
||||
The directories listed here will replace any include paths discovered from
|
||||
target properties such as :prop_tgt:`INCLUDE_DIRECTORIES`.
|
||||
|
||||
This variable is used to initialize the :prop_tgt:`AUTOMOC_INCLUDE_DIRECTORIES`
|
||||
property on all the targets. See that target property for additional
|
||||
information.
|
@@ -723,33 +723,71 @@ bool cmQtAutoGenInitializer::InitMoc()
|
||||
|
||||
// Moc includes
|
||||
{
|
||||
SearchPathSanitizer const sanitizer(this->Makefile);
|
||||
auto getDirs =
|
||||
[this, &sanitizer](std::string const& cfg) -> std::vector<std::string> {
|
||||
// Get the include dirs for this target, without stripping the implicit
|
||||
// include dirs off, see issue #13667.
|
||||
std::vector<std::string> dirs;
|
||||
bool const appendImplicit = (this->QtVersion.Major >= 5);
|
||||
this->LocalGen->GetIncludeDirectoriesImplicit(
|
||||
dirs, this->GenTarget, "CXX", cfg, false, appendImplicit);
|
||||
return sanitizer(dirs);
|
||||
};
|
||||
// If the property AUTOMOC_INCLUDE_DIRECTORIES is set on the target,
|
||||
// use its value for moc include paths instead of gathering all
|
||||
// include directories from the target.
|
||||
cmValue autoIncDirs =
|
||||
this->GenTarget->GetProperty("AUTOMOC_INCLUDE_DIRECTORIES");
|
||||
if (autoIncDirs) {
|
||||
cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
|
||||
cmGeneratorExpression ge(*this->Makefile->GetCMakeInstance(), lfbt);
|
||||
auto cge = ge.Parse(*autoIncDirs);
|
||||
|
||||
// Other configuration settings
|
||||
if (this->MultiConfig) {
|
||||
for (std::string const& cfg : this->ConfigsList) {
|
||||
std::vector<std::string> dirs = getDirs(cfg);
|
||||
if (dirs == this->Moc.Includes.Default) {
|
||||
continue;
|
||||
// Build a single list of configs to iterate, whether single or multi
|
||||
std::vector<std::string> configs = this->MultiConfig
|
||||
? this->ConfigsList
|
||||
: std::vector<std::string>{ this->ConfigDefault };
|
||||
|
||||
for (auto const& cfg : configs) {
|
||||
std::string eval = cge->Evaluate(this->LocalGen, cfg);
|
||||
std::vector<std::string> incList = cmList(eval);
|
||||
|
||||
// Validate absolute paths
|
||||
for (auto const& path : incList) {
|
||||
if (!cmGeneratorExpression::StartsWithGeneratorExpression(path) &&
|
||||
!cmSystemTools::FileIsFullPath(path)) {
|
||||
this->Makefile->IssueMessage(
|
||||
MessageType::FATAL_ERROR,
|
||||
cmStrCat("AUTOMOC_INCLUDE_DIRECTORIES: path '", path,
|
||||
"' is not absolute."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this->MultiConfig) {
|
||||
this->Moc.Includes.Config[cfg] = std::move(incList);
|
||||
} else {
|
||||
this->Moc.Includes.Default = std::move(incList);
|
||||
}
|
||||
this->Moc.Includes.Config[cfg] = std::move(dirs);
|
||||
}
|
||||
} else {
|
||||
// Default configuration include directories
|
||||
this->Moc.Includes.Default = getDirs(this->ConfigDefault);
|
||||
// Otherwise, discover include directories from the target for moc.
|
||||
SearchPathSanitizer const sanitizer(this->Makefile);
|
||||
auto getDirs = [this, &sanitizer](
|
||||
std::string const& cfg) -> std::vector<std::string> {
|
||||
// Get the include dirs for this target, without stripping the implicit
|
||||
// include dirs off, see issue #13667.
|
||||
std::vector<std::string> dirs;
|
||||
bool const appendImplicit = (this->QtVersion.Major >= 5);
|
||||
this->LocalGen->GetIncludeDirectoriesImplicit(
|
||||
dirs, this->GenTarget, "CXX", cfg, false, appendImplicit);
|
||||
return sanitizer(dirs);
|
||||
};
|
||||
|
||||
// Other configuration settings
|
||||
if (this->MultiConfig) {
|
||||
for (std::string const& cfg : this->ConfigsList) {
|
||||
std::vector<std::string> dirs = getDirs(cfg);
|
||||
if (dirs == this->Moc.Includes.Default) {
|
||||
continue;
|
||||
}
|
||||
this->Moc.Includes.Config[cfg] = std::move(dirs);
|
||||
}
|
||||
} else {
|
||||
// Default configuration include directories
|
||||
this->Moc.Includes.Default = getDirs(this->ConfigDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Moc compile definitions
|
||||
{
|
||||
auto getDefs = [this](std::string const& cfg) -> std::set<std::string> {
|
||||
|
@@ -381,6 +381,7 @@ TargetProperty const StaticTargetProperties[] = {
|
||||
// ---- moc
|
||||
{ "AUTOMOC"_s, IC::CanCompileSources },
|
||||
{ "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources },
|
||||
{ "AUTOMOC_INCLUDE_DIRECTORIES"_s, IC::CanCompileSources },
|
||||
{ "AUTOMOC_MACRO_NAMES"_s, IC::CanCompileSources },
|
||||
{ "AUTOMOC_MOC_OPTIONS"_s, IC::CanCompileSources },
|
||||
{ "AUTOMOC_PATH_PREFIX"_s, IC::CanCompileSources },
|
||||
|
@@ -0,0 +1,46 @@
|
||||
# Read the JSON file into a variable
|
||||
set(autogenInfoFilePath "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/foo_autogen.dir/AutogenInfo.json")
|
||||
|
||||
if(NOT IS_READABLE "${autogenInfoFilePath}")
|
||||
set(RunCMake_TEST_FAILED "Expected autogen info file missing:\n \"${autogenInfoFilePath}\"")
|
||||
return()
|
||||
endif()
|
||||
file(READ "${autogenInfoFilePath}" jsonRaw)
|
||||
|
||||
# If multi-config generator, we are looking for MOC_INCLUDES_<CONFIG>.
|
||||
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
set(mocKey "MOC_INCLUDES_Debug") # Pick one arbitrarily (they will all be the same in this test)
|
||||
# If single-config generator, we are looking for MOC_INCLUDES.
|
||||
else()
|
||||
set(mocKey "MOC_INCLUDES")
|
||||
endif()
|
||||
|
||||
string(JSON actualValue GET "${jsonRaw}" "${mocKey}")
|
||||
|
||||
# The format of the MOC_INCLUDES entries in AutogenInfo.json depends on how long the paths are.
|
||||
# For short entries:
|
||||
# "MOC_INCLUDES" : [ "<SHORT_PATH>" ]
|
||||
# For long entries:
|
||||
# "MOC_INCLUDES_Debug" :
|
||||
# [
|
||||
# "<SOME_PARTICULARLY_LONG_PATH>"
|
||||
# ],
|
||||
|
||||
# Also, paths given to AUTOMOC_INCLUDE_DIRECTORIES must be absolute paths.
|
||||
# The code uses SystemTools::FileIsFullPath() to verify this, and it accepts
|
||||
# a forward slash at the beginning for both Windows (network path) and UNIX platforms.
|
||||
# Therefore, for the simplicity of this test, use a dummy value "/pass".
|
||||
|
||||
# Strip the JSON format around the string for a true before/after comparison.
|
||||
string(REPLACE "[ \"" "" actualValue ${actualValue})
|
||||
string(REPLACE "\" ]" "" actualValue ${actualValue})
|
||||
|
||||
# Final pass/fail comparison.
|
||||
set(expectedValue "/pass")
|
||||
|
||||
if (NOT actualValue STREQUAL expectedValue)
|
||||
set(RunCMake_TEST_FAILED "AUTOMOC_INCLUDE_DIRECTORIES override property not honored.")
|
||||
string(APPEND RunCMake_TEST_FAILURE_MESSAGE
|
||||
"Expected MOC_INCLUDES in AutogenInfo.json to have ${expectedValue} but found ${actualValue}."
|
||||
)
|
||||
endif()
|
16
Tests/RunCMake/Autogen_7/AutoMocIncludeDirectories.cmake
Normal file
16
Tests/RunCMake/Autogen_7/AutoMocIncludeDirectories.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
enable_language(CXX)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core)
|
||||
|
||||
# Create a test library with an arbitrary include directory to later override with the property
|
||||
add_library(foo STATIC ../Autogen_common/example.cpp)
|
||||
target_include_directories(foo PRIVATE ../Autogen_common/example.h)
|
||||
|
||||
# Set AUTOMOC_INCLUDE_DIRECTORIES with a test value to verify it replaces the above include directory
|
||||
# in AutogenInfo.json's MOC_INCLUDES list.
|
||||
# See comments in the -check.cmake counterpart for more information about this test.
|
||||
set_target_properties(foo PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTOMOC_INCLUDE_DIRECTORIES "/pass"
|
||||
)
|
3
Tests/RunCMake/Autogen_7/CMakeLists.txt
Normal file
3
Tests/RunCMake/Autogen_7/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
10
Tests/RunCMake/Autogen_7/RunCMakeTest.cmake
Normal file
10
Tests/RunCMake/Autogen_7/RunCMakeTest.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
include(RunCMake)
|
||||
|
||||
if (DEFINED with_qt_version)
|
||||
set(RunCMake_TEST_OPTIONS
|
||||
-Dwith_qt_version=${with_qt_version}
|
||||
"-DQt${with_qt_version}_DIR:PATH=${Qt${with_qt_version}_DIR}"
|
||||
"-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
|
||||
)
|
||||
run_cmake(AutoMocIncludeDirectories)
|
||||
endif()
|
@@ -291,7 +291,7 @@ if(CMake_TEST_APPLE_SILICON)
|
||||
add_RunCMake_test(AppleSilicon)
|
||||
endif()
|
||||
set(want_NoQt_test TRUE)
|
||||
set(autogen_test_number 1 2 3 4 5 6)
|
||||
set(autogen_test_number 1 2 3 4 5 6 7)
|
||||
if(CMake_TEST_Qt6 AND Qt6Widgets_FOUND)
|
||||
# Work around Qt6 not finding sibling dependencies without CMAKE_PREFIX_PATH
|
||||
cmake_path(GET Qt6_DIR PARENT_PATH base_dir) # <base>/lib/cmake
|
||||
|
Reference in New Issue
Block a user