diff options
Diffstat (limited to 'Source/cmQtAutoGenInitializer.cxx')
-rw-r--r-- | Source/cmQtAutoGenInitializer.cxx | 1910 |
1 files changed, 1046 insertions, 864 deletions
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 34196d9..8a202a2 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -37,23 +37,6 @@ #include <utility> #include <vector> -inline static const char* SafeString(const char* value) -{ - return (value != nullptr) ? value : ""; -} - -inline static std::string GetSafeProperty(cmGeneratorTarget const* target, - const char* key) -{ - return std::string(SafeString(target->GetProperty(key))); -} - -inline static std::string GetSafeProperty(cmSourceFile const* sf, - const char* key) -{ - return std::string(SafeString(sf->GetProperty(key))); -} - static std::size_t GetParallelCPUCount() { static std::size_t count = 0; @@ -190,21 +173,20 @@ static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin, return cycle; } -cmQtAutoGenInitializer::cmQtAutoGenInitializer( - cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled, - std::string const& qtVersionMajor) +cmQtAutoGenInitializer::cmQtAutoGenInitializer(cmGeneratorTarget* target, + bool mocEnabled, + bool uicEnabled, + bool rccEnabled, + IntegerVersion const& qtVersion) : Target(target) - , MocEnabled(mocEnabled) - , UicEnabled(uicEnabled) - , RccEnabled(rccEnabled) - , MultiConfig(false) - , QtVersionMajor(qtVersionMajor) + , QtVersion(qtVersion) { - this->QtVersionMinor = - cmQtAutoGenInitializer::GetQtMinorVersion(target, this->QtVersionMajor); + Moc.Enabled = mocEnabled; + Uic.Enabled = uicEnabled; + Rcc.Enabled = rccEnabled; } -void cmQtAutoGenInitializer::InitCustomTargets() +bool cmQtAutoGenInitializer::InitCustomTargets() { cmMakefile* makefile = this->Target->Target->GetMakefile(); cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); @@ -217,48 +199,17 @@ void cmQtAutoGenInitializer::InitCustomTargets() this->ConfigsList.push_back(this->ConfigDefault); } - // Autogen target name - this->AutogenTargetName = this->Target->GetName(); - this->AutogenTargetName += "_autogen"; - - // Autogen directories - { - // Collapsed current binary directory - std::string const cbd = cmSystemTools::CollapseFullPath( - "", makefile->GetCurrentBinaryDirectory()); - - // Autogen info dir - this->DirInfo = cbd; - this->DirInfo += makefile->GetCMakeInstance()->GetCMakeFilesDirectory(); - this->DirInfo += '/'; - this->DirInfo += this->AutogenTargetName; - this->DirInfo += ".dir"; - cmSystemTools::ConvertToUnixSlashes(this->DirInfo); - - // Autogen build dir - this->DirBuild = GetSafeProperty(this->Target, "AUTOGEN_BUILD_DIR"); - if (this->DirBuild.empty()) { - this->DirBuild = cbd; - this->DirBuild += '/'; - this->DirBuild += this->AutogenTargetName; + // Verbosity + this->Verbosity = makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE"); + if (!this->Verbosity.empty()) { + unsigned long iVerb = 0; + if (!cmSystemTools::StringToULong(this->Verbosity.c_str(), &iVerb)) { + // Non numeric verbosity + this->Verbosity = cmSystemTools::IsOn(this->Verbosity) ? "1" : "0"; } - cmSystemTools::ConvertToUnixSlashes(this->DirBuild); - - // Working directory - this->DirWork = cbd; - cmSystemTools::ConvertToUnixSlashes(this->DirWork); } - // Autogen files - { - this->AutogenInfoFile = this->DirInfo; - this->AutogenInfoFile += "/AutogenInfo.cmake"; - - this->AutogenSettingsFile = this->DirInfo; - this->AutogenSettingsFile += "/AutogenOldSettings.txt"; - } - - // Autogen target FOLDER property + // Targets FOLDER { const char* folder = makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); @@ -268,107 +219,321 @@ void cmQtAutoGenInitializer::InitCustomTargets() } // Inherit FOLDER property from target (#13688) if (folder == nullptr) { - folder = SafeString(this->Target->Target->GetProperty("FOLDER")); + folder = this->Target->GetProperty("FOLDER"); } if (folder != nullptr) { - this->AutogenFolder = folder; + this->TargetsFolder = folder; } } - std::set<std::string> autogenDependFiles; - std::set<cmTarget*> autogenDependTargets; - std::vector<std::string> autogenProvides; - - // Remove build directories on cleanup - AddCleanFile(makefile, this->DirBuild); - // Remove old settings on cleanup + // Common directories { - std::string base = this->DirInfo; - base += "/AutogenOldSettings"; + // Collapsed current binary directory + std::string const cbd = cmSystemTools::CollapseFullPath( + std::string(), makefile->GetCurrentBinaryDirectory()); + + // Info directory + this->Dir.Info = cbd; + this->Dir.Info += makefile->GetCMakeInstance()->GetCMakeFilesDirectory(); + this->Dir.Info += '/'; + this->Dir.Info += this->Target->GetName(); + this->Dir.Info += "_autogen"; + this->Dir.Info += ".dir"; + cmSystemTools::ConvertToUnixSlashes(this->Dir.Info); + + // Build directory + this->Dir.Build = this->Target->GetSafeProperty("AUTOGEN_BUILD_DIR"); + if (this->Dir.Build.empty()) { + this->Dir.Build = cbd; + this->Dir.Build += '/'; + this->Dir.Build += this->Target->GetName(); + this->Dir.Build += "_autogen"; + } + cmSystemTools::ConvertToUnixSlashes(this->Dir.Build); + // Cleanup build directory + AddCleanFile(makefile, this->Dir.Build); + + // Working directory + this->Dir.Work = cbd; + cmSystemTools::ConvertToUnixSlashes(this->Dir.Work); + + // Include directory + this->Dir.Include = this->Dir.Build; + this->Dir.Include += "/include"; + if (this->MultiConfig) { + this->Dir.Include += "_$<CONFIG>"; + } + // Per config include directories if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { - std::string filename = base; - filename += '_'; - filename += cfg; - filename += ".cmake"; - AddCleanFile(makefile, filename); + std::string& dir = this->Dir.ConfigInclude[cfg]; + dir = this->Dir.Build; + dir += "/include_"; + dir += cfg; } - } else { - AddCleanFile(makefile, base.append(".cmake")); } } - // Add moc compilation to generated files list - if (this->MocEnabled) { - std::string mocsComp = this->DirBuild + "/mocs_compilation.cpp"; - this->AddGeneratedSource(mocsComp, GeneratorT::MOC); - autogenProvides.push_back(std::move(mocsComp)); + // Moc, Uic and _autogen target settings + if (this->Moc.Enabled || this->Uic.Enabled) { + // Init moc specific settings + if (this->Moc.Enabled && !InitMoc()) { + return false; + } + + // Init uic specific settings + if (this->Uic.Enabled && !InitUic()) { + return false; + } + + // Autogen target name + this->AutogenTarget.Name = this->Target->GetName(); + this->AutogenTarget.Name += "_autogen"; + + // Autogen target parallel processing + this->AutogenTarget.Parallel = + this->Target->GetSafeProperty("AUTOGEN_PARALLEL"); + if (this->AutogenTarget.Parallel.empty() || + (this->AutogenTarget.Parallel == "AUTO")) { + // Autodetect number of CPUs + this->AutogenTarget.Parallel = std::to_string(GetParallelCPUCount()); + } + + // Autogen target info and settings files + { + this->AutogenTarget.InfoFile = this->Dir.Info; + this->AutogenTarget.InfoFile += "/AutogenInfo.cmake"; + + this->AutogenTarget.SettingsFile = this->Dir.Info; + this->AutogenTarget.SettingsFile += "/AutogenOldSettings.txt"; + + if (this->MultiConfig) { + for (std::string const& cfg : this->ConfigsList) { + std::string& filename = this->AutogenTarget.ConfigSettingsFile[cfg]; + filename = + AppendFilenameSuffix(this->AutogenTarget.SettingsFile, "_" + cfg); + AddCleanFile(makefile, filename); + } + } else { + AddCleanFile(makefile, this->AutogenTarget.SettingsFile); + } + } + + // Autogen target: Compute user defined dependencies + { + std::string const deps = + this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS"); + if (!deps.empty()) { + std::vector<std::string> extraDeps; + cmSystemTools::ExpandListArgument(deps, extraDeps); + for (std::string const& depName : extraDeps) { + // Allow target and file dependencies + auto* depTarget = makefile->FindTargetToUse(depName); + if (depTarget != nullptr) { + this->AutogenTarget.DependTargets.insert(depTarget); + } else { + this->AutogenTarget.DependFiles.insert(depName); + } + } + } + } } - // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES - if (this->MocEnabled || this->UicEnabled || - (this->RccEnabled && this->MultiConfig)) { - std::string includeDir = this->DirBuild; - includeDir += "/include"; + // Init rcc specific settings + if (this->Rcc.Enabled && !InitRcc()) { + return false; + } + + // Add autogen include directory to the origin target INCLUDE_DIRECTORIES + if (this->Moc.Enabled || this->Uic.Enabled || + (this->Rcc.Enabled && this->MultiConfig)) { + this->Target->AddIncludeDirectory(this->Dir.Include, true); + } + + // Scan files + if (!this->InitScanFiles()) { + return false; + } + + // Create autogen target + if ((this->Moc.Enabled || this->Uic.Enabled) && !this->InitAutogenTarget()) { + return false; + } + + // Create rcc targets + if (this->Rcc.Enabled && !this->InitRccTargets()) { + return false; + } + + return true; +} + +bool cmQtAutoGenInitializer::InitMoc() +{ + cmMakefile* makefile = this->Target->Target->GetMakefile(); + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + + // Mocs compilation file + this->Moc.MocsCompilation = this->Dir.Build; + this->Moc.MocsCompilation += "/mocs_compilation.cpp"; + + // Moc predefs command + if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") && + (this->QtVersion >= IntegerVersion(5, 8))) { + this->Moc.PredefsCmd = + makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"); + } + + // Moc includes + { + bool const appendImplicit = (this->QtVersion.Major == 5); + auto GetIncludeDirs = + [this, localGen, appendImplicit](std::string const& cfg) -> std::string { + // Get the include dirs for this target, without stripping the implicit + // include dirs off, see + // https://gitlab.kitware.com/cmake/cmake/issues/13667 + std::vector<std::string> dirs; + localGen->GetIncludeDirectories(dirs, this->Target, "CXX", cfg, false, + appendImplicit); + return cmJoin(dirs, ";"); + }; + + // Default configuration include directories + this->Moc.Includes = GetIncludeDirs(this->ConfigDefault); + // Other configuration settings if (this->MultiConfig) { - includeDir += "_$<CONFIG>"; + for (std::string const& cfg : this->ConfigsList) { + std::string dirs = GetIncludeDirs(cfg); + if (dirs != this->Moc.Includes) { + this->Moc.ConfigIncludes[cfg] = std::move(dirs); + } + } } - this->Target->AddIncludeDirectory(includeDir, true); } - // Acquire rcc executable and features - if (this->RccEnabled) { - { - std::string err; - if (this->QtVersionMajor == "5") { - cmGeneratorTarget* tgt = - localGen->FindGeneratorTargetToUse("Qt5::rcc"); - if (tgt != nullptr) { - this->RccExecutable = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTORCC: Qt5::rcc target not found"; - } - } else if (QtVersionMajor == "4") { - cmGeneratorTarget* tgt = - localGen->FindGeneratorTargetToUse("Qt4::rcc"); - if (tgt != nullptr) { - this->RccExecutable = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTORCC: Qt4::rcc target not found"; + // Moc compile definitions + { + auto GetCompileDefinitions = + [this, localGen](std::string const& cfg) -> std::string { + std::set<std::string> defines; + localGen->AddCompileDefinitions(defines, this->Target, cfg, "CXX"); + return cmJoin(defines, ";"); + }; + + // Default configuration defines + this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault); + // Other configuration defines + if (this->MultiConfig) { + for (std::string const& cfg : this->ConfigsList) { + std::string defines = GetCompileDefinitions(cfg); + if (defines != this->Moc.Defines) { + this->Moc.ConfigDefines[cfg] = std::move(defines); } - } else { - err = "The AUTORCC feature supports only Qt 4 and Qt 5"; } - if (!err.empty()) { - err += " ("; - err += this->Target->GetName(); - err += ")"; - cmSystemTools::Error(err.c_str()); + } + } + + // Moc executable + if (!GetMocExecutable()) { + return false; + } + + return true; +} + +bool cmQtAutoGenInitializer::InitUic() +{ + cmMakefile* makefile = this->Target->Target->GetMakefile(); + + // Uic search paths + { + std::string const usp = + this->Target->GetSafeProperty("AUTOUIC_SEARCH_PATHS"); + if (!usp.empty()) { + cmSystemTools::ExpandListArgument(usp, this->Uic.SearchPaths); + std::string const& srcDir = makefile->GetCurrentSourceDirectory(); + for (std::string& path : this->Uic.SearchPaths) { + path = cmSystemTools::CollapseFullPath(path, srcDir); } } - // Detect if rcc supports (-)-list - if (!this->RccExecutable.empty() && (this->QtVersionMajor == "5")) { - std::vector<std::string> command; - command.push_back(this->RccExecutable); - command.push_back("--help"); - std::string rccStdOut; - std::string rccStdErr; - int retVal = 0; - bool result = cmSystemTools::RunSingleCommand( - command, &rccStdOut, &rccStdErr, &retVal, nullptr, - cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto); - if (result && retVal == 0 && - rccStdOut.find("--list") != std::string::npos) { - this->RccListOptions.push_back("--list"); - } else { - this->RccListOptions.push_back("-list"); + } + // Uic target options + { + auto UicGetOpts = [this](std::string const& cfg) -> std::string { + std::vector<std::string> opts; + this->Target->GetAutoUicOptions(opts, cfg); + return cmJoin(opts, ";"); + }; + + // Default settings + this->Uic.Options = UicGetOpts(this->ConfigDefault); + + // Configuration specific settings + if (this->MultiConfig) { + for (std::string const& cfg : this->ConfigsList) { + std::string options = UicGetOpts(cfg); + if (options != this->Uic.Options) { + this->Uic.ConfigOptions[cfg] = std::move(options); + } + } + } + } + // .ui files skip and options + { + std::string const uiExt = "ui"; + std::string pathError; + for (cmSourceFile* sf : makefile->GetSourceFiles()) { + // sf->GetExtension() is only valid after sf->GetFullPath() ... + // Since we're iterating over source files that might be not in the + // target we need to check for path errors (not existing files). + std::string const& fPath = sf->GetFullPath(&pathError); + if (!pathError.empty()) { + pathError.clear(); + continue; + } + if (sf->GetExtension() == uiExt) { + std::string const absFile = cmSystemTools::GetRealPath(fPath); + // Check if the .ui file should be skipped + if (sf->GetPropertyAsBool("SKIP_AUTOUIC") || + sf->GetPropertyAsBool("SKIP_AUTOGEN")) { + this->Uic.Skip.insert(absFile); + } + // Check if the .ui file has uic options + std::string const uicOpts = sf->GetSafeProperty("AUTOUIC_OPTIONS"); + if (!uicOpts.empty()) { + // Check if file isn't skipped + if (this->Uic.Skip.count(absFile) == 0) { + this->Uic.FileFiles.push_back(absFile); + std::vector<std::string> optsVec; + cmSystemTools::ExpandListArgument(uicOpts, optsVec); + this->Uic.FileOptions.push_back(std::move(optsVec)); + } + } } } } - // Extract relevant source files - std::vector<std::string> generatedSources; - std::vector<std::string> generatedHeaders; + // Uic executable + if (!GetUicExecutable()) { + return false; + } + + return true; +} + +bool cmQtAutoGenInitializer::InitRcc() +{ + if (!GetRccExecutable()) { + return false; + } + return true; +} + +bool cmQtAutoGenInitializer::InitScanFiles() +{ + cmMakefile* makefile = this->Target->Target->GetMakefile(); + + // Scan through target files { std::string const qrcExt = "qrc"; std::vector<cmSourceFile*> srcFiles; @@ -381,34 +546,34 @@ void cmQtAutoGenInitializer::InitCustomTargets() std::string const& fPath = sf->GetFullPath(); std::string const& ext = sf->GetExtension(); // Register generated files that will be scanned by moc or uic - if (this->MocEnabled || this->UicEnabled) { + if (this->Moc.Enabled || this->Uic.Enabled) { cmSystemTools::FileFormat const fileType = cmSystemTools::GetFileFormat(ext.c_str()); if ((fileType == cmSystemTools::CXX_FILE_FORMAT) || (fileType == cmSystemTools::HEADER_FILE_FORMAT)) { std::string const absPath = cmSystemTools::GetRealPath(fPath); - if ((this->MocEnabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) || - (this->UicEnabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) { + if ((this->Moc.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) || + (this->Uic.Enabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) { // Register source const bool generated = sf->GetPropertyAsBool("GENERATED"); if (fileType == cmSystemTools::HEADER_FILE_FORMAT) { if (generated) { - generatedHeaders.push_back(absPath); + this->AutogenTarget.HeadersGenerated.push_back(absPath); } else { - this->Headers.push_back(absPath); + this->AutogenTarget.Headers.push_back(absPath); } } else { if (generated) { - generatedSources.push_back(absPath); + this->AutogenTarget.SourcesGenerated.push_back(absPath); } else { - this->Sources.push_back(absPath); + this->AutogenTarget.Sources.push_back(absPath); } } } } } // Register rcc enabled files - if (this->RccEnabled && (ext == qrcExt) && + if (this->Rcc.Enabled && (ext == qrcExt) && !sf->GetPropertyAsBool("SKIP_AUTORCC")) { // Register qrc file { @@ -419,148 +584,155 @@ void cmQtAutoGenInitializer::InitCustomTargets() qrc.Generated = sf->GetPropertyAsBool("GENERATED"); // RCC options { - std::string const opts = GetSafeProperty(sf, "AUTORCC_OPTIONS"); + std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS"); if (!opts.empty()) { cmSystemTools::ExpandListArgument(opts, qrc.Options); } } - this->Qrcs.push_back(std::move(qrc)); + this->Rcc.Qrcs.push_back(std::move(qrc)); } } } - // cmGeneratorTarget::GetConfigCommonSourceFiles computes the target's - // sources meta data cache. Clear it so that OBJECT library targets that - // are AUTOGEN initialized after this target get their added - // mocs_compilation.cpp source acknowledged by this target. - this->Target->ClearSourcesCache(); } - // Read skip files from makefile sources - if (this->MocEnabled || this->UicEnabled) { - std::string pathError; - for (cmSourceFile* sf : makefile->GetSourceFiles()) { - // sf->GetExtension() is only valid after sf->GetFullPath() ... - // Since we're iterating over source files that might be not in the - // target we need to check for path errors (not existing files). - std::string const& fPath = sf->GetFullPath(&pathError); - if (!pathError.empty()) { - pathError.clear(); - continue; - } - cmSystemTools::FileFormat const fileType = - cmSystemTools::GetFileFormat(sf->GetExtension().c_str()); - if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) && - !(fileType == cmSystemTools::HEADER_FILE_FORMAT)) { - continue; - } - const bool skipAll = sf->GetPropertyAsBool("SKIP_AUTOGEN"); - const bool mocSkip = - this->MocEnabled && (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC")); - const bool uicSkip = - this->UicEnabled && (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC")); - if (mocSkip || uicSkip) { - std::string const absFile = cmSystemTools::GetRealPath(fPath); - if (mocSkip) { - this->MocSkip.insert(absFile); + // cmGeneratorTarget::GetConfigCommonSourceFiles computes the target's + // sources meta data cache. Clear it so that OBJECT library targets that + // are AUTOGEN initialized after this target get their added + // mocs_compilation.cpp source acknowledged by this target. + this->Target->ClearSourcesCache(); + + if (this->Moc.Enabled || this->Uic.Enabled) { + // Read skip files from makefile sources + { + std::string pathError; + for (cmSourceFile* sf : makefile->GetSourceFiles()) { + // sf->GetExtension() is only valid after sf->GetFullPath() ... + // Since we're iterating over source files that might be not in the + // target we need to check for path errors (not existing files). + std::string const& fPath = sf->GetFullPath(&pathError); + if (!pathError.empty()) { + pathError.clear(); + continue; + } + cmSystemTools::FileFormat const fileType = + cmSystemTools::GetFileFormat(sf->GetExtension().c_str()); + if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) && + !(fileType == cmSystemTools::HEADER_FILE_FORMAT)) { + continue; } - if (uicSkip) { - this->UicSkip.insert(absFile); + const bool skipAll = sf->GetPropertyAsBool("SKIP_AUTOGEN"); + const bool mocSkip = this->Moc.Enabled && + (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC")); + const bool uicSkip = this->Uic.Enabled && + (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC")); + if (mocSkip || uicSkip) { + std::string const absFile = cmSystemTools::GetRealPath(fPath); + if (mocSkip) { + this->Moc.Skip.insert(absFile); + } + if (uicSkip) { + this->Uic.Skip.insert(absFile); + } } } } - } - // Process GENERATED sources and headers - if (!generatedSources.empty() || !generatedHeaders.empty()) { - // Check status of policy CMP0071 - bool policyAccept = false; - bool policyWarn = false; - cmPolicies::PolicyStatus const CMP0071_status = - makefile->GetPolicyStatus(cmPolicies::CMP0071); - switch (CMP0071_status) { - case cmPolicies::WARN: - policyWarn = true; - CM_FALLTHROUGH; - case cmPolicies::OLD: - // Ignore GENERATED file - break; - case cmPolicies::REQUIRED_IF_USED: - case cmPolicies::REQUIRED_ALWAYS: - case cmPolicies::NEW: - // Process GENERATED file - policyAccept = true; - break; - } - - if (policyAccept) { - // Accept GENERATED sources - for (std::string const& absFile : generatedHeaders) { - this->Headers.push_back(absFile); - autogenDependFiles.insert(absFile); - } - for (std::string const& absFile : generatedSources) { - this->Sources.push_back(absFile); - autogenDependFiles.insert(absFile); + // Process GENERATED sources and headers + if (!this->AutogenTarget.SourcesGenerated.empty() || + !this->AutogenTarget.HeadersGenerated.empty()) { + // Check status of policy CMP0071 + bool policyAccept = false; + bool policyWarn = false; + cmPolicies::PolicyStatus const CMP0071_status = + makefile->GetPolicyStatus(cmPolicies::CMP0071); + switch (CMP0071_status) { + case cmPolicies::WARN: + policyWarn = true; + CM_FALLTHROUGH; + case cmPolicies::OLD: + // Ignore GENERATED file + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Process GENERATED file + policyAccept = true; + break; } - } else { - if (policyWarn) { - std::string msg; - msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071); - msg += "\n"; - std::string tools; - std::string property; - if (this->MocEnabled && this->UicEnabled) { - tools = "AUTOMOC and AUTOUIC"; - property = "SKIP_AUTOGEN"; - } else if (this->MocEnabled) { - tools = "AUTOMOC"; - property = "SKIP_AUTOMOC"; - } else if (this->UicEnabled) { - tools = "AUTOUIC"; - property = "SKIP_AUTOUIC"; + + if (policyAccept) { + // Accept GENERATED sources + for (std::string const& absFile : + this->AutogenTarget.HeadersGenerated) { + this->AutogenTarget.Headers.push_back(absFile); + this->AutogenTarget.DependFiles.insert(absFile); } - msg += "For compatibility, CMake is excluding the GENERATED source " - "file(s):\n"; - for (const std::string& absFile : generatedHeaders) { - msg.append(" ").append(Quoted(absFile)).append("\n"); + for (std::string const& absFile : + this->AutogenTarget.SourcesGenerated) { + this->AutogenTarget.Sources.push_back(absFile); + this->AutogenTarget.DependFiles.insert(absFile); } - for (const std::string& absFile : generatedSources) { - msg.append(" ").append(Quoted(absFile)).append("\n"); + } else { + if (policyWarn) { + std::string msg; + msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071); + msg += "\n"; + std::string tools; + std::string property; + if (this->Moc.Enabled && this->Uic.Enabled) { + tools = "AUTOMOC and AUTOUIC"; + property = "SKIP_AUTOGEN"; + } else if (this->Moc.Enabled) { + tools = "AUTOMOC"; + property = "SKIP_AUTOMOC"; + } else if (this->Uic.Enabled) { + tools = "AUTOUIC"; + property = "SKIP_AUTOUIC"; + } + msg += "For compatibility, CMake is excluding the GENERATED source " + "file(s):\n"; + for (const std::string& absFile : + this->AutogenTarget.HeadersGenerated) { + msg.append(" ").append(Quoted(absFile)).append("\n"); + } + for (const std::string& absFile : + this->AutogenTarget.SourcesGenerated) { + msg.append(" ").append(Quoted(absFile)).append("\n"); + } + msg += "from processing by "; + msg += tools; + msg += + ". If any of the files should be processed, set CMP0071 to NEW. " + "If any of the files should not be processed, " + "explicitly exclude them by setting the source file property "; + msg += property; + msg += ":\n set_property(SOURCE file.h PROPERTY "; + msg += property; + msg += " ON)\n"; + makefile->IssueMessage(cmake::AUTHOR_WARNING, msg); } - msg += "from processing by "; - msg += tools; - msg += - ". If any of the files should be processed, set CMP0071 to NEW. " - "If any of the files should not be processed, " - "explicitly exclude them by setting the source file property "; - msg += property; - msg += ":\n set_property(SOURCE file.h PROPERTY "; - msg += property; - msg += " ON)\n"; - makefile->IssueMessage(cmake::AUTHOR_WARNING, msg); } } - // Clear lists - generatedSources.clear(); - generatedHeaders.clear(); - } - // Sort headers and sources - if (this->MocEnabled || this->UicEnabled) { - std::sort(this->Headers.begin(), this->Headers.end()); - std::sort(this->Sources.begin(), this->Sources.end()); + // Sort headers and sources + if (this->Moc.Enabled || this->Uic.Enabled) { + std::sort(this->AutogenTarget.Headers.begin(), + this->AutogenTarget.Headers.end()); + std::sort(this->AutogenTarget.Sources.begin(), + this->AutogenTarget.Sources.end()); + } } // Process qrc files - if (!this->Qrcs.empty()) { - const bool QtV5 = (this->QtVersionMajor == "5"); + if (!this->Rcc.Qrcs.empty()) { + const bool QtV5 = (this->QtVersion.Major == 5); // Target rcc options std::vector<std::string> optionsTarget; cmSystemTools::ExpandListArgument( - GetSafeProperty(this->Target, "AUTORCC_OPTIONS"), optionsTarget); + this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget); // Check if file name is unique - for (Qrc& qrc : this->Qrcs) { + for (Qrc& qrc : this->Rcc.Qrcs) { qrc.Unique = true; - for (Qrc const& qrc2 : this->Qrcs) { + for (Qrc const& qrc2 : this->Rcc.Qrcs) { if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) { qrc.Unique = false; break; @@ -570,11 +742,11 @@ void cmQtAutoGenInitializer::InitCustomTargets() // Path checksum and file names { cmFilePathChecksum const fpathCheckSum(makefile); - for (Qrc& qrc : this->Qrcs) { + for (Qrc& qrc : this->Rcc.Qrcs) { qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile); // RCC output file name { - std::string rccFile = this->DirBuild + "/"; + std::string rccFile = this->Dir.Build + "/"; rccFile += qrc.PathChecksum; rccFile += "/qrc_"; rccFile += qrc.QrcName; @@ -582,7 +754,7 @@ void cmQtAutoGenInitializer::InitCustomTargets() qrc.RccFile = std::move(rccFile); } { - std::string base = this->DirInfo; + std::string base = this->Dir.Info; base += "/RCC"; base += qrc.QrcName; if (!qrc.Unique) { @@ -597,11 +769,18 @@ void cmQtAutoGenInitializer::InitCustomTargets() qrc.SettingsFile = base; qrc.SettingsFile += "Settings.txt"; + + if (this->MultiConfig) { + for (std::string const& cfg : this->ConfigsList) { + qrc.ConfigSettingsFile[cfg] = + AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg); + } + } } } } // RCC options - for (Qrc& qrc : this->Qrcs) { + for (Qrc& qrc : this->Rcc.Qrcs) { // Target options std::vector<std::string> opts = optionsTarget; // Merge computed "-name XYZ" option @@ -622,313 +801,410 @@ void cmQtAutoGenInitializer::InitCustomTargets() RccMergeOptions(opts, qrc.Options, QtV5); qrc.Options = std::move(opts); } - for (Qrc& qrc : this->Qrcs) { - // Register file at target - this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC); - - std::vector<std::string> ccOutput; - ccOutput.push_back(qrc.RccFile); - - cmCustomCommandLines commandLines; - if (this->MultiConfig) { - // Build for all configurations - for (std::string const& config : this->ConfigsList) { - cmCustomCommandLine currentLine; - currentLine.push_back(cmSystemTools::GetCMakeCommand()); - currentLine.push_back("-E"); - currentLine.push_back("cmake_autorcc"); - currentLine.push_back(qrc.InfoFile); - currentLine.push_back(config); - commandLines.push_back(std::move(currentLine)); + // RCC resources + for (Qrc& qrc : this->Rcc.Qrcs) { + if (!qrc.Generated) { + std::string error; + if (!RccListInputs(qrc.QrcFile, qrc.Resources, error)) { + cmSystemTools::Error(error.c_str()); + return false; } - } else { - cmCustomCommandLine currentLine; - currentLine.push_back(cmSystemTools::GetCMakeCommand()); - currentLine.push_back("-E"); - currentLine.push_back("cmake_autorcc"); - currentLine.push_back(qrc.InfoFile); - currentLine.push_back("$<CONFIG>"); - commandLines.push_back(std::move(currentLine)); } - std::string ccComment = "Automatic RCC for "; - ccComment += FileProjectRelativePath(makefile, qrc.QrcFile); + } + } - if (qrc.Generated) { - // Create custom rcc target - std::string ccName; - { - ccName = this->Target->GetName(); - ccName += "_arcc_"; - ccName += qrc.QrcName; - if (!qrc.Unique) { - ccName += "_"; - ccName += qrc.PathChecksum; - } - std::vector<std::string> ccDepends; - // Add the .qrc and info file to the custom target dependencies - ccDepends.push_back(qrc.QrcFile); - ccDepends.push_back(qrc.InfoFile); - - cmTarget* autoRccTarget = makefile->AddUtilityCommand( - ccName, cmMakefile::TargetOrigin::Generator, true, - this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false, - ccComment.c_str()); - // Create autogen generator target - localGen->AddGeneratorTarget( - new cmGeneratorTarget(autoRccTarget, localGen)); - - // Set FOLDER property in autogen target - if (!this->AutogenFolder.empty()) { - autoRccTarget->SetProperty("FOLDER", this->AutogenFolder.c_str()); - } - } - // Add autogen target to the origin target dependencies - this->Target->Target->AddUtility(ccName, makefile); - } else { - // Create custom rcc command - { - std::vector<std::string> ccByproducts; - std::vector<std::string> ccDepends; - // Add the .qrc and info file to the custom command dependencies - ccDepends.push_back(qrc.QrcFile); - ccDepends.push_back(qrc.InfoFile); + return true; +} - // Add the resource files to the dependencies - { - std::string error; - if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) { - for (std::string const& fileName : qrc.Resources) { - // Add resource file to the custom command dependencies - ccDepends.push_back(fileName); - } - } else { - cmSystemTools::Error(error.c_str()); - } - } - makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends, - /*main_dependency*/ std::string(), - commandLines, ccComment.c_str(), - this->DirWork.c_str()); - } - // Reconfigure when .qrc file changes - makefile->AddCMakeDependFile(qrc.QrcFile); +bool cmQtAutoGenInitializer::InitAutogenTarget() +{ + cmMakefile* makefile = this->Target->Target->GetMakefile(); + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator(); + + // Register info file as generated by CMake + makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile); + + // Files provided by the autogen target + std::vector<std::string> autogenProvides; + if (this->Moc.Enabled) { + this->AddGeneratedSource(this->Moc.MocsCompilation, GeneratorT::MOC); + autogenProvides.push_back(this->Moc.MocsCompilation); + } + + // Compose target comment + std::string autogenComment; + { + std::string tools; + if (this->Moc.Enabled) { + tools += "MOC"; + } + if (this->Uic.Enabled) { + if (!tools.empty()) { + tools += " and "; } + tools += "UIC"; } + autogenComment = "Automatic "; + autogenComment += tools; + autogenComment += " for target "; + autogenComment += this->Target->GetName(); } - // Create _autogen target - if (this->MocEnabled || this->UicEnabled) { - // Add user defined autogen target dependencies - { - std::string const deps = - GetSafeProperty(this->Target, "AUTOGEN_TARGET_DEPENDS"); - if (!deps.empty()) { - std::vector<std::string> extraDeps; - cmSystemTools::ExpandListArgument(deps, extraDeps); - for (std::string const& depName : extraDeps) { - // Allow target and file dependencies - auto* depTarget = makefile->FindTargetToUse(depName); - if (depTarget != nullptr) { - autogenDependTargets.insert(depTarget); - } else { - autogenDependFiles.insert(depName); - } - } - } + // Compose command lines + cmCustomCommandLines commandLines; + { + cmCustomCommandLine currentLine; + currentLine.push_back(cmSystemTools::GetCMakeCommand()); + currentLine.push_back("-E"); + currentLine.push_back("cmake_autogen"); + currentLine.push_back(this->AutogenTarget.InfoFile); + currentLine.push_back("$<CONFIGURATION>"); + commandLines.push_back(std::move(currentLine)); + } + + // Use PRE_BUILD on demand + bool usePRE_BUILD = false; + if (globalGen->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; + } + // Disable PRE_BUILD in some cases + if (usePRE_BUILD) { + // Cannot use PRE_BUILD with file depends + if (!this->AutogenTarget.DependFiles.empty()) { + usePRE_BUILD = false; + } + } + // Create the autogen target/command + if (usePRE_BUILD) { + // Add additional autogen target dependencies to origin target + for (cmTarget* depTarget : this->AutogenTarget.DependTargets) { + this->Target->Target->AddUtility(depTarget->GetName(), makefile); } - // Compose target comment - std::string autogenComment; + // 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. + // + // PRE_BUILD does not support file dependencies! + const std::vector<std::string> no_output; + const std::vector<std::string> no_deps; + cmCustomCommand cc(makefile, no_output, autogenProvides, no_deps, + commandLines, autogenComment.c_str(), + this->Dir.Work.c_str()); + cc.SetEscapeOldStyle(false); + cc.SetEscapeAllowMakeVars(true); + this->Target->Target->AddPreBuildCommand(cc); + } else { + + // Add link library target dependencies to the autogen target + // dependencies { - std::string tools; - if (this->MocEnabled) { - tools += "MOC"; + // add_dependencies/addUtility do not support generator expressions. + // We depend only on the libraries found in all configs therefore. + std::map<cmGeneratorTarget const*, std::size_t> commonTargets; + for (std::string const& config : this->ConfigsList) { + cmLinkImplementationLibraries const* libs = + this->Target->GetLinkImplementationLibraries(config); + if (libs != nullptr) { + for (cmLinkItem const& item : libs->Libraries) { + cmGeneratorTarget const* libTarget = item.Target; + if ((libTarget != nullptr) && + !StaticLibraryCycle(this->Target, libTarget, config)) { + // Increment target config count + commonTargets[libTarget]++; + } + } + } } - if (this->UicEnabled) { - if (!tools.empty()) { - tools += " and "; + for (auto const& item : commonTargets) { + if (item.second == this->ConfigsList.size()) { + this->AutogenTarget.DependTargets.insert(item.first->Target); } - tools += "UIC"; } - autogenComment = "Automatic "; - autogenComment += tools; - autogenComment += " for target "; - autogenComment += this->Target->GetName(); } - // Compose command lines + // Create autogen target + cmTarget* autogenTarget = makefile->AddUtilityCommand( + this->AutogenTarget.Name, cmMakefile::TargetOrigin::Generator, true, + this->Dir.Work.c_str(), /*byproducts=*/autogenProvides, + std::vector<std::string>(this->AutogenTarget.DependFiles.begin(), + this->AutogenTarget.DependFiles.end()), + commandLines, false, autogenComment.c_str()); + // Create autogen generator target + localGen->AddGeneratorTarget( + new cmGeneratorTarget(autogenTarget, localGen)); + + // Forward origin utilities to autogen target + for (std::string const& depName : this->Target->Target->GetUtilities()) { + autogenTarget->AddUtility(depName, makefile); + } + // Add additional autogen target dependencies to autogen target + for (cmTarget* depTarget : this->AutogenTarget.DependTargets) { + autogenTarget->AddUtility(depTarget->GetName(), makefile); + } + + // Set FOLDER property in autogen target + if (!this->TargetsFolder.empty()) { + autogenTarget->SetProperty("FOLDER", this->TargetsFolder.c_str()); + } + + // Add autogen target to the origin target dependencies + this->Target->Target->AddUtility(this->AutogenTarget.Name, makefile); + } + + return true; +} + +bool cmQtAutoGenInitializer::InitRccTargets() +{ + cmMakefile* makefile = this->Target->Target->GetMakefile(); + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + + for (Qrc const& qrc : this->Rcc.Qrcs) { + // Register info file as generated by CMake + makefile->AddCMakeOutputFile(qrc.InfoFile); + // Register file at target + this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC); + + std::vector<std::string> ccOutput; + ccOutput.push_back(qrc.RccFile); + cmCustomCommandLines commandLines; - { + if (this->MultiConfig) { + // Build for all configurations + for (std::string const& config : this->ConfigsList) { + cmCustomCommandLine currentLine; + currentLine.push_back(cmSystemTools::GetCMakeCommand()); + currentLine.push_back("-E"); + currentLine.push_back("cmake_autorcc"); + currentLine.push_back(qrc.InfoFile); + currentLine.push_back(config); + commandLines.push_back(std::move(currentLine)); + } + } else { cmCustomCommandLine currentLine; currentLine.push_back(cmSystemTools::GetCMakeCommand()); currentLine.push_back("-E"); - currentLine.push_back("cmake_autogen"); - currentLine.push_back(this->AutogenInfoFile); - currentLine.push_back("$<CONFIGURATION>"); + currentLine.push_back("cmake_autorcc"); + currentLine.push_back(qrc.InfoFile); + currentLine.push_back("$<CONFIG>"); commandLines.push_back(std::move(currentLine)); } + std::string ccComment = "Automatic RCC for "; + ccComment += FileProjectRelativePath(makefile, qrc.QrcFile); - // Use PRE_BUILD on demand - bool usePRE_BUILD = false; - if (globalGen->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; - } - // Disable PRE_BUILD in some cases - if (usePRE_BUILD) { - // Cannot use PRE_BUILD with file depends - if (!autogenDependFiles.empty()) { - usePRE_BUILD = false; - } - } - // Create the autogen target/command - if (usePRE_BUILD) { - // Add additional autogen target dependencies to origin target - for (cmTarget* depTarget : autogenDependTargets) { - this->Target->Target->AddUtility(depTarget->GetName(), makefile); - } - - // 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. - // - // PRE_BUILD does not support file dependencies! - const std::vector<std::string> no_output; - const std::vector<std::string> no_deps; - cmCustomCommand cc(makefile, no_output, autogenProvides, no_deps, - commandLines, autogenComment.c_str(), - this->DirWork.c_str()); - cc.SetEscapeOldStyle(false); - cc.SetEscapeAllowMakeVars(true); - this->Target->Target->AddPreBuildCommand(cc); - } else { - - // Add link library target dependencies to the autogen target - // dependencies + if (qrc.Generated) { + // Create custom rcc target + std::string ccName; { - // add_dependencies/addUtility do not support generator expressions. - // We depend only on the libraries found in all configs therefore. - std::map<cmGeneratorTarget const*, std::size_t> commonTargets; - for (std::string const& config : this->ConfigsList) { - cmLinkImplementationLibraries const* libs = - this->Target->GetLinkImplementationLibraries(config); - if (libs != nullptr) { - for (cmLinkItem const& item : libs->Libraries) { - cmGeneratorTarget const* libTarget = item.Target; - if ((libTarget != nullptr) && - !StaticLibraryCycle(this->Target, libTarget, config)) { - // Increment target config count - commonTargets[libTarget]++; - } - } - } + ccName = this->Target->GetName(); + ccName += "_arcc_"; + ccName += qrc.QrcName; + if (!qrc.Unique) { + ccName += "_"; + ccName += qrc.PathChecksum; } - for (auto const& item : commonTargets) { - if (item.second == this->ConfigsList.size()) { - autogenDependTargets.insert(item.first->Target); - } + std::vector<std::string> ccDepends; + // Add the .qrc and info file to the custom target dependencies + ccDepends.push_back(qrc.QrcFile); + ccDepends.push_back(qrc.InfoFile); + + cmTarget* autoRccTarget = makefile->AddUtilityCommand( + ccName, cmMakefile::TargetOrigin::Generator, true, + this->Dir.Work.c_str(), ccOutput, ccDepends, commandLines, false, + ccComment.c_str()); + // Create autogen generator target + localGen->AddGeneratorTarget( + new cmGeneratorTarget(autoRccTarget, localGen)); + + // Set FOLDER property in autogen target + if (!this->TargetsFolder.empty()) { + autoRccTarget->SetProperty("FOLDER", this->TargetsFolder.c_str()); } } - - // Create autogen target - cmTarget* autogenTarget = makefile->AddUtilityCommand( - this->AutogenTargetName, cmMakefile::TargetOrigin::Generator, true, - this->DirWork.c_str(), /*byproducts=*/autogenProvides, - std::vector<std::string>(autogenDependFiles.begin(), - autogenDependFiles.end()), - commandLines, false, autogenComment.c_str()); - // Create autogen generator target - localGen->AddGeneratorTarget( - new cmGeneratorTarget(autogenTarget, localGen)); - - // Forward origin utilities to autogen target - for (std::string const& depName : this->Target->Target->GetUtilities()) { - autogenTarget->AddUtility(depName, makefile); - } - // Add additional autogen target dependencies to autogen target - for (cmTarget* depTarget : autogenDependTargets) { - autogenTarget->AddUtility(depTarget->GetName(), makefile); - } - - // Set FOLDER property in autogen target - if (!this->AutogenFolder.empty()) { - autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str()); - } - // Add autogen target to the origin target dependencies - this->Target->Target->AddUtility(this->AutogenTargetName, makefile); + this->Target->Target->AddUtility(ccName, makefile); + } else { + // Create custom rcc command + { + std::vector<std::string> ccByproducts; + std::vector<std::string> ccDepends; + // Add the .qrc and info file to the custom command dependencies + ccDepends.push_back(qrc.QrcFile); + ccDepends.push_back(qrc.InfoFile); + + // Add the resource files to the dependencies + for (std::string const& fileName : qrc.Resources) { + // Add resource file to the custom command dependencies + ccDepends.push_back(fileName); + } + makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends, + /*main_dependency*/ std::string(), + commandLines, ccComment.c_str(), + this->Dir.Work.c_str()); + } + // Reconfigure when .qrc file changes + makefile->AddCMakeDependFile(qrc.QrcFile); } } + + return true; } -void cmQtAutoGenInitializer::SetupCustomTargets() +bool cmQtAutoGenInitializer::SetupCustomTargets() { - cmMakefile* makefile = this->Target->Target->GetMakefile(); - // Create info directory on demand - if (!cmSystemTools::MakeDirectory(this->DirInfo)) { - std::string emsg = ("Could not create directory: "); - emsg += Quoted(this->DirInfo); + if (!cmSystemTools::MakeDirectory(this->Dir.Info)) { + std::string emsg = ("AutoGen: Could not create directory: "); + emsg += Quoted(this->Dir.Info); cmSystemTools::Error(emsg.c_str()); - } - - // Configuration include directories - std::string includeDir = "include"; - std::map<std::string, std::string> includeDirs; - for (std::string const& cfg : this->ConfigsList) { - std::string& dir = includeDirs[cfg]; - dir = "include_"; - dir += cfg; + return false; } // Generate autogen target info file - if (this->MocEnabled || this->UicEnabled) { - if (this->MocEnabled) { - this->SetupCustomTargetsMoc(); + if (this->Moc.Enabled || this->Uic.Enabled) { + // Write autogen target info files + if (!this->SetupWriteAutogenInfo()) { + return false; } - if (this->UicEnabled) { - this->SetupCustomTargetsUic(); + } + + // Write AUTORCC info files + if (this->Rcc.Enabled && !this->SetupWriteRccInfo()) { + return false; + } + + return true; +} + +bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() +{ + cmMakefile* makefile = this->Target->Target->GetMakefile(); + + cmGeneratedFileStream ofs; + ofs.SetCopyIfDifferent(true); + ofs.Open(this->AutogenTarget.InfoFile, false, true); + if (ofs) { + // Utility lambdas + auto CWrite = [&ofs](const char* key, std::string const& value) { + ofs << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value) + << ")\n"; + }; + auto CWriteUInt = [&ofs](const char* key, unsigned int value) { + ofs << "set(" << key << " " << value << ")\n"; + }; + auto CWriteList = [&CWrite](const char* key, + std::vector<std::string> const& list) { + CWrite(key, cmJoin(list, ";")); + }; + auto CWriteNestedLists = + [&CWrite](const char* key, + std::vector<std::vector<std::string>> const& lists) { + std::vector<std::string> seplist; + for (const std::vector<std::string>& list : lists) { + std::string blist = "{"; + blist += cmJoin(list, ";"); + blist += "}"; + seplist.push_back(std::move(blist)); + } + CWrite(key, cmJoin(seplist, cmQtAutoGen::ListSep)); + }; + auto CWriteSet = [&CWrite](const char* key, + std::set<std::string> const& list) { + CWrite(key, cmJoin(list, ";")); + }; + auto CWriteMap = [&ofs](const char* key, + std::map<std::string, std::string> const& map) { + for (auto const& item : map) { + ofs << "set(" << key << "_" << item.first << " " + << cmOutputConverter::EscapeForCMake(item.second) << ")\n"; + } + }; + auto MfDef = [makefile](const char* key) { + return makefile->GetSafeDefinition(key); + }; + + // Write + ofs << "# Meta\n"; + CWrite("AM_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); + CWrite("AM_PARALLEL", this->AutogenTarget.Parallel); + CWrite("AM_VERBOSITY", this->Verbosity); + + ofs << "# Directories\n"; + CWrite("AM_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); + CWrite("AM_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); + CWrite("AM_CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR")); + CWrite("AM_CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR")); + CWrite("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE", + MfDef("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")); + CWrite("AM_BUILD_DIR", this->Dir.Build); + CWrite("AM_INCLUDE_DIR", this->Dir.Include); + CWriteMap("AM_INCLUDE_DIR", this->Dir.ConfigInclude); + + ofs << "# Files\n"; + CWriteList("AM_SOURCES", this->AutogenTarget.Sources); + CWriteList("AM_HEADERS", this->AutogenTarget.Headers); + CWrite("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile); + CWriteMap("AM_SETTINGS_FILE", this->AutogenTarget.ConfigSettingsFile); + + ofs << "# Qt\n"; + CWriteUInt("AM_QT_VERSION_MAJOR", this->QtVersion.Major); + CWrite("AM_QT_MOC_EXECUTABLE", this->Moc.Executable); + CWrite("AM_QT_UIC_EXECUTABLE", this->Uic.Executable); + + if (this->Moc.Enabled) { + ofs << "# MOC settings\n"; + CWriteSet("AM_MOC_SKIP", this->Moc.Skip); + CWrite("AM_MOC_DEFINITIONS", this->Moc.Defines); + CWriteMap("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines); + CWrite("AM_MOC_INCLUDES", this->Moc.Includes); + CWriteMap("AM_MOC_INCLUDES", this->Moc.ConfigIncludes); + CWrite("AM_MOC_OPTIONS", + this->Target->GetSafeProperty("AUTOMOC_MOC_OPTIONS")); + CWrite("AM_MOC_RELAXED_MODE", MfDef("CMAKE_AUTOMOC_RELAXED_MODE")); + CWrite("AM_MOC_MACRO_NAMES", + this->Target->GetSafeProperty("AUTOMOC_MACRO_NAMES")); + CWrite("AM_MOC_DEPEND_FILTERS", + this->Target->GetSafeProperty("AUTOMOC_DEPEND_FILTERS")); + CWrite("AM_MOC_PREDEFS_CMD", this->Moc.PredefsCmd); } - // Parallel processing - this->Parallel = GetSafeProperty(this->Target, "AUTOGEN_PARALLEL"); - if (this->Parallel.empty() || (this->Parallel == "AUTO")) { - // Autodetect number of CPUs - this->Parallel = std::to_string(GetParallelCPUCount()); + if (this->Uic.Enabled) { + ofs << "# UIC settings\n"; + CWriteSet("AM_UIC_SKIP", this->Uic.Skip); + CWrite("AM_UIC_TARGET_OPTIONS", this->Uic.Options); + CWriteMap("AM_UIC_TARGET_OPTIONS", this->Uic.ConfigOptions); + CWriteList("AM_UIC_OPTIONS_FILES", this->Uic.FileFiles); + CWriteNestedLists("AM_UIC_OPTIONS_OPTIONS", this->Uic.FileOptions); + CWriteList("AM_UIC_SEARCH_PATHS", this->Uic.SearchPaths); } + } else { + std::string err = "AutoGen: Could not write file "; + err += this->AutogenTarget.InfoFile; + cmSystemTools::Error(err.c_str()); + return false; + } + + return true; +} +bool cmQtAutoGenInitializer::SetupWriteRccInfo() +{ + for (Qrc const& qrc : this->Rcc.Qrcs) { cmGeneratedFileStream ofs; ofs.SetCopyIfDifferent(true); - ofs.Open(this->AutogenInfoFile.c_str(), false, true); + ofs.Open(qrc.InfoFile, false, true); if (ofs) { // Utility lambdas auto CWrite = [&ofs](const char* key, std::string const& value) { ofs << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value) << ")\n"; }; - auto CWriteList = [&CWrite](const char* key, - std::vector<std::string> const& list) { - CWrite(key, cmJoin(list, ";")); - }; - auto CWriteNestedLists = - [&CWrite](const char* key, - std::vector<std::vector<std::string>> const& lists) { - std::vector<std::string> seplist; - for (const std::vector<std::string>& list : lists) { - std::string blist = "{"; - blist += cmJoin(list, ";"); - blist += "}"; - seplist.push_back(std::move(blist)); - } - CWrite(key, cmJoin(seplist, cmQtAutoGen::ListSep)); - }; - auto CWriteSet = [&CWrite](const char* key, - std::set<std::string> const& list) { - CWrite(key, cmJoin(list, ";")); - }; auto CWriteMap = [&ofs](const char* key, std::map<std::string, std::string> const& map) { for (auto const& item : map) { @@ -936,401 +1212,306 @@ void cmQtAutoGenInitializer::SetupCustomTargets() << cmOutputConverter::EscapeForCMake(item.second) << ")\n"; } }; - auto MfDef = [makefile](const char* key) { - return std::string(makefile->GetSafeDefinition(key)); - }; // Write - ofs << "# Meta\n"; - CWrite("AM_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); - CWrite("AM_PARALLEL", this->Parallel); + ofs << "# Configurations\n"; + CWrite("ARCC_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); + CWrite("ARCC_VERBOSITY", this->Verbosity); + ofs << "# Settings file\n"; + CWrite("ARCC_SETTINGS_FILE", qrc.SettingsFile); + CWriteMap("ARCC_SETTINGS_FILE", qrc.ConfigSettingsFile); ofs << "# Directories\n"; - CWrite("AM_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); - CWrite("AM_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); - CWrite("AM_CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR")); - CWrite("AM_CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR")); - CWrite("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE", - MfDef("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")); - CWrite("AM_BUILD_DIR", this->DirBuild); - if (this->MultiConfig) { - CWriteMap("AM_INCLUDE_DIR", includeDirs); - } else { - CWrite("AM_INCLUDE_DIR", includeDir); - } - - ofs << "# Files\n"; - CWriteList("AM_SOURCES", this->Sources); - CWriteList("AM_HEADERS", this->Headers); - if (this->MultiConfig) { - std::map<std::string, std::string> settingsFiles; - for (std::string const& cfg : this->ConfigsList) { - settingsFiles[cfg] = - AppendFilenameSuffix(this->AutogenSettingsFile, "_" + cfg); - } - CWriteMap("AM_SETTINGS_FILE", settingsFiles); - } else { - CWrite("AM_SETTINGS_FILE", this->AutogenSettingsFile); - } - - ofs << "# Qt\n"; - CWrite("AM_QT_VERSION_MAJOR", this->QtVersionMajor); - CWrite("AM_QT_MOC_EXECUTABLE", this->MocExecutable); - CWrite("AM_QT_UIC_EXECUTABLE", this->UicExecutable); - - if (this->MocEnabled) { - ofs << "# MOC settings\n"; - CWriteSet("AM_MOC_SKIP", this->MocSkip); - CWrite("AM_MOC_DEFINITIONS", this->MocDefines); - CWriteMap("AM_MOC_DEFINITIONS", this->MocDefinesConfig); - CWrite("AM_MOC_INCLUDES", this->MocIncludes); - CWriteMap("AM_MOC_INCLUDES", this->MocIncludesConfig); - CWrite("AM_MOC_OPTIONS", - GetSafeProperty(this->Target, "AUTOMOC_MOC_OPTIONS")); - CWrite("AM_MOC_RELAXED_MODE", MfDef("CMAKE_AUTOMOC_RELAXED_MODE")); - CWrite("AM_MOC_MACRO_NAMES", - GetSafeProperty(this->Target, "AUTOMOC_MACRO_NAMES")); - CWrite("AM_MOC_DEPEND_FILTERS", - GetSafeProperty(this->Target, "AUTOMOC_DEPEND_FILTERS")); - CWrite("AM_MOC_PREDEFS_CMD", this->MocPredefsCmd); - } - - if (this->UicEnabled) { - ofs << "# UIC settings\n"; - CWriteSet("AM_UIC_SKIP", this->UicSkip); - CWrite("AM_UIC_TARGET_OPTIONS", this->UicOptions); - CWriteMap("AM_UIC_TARGET_OPTIONS", this->UicOptionsConfig); - CWriteList("AM_UIC_OPTIONS_FILES", this->UicFileFiles); - CWriteNestedLists("AM_UIC_OPTIONS_OPTIONS", this->UicFileOptions); - CWriteList("AM_UIC_SEARCH_PATHS", this->UicSearchPaths); - } + CWrite("ARCC_BUILD_DIR", this->Dir.Build); + CWrite("ARCC_INCLUDE_DIR", this->Dir.Include); + CWriteMap("ARCC_INCLUDE_DIR", this->Dir.ConfigInclude); + + ofs << "# Rcc executable\n"; + CWrite("ARCC_RCC_EXECUTABLE", this->Rcc.Executable); + CWrite("ARCC_RCC_LIST_OPTIONS", cmJoin(this->Rcc.ListOptions, ";")); + + ofs << "# Rcc job\n"; + CWrite("ARCC_LOCK_FILE", qrc.LockFile); + CWrite("ARCC_SOURCE", qrc.QrcFile); + CWrite("ARCC_OUTPUT_CHECKSUM", qrc.PathChecksum); + CWrite("ARCC_OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.RccFile)); + CWrite("ARCC_OPTIONS", cmJoin(qrc.Options, ";")); + CWrite("ARCC_INPUTS", cmJoin(qrc.Resources, ";")); } else { - return; + std::string err = "AutoRcc: Could not write file "; + err += qrc.InfoFile; + cmSystemTools::Error(err.c_str()); + return false; } } - // Generate auto RCC info files - if (this->RccEnabled) { - for (Qrc const& qrc : this->Qrcs) { - // Register rcc info file as generated - makefile->AddCMakeOutputFile(qrc.InfoFile); - - cmGeneratedFileStream ofs; - ofs.SetCopyIfDifferent(true); - ofs.Open(qrc.InfoFile.c_str(), false, true); - if (ofs) { - // Utility lambdas - auto CWrite = [&ofs](const char* key, std::string const& value) { - ofs << "set(" << key << " " - << cmOutputConverter::EscapeForCMake(value) << ")\n"; - }; - auto CWriteMap = - [&ofs](const char* key, - std::map<std::string, std::string> const& map) { - for (auto const& item : map) { - ofs << "set(" << key << "_" << item.first << " " - << cmOutputConverter::EscapeForCMake(item.second) << ")\n"; - } - }; - - // Write - ofs << "# Configurations\n"; - CWrite("ARCC_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); - - ofs << "# Settings file\n"; - if (this->MultiConfig) { - std::map<std::string, std::string> settingsFiles; - for (std::string const& cfg : this->ConfigsList) { - settingsFiles[cfg] = - AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg); - } - CWriteMap("ARCC_SETTINGS_FILE", settingsFiles); - } else { - CWrite("ARCC_SETTINGS_FILE", qrc.SettingsFile); - } - - ofs << "# Directories\n"; - CWrite("ARCC_BUILD_DIR", this->DirBuild); - if (this->MultiConfig) { - CWriteMap("ARCC_INCLUDE_DIR", includeDirs); - } else { - CWrite("ARCC_INCLUDE_DIR", includeDir); - } + return true; +} - ofs << "# Rcc executable\n"; - CWrite("ARCC_RCC_EXECUTABLE", this->RccExecutable); - CWrite("ARCC_RCC_LIST_OPTIONS", cmJoin(this->RccListOptions, ";")); - - ofs << "# Rcc job\n"; - CWrite("ARCC_LOCK_FILE", qrc.LockFile); - CWrite("ARCC_SOURCE", qrc.QrcFile); - CWrite("ARCC_OUTPUT_CHECKSUM", qrc.PathChecksum); - CWrite("ARCC_OUTPUT_NAME", - cmSystemTools::GetFilenameName(qrc.RccFile)); - CWrite("ARCC_OPTIONS", cmJoin(qrc.Options, ";")); - CWrite("ARCC_INPUTS", cmJoin(qrc.Resources, ";")); - } else { - return; - } - } +void cmQtAutoGenInitializer::AddGeneratedSource(std::string const& filename, + GeneratorT genType) +{ + // Register source file in makefile + cmMakefile* makefile = this->Target->Target->GetMakefile(); + { + cmSourceFile* gFile = makefile->GetOrCreateSource(filename, true); + gFile->SetProperty("GENERATED", "1"); + gFile->SetProperty("SKIP_AUTOGEN", "On"); } + + // Add source file to source group + AddToSourceGroup(makefile, filename, genType); + + // Add source file to target + this->Target->AddSource(filename); } -void cmQtAutoGenInitializer::SetupCustomTargetsMoc() +cmQtAutoGenInitializer::IntegerVersion cmQtAutoGenInitializer::GetQtVersion( + cmGeneratorTarget const* target) { - cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); - cmMakefile* makefile = this->Target->Target->GetMakefile(); + cmQtAutoGenInitializer::IntegerVersion res; + cmMakefile* makefile = target->Target->GetMakefile(); - // Moc predefs command - if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") && - this->QtVersionGreaterOrEqual(5, 8)) { - this->MocPredefsCmd = - makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"); + // -- Major version + std::string qtMajor = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); + if (qtMajor.empty()) { + qtMajor = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); } - - // Moc includes and compile definitions { - auto GetIncludeDirs = [this, - localGen](std::string const& cfg) -> std::string { - // Get the include dirs for this target, without stripping the implicit - // include dirs off, see - // https://gitlab.kitware.com/cmake/cmake/issues/13667 - std::vector<std::string> includeDirs; - localGen->GetIncludeDirectories(includeDirs, this->Target, "CXX", cfg, - false); - return cmJoin(includeDirs, ";"); - }; - auto GetCompileDefinitions = - [this, localGen](std::string const& cfg) -> std::string { - std::set<std::string> defines; - localGen->AddCompileDefinitions(defines, this->Target, cfg, "CXX"); - return cmJoin(defines, ";"); - }; - - // Default configuration settings - this->MocIncludes = GetIncludeDirs(this->ConfigDefault); - this->MocDefines = GetCompileDefinitions(this->ConfigDefault); - // Other configuration settings - for (std::string const& cfg : this->ConfigsList) { - { - std::string const configIncludeDirs = GetIncludeDirs(cfg); - if (configIncludeDirs != this->MocIncludes) { - this->MocIncludesConfig[cfg] = configIncludeDirs; - } - } - { - std::string const configCompileDefs = GetCompileDefinitions(cfg); - if (configCompileDefs != this->MocDefines) { - this->MocDefinesConfig[cfg] = configCompileDefs; - } - } + const char* targetQtVersion = + target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", ""); + if (targetQtVersion != nullptr) { + qtMajor = targetQtVersion; } } - // Moc executable - { - std::string mocExec; - std::string err; - - if (this->QtVersionMajor == "5") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::moc"); - if (tgt != nullptr) { - mocExec = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTOMOC: Qt5::moc target not found"; - } - } else if (this->QtVersionMajor == "4") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::moc"); - if (tgt != nullptr) { - mocExec = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTOMOC: Qt4::moc target not found"; + // -- Minor version + std::string qtMinor; + if (!qtMajor.empty()) { + if (qtMajor == "5") { + qtMinor = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR"); + } + if (qtMinor.empty()) { + qtMinor = makefile->GetSafeDefinition("QT_VERSION_MINOR"); + } + { + const char* targetQtVersion = + target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", + ""); + if (targetQtVersion != nullptr) { + qtMinor = targetQtVersion; } - } else { - err = "The AUTOMOC feature supports only Qt 4 and Qt 5"; } + } - if (err.empty()) { - this->MocExecutable = mocExec; - } else { - err += " ("; - err += this->Target->GetName(); - err += ")"; - cmSystemTools::Error(err.c_str()); + // -- Convert to integer + if (!qtMajor.empty() && !qtMinor.empty()) { + unsigned long majorUL(0); + unsigned long minorUL(0); + if (cmSystemTools::StringToULong(qtMajor.c_str(), &majorUL) && + cmSystemTools::StringToULong(qtMinor.c_str(), &minorUL)) { + res.Major = static_cast<unsigned int>(majorUL); + res.Minor = static_cast<unsigned int>(minorUL); } } + + return res; } -void cmQtAutoGenInitializer::SetupCustomTargetsUic() +bool cmQtAutoGenInitializer::GetMocExecutable() { - cmMakefile* makefile = this->Target->Target->GetMakefile(); + std::string err; - // Uic search paths + // Find moc executable { - std::string const usp = - GetSafeProperty(this->Target, "AUTOUIC_SEARCH_PATHS"); - if (!usp.empty()) { - cmSystemTools::ExpandListArgument(usp, this->UicSearchPaths); - std::string const srcDir = makefile->GetCurrentSourceDirectory(); - for (std::string& path : this->UicSearchPaths) { - path = cmSystemTools::CollapseFullPath(path, srcDir); + std::string targetName; + if (this->QtVersion.Major == 5) { + targetName = "Qt5::moc"; + } else if (this->QtVersion.Major == 4) { + targetName = "Qt4::moc"; + } else { + err = "The AUTOMOC feature supports only Qt 4 and Qt 5"; + } + if (!targetName.empty()) { + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse(targetName); + if (tgt != nullptr) { + this->Moc.Executable = tgt->ImportedGetLocation(""); + } else { + err = "Could not find target " + targetName; } } } - // Uic target options - { - auto UicGetOpts = [this](std::string const& cfg) -> std::string { - std::vector<std::string> opts; - this->Target->GetAutoUicOptions(opts, cfg); - return cmJoin(opts, ";"); - }; - // Default settings - this->UicOptions = UicGetOpts(this->ConfigDefault); - - // Configuration specific settings - for (std::string const& cfg : this->ConfigsList) { - std::string const configUicOpts = UicGetOpts(cfg); - if (configUicOpts != this->UicOptions) { - this->UicOptionsConfig[cfg] = configUicOpts; + // Test moc command + if (err.empty()) { + if (cmSystemTools::FileExists(this->Moc.Executable, true)) { + std::vector<std::string> command; + command.push_back(this->Moc.Executable); + command.push_back("-h"); + std::string stdOut; + std::string stdErr; + int retVal = 0; + bool result = cmSystemTools::RunSingleCommand( + command, &stdOut, &stdErr, &retVal, nullptr, + cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto); + if (!result) { + err = "The moc test command failed: "; + err += QuotedCommand(command); } + } else { + err = "The moc executable "; + err += Quoted(this->Moc.Executable); + err += " does not exist"; } } - // .ui files skip and options - { - std::string const uiExt = "ui"; - std::string pathError; - for (cmSourceFile* sf : makefile->GetSourceFiles()) { - // sf->GetExtension() is only valid after sf->GetFullPath() ... - // Since we're iterating over source files that might be not in the - // target we need to check for path errors (not existing files). - std::string const& fPath = sf->GetFullPath(&pathError); - if (!pathError.empty()) { - pathError.clear(); - continue; - } - if (sf->GetExtension() == uiExt) { - std::string const absFile = cmSystemTools::GetRealPath(fPath); - // Check if the .ui file should be skipped - if (sf->GetPropertyAsBool("SKIP_AUTOUIC") || - sf->GetPropertyAsBool("SKIP_AUTOGEN")) { - this->UicSkip.insert(absFile); - } - // Check if the .ui file has uic options - std::string const uicOpts = GetSafeProperty(sf, "AUTOUIC_OPTIONS"); - if (!uicOpts.empty()) { - // Check if file isn't skipped - if (this->UicSkip.count(absFile) == 0) { - this->UicFileFiles.push_back(absFile); - std::vector<std::string> optsVec; - cmSystemTools::ExpandListArgument(uicOpts, optsVec); - this->UicFileOptions.push_back(std::move(optsVec)); - } - } - } - } + + // Print error + if (!err.empty()) { + std::string msg = "AutoMoc ("; + msg += this->Target->GetName(); + msg += "): "; + msg += err; + cmSystemTools::Error(msg.c_str()); + return false; } - // Uic executable - { - std::string err; - std::string uicExec; + return true; +} - cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); - if (this->QtVersionMajor == "5") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::uic"); - if (tgt != nullptr) { - uicExec = SafeString(tgt->ImportedGetLocation("")); - } else { - // Project does not use Qt5Widgets, but has AUTOUIC ON anyway - } - } else if (this->QtVersionMajor == "4") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::uic"); +bool cmQtAutoGenInitializer::GetUicExecutable() +{ + std::string err; + + // Find uic executable + { + std::string targetName; + if (this->QtVersion.Major == 5) { + targetName = "Qt5::uic"; + } else if (this->QtVersion.Major == 4) { + targetName = "Qt4::uic"; + } else { + err = "The AUTOUIC feature supports only Qt 4 and Qt 5"; + } + if (!targetName.empty()) { + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse(targetName); if (tgt != nullptr) { - uicExec = SafeString(tgt->ImportedGetLocation("")); + this->Uic.Executable = tgt->ImportedGetLocation(""); } else { - err = "AUTOUIC: Qt4::uic target not found"; + if (this->QtVersion.Major == 5) { + // Project does not use Qt5Widgets, but has AUTOUIC ON anyway + } else { + err = "Could not find target " + targetName; + } } - } else { - err = "The AUTOUIC feature supports only Qt 4 and Qt 5"; } + } - if (err.empty()) { - this->UicExecutable = uicExec; + // Test uic command + if (err.empty()) { + if (cmSystemTools::FileExists(this->Uic.Executable, true)) { + std::vector<std::string> command; + command.push_back(this->Uic.Executable); + command.push_back("-h"); + std::string stdOut; + std::string stdErr; + int retVal = 0; + bool result = cmSystemTools::RunSingleCommand( + command, &stdOut, &stdErr, &retVal, nullptr, + cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto); + if (!result) { + err = "The uic test command failed: "; + err += QuotedCommand(command); + } } else { - err += " ("; - err += this->Target->GetName(); - err += ")"; - cmSystemTools::Error(err.c_str()); + err = "The uic executable "; + err += Quoted(this->Uic.Executable); + err += " does not exist"; } } -} -void cmQtAutoGenInitializer::AddGeneratedSource(std::string const& filename, - GeneratorT genType) -{ - // Register source file in makefile - cmMakefile* makefile = this->Target->Target->GetMakefile(); - { - cmSourceFile* gFile = makefile->GetOrCreateSource(filename, true); - gFile->SetProperty("GENERATED", "1"); - gFile->SetProperty("SKIP_AUTOGEN", "On"); + // Print error + if (!err.empty()) { + std::string msg = "AutoUic ("; + msg += this->Target->GetName(); + msg += "): "; + msg += err; + cmSystemTools::Error(msg.c_str()); + return false; } - // Add source file to source group - AddToSourceGroup(makefile, filename, genType); - - // Add source file to target - this->Target->AddSource(filename); + return true; } -std::string cmQtAutoGenInitializer::GetQtMajorVersion( - cmGeneratorTarget const* target) +bool cmQtAutoGenInitializer::GetRccExecutable() { - cmMakefile* makefile = target->Target->GetMakefile(); - std::string qtMajor = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); - if (qtMajor.empty()) { - qtMajor = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); - } - const char* targetQtVersion = - target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", ""); - if (targetQtVersion != nullptr) { - qtMajor = targetQtVersion; - } - return qtMajor; -} + std::string err; -std::string cmQtAutoGenInitializer::GetQtMinorVersion( - cmGeneratorTarget const* target, std::string const& qtVersionMajor) -{ - cmMakefile* makefile = target->Target->GetMakefile(); - std::string qtMinor; - if (qtVersionMajor == "5") { - qtMinor = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR"); - } - if (qtMinor.empty()) { - qtMinor = makefile->GetSafeDefinition("QT_VERSION_MINOR"); + // Find rcc executable + { + std::string targetName; + if (this->QtVersion.Major == 5) { + targetName = "Qt5::rcc"; + } else if (this->QtVersion.Major == 4) { + targetName = "Qt4::rcc"; + } else { + err = "The AUTORCC feature supports only Qt 4 and Qt 5"; + } + if (!targetName.empty()) { + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse(targetName); + if (tgt != nullptr) { + this->Rcc.Executable = tgt->ImportedGetLocation(""); + } else { + err = "Could not find target " + targetName; + } + } } - const char* targetQtVersion = - target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", ""); - if (targetQtVersion != nullptr) { - qtMinor = targetQtVersion; + // Test rcc command + if (err.empty()) { + if (cmSystemTools::FileExists(this->Rcc.Executable, true)) { + std::vector<std::string> command; + command.push_back(this->Rcc.Executable); + command.push_back("-h"); + std::string stdOut; + std::string stdErr; + int retVal = 0; + bool result = cmSystemTools::RunSingleCommand( + command, &stdOut, &stdErr, &retVal, nullptr, + cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto); + if (result) { + // Detect if rcc supports (-)-list + if (this->QtVersion.Major == 5) { + if (stdOut.find("--list") != std::string::npos) { + this->Rcc.ListOptions.push_back("--list"); + } else { + this->Rcc.ListOptions.push_back("-list"); + } + } + } else { + err = "The rcc test command failed: "; + err += QuotedCommand(command); + } + } else { + err = "The rcc executable "; + err += Quoted(this->Rcc.Executable); + err += " does not exist"; + } } - return qtMinor; -} -bool cmQtAutoGenInitializer::QtVersionGreaterOrEqual( - unsigned long requestMajor, unsigned long requestMinor) const -{ - unsigned long majorUL(0); - unsigned long minorUL(0); - if (cmSystemTools::StringToULong(this->QtVersionMajor.c_str(), &majorUL) && - cmSystemTools::StringToULong(this->QtVersionMinor.c_str(), &minorUL)) { - return (majorUL > requestMajor) || - (majorUL == requestMajor && minorUL >= requestMinor); + // Print error + if (!err.empty()) { + std::string msg = "AutoRcc ("; + msg += this->Target->GetName(); + msg += "): "; + msg += err; + cmSystemTools::Error(msg.c_str()); + return false; } - return false; + + return true; } /// @brief Reads the resource files list from from a .qrc file @@ -1346,9 +1527,9 @@ bool cmQtAutoGenInitializer::RccListInputs(std::string const& fileName, error += "\n"; return false; } - if (!RccListOptions.empty()) { + if (!this->Rcc.ListOptions.empty()) { // Use rcc for file listing - if (RccExecutable.empty()) { + if (this->Rcc.Executable.empty()) { error = "rcc executable not available"; return false; } @@ -1367,8 +1548,9 @@ bool cmQtAutoGenInitializer::RccListInputs(std::string const& fileName, std::string rccStdErr; { std::vector<std::string> cmd; - cmd.push_back(RccExecutable); - cmd.insert(cmd.end(), RccListOptions.begin(), RccListOptions.end()); + cmd.push_back(this->Rcc.Executable); + cmd.insert(cmd.end(), this->Rcc.ListOptions.begin(), + this->Rcc.ListOptions.end()); cmd.push_back(fileNameName); result = cmSystemTools::RunSingleCommand( cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(), |