Browse Source

Merge topic 'autogen_depends'

62a1e292 Autogen: Add hint to SKIP_AUTOMOC in error message
e98465cf Autogen: Add Quote function and use it for logging
96f6f392 Autogen: Add AUTOMOC_DEPEND_FILTERS documentation
94e3f82c Autogen: Add AUTOMOC_DEPEND_FILTERS test
70ebf35c Autogen: Add AUTOMOC_DEPEND_FILTERS support
2a6fd432 Autogen: Single point of return in Run() method
e9a8a207 Autogen: Log simplifications
Brad King 8 years ago
parent
commit
31282e7854

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

@@ -115,6 +115,7 @@ Properties on Targets
    /prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG
    /prop_tgt/ARCHIVE_OUTPUT_NAME
    /prop_tgt/AUTOGEN_TARGET_DEPENDS
+   /prop_tgt/AUTOMOC_DEPEND_FILTERS
    /prop_tgt/AUTOMOC_MOC_OPTIONS
    /prop_tgt/AUTOMOC
    /prop_tgt/AUTOUIC

+ 3 - 0
Help/manual/cmake-qt.7.rst

@@ -87,6 +87,9 @@ following targets by setting the :variable:`CMAKE_AUTOMOC` variable.  The
 options to pass to ``moc``. The :variable:`CMAKE_AUTOMOC_MOC_OPTIONS`
 variable may be populated to pre-set the options for all following targets.
 
+Additional ``moc`` dependency file names can be extracted from source code
+by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`.
+
 Source C++ files can be excluded from :prop_tgt:`AUTOMOC` processing by
 enabling :prop_sf:`SKIP_AUTOMOC` or the broader :prop_sf:`SKIP_AUTOGEN`.
 

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

@@ -256,6 +256,7 @@ Variables that Control the Build
    /variable/CMAKE_ANDROID_STL_TYPE
    /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY
    /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG
+   /variable/CMAKE_AUTOMOC_DEPEND_FILTERS
    /variable/CMAKE_AUTOMOC_MOC_OPTIONS
    /variable/CMAKE_AUTOMOC
    /variable/CMAKE_AUTORCC

+ 3 - 0
Help/prop_tgt/AUTOMOC.rst

@@ -57,6 +57,9 @@ See the documentation for this variable for more details.
 The global property :prop_gbl:`AUTOGEN_TARGETS_FOLDER` can be used to group the
 automoc targets together in an IDE, e.g.  in MSVS.
 
+Additional ``moc`` dependency file names can be extracted from source code
+by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`.
+
 Source C++ files can be excluded from :prop_tgt:`AUTOMOC` processing by
 enabling :prop_sf:`SKIP_AUTOMOC` or the broader :prop_sf:`SKIP_AUTOGEN`.
 

+ 45 - 0
Help/prop_tgt/AUTOMOC_DEPEND_FILTERS.rst

