Browse Source

Merge topic 'autogen_uic_paths'

3bf28f5e Autogen: New short InfoGet functions
154d8339 Autogen: Parse enabled feature configuration only
ac77fa35 Autogen: Add missing return on error
662ad240 Autogen: Rename and merge moc related methods
5adf22bb Autogen: Add AUTOUIC_SEARCH_PATHS release notes
36fa535d Autogen: Add AUTOUIC_SEARCH_PATHS documentation
6d7c02db Autogen: Add AUTOUIC_SEARCH_PATHS test
1cdf7c1b Autogen: Add AUTOUIC_SEARCH_PATHS support
110c1bf4 Autogen: Add subDirPrefix function
db431ecf Autogen: Merge FindInIncludeDirectories into FindIncludeFile
Brad King 8 years ago
parent
commit
c1fb7213ed

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

@@ -121,6 +121,7 @@ Properties on Targets
    /prop_tgt/AUTOMOC
    /prop_tgt/AUTOUIC
    /prop_tgt/AUTOUIC_OPTIONS
+   /prop_tgt/AUTOUIC_SEARCH_PATHS
    /prop_tgt/AUTORCC
    /prop_tgt/AUTORCC_OPTIONS
    /prop_tgt/BINARY_DIR

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

@@ -104,7 +104,9 @@ be run, and to create rules to execute ``uic`` at the appropriate time.
 
 If a preprocessor ``#include`` directive is found which matches
 ``ui_<basename>.h``, and a ``<basename>.ui`` file exists, then ``uic`` will
-be executed to generate the appropriate file.
+be executed to generate the appropriate file. The ``<basename>.ui`` file is
+searched for first in the vicinity of including file and afterwards in the
+optional :prop_tgt:`AUTOUIC_SEARCH_PATHS` of the target.
 
 The generated generated ``ui_*.h`` files are placed in the
 ``<CMAKE_CURRENT_BINARY_DIR>/<TARGETNAME>_autogen/include`` directory which is

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

@@ -264,6 +264,7 @@ Variables that Control the Build
    /variable/CMAKE_AUTORCC_OPTIONS
    /variable/CMAKE_AUTOUIC
    /variable/CMAKE_AUTOUIC_OPTIONS
+   /variable/CMAKE_AUTOUIC_SEARCH_PATHS
    /variable/CMAKE_BUILD_RPATH
    /variable/CMAKE_BUILD_WITH_INSTALL_NAME_DIR
    /variable/CMAKE_BUILD_WITH_INSTALL_RPATH

+ 7 - 2
Help/prop_tgt/AUTOUIC.rst

@@ -10,8 +10,13 @@ Qt4 and Qt5 are supported.
 
 When this property is ``ON``, CMake will scan the source files at build time
 and invoke ``uic`` accordingly.  If an ``#include`` statement like
-``#include "ui_foo.h"`` is found in ``foo.cpp``, a ``foo.ui`` file is
-expected next to ``foo.cpp``, and ``uic`` is run on the ``foo.ui`` file.
+``#include "ui_foo.h"`` is found in ``source.cpp``, a ``foo.ui`` file is
+searched for first in the vicinity of ``source.cpp`` and afterwards in the
+optional :prop_tgt:`AUTOUIC_SEARCH_PATHS` of the target.
+``uic`` is run on the ``foo.ui`` file to generate ``ui_foo.h`` in the directory
+``<CMAKE_CURRENT_BINARY_DIR>/<TARGETNAME>_autogen/include``,
+which is added to the target's :prop_tgt:`INCLUDE_DIRECTORIES` automatically.
+
 This property is initialized by the value of the :variable:`CMAKE_AUTOUIC`
 variable if it is set when a target is created.
 

+ 1 - 1
Help/prop_tgt/AUTOUIC_OPTIONS.rst

@@ -1,7 +1,7 @@
 AUTOUIC_OPTIONS
 ---------------
 
-Additional options for uic when using :prop_tgt:`AUTOUIC`
+Additional options for ``uic`` when using :prop_tgt:`AUTOUIC`
 
 This property holds additional command line options which will be used when
 ``uic`` is executed during the build via :prop_tgt:`AUTOUIC`, i.e. it is

+ 12 - 0
Help/prop_tgt/AUTOUIC_SEARCH_PATHS.rst

@@ -0,0 +1,12 @@
+AUTOUIC_SEARCH_PATHS
+--------------------
+
+Search path list used by :prop_tgt:`AUTOUIC` to find included
+``.ui`` files.
+
+This property is initialized by the value of the
+:variable:`CMAKE_AUTOUIC_SEARCH_PATHS` variable if it is set
+when a target is created. Otherwise it is empty.
+
+See the :manual:`cmake-qt(7)` manual for more information on using CMake
+with Qt.

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

@@ -0,0 +1,10 @@
+AutoGen uic paths
+-----------------
+
+* Variable :variable:`CMAKE_AUTOUIC_SEARCH_PATHS` was introduced to
+  allow :variable:`CMAKE_AUTOUIC` to search for ``foo.ui`` in more
+  places than the vicinity of the ``ui_foo.h`` including file.
+
+* The new target property :prop_tgt:`AUTOUIC_SEARCH_PATHS` was introduced to
+  allow :prop_tgt:`AUTOUIC` to search for ``foo.ui`` in more
+  places than the vicinity of the ``ui_foo.h`` including file.

+ 11 - 0
Help/variable/CMAKE_AUTOUIC_SEARCH_PATHS.rst

@@ -0,0 +1,11 @@
+CMAKE_AUTOUIC_SEARCH_PATHS
+--------------------------
+
+Search path list used by :variable:`CMAKE_AUTOUIC` to find included
+``.ui`` files.
+
+This variable is used to initialize the :prop_tgt:`AUTOUIC_SEARCH_PATHS`
+property on all the targets. See that target property for additional
+information.
+
+By default it is empty.

+ 2 - 1
Modules/AutogenInfo.cmake.in

@@ -16,7 +16,7 @@ set(AM_QT_UIC_EXECUTABLE @_qt_uic_executable@)
 set(AM_QT_RCC_EXECUTABLE @_qt_rcc_executable@)
 # MOC settings
 set(AM_MOC_SKIP @_moc_skip@)
-set(AM_MOC_COMPILE_DEFINITIONS @_moc_compile_defs@)
+set(AM_MOC_DEFINITIONS @_moc_compile_defs@)
 set(AM_MOC_INCLUDES @_moc_incs@)
 set(AM_MOC_OPTIONS @_moc_options@)
 set(AM_MOC_RELAXED_MODE @_moc_relaxed_mode@)
@@ -26,6 +26,7 @@ set(AM_UIC_SKIP @_uic_skip@)
 set(AM_UIC_TARGET_OPTIONS @_uic_target_options@)
 set(AM_UIC_OPTIONS_FILES @_qt_uic_options_files@)
 set(AM_UIC_OPTIONS_OPTIONS @_qt_uic_options_options@)
+set(AM_UIC_SEARCH_PATHS @_uic_search_paths@)
 # RCC settings
 set(AM_RCC_SOURCES @_rcc_files@ )
 set(AM_RCC_INPUTS @_rcc_inputs@)

+ 14 - 1
Source/cmQtAutoGeneratorInitializer.cxx

@@ -306,6 +306,19 @@ static void UicSetupAutoTarget(
 
   AddDefinitionEscaped(makefile, "_uic_skip", uicSkipList);
 
+  // Uic search paths
+  {
+    std::vector<std::string> uicSearchPaths;
+    cmSystemTools::ExpandListArgument(
+      GetSafeProperty(target, "AUTOUIC_SEARCH_PATHS"), uicSearchPaths);
+    const std::string srcDir = makefile->GetCurrentSourceDirectory();
+    for (std::vector<std::string>::iterator it = uicSearchPaths.begin();
+         it != uicSearchPaths.end(); ++it) {
+      *it = cmSystemTools::CollapseFullPath(*it, srcDir);
+    }
+    AddDefinitionEscaped(makefile, "_uic_search_paths", uicSearchPaths);
+  }
+
   // Uic target options
   {
     std::string _uic_opts;
@@ -959,7 +972,7 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget(
                it = configMocDefines.begin(),
                end = configMocDefines.end();
              it != end; ++it) {
-          infoFile << "set(AM_MOC_COMPILE_DEFINITIONS_" << it->first << " "
+          infoFile << "set(AM_MOC_DEFINITIONS_" << it->first << " "
                    << it->second << ")\n";
         }
       }

+ 268 - 248
Source/cmQtAutoGenerators.cxx

@@ -55,20 +55,38 @@ static std::string Quoted(const std::string& text)
   return res;
 }
 
-static std::string GetConfigDefinition(cmMakefile* makefile,
-                                       const std::string& key,
-                                       const std::string& config)
+static void InfoGet(cmMakefile* makefile, const char* key, std::string& value)
 {
-  std::string keyConf = key;
-  if (!config.empty()) {
-    keyConf += "_";
-    keyConf += config;
+  value = makefile->GetSafeDefinition(key);
+}
+
+static void InfoGet(cmMakefile* makefile, const char* key, bool& value)
+{
+  value = makefile->IsOn(key);
+}
+
+static void InfoGet(cmMakefile* makefile, const char* key,
+                    std::vector<std::string>& list)
+{
+  cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
+}
+
+static void InfoGet(cmMakefile* makefile, const char* key,
+                    const std::string& config, std::vector<std::string>& list)
+{
+  const char* valueConf = CM_NULLPTR;
+  {
+    std::string keyConf = key;
+    if (!config.empty()) {
+      keyConf += "_";
+      keyConf += config;
+    }
+    valueConf = makefile->GetDefinition(keyConf);
   }
-  const char* valueConf = makefile->GetDefinition(keyConf);
-  if (valueConf != CM_NULLPTR) {
-    return valueConf;
+  if (valueConf == CM_NULLPTR) {
+    valueConf = makefile->GetSafeDefinition(key);
   }
-  return makefile->GetSafeDefinition(key);
+  cmSystemTools::ExpandListArgument(valueConf, list);
 }
 
 static std::string SettingsFile(const std::string& targetDirectory)
@@ -94,6 +112,15 @@ static void SettingWrite(std::ostream& ostr, const char* key,
   }
 }
 
+std::string subDirPrefix(const std::string& fileName)
+{
+  std::string res(cmsys::SystemTools::GetFilenamePath(fileName));
+  if (!res.empty()) {
+    res += '/';
+  }
+  return res;
+}
+
 static bool FileNameIsUnique(const std::string& filePath,
                              const std::map<std::string, std::string>& fileMap)
 {
@@ -319,24 +346,27 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(
   }
 
   // - Target names
-  this->OriginTargetName =
-    makefile->GetSafeDefinition("AM_ORIGIN_TARGET_NAME");
-  this->AutogenTargetName = makefile->GetSafeDefinition("AM_TARGET_NAME");
-
-  // - Directories
-  this->ProjectSourceDir = makefile->GetSafeDefinition("AM_CMAKE_SOURCE_DIR");
-  this->ProjectBinaryDir = makefile->GetSafeDefinition("AM_CMAKE_BINARY_DIR");
-  this->CurrentSourceDir =
-    makefile->GetSafeDefinition("AM_CMAKE_CURRENT_SOURCE_DIR");
-  this->CurrentBinaryDir =
-    makefile->GetSafeDefinition("AM_CMAKE_CURRENT_BINARY_DIR");
+  InfoGet(makefile, "AM_TARGET_NAME", this->AutogenTargetName);
+  InfoGet(makefile, "AM_ORIGIN_TARGET_NAME", this->OriginTargetName);
+
+  // - Files and directories
+  InfoGet(makefile, "AM_CMAKE_SOURCE_DIR", this->ProjectSourceDir);
+  InfoGet(makefile, "AM_CMAKE_BINARY_DIR", this->ProjectBinaryDir);
+  InfoGet(makefile, "AM_CMAKE_CURRENT_SOURCE_DIR", this->CurrentSourceDir);
+  InfoGet(makefile, "AM_CMAKE_CURRENT_BINARY_DIR", this->CurrentBinaryDir);
+  InfoGet(makefile, "AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE",
+          this->IncludeProjectDirsBefore);
+  InfoGet(makefile, "AM_SOURCES", this->Sources);
+  InfoGet(makefile, "AM_HEADERS", this->Headers);
 
   // - Qt environment
-  this->QtMajorVersion = makefile->GetSafeDefinition("AM_QT_VERSION_MAJOR");
-  if (this->QtMajorVersion == "") {
-    this->QtMajorVersion =
-      makefile->GetSafeDefinition("AM_Qt5Core_VERSION_MAJOR");
+  InfoGet(makefile, "AM_QT_VERSION_MAJOR", this->QtMajorVersion);
+  if (this->QtMajorVersion.empty()) {
+    InfoGet(makefile, "AM_Qt5Core_VERSION_MAJOR", this->QtMajorVersion);
   }
+  InfoGet(makefile, "AM_QT_MOC_EXECUTABLE", this->MocExecutable);
+  InfoGet(makefile, "AM_QT_UIC_EXECUTABLE", this->UicExecutable);
+  InfoGet(makefile, "AM_QT_RCC_EXECUTABLE", this->RccExecutable);
   // Check Qt version
   if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) {
     this->LogError("AutoGen: Error: Unsupported Qt version: " +
@@ -344,132 +374,115 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(
     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");
-
-  // - File Lists
-  cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_SOURCES"),
-                                    this->Sources);
-  cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_HEADERS"),
-                                    this->Headers);
-
   // - Moc
-  cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition("AM_MOC_SKIP"),
-                                    this->MocSkipList);
-  cmSystemTools::ExpandListArgument(
-    GetConfigDefinition(makefile, "AM_MOC_COMPILE_DEFINITIONS", config),
-    this->MocDefinitions);
-  cmSystemTools::ExpandListArgument(
-    GetConfigDefinition(makefile, "AM_MOC_INCLUDES", config),
-    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;
+  if (this->MocEnabled()) {
+    InfoGet(makefile, "AM_MOC_SKIP", this->MocSkipList);
+    InfoGet(makefile, "AM_MOC_DEFINITIONS", config, this->MocDefinitions);
+    InfoGet(makefile, "AM_MOC_INCLUDES", config, this->MocIncludePaths);
+    InfoGet(makefile, "AM_MOC_OPTIONS", this->MocOptions);
+    InfoGet(makefile, "AM_MOC_RELAXED_MODE", this->MocRelaxedMode);
+    {
+      std::vector<std::string> mocDependFilters;
+      InfoGet(makefile, "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");
+        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"),
-                                    this->UicSkipList);
-  cmSystemTools::ExpandListArgument(
-    GetConfigDefinition(makefile, "AM_UIC_TARGET_OPTIONS", config),
-    this->UicTargetOptions);
-  {
-    std::vector<std::string> uicFilesVec;
-    std::vector<std::string> uicOptionsVec;
-    cmSystemTools::ExpandListArgument(
-      makefile->GetSafeDefinition("AM_UIC_OPTIONS_FILES"), uicFilesVec);
-    cmSystemTools::ExpandListArgument(
-      makefile->GetSafeDefinition("AM_UIC_OPTIONS_OPTIONS"), uicOptionsVec);
-    if (uicFilesVec.size() != uicOptionsVec.size()) {
-      this->LogError(
-        "AutoGen: Error: Uic files/options lists size missmatch in: " +
-        filename);
-      return false;
-    }
-    for (std::vector<std::string>::iterator fileIt = uicFilesVec.begin(),
-                                            optionIt = uicOptionsVec.begin();
-         fileIt != uicFilesVec.end(); ++fileIt, ++optionIt) {
-      cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";");
-      this->UicOptions[*fileIt] = *optionIt;
+  if (this->UicEnabled()) {
+    InfoGet(makefile, "AM_UIC_SKIP", this->UicSkipList);
+    InfoGet(makefile, "AM_UIC_SEARCH_PATHS", this->UicSearchPaths);
+    InfoGet(makefile, "AM_UIC_TARGET_OPTIONS", config, this->UicTargetOptions);
+    {
+      std::vector<std::string> uicFilesVec;
+      std::vector<std::string> uicOptionsVec;
+      InfoGet(makefile, "AM_UIC_OPTIONS_FILES", uicFilesVec);
+      InfoGet(makefile, "AM_UIC_OPTIONS_OPTIONS", uicOptionsVec);
+      // Compare list sizes
+      if (uicFilesVec.size() == uicOptionsVec.size()) {
+        for (std::vector<std::string>::iterator
+               fileIt = uicFilesVec.begin(),
+               optionIt = uicOptionsVec.begin();
+             fileIt != uicFilesVec.end(); ++fileIt, ++optionIt) {
+          cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";");
+          this->UicOptions[*fileIt] = *optionIt;
+        }
+      } else {
+        this->LogError(
+          "AutoGen: Error: Uic files/options lists size missmatch in: " +
+          filename);
+        return false;
+      }
     }
   }
 
   // - Rcc
-  cmSystemTools::ExpandListArgument(
-    makefile->GetSafeDefinition("AM_RCC_SOURCES"), this->RccSources);
-  {
-    std::vector<std::string> rccFilesVec;
-    std::vector<std::string> rccOptionsVec;
-    cmSystemTools::ExpandListArgument(
-      makefile->GetSafeDefinition("AM_RCC_OPTIONS_FILES"), rccFilesVec);
-    cmSystemTools::ExpandListArgument(
-      makefile->GetSafeDefinition("AM_RCC_OPTIONS_OPTIONS"), rccOptionsVec);
-    if (rccFilesVec.size() != rccOptionsVec.size()) {
-      this->LogError(
-        "AutoGen: Error: RCC files/options lists size missmatch in: " +
-        filename);
-      return false;
-    }
-    for (std::vector<std::string>::iterator fileIt = rccFilesVec.begin(),
-                                            optionIt = rccOptionsVec.begin();
-         fileIt != rccFilesVec.end(); ++fileIt, ++optionIt) {
-      cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";");
-      this->RccOptions[*fileIt] = *optionIt;
-    }
-  }
-  {
-    std::vector<std::string> rccInputLists;
-    cmSystemTools::ExpandListArgument(
-      makefile->GetSafeDefinition("AM_RCC_INPUTS"), rccInputLists);
-
-    // qrc files in the end of the list may have been empty
-    if (rccInputLists.size() < this->RccSources.size()) {
-      rccInputLists.resize(this->RccSources.size());
-    }
-    if (this->RccSources.size() != rccInputLists.size()) {
-      this->LogError(
-        "AutoGen: Error: RCC sources/inputs lists size missmatch in: " +
-        filename);
-      return false;
+  if (this->RccEnabled()) {
+    InfoGet(makefile, "AM_RCC_SOURCES", this->RccSources);
+    {
+      std::vector<std::string> rccFilesVec;
+      std::vector<std::string> rccOptionsVec;
+      InfoGet(makefile, "AM_RCC_OPTIONS_FILES", rccFilesVec);
+      InfoGet(makefile, "AM_RCC_OPTIONS_OPTIONS", rccOptionsVec);
+      if (rccFilesVec.size() != rccOptionsVec.size()) {
+        this->LogError(
+          "AutoGen: Error: RCC files/options lists size missmatch in: " +
+          filename);
+        return false;
+      }
+      for (std::vector<std::string>::iterator fileIt = rccFilesVec.begin(),
+                                              optionIt = rccOptionsVec.begin();
+           fileIt != rccFilesVec.end(); ++fileIt, ++optionIt) {
+        cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";");
+        this->RccOptions[*fileIt] = *optionIt;
+      }
     }
-    for (std::vector<std::string>::iterator fileIt = this->RccSources.begin(),
-                                            inputIt = rccInputLists.begin();
-         fileIt != this->RccSources.end(); ++fileIt, ++inputIt) {
-      cmSystemTools::ReplaceString(*inputIt, "@list_sep@", ";");
-      std::vector<std::string> rccInputFiles;
-      cmSystemTools::ExpandListArgument(*inputIt, rccInputFiles);
-      this->RccInputs[*fileIt] = rccInputFiles;
+    {
+      std::vector<std::string> rccInputLists;
+      InfoGet(makefile, "AM_RCC_INPUTS", rccInputLists);
+
+      // qrc files in the end of the list may have been empty
+      if (rccInputLists.size() < this->RccSources.size()) {
+        rccInputLists.resize(this->RccSources.size());
+      }
+      if (this->RccSources.size() != rccInputLists.size()) {
+        this->LogError(
+          "AutoGen: Error: RCC sources/inputs lists size missmatch in: " +
+          filename);
+        return false;
+      }
+      for (std::vector<std::string>::iterator
+             fileIt = this->RccSources.begin(),
+             inputIt = rccInputLists.begin();
+           fileIt != this->RccSources.end(); ++fileIt, ++inputIt) {
+        cmSystemTools::ReplaceString(*inputIt, "@list_sep@", ";");
+        std::vector<std::string> rccInputFiles;
+        cmSystemTools::ExpandListArgument(*inputIt, rccInputFiles);
+        this->RccInputs[*fileIt] = rccInputFiles;
+      }
     }
   }
 
-  // - Flags
-  this->IncludeProjectDirsBefore =
-    makefile->IsOn("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
-  this->MocRelaxedMode = makefile->IsOn("AM_MOC_RELAXED_MODE");
-
   return true;
 }
 
@@ -727,15 +740,15 @@ void cmQtAutoGenerators::MocFindDepends(
     // regular expression check
     if (contentText.find(filter.key) != std::string::npos) {
       // Run regular expression check loop
+      const std::string sourcePath = subDirPrefix(absFilename);
       const char* contentChars = contentText.c_str();
       while (filter.regExp.find(contentChars)) {
         // Evaluate match
         const std::string match = filter.regExp.match(1);
         if (!match.empty()) {
           // Find the dependency file
-          const std::string incFile =
-            this->FindIncludedFile(absFilename, match);
-          if (!incFile.empty()) {
+          std::string incFile;
+          if (this->MocFindIncludedFile(incFile, sourcePath, match)) {
             mocDepends[absFilename].insert(incFile);
             if (this->Verbose) {
               this->LogInfo("AutoMoc: Found dependency:\n  " +
@@ -823,12 +836,7 @@ void cmQtAutoGenerators::UicParseContent(
   const char* contentChars = contentText.c_str();
   if (strstr(contentChars, "ui_") != CM_NULLPTR) {
     while (this->RegExpUicInclude.find(contentChars)) {
-      const std::string currentUi = this->RegExpUicInclude.match(1);
-      const std::string basename =
-        cmsys::SystemTools::GetFilenameWithoutLastExtension(currentUi);
-      // basename should be the part of the ui filename used for
-      // finding the correct header, so we need to remove the ui_ part
-      uisIncluded[absFilename].push_back(basename.substr(3));
+      uisIncluded[absFilename].push_back(this->RegExpUicInclude.match(1));
       contentChars += this->RegExpUicInclude.end();
     }
   }
@@ -846,8 +854,7 @@ bool cmQtAutoGenerators::MocParseSourceContent(
     this->LogInfo("AutoMoc: Checking " + absFilename);
   }
 
-  const std::string scannedFileAbsPath =
-    cmsys::SystemTools::GetFilenamePath(absFilename) + '/';
+  const std::string scannedFileAbsPath = subDirPrefix(absFilename);
   const std::string scannedFileBasename =
     cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename);
 
@@ -866,13 +873,9 @@ bool cmQtAutoGenerators::MocParseSourceContent(
     while (this->RegExpMocInclude.find(contentChars)) {
       const std::string incString = this->RegExpMocInclude.match(1);
       // Basename of the moc include
+      const std::string incSubDir(subDirPrefix(incString));
       const std::string incBasename =
         cmsys::SystemTools::GetFilenameWithoutLastExtension(incString);
-      std::string incSubDir;
-      if (incString.find_first_of('/') != std::string::npos) {
-        incSubDir = cmsys::SystemTools::GetFilenamePath(incString);
-        incSubDir += '/';
-      }
 
       // If the moc include is of the moc_foo.cpp style we expect
       // the Q_OBJECT class declaration in a header file.
@@ -884,7 +887,7 @@ bool cmQtAutoGenerators::MocParseSourceContent(
         // Remove the moc_ part
         const std::string incRealBasename = incBasename.substr(4);
         const std::string headerToMoc =
-          this->FindMocHeader(scannedFileAbsPath, incRealBasename, incSubDir);
+          this->MocFindHeader(scannedFileAbsPath, incSubDir + incRealBasename);
         if (!headerToMoc.empty()) {
           // Register moc job
           mocsIncluded[headerToMoc] = incString;
@@ -917,7 +920,7 @@ bool cmQtAutoGenerators::MocParseSourceContent(
           } else {
             // In relaxed mode try to find a header instead but issue a warning
             const std::string headerToMoc =
-              this->FindMocHeader(scannedFileAbsPath, incBasename, incSubDir);
+              this->MocFindHeader(scannedFileAbsPath, incSubDir + incBasename);
             if (!headerToMoc.empty()) {
               // This is for KDE4 compatibility:
               fileToMoc = headerToMoc;
@@ -1056,8 +1059,7 @@ void cmQtAutoGenerators::SearchHeadersForSourceFile(
 {
   std::string basepaths[2];
   {
-    std::string bpath = cmsys::SystemTools::GetFilenamePath(absFilename);
-    bpath += '/';
+    std::string bpath = subDirPrefix(absFilename);
     bpath += cmsys::SystemTools::GetFilenameWithoutLastExtension(absFilename);
     // search for default header files and private header files
     basepaths[0] = bpath;
@@ -1235,14 +1237,14 @@ bool cmQtAutoGenerators::MocGenerateAll(
  */
 bool cmQtAutoGenerators::MocGenerateFile(
   const std::string& sourceFile, const std::string& mocFileName,
-  const std::string& subDirPrefix,
+  const std::string& subDir,
   const std::map<std::string, std::set<std::string> >& mocDepends)
 {
   bool mocGenerated = false;
   bool generateMoc = this->GenerateAllMoc;
 
   const std::string mocFileRel =
-    this->AutogenBuildSubDir + subDirPrefix + mocFileName;
+    this->AutogenBuildSubDir + subDir + mocFileName;
   const std::string mocFileAbs = this->CurrentBinaryDir + mocFileRel;
 
   if (!generateMoc) {
@@ -1323,6 +1325,36 @@ bool cmQtAutoGenerators::MocGenerateFile(
   return mocGenerated;
 }
 
+bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile,
+                                             const std::string& sourceFile,
+                                             const std::string& includeString)
+{
+  bool success = false;
+  // Search in vicinity of the source
+  {
+    std::string testPath = subDirPrefix(sourceFile);
+    testPath += includeString;
+    if (cmsys::SystemTools::FileExists(testPath.c_str())) {
+      absFile = cmsys::SystemTools::GetRealPath(testPath);
+      success = true;
+    }
+  }
+  // Search in include directories
+  if (!success) {
+    for (std::vector<std::string>::const_iterator iit =
+           this->UicSearchPaths.begin();
+         iit != this->UicSearchPaths.end(); ++iit) {
+      const std::string fullPath = ((*iit) + '/' + includeString);
+      if (cmsys::SystemTools::FileExists(fullPath.c_str())) {
+        absFile = cmsys::SystemTools::GetRealPath(fullPath);
+        success = true;
+        break;
+      }
+    }
+  }
+  return success;
+}
+
 bool cmQtAutoGenerators::UicGenerateAll(
   const std::map<std::string, std::vector<std::string> >& uisIncluded)
 {
@@ -1331,46 +1363,57 @@ bool cmQtAutoGenerators::UicGenerateAll(
   }
 
   // single map with input / output names
-  std::map<std::string, std::map<std::string, std::string> > uiGenMap;
-  std::map<std::string, std::string> testMap;
-  for (std::map<std::string, std::vector<std::string> >::const_iterator it =
-         uisIncluded.begin();
-       it != uisIncluded.end(); ++it) {
-    // source file path
-    std::string sourcePath = cmsys::SystemTools::GetFilenamePath(it->first);
-    sourcePath += '/';
-    // insert new map for source file an use new reference
-    uiGenMap[it->first] = std::map<std::string, std::string>();
-    std::map<std::string, std::string>& sourceMap = uiGenMap[it->first];
-    for (std::vector<std::string>::const_iterator sit = it->second.begin();
-         sit != it->second.end(); ++sit) {
-      const std::string& uiFileName = *sit;
-      const std::string uiInputFile = sourcePath + uiFileName + ".ui";
-      const std::string uiOutputFile = "ui_" + uiFileName + ".h";
-      sourceMap[uiInputFile] = uiOutputFile;
-      testMap[uiInputFile] = uiOutputFile;
-    }
-  }
-
-  // look for name collisions
+  std::map<std::string, std::map<std::string, std::string> > sourceGenMap;
   {
-    std::multimap<std::string, std::string> collisions;
-    if (this->NameCollisionTest(testMap, collisions)) {
-      std::ostringstream ost;
-      ost << "AutoUic: Error: The same ui_NAME.h file will be generated "
-             "from different sources.\n"
-             "To avoid this error rename the source files.\n";
-      this->LogErrorNameCollision(ost.str(), collisions);
-      return false;
+    // Collision lookup map
+    std::map<std::string, std::string> testMap;
+    // Compile maps
+    for (std::map<std::string, std::vector<std::string> >::const_iterator sit =
+           uisIncluded.begin();
+         sit != uisIncluded.end(); ++sit) {
+      const std::string& source(sit->first);
+      const std::vector<std::string>& sourceIncs(sit->second);
+      // insert new source/destination map
+      std::map<std::string, std::string>& uiGenMap = sourceGenMap[source];
+      for (std::vector<std::string>::const_iterator uit = sourceIncs.begin();
+           uit != sourceIncs.end(); ++uit) {
+        // Remove ui_ from the begin filename by substr()
+        const std::string uiBasePath = subDirPrefix(*uit);
+        const std::string uiBaseName =
+          cmsys::SystemTools::GetFilenameWithoutLastExtension(*uit).substr(3);
+        const std::string searchFileName = uiBasePath + uiBaseName + ".ui";
+        std::string uiInputFile;
+        if (UicFindIncludedFile(uiInputFile, source, searchFileName)) {
+          std::string uiOutputFile = uiBasePath + "ui_" + uiBaseName + ".h";
+          cmSystemTools::ReplaceString(uiOutputFile, "..", "__");
+          uiGenMap[uiInputFile] = uiOutputFile;
+          testMap[uiInputFile] = uiOutputFile;
+        } else {
+          this->LogError("AutoUic: Error: " + Quoted(sit->first) +
+                         "\nCould not find " + Quoted(searchFileName));
+          return false;
+        }
+      }
+    }
+    // look for name collisions
+    {
+      std::multimap<std::string, std::string> collisions;
+      if (this->NameCollisionTest(testMap, collisions)) {
+        std::ostringstream ost;
+        ost << "AutoUic: Error: The same ui_NAME.h file will be generated "
+               "from different sources.\n"
+               "To avoid this error rename the source files.\n";
+        this->LogErrorNameCollision(ost.str(), collisions);
+        return false;
+      }
     }
   }
-  testMap.clear();
 
   // generate ui files
   for (std::map<std::string,
                 std::map<std::string, std::string> >::const_iterator it =
-         uiGenMap.begin();
-       it != uiGenMap.end(); ++it) {
+         sourceGenMap.begin();
+       it != sourceGenMap.end(); ++it) {
     for (std::map<std::string, std::string>::const_iterator sit =
            it->second.begin();
          sit != it->second.end(); ++sit) {
@@ -1413,15 +1456,15 @@ bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName,
       std::vector<std::string> cmd;
       cmd.push_back(this->UicExecutable);
       {
-        std::vector<std::string> opts = this->UicTargetOptions;
+        std::vector<std::string> allOpts = this->UicTargetOptions;
         std::map<std::string, std::string>::const_iterator optionIt =
           this->UicOptions.find(uiInputFile);
         if (optionIt != this->UicOptions.end()) {
           std::vector<std::string> fileOpts;
           cmSystemTools::ExpandListArgument(optionIt->second, fileOpts);
-          UicMergeOptions(opts, fileOpts, (this->QtMajorVersion == "5"));
+          UicMergeOptions(allOpts, fileOpts, (this->QtMajorVersion == "5"));
         }
-        cmd.insert(cmd.end(), opts.begin(), opts.end());
+        cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
       }
       cmd.push_back("-o");
       cmd.push_back(uicFileAbs);
@@ -1740,39 +1783,22 @@ bool cmQtAutoGenerators::FindHeader(std::string& header,
   return false;
 }
 
-bool cmQtAutoGenerators::FindHeaderGlobal(
-  std::string& header, const std::string& testBasePath) const
-{
-  for (std::vector<std::string>::const_iterator iit =
-         this->MocIncludePaths.begin();
-       iit != this->MocIncludePaths.end(); ++iit) {
-    const std::string fullPath = ((*iit) + '/' + testBasePath);
-    if (FindHeader(header, fullPath)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-std::string cmQtAutoGenerators::FindMocHeader(const std::string& basePath,
-                                              const std::string& baseName,
-                                              const std::string& subDir) const
+std::string cmQtAutoGenerators::MocFindHeader(
+  const std::string& sourcePath, const std::string& includeBase) const
 {
   std::string header;
-  do {
-    if (!subDir.empty()) {
-      if (this->FindHeader(header, basePath + subDir + baseName)) {
+  // Search in vicinity of the source
+  if (!this->FindHeader(header, sourcePath + includeBase)) {
+    // Search in include directories
+    for (std::vector<std::string>::const_iterator iit =
+           this->MocIncludePaths.begin();
+         iit != this->MocIncludePaths.end(); ++iit) {
+      const std::string fullPath = ((*iit) + '/' + includeBase);
+      if (FindHeader(header, fullPath)) {
         break;
       }
     }
-    if (this->FindHeader(header, basePath + baseName)) {
-      break;
-    }
-    // Try include directories
-    if (this->FindHeaderGlobal(header, subDir + baseName)) {
-      break;
-    }
-  } while (false);
+  }
   // Sanitize
   if (!header.empty()) {
     header = cmsys::SystemTools::GetRealPath(header);
@@ -1780,40 +1806,34 @@ std::string cmQtAutoGenerators::FindMocHeader(const std::string& basePath,
   return header;
 }
 
-std::string cmQtAutoGenerators::FindIncludedFile(
-  const std::string& sourceFile, const std::string& includeString) const
+bool cmQtAutoGenerators::MocFindIncludedFile(
+  std::string& absFile, const std::string& sourcePath,
+  const std::string& includeString) const
 {
+  bool success = false;
   // Search in vicinity of the source
   {
-    std::string testPath = cmSystemTools::GetFilenamePath(sourceFile);
-    testPath += '/';
+    std::string testPath = sourcePath;
     testPath += includeString;
     if (cmsys::SystemTools::FileExists(testPath.c_str())) {
-      return cmsys::SystemTools::GetRealPath(testPath);
+      absFile = cmsys::SystemTools::GetRealPath(testPath);
+      success = true;
     }
   }
-  // Search globally
-  return FindInIncludeDirectories(includeString);
-}
-
-/**
- * @brief Tries to find a file in the include directories
- * @return True on success
- */
-std::string cmQtAutoGenerators::FindInIncludeDirectories(
-  const std::string& includeString) const
-{
-  std::string res;
-  for (std::vector<std::string>::const_iterator iit =
-         this->MocIncludePaths.begin();
-       iit != this->MocIncludePaths.end(); ++iit) {
-    const std::string fullPath = ((*iit) + '/' + includeString);
-    if (cmsys::SystemTools::FileExists(fullPath.c_str())) {
-      res = cmsys::SystemTools::GetRealPath(fullPath);
-      break;
+  // Search in include directories
+  if (!success) {
+    for (std::vector<std::string>::const_iterator iit =
+           this->MocIncludePaths.begin();
+         iit != this->MocIncludePaths.end(); ++iit) {
+      const std::string fullPath = ((*iit) + '/' + includeString);
+      if (cmsys::SystemTools::FileExists(fullPath.c_str())) {
+        absFile = cmsys::SystemTools::GetRealPath(fullPath);
+        success = true;
+        break;
+      }
     }
   }
-  return res;
+  return success;
 }
 
 /**

+ 9 - 9
Source/cmQtAutoGenerators.h

@@ -107,10 +107,12 @@ private:
     const std::map<std::string, std::set<std::string> >& mocDepends);
   bool MocGenerateFile(
     const std::string& sourceFile, const std::string& mocFileName,
-    const std::string& subDirPrefix,
+    const std::string& subDir,
     const std::map<std::string, std::set<std::string> >& mocDepends);
 
   // - Uic file generation
+  bool UicFindIncludedFile(std::string& absFile, const std::string& sourceFile,
+                           const std::string& includeString);
   bool UicGenerateAll(
     const std::map<std::string, std::vector<std::string> >& includedUis);
   bool UicGenerateFile(const std::string& realName,
@@ -142,14 +144,11 @@ private:
   bool MakeParentDirectory(const std::string& filename) const;
 
   bool FindHeader(std::string& header, const std::string& testBasePath) const;
-  bool FindHeaderGlobal(std::string& header,
-                        const std::string& testBasePath) const;
-  std::string FindMocHeader(const std::string& basePath,
-                            const std::string& baseName,
-                            const std::string& subDir) const;
-  std::string FindIncludedFile(const std::string& sourceFile,
-                               const std::string& includeString) const;
-  std::string FindInIncludeDirectories(const std::string& includeString) const;
+
+  std::string MocFindHeader(const std::string& sourcePath,
+                            const std::string& includeBase) const;
+  bool MocFindIncludedFile(std::string& absFile, const std::string& sourceFile,
+                           const std::string& includeString) const;
 
   // - Target names
   std::string OriginTargetName;
@@ -185,6 +184,7 @@ private:
   std::vector<std::string> UicSkipList;
   std::vector<std::string> UicTargetOptions;
   std::map<std::string, std::string> UicOptions;
+  std::vector<std::string> UicSearchPaths;
   // - Rcc
   std::vector<std::string> RccSources;
   std::map<std::string, std::string> RccOptions;

+ 1 - 0
Source/cmTarget.cxx

@@ -248,6 +248,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     this->SetPropertyDefault("AUTOMOC_DEPEND_FILTERS", CM_NULLPTR);
     this->SetPropertyDefault("AUTOMOC_MOC_OPTIONS", CM_NULLPTR);
     this->SetPropertyDefault("AUTOUIC_OPTIONS", CM_NULLPTR);
+    this->SetPropertyDefault("AUTOUIC_SEARCH_PATHS", CM_NULLPTR);
     this->SetPropertyDefault("AUTORCC_OPTIONS", CM_NULLPTR);
     this->SetPropertyDefault("LINK_DEPENDS_NO_SHARED", CM_NULLPTR);
     this->SetPropertyDefault("LINK_INTERFACE_LIBRARIES", CM_NULLPTR);

+ 4 - 0
Tests/QtAutogen/CMakeLists.txt

@@ -286,6 +286,10 @@ if (NOT QT_TEST_VERSION STREQUAL 4)
 
 endif()
 
+# -- Test
+# Tests various .ui include directories
+add_subdirectory(uicInclude)
+
 # -- Test
 # Complex test case
 add_subdirectory(complex)

+ 8 - 0
Tests/QtAutogen/uicInclude/CMakeLists.txt

@@ -0,0 +1,8 @@
+# Test moc include patterns
+
+set(CMAKE_AUTOUIC_SEARCH_PATHS "dirA")
+
+add_executable(uicInclude main.cpp)
+target_link_libraries(uicInclude ${QT_LIBRARIES})
+set_target_properties(uicInclude PROPERTIES AUTOUIC ON)
+set_property(TARGET uicInclude APPEND PROPERTY AUTOUIC_SEARCH_PATHS "dirB")

+ 24 - 0
Tests/QtAutogen/uicInclude/PageC.ui

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PageC</class>
+ <widget class="QWidget" name="PageC">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QTreeView" name="treeView"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 24 - 0
Tests/QtAutogen/uicInclude/dirA/PageA.ui

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PageA</class>
+ <widget class="QWidget" name="PageA">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QTreeView" name="treeView"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 24 - 0
Tests/QtAutogen/uicInclude/dirB/sub/PageB.ui

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PageB</class>
+ <widget class="QWidget" name="PageB">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QTreeView" name="treeView"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 10 - 0
Tests/QtAutogen/uicInclude/main.cpp

@@ -0,0 +1,10 @@
+
+#include "main.hpp"
+
+int main(int argv, char** args)
+{
+  return 0;
+}
+
+#include "sub/ui_PageB.h"
+#include "ui_PageC.h"

+ 6 - 0
Tests/QtAutogen/uicInclude/main.hpp

@@ -0,0 +1,6 @@
+#ifndef MAIN_HPP
+#define MAIN_HPP
+
+#include "ui_PageA.h"
+
+#endif