/* 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 "cmQtAutoGeneratorCommon.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 "cmSourceGroup.h" #include "cmState.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cm_sys_stat.h" #include "cmake.h" #if defined(_WIN32) && !defined(__CYGWIN__) #include "cmGlobalVisualStudioGenerator.h" #endif #include "cmConfigure.h" #include "cmsys/FStream.hxx" #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); } } inline static bool PropertyEnabled(cmSourceFile* sourceFile, const char* key) { return cmSystemTools::IsOn(sourceFile->GetPropertyForUser(key)); } static std::string GetSafeProperty(cmGeneratorTarget const* target, const char* key) { const char* tmp = target->GetProperty(key); return std::string((tmp != CM_NULLPTR) ? tmp : ""); } 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) { std::string targetDir = GetSafeProperty(target, "AUTOGEN_BUILD_DIR"); if (targetDir.empty()) { cmMakefile* makefile = target->Target->GetMakefile(); targetDir = makefile->GetCurrentBinaryDirectory(); targetDir += "/"; targetDir += GetAutogenTargetName(target); } 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 std::string GetQtMinorVersion(cmGeneratorTarget const* target, const std::string& qtMajorVersion) { cmMakefile* makefile = target->Target->GetMakefile(); std::string qtMinorVersion; if (qtMajorVersion == "5") { qtMinorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR"); } if (qtMinorVersion.empty()) { qtMinorVersion = makefile->GetSafeDefinition("QT_VERSION_MINOR"); } const char* targetQtVersion = target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", ""); if (targetQtVersion != CM_NULLPTR) { qtMinorVersion = targetQtVersion; } return qtMinorVersion; } static bool QtVersionGreaterOrEqual(const std::string& major, const std::string& minor, unsigned long requestMajor, unsigned long requestMinor) { unsigned long majorUL(0); unsigned long minorUL(0); if (cmSystemTools::StringToULong(major.c_str(), &majorUL) && cmSystemTools::StringToULong(minor.c_str(), &minorUL)) { return (majorUL > requestMajor) || (majorUL == requestMajor && minorUL >= requestMinor); } return false; } static void GetCompileDefinitionsAndDirectories( cmGeneratorTarget const* target, const std::string& config, std::string& incs, std::string& defs) { cmLocalGenerator* localGen = target->GetLocalGenerator(); { std::vector includeDirs; // 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 bool IsMultiConfig(cmGlobalGenerator* globalGen) { // FIXME: Xcode does not support per-config sources, yet. // (EXCLUDED_SOURCE_FILE_NAMES) // Treat it as a single configuration generator meanwhile. if (globalGen->GetName().find("Xcode") != std::string::npos) { return false; } // FIXME: Visual Studio does not fully support per-config sources yet. if (globalGen->GetName().find("Visual Studio") != std::string::npos) { return false; } return globalGen->IsMultiConfig(); } static std::vector GetConfigurations( cmMakefile* makefile, std::string* config = CM_NULLPTR) { std::vector configs; { std::string cfg = makefile->GetConfigurations(configs); if (config != CM_NULLPTR) { *config = cfg; } } // Add empty configuration on demand if (configs.empty()) { configs.push_back(""); } return configs; } static std::vector GetConfigurationSuffixes(cmMakefile* makefile) { std::vector suffixes; if (IsMultiConfig(makefile->GetGlobalGenerator())) { makefile->GetConfigurations(suffixes); for (std::vector::iterator it = suffixes.begin(); it != suffixes.end(); ++it) { it->insert(0, "_"); } } if (suffixes.empty()) { suffixes.push_back(""); } return suffixes; } static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, const std::string& value) { makefile->AddDefinition(key, cmOutputConverter::EscapeForCMake(value).c_str()); } static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, const std::vector& values) { makefile->AddDefinition( key, cmOutputConverter::EscapeForCMake(cmJoin(values, ";")).c_str()); } static bool AddToSourceGroup(cmMakefile* makefile, const std::string& fileName, cmQtAutoGeneratorCommon::GeneratorType genType) { cmSourceGroup* sourceGroup = CM_NULLPTR; // Acquire source group { const char* groupName = CM_NULLPTR; // Use generator specific group name switch (genType) { case cmQtAutoGeneratorCommon::MOC: groupName = makefile->GetState()->GetGlobalProperty("AUTOMOC_SOURCE_GROUP"); break; case cmQtAutoGeneratorCommon::RCC: groupName = makefile->GetState()->GetGlobalProperty("AUTORCC_SOURCE_GROUP"); break; default: break; } // Use default group name on demand if ((groupName == CM_NULLPTR) || (*groupName == 0)) { groupName = makefile->GetState()->GetGlobalProperty("AUTOGEN_SOURCE_GROUP"); } // Generate a source group on demand if ((groupName != CM_NULLPTR) && (*groupName != 0)) { { const char* delimiter = makefile->GetDefinition("SOURCE_GROUP_DELIMITER"); if (delimiter == CM_NULLPTR) { delimiter = "\\"; } std::vector folders = cmSystemTools::tokenize(groupName, delimiter); sourceGroup = makefile->GetSourceGroup(folders); if (sourceGroup == CM_NULLPTR) { makefile->AddSourceGroup(folders); sourceGroup = makefile->GetSourceGroup(folders); } } if (sourceGroup == CM_NULLPTR) { cmSystemTools::Error( "Autogen: Could not create or find source group: ", cmQtAutoGeneratorCommon::Quoted(groupName).c_str()); return false; } } } if (sourceGroup != CM_NULLPTR) { sourceGroup->AddGroupFile(fileName); } return true; } static void AddGeneratedSource(cmMakefile* makefile, const std::string& filename, cmQtAutoGeneratorCommon::GeneratorType genType) { cmSourceFile* gFile = makefile->GetOrCreateSource(filename, true); gFile->SetProperty("GENERATED", "1"); gFile->SetProperty("SKIP_AUTOGEN", "On"); AddToSourceGroup(makefile, filename, genType); } static void AcquireScanFiles(cmGeneratorTarget const* target, std::vector& mocUicSources, std::vector& mocUicHeaders, std::vector& mocSkipList, std::vector& uicSkipList) { const bool mocTarget = target->GetPropertyAsBool("AUTOMOC"); const bool uicTarget = target->GetPropertyAsBool("AUTOUIC"); std::vector srcFiles; target->GetConfigCommonSourceFiles(srcFiles); 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 (PropertyEnabled(sf, "GENERATED") && !target->GetPropertyAsBool("__UNDOCUMENTED_AUTOGEN_GENERATED_FILES")) { // FIXME: Add a policy whose NEW behavior allows generated files. // The implementation already works. We disable it here to avoid // changing behavior for existing projects that do not expect it. continue; } const std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); // Skip flags const bool skipAll = PropertyEnabled(sf, "SKIP_AUTOGEN"); const bool mocSkip = skipAll || PropertyEnabled(sf, "SKIP_AUTOMOC"); const bool uicSkip = skipAll || PropertyEnabled(sf, "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 (mocSkip) { mocSkipList.push_back(absFile); } if (uicSkip) { uicSkipList.push_back(absFile); } if ((mocTarget && !mocSkip) || (uicTarget && !uicSkip)) { // 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 MocSetupAutoTarget( cmGeneratorTarget const* target, const std::string& autogenTargetName, std::string const& qtMajorVersion, std::string const& config, std::vector const& configs, std::vector const& mocSkipList, std::map& configMocIncludes, std::map& configMocDefines) { cmLocalGenerator* lg = target->GetLocalGenerator(); cmMakefile* makefile = target->Target->GetMakefile(); AddDefinitionEscaped(makefile, "_moc_options", GetSafeProperty(target, "AUTOMOC_MOC_OPTIONS")); AddDefinitionEscaped(makefile, "_moc_skip", mocSkipList); AddDefinitionEscaped(makefile, "_moc_relaxed_mode", makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE") ? "TRUE" : "FALSE"); AddDefinitionEscaped(makefile, "_moc_depend_filters", GetSafeProperty(target, "AUTOMOC_DEPEND_FILTERS")); if (QtVersionGreaterOrEqual( qtMajorVersion, GetQtMinorVersion(target, qtMajorVersion), 5, 8)) { AddDefinitionEscaped( makefile, "_moc_predefs_cmd", makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND")); } // Moc includes and compile definitions { // Default settings std::string incs; std::string compileDefs; GetCompileDefinitionsAndDirectories(target, config, incs, compileDefs); AddDefinitionEscaped(makefile, "_moc_incs", incs); AddDefinitionEscaped(makefile, "_moc_compile_defs", compileDefs); // Configuration specific settings for (std::vector::const_iterator li = configs.begin(); li != configs.end(); ++li) { std::string configIncs; std::string configCompileDefs; GetCompileDefinitionsAndDirectories(target, *li, configIncs, configCompileDefs); if (configIncs != incs) { configMocIncludes[*li] = cmOutputConverter::EscapeForCMake(configIncs); } if (configCompileDefs != compileDefs) { configMocDefines[*li] = cmOutputConverter::EscapeForCMake(configCompileDefs); } } } // Moc executable { std::string err; const char* mocExec = CM_NULLPTR; if (qtMajorVersion == "5") { cmGeneratorTarget* qt5Moc = lg->FindGeneratorTargetToUse("Qt5::moc"); if (qt5Moc != CM_NULLPTR) { mocExec = qt5Moc->ImportedGetLocation(""); } else { err = "Qt5::moc target not found " + autogenTargetName; } } else if (qtMajorVersion == "4") { cmGeneratorTarget* qt4Moc = lg->FindGeneratorTargetToUse("Qt4::moc"); if (qt4Moc != CM_NULLPTR) { mocExec = qt4Moc->ImportedGetLocation(""); } else { err = "Qt4::moc target not found " + autogenTargetName; } } else { err = "The CMAKE_AUTOMOC feature supports only Qt 4 and Qt 5 "; err += autogenTargetName; } // Add definition or error if (err.empty()) { AddDefinitionEscaped(makefile, "_qt_moc_executable", mocExec ? mocExec : ""); } else { cmSystemTools::Error(err.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::string const& qtMajorVersion, std::string const& config, std::vector const& configs, std::vector const& uicSkipList, std::map& configUicOptions) { cmLocalGenerator* lg = target->GetLocalGenerator(); cmMakefile* makefile = target->Target->GetMakefile(); AddDefinitionEscaped(makefile, "_uic_skip", uicSkipList); // Uic search paths { std::vector uicSearchPaths; cmSystemTools::ExpandListArgument( GetSafeProperty(target, "AUTOUIC_SEARCH_PATHS"), uicSearchPaths); const std::string srcDir = makefile->GetCurrentSourceDirectory(); for (std::vector::iterator it = uicSearchPaths.begin(); it != uicSearchPaths.end(); ++it) { *it = cmSystemTools::CollapseFullPath(*it, srcDir); } AddDefinitionEscaped(makefile, "_uic_search_paths", uicSearchPaths); } // Uic target options { // Default settings std::string uicOpts; UicGetOpts(target, config, uicOpts); AddDefinitionEscaped(makefile, "_uic_target_options", uicOpts); // Configuration specific settings for (std::vector::const_iterator li = configs.begin(); li != configs.end(); ++li) { std::string configUicOpts; UicGetOpts(target, *li, configUicOpts); if (configUicOpts != uicOpts) { configUicOptions[*li] = cmOutputConverter::EscapeForCMake(configUicOpts); } } } // Uic files options { std::vector uiFileFiles; std::vector uiFileOptions; { std::set skipped; skipped.insert(uicSkipList.begin(), uicSkipList.end()); const std::vector uiFilesWithOptions = makefile->GetQtUiFilesWithOptions(); for (std::vector::const_iterator fileIt = uiFilesWithOptions.begin(); fileIt != uiFilesWithOptions.end(); ++fileIt) { cmSourceFile* sf = *fileIt; const std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); if (skipped.insert(absFile).second) { // The file wasn't skipped uiFileFiles.push_back(absFile); { std::string opts = sf->GetProperty("AUTOUIC_OPTIONS"); cmSystemTools::ReplaceString(opts, ";", cmQtAutoGeneratorCommon::listSep); uiFileOptions.push_back(opts); } } } } AddDefinitionEscaped(makefile, "_qt_uic_options_files", uiFileFiles); AddDefinitionEscaped(makefile, "_qt_uic_options_options", uiFileOptions); } // Uic executable { std::string err; const char* uicExec = CM_NULLPTR; if (qtMajorVersion == "5") { cmGeneratorTarget* qt5Uic = lg->FindGeneratorTargetToUse("Qt5::uic"); if (qt5Uic != CM_NULLPTR) { uicExec = qt5Uic->ImportedGetLocation(""); } else { // Project does not use Qt5Widgets, but has AUTOUIC ON anyway } } else if (qtMajorVersion == "4") { cmGeneratorTarget* qt4Uic = lg->FindGeneratorTargetToUse("Qt4::uic"); if (qt4Uic != CM_NULLPTR) { uicExec = qt4Uic->ImportedGetLocation(""); } else { err = "Qt4::uic target not found " + target->GetName(); } } else { err = "The CMAKE_AUTOUIC feature supports only Qt 4 and Qt 5 "; err += target->GetName(); } // Add definition or error if (err.empty()) { AddDefinitionEscaped(makefile, "_qt_uic_executable", uicExec ? uicExec : ""); } else { cmSystemTools::Error(err.c_str()); } } } static std::string RccGetExecutable(cmGeneratorTarget const* target, const std::string& qtMajorVersion) { std::string rccExec; cmLocalGenerator* lg = target->GetLocalGenerator(); if (qtMajorVersion == "5") { cmGeneratorTarget* qt5Rcc = lg->FindGeneratorTargetToUse("Qt5::rcc"); if (qt5Rcc != CM_NULLPTR) { rccExec = qt5Rcc->ImportedGetLocation(""); } else { cmSystemTools::Error("Qt5::rcc target not found ", target->GetName().c_str()); } } else if (qtMajorVersion == "4") { cmGeneratorTarget* qt4Rcc = lg->FindGeneratorTargetToUse("Qt4::rcc"); if (qt4Rcc != CM_NULLPTR) { rccExec = qt4Rcc->ImportedGetLocation(""); } else { cmSystemTools::Error("Qt4::rcc target not found ", target->GetName().c_str()); } } else { cmSystemTools::Error( "The CMAKE_AUTORCC feature supports only Qt 4 and Qt 5 ", target->GetName().c_str()); } return rccExec; } 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 fit = fileOpts.begin(); fit != fileOpts.end(); ++fit) { std::vector::iterator existingIt = std::find(opts.begin(), opts.end(), *fit); if (existingIt != opts.end()) { const char* optName = fit->c_str(); if (*optName == '-') { ++optName; if (isQt5 && *optName == '-') { ++optName; } } // Test if this is a value option and change the existing value if ((optName != fit->c_str()) && std::find_if(cmArrayBegin(valueOptions), cmArrayEnd(valueOptions), cmStrCmp(optName)) != cmArrayEnd(valueOptions)) { const std::vector::iterator existValueIt(existingIt + 1); const std::vector::const_iterator fileValueIt(fit + 1); if ((existValueIt != opts.end()) && (fileValueIt != fileOpts.end())) { *existValueIt = *fileValueIt; ++fit; } } } else { extraOpts.push_back(*fit); } } opts.insert(opts.end(), extraOpts.begin(), extraOpts.end()); } static void RccSetupAutoTarget(cmGeneratorTarget const* target, const std::string& qtMajorVersion) { cmMakefile* makefile = target->Target->GetMakefile(); const bool qtMajorVersion5 = (qtMajorVersion == "5"); const std::string rccCommand = RccGetExecutable(target, qtMajorVersion); std::vector _rcc_files; std::vector _rcc_inputs; std::vector rccFileFiles; std::vector rccFileOptions; std::vector rccOptionsTarget; if (const char* opts = target->GetProperty("AUTORCC_OPTIONS")) { cmSystemTools::ExpandListArgument(opts, rccOptionsTarget); } 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") && !PropertyEnabled(sf, "SKIP_AUTOGEN") && !PropertyEnabled(sf, "SKIP_AUTORCC")) { const std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); // qrc file _rcc_files.push_back(absFile); // qrc file entries { std::string entriesList = "{"; // Read input file list only for non generated .qrc files. if (!PropertyEnabled(sf, "GENERATED")) { std::string error; std::vector files; if (cmQtAutoGeneratorCommon::RccListInputs( qtMajorVersion, rccCommand, absFile, files, &error)) { entriesList += cmJoin(files, cmQtAutoGeneratorCommon::listSep); } else { cmSystemTools::Error(error.c_str()); } } entriesList += "}"; _rcc_inputs.push_back(entriesList); } // rcc options for this qrc file { // Merged target and file options std::vector rccOptions(rccOptionsTarget); if (const char* prop = sf->GetProperty("AUTORCC_OPTIONS")) { std::vector optsVec; cmSystemTools::ExpandListArgument(prop, optsVec); RccMergeOptions(rccOptions, optsVec, qtMajorVersion5); } // Only store non empty options lists if (!rccOptions.empty()) { rccFileFiles.push_back(absFile); rccFileOptions.push_back( cmJoin(rccOptions, cmQtAutoGeneratorCommon::listSep)); } } } } AddDefinitionEscaped(makefile, "_qt_rcc_executable", rccCommand); AddDefinitionEscaped(makefile, "_rcc_files", _rcc_files); AddDefinitionEscaped(makefile, "_rcc_inputs", _rcc_inputs); AddDefinitionEscaped(makefile, "_rcc_options_files", rccFileFiles); AddDefinitionEscaped(makefile, "_rcc_options_options", rccFileOptions); } void cmQtAutoGeneratorInitializer::InitializeAutogenSources( cmGeneratorTarget* target) { if (target->GetPropertyAsBool("AUTOMOC")) { cmMakefile* makefile = target->Target->GetMakefile(); const std::vector suffixes = GetConfigurationSuffixes(makefile); // Get build directory const std::string autogenBuildDir = GetAutogenTargetBuildDir(target); // Register all compilation files as generated for (std::vector::const_iterator it = suffixes.begin(); it != suffixes.end(); ++it) { std::string mcFile = autogenBuildDir + "/mocs_compilation"; mcFile += *it; mcFile += ".cpp"; AddGeneratedSource(makefile, mcFile, cmQtAutoGeneratorCommon::MOC); } // Mocs compilation file if (IsMultiConfig(target->GetGlobalGenerator())) { target->AddSource(autogenBuildDir + "/mocs_compilation_$.cpp"); } else { target->AddSource(autogenBuildDir + "/mocs_compilation.cpp"); } } } void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( cmLocalGenerator* lg, cmGeneratorTarget* target) { cmMakefile* makefile = target->Target->GetMakefile(); // Create a custom target for running generators at buildtime const bool mocEnabled = target->GetPropertyAsBool("AUTOMOC"); const bool uicEnabled = target->GetPropertyAsBool("AUTOUIC"); const bool rccEnabled = target->GetPropertyAsBool("AUTORCC"); const bool multiConfig = IsMultiConfig(target->GetGlobalGenerator()); 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); const std::string rccCommand = RccGetExecutable(target, qtMajorVersion); const std::vector suffixes = GetConfigurationSuffixes(makefile); std::vector autogenDependFiles; std::vector autogenDependTargets; std::vector autogenProvides; // Remove build directories on cleanup makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", autogenBuildDir.c_str(), false); // Remove old settings on cleanup { std::string base = GetAutogenTargetFilesDir(target); for (std::vector::const_iterator it = suffixes.begin(); it != suffixes.end(); ++it) { std::string fname = base + "/AutogenOldSettings" + *it + ".cmake"; makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fname.c_str(), false); } } // 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 (mocEnabled) { toolNames.push_back("MOC"); } if (uicEnabled) { toolNames.push_back("UIC"); } if (rccEnabled) { 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(); } // Add moc compilation to generated files list if (mocEnabled) { for (std::vector::const_iterator it = suffixes.begin(); it != suffixes.end(); ++it) { std::string mcFile = autogenBuildDir + "/mocs_compilation"; mcFile += *it; mcFile += ".cpp"; autogenProvides.push_back(mcFile); } } // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES if (mocEnabled || uicEnabled) { if (multiConfig) { target->AddIncludeDirectory(autogenBuildDir + "/include_$", true); } else { target->AddIncludeDirectory(autogenBuildDir + "/include", true); } } #if defined(_WIN32) && !defined(__CYGWIN__) bool usePRE_BUILD = false; cmGlobalGenerator* gg = lg->GetGlobalGenerator(); if (gg->GetName().find("Visual Studio") != std::string::npos) { // Under VS 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 = true; } #endif // Initialize autogen target dependencies if (const char* extraDeps = target->GetProperty("AUTOGEN_TARGET_DEPENDS")) { std::vector deps; cmSystemTools::ExpandListArgument(extraDeps, deps); for (std::vector::const_iterator itC = deps.begin(), itE = deps.end(); itC != itE; ++itC) { if (makefile->FindTargetToUse(*itC) != CM_NULLPTR) { autogenDependTargets.push_back(*itC); } else { autogenDependFiles.push_back(*itC); } } } { cmFilePathChecksum fpathCheckSum(makefile); // Iterate over all source files std::vector srcFiles; target->GetConfigCommonSourceFiles(srcFiles); for (std::vector::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); ++fileIt) { cmSourceFile* sf = *fileIt; if (!PropertyEnabled(sf, "SKIP_AUTOGEN")) { std::string const& ext = sf->GetExtension(); // Add generated file that will be scanned by moc or uic to // the dependencies if (mocEnabled || uicEnabled) { const cmSystemTools::FileFormat fileType = cmSystemTools::GetFileFormat(ext.c_str()); if ((fileType == cmSystemTools::CXX_FILE_FORMAT) || (fileType == cmSystemTools::HEADER_FILE_FORMAT)) { if (PropertyEnabled(sf, "GENERATED")) { if ((mocEnabled && !PropertyEnabled(sf, "SKIP_AUTOMOC")) || (uicEnabled && !PropertyEnabled(sf, "SKIP_AUTOUIC"))) { autogenDependFiles.push_back( cmsys::SystemTools::GetRealPath(sf->GetFullPath())); #if defined(_WIN32) && !defined(__CYGWIN__) // Cannot use PRE_BUILD with generated files usePRE_BUILD = false; #endif } } } } // Process rcc enabled files if (rccEnabled && (ext == "qrc") && !PropertyEnabled(sf, "SKIP_AUTORCC")) { const std::string absFile = cmsys::SystemTools::GetRealPath(sf->GetFullPath()); // Compose rcc output file name { std::string rccOutBase = autogenBuildDir + "/"; rccOutBase += fpathCheckSum.getPart(absFile); rccOutBase += "/qrc_"; rccOutBase += cmsys::SystemTools::GetFilenameWithoutLastExtension(absFile); // Register rcc ouput file as generated for (std::vector::const_iterator it = suffixes.begin(); it != suffixes.end(); ++it) { std::string rccOutCfg = rccOutBase; rccOutCfg += *it; rccOutCfg += ".cpp"; AddGeneratedSource(makefile, rccOutCfg, cmQtAutoGeneratorCommon::RCC); autogenProvides.push_back(rccOutCfg); } // Add rcc output file to origin target sources if (multiConfig) { target->AddSource(rccOutBase + "_$.cpp"); } else { target->AddSource(rccOutBase + ".cpp"); } } if (PropertyEnabled(sf, "GENERATED")) { // Add generated qrc file to the dependencies autogenDependFiles.push_back(absFile); } else { // Run cmake again when .qrc file changes makefile->AddCMakeDependFile(absFile); // Add the qrc input files to the dependencies std::string error; if (!cmQtAutoGeneratorCommon::RccListInputs( qtMajorVersion, rccCommand, absFile, autogenDependFiles, &error)) { cmSystemTools::Error(error.c_str()); } } #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) { // We can't use pre-build if we depend on additional files if (!autogenDependFiles.empty()) { usePRE_BUILD = false; } } 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_depends; cmCustomCommand cc(makefile, no_output, autogenProvides, no_depends, commandLines, autogenComment.c_str(), workingDirectory.c_str()); cc.SetEscapeOldStyle(false); cc.SetEscapeAllowMakeVars(true); target->Target->AddPreBuildCommand(cc); // Add additional target dependencies to the origin target for (std::vector::const_iterator itC = autogenDependTargets.begin(), itE = autogenDependTargets.end(); itC != itE; ++itC) { target->Target->AddUtility(*itC); } } else #endif { cmTarget* autogenTarget = makefile->AddUtilityCommand( autogenTargetName, true, workingDirectory.c_str(), /*byproducts=*/autogenProvides, autogenDependFiles, commandLines, false, autogenComment.c_str()); cmGeneratorTarget* gt = new cmGeneratorTarget(autogenTarget, lg); lg->AddGeneratorTarget(gt); // Add origin link library targets to the autogen target dependencies { const cmTarget::LinkLibraryVectorType& libVec = target->Target->GetOriginalLinkLibraries(); for (cmTarget::LinkLibraryVectorType::const_iterator itC = libVec.begin(), itE = libVec.end(); itC != itE; ++itC) { const std::string& libName = itC->first; if (makefile->FindTargetToUse(libName) != CM_NULLPTR) { autogenDependTargets.push_back(libName); } } } // Add origin utility targets to the autogen target dependencies { const std::set& utils = target->Target->GetUtilities(); for (std::set::const_iterator itC = utils.begin(), itE = utils.end(); itC != itE; ++itC) { autogenDependTargets.push_back(*itC); } } // Add additional target dependencies to the autogen target for (std::vector::const_iterator itC = autogenDependTargets.begin(), itE = autogenDependTargets.end(); itC != itE; ++itC) { autogenTarget->AddUtility(*itC); } // 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); // Get configurations std::string config; const std::vector configs(GetConfigurations(makefile, &config)); // Configurations settings buffers std::map configSuffix; std::map configMocIncludes; std::map configMocDefines; std::map configUicOptions; // Configuration suffix if (IsMultiConfig(target->GetGlobalGenerator())) { for (std::vector::const_iterator it = configs.begin(); it != configs.end(); ++it) { configSuffix[*it] = "_" + *it; } } // Basic setup { const bool mocEnabled = target->GetPropertyAsBool("AUTOMOC"); const bool uicEnabled = target->GetPropertyAsBool("AUTOUIC"); const bool rccEnabled = target->GetPropertyAsBool("AUTORCC"); const std::string autogenTargetName = GetAutogenTargetName(target); const std::string qtMajorVersion = GetQtMajorVersion(target); std::vector sources; std::vector headers; if (mocEnabled || uicEnabled || rccEnabled) { std::vector mocSkipList; std::vector uicSkipList; AcquireScanFiles(target, sources, headers, mocSkipList, uicSkipList); if (mocEnabled) { MocSetupAutoTarget(target, autogenTargetName, qtMajorVersion, config, configs, mocSkipList, configMocIncludes, configMocDefines); } if (uicEnabled) { UicSetupAutoTarget(target, qtMajorVersion, config, configs, uicSkipList, configUicOptions); } if (rccEnabled) { RccSetupAutoTarget(target, qtMajorVersion); } } AddDefinitionEscaped(makefile, "_autogen_build_dir", GetAutogenTargetBuildDir(target)); AddDefinitionEscaped(makefile, "_qt_version_major", qtMajorVersion); AddDefinitionEscaped(makefile, "_sources", sources); AddDefinitionEscaped(makefile, "_headers", headers); } // Generate info file std::string infoFile = GetAutogenTargetFilesDir(target); infoFile += "/AutogenInfo.cmake"; { std::string inf = cmSystemTools::GetCMakeRoot(); inf += "/Modules/AutogenInfo.cmake.in"; makefile->ConfigureFile(inf.c_str(), infoFile.c_str(), false, true, false); } // Append custom definitions to info file on demand if (!configSuffix.empty() || !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(infoFile, perm); if (!(perm & mode_write)) { cmSystemTools::SetPermissions(infoFile, perm | mode_write); } // Open and write file cmsys::ofstream ofs(infoFile.c_str(), std::ios::app); if (ofs) { ofs << "# Configuration specific options\n"; for (std::map::iterator it = configSuffix.begin(), end = configSuffix.end(); it != end; ++it) { ofs << "set(AM_CONFIG_SUFFIX_" << it->first << " " << it->second << ")\n"; } for (std::map::iterator it = configMocDefines.begin(), end = configMocDefines.end(); it != end; ++it) { ofs << "set(AM_MOC_DEFINITIONS_" << it->first << " " << it->second << ")\n"; } for (std::map::iterator it = configMocIncludes.begin(), end = configMocIncludes.end(); it != end; ++it) { ofs << "set(AM_MOC_INCLUDES_" << it->first << " " << it->second << ")\n"; } for (std::map::iterator it = configUicOptions.begin(), end = configUicOptions.end(); it != end; ++it) { ofs << "set(AM_UIC_TARGET_OPTIONS_" << it->first << " " << it->second << ")\n"; } } else { // File open error std::string error = "Internal CMake error when trying to open file: "; error += cmQtAutoGeneratorCommon::Quoted(infoFile); error += " for writing."; cmSystemTools::Error(error.c_str()); } } }