From a87f82e0258148f3047a3487c832a569dd841e09 Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Thu, 16 Nov 2017 15:17:14 +0100 Subject: Autogen: Switch to use custom commands for RCC Instead of processing all `rcc` invocation requests in the _autogen target that calls `cmake -E cmake_autogen ...` once, use a dedicated custom command that calls `cmake -E cmake_autorcc ...` for each `.qrc` file. This allows parallel `.qrc` file processing and reduces the workload (and complexity) in the _autogen target. If only `AUTORCC` is enabled, the _autogen target won't be created at all since it is now used for `AUTOMOC` and `AUTOUIC` only. For `.qrc` files that are GENERATED a custom target is used instead of a custom command. Closes #17161 --- Modules/AutoRccInfo.cmake.in | 11 + Source/cmQtAutoGenDigest.h | 2 + Source/cmQtAutoGeneratorInitializer.cxx | 527 +++++++++++++++++++------------- 3 files changed, 331 insertions(+), 209 deletions(-) create mode 100644 Modules/AutoRccInfo.cmake.in diff --git a/Modules/AutoRccInfo.cmake.in b/Modules/AutoRccInfo.cmake.in new file mode 100644 index 0000000..7b13b9e --- /dev/null +++ b/Modules/AutoRccInfo.cmake.in @@ -0,0 +1,11 @@ +# Meta +set(ARCC_MULTI_CONFIG @_multi_config@) +# Directories and files +set(ARCC_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/") +set(ARCC_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/") +set(ARCC_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/") +set(ARCC_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/") +set(ARCC_BUILD_DIR @_build_dir@) +# Qt environment +set(ARCC_QT_VERSION_MAJOR @_qt_version_major@) +set(ARCC_QT_RCC_EXECUTABLE @_qt_rcc_executable@) diff --git a/Source/cmQtAutoGenDigest.h b/Source/cmQtAutoGenDigest.h index 677c397..9ee1117 100644 --- a/Source/cmQtAutoGenDigest.h +++ b/Source/cmQtAutoGenDigest.h @@ -24,6 +24,8 @@ public: std::string QrcFile; std::string QrcName; std::string PathChecksum; + std::string InfoFile; + std::string SettingsFile; std::string RccFile; bool Generated; bool Unique; diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index c7550e6..9477bc3 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -635,29 +635,6 @@ static std::string RccGetExecutable(cmGeneratorTarget const* target, return rccExec; } -static void SetupAutoTargetRcc(cmQtAutoGenDigest const& digest) -{ - std::vector rccFiles; - std::vector rccBuilds; - std::vector> rccOptions; - std::vector> rccInputs; - - for (cmQtAutoGenDigestQrc const& qrcDigest : digest.Qrcs) { - rccFiles.push_back(qrcDigest.QrcFile); - rccBuilds.push_back(qrcDigest.RccFile); - rccOptions.push_back(qrcDigest.Options); - rccInputs.push_back(qrcDigest.Resources); - } - - cmMakefile* makefile = digest.Target->Target->GetMakefile(); - AddDefinitionEscaped(makefile, "_qt_rcc_executable", - RccGetExecutable(digest.Target, digest.QtVersionMajor)); - AddDefinitionEscaped(makefile, "_rcc_files", rccFiles); - AddDefinitionEscaped(makefile, "_rcc_builds", rccBuilds); - AddDefinitionEscaped(makefile, "_rcc_options", rccOptions); - AddDefinitionEscaped(makefile, "_rcc_inputs", rccInputs); -} - void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( cmQtAutoGenDigest& digest) { @@ -681,6 +658,24 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( std::set autogenDependTargets; std::vector autogenProvides; + // Autogen target FOLDER property + std::string autogenFolder; + { + 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 = SafeString(target->Target->GetProperty("FOLDER")); + } + if (folder != nullptr) { + autogenFolder = folder; + } + } + // Remove build directories on cleanup AddCleanFile(makefile, autogenBuildDir); // Remove old settings on cleanup @@ -699,45 +694,6 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( } } - // Compose command lines - cmCustomCommandLines commandLines; - { - cmCustomCommandLine currentLine; - currentLine.push_back(cmSystemTools::GetCMakeCommand()); - currentLine.push_back("-E"); - currentLine.push_back("cmake_autogen"); - currentLine.push_back(autogenInfoDir); - currentLine.push_back("$"); - commandLines.push_back(currentLine); - } - - // Compose target comment - std::string autogenComment; - { - std::vector toolNames; - if (digest.MocEnabled) { - toolNames.emplace_back("MOC"); - } - if (digest.UicEnabled) { - toolNames.emplace_back("UIC"); - } - if (digest.RccEnabled) { - toolNames.emplace_back("RCC"); - } - - std::string tools = toolNames.front(); - toolNames.erase(toolNames.begin()); - if (!toolNames.empty()) { - while (toolNames.size() > 1) { - tools += ", "; - tools += toolNames.front(); - toolNames.erase(toolNames.begin()); - } - tools += " and " + toolNames.front(); - } - autogenComment = "Automatic " + tools + " for target " + target->GetName(); - } - // Add moc compilation to generated files list if (digest.MocEnabled) { std::string const mocsComp = autogenBuildDir + "/mocs_compilation.cpp"; @@ -921,18 +877,30 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( } } } - // Path checksum + // Path checksum and file names { cmFilePathChecksum const fpathCheckSum(makefile); for (cmQtAutoGenDigestQrc& qrcDigest : digest.Qrcs) { qrcDigest.PathChecksum = fpathCheckSum.getPart(qrcDigest.QrcFile); // RCC output file name - std::string rccFile = autogenBuildDir + "/"; - rccFile += qrcDigest.PathChecksum; - rccFile += "/qrc_"; - rccFile += qrcDigest.QrcName; - rccFile += ".cpp"; - qrcDigest.RccFile = std::move(rccFile); + { + std::string rccFile = autogenBuildDir + "/"; + rccFile += qrcDigest.PathChecksum; + rccFile += "/qrc_"; + rccFile += qrcDigest.QrcName; + rccFile += ".cpp"; + qrcDigest.RccFile = std::move(rccFile); + } + { + std::string base = autogenInfoDir; + base += "/RCC"; + base += qrcDigest.QrcName; + if (!qrcDigest.Unique) { + base += qrcDigest.PathChecksum; + } + qrcDigest.InfoFile = base + "Info.cmake"; + qrcDigest.SettingsFile = base + "Settings.cmake"; + } } } // RCC options @@ -959,159 +927,239 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( } for (cmQtAutoGenDigestQrc& qrcDigest : digest.Qrcs) { // Register file at target + std::vector const ccOutput = AddGeneratedSource( + target, qrcDigest.RccFile, multiConfig, configsList, cmQtAutoGen::RCC); + + cmCustomCommandLines commandLines; { - auto files = AddGeneratedSource(target, qrcDigest.RccFile, multiConfig, - configsList, cmQtAutoGen::RCC); - for (std::string& file : files) { - autogenProvides.push_back(std::move(file)); - } + cmCustomCommandLine currentLine; + currentLine.push_back(cmSystemTools::GetCMakeCommand()); + currentLine.push_back("-E"); + currentLine.push_back("cmake_autorcc"); + currentLine.push_back(qrcDigest.InfoFile); + currentLine.push_back("$"); + commandLines.push_back(std::move(currentLine)); } - // Dependencies + std::string ccComment = "Automatic RCC for "; + ccComment += qrcDigest.QrcFile; + if (qrcDigest.Generated) { - // Add the GENERATED .qrc file to the dependencies - autogenDependFiles.insert(qrcDigest.QrcFile); + // Create custom rcc target + std::string ccName; + { + ccName = target->GetName(); + ccName += "_arcc_"; + ccName += qrcDigest.QrcName; + if (!qrcDigest.Unique) { + ccName += "_"; + ccName += qrcDigest.PathChecksum; + } + std::vector ccDepends; + // Add the .qrc file to the custom target dependencies + ccDepends.push_back(qrcDigest.QrcFile); + + cmTarget* autoRccTarget = makefile->AddUtilityCommand( + ccName, true, workingDirectory.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 (!autogenFolder.empty()) { + autoRccTarget->SetProperty("FOLDER", autogenFolder.c_str()); + } + } + // Add autogen target to the origin target dependencies + target->Target->AddUtility(ccName, makefile); } else { - // Add the resource files to the dependencies + // Create custom rcc command { - std::string error; - if (cmQtAutoGen::RccListInputs(digest.QtVersionMajor, rcc, - qrcDigest.QrcFile, - qrcDigest.Resources, &error)) { - for (std::string const& fileName : qrcDigest.Resources) { - autogenDependFiles.insert(fileName); + std::vector ccByproducts; + std::vector ccDepends; + // Add the .qrc file to the custom command dependencies + ccDepends.push_back(qrcDigest.QrcFile); + + // Add the resource files to the dependencies + { + std::string error; + if (cmQtAutoGen::RccListInputs(digest.QtVersionMajor, rcc, + qrcDigest.QrcFile, + qrcDigest.Resources, &error)) { + for (std::string const& fileName : qrcDigest.Resources) { + // Add resource file to the custom command dependencies + ccDepends.push_back(fileName); + } + } else { + cmSystemTools::Error(error.c_str()); } - } else { - cmSystemTools::Error(error.c_str()); } + makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends, + /*main_dependency*/ std::string(), + commandLines, ccComment.c_str(), + workingDirectory.c_str()); } - // Run cmake again when .qrc file changes + // Reconfigure when .qrc file changes makefile->AddCMakeDependFile(qrcDigest.QrcFile); } } } - // Add user defined autogen target dependencies - { - std::string const deps = GetSafeProperty(target, "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) { - autogenDependTargets.insert(depTarget); - } else { - autogenDependFiles.insert(depName); + // Create _autogen target + if (digest.MocEnabled || digest.UicEnabled) { + // Add user defined autogen target dependencies + { + std::string const deps = + GetSafeProperty(target, "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) { + autogenDependTargets.insert(depTarget); + } else { + autogenDependFiles.insert(depName); + } } } } - } - // 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; + // Compose target comment + std::string autogenComment; + { + std::vector toolNames; + if (digest.MocEnabled) { + toolNames.emplace_back("MOC"); + } + if (digest.UicEnabled) { + toolNames.emplace_back("UIC"); + } + if (digest.RccEnabled) { + toolNames.emplace_back("RCC"); + } + + std::string tools = toolNames.front(); + toolNames.erase(toolNames.begin()); + if (!toolNames.empty()) { + while (toolNames.size() > 1) { + tools += ", "; + tools += toolNames.front(); + toolNames.erase(toolNames.begin()); + } + tools += " and " + toolNames.front(); + } + autogenComment = + "Automatic " + tools + " for target " + target->GetName(); } - } - // Create the autogen target/command - if (usePRE_BUILD) { - // Add additional autogen target dependencies to origin target - for (cmTarget* depTarget : autogenDependTargets) { - target->Target->AddUtility(depTarget->GetName(), makefile); + + // Compose command lines + cmCustomCommandLines commandLines; + { + cmCustomCommandLine currentLine; + currentLine.push_back(cmSystemTools::GetCMakeCommand()); + currentLine.push_back("-E"); + currentLine.push_back("cmake_autogen"); + currentLine.push_back(autogenInfoDir); + currentLine.push_back("$"); + commandLines.push_back(std::move(currentLine)); } - // 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(), - workingDirectory.c_str()); - cc.SetEscapeOldStyle(false); - cc.SetEscapeAllowMakeVars(true); - target->Target->AddPreBuildCommand(cc); - } else { + // 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) { + target->Target->AddUtility(depTarget->GetName(), makefile); + } - // Convert file dependencies std::set to std::vector - std::vector autogenDepends(autogenDependFiles.begin(), - autogenDependFiles.end()); + // 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(), + workingDirectory.c_str()); + cc.SetEscapeOldStyle(false); + cc.SetEscapeAllowMakeVars(true); + target->Target->AddPreBuildCommand(cc); + } else { - // Add link library target dependencies to the autogen target dependencies - for (std::string const& config : configsList) { - cmLinkImplementationLibraries const* libs = - target->GetLinkImplementationLibraries(config); - if (libs != nullptr) { - for (cmLinkItem const& item : libs->Libraries) { - cmGeneratorTarget const* libTarget = item.Target; - if ((libTarget != nullptr) && - !StaticLibraryCycle(target, libTarget, config)) { - std::string util; - if (configsList.size() > 1) { - util += "$<$GetName(); - if (configsList.size() > 1) { - util += ">"; + // Convert file dependencies std::set to std::vector + std::vector autogenDepends(autogenDependFiles.begin(), + autogenDependFiles.end()); + + // Add link library target dependencies to the autogen target + // dependencies + for (std::string const& config : configsList) { + cmLinkImplementationLibraries const* libs = + target->GetLinkImplementationLibraries(config); + if (libs != nullptr) { + for (cmLinkItem const& item : libs->Libraries) { + cmGeneratorTarget const* libTarget = item.Target; + if ((libTarget != nullptr) && + !StaticLibraryCycle(target, libTarget, config)) { + std::string util; + if (configsList.size() > 1) { + util += "$<$GetName(); + if (configsList.size() > 1) { + util += ">"; + } + autogenDepends.push_back(util); } - autogenDepends.push_back(util); } } } - } - // Create autogen target - cmTarget* autogenTarget = makefile->AddUtilityCommand( - autogenTargetName, true, workingDirectory.c_str(), - /*byproducts=*/autogenProvides, autogenDepends, 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 : 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 - { - const char* autogenFolder = - makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); - if (autogenFolder == nullptr) { - autogenFolder = - makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER"); + // Create autogen target + cmTarget* autogenTarget = makefile->AddUtilityCommand( + autogenTargetName, true, workingDirectory.c_str(), + /*byproducts=*/autogenProvides, autogenDepends, 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 : target->Target->GetUtilities()) { + autogenTarget->AddUtility(depName, makefile); } - // Inherit FOLDER property from target (#13688) - if (autogenFolder == nullptr) { - autogenFolder = SafeString(target->Target->GetProperty("FOLDER")); + // Add additional autogen target dependencies to autogen target + for (cmTarget* depTarget : autogenDependTargets) { + autogenTarget->AddUtility(depTarget->GetName(), makefile); } - if ((autogenFolder != nullptr) && (*autogenFolder != '\0')) { - autogenTarget->SetProperty("FOLDER", autogenFolder); + + // Set FOLDER property in autogen target + if (!autogenFolder.empty()) { + autogenTarget->SetProperty("FOLDER", autogenFolder.c_str()); } - } - // Add autogen target to the origin target dependencies - target->Target->AddUtility(autogenTargetName, makefile); + // Add autogen target to the origin target dependencies + target->Target->AddUtility(autogenTargetName, makefile); + } } } @@ -1164,18 +1212,36 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( } } if (digest.RccEnabled) { - SetupAutoTargetRcc(digest); + AddDefinitionEscaped( + makefile, "_qt_rcc_executable", + RccGetExecutable(digest.Target, digest.QtVersionMajor)); } } - // Generate info file - { - std::string const infoDir = GetAutogenTargetFilesDir(target); - if (!cmSystemTools::MakeDirectory(infoDir)) { - std::string emsg = ("Could not create directory: "); - emsg += cmQtAutoGen::Quoted(infoDir); - cmSystemTools::Error(emsg.c_str()); + // Create info directory on demand + std::string const infoDir = GetAutogenTargetFilesDir(target); + if (!cmSystemTools::MakeDirectory(infoDir)) { + std::string emsg = ("Could not create directory: "); + emsg += cmQtAutoGen::Quoted(infoDir); + cmSystemTools::Error(emsg.c_str()); + } + + auto AdjustFilePermissions = [](std::string const& fileName) { + // Ensure we have write permission + mode_t perm = 0; +#if defined(_WIN32) && !defined(__CYGWIN__) + mode_t mode_write = S_IWRITE; +#else + mode_t mode_write = S_IWUSR; +#endif + cmSystemTools::GetPermissions(fileName, perm); + if (!(perm & mode_write)) { + cmSystemTools::SetPermissions(fileName, perm | mode_write); } + }; + + // Generate autogen target info file + { std::string const infoFile = infoDir + "/AutogenInfo.cmake"; { std::string infoFileIn = cmSystemTools::GetCMakeRoot(); @@ -1188,16 +1254,7 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( // -------------------------------------- // Ensure we have write permission in case .in was read-only. - mode_t perm = 0; -#if defined(_WIN32) && !defined(__CYGWIN__) - mode_t mode_write = S_IWRITE; -#else - mode_t mode_write = S_IWUSR; -#endif - cmSystemTools::GetPermissions(infoFile, perm); - if (!(perm & mode_write)) { - cmSystemTools::SetPermissions(infoFile, perm | mode_write); - } + AdjustFilePermissions(infoFile); // Open and write file cmsys::ofstream ofs(infoFile.c_str(), std::ios::app); @@ -1222,4 +1279,56 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( cmSystemTools::Error(error.c_str()); } } + + // Generate auto RCC info files + { + std::string infoFileIn = cmSystemTools::GetCMakeRoot(); + infoFileIn += "/Modules/AutoRccInfo.cmake.in"; + for (cmQtAutoGenDigestQrc const& qrcDigest : digest.Qrcs) { + // Configure info file + makefile->ConfigureFile(infoFileIn.c_str(), qrcDigest.InfoFile.c_str(), + false, true, false); + + // Append custom definitions to info file + // -------------------------------------- + + // Ensure we have write permission in case .in was read-only. + AdjustFilePermissions(qrcDigest.InfoFile); + + // Open and write file + cmsys::ofstream ofs(qrcDigest.InfoFile.c_str(), std::ios::app); + if (ofs) { + { + ofs << "# Configurations options\n"; + auto OfsWriteMap = [&ofs]( + const char* key, std::map const& map) { + for (auto const& item : map) { + ofs << "set(" << key << "_" << item.first << " " + << cmOutputConverter::EscapeForCMake(item.second) << ")\n"; + } + }; + OfsWriteMap("ARCC_CONFIG_SUFFIX", configSuffixes); + } + { + ofs << "# Job\n"; + auto OfsWrite = [&ofs](const char* key, std::string const& value) { + ofs << "set(" << key << " " + << cmOutputConverter::EscapeForCMake(value) << ")\n"; + + }; + OfsWrite("ARCC_SETTINGS_FILE", qrcDigest.SettingsFile); + OfsWrite("ARCC_SOURCE", qrcDigest.QrcFile); + OfsWrite("ARCC_OUTPUT", qrcDigest.RccFile); + OfsWrite("ARCC_OPTIONS", cmJoin(qrcDigest.Options, ";")); + OfsWrite("ARCC_INPUTS", cmJoin(qrcDigest.Resources, ";")); + } + } else { + // File open error + std::string error = "Internal CMake error when trying to open file: "; + error += cmQtAutoGen::Quoted(qrcDigest.InfoFile); + error += " for writing."; + cmSystemTools::Error(error.c_str()); + } + } + } } -- cgit v0.12