@@ -0,0 +1,45 @@
+AUTOMOC_DEPEND_FILTERS
+----------------------
+
+Filter definitions used by :prop_tgt:`AUTOMOC` to extract file names from
+source code as additional dependencies for the ``moc`` file.
+
+This property is only used if the :prop_tgt:`AUTOMOC` property is ``ON``
+for this target.
+
+Filters are defined as ``KEYWORD;REGULAR_EXPRESSION`` pairs. First the file
+content is searched for ``KEYWORD``. If it is found at least once, then file
+names are extracted by successively searching for ``REGULAR_EXPRESSION`` and
+taking the first match group.
+
+Consider a filter extracts the file name ``DEP`` from the content of a file
+``FOO``. If ``DEP`` changes, then the ``moc`` file for ``FOO`` gets rebuilt.
+The file ``DEP`` is searched for first in the vicinity
+of ``FOO`` and afterwards in the target's :prop_tgt:`INCLUDE_DIRECTORIES`.
+
+By default :prop_tgt:`AUTOMOC_DEPEND_FILTERS` is initialized from
+:variable:`CMAKE_AUTOMOC_DEPEND_FILTERS`, which is empty by default.
+
+See the :manual:`cmake-qt(7)` manual for more information on using CMake
+with Qt.
+
+
+Example
+-------
+
+Consider a file ``FOO.hpp`` holds a custom macro ``OBJ_JSON_FILE`` and we
+want the ``moc`` file to depend on the macro`s file name argument::
+
+  class My_Class : public QObject
+  {
+    Q_OBJECT
+    OBJ_JSON_FILE ( "DEP.json" )
+  ...
+  };
+
+Then we might use :variable:`CMAKE_AUTOMOC_DEPEND_FILTERS` to
+define a filter like this::
+
+  set(CMAKE_AUTOMOC_DEPEND_FILTERS
+    "OBJ_JSON_FILE" "[\n][ \t]*OBJ_JSON_FILE[ \t]*\\([ \t]*\"([^\"]+)\""
+  )

+ 10 - 0
Help/release/dev/Autogen_depends.rst

@@ -0,0 +1,10 @@
+AutoGen depends
+---------------
+
+* Variable :variable:`CMAKE_AUTOMOC_DEPEND_FILTERS` was introduced to
+  allow :variable:`CMAKE_AUTOMOC` to extract additional dependency file names
+  for ``moc`` from the contents of source files.
+
+* The new target property :prop_tgt:`AUTOMOC_DEPEND_FILTERS` was introduced to
+  allow :prop_tgt:`AUTOMOC` to extract additional dependency file names
+  for ``moc`` from the contents of source files.

+ 12 - 0
Help/variable/CMAKE_AUTOMOC_DEPEND_FILTERS.rst

@@ -0,0 +1,12 @@
+CMAKE_AUTOMOC_DEPEND_FILTERS
+----------------------------
+
+Filter definitions used by :variable:`CMAKE_AUTOMOC`
+to extract file names from source code as additional dependencies
+for the ``moc`` file.
+
+This variable is used to initialize the :prop_tgt:`AUTOMOC_DEPEND_FILTERS`
+property on all the targets. See that target property for additional
+information.
+
+By default it is empty.

+ 1 - 0
Modules/AutogenInfo.cmake.in

@@ -20,6 +20,7 @@ set(AM_MOC_COMPILE_DEFINITIONS @_moc_compile_defs@)
 set(AM_MOC_INCLUDES @_moc_incs@)
 set(AM_MOC_OPTIONS @_moc_options@)
 set(AM_MOC_RELAXED_MODE @_moc_relaxed_mode@)
+set(AM_MOC_DEPEND_FILTERS @_moc_depend_filters@)
 # UIC settings
 set(AM_UIC_SKIP @_uic_skip@)
 set(AM_UIC_TARGET_OPTIONS @_uic_target_options@)

+ 2 - 0
Source/cmQtAutoGeneratorInitializer.cxx

@@ -218,6 +218,8 @@ static void MocSetupAutoTarget(
   AddDefinitionEscaped(makefile, "_moc_relaxed_mode",
                        makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE") ? "TRUE"
                                                                     : "FALSE");
+  AddDefinitionEscaped(makefile, "_moc_depend_filters",
+                       GetSafeProperty(target, "AUTOMOC_DEPEND_FILTERS"));
 
   // Moc includes and compile definitions
   {

+ 154 - 95
Source/cmQtAutoGenerators.cxx

@@ -36,6 +36,25 @@ static const char* SettingsKeyRcc = "AM_RCC_OLD_SETTINGS";
 
 // -- Static functions
 
+/**
+ * @brief Returns a the string escaped and enclosed in quotes
+ */
+static std::string Quoted(const std::string& text)
+{
+  static const char* rep[18] = { "\\", "\\\\", "\"", "\\\"", "\a", "\\a",
+                                 "\b", "\\b",  "\f", "\\f",  "\n", "\\n",
+                                 "\r", "\\r",  "\t", "\\t",  "\v", "\\v" };
+
+  std::string res = text;
+  for (const char* const* it = cmArrayBegin(rep); it != cmArrayEnd(rep);
+       it += 2) {
+    cmSystemTools::ReplaceString(res, *it, *(it + 1));
+  }
+  res = '"' + res;
+  res += '"';
+  return res;
+}
+
 static std::string GetConfigDefinition(cmMakefile* makefile,
                                        const std::string& key,
                                        const std::string& config)
@@ -244,23 +263,46 @@ bool cmQtAutoGenerators::Run(const std::string& targetDirectory,
   CM_AUTO_PTR<cmMakefile> mf(new cmMakefile(&gg, snapshot));
   gg.SetCurrentMakefile(mf.get());
 
-  if (!this->ReadAutogenInfoFile(mf.get(), targetDirectory, config)) {
-    return false;
-  }
-  // Read old settings
-  this->SettingsFileRead(mf.get(), targetDirectory);
-  // Init and run
-  this->Init(mf.get());
-  if (this->QtMajorVersion == "4" || this->QtMajorVersion == "5") {
-    if (!this->RunAutogen()) {
-      return false;
+  bool success = false;
+  if (this->ReadAutogenInfoFile(mf.get(), targetDirectory, config)) {
+    // Read old settings
+    this->SettingsFileRead(mf.get(), targetDirectory);
+    // Init and run
+    this->Init(mf.get());
+    if (this->RunAutogen()) {
+      // Write current settings
+      if (this->SettingsFileWrite(targetDirectory)) {
+        success = true;
+      }
     }
   }
-  // Write latest settings
-  if (!this->SettingsFileWrite(targetDirectory)) {
-    return false;
+  return success;
+}
+
+bool cmQtAutoGenerators::MocDependFilterPush(const std::string& key,
+                                             const std::string& regExp)
+{
+  bool success = false;
+  if (!key.empty()) {
+    if (!regExp.empty()) {
+      MocDependFilter filter;
+      filter.key = key;
+      if (filter.regExp.compile(regExp)) {
+        this->MocDependFilters.push_back(filter);
+        success = true;
+      } else {
+        this->LogError("AutoMoc: Error in AUTOMOC_DEPEND_FILTERS: Compiling "
+                       "regular expression failed.\nKey:  " +
+                       Quoted(key) + "\nExp.: " + Quoted(regExp));
+      }
+    } else {
+      this->LogError("AutoMoc: Error in AUTOMOC_DEPEND_FILTERS: Regular "
+                     "expression is empty");
+    }
+  } else {
+    this->LogError("AutoMoc: Error in AUTOMOC_DEPEND_FILTERS: Key is empty");
   }
-  return true;
+  return success;
 }
 
 bool cmQtAutoGenerators::ReadAutogenInfoFile(
@@ -272,9 +314,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(
   filename += "/AutogenInfo.cmake";
 
   if (!makefile->ReadListFile(filename.c_str())) {
-    std::ostringstream err;
-    err << "AutoGen: error processing file: " << filename << std::endl;
-    this->LogError(err.str());
+    this->LogError("AutoGen: Error processing file: " + filename);
     return false;
   }
 
@@ -297,6 +337,13 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(
     this->QtMajorVersion =
       makefile->GetSafeDefinition("AM_Qt5Core_VERSION_MAJOR");
   }
+  // Check Qt version
+  if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) {
+    this->LogError("AutoGen: Error: Unsupported Qt version: " +
+                   Quoted(this->QtMajorVersion));
+    return false;
+  }
+
   this->MocExecutable = makefile->GetSafeDefinition("AM_QT_MOC_EXECUTABLE");
   this->UicExecutable = makefile->GetSafeDefinition("AM_QT_UIC_EXECUTABLE");
   this->RccExecutable = makefile->GetSafeDefinition("AM_QT_RCC_EXECUTABLE");
@@ -318,6 +365,30 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(
     this->MocIncludePaths);
   cmSystemTools::ExpandListArgument(
     makefile->GetSafeDefinition("AM_MOC_OPTIONS"), this->MocOptions);
+  {
+    std::vector<std::string> mocDependFilters;
+    cmSystemTools::ExpandListArgument(
+      makefile->GetSafeDefinition("AM_MOC_DEPEND_FILTERS"), mocDependFilters);
+    // Insert Q_PLUGIN_METADATA dependency filter
+    if (this->QtMajorVersion != "4") {
+      this->MocDependFilterPush("Q_PLUGIN_METADATA",
+                                "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
+                                "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
+    }
+    // Insert user defined dependency filters
+    if ((mocDependFilters.size() % 2) == 0) {
+      for (std::vector<std::string>::const_iterator dit =
+             mocDependFilters.begin();
+           dit != mocDependFilters.end(); dit += 2) {
+        if (!this->MocDependFilterPush(*dit, *(dit + 1))) {
+          return false;
+        }
+      }
+    } else {
+      this->LogError("AutoMoc: Error: AUTOMOC_DEPEND_FILTERS list size is not "
+                     "a multiple of 2");
+    }
+  }
 
   // - Uic
   cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_UIC_SKIP"),
@@ -333,10 +404,9 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(
     cmSystemTools::ExpandListArgument(
       makefile->GetSafeDefinition("AM_UIC_OPTIONS_OPTIONS"), uicOptionsVec);
     if (uicFilesVec.size() != uicOptionsVec.size()) {
-      std::ostringstream err;
-      err << "AutoGen: Error: Uic files/options lists size missmatch in: "
-          << filename << std::endl;
-      this->LogError(err.str());
+      this->LogError(
+        "AutoGen: Error: Uic files/options lists size missmatch in: " +
+        filename);
       return false;
     }
     for (std::vector<std::string>::iterator fileIt = uicFilesVec.begin(),
@@ -358,10 +428,9 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(
     cmSystemTools::ExpandListArgument(
       makefile->GetSafeDefinition("AM_RCC_OPTIONS_OPTIONS"), rccOptionsVec);
     if (rccFilesVec.size() != rccOptionsVec.size()) {
-      std::ostringstream err;
-      err << "AutoGen: Error: RCC files/options lists size missmatch in: "
-          << filename << std::endl;
-      this->LogError(err.str());
+      this->LogError(
+        "AutoGen: Error: RCC files/options lists size missmatch in: " +
+        filename);
       return false;
     }
     for (std::vector<std::string>::iterator fileIt = rccFilesVec.begin(),
@@ -381,10 +450,9 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(
       rccInputLists.resize(this->RccSources.size());
     }
     if (this->RccSources.size() != rccInputLists.size()) {
-      std::ostringstream err;
-      err << "AutoGen: Error: RCC sources/inputs lists size missmatch in: "
-          << filename << std::endl;
-      this->LogError(err.str());
+      this->LogError(
+        "AutoGen: Error: RCC sources/inputs lists size missmatch in: " +
+        filename);
       return false;
     }
     for (std::vector<std::string>::iterator fileIt = this->RccSources.begin(),
@@ -480,12 +548,8 @@ bool cmQtAutoGenerators::SettingsFileWrite(const std::string& targetDirectory)
       success = false;
       // Remove old settings file to trigger full rebuild on next run
       cmSystemTools::RemoveFile(filename);
-      {
-        std::ostringstream err;
-        err << "AutoGen: Error: Writing old settings file failed: "
-            << filename;
-        this->LogError(err.str());
-      }
+      this->LogError("AutoGen: Error: Writing old settings file failed: " +
+                     filename);
     }
   }
   return success;
@@ -564,16 +628,6 @@ void cmQtAutoGenerators::Init(cmMakefile* makefile)
       this->MocIncludes.push_back(*it);
     }
   }
-
-  // Insert MocDependFilter for Q_PLUGIN_METADATA
-  if (QtMajorVersion != "4") {
-    MocDependFilter filter;
-    filter.key = "Q_PLUGIN_METADATA";
-    filter.regExp.compile("[\n][ \t]*"
-                          "Q_PLUGIN_METADATA[ \t]*\\("
-                          "[^\\)]*FILE[ \t]*\"([^\"]+)\"");
-    this->MocDependFilters.push_back(filter);
-  }
 }
 
 bool cmQtAutoGenerators::RunAutogen()
@@ -684,13 +738,13 @@ void cmQtAutoGenerators::MocFindDepends(
           if (!incFile.empty()) {
             mocDepends[absFilename].insert(incFile);
             if (this->Verbose) {
-              this->LogInfo("AutoMoc: Found dependency:\n  \"" + absFilename +
-                            "\"\n  \"" + incFile + "\"");
+              this->LogInfo("AutoMoc: Found dependency:\n  " +
+                            Quoted(absFilename) + "\n  " + Quoted(incFile));
             }
           } else {
-            this->LogWarning("AutoMoc: Warning: \"" + absFilename + "\"\n" +
-                             "Could not find dependency file \"" + match +
-                             "\"");
+            this->LogWarning("AutoMoc: Warning: " + Quoted(absFilename) +
+                             "\n" + "Could not find dependency file " +
+                             Quoted(match));
           }
         }
         contentChars += filter.regExp.end();
@@ -843,9 +897,11 @@ bool cmQtAutoGenerators::MocParseSourceContent(
         } else {
           std::ostringstream ost;
           ost << "AutoMoc: Error: " << absFilename << "\n"
-              << "The file includes the moc file \"" << incString
-              << "\", but could not find header \"" << incRealBasename << '{'
-              << JoinExts(this->HeaderExtensions) << "}\"\n";
+              << "The file includes the moc file " << Quoted(incString)
+              << ", but could not find header "
+              << Quoted(incRealBasename + "{" +
+                        JoinExts(this->HeaderExtensions) + "}");
+          ;
           this->LogError(ost.str());
           return false;
         }
@@ -867,32 +923,33 @@ bool cmQtAutoGenerators::MocParseSourceContent(
               fileToMoc = headerToMoc;
               if (!requiresMoc && (incBasename == scannedFileBasename)) {
                 std::ostringstream ost;
-                ost << "AutoMoc: Warning: " << absFilename << "\n"
-                    << "The file includes the moc file \"" << incString << "\""
+                ost << "AutoMoc: Warning: " << Quoted(absFilename) << "\n"
+                    << "The file includes the moc file " << Quoted(incString)
                     << ", but does not contain a Q_OBJECT or Q_GADGET macro.\n"
-                    << "Running moc on \"" << headerToMoc << "\"!\n"
-                    << "Include \"moc_" << incBasename
-                    << ".cpp\" for a compatibility with "
-                       "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n";
+                    << "Running moc on " << Quoted(headerToMoc) << "!\n"
+                    << "Include " << Quoted("moc_" + incBasename + ".cpp")
+                    << " for a compatibility with strict mode (see "
+                       "CMAKE_AUTOMOC_RELAXED_MODE).\n";
                 this->LogWarning(ost.str());
               } else {
                 std::ostringstream ost;
-                ost << "AutoMoc: Warning: " << absFilename << "\n"
-                    << "The file includes the moc file \"" << incString
-                    << "\" instead of \"moc_" << incBasename << ".cpp\".\n"
-                    << "Running moc on \"" << headerToMoc << "\"!\n"
-                    << "Include \"moc_" << incBasename
-                    << ".cpp\" for compatibility with "
-                       "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n";
+                ost << "AutoMoc: Warning: " << Quoted(absFilename) << "\n"
+                    << "The file includes the moc file " << Quoted(incString)
+                    << " instead of " << Quoted("moc_" + incBasename + ".cpp")
+                    << ".\n"
+                    << "Running moc on " << Quoted(headerToMoc) << "!\n"
+                    << "Include " << Quoted("moc_" + incBasename + ".cpp")
+                    << " for compatibility with strict mode (see "
+                       "CMAKE_AUTOMOC_RELAXED_MODE).\n";
                 this->LogWarning(ost.str());
               }
             } else {
               std::ostringstream ost;
-              ost << "AutoMoc: Error: " << absFilename << "\n"
-                  << "The file includes the moc file \"" << incString
-                  << "\", which seems to be the moc file from a different "
+              ost << "AutoMoc: Error: " << Quoted(absFilename) << "\n"
+                  << "The file includes the moc file " << Quoted(incString)
+                  << ". which seems to be the moc file from a different "
                      "source file. CMake also could not find a matching "
-                     "header.\n";
+                     "header.";
               this->LogError(ost.str());
               return false;
             }
@@ -906,21 +963,21 @@ bool cmQtAutoGenerators::MocParseSourceContent(
             // Accept but issue a warning if moc isn't required
             if (!requiresMoc) {
               std::ostringstream ost;
-              ost << "AutoMoc: Error: " << absFilename << "\n"
-                  << "The file includes the moc file \"" << incString << "\""
+              ost << "AutoMoc: Error: " << Quoted(absFilename) << "\n"
+                  << "The file includes the moc file " << Quoted(incString)
                   << ", but does not contain a Q_OBJECT or Q_GADGET "
-                     "macro.\n";
+                     "macro.";
               this->LogWarning(ost.str());
             }
           } else {
             // Don't allow FOO.moc include other than self in strict mode
             std::ostringstream ost;
-            ost << "AutoMoc: Error: " << absFilename << "\n"
-                << "The file includes the moc file \"" << incString
-                << "\", which seems to be the moc file from a different "
-                   "source file. This is not supported. Include \""
-                << scannedFileBasename
-                << ".moc\" to run moc on this source file.\n";
+            ost << "AutoMoc: Error: " << Quoted(absFilename) << "\n"
+                << "The file includes the moc file " << Quoted(incString)
+                << ", which seems to be the moc file from a different "
+                   "source file. This is not supported. Include "
+                << Quoted(scannedFileBasename + ".moc")
+                << " to run moc on this source file.";
             this->LogError(ost.str());
             return false;
           }
@@ -943,15 +1000,15 @@ bool cmQtAutoGenerators::MocParseSourceContent(
     if (relaxed && !ownMocUnderscoreInclude.empty()) {
       // This is for KDE4 compatibility:
       std::ostringstream ost;
-      ost << "AutoMoc: Warning: " << absFilename << "\n"
+      ost << "AutoMoc: Warning: " << Quoted(absFilename) << "\n"
           << "The file contains a " << macroName
           << " macro, but does not include "
-          << "\"" << scannedFileBasename << ".moc\", but instead includes "
-          << "\"" << ownMocUnderscoreInclude << "\".\n"
-          << "Running moc on \"" << absFilename << "\"!\n"
-          << "Better include \"" << scannedFileBasename
-          << ".moc\" for compatibility with "
-             "strict mode (see CMAKE_AUTOMOC_RELAXED_MODE).\n";
+          << Quoted(scannedFileBasename + ".moc") << ", but instead includes "
+          << Quoted(ownMocUnderscoreInclude) << ".\n"
+          << "Running moc on " << Quoted(absFilename) << "!\n"
+          << "Better include " << Quoted(scannedFileBasename + ".moc")
+          << " for compatibility with strict mode (see "
+             "CMAKE_AUTOMOC_RELAXED_MODE).";
       this->LogWarning(ost.str());
 
       // Use scanned source file instead of scanned header file as moc source
@@ -962,10 +1019,12 @@ bool cmQtAutoGenerators::MocParseSourceContent(
     } else {
       // Otherwise always error out since it will not compile:
       std::ostringstream ost;
-      ost << "AutoMoc: Error: " << absFilename << "\n"
+      ost << "AutoMoc: Error: " << Quoted(absFilename) << "\n"
           << "The file contains a " << macroName
           << " macro, but does not include "
-          << "\"" << scannedFileBasename << ".moc\"!\n";
+          << Quoted(scannedFileBasename + ".moc") << "!\n"
+          << "Consider adding the include or enabling SKIP_AUTOMOC for this "
+             "file.";
       this->LogError(ost.str());
       return false;
     }
@@ -1150,13 +1209,13 @@ bool cmQtAutoGenerators::MocGenerateAll(
       outfile.open(this->MocCppFilenameAbs.c_str(), std::ios::trunc);
       if (!outfile) {
         success = false;
-        this->LogError("AutoMoc: error opening " + this->MocCppFilenameAbs);
+        this->LogError("AutoMoc: Error opening " + this->MocCppFilenameAbs);
       } else {
         outfile << automocSource;
         // Check for write errors
         if (!outfile.good()) {
           success = false;
-          this->LogError("AutoMoc: error writing " + this->MocCppFilenameAbs);
+          this->LogError("AutoMoc: Error writing " + this->MocCppFilenameAbs);
         }
       }
     }
@@ -1245,7 +1304,7 @@ bool cmQtAutoGenerators::MocGenerateFile(
         {
           std::ostringstream ost;
           ost << "AutoMoc: Error: moc process failed for\n";
-          ost << "\"" << mocFileRel << "\"\n";
+          ost << Quoted(mocFileRel) << "\n";
           ost << "AutoMoc: Command:\n" << cmJoin(cmd, " ") << "\n";
           ost << "AutoMoc: Command output:\n" << output << "\n";
           this->LogError(ost.str());
@@ -1384,8 +1443,8 @@ bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName,
         {
           std::ostringstream ost;
           ost << "AutoUic: Error: uic process failed for\n";
-          ost << "\"" << uicFileRel << "\" needed by\n";
-          ost << "\"" << realName << "\"\n";
+          ost << Quoted(uicFileRel) << " needed by\n";
+          ost << Quoted(realName) << "\n";
           ost << "AutoUic: Command:\n" << cmJoin(cmd, " ") << "\n";
           ost << "AutoUic: Command output:\n" << output << "\n";
           this->LogError(ost.str());
@@ -1523,7 +1582,7 @@ bool cmQtAutoGenerators::RccGenerateFile(const std::string& rccInputFile,
         {
           std::ostringstream ost;
           ost << "AutoRcc: Error: rcc process failed for\n";
-          ost << "\"" << rccOutputFile << "\"\n";
+          ost << Quoted(rccOutputFile) << "\n";
           ost << "AutoRcc: Command:\n" << cmJoin(cmd, " ") << "\n";
           ost << "AutoRcc: Command output:\n" << output << "\n";
           this->LogError(ost.str());
@@ -1768,7 +1827,7 @@ bool cmQtAutoGenerators::MakeParentDirectory(const std::string& filename) const
   if (!dirName.empty()) {
     success = cmsys::SystemTools::MakeDirectory(dirName);
     if (!success) {
-      this->LogError("AutoGen: Directory creation failed: " + dirName);
+      this->LogError("AutoGen: Error: Directory creation failed: " + dirName);
     }
   }
   return success;

+ 1 - 0
Source/cmQtAutoGenerators.h

@@ -33,6 +33,7 @@ private:
   typedef std::pair<std::string, cmsys::RegularExpression> MacroFilter;
 
   // - Configuration
+  bool MocDependFilterPush(const std::string& key, const std::string& regExp);
   bool ReadAutogenInfoFile(cmMakefile* makefile,
                            const std::string& targetDirectory,
                            const std::string& config);

+ 1 - 0
Source/cmTarget.cxx

@@ -245,6 +245,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     this->SetPropertyDefault("AUTOMOC", CM_NULLPTR);
     this->SetPropertyDefault("AUTOUIC", CM_NULLPTR);
     this->SetPropertyDefault("AUTORCC", CM_NULLPTR);
+    this->SetPropertyDefault("AUTOMOC_DEPEND_FILTERS", CM_NULLPTR);
     this->SetPropertyDefault("AUTOMOC_MOC_OPTIONS", CM_NULLPTR);
     this->SetPropertyDefault("AUTOUIC_OPTIONS", CM_NULLPTR);
     this->SetPropertyDefault("AUTORCC_OPTIONS", CM_NULLPTR);

+ 18 - 4
Tests/QtAutogen/CMakeLists.txt

@@ -247,10 +247,7 @@ if (NOT QT_TEST_VERSION STREQUAL 4)
   execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
   execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${mocPluginBinDir}/jsonFiles/StyleC.json")
   execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${mocPluginBinDir}/jsonFiles/sub/StyleD.json")
-
-  execute_process(COMMAND "${CMAKE_COMMAND}" --build .
-    WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mocPlugin"
-  )
+  execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPluginBinDir}")
 
   file(TIMESTAMP "${style_a_file}" style_a_after "${timeformat}")
   file(TIMESTAMP "${style_b_file}" style_b_after "${timeformat}")
@@ -269,6 +266,23 @@ if (NOT QT_TEST_VERSION STREQUAL 4)
   if (NOT style_d_after GREATER style_d_before)
     message(SEND_ERROR "file (${style_d_file}) should have changed!")
   endif()
+
+  # Test custom macro
+  file(TIMESTAMP "${style_c_file}" style_c_before "${timeformat}")
+  file(TIMESTAMP "${style_d_file}" style_d_before "${timeformat}")
+  execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1)
+  execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${mocPluginBinDir}/jsonFiles/StyleC_Custom.json")
+  execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${mocPluginBinDir}/jsonFiles/sub/StyleD_Custom.json")
+  execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPluginBinDir}")
+  file(TIMESTAMP "${style_c_file}" style_c_after "${timeformat}")
+  file(TIMESTAMP "${style_d_file}" style_d_after "${timeformat}")
+  if (NOT style_c_after GREATER style_c_before)
+    message(SEND_ERROR "file (${style_c_file}) should have changed!")
+  endif()
+  if (NOT style_d_after GREATER style_d_before)
+    message(SEND_ERROR "file (${style_d_file}) should have changed!")
+  endif()
+
 endif()
 
 # -- Test

+ 9 - 2
Tests/QtAutogen/mocPlugin/CMakeLists.txt

@@ -1,5 +1,10 @@
 cmake_minimum_required(VERSION 3.8)
 
+set(CMAKE_AUTOMOC_DEPEND_FILTERS
+  "A_CUSTOM_MACRO"
+  "[\n][ \t]*A_CUSTOM_MACRO[ \t\r\n]*\\([^,]+,[ \t\r\n]*\"([^\"]+)\""
+  )
+
 if (NOT QT_TEST_VERSION STREQUAL 5)
   message(SEND_ERROR "Invalid Qt version specified.")
 endif()
@@ -9,8 +14,10 @@ if(Qt5_POSITION_INDEPENDENT_CODE AND CMAKE_CXX_COMPILE_OPTIONS_PIC)
   add_definitions(${CMAKE_CXX_COMPILE_OPTIONS_PIC})
 endif()
 
-configure_file(jsonIn/StyleC.json jsonFiles/StyleC.json @ONLY)
-configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD.json @ONLY)
+configure_file(jsonIn/StyleC.json jsonFiles/StyleC.json)
+configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD.json)
+configure_file(jsonIn/StyleC.json jsonFiles/StyleC_Custom.json)
+configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD_Custom.json)
 
 # Enable automoc
 set(CMAKE_AUTOMOC TRUE)

+ 2 - 0
Tests/QtAutogen/mocPlugin/StyleA.hpp

@@ -1,6 +1,7 @@
 #ifndef STYLEA_HPP
 #define STYLEA_HPP
 
+#include "StyleCommon.hpp"
 #include <QStylePlugin>
 
 class StyleA : public QStylePlugin
@@ -8,6 +9,7 @@ class StyleA : public QStylePlugin
   Q_OBJECT
   // Json file in local directory
   Q_PLUGIN_METADATA(IID "org.styles.A" FILE "StyleA.json")
+  A_CUSTOM_MACRO(SomeArg, "StyleA_Custom.json", AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 1 - 0
Tests/QtAutogen/mocPlugin/StyleA_Custom.json

@@ -0,0 +1 @@
+{ "Keys": [ "Rocket", "Starbuster" ] }

+ 2 - 0
Tests/QtAutogen/mocPlugin/StyleB.hpp

@@ -1,6 +1,7 @@
 #ifndef STYLEB_HPP
 #define STYLEB_HPP
 
+#include "StyleCommon.hpp"
 #include <QStylePlugin>
 
 class StyleB : public QStylePlugin
@@ -8,6 +9,7 @@ class StyleB : public QStylePlugin
   Q_OBJECT
   // Json file in local subdirectory
   Q_PLUGIN_METADATA(IID "org.styles.B" FILE "jsonIn/StyleB.json")
+  A_CUSTOM_MACRO(SomeArg, "jsonIn/StyleB_Custom.json", AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 2 - 0
Tests/QtAutogen/mocPlugin/StyleC.hpp

@@ -1,6 +1,7 @@
 #ifndef STYLEC_HPP
 #define STYLEC_HPP
 
+#include "StyleCommon.hpp"
 #include <QStylePlugin>
 
 class StyleC : public QStylePlugin
@@ -8,6 +9,7 @@ class StyleC : public QStylePlugin
   Q_OBJECT
   // Json file in global root directory
   Q_PLUGIN_METADATA(IID "org.styles.C" FILE "StyleC.json")
+  A_CUSTOM_MACRO(SomeArg, "StyleC_Custom.json", AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 7 - 0
Tests/QtAutogen/mocPlugin/StyleCommon.hpp

@@ -0,0 +1,7 @@
+#ifndef STYLECOMMON_HPP
+#define STYLECOMMON_HPP
+
+// Empty test macro definition
+#define A_CUSTOM_MACRO(name, jsonFile, pluginRegistrations)
+
+#endif

+ 2 - 0
Tests/QtAutogen/mocPlugin/StyleD.hpp

@@ -1,6 +1,7 @@
 #ifndef STYLED_HPP
 #define STYLED_HPP
 
+#include "StyleCommon.hpp"
 #include <QStylePlugin>
 
 class StyleD : public QStylePlugin
@@ -8,6 +9,7 @@ class StyleD : public QStylePlugin
   Q_OBJECT
   // Json file in global sub director
   Q_PLUGIN_METADATA(IID "org.styles.D" FILE "sub/StyleD.json")
+  A_CUSTOM_MACRO(SomeArg, "sub/StyleD_Custom.json", AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 2 - 0
Tests/QtAutogen/mocPlugin/StyleE.hpp

@@ -1,6 +1,7 @@
 #ifndef STYLEE_HPP
 #define STYLEE_HPP
 
+#include "StyleCommon.hpp"
 #include <QStylePlugin>
 
 class StyleE : public QStylePlugin
@@ -8,6 +9,7 @@ class StyleE : public QStylePlugin
   Q_OBJECT
   // No Json file
   Q_PLUGIN_METADATA(IID "org.styles.E")
+  A_CUSTOM_MACRO(SomeArg, InvalidFileArg, AnotherArg)
 public:
   QStyle* create(const QString& key);
 };

+ 1 - 0
Tests/QtAutogen/mocPlugin/jsonIn/StyleB_Custom.json

@@ -0,0 +1 @@
+{ "Keys": [ "Rocket", "StarbusterB" ] }