/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGeneratorInitializer.h" #include "cmAlgorithms.h" #include "cmCustomCommandLines.h" #include "cmFilePathChecksum.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmOutputConverter.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmState.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmake.h" #if defined(_WIN32) && !defined(__CYGWIN__) #include "cmGlobalVisualStudioGenerator.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include static void utilCopyTargetProperty(cmTarget* destinationTarget, cmTarget* sourceTarget, const std::string& propertyName) { const char* propertyValue = sourceTarget->GetProperty(propertyName); if (propertyValue) { destinationTarget->SetProperty(propertyName, propertyValue); } } static std::string utilStripCR(std::string const& line) { // Strip CR characters rcc may have printed (possibly more than one!). std::string::size_type cr = line.find('\r'); if (cr != line.npos) { return line.substr(0, cr); } return line; } static std::string GetAutogenTargetName(cmGeneratorTarget const* target) { std::string autogenTargetName = target->GetName(); autogenTargetName += "_autogen"; return autogenTargetName; } static std::string GetAutogenTargetFilesDir(cmGeneratorTarget const* target) { cmMakefile* makefile = target->Target->GetMakefile(); std::string targetDir = makefile->GetCurrentBinaryDirectory(); targetDir += makefile->GetCMakeInstance()->GetCMakeFilesDirectory(); targetDir += "/"; targetDir += GetAutogenTargetName(target); targetDir += ".dir/"; return targetDir; } static std::string GetAutogenTargetBuildDir(cmGeneratorTarget const* target) { cmMakefile* makefile = target->Target->GetMakefile(); std::string targetDir = makefile->GetCurrentBinaryDirectory(); targetDir += "/"; targetDir += GetAutogenTargetName(target); targetDir += "/"; return targetDir; } static std::string GetQtMajorVersion(cmGeneratorTarget const* target) { cmMakefile* makefile = target->Target->GetMakefile(); std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); if (qtMajorVersion.empty()) { qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); } const char* targetQtVersion = target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", ""); if (targetQtVersion != CM_NULLPTR) { qtMajorVersion = targetQtVersion; } return qtMajorVersion; } static void SetupSourceFiles(cmGeneratorTarget const* target, std::vector& mocUicSources, std::vector& mocUicHeaders, std::vector& skipMocList, std::vector& skipUicList) { cmMakefile* makefile = target->Target->GetMakefile(); std::vector srcFiles; target->GetConfigCommonSourceFiles(srcFiles); const bool targetMoc = target->GetPropertyAsBool("AUTOMOC"); const bool targetUic = target->GetPropertyAsBool("AUTOUIC"); cmFilePathChecksum fpathCheckSum(makefile); for (std::vector::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); ++fileIt) { cmSourceFile* sf = *fileIt; const cmSystemTools::FileFormat fileType = cmSystemTools::GetFileFormat(sf->GetExtension().c_str()); if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) && !(fileType == cmSystemTools::HEADER_FILE_FORMAT)) { continue; } if (cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) { continue; } const std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); // Skip flags const bool skipAll = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOGEN")); const bool skipMoc = skipAll || cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOMOC")); const bool skipUic = skipAll || cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOUIC")); // Add file name to skip lists. // Do this even when the file is not added to the sources/headers lists // because the file name may be extracted from an other file when // processing if (skipMoc) { skipMocList.push_back(absFile); } if (skipUic) { skipUicList.push_back(absFile); } if ((targetMoc && !skipMoc) || (targetUic && !skipUic)) { // Add file name to sources or headers list switch (fileType) { case cmSystemTools::CXX_FILE_FORMAT: mocUicSources.push_back(absFile); break; case cmSystemTools::HEADER_FILE_FORMAT: mocUicHeaders.push_back(absFile); break; default: break; } } } } static void GetCompileDefinitionsAndDirectories( cmGeneratorTarget const* target, const std::string& config, std::string& incs, std::string& defs) { std::vector includeDirs; cmLocalGenerator* localGen = target->GetLocalGenerator(); // Get the include dirs for this target, without stripping the implicit // include dirs off, see https://gitlab.kitware.com/cmake/cmake/issues/13667 localGen->GetIncludeDirectories(includeDirs, target, "CXX", config, false); incs = cmJoin(includeDirs, ";"); std::set defines; localGen->AddCompileDefinitions(defines, target, config, "CXX"); defs += cmJoin(defines, ";"); } static void MocSetupAutoTarget( cmGeneratorTarget const* target, const std::string& autogenTargetName, std::vector const& skipMoc, std::map& configIncludes, std::map& configDefines) { cmLocalGenerator* lg = target->GetLocalGenerator(); cmMakefile* makefile = target->Target->GetMakefile(); const char* tmp = target->GetProperty("AUTOMOC_MOC_OPTIONS"); std::string _moc_options = (tmp != CM_NULLPTR ? tmp : ""); makefile->AddDefinition( "_moc_options", cmOutputConverter::EscapeForCMake(_moc_options).c_str()); makefile->AddDefinition( "_skip_moc", cmOutputConverter::EscapeForCMake(cmJoin(skipMoc, ";")).c_str()); bool relaxedMode = makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE"); makefile->AddDefinition("_moc_relaxed_mode", relaxedMode ? "TRUE" : "FALSE"); std::string _moc_incs; std::string _moc_compile_defs; std::vector configs; const std::string& config = makefile->GetConfigurations(configs); GetCompileDefinitionsAndDirectories(target, config, _moc_incs, _moc_compile_defs); makefile->AddDefinition( "_moc_incs", cmOutputConverter::EscapeForCMake(_moc_incs).c_str()); makefile->AddDefinition( "_moc_compile_defs", cmOutputConverter::EscapeForCMake(_moc_compile_defs).c_str()); for (std::vector::const_iterator li = configs.begin(); li != configs.end(); ++li) { std::string config_moc_incs; std::string config_moc_compile_defs; GetCompileDefinitionsAndDirectories(target, *li, config_moc_incs, config_moc_compile_defs); if (config_moc_incs != _moc_incs) { configIncludes[*li] = cmOutputConverter::EscapeForCMake(config_moc_incs); if (_moc_incs.empty()) { _moc_incs = config_moc_incs; } } if (config_moc_compile_defs != _moc_compile_defs) { configDefines[*li] = cmOutputConverter::EscapeForCMake(config_moc_compile_defs); if (_moc_compile_defs.empty()) { _moc_compile_defs = config_moc_compile_defs; } } } const char* qtVersion = makefile->GetDefinition("_target_qt_version"); if (strcmp(qtVersion, "5") == 0) { cmGeneratorTarget* qt5Moc = lg->FindGeneratorTargetToUse("Qt5::moc"); if (!qt5Moc) { cmSystemTools::Error("Qt5::moc target not found ", autogenTargetName.c_str()); return; } makefile->AddDefinition("_qt_moc_executable", qt5Moc->ImportedGetLocation("")); } else if (strcmp(qtVersion, "4") == 0) { cmGeneratorTarget* qt4Moc = lg->FindGeneratorTargetToUse("Qt4::moc"); if (!qt4Moc) { cmSystemTools::Error("Qt4::moc target not found ", autogenTargetName.c_str()); return; } makefile->AddDefinition("_qt_moc_executable", qt4Moc->ImportedGetLocation("")); } else { cmSystemTools::Error("The CMAKE_AUTOMOC feature supports only Qt 4 and " "Qt 5 ", autogenTargetName.c_str()); } } static void UicGetOpts(cmGeneratorTarget const* target, const std::string& config, std::string& optString) { std::vector opts; target->GetAutoUicOptions(opts, config); optString = cmJoin(opts, ";"); } static void UicSetupAutoTarget( cmGeneratorTarget const* target, std::vector const& skipUic, std::map& configUicOptions) { cmLocalGenerator* lg = target->GetLocalGenerator(); cmMakefile* makefile = target->Target->GetMakefile(); std::set skipped; skipped.insert(skipUic.begin(), skipUic.end()); makefile->AddDefinition( "_skip_uic", cmOutputConverter::EscapeForCMake(cmJoin(skipUic, ";")).c_str()); std::vector uiFilesWithOptions = makefile->GetQtUiFilesWithOptions(); const char* qtVersion = makefile->GetDefinition("_target_qt_version"); std::string _uic_opts; std::vector configs; const std::string& config = makefile->GetConfigurations(configs); UicGetOpts(target, config, _uic_opts); if (!_uic_opts.empty()) { _uic_opts = cmOutputConverter::EscapeForCMake(_uic_opts); makefile->AddDefinition("_uic_target_options", _uic_opts.c_str()); } for (std::vector::const_iterator li = configs.begin(); li != configs.end(); ++li) { std::string config_uic_opts; UicGetOpts(target, *li, config_uic_opts); if (config_uic_opts != _uic_opts) { configUicOptions[*li] = cmOutputConverter::EscapeForCMake(config_uic_opts); if (_uic_opts.empty()) { _uic_opts = config_uic_opts; } } } std::string uiFileFiles; std::string uiFileOptions; const char* sep = ""; for (std::vector::const_iterator fileIt = uiFilesWithOptions.begin(); fileIt != uiFilesWithOptions.end(); ++fileIt) { cmSourceFile* sf = *fileIt; std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); 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", cmOutputConverter::EscapeForCMake(uiFileFiles).c_str()); makefile->AddDefinition( "_qt_uic_options_options", cmOutputConverter::EscapeForCMake(uiFileOptions).c_str()); std::string targetName = target->GetName(); if (strcmp(qtVersion, "5") == 0) { cmGeneratorTarget* qt5Uic = lg->FindGeneratorTargetToUse("Qt5::uic"); if (!qt5Uic) { // Project does not use Qt5Widgets, but has AUTOUIC ON anyway } else { makefile->AddDefinition("_qt_uic_executable", qt5Uic->ImportedGetLocation("")); } } else if (strcmp(qtVersion, "4") == 0) { cmGeneratorTarget* qt4Uic = lg->FindGeneratorTargetToUse("Qt4::uic"); if (!qt4Uic) { cmSystemTools::Error("Qt4::uic target not found ", targetName.c_str()); return; } makefile->AddDefinition("_qt_uic_executable", qt4Uic->ImportedGetLocation("")); } else { cmSystemTools::Error("The CMAKE_AUTOUIC feature supports only Qt 4 and " "Qt 5 ", targetName.c_str()); } } static std::string RccGetExecutable(cmGeneratorTarget const* target, const std::string& qtMajorVersion) { cmLocalGenerator* lg = target->GetLocalGenerator(); std::string const& targetName = target->GetName(); if (qtMajorVersion == "5") { cmGeneratorTarget* qt5Rcc = lg->FindGeneratorTargetToUse("Qt5::rcc"); if (!qt5Rcc) { cmSystemTools::Error("Qt5::rcc target not found ", targetName.c_str()); return std::string(); } return qt5Rcc->ImportedGetLocation(""); } if (qtMajorVersion == "4") { cmGeneratorTarget* qt4Rcc = lg->FindGeneratorTargetToUse("Qt4::rcc"); if (!qt4Rcc) { cmSystemTools::Error("Qt4::rcc target not found ", targetName.c_str()); return std::string(); } return qt4Rcc->ImportedGetLocation(""); } cmSystemTools::Error("The CMAKE_AUTORCC feature supports only Qt 4 and " "Qt 5 ", targetName.c_str()); return std::string(); } static void RccMergeOptions(std::vector& opts, const std::vector& fileOpts, bool isQt5) { static const char* valueOptions[] = { "name", "root", "compress", "threshold" }; 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(*it)) != 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()); } /// @brief Reads the resource files list from from a .qrc file - Qt5 version /// @return True if the .qrc file was successfully parsed static bool RccListInputsQt5(cmSourceFile* sf, cmGeneratorTarget const* target, std::vector& depends) { const std::string rccCommand = RccGetExecutable(target, "5"); if (rccCommand.empty()) { cmSystemTools::Error("AUTOGEN: error: rcc executable not available\n"); return false; } bool hasDashDashList = false; // Read rcc features { std::vector command; command.push_back(rccCommand); command.push_back("--help"); std::string rccStdOut; std::string rccStdErr; int retVal = 0; bool result = cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal, CM_NULLPTR, cmSystemTools::OUTPUT_NONE); if (result && retVal == 0 && rccStdOut.find("--list") != std::string::npos) { hasDashDashList = true; } } // Run rcc list command std::vector command; command.push_back(rccCommand); command.push_back(hasDashDashList ? "--list" : "-list"); std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); command.push_back(absFile); std::string rccStdOut; std::string rccStdErr; int retVal = 0; bool result = cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal, CM_NULLPTR, cmSystemTools::OUTPUT_NONE); if (!result || retVal) { std::ostringstream err; err << "AUTOGEN: error: Rcc list process for " << sf->GetFullPath() << " failed:\n" << rccStdOut << "\n" << rccStdErr << std::endl; cmSystemTools::Error(err.str().c_str()); return false; } // Parse rcc list output { std::istringstream ostr(rccStdOut); std::string oline; while (std::getline(ostr, oline)) { oline = utilStripCR(oline); if (!oline.empty()) { depends.push_back(oline); } } } { std::istringstream estr(rccStdErr); std::string eline; while (std::getline(estr, eline)) { eline = utilStripCR(eline); if (cmHasLiteralPrefix(eline, "RCC: Error in")) { static std::string searchString = "Cannot find file '"; std::string::size_type pos = eline.find(searchString); if (pos == std::string::npos) { std::ostringstream err; err << "AUTOGEN: error: Rcc lists unparsable output " << eline << std::endl; cmSystemTools::Error(err.str().c_str()); return false; } pos += searchString.length(); std::string::size_type sz = eline.size() - pos - 1; depends.push_back(eline.substr(pos, sz)); } } } return true; } /// @brief Reads the resource files list from from a .qrc file - Qt4 version /// @return True if the .qrc file was successfully parsed static bool RccListInputsQt4(cmSourceFile* sf, std::vector& depends) { // Read file into string std::string qrcContents; { std::ostringstream stream; stream << cmsys::ifstream(sf->GetFullPath().c_str()).rdbuf(); qrcContents = stream.str(); } cmsys::RegularExpression fileMatchRegex("(]*>)"); fileReplaceRegex.find(qrcEntry); std::string tag = fileReplaceRegex.match(1); qrcEntry = qrcEntry.substr(tag.size()); if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) { qrcEntry = sf->GetLocation().GetDirectory() + "/" + qrcEntry; } depends.push_back(qrcEntry); } return true; } /// @brief Reads the resource files list from from a .qrc file /// @return True if the rcc file was successfully parsed static bool RccListInputs(const std::string& qtMajorVersion, cmSourceFile* sf, cmGeneratorTarget const* target, std::vector& depends) { if (qtMajorVersion == "5") { return RccListInputsQt5(sf, target, depends); } return RccListInputsQt4(sf, depends); } static void RccSetupAutoTarget(cmGeneratorTarget const* target, const std::string& qtMajorVersion) { std::string _rcc_files; const char* sepRccFiles = ""; cmMakefile* makefile = target->Target->GetMakefile(); std::vector srcFiles; target->GetConfigCommonSourceFiles(srcFiles); std::string qrcInputs; const char* qrcInputsSep = ""; std::string rccFileFiles; std::string rccFileOptions; const char* optionSep = ""; const bool qtMajorVersion5 = (qtMajorVersion == "5"); std::vector rccOptions; if (const char* opts = target->GetProperty("AUTORCC_OPTIONS")) { cmSystemTools::ExpandListArgument(opts, rccOptions); } for (std::vector::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); ++fileIt) { cmSourceFile* sf = *fileIt; std::string ext = sf->GetExtension(); if (ext == "qrc") { std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); const bool skip = cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOGEN")) || cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC")); if (!skip) { _rcc_files += sepRccFiles; _rcc_files += absFile; sepRccFiles = ";"; if (const char* prop = sf->GetProperty("AUTORCC_OPTIONS")) { std::vector optsVec; cmSystemTools::ExpandListArgument(prop, optsVec); RccMergeOptions(rccOptions, optsVec, qtMajorVersion5); } if (!rccOptions.empty()) { rccFileFiles += optionSep; rccFileFiles += absFile; rccFileOptions += optionSep; } const char* listSep = ""; for (std::vector::const_iterator it = rccOptions.begin(); it != rccOptions.end(); ++it) { rccFileOptions += listSep; rccFileOptions += *it; listSep = "@list_sep@"; } optionSep = ";"; std::string entriesList; if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) { std::vector depends; if (RccListInputs(qtMajorVersion, sf, target, depends)) { entriesList = cmJoin(depends, "@list_sep@"); } else { return; } } qrcInputs += qrcInputsSep; qrcInputs += entriesList; qrcInputsSep = ";"; } } } makefile->AddDefinition( "_rcc_inputs", cmOutputConverter::EscapeForCMake(qrcInputs).c_str()); makefile->AddDefinition( "_rcc_files", cmOutputConverter::EscapeForCMake(_rcc_files).c_str()); makefile->AddDefinition( "_rcc_options_files", cmOutputConverter::EscapeForCMake(rccFileFiles).c_str()); makefile->AddDefinition( "_rcc_options_options", cmOutputConverter::EscapeForCMake(rccFileOptions).c_str()); makefile->AddDefinition("_qt_rcc_executable", RccGetExecutable(target, qtMajorVersion).c_str()); } void cmQtAutoGeneratorInitializer::InitializeAutogenSources( cmGeneratorTarget* target) { if (target->GetPropertyAsBool("AUTOMOC")) { cmMakefile* makefile = target->Target->GetMakefile(); const std::string mocCppFile = GetAutogenTargetBuildDir(target) + "moc_compilation.cpp"; cmSourceFile* gf = makefile->GetOrCreateSource(mocCppFile, true); gf->SetProperty("SKIP_AUTOGEN", "On"); target->AddSource(mocCppFile); } } void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( cmLocalGenerator* lg, cmGeneratorTarget* target) { cmMakefile* makefile = target->Target->GetMakefile(); // Create a custom target for running generators at buildtime const std::string autogenTargetName = GetAutogenTargetName(target); const std::string autogenBuildDir = GetAutogenTargetBuildDir(target); const std::string workingDirectory = cmSystemTools::CollapseFullPath("", makefile->GetCurrentBinaryDirectory()); const std::string qtMajorVersion = GetQtMajorVersion(target); std::vector autogenOutputFiles; // Remove old settings on cleanup { std::string fname = GetAutogenTargetFilesDir(target); fname += "/AutogenOldSettings.cmake"; makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fname.c_str(), false); } // Create autogen target build directory and add it to the clean files cmSystemTools::MakeDirectory(autogenBuildDir); makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", autogenBuildDir.c_str(), false); if (target->GetPropertyAsBool("AUTOMOC") || target->GetPropertyAsBool("AUTOUIC")) { // Create autogen target includes directory and // add it to the origin target INCLUDE_DIRECTORIES const std::string incsDir = autogenBuildDir + "include"; cmSystemTools::MakeDirectory(incsDir); target->AddIncludeDirectory(incsDir, true); } if (target->GetPropertyAsBool("AUTOMOC")) { // Register moc compilation file as generated autogenOutputFiles.push_back(autogenBuildDir + "moc_compilation.cpp"); } // Initialize autogen target dependencies std::vector depends; if (const char* autogenDepends = target->GetProperty("AUTOGEN_TARGET_DEPENDS")) { cmSystemTools::ExpandListArgument(autogenDepends, depends); } // Compose command lines cmCustomCommandLines commandLines; { cmCustomCommandLine currentLine; currentLine.push_back(cmSystemTools::GetCMakeCommand()); currentLine.push_back("-E"); currentLine.push_back("cmake_autogen"); currentLine.push_back(GetAutogenTargetFilesDir(target)); currentLine.push_back("$"); commandLines.push_back(currentLine); } // Compose target comment std::string autogenComment; { std::vector toolNames; if (target->GetPropertyAsBool("AUTOMOC")) { toolNames.push_back("MOC"); } if (target->GetPropertyAsBool("AUTOUIC")) { toolNames.push_back("UIC"); } if (target->GetPropertyAsBool("AUTORCC")) { toolNames.push_back("RCC"); } std::string tools = toolNames[0]; toolNames.erase(toolNames.begin()); while (toolNames.size() > 1) { tools += ", " + toolNames[0]; toolNames.erase(toolNames.begin()); } if (toolNames.size() == 1) { tools += " and " + toolNames[0]; } autogenComment = "Automatic " + tools + " for target " + target->GetName(); } #if defined(_WIN32) && !defined(__CYGWIN__) bool usePRE_BUILD = false; cmGlobalGenerator* gg = lg->GetGlobalGenerator(); if (gg->GetName().find("Visual Studio") != std::string::npos) { cmGlobalVisualStudioGenerator* vsgg = static_cast(gg); // Under VS >= 7 use a PRE_BUILD event instead of a separate target to // reduce the number of targets loaded into the IDE. // This also works around a VS 11 bug that may skip updating the target: // https://connect.microsoft.com/VisualStudio/feedback/details/769495 usePRE_BUILD = vsgg->GetVersion() >= cmGlobalVisualStudioGenerator::VS7; if (usePRE_BUILD) { // If the autogen target depends on an other target // don't use PRE_BUILD for (std::vector::iterator it = depends.begin(); it != depends.end(); ++it) { if (!makefile->FindTargetToUse(it->c_str())) { usePRE_BUILD = false; break; } } } } #endif if (target->GetPropertyAsBool("AUTORCC")) { cmFilePathChecksum fpathCheckSum(makefile); std::vector srcFiles; target->GetConfigCommonSourceFiles(srcFiles); for (std::vector::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); ++fileIt) { cmSourceFile* sf = *fileIt; if (sf->GetExtension() == "qrc" && !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTOGEN")) && !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) { { const std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); // Run cmake again when .qrc file changes makefile->AddCMakeDependFile(absFile); std::string rccOutputFile = autogenBuildDir; rccOutputFile += fpathCheckSum.getPart(absFile); rccOutputFile += "/qrc_"; rccOutputFile += cmsys::SystemTools::GetFilenameWithoutLastExtension(absFile); rccOutputFile += ".cpp"; // Add rcc output file to origin target sources cmSourceFile* gf = makefile->GetOrCreateSource(rccOutputFile, true); gf->SetProperty("SKIP_AUTOGEN", "On"); target->AddSource(rccOutputFile); // Register rcc output file as generated autogenOutputFiles.push_back(rccOutputFile); } if (lg->GetGlobalGenerator()->GetName() == "Ninja" #if defined(_WIN32) && !defined(__CYGWIN__) || usePRE_BUILD #endif ) { if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) { RccListInputs(qtMajorVersion, sf, target, depends); #if defined(_WIN32) && !defined(__CYGWIN__) // Cannot use PRE_BUILD because the resource files themselves // may not be sources within the target so VS may not know the // target needs to re-build at all. usePRE_BUILD = false; #endif } } } } } #if defined(_WIN32) && !defined(__CYGWIN__) if (usePRE_BUILD) { // Add the pre-build command directly to bypass the OBJECT_LIBRARY // rejection in cmMakefile::AddCustomCommandToTarget because we know // PRE_BUILD will work for an OBJECT_LIBRARY in this specific case. std::vector no_output; std::vector no_byproducts; cmCustomCommand cc(makefile, no_output, no_byproducts, depends, commandLines, autogenComment.c_str(), workingDirectory.c_str()); cc.SetEscapeOldStyle(false); cc.SetEscapeAllowMakeVars(true); target->Target->AddPreBuildCommand(cc); } else #endif { cmTarget* autogenTarget = makefile->AddUtilityCommand( autogenTargetName, true, workingDirectory.c_str(), /*byproducts=*/autogenOutputFiles, depends, commandLines, false, autogenComment.c_str()); cmGeneratorTarget* gt = new cmGeneratorTarget(autogenTarget, lg); lg->AddGeneratorTarget(gt); // Set target folder const char* autogenFolder = makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); if (!autogenFolder) { autogenFolder = makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER"); } if (autogenFolder && *autogenFolder) { autogenTarget->SetProperty("FOLDER", autogenFolder); } else { // inherit FOLDER property from target (#13688) utilCopyTargetProperty(gt->Target, target->Target, "FOLDER"); } target->Target->AddUtility(autogenTargetName); } } void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( cmGeneratorTarget const* target) { cmMakefile* makefile = target->Target->GetMakefile(); // forget the variables added here afterwards again: cmMakefile::ScopePushPop varScope(makefile); static_cast(varScope); // create a custom target for running generators at buildtime: const std::string autogenTargetName = GetAutogenTargetName(target); const std::string qtMajorVersion = GetQtMajorVersion(target); makefile->AddDefinition( "_moc_target_name", cmOutputConverter::EscapeForCMake(autogenTargetName).c_str()); makefile->AddDefinition( "_origin_target_name", cmOutputConverter::EscapeForCMake(target->GetName()).c_str()); makefile->AddDefinition("_target_qt_version", qtMajorVersion.c_str()); std::vector mocUicSources; std::vector mocUicHeaders; std::vector skipMoc; std::vector skipUic; std::map configMocIncludes; std::map configMocDefines; std::map configUicOptions; if (target->GetPropertyAsBool("AUTOMOC") || target->GetPropertyAsBool("AUTOUIC") || target->GetPropertyAsBool("AUTORCC")) { SetupSourceFiles(target, mocUicSources, mocUicHeaders, skipMoc, skipUic); } makefile->AddDefinition( "_moc_uic_sources", cmOutputConverter::EscapeForCMake(cmJoin(mocUicSources, ";")).c_str()); makefile->AddDefinition( "_moc_uic_headers", cmOutputConverter::EscapeForCMake(cmJoin(mocUicHeaders, ";")).c_str()); if (target->GetPropertyAsBool("AUTOMOC")) { MocSetupAutoTarget(target, autogenTargetName, skipMoc, configMocIncludes, configMocDefines); } if (target->GetPropertyAsBool("AUTOUIC")) { UicSetupAutoTarget(target, skipUic, configUicOptions); } if (target->GetPropertyAsBool("AUTORCC")) { RccSetupAutoTarget(target, qtMajorVersion); } // Generate config file std::string inputFile = cmSystemTools::GetCMakeRoot(); inputFile += "/Modules/AutogenInfo.cmake.in"; std::string outputFile = GetAutogenTargetFilesDir(target); outputFile += "/AutogenInfo.cmake"; makefile->ConfigureFile(inputFile.c_str(), outputFile.c_str(), false, true, false); // Append custom definitions to config file if (!configMocDefines.empty() || !configMocIncludes.empty() || !configUicOptions.empty()) { // Ensure we have write permission in case .in was read-only. mode_t perm = 0; #if defined(_WIN32) && !defined(__CYGWIN__) mode_t mode_write = S_IWRITE; #else mode_t mode_write = S_IWUSR; #endif cmSystemTools::GetPermissions(outputFile, perm); if (!(perm & mode_write)) { cmSystemTools::SetPermissions(outputFile, perm | mode_write); } cmsys::ofstream infoFile(outputFile.c_str(), std::ios::app); if (!infoFile) { std::string error = "Internal CMake error when trying to open file: "; error += outputFile; error += " for writing."; cmSystemTools::Error(error.c_str()); return; } if (!configMocDefines.empty()) { for (std::map::iterator it = configMocDefines.begin(), end = configMocDefines.end(); it != end; ++it) { infoFile << "set(AM_MOC_COMPILE_DEFINITIONS_" << it->first << " " << it->second << ")\n"; } } if (!configMocIncludes.empty()) { for (std::map::iterator it = configMocIncludes.begin(), end = configMocIncludes.end(); it != end; ++it) { infoFile << "set(AM_MOC_INCLUDES_" << it->first << " " << it->second << ")\n"; } } if (!configUicOptions.empty()) { for (std::map::iterator it = configUicOptions.begin(), end = configUicOptions.end(); it != end; ++it) { infoFile << "set(AM_UIC_TARGET_OPTIONS_" << it->first << " " << it->second << ")\n"; } } } }