Просмотр исходного кода

Merge topic 'automoc-specific-include-dirs'

39677f4cc6 AUTOMOC: Add option to specify moc include directories explicitly

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Acked-by: Osyotr <[email protected]>
Merge-request: !10640
Brad King 5 месяцев назад
Родитель
Сommit
fc9e9d1196

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -145,6 +145,7 @@ Properties on Targets
    /prop_tgt/AUTOMOC_COMPILER_PREDEFINES
    /prop_tgt/AUTOMOC_COMPILER_PREDEFINES
    /prop_tgt/AUTOMOC_DEPEND_FILTERS
    /prop_tgt/AUTOMOC_DEPEND_FILTERS
    /prop_tgt/AUTOMOC_EXECUTABLE
    /prop_tgt/AUTOMOC_EXECUTABLE
+   /prop_tgt/AUTOMOC_INCLUDE_DIRECTORIES
    /prop_tgt/AUTOMOC_MACRO_NAMES
    /prop_tgt/AUTOMOC_MACRO_NAMES
    /prop_tgt/AUTOMOC_MOC_OPTIONS
    /prop_tgt/AUTOMOC_MOC_OPTIONS
    /prop_tgt/AUTOMOC_PATH_PREFIX
    /prop_tgt/AUTOMOC_PATH_PREFIX

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -427,6 +427,7 @@ Variables that Control the Build
    /variable/CMAKE_AUTOMOC
    /variable/CMAKE_AUTOMOC
    /variable/CMAKE_AUTOMOC_COMPILER_PREDEFINES
    /variable/CMAKE_AUTOMOC_COMPILER_PREDEFINES
    /variable/CMAKE_AUTOMOC_DEPEND_FILTERS
    /variable/CMAKE_AUTOMOC_DEPEND_FILTERS
+   /variable/CMAKE_AUTOMOC_INCLUDE_DIRECTORIES
    /variable/CMAKE_AUTOMOC_MACRO_NAMES
    /variable/CMAKE_AUTOMOC_MACRO_NAMES
    /variable/CMAKE_AUTOMOC_MOC_OPTIONS
    /variable/CMAKE_AUTOMOC_MOC_OPTIONS
    /variable/CMAKE_AUTOMOC_PATH_PREFIX
    /variable/CMAKE_AUTOMOC_PATH_PREFIX

+ 9 - 0
Help/prop_tgt/AUTOMOC.rst

@@ -34,6 +34,11 @@ At configuration time, a list of header files that should be scanned by
 
 
   and adds these to the scan list.
   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
 At build time, CMake scans each unknown or modified header file from the
 list and searches for
 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.
 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.
 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`:
 :prop_sf:`SKIP_AUTOMOC`:
 Sources and headers can be excluded from ``AUTOMOC`` processing by
 Sources and headers can be excluded from ``AUTOMOC`` processing by
 setting this source file property.
 setting this source file property.

+ 30 - 0
Help/prop_tgt/AUTOMOC_INCLUDE_DIRECTORIES.rst

@@ -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 - 0
Help/release/dev/automoc-include-directories.rst

@@ -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 - 0
Help/variable/CMAKE_AUTOMOC_INCLUDE_DIRECTORIES.rst

@@ -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.

+ 60 - 22
Source/cmQtAutoGenInitializer.cxx

@@ -723,33 +723,71 @@ bool cmQtAutoGenInitializer::InitMoc()
 
 
   // Moc includes
   // 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);
-    };
-
-    // 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;
+    // 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);
+
+      // 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 {
     } 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
   // Moc compile definitions
   {
   {
     auto getDefs = [this](std::string const& cfg) -> std::set<std::string> {
     auto getDefs = [this](std::string const& cfg) -> std::set<std::string> {

+ 1 - 0
Source/cmTarget.cxx

@@ -381,6 +381,7 @@ TargetProperty const StaticTargetProperties[] = {
   // ---- moc
   // ---- moc
   { "AUTOMOC"_s, IC::CanCompileSources },
   { "AUTOMOC"_s, IC::CanCompileSources },
   { "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources },
   { "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources },
+  { "AUTOMOC_INCLUDE_DIRECTORIES"_s, IC::CanCompileSources },
   { "AUTOMOC_MACRO_NAMES"_s, IC::CanCompileSources },
   { "AUTOMOC_MACRO_NAMES"_s, IC::CanCompileSources },
   { "AUTOMOC_MOC_OPTIONS"_s, IC::CanCompileSources },
   { "AUTOMOC_MOC_OPTIONS"_s, IC::CanCompileSources },
   { "AUTOMOC_PATH_PREFIX"_s, IC::CanCompileSources },
   { "AUTOMOC_PATH_PREFIX"_s, IC::CanCompileSources },

+ 46 - 0
Tests/RunCMake/Autogen_7/AutoMocIncludeDirectories-check.cmake

@@ -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 - 0
Tests/RunCMake/Autogen_7/AutoMocIncludeDirectories.cmake

@@ -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 - 0
Tests/RunCMake/Autogen_7/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.16)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 10 - 0
Tests/RunCMake/Autogen_7/RunCMakeTest.cmake

@@ -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()

+ 1 - 1
Tests/RunCMake/CMakeLists.txt

@@ -291,7 +291,7 @@ if(CMake_TEST_APPLE_SILICON)
   add_RunCMake_test(AppleSilicon)
   add_RunCMake_test(AppleSilicon)
 endif()
 endif()
 set(want_NoQt_test TRUE)
 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)
 if(CMake_TEST_Qt6 AND Qt6Widgets_FOUND)
   # Work around Qt6 not finding sibling dependencies without CMAKE_PREFIX_PATH
   # Work around Qt6 not finding sibling dependencies without CMAKE_PREFIX_PATH
   cmake_path(GET Qt6_DIR  PARENT_PATH base_dir)  # <base>/lib/cmake
   cmake_path(GET Qt6_DIR  PARENT_PATH base_dir)  # <base>/lib/cmake