diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index c2480858d..ddf24d4a0 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -73,6 +73,8 @@ Properties on Targets /prop_tgt/ARCHIVE_OUTPUT_NAME /prop_tgt/AUTOMOC_MOC_OPTIONS /prop_tgt/AUTOMOC + /prop_tgt/AUTOUIC + /prop_tgt/AUTOUIC_OPTIONS /prop_tgt/BUILD_WITH_INSTALL_RPATH /prop_tgt/BUNDLE_EXTENSION /prop_tgt/BUNDLE @@ -234,6 +236,7 @@ Properties on Source Files .. toctree:: /prop_sf/ABSTRACT + /prop_sf/AUTOUIC_OPTIONS /prop_sf/COMPILE_DEFINITIONS_CONFIG /prop_sf/COMPILE_DEFINITIONS /prop_sf/COMPILE_FLAGS diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 2311ac83d..9a5f25437 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -155,6 +155,8 @@ Variables that Control the Build /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY /variable/CMAKE_AUTOMOC_MOC_OPTIONS /variable/CMAKE_AUTOMOC + /variable/CMAKE_AUTOUIC + /variable/CMAKE_AUTOUIC_OPTIONS /variable/CMAKE_BUILD_WITH_INSTALL_RPATH /variable/CMAKE_CONFIG_POSTFIX /variable/CMAKE_DEBUG_POSTFIX diff --git a/Help/prop_sf/AUTOUIC_OPTIONS.rst b/Help/prop_sf/AUTOUIC_OPTIONS.rst new file mode 100644 index 000000000..a38b2f88c --- /dev/null +++ b/Help/prop_sf/AUTOUIC_OPTIONS.rst @@ -0,0 +1,14 @@ +AUTOUIC_OPTIONS +--------------- + +Additional options for uic when using autouic (see the :prop_tgt:`AUTOUIC` target property) + +This property holds additional command line options +which will be used when uic is executed during the build via autouic, +i.e. it is equivalent to the optional OPTIONS argument of the +qt4_wrap_ui() macro. + +By default it is empty. + +The options set on the .ui source file may override :prop_tgt:`AUTOUIC_OPTIONS` set +on the target. diff --git a/Help/prop_tgt/AUTOUIC.rst b/Help/prop_tgt/AUTOUIC.rst new file mode 100644 index 000000000..548c2598d --- /dev/null +++ b/Help/prop_tgt/AUTOUIC.rst @@ -0,0 +1,22 @@ +AUTOUIC +------- + +Should the target be processed with autouic (for Qt projects). + +AUTOUIC is a boolean specifying whether CMake will handle +the Qt uic code generator automatically, i.e. without having to use +the QT4_WRAP_UI() or QT5_WRAP_UI() macro. Currently Qt4 and Qt5 are +supported. + +When this property is set to TRUE, 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. +This property is initialized by the value of the :variable:`CMAKE_AUTOUIC` +variable if it is set when a target is created. + +Additional command line options for uic can be set via the +:prop_sf:`AUTOUIC_OPTIONS` source file property on the foo.ui file. +The global property :prop_gbl:`AUTOGEN_TARGETS_FOLDER` can be used to group the +autouic targets together in an IDE, e.g. in MSVS. diff --git a/Help/prop_tgt/AUTOUIC_OPTIONS.rst b/Help/prop_tgt/AUTOUIC_OPTIONS.rst new file mode 100644 index 000000000..c6cf885c5 --- /dev/null +++ b/Help/prop_tgt/AUTOUIC_OPTIONS.rst @@ -0,0 +1,17 @@ +AUTOUIC_OPTIONS +--------------- + +Additional options for uic when using autouic (see the :prop_tgt:`AUTOUIC` target property) + +This property holds additional command line options +which will be used when uic is executed during the build via autouic, +i.e. it is equivalent to the optional OPTIONS argument of the +qt4_wrap_ui() macro. + +By default it is empty. + +This property is initialized by the value of the variable +:variable:`CMAKE_AUTOUIC` if it is set when a target is created. + +The options set on the target may be overridden by :prop_sf:`AUTOUIC_OPTIONS` set +on the .ui source file. diff --git a/Help/variable/CMAKE_AUTOUIC.rst b/Help/variable/CMAKE_AUTOUIC.rst new file mode 100644 index 000000000..3b016b02e --- /dev/null +++ b/Help/variable/CMAKE_AUTOUIC.rst @@ -0,0 +1,7 @@ +CMAKE_AUTOUIC +------------- + +Whether to handle uic automatically for Qt targets. + +This variable is used to initialize the :prop_tgt:`AUTOUIC` property on all the targets. +See that target property for additional information. diff --git a/Help/variable/CMAKE_AUTOUIC_OPTIONS.rst b/Help/variable/CMAKE_AUTOUIC_OPTIONS.rst new file mode 100644 index 000000000..6a88669c0 --- /dev/null +++ b/Help/variable/CMAKE_AUTOUIC_OPTIONS.rst @@ -0,0 +1,7 @@ +CMAKE_AUTOUIC_OPTIONS +--------------------- + +Whether to handle uic automatically for Qt targets. + +This variable is used to initialize the :prop_tgt:`AUTOUIC_OPTIONS` property on +all the targets. See that target property for additional information. diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in index 19663ff6b..d2fb807fe 100644 --- a/Modules/AutogenInfo.cmake.in +++ b/Modules/AutogenInfo.cmake.in @@ -1,5 +1,6 @@ set(AM_SOURCES @_moc_files@ ) set(AM_SKIP_MOC @_skip_moc@ ) +set(AM_SKIP_UIC @_skip_uic@ ) set(AM_HEADERS @_moc_headers@ ) set(AM_MOC_COMPILE_DEFINITIONS @_moc_compile_defs@) set(AM_MOC_INCLUDES @_moc_incs@) @@ -8,8 +9,12 @@ set(AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE "@CMAKE_INCLUDE_DIRECTORIES_PROJ set(AM_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/") set(AM_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/") set(AM_QT_MOC_EXECUTABLE "@_qt_moc_executable@") +set(AM_QT_UIC_EXECUTABLE "@_qt_uic_executable@") set(AM_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/") set(AM_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/") set(AM_QT_VERSION_MAJOR "@_target_qt_version@") set(AM_TARGET_NAME @_moc_target_name@) set(AM_RELAXED_MODE "@_moc_relaxed_mode@") +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@) diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 386af6d95..3fb684892 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1225,7 +1225,9 @@ void cmGlobalGenerator::CreateQtAutoGeneratorsTargets() target.GetType() == cmTarget::MODULE_LIBRARY || target.GetType() == cmTarget::OBJECT_LIBRARY) { - if(target.GetPropertyAsBool("AUTOMOC") && !target.IsImported()) + if((target.GetPropertyAsBool("AUTOMOC") + || target.GetPropertyAsBool("AUTOUIC")) + && !target.IsImported()) { cmQtAutoGenerators autogen; if(autogen.InitializeMocSourceFile(&target)) diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 13c43faf5..df64ca671 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -4239,6 +4239,18 @@ bool cmMakefile::EnforceUniqueDir(const char* srcPath, const char* binPath) return false; } +//---------------------------------------------------------------------------- +void cmMakefile::AddQtUiFileWithOptions(cmSourceFile *sf) +{ + this->QtUiFilesWithOptions.push_back(sf); +} + +//---------------------------------------------------------------------------- +std::vector cmMakefile::GetQtUiFilesWithOptions() const +{ + return this->QtUiFilesWithOptions; +} + //---------------------------------------------------------------------------- cmPolicies::PolicyStatus cmMakefile::GetPolicyStatus(cmPolicies::PolicyID id) diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index ca8233669..794873ae5 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -884,6 +884,9 @@ public: bool IsGeneratingBuildSystem(){ return this->GeneratingBuildSystem; } void SetGeneratingBuildSystem(){ this->GeneratingBuildSystem = true; } + void AddQtUiFileWithOptions(cmSourceFile *sf); + std::vector GetQtUiFilesWithOptions() const; + std::set const & GetSystemIncludeDirectories() const { return this->SystemIncludeDirectories; } @@ -1061,6 +1064,8 @@ private: cmSourceFile* source); void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source); + + std::vector QtUiFilesWithOptions; }; //---------------------------------------------------------------------------- diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index 7f8d283f4..30211f22f 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -23,6 +23,7 @@ #include #include +#include #include #if defined(__APPLE__) @@ -117,6 +118,7 @@ cmQtAutoGenerators::cmQtAutoGenerators() :Verbose(cmsys::SystemTools::GetEnv("VERBOSE") != 0) ,ColorOutput(true) ,RunMocFailed(false) +,RunUicFailed(false) ,GenerateAll(false) { @@ -255,7 +257,22 @@ void cmQtAutoGenerators::SetupAutoGenerateTarget(cmTarget* target) "", makefile->GetCurrentOutputDirectory()); std::vector depends; - std::string tools = "moc"; + std::vector toolNames; + if (target->GetPropertyAsBool("AUTOMOC")) + { + toolNames.push_back("moc"); + } + if (target->GetPropertyAsBool("AUTOUIC")) + { + toolNames.push_back("uic"); + } + + std::string tools = toolNames[0]; + toolNames.erase(toolNames.begin()); + if (toolNames.size() == 1) + { + tools += " and " + toolNames[0]; + } std::string autogenComment = "Automatic " + tools + " for target "; autogenComment += targetName; @@ -322,6 +339,10 @@ void cmQtAutoGenerators::SetupAutoGenerateTarget(cmTarget* target) this->SetupAutoMocTarget(target, autogenTargetName, configIncludes, configDefines); } + if (target->GetPropertyAsBool("AUTOUIC")) + { + this->SetupAutoUicTarget(target); + } const char* cmakeRoot = makefile->GetSafeDefinition("CMAKE_ROOT"); std::string inputFile = cmakeRoot; @@ -499,6 +520,156 @@ void cmQtAutoGenerators::SetupAutoMocTarget(cmTarget* target, } } +void cmQtAutoGenerators::MergeUicOptions(std::vector &opts, + const std::vector &fileOpts, + bool isQt5) +{ + static const char* valueOptions[] = { + "tr", + "translate", + "postfix", + "generator", + "g" + }; + std::vector extraOpts; + for(std::vector::const_iterator it = fileOpts.begin(); + it != fileOpts.end(); ++it) + { + std::vector::iterator existingIt + = std::find(opts.begin(), opts.end(), *it); + if (existingIt != opts.end()) + { + const char *o = it->c_str(); + if (*o == '-') + { + ++o; + } + if (isQt5 && *o == '-') + { + ++o; + } + if (std::find_if(cmArrayBegin(valueOptions), cmArrayEnd(valueOptions), + cmStrCmp(o)) != cmArrayEnd(valueOptions)) + { + assert(existingIt + 1 != opts.end()); + *(existingIt + 1) = *(it + 1); + ++it; + } + } + else + { + extraOpts.push_back(*it); + } + } + opts.insert(opts.end(), extraOpts.begin(), extraOpts.end()); +} + +void cmQtAutoGenerators::SetupAutoUicTarget(cmTarget* target) +{ + cmMakefile *makefile = target->GetMakefile(); + + const char *qtUic = makefile->GetSafeDefinition("QT_UIC_EXECUTABLE"); + makefile->AddDefinition("_qt_uic_executable", qtUic); + + const std::vector& srcFiles = target->GetSourceFiles(); + + std::string skip_uic; + const char *sep = ""; + + bool skip = target->GetPropertyAsBool("SKIP_AUTOUIC"); + + std::set skipped; + + for(std::vector::const_iterator fileIt = srcFiles.begin(); + fileIt != srcFiles.end(); + ++fileIt) + { + cmSourceFile* sf = *fileIt; + std::string absFile = cmsys::SystemTools::GetRealPath( + sf->GetFullPath().c_str()); + if (!skip) + { + skip = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOUIC")); + } + + if (skip) + { + skip_uic += sep; + skip_uic += absFile; + sep = ";"; + skipped.insert(absFile); + } + } + + makefile->AddDefinition("_skip_uic", + cmLocalGenerator::EscapeForCMake(skip_uic.c_str()).c_str()); + + std::vector uiFilesWithOptions + = makefile->GetQtUiFilesWithOptions(); + + std::string uiFileFiles; + std::string uiFileOptions; + sep = ""; + + const char *qtVersion = makefile->GetDefinition("_target_qt_version"); + + if (const char* opts = target->GetProperty("AUTOUIC_OPTIONS")) + { + makefile->AddDefinition("_uic_target_options", + cmLocalGenerator::EscapeForCMake(opts).c_str()); + } + + for(std::vector::const_iterator fileIt = + uiFilesWithOptions.begin(); + fileIt != uiFilesWithOptions.end(); + ++fileIt) + { + cmSourceFile* sf = *fileIt; + std::string absFile = cmsys::SystemTools::GetRealPath( + sf->GetFullPath().c_str()); + + if (!skipped.insert(absFile).second) + { + continue; + } + uiFileFiles += sep; + uiFileFiles += absFile; + uiFileOptions += sep; + std::string opts = sf->GetProperty("AUTOUIC_OPTIONS"); + cmSystemTools::ReplaceString(opts, ";", "@list_sep@"); + uiFileOptions += opts; + sep = ";"; + } + + makefile->AddDefinition("_qt_uic_options_files", + cmLocalGenerator::EscapeForCMake(uiFileFiles.c_str()).c_str()); + makefile->AddDefinition("_qt_uic_options_options", + cmLocalGenerator::EscapeForCMake(uiFileOptions.c_str()).c_str()); + + const char* targetName = target->GetName(); + if (strcmp(qtVersion, "5") == 0) + { + cmTarget *qt5Uic = makefile->FindTargetToUse("Qt5::uic"); + if (!qt5Uic) + { + // Project does not use Qt5Widgets, but has AUTOUIC ON anyway + makefile->RemoveDefinition("_qt_uic_executable"); + } + else + { + makefile->AddDefinition("_qt_uic_executable", qt5Uic->GetLocation(0)); + } + } + else + { + if (strcmp(qtVersion, "4") != 0) + { + cmSystemTools::Error("The CMAKE_AUTOUIC feature supports only Qt 4 and " + "Qt 5 ", targetName); + } + } +} + bool cmQtAutoGenerators::Run(const char* targetDirectory, const char *config) { bool success = true; @@ -563,12 +734,15 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(cmMakefile* makefile, "AM_Qt5Core_VERSION_MAJOR"); } this->Sources = makefile->GetSafeDefinition("AM_SOURCES"); + this->SkipMoc = makefile->GetSafeDefinition("AM_SKIP_MOC"); + this->SkipUic = makefile->GetSafeDefinition("AM_SKIP_UIC"); this->Headers = makefile->GetSafeDefinition("AM_HEADERS"); this->IncludeProjectDirsBefore = makefile->IsOn( "AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); this->Srcdir = makefile->GetSafeDefinition("AM_CMAKE_CURRENT_SOURCE_DIR"); this->Builddir = makefile->GetSafeDefinition("AM_CMAKE_CURRENT_BINARY_DIR"); this->MocExecutable = makefile->GetSafeDefinition("AM_QT_MOC_EXECUTABLE"); + this->UicExecutable = makefile->GetSafeDefinition("AM_QT_UIC_EXECUTABLE"); std::string compileDefsPropOrig = "AM_MOC_COMPILE_DEFINITIONS"; std::string compileDefsProp = compileDefsPropOrig; if(config) @@ -594,6 +768,31 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(cmMakefile* makefile, this->ProjectSourceDir = makefile->GetSafeDefinition("AM_CMAKE_SOURCE_DIR"); this->TargetName = makefile->GetSafeDefinition("AM_TARGET_NAME"); + { + const char *uicOptionsFiles + = makefile->GetSafeDefinition("AM_UIC_OPTIONS_FILES"); + const char *uicTargetOptions + = makefile->GetSafeDefinition("AM_UIC_TARGET_OPTIONS"); + cmSystemTools::ExpandListArgument(uicTargetOptions, this->UicTargetOptions); + const char *uicOptionsOptions + = makefile->GetSafeDefinition("AM_UIC_OPTIONS_OPTIONS"); + std::vector uicFilesVec; + cmSystemTools::ExpandListArgument(uicOptionsFiles, uicFilesVec); + std::vector uicOptionsVec; + cmSystemTools::ExpandListArgument(uicOptionsOptions, uicOptionsVec); + if (uicFilesVec.size() != uicOptionsVec.size()) + { + return false; + } + for (std::vector::iterator fileIt = uicFilesVec.begin(), + optionIt = uicOptionsVec.begin(); + fileIt != uicFilesVec.end(); + ++fileIt, ++optionIt) + { + cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";"); + this->UicOptions[*fileIt] = *optionIt; + } + } this->CurrentCompileSettingsStr = this->MakeCompileSettingsString(makefile); this->RelaxedMode = makefile->IsOn("AM_RELAXED_MODE"); @@ -767,10 +966,18 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) const std::vector& headerExtensions = makefile->GetHeaderExtensions(); + std::vector includedUis; + std::vector skippedUis; + std::vector uicSkipped; + cmSystemTools::ExpandListArgument(this->SkipUic, uicSkipped); + for (std::vector::const_iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) { + const bool skipUic = std::find(uicSkipped.begin(), uicSkipped.end(), *it) + != uicSkipped.end(); + std::vector& uiFiles = skipUic ? skippedUis : includedUis; const std::string &absFilename = *it; if (this->Verbose) { @@ -778,15 +985,37 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) } if (this->RelaxedMode) { - this->ParseCppFile(absFilename, headerExtensions, includedMocs); + this->ParseCppFile(absFilename, headerExtensions, includedMocs, + uiFiles); } else { - this->StrictParseCppFile(absFilename, headerExtensions, includedMocs); + this->StrictParseCppFile(absFilename, headerExtensions, includedMocs, + uiFiles); } this->SearchHeadersForCppFile(absFilename, headerExtensions, headerFiles); } + { + std::vector mocSkipped; + cmSystemTools::ExpandListArgument(this->SkipMoc, mocSkipped); + for (std::vector::const_iterator it = mocSkipped.begin(); + it != mocSkipped.end(); + ++it) + { + if (std::find(uicSkipped.begin(), uicSkipped.end(), *it) + != uicSkipped.end()) + { + const std::string &absFilename = *it; + if (this->Verbose) + { + std::cout << "AUTOGEN: Checking " << absFilename << std::endl; + } + this->ParseForUic(absFilename, includedUis); + } + } + } + std::vector headerFilesVec; cmSystemTools::ExpandListArgument(this->Headers, headerFilesVec); for (std::vector::const_iterator it = headerFilesVec.begin(); @@ -798,7 +1027,7 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) // key = moc source filepath, value = moc output filename std::map notIncludedMocs; - this->ParseHeaders(headerFiles, includedMocs, notIncludedMocs); + this->ParseHeaders(headerFiles, includedMocs, notIncludedMocs, includedUis); // run moc on all the moc's that are #included in source files for(std::map::const_iterator @@ -808,6 +1037,12 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) { this->GenerateMoc(it->first, it->second); } + for(std::vector::const_iterator it = includedUis.begin(); + it != includedUis.end(); + ++it) + { + this->GenerateUi(*it); + } cmsys_ios::stringstream outStream; outStream << "/* This file is autogenerated, do not edit*/\n"; @@ -840,6 +1075,12 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) std::cerr << "moc failed..."<< std::endl; return false; } + + if (this->RunUicFailed) + { + std::cerr << "uic failed..."<< std::endl; + return false; + } outStream.flush(); std::string automocSource = outStream.str(); if (!automocCppChanged) @@ -866,7 +1107,8 @@ bool cmQtAutoGenerators::RunAutogen(cmMakefile* makefile) void cmQtAutoGenerators::ParseCppFile(const std::string& absFilename, const std::vector& headerExtensions, - std::map& includedMocs) + std::map& includedMocs, + std::vector &includedUis) { cmsys::RegularExpression mocIncludeRegExp( "[\n][ \t]*#[ \t]*include[ \t]+" @@ -1007,6 +1249,7 @@ void cmQtAutoGenerators::ParseCppFile(const std::string& absFilename, matchOffset += mocIncludeRegExp.end(); } while(mocIncludeRegExp.find(contentsString.c_str() + matchOffset)); } + this->ParseForUic(absFilename, contentsString, includedUis); // In this case, check whether the scanned file itself contains a Q_OBJECT. // If this is the case, the moc_foo.cpp should probably be generated from @@ -1047,7 +1290,8 @@ void cmQtAutoGenerators::ParseCppFile(const std::string& absFilename, void cmQtAutoGenerators::StrictParseCppFile(const std::string& absFilename, const std::vector& headerExtensions, - std::map& includedMocs) + std::map& includedMocs, + std::vector& includedUis) { cmsys::RegularExpression mocIncludeRegExp( "[\n][ \t]*#[ \t]*include[ \t]+" @@ -1138,6 +1382,7 @@ void cmQtAutoGenerators::StrictParseCppFile(const std::string& absFilename, matchOffset += mocIncludeRegExp.end(); } while(mocIncludeRegExp.find(contentsString.c_str() + matchOffset)); } + this->ParseForUic(absFilename, contentsString, includedUis); // In this case, check whether the scanned file itself contains a Q_OBJECT. // If this is the case, the moc_foo.cpp should probably be generated from @@ -1158,6 +1403,61 @@ void cmQtAutoGenerators::StrictParseCppFile(const std::string& absFilename, } +void cmQtAutoGenerators::ParseForUic(const std::string& absFilename, + std::vector& includedUis) +{ + if (this->UicExecutable.empty()) + { + return; + } + const std::string contentsString = this->ReadAll(absFilename); + if (contentsString.empty()) + { + std::cerr << "AUTOGEN: warning: " << absFilename << ": file is empty\n" + << std::endl; + return; + } + this->ParseForUic(absFilename, contentsString, includedUis); +} + + +void cmQtAutoGenerators::ParseForUic(const std::string&, + const std::string& contentsString, + std::vector& includedUis) +{ + if (this->UicExecutable.empty()) + { + return; + } + cmsys::RegularExpression uiIncludeRegExp( + "[\n][ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); + + std::string::size_type matchOffset = 0; + + matchOffset = 0; + if ((strstr(contentsString.c_str(), "ui_") != NULL) + && (uiIncludeRegExp.find(contentsString))) + { + do + { + const std::string currentUi = uiIncludeRegExp.match(1); + + 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 + basename = basename.substr(3); + + includedUis.push_back(basename); + + matchOffset += uiIncludeRegExp.end(); + } while(uiIncludeRegExp.find(contentsString.c_str() + matchOffset)); + } +} + + void cmQtAutoGenerators::SearchHeadersForCppFile(const std::string& absFilename, const std::vector& headerExtensions, @@ -1197,13 +1497,15 @@ cmQtAutoGenerators::SearchHeadersForCppFile(const std::string& absFilename, void cmQtAutoGenerators::ParseHeaders(const std::set& absHeaders, const std::map& includedMocs, - std::map& notIncludedMocs) + std::map& notIncludedMocs, + std::vector& includedUis) { for(std::set::const_iterator hIt=absHeaders.begin(); hIt!=absHeaders.end(); ++hIt) { const std::string& headerName = *hIt; + const std::string contents = this->ReadAll(headerName); if (includedMocs.find(headerName) == includedMocs.end()) { @@ -1216,7 +1518,6 @@ void cmQtAutoGenerators::ParseHeaders(const std::set& absHeaders, GetFilenameWithoutLastExtension(headerName); const std::string currentMoc = "moc_" + basename + ".cpp"; - const std::string contents = this->ReadAll(headerName); std::string macroName; if (requiresMocing(contents, macroName)) { @@ -1224,11 +1525,10 @@ void cmQtAutoGenerators::ParseHeaders(const std::set& absHeaders, notIncludedMocs[headerName] = currentMoc; } } + this->ParseForUic(headerName, contents, includedUis); } - } - bool cmQtAutoGenerators::GenerateMoc(const std::string& sourceFile, const std::string& mocFileName) { @@ -1305,6 +1605,77 @@ bool cmQtAutoGenerators::GenerateMoc(const std::string& sourceFile, return false; } +bool cmQtAutoGenerators::GenerateUi(const std::string& uiFileName) +{ + if (!cmsys::SystemTools::FileExists(this->Builddir.c_str(), false)) + { + cmsys::SystemTools::MakeDirectory(this->Builddir.c_str()); + } + + std::string ui_output_file = "ui_" + uiFileName + ".h"; + std::string ui_input_file = this->Srcdir + uiFileName + ".ui"; + + int sourceNewerThanUi = 0; + bool success = cmsys::SystemTools::FileTimeCompare(ui_input_file.c_str(), + (this->Builddir + ui_output_file).c_str(), + &sourceNewerThanUi); + if (this->GenerateAll || !success || sourceNewerThanUi >= 0) + { + std::string msg = "Generating "; + msg += ui_output_file; + cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue + |cmsysTerminal_Color_ForegroundBold, + msg.c_str(), true, this->ColorOutput); + + std::vector command; + command.push_back(this->UicExecutable); + + std::string options; + std::vector opts = this->UicTargetOptions; + std::map::const_iterator optionIt + = this->UicOptions.find(ui_input_file); + if (optionIt != this->UicOptions.end()) + { + std::vector fileOpts; + cmSystemTools::ExpandListArgument(optionIt->second, fileOpts); + this->MergeUicOptions(opts, fileOpts, this->QtMajorVersion == "5"); + } + for(std::vector::const_iterator optIt = opts.begin(); + optIt != opts.end(); + ++optIt) + { + command.push_back(*optIt); + } + + command.push_back("-o"); + command.push_back(this->Builddir + ui_output_file); + command.push_back(ui_input_file); + + if (this->Verbose) + { + for(std::vector::const_iterator cmdIt = command.begin(); + cmdIt != command.end(); + ++cmdIt) + { + std::cout << *cmdIt << " "; + } + std::cout << std::endl; + } + std::string output; + int retVal = 0; + bool result = cmSystemTools::RunSingleCommand(command, &output, &retVal); + if (!result || retVal) + { + std::cerr << "AUTOUIC: error: process for " << ui_output_file << + " failed:\n" << output << std::endl; + this->RunUicFailed = true; + cmSystemTools::RemoveFile(ui_output_file.c_str()); + return false; + } + return true; + } + return false; +} std::string cmQtAutoGenerators::Join(const std::vector& lst, char separator) diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h index fe38b057b..37b615489 100644 --- a/Source/cmQtAutoGenerators.h +++ b/Source/cmQtAutoGenerators.h @@ -31,6 +31,7 @@ private: const std::string &autogenTargetName, std::map &configIncludes, std::map &configDefines); + void SetupAutoUicTarget(cmTarget* target); cmGlobalGenerator* CreateGlobalGenerator(cmake* cm, const char* targetDirectory); @@ -47,19 +48,30 @@ private: bool RunAutogen(cmMakefile* makefile); bool GenerateMoc(const std::string& sourceFile, const std::string& mocFileName); + bool GenerateUi(const std::string& uiFileName); void ParseCppFile(const std::string& absFilename, const std::vector& headerExtensions, - std::map& includedMocs); + std::map& includedMocs, + std::vector& includedUis); void StrictParseCppFile(const std::string& absFilename, const std::vector& headerExtensions, - std::map& includedMocs); + std::map& includedMocs, + std::vector& includedUis); void SearchHeadersForCppFile(const std::string& absFilename, const std::vector& headerExtensions, std::set& absHeaders); void ParseHeaders(const std::set& absHeaders, const std::map& includedMocs, - std::map& notIncludedMocs); + std::map& notIncludedMocs, + std::vector& includedUis); + + void ParseForUic(const std::string& fileName, + const std::string& contentsString, + std::vector& includedUis); + + void ParseForUic(const std::string& fileName, + std::vector& includedUis); void Init(); @@ -68,13 +80,19 @@ private: bool StartsWith(const std::string& str, const std::string& with); std::string ReadAll(const std::string& filename); + void MergeUicOptions(std::vector &opts, + const std::vector &fileOpts, bool isQt5); + std::string QtMajorVersion; std::string Sources; + std::string SkipMoc; + std::string SkipUic; std::string Headers; bool IncludeProjectDirsBefore; std::string Srcdir; std::string Builddir; std::string MocExecutable; + std::string UicExecutable; std::string MocCompileDefinitionsStr; std::string MocIncludesStr; std::string MocOptionsStr; @@ -89,10 +107,13 @@ private: std::list MocIncludes; std::list MocDefinitions; std::vector MocOptions; + std::vector UicTargetOptions; + std::map UicOptions; bool Verbose; bool ColorOutput; bool RunMocFailed; + bool RunUicFailed; bool GenerateAll; bool RelaxedMode; diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx index d747309cd..ec98c2c2c 100644 --- a/Source/cmSourceFile.cxx +++ b/Source/cmSourceFile.cxx @@ -287,6 +287,17 @@ void cmSourceFile::SetProperty(const char* prop, const char* value) } this->Properties.SetProperty(prop, value, cmProperty::SOURCE_FILE); + + std::string ext = + cmSystemTools::GetFilenameLastExtension(this->Location.GetName()); + if (ext == ".ui") + { + cmMakefile* mf = this->Location.GetMakefile(); + if (strcmp(prop, "AUTOUIC_OPTIONS") == 0) + { + mf->AddQtUiFileWithOptions(this); + } + } } //---------------------------------------------------------------------------- diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index ad4ae0c48..5b77c27c8 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -274,7 +274,9 @@ void cmTarget::SetMakefile(cmMakefile* mf) this->SetPropertyDefault("GNUtoMS", 0); this->SetPropertyDefault("OSX_ARCHITECTURES", 0); this->SetPropertyDefault("AUTOMOC", 0); + this->SetPropertyDefault("AUTOUIC", 0); this->SetPropertyDefault("AUTOMOC_MOC_OPTIONS", 0); + this->SetPropertyDefault("AUTOUIC_OPTIONS", 0); this->SetPropertyDefault("LINK_DEPENDS_NO_SHARED", 0); this->SetPropertyDefault("LINK_INTERFACE_LIBRARIES", 0); this->SetPropertyDefault("WIN32_EXECUTABLE", 0); diff --git a/Tests/QtAutogen/CMakeLists.txt b/Tests/QtAutogen/CMakeLists.txt index 39c1ae9c8..e194b9475 100644 --- a/Tests/QtAutogen/CMakeLists.txt +++ b/Tests/QtAutogen/CMakeLists.txt @@ -35,6 +35,8 @@ add_definitions(-DFOO -DSomeDefine="Barx") # enable relaxed mode so automoc can handle all the special cases: set(CMAKE_AUTOMOC_RELAXED_MODE TRUE) +set(CMAKE_AUTOUIC ON) + # create an executable and two library targets, each requiring automoc: add_library(codeeditorLib STATIC codeeditor.cpp) diff --git a/Tests/QtAutogen/calwidget.cpp b/Tests/QtAutogen/calwidget.cpp index cbfa5a8a6..defde2006 100644 --- a/Tests/QtAutogen/calwidget.cpp +++ b/Tests/QtAutogen/calwidget.cpp @@ -49,7 +49,10 @@ #include "calwidget.h" + #include "ui_calwidget.h" + Window::Window() + : ui(new Ui::Window) { createPreviewGroupBox(); createGeneralOptionsGroupBox(); diff --git a/Tests/QtAutogen/calwidget.h b/Tests/QtAutogen/calwidget.h index 844738937..d21a473de 100644 --- a/Tests/QtAutogen/calwidget.h +++ b/Tests/QtAutogen/calwidget.h @@ -52,6 +52,11 @@ class QGroupBox; class QLabel; + namespace Ui + { + class Window; + } + class Window : public QWidget { Q_OBJECT @@ -116,6 +121,8 @@ QCheckBox *firstFridayCheckBox; QCheckBox *mayFirstCheckBox; + + Ui::Window *ui; }; #endif diff --git a/Tests/QtAutogen/calwidget.ui b/Tests/QtAutogen/calwidget.ui new file mode 100644 index 000000000..1c245cac9 --- /dev/null +++ b/Tests/QtAutogen/calwidget.ui @@ -0,0 +1,32 @@ + + + Window + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + 90 + 180 + 94 + 24 + + + + PushButton + + + + + +