/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGenInitializer.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenGlobalInitializer.h" #include "cmAlgorithms.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" #include "cmDuration.h" #include "cmFilePathChecksum.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLinkItem.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmProcessOutput.h" #include "cmSourceFile.h" #include "cmSourceGroup.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmake.h" #include "cmsys/FStream.hxx" #include "cmsys/SystemInformation.hxx" #include #include #include #include #include #include #include #include #include #include std::string GetQtExecutableTargetName( const cmQtAutoGen::IntegerVersion& qtVersion, std::string const& executable) { if (qtVersion.Major == 6) { return ("Qt6::" + executable); } if (qtVersion.Major == 5) { return ("Qt5::" + executable); } if (qtVersion.Major == 4) { return ("Qt4::" + executable); } return (""); } static std::size_t GetParallelCPUCount() { static std::size_t count = 0; // Detect only on the first call if (count == 0) { cmsys::SystemInformation info; info.RunCPUCheck(); count = info.GetNumberOfPhysicalCPU(); count = std::max(count, 1); count = std::min(count, cmQtAutoGen::ParallelMax); } return count; } static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName, cmQtAutoGen::GeneratorT genType) { cmSourceGroup* sourceGroup = nullptr; // Acquire source group { std::string property; std::string groupName; { std::array props; // Use generator specific group name switch (genType) { case cmQtAutoGen::GeneratorT::MOC: props[0] = "AUTOMOC_SOURCE_GROUP"; break; case cmQtAutoGen::GeneratorT::RCC: props[0] = "AUTORCC_SOURCE_GROUP"; break; default: props[0] = "AUTOGEN_SOURCE_GROUP"; break; } props[1] = "AUTOGEN_SOURCE_GROUP"; for (std::string& prop : props) { const char* propName = makefile->GetState()->GetGlobalProperty(prop); if ((propName != nullptr) && (*propName != '\0')) { groupName = propName; property = std::move(prop); break; } } } // Generate a source group on demand if (!groupName.empty()) { sourceGroup = makefile->GetOrCreateSourceGroup(groupName); if (sourceGroup == nullptr) { std::ostringstream ost; ost << cmQtAutoGen::GeneratorNameUpper(genType); ost << ": " << property; ost << ": Could not find or create the source group "; ost << cmQtAutoGen::Quoted(groupName); cmSystemTools::Error(ost.str().c_str()); return false; } } } if (sourceGroup != nullptr) { sourceGroup->AddGroupFile(fileName); } return true; } static void AddCleanFile(cmMakefile* makefile, std::string const& fileName) { makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(), false); } static std::string FileProjectRelativePath(cmMakefile* makefile, std::string const& fileName) { std::string res; { std::string pSource = cmSystemTools::RelativePath( makefile->GetCurrentSourceDirectory(), fileName); std::string pBinary = cmSystemTools::RelativePath( makefile->GetCurrentBinaryDirectory(), fileName); if (pSource.size() < pBinary.size()) { res = std::move(pSource); } else if (pBinary.size() < fileName.size()) { res = std::move(pBinary); } else { res = fileName; } } return res; } /* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its * recursive STATIC_LIBRARY dependencies depends on targetOrigin * (STATIC_LIBRARY cycle). */ static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin, cmGeneratorTarget const* targetDepend, std::string const& config) { bool cycle = false; if ((targetOrigin->GetType() == cmStateEnums::STATIC_LIBRARY) && (targetDepend->GetType() == cmStateEnums::STATIC_LIBRARY)) { std::set knownLibs; std::deque testLibs; // Insert initial static_library dependency knownLibs.insert(targetDepend); testLibs.push_back(targetDepend); while (!testLibs.empty()) { cmGeneratorTarget const* testTarget = testLibs.front(); testLibs.pop_front(); // Check if the test target is the origin target (cycle) if (testTarget == targetOrigin) { cycle = true; break; } // Collect all static_library dependencies from the test target cmLinkImplementationLibraries const* libs = testTarget->GetLinkImplementationLibraries(config); if (libs != nullptr) { for (cmLinkItem const& item : libs->Libraries) { cmGeneratorTarget const* depTarget = item.Target; if ((depTarget != nullptr) && (depTarget->GetType() == cmStateEnums::STATIC_LIBRARY) && knownLibs.insert(depTarget).second) { testLibs.push_back(depTarget); } } } } } return cycle; } cmQtAutoGenInitializer::InfoWriter::InfoWriter(std::string const& filename) { Ofs_.SetCopyIfDifferent(true); Ofs_.Open(filename, false, true); } template std::string cmQtAutoGenInitializer::InfoWriter::ListJoin(IT it_begin, IT it_end) { std::string res; for (IT it = it_begin; it != it_end; ++it) { if (it != it_begin) { res += ';'; } for (const char* c = it->c_str(); *c; ++c) { if (*c == '"') { // Escape the double quote to avoid ending the argument. res += "\\\""; } else if (*c == '$') { // Escape the dollar to avoid expanding variables. res += "\\$"; } else if (*c == '\\') { // Escape the backslash to avoid other escapes. res += "\\\\"; } else if (*c == ';') { // Escape the semicolon to avoid list expansion. res += "\\;"; } else { // Other characters will be parsed correctly. res += *c; } } } return res; } std::string cmQtAutoGenInitializer::InfoWriter::ConfigKey( const char* key, std::string const& config) { std::string ckey = key; ckey += '_'; ckey += config; return ckey; } void cmQtAutoGenInitializer::InfoWriter::Write(const char* key, std::string const& value) { Ofs_ << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value) << ")\n"; }; void cmQtAutoGenInitializer::InfoWriter::WriteUInt(const char* key, unsigned int value) { Ofs_ << "set(" << key << " " << value << ")\n"; }; template void cmQtAutoGenInitializer::InfoWriter::WriteStrings(const char* key, C const& container) { Ofs_ << "set(" << key << " \"" << ListJoin(container.begin(), container.end()) << "\")\n"; } void cmQtAutoGenInitializer::InfoWriter::WriteConfig( const char* key, std::map const& map) { for (auto const& item : map) { Write(ConfigKey(key, item.first).c_str(), item.second); } }; template void cmQtAutoGenInitializer::InfoWriter::WriteConfigStrings( const char* key, std::map const& map) { for (auto const& item : map) { WriteStrings(ConfigKey(key, item.first).c_str(), item.second); } } void cmQtAutoGenInitializer::InfoWriter::WriteNestedLists( const char* key, std::vector> const& lists) { std::vector seplist; for (const std::vector& list : lists) { std::string blist = "{"; blist += ListJoin(list.begin(), list.end()); blist += "}"; seplist.push_back(std::move(blist)); } Write(key, cmJoin(seplist, cmQtAutoGen::ListSep)); }; cmQtAutoGenInitializer::cmQtAutoGenInitializer( cmQtAutoGenGlobalInitializer* globalInitializer, cmGeneratorTarget* target, IntegerVersion const& qtVersion, bool mocEnabled, bool uicEnabled, bool rccEnabled, bool globalAutogenTarget, bool globalAutoRccTarget) : GlobalInitializer(globalInitializer) , Target(target) , QtVersion(qtVersion) { AutogenTarget.GlobalTarget = globalAutogenTarget; Moc.Enabled = mocEnabled; Uic.Enabled = uicEnabled; Rcc.Enabled = rccEnabled; Rcc.GlobalTarget = globalAutoRccTarget; } bool cmQtAutoGenInitializer::InitCustomTargets() { cmMakefile* makefile = this->Target->Target->GetMakefile(); cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator(); // Configurations this->MultiConfig = globalGen->IsMultiConfig(); this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList); if (this->ConfigsList.empty()) { this->ConfigsList.push_back(this->ConfigDefault); } // 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"; } } // Targets FOLDER { const char* folder = makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); if (folder == nullptr) { folder = makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER"); } // Inherit FOLDER property from target (#13688) if (folder == nullptr) { folder = this->Target->GetProperty("FOLDER"); } if (folder != nullptr) { this->TargetsFolder = folder; } } // Common directories { // Collapsed current binary directory std::string const cbd = cmSystemTools::CollapseFullPath( std::string(), makefile->GetCurrentBinaryDirectory()); // Info directory this->Dir.Info = cbd; this->Dir.Info += cmake::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 += "_$"; } // Per config include directories if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { std::string& dir = this->Dir.ConfigInclude[cfg]; dir = this->Dir.Build; dir += "/include_"; dir += cfg; } } } // 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) { if (InitUic()) { auto* uicTarget = makefile->FindTargetToUse( GetQtExecutableTargetName(this->QtVersion, "uic")); if (uicTarget != nullptr) { this->AutogenTarget.DependTargets.insert(uicTarget); } } else { 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 { this->AutogenTarget.DependOrigin = this->Target->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS"); auto* mocTarget = makefile->FindTargetToUse( GetQtExecutableTargetName(this->QtVersion, "moc")); if (mocTarget != nullptr) { this->AutogenTarget.DependTargets.insert(mocTarget); } std::string const deps = this->Target->GetSafeProperty("AUTOGEN_TARGET_DEPENDS"); if (!deps.empty()) { std::vector 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); } } } } } // 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 { // We need to disable this until we have all implicit includes available. // See issue #18669. // bool const appendImplicit = (this->QtVersion.Major >= 5); auto GetIncludeDirs = [this, localGen](std::string const& cfg) -> std::vector { bool const appendImplicit = false; // 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 dirs; localGen->GetIncludeDirectories(dirs, this->Target, "CXX", cfg, false, appendImplicit); return dirs; }; // Default configuration include directories this->Moc.Includes = GetIncludeDirs(this->ConfigDefault); // Other configuration settings if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { std::vector dirs = GetIncludeDirs(cfg); if (dirs != this->Moc.Includes) { this->Moc.ConfigIncludes[cfg] = std::move(dirs); } } } } // Moc compile definitions { auto GetCompileDefinitions = [this, localGen](std::string const& cfg) -> std::set { std::set defines; localGen->GetTargetDefines(this->Target, cfg, "CXX", defines); #ifdef _WIN32 if (this->Moc.PredefsCmd.empty()) { // Add WIN32 definition if we don't have a moc_predefs.h defines.insert("WIN32"); } #endif return defines; }; // Default configuration defines this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault); // Other configuration defines if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { std::set defines = GetCompileDefinitions(cfg); if (defines != this->Moc.Defines) { this->Moc.ConfigDefines[cfg] = std::move(defines); } } } } // Moc executable return GetMocExecutable(); } 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); } } } // Uic target options { auto UicGetOpts = [this](std::string const& cfg) -> std::vector { std::vector opts; this->Target->GetAutoUicOptions(opts, cfg); return opts; }; // Default settings this->Uic.Options = UicGetOpts(this->ConfigDefault); // Configuration specific settings if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { std::vector 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 optsVec; cmSystemTools::ExpandListArgument(uicOpts, optsVec); this->Uic.FileOptions.push_back(std::move(optsVec)); } } } } } // Uic executable return GetUicExecutable(); } bool cmQtAutoGenInitializer::InitRcc() { return GetRccExecutable(); } bool cmQtAutoGenInitializer::InitScanFiles() { cmMakefile* makefile = this->Target->Target->GetMakefile(); // Scan through target files { std::string const qrcExt = "qrc"; std::vector srcFiles; this->Target->GetConfigCommonSourceFiles(srcFiles); for (cmSourceFile* sf : srcFiles) { if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) { continue; } // sf->GetExtension() is only valid after sf->GetFullPath() ... 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->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->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) { this->AutogenTarget.HeadersGenerated.push_back(absPath); } else { this->AutogenTarget.Headers.push_back(absPath); } } else { if (generated) { this->AutogenTarget.SourcesGenerated.push_back(absPath); } else { this->AutogenTarget.Sources.push_back(absPath); } } } } } // Register rcc enabled files if (this->Rcc.Enabled && (ext == qrcExt) && !sf->GetPropertyAsBool("SKIP_AUTORCC")) { // Register qrc file { Qrc qrc; qrc.QrcFile = cmSystemTools::GetRealPath(fPath); qrc.QrcName = cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile); qrc.Generated = sf->GetPropertyAsBool("GENERATED"); // RCC options { std::string const opts = sf->GetSafeProperty("AUTORCC_OPTIONS"); if (!opts.empty()) { cmSystemTools::ExpandListArgument(opts, qrc.Options); } } 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(); 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; } 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 (!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; } if (policyAccept) { // Accept GENERATED sources for (std::string const& absFile : this->AutogenTarget.HeadersGenerated) { this->AutogenTarget.Headers.push_back(absFile); this->AutogenTarget.DependFiles.insert(absFile); } for (std::string const& absFile : this->AutogenTarget.SourcesGenerated) { this->AutogenTarget.Sources.push_back(absFile); this->AutogenTarget.DependFiles.insert(absFile); } } 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); } } } // 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->Rcc.Qrcs.empty()) { const bool modernQt = (this->QtVersion.Major >= 5); // Target rcc options std::vector optionsTarget; cmSystemTools::ExpandListArgument( this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget); // Check if file name is unique for (Qrc& qrc : this->Rcc.Qrcs) { qrc.Unique = true; for (Qrc const& qrc2 : this->Rcc.Qrcs) { if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) { qrc.Unique = false; break; } } } // Path checksum and file names { cmFilePathChecksum const fpathCheckSum(makefile); for (Qrc& qrc : this->Rcc.Qrcs) { qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile); // RCC output file name { std::string rccFile = this->Dir.Build + "/"; rccFile += qrc.PathChecksum; rccFile += "/qrc_"; rccFile += qrc.QrcName; rccFile += ".cpp"; qrc.RccFile = std::move(rccFile); } { std::string base = this->Dir.Info; base += "/RCC"; base += qrc.QrcName; if (!qrc.Unique) { base += qrc.PathChecksum; } qrc.LockFile = base; qrc.LockFile += ".lock"; qrc.InfoFile = base; qrc.InfoFile += "Info.cmake"; 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->Rcc.Qrcs) { // Target options std::vector opts = optionsTarget; // Merge computed "-name XYZ" option { std::string name = qrc.QrcName; // Replace '-' with '_'. The former is not valid for symbol names. std::replace(name.begin(), name.end(), '-', '_'); if (!qrc.Unique) { name += "_"; name += qrc.PathChecksum; } std::vector nameOpts; nameOpts.emplace_back("-name"); nameOpts.emplace_back(std::move(name)); RccMergeOptions(opts, nameOpts, modernQt); } // Merge file option RccMergeOptions(opts, qrc.Options, modernQt); qrc.Options = std::move(opts); } // 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; } } } } return true; } 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 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(); } // 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("$"); 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; } // Cannot use PRE_BUILD when a global autogen target is in place if (AutogenTarget.GlobalTarget) { 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); } // 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 no_output; const std::vector 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 if (this->AutogenTarget.DependOrigin) { // add_dependencies/addUtility do not support generator expressions. // We depend only on the libraries found in all configs therefore. std::map 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]++; } } } } for (auto const& item : commonTargets) { if (item.second == this->ConfigsList.size()) { this->AutogenTarget.DependTargets.insert(item.first->Target); } } } // Create autogen target cmTarget* autogenTarget = makefile->AddUtilityCommand( this->AutogenTarget.Name, cmMakefile::TargetOrigin::Generator, true, this->Dir.Work.c_str(), /*byproducts=*/autogenProvides, std::vector(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 if (this->AutogenTarget.DependOrigin) { for (BT const& depName : this->Target->GetUtilities()) { autogenTarget->AddUtility(depName.Value, 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); // Add autogen target to the global autogen target dependencies if (this->AutogenTarget.GlobalTarget) { this->GlobalInitializer->AddToGlobalAutoGen(localGen, this->AutogenTarget.Name); } } return true; } bool cmQtAutoGenInitializer::InitRccTargets() { cmMakefile* makefile = this->Target->Target->GetMakefile(); cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); auto rccTargetName = GetQtExecutableTargetName(this->QtVersion, "rcc"); 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 ccOutput; ccOutput.push_back(qrc.RccFile); std::vector ccDepends; // Add the .qrc and info file to the custom command dependencies ccDepends.push_back(qrc.QrcFile); ccDepends.push_back(qrc.InfoFile); 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_autorcc"); currentLine.push_back(qrc.InfoFile); currentLine.push_back("$"); commandLines.push_back(std::move(currentLine)); } std::string ccComment = "Automatic RCC for "; ccComment += FileProjectRelativePath(makefile, qrc.QrcFile); if (qrc.Generated || this->Rcc.GlobalTarget) { // Create custom rcc target std::string ccName; { ccName = this->Target->GetName(); ccName += "_arcc_"; ccName += qrc.QrcName; if (!qrc.Unique) { ccName += "_"; ccName += qrc.PathChecksum; } 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()); } if (!rccTargetName.empty()) { autoRccTarget->AddUtility(rccTargetName, makefile); } } // Add autogen target to the origin target dependencies this->Target->Target->AddUtility(ccName, makefile); // Add autogen target to the global autogen target dependencies if (this->Rcc.GlobalTarget) { this->GlobalInitializer->AddToGlobalAutoRcc(localGen, ccName); } } else { // Create custom rcc command { std::vector ccByproducts; // 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); } if (!rccTargetName.empty()) { ccDepends.push_back(rccTargetName); } 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; } bool cmQtAutoGenInitializer::SetupCustomTargets() { // Create info directory on demand if (!cmSystemTools::MakeDirectory(this->Dir.Info)) { std::string emsg = ("AutoGen: Could not create directory: "); emsg += Quoted(this->Dir.Info); cmSystemTools::Error(emsg.c_str()); return false; } // Generate autogen target info file if (this->Moc.Enabled || this->Uic.Enabled) { // Write autogen target info files if (!this->SetupWriteAutogenInfo()) { return false; } } // Write AUTORCC info files return !this->Rcc.Enabled || this->SetupWriteRccInfo(); } bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() { InfoWriter ofs(this->AutogenTarget.InfoFile); if (ofs) { // Utility lambdas cmMakefile* makefile = this->Target->Target->GetMakefile(); auto MfDef = [makefile](const char* key) { return makefile->GetSafeDefinition(key); }; // Write common settings ofs.Write("# Meta\n"); ofs.Write("AM_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); ofs.Write("AM_PARALLEL", this->AutogenTarget.Parallel); ofs.Write("AM_VERBOSITY", this->Verbosity); ofs.Write("# Directories\n"); ofs.Write("AM_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); ofs.Write("AM_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); ofs.Write("AM_CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR")); ofs.Write("AM_CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR")); ofs.Write("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE", MfDef("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")); ofs.Write("AM_BUILD_DIR", this->Dir.Build); ofs.Write("AM_INCLUDE_DIR", this->Dir.Include); ofs.WriteConfig("AM_INCLUDE_DIR", this->Dir.ConfigInclude); ofs.Write("# Files\n"); ofs.WriteStrings("AM_SOURCES", this->AutogenTarget.Sources); ofs.WriteStrings("AM_HEADERS", this->AutogenTarget.Headers); ofs.Write("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile); ofs.WriteConfig("AM_SETTINGS_FILE", this->AutogenTarget.ConfigSettingsFile); ofs.Write("# Qt\n"); ofs.WriteUInt("AM_QT_VERSION_MAJOR", this->QtVersion.Major); ofs.Write("AM_QT_MOC_EXECUTABLE", this->Moc.Executable); ofs.Write("AM_QT_UIC_EXECUTABLE", this->Uic.Executable); // Write moc settings if (this->Moc.Enabled) { ofs.Write("# MOC settings\n"); ofs.WriteStrings("AM_MOC_SKIP", this->Moc.Skip); ofs.WriteStrings("AM_MOC_DEFINITIONS", this->Moc.Defines); ofs.WriteConfigStrings("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines); ofs.WriteStrings("AM_MOC_INCLUDES", this->Moc.Includes); ofs.WriteConfigStrings("AM_MOC_INCLUDES", this->Moc.ConfigIncludes); ofs.Write("AM_MOC_OPTIONS", this->Target->GetSafeProperty("AUTOMOC_MOC_OPTIONS")); ofs.Write("AM_MOC_RELAXED_MODE", MfDef("CMAKE_AUTOMOC_RELAXED_MODE")); ofs.Write("AM_MOC_MACRO_NAMES", this->Target->GetSafeProperty("AUTOMOC_MACRO_NAMES")); ofs.Write("AM_MOC_DEPEND_FILTERS", this->Target->GetSafeProperty("AUTOMOC_DEPEND_FILTERS")); ofs.Write("AM_MOC_PREDEFS_CMD", this->Moc.PredefsCmd); } // Write uic settings if (this->Uic.Enabled) { ofs.Write("# UIC settings\n"); ofs.WriteStrings("AM_UIC_SKIP", this->Uic.Skip); ofs.WriteStrings("AM_UIC_TARGET_OPTIONS", this->Uic.Options); ofs.WriteConfigStrings("AM_UIC_TARGET_OPTIONS", this->Uic.ConfigOptions); ofs.WriteStrings("AM_UIC_OPTIONS_FILES", this->Uic.FileFiles); ofs.WriteNestedLists("AM_UIC_OPTIONS_OPTIONS", this->Uic.FileOptions); ofs.WriteStrings("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) { InfoWriter ofs(qrc.InfoFile); if (ofs) { // Write ofs.Write("# Configurations\n"); ofs.Write("ARCC_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); ofs.Write("ARCC_VERBOSITY", this->Verbosity); ofs.Write("# Settings file\n"); ofs.Write("ARCC_SETTINGS_FILE", qrc.SettingsFile); ofs.WriteConfig("ARCC_SETTINGS_FILE", qrc.ConfigSettingsFile); ofs.Write("# Directories\n"); ofs.Write("ARCC_BUILD_DIR", this->Dir.Build); ofs.Write("ARCC_INCLUDE_DIR", this->Dir.Include); ofs.WriteConfig("ARCC_INCLUDE_DIR", this->Dir.ConfigInclude); ofs.Write("# Rcc executable\n"); ofs.Write("ARCC_RCC_EXECUTABLE", this->Rcc.Executable); ofs.WriteStrings("ARCC_RCC_LIST_OPTIONS", this->Rcc.ListOptions); ofs.Write("# Rcc job\n"); ofs.Write("ARCC_LOCK_FILE", qrc.LockFile); ofs.Write("ARCC_SOURCE", qrc.QrcFile); ofs.Write("ARCC_OUTPUT_CHECKSUM", qrc.PathChecksum); ofs.Write("ARCC_OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.RccFile)); ofs.WriteStrings("ARCC_OPTIONS", qrc.Options); ofs.WriteStrings("ARCC_INPUTS", qrc.Resources); } else { std::string err = "AutoRcc: Could not write file "; err += qrc.InfoFile; cmSystemTools::Error(err.c_str()); return false; } } return true; } 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); } static unsigned int CharPtrToInt(const char* const input) { unsigned long tmp = 0; if (input != nullptr && cmSystemTools::StringToULong(input, &tmp)) { return static_cast(tmp); } return 0; } static unsigned int StringToInt(const std::string& input) { return input.empty() ? 0 : CharPtrToInt(input.c_str()); } static std::vector GetKnownQtVersions( cmGeneratorTarget const* target) { cmMakefile* makefile = target->Target->GetMakefile(); std::vector result; for (const std::string& prefix : std::vector({ "Qt6Core", "Qt5Core", "QT" })) { auto tmp = cmQtAutoGenInitializer::IntegerVersion( StringToInt(makefile->GetSafeDefinition(prefix + "_VERSION_MAJOR")), StringToInt(makefile->GetSafeDefinition(prefix + "_VERSION_MINOR"))); if (tmp.Major != 0) { result.push_back(tmp); } } return result; } cmQtAutoGenInitializer::IntegerVersion cmQtAutoGenInitializer::GetQtVersion( cmGeneratorTarget const* target) { auto knownQtVersions = GetKnownQtVersions(target); if (knownQtVersions.empty()) { return cmQtAutoGenInitializer::IntegerVersion(); // No Qt } // Pick a version from the known versions: auto targetVersion = CharPtrToInt( target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", "")); if (targetVersion == 0) { // No specific version was requested by the target: // Use highest known Qt version. return knownQtVersions.at(0); } for (auto it : knownQtVersions) { if (it.Major == targetVersion) { return it; } } // Requested version was not found return cmQtAutoGenInitializer::IntegerVersion(); } std::pair GetQtExecutable( const cmQtAutoGen::IntegerVersion& qtVersion, cmGeneratorTarget* target, const std::string& executable, bool ignoreMissingTarget, std::string* output) { const std::string upperExecutable = cmSystemTools::UpperCase(executable); std::string result = target->Target->GetSafeProperty("AUTO" + upperExecutable + "_EXECUTABLE"); if (!result.empty()) { cmListFileBacktrace lfbt = target->Target->GetMakefile()->GetBacktrace(); cmGeneratorExpression ge(lfbt); std::unique_ptr cge = ge.Parse(result); result = cge->Evaluate(target->GetLocalGenerator(), ""); return std::make_pair(true, result); } std::string err; // Find executable { const std::string targetName = GetQtExecutableTargetName(qtVersion, executable); if (targetName.empty()) { err = "The AUTO" + upperExecutable + " feature "; err += "supports only Qt 4, Qt 5 and Qt 6."; } else { cmLocalGenerator* localGen = target->GetLocalGenerator(); cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse(targetName); if (tgt != nullptr) { if (tgt->IsImported()) { result = tgt->ImportedGetLocation(""); } else { result = tgt->GetLocation(""); } } else { if (ignoreMissingTarget) { return std::make_pair(true, ""); } err = "Could not find target " + targetName; } } } // Test executable if (err.empty()) { if (cmSystemTools::FileExists(result, true)) { std::vector command; command.push_back(result); command.push_back("-h"); std::string stdOut; std::string stdErr; int retVal = 0; const bool runResult = cmSystemTools::RunSingleCommand( command, &stdOut, &stdErr, &retVal, nullptr, cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto); if (!runResult) { err = "Test of \"" + executable + "\" binary "; err += cmQtAutoGen::Quoted(result) + " failed: "; err += cmQtAutoGen::QuotedCommand(command); } else { if (output != nullptr) { *output = stdOut; } } } else { err = "The \"" + executable + "\" binary "; err += cmQtAutoGen::Quoted(result); err += " does not exist"; } } // Print error if (!err.empty()) { std::string msg = "AutoGen ("; msg += target->GetName(); msg += "): "; msg += err; cmSystemTools::Error(msg.c_str()); return std::make_pair(false, ""); } return std::make_pair(true, result); } bool cmQtAutoGenInitializer::GetMocExecutable() { const auto result = GetQtExecutable(this->QtVersion, this->Target, "moc", false, nullptr); this->Moc.Executable = result.second; return result.first; } bool cmQtAutoGenInitializer::GetUicExecutable() { const auto result = GetQtExecutable(this->QtVersion, this->Target, "uic", true, nullptr); this->Uic.Executable = result.second; return result.first; } bool cmQtAutoGenInitializer::GetRccExecutable() { std::string stdOut; const auto result = GetQtExecutable(this->QtVersion, this->Target, "rcc", false, &stdOut); this->Rcc.Executable = result.second; if (!result.first) { return false; } if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) { if (stdOut.find("--list") != std::string::npos) { this->Rcc.ListOptions.push_back("--list"); } else { this->Rcc.ListOptions.push_back("-list"); } } return true; } /// @brief Reads the resource files list from from a .qrc file /// @arg fileName Must be the absolute path of the .qrc file /// @return True if the rcc file was successfully read bool cmQtAutoGenInitializer::RccListInputs(std::string const& fileName, std::vector& files, std::string& error) { if (!cmSystemTools::FileExists(fileName)) { error = "rcc resource file does not exist:\n "; error += Quoted(fileName); error += "\n"; return false; } if (!this->Rcc.ListOptions.empty()) { // Use rcc for file listing if (this->Rcc.Executable.empty()) { error = "rcc executable not available"; return false; } // Run rcc list command in the directory of the qrc file with the // pathless // qrc file name argument. This way rcc prints relative paths. // This avoids issues on Windows when the qrc file is in a path that // contains non-ASCII characters. std::string const fileDir = cmSystemTools::GetFilenamePath(fileName); std::string const fileNameName = cmSystemTools::GetFilenameName(fileName); bool result = false; int retVal = 0; std::string rccStdOut; std::string rccStdErr; { std::vector cmd; 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(), cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto); } if (!result || retVal) { error = "rcc list process failed for:\n "; error += Quoted(fileName); error += "\n"; error += rccStdOut; error += "\n"; error += rccStdErr; error += "\n"; return false; } if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) { return false; } } else { // We can't use rcc for the file listing. // Read the qrc file content into string and parse it. { std::string qrcContents; { cmsys::ifstream ifs(fileName.c_str()); if (ifs) { std::ostringstream osst; osst << ifs.rdbuf(); qrcContents = osst.str(); } else { error = "rcc file not readable:\n "; error += Quoted(fileName); error += "\n"; return false; } } // Parse string content RccListParseContent(qrcContents, files); } } // Convert relative paths to absolute paths RccListConvertFullPath(cmSystemTools::GetFilenamePath(fileName), files); return true; }