diff options
Diffstat (limited to 'Source')
336 files changed, 13494 insertions, 11228 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 1c06052..01c6cd7 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -92,7 +92,6 @@ include_directories( ${CMAKE_ZLIB_INCLUDES} ${CMAKE_EXPAT_INCLUDES} ${CMAKE_TAR_INCLUDES} - ${CMAKE_COMPRESS_INCLUDES} ${CMake_HAIKU_INCLUDE_DIRS} ) @@ -144,6 +143,8 @@ set(SRCS cmAffinity.cxx cmAffinity.h cmArchiveWrite.cxx + cmArgumentParser.cxx + cmArgumentParser.h cmBase32.cxx cmCacheManager.cxx cmCacheManager.h @@ -225,6 +226,10 @@ set(SRCS cmFileAPICodemodel.h cmFileAPICMakeFiles.cxx cmFileAPICMakeFiles.h + cmFileCopier.cxx + cmFileCopier.h + cmFileInstaller.cxx + cmFileInstaller.h cmFileLock.cxx cmFileLock.h cmFileLockPool.cxx @@ -233,8 +238,10 @@ set(SRCS cmFileLockResult.h cmFilePathChecksum.cxx cmFilePathChecksum.h - cmFileTimeComparison.cxx - cmFileTimeComparison.h + cmFileTime.cxx + cmFileTime.h + cmFileTimeCache.cxx + cmFileTimeCache.h cmFortranParserImpl.cxx cmFSPermissions.cxx cmFSPermissions.h @@ -257,6 +264,8 @@ set(SRCS cmGeneratorExpression.h cmGeneratorTarget.cxx cmGeneratorTarget.h + cmGetPipes.cxx + cmGetPipes.h cmGlobalCommonGenerator.cxx cmGlobalCommonGenerator.h cmGlobalGenerator.cxx @@ -343,10 +352,10 @@ set(SRCS cmQtAutoGenGlobalInitializer.h cmQtAutoGenInitializer.cxx cmQtAutoGenInitializer.h - cmQtAutoGeneratorMocUic.cxx - cmQtAutoGeneratorMocUic.h - cmQtAutoGeneratorRcc.cxx - cmQtAutoGeneratorRcc.h + cmQtAutoMocUic.cxx + cmQtAutoMocUic.h + cmQtAutoRcc.cxx + cmQtAutoRcc.h cmRST.cxx cmRST.h cmScriptGenerator.h @@ -379,11 +388,14 @@ set(SRCS cmUuid.cxx cmUVHandlePtr.cxx cmUVHandlePtr.h + cmUVStreambuf.h cmUVSignalHackRAII.h cmVariableWatch.cxx cmVariableWatch.h cmVersion.cxx cmVersion.h + cmWorkerPool.cxx + cmWorkerPool.h cmWorkingDirectory.cxx cmWorkingDirectory.h cmXMLParser.cxx @@ -438,8 +450,6 @@ set(SRCS cmCMakeMinimumRequired.h cmCMakePolicyCommand.cxx cmCMakePolicyCommand.h - cmCommandArgumentsHelper.cxx - cmCommandArgumentsHelper.h cmConditionEvaluator.cxx cmConditionEvaluator.h cmConfigureFileCommand.cxx @@ -724,14 +734,6 @@ if (WIN32) cmVisualStudioSlnParser.cxx cmVisualStudioWCEPlatformParser.h cmVisualStudioWCEPlatformParser.cxx - cmGlobalGhsMultiGenerator.cxx - cmGlobalGhsMultiGenerator.h - cmLocalGhsMultiGenerator.cxx - cmLocalGhsMultiGenerator.h - cmGhsMultiTargetGenerator.cxx - cmGhsMultiTargetGenerator.h - cmGhsMultiGpj.cxx - cmGhsMultiGpj.h cmVSSetupHelper.cxx cmVSSetupHelper.h ) @@ -751,6 +753,22 @@ if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux") ) endif() +# GHS support +# Works only for windows and linux +if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(SRCS ${SRCS} + cmGlobalGhsMultiGenerator.cxx + cmGlobalGhsMultiGenerator.h + cmLocalGhsMultiGenerator.cxx + cmLocalGhsMultiGenerator.h + cmGhsMultiTargetGenerator.cxx + cmGhsMultiTargetGenerator.h + cmGhsMultiGpj.cxx + cmGhsMultiGpj.h + ) +endif() + + # Ninja support set(SRCS ${SRCS} cmGlobalNinjaGenerator.cxx @@ -793,7 +811,7 @@ foreach(check else() set(CMake_${check} 0) endif() - set_property(SOURCE cmFileTimeComparison.cxx APPEND PROPERTY + set_property(SOURCE cmFileTime.cxx APPEND PROPERTY COMPILE_DEFINITIONS CMake_${check}=${CMake_${check}}) endforeach() @@ -801,7 +819,7 @@ endforeach() add_library(CMakeLib ${SRCS}) target_link_libraries(CMakeLib cmsys ${CMAKE_EXPAT_LIBRARIES} ${CMAKE_ZLIB_LIBRARIES} - ${CMAKE_TAR_LIBRARIES} ${CMAKE_COMPRESS_LIBRARIES} + ${CMAKE_TAR_LIBRARIES} ${CMAKE_CURL_LIBRARIES} ${CMAKE_JSONCPP_LIBRARIES} ${CMAKE_LIBUV_LIBRARIES} @@ -1060,9 +1078,6 @@ if(CPACK_ENABLE_FREEBSD_PKG AND FREEBSD_PKG_INCLUDE_DIRS AND FREEBSD_PKG_LIBRARI endif() if(APPLE) - add_executable(cmakexbuild cmakexbuild.cxx) - list(APPEND _tools cmakexbuild) - target_link_libraries(cmakexbuild CMakeLib) add_executable(OSXScriptLauncher CPack/OSXScriptLauncher.cxx) target_link_libraries(OSXScriptLauncher cmsys) diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 3beb8af..4e8066a 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 14) -set(CMake_VERSION_PATCH 2) -#set(CMake_VERSION_RC 0) +set(CMake_VERSION_PATCH 20190507) +#set(CMake_VERSION_RC 1) diff --git a/Source/CMakeVersion.rc.in b/Source/CMakeVersion.rc.in index 22b4a36..762d9bb 100644 --- a/Source/CMakeVersion.rc.in +++ b/Source/CMakeVersion.rc.in @@ -1,25 +1,16 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#define VER_FILEVERSION @CMake_RCVERSION@ -#define VER_FILEVERSION_STR "@CMake_RCVERSION_STR@\0" - -#define VER_PRODUCTVERSION @CMake_RCVERSION@ -#define VER_PRODUCTVERSION_STR "@CMake_RCVERSION_STR@\0" - -/* Version-information resource identifier. */ -#define VS_VERSION_INFO 1 - -VS_VERSION_INFO VERSIONINFO -FILEVERSION VER_FILEVERSION -PRODUCTVERSION VER_PRODUCTVERSION +1 VERSIONINFO +FILEVERSION @CMake_RCVERSION@ +PRODUCTVERSION @CMake_RCVERSION@ BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN - VALUE "FileVersion", VER_FILEVERSION_STR - VALUE "ProductVersion", VER_PRODUCTVERSION_STR + VALUE "FileVersion", "@CMake_RCVERSION_STR@\0" + VALUE "ProductVersion", "@CMake_RCVERSION_STR@\0" END END diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.cxx b/Source/CPack/IFW/cmCPackIFWGenerator.cxx index 9102e3e..c1b6eea 100644 --- a/Source/CPack/IFW/cmCPackIFWGenerator.cxx +++ b/Source/CPack/IFW/cmCPackIFWGenerator.cxx @@ -85,8 +85,8 @@ int cmCPackIFWGenerator::PackageFiles() int retVal = 1; cmCPackIFWLogger(OUTPUT, "- Generate repository" << std::endl); bool res = cmSystemTools::RunSingleCommand( - ifwCmd.c_str(), &output, &output, &retVal, nullptr, - this->GeneratorVerbose, cmDuration::zero()); + ifwCmd, &output, &output, &retVal, nullptr, this->GeneratorVerbose, + cmDuration::zero()); if (!res || retVal) { cmGeneratedFileStream ofs(ifwTmpFile); ofs << "# Run command: " << ifwCmd << std::endl @@ -198,8 +198,8 @@ int cmCPackIFWGenerator::PackageFiles() int retVal = 1; cmCPackIFWLogger(OUTPUT, "- Generate package" << std::endl); bool res = cmSystemTools::RunSingleCommand( - ifwCmd.c_str(), &output, &output, &retVal, nullptr, - this->GeneratorVerbose, cmDuration::zero()); + ifwCmd, &output, &output, &retVal, nullptr, this->GeneratorVerbose, + cmDuration::zero()); if (!res || retVal) { cmGeneratedFileStream ofs(ifwTmpFile); ofs << "# Run command: " << ifwCmd << std::endl diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.cxx b/Source/CPack/IFW/cmCPackIFWInstaller.cxx index 01e3ea4..a075a17 100644 --- a/Source/CPack/IFW/cmCPackIFWInstaller.cxx +++ b/Source/CPack/IFW/cmCPackIFWInstaller.cxx @@ -152,6 +152,15 @@ void cmCPackIFWInstaller::ConfigureFromOptions() } } + // StyleSheet + if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_STYLE_SHEET")) { + if (cmSystemTools::FileExists(option)) { + this->StyleSheet = option; + } else { + this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_STYLE_SHEET", option); + } + } + // WizardDefaultWidth if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_WIZARD_DEFAULT_WIDTH")) { @@ -381,6 +390,14 @@ void cmCPackIFWInstaller::GenerateInstallerFile() xout.Element("WizardStyle", this->WizardStyle); } + // Stylesheet + if (!this->StyleSheet.empty()) { + std::string name = cmSystemTools::GetFilenameName(this->StyleSheet); + std::string path = this->Directory + "/config/" + name; + cmsys::SystemTools::CopyFileIfDifferent(this->StyleSheet, path); + xout.Element("StyleSheet", name); + } + // WizardDefaultWidth if (!this->WizardDefaultWidth.empty()) { xout.Element("WizardDefaultWidth", this->WizardDefaultWidth); diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.h b/Source/CPack/IFW/cmCPackIFWInstaller.h index 37ad339..be51fa5 100644 --- a/Source/CPack/IFW/cmCPackIFWInstaller.h +++ b/Source/CPack/IFW/cmCPackIFWInstaller.h @@ -72,6 +72,9 @@ public: /// Wizard style name std::string WizardStyle; + /// Filename for a style sheet + std::string StyleSheet; + /// Wizard width std::string WizardDefaultWidth; diff --git a/Source/CPack/OSXScriptLauncher.cxx b/Source/CPack/OSXScriptLauncher.cxx index 4966d09..00d272c 100644 --- a/Source/CPack/OSXScriptLauncher.cxx +++ b/Source/CPack/OSXScriptLauncher.cxx @@ -73,7 +73,7 @@ int main(int argc, char* argv[]) args.push_back(nullptr); cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, &*args.begin()); + cmsysProcess_SetCommand(cp, args.data()); cmsysProcess_SetWorkingDirectory(cp, scriptDirectory.c_str()); cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); cmsysProcess_SetTimeout(cp, 0); diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx index 398ebd3..045d93d 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx @@ -61,9 +61,8 @@ bool cmCPackWIXGenerator::RunWiXCommand(std::string const& command) std::string output; int returnValue = 0; - bool status = cmSystemTools::RunSingleCommand(command.c_str(), &output, - &output, &returnValue, 0, - cmSystemTools::OUTPUT_NONE); + bool status = cmSystemTools::RunSingleCommand( + command, &output, &output, &returnValue, 0, cmSystemTools::OUTPUT_NONE); cmsys::ofstream logFile(logFileName.c_str(), std::ios::app); logFile << command << std::endl; @@ -619,7 +618,7 @@ bool cmCPackWIXGenerator::GenerateMainSourceFileFromTemplate() std::string mainSourceFilePath = this->CPackTopLevel + "/main.wxs"; - if (!ConfigureFile(wixTemplate.c_str(), mainSourceFilePath.c_str())) { + if (!ConfigureFile(wixTemplate, mainSourceFilePath)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Failed creating '" << mainSourceFilePath << "'' from template." << std::endl); diff --git a/Source/CPack/bills-comments.txt b/Source/CPack/bills-comments.txt deleted file mode 100644 index 1aaf9af..0000000 --- a/Source/CPack/bills-comments.txt +++ /dev/null @@ -1,68 +0,0 @@ -cpack.cxx - -cmCPackGenerators -- creates cmCPackGenericGenerator's via NewGenerator - - a cmCPackGenericGenerator factory - - -cmCPackGenericGenerator::Initialize - this->InitializeInternal - CPACK_INCLUDE_TOPLEVEL_DIRECTORY = 0 turns off - - -// binary package run -cmCPackGenericGenerator::ProcessGenerator // DoPackage - cmCPackGenericGenerator::PrepareNames -- sets a bunch of CPACK_vars - cmCPackGenericGenerator::InstallProject - run preinstall (make preinstall/fast) - call ReadListFile(cmake_install.cmake) - glob recurse in install directory to get list of files - this->CompressFiles with the list of files - - -// source package run -cmCPackGenericGenerator::ProcessGenerator // DoPackage - cmCPackGenericGenerator::PrepareNames -- sets a bunch of CPACK_vars - cmCPackGenericGenerator::InstallProject --> - if set CPACK_INSTALLED_DIRECTORIES - glob the files in that directory - copy those files to the tmp install directory _CPack something - glob recurse in install directory to get list of files - this->CompressFiles with the list of files - - -cmCPackGenericGenerator::InstallProject is used for both source and binary -packages. It is controlled based on values set in CPACK_ variables. - - -InstallProject - 1. CPACK_INSTALL_COMMANDS - a list of commands used to install the package - - 2. CPACK_INSTALLED_DIRECTORIES - copy this directory to CPACK_TEMPORARY_DIRECTORY - - 3. CPACK_INSTALL_CMAKE_PROJECTS - a cmake install script - - run make preinstall - - run cmake_install.cmake - - set CMAKE_INSTALL_PREFIX to the temp directory - - CPACK_BUILD_CONFIG check this and set the BUILD_TYPE to it - - ReadListFile on the install script cmake_install.cmake - - run strip on the executables and libraries if CPACK_STRIP_FILES is TRUE - -Recommendations: - -rename cmCPackGenerators to cmCPackGeneratorFactory - -rename cmCPackGenericGenerator --> cmCPackGenerator - -rename cmCPackGenericGenerator::ProcessGenerator -> cmCPackGenerator::DoPackage - - -break up cmCPackGenerator::InstallProject so it calls the following: - -// run user provided install commands - cmCPackGenerator::RunInstallCommands(); -// copy entire directories that need no processing like source trees - cmCPackGenerator::CopyPreInstalledDirectories(); -// run the cmake install scripts if provided - cmCPackGenerator::RunCMakeInstallScripts() - -- diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx index 2119f78..49a9f15 100644 --- a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx +++ b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx @@ -44,10 +44,9 @@ int cmCPackCygwinBinaryGenerator::PackageFiles() // to create the file before the super class is called { cmGeneratedFileStream ofs(manifestFile.c_str()); - for (std::vector<std::string>::const_iterator i = files.begin(); - i != files.end(); ++i) { + for (std::string const& file : files) { // remove the temp dir and replace with /usr - ofs << (*i).substr(tempdir.size()) << "\n"; + ofs << file.substr(tempdir.size()) << "\n"; } ofs << manifest << "\n"; } diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 635de49..cfb5efd 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -195,7 +195,7 @@ bool DebGenerator::generateDataTar() const // XXX/application/usr/bin/myprogram with GEN_WDIR=XXX/application // should not add XXX/application orderedFiles.insert(currentPath); - currentPath = cmSystemTools::CollapseCombinedPath(currentPath, ".."); + currentPath = cmSystemTools::CollapseFullPath("..", currentPath); } } diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx index 013ad81..7a3742b 100644 --- a/Source/CPack/cmCPackDragNDropGenerator.cxx +++ b/Source/CPack/cmCPackDragNDropGenerator.cxx @@ -244,8 +244,8 @@ bool cmCPackDragNDropGenerator::RunCommand(std::ostringstream& command, int exit_code = 1; bool result = cmSystemTools::RunSingleCommand( - command.str().c_str(), output, output, &exit_code, nullptr, - this->GeneratorVerbose, cmDuration::zero()); + command.str(), output, output, &exit_code, nullptr, this->GeneratorVerbose, + cmDuration::zero()); if (!result || exit_code) { cmCPackLogger(cmCPackLog::LOG_ERROR, diff --git a/Source/CPack/cmCPackFreeBSDGenerator.cxx b/Source/CPack/cmCPackFreeBSDGenerator.cxx index fcf8af1..9fdafa4 100644 --- a/Source/CPack/cmCPackFreeBSDGenerator.cxx +++ b/Source/CPack/cmCPackFreeBSDGenerator.cxx @@ -181,7 +181,7 @@ public: { s << "{\n"; for (std::string const& elem : value) { - s << " \"" << elem << "\": {\"origin\": \"" << elem << "\"},\n"; + s << " \"" << elem << R"(": {"origin": ")" << elem << "\"},\n"; } s << '}'; } @@ -325,8 +325,7 @@ int cmCPackFreeBSDGenerator::PackageFiles() ONE_PACKAGE_PER_COMPONENT); } - std::string output_dir = - cmSystemTools::CollapseCombinedPath(toplevel, "../"); + std::string output_dir = cmSystemTools::CollapseFullPath("../", toplevel); pkg_create_from_manifest(output_dir.c_str(), ::TXZ, toplevel.c_str(), manifestname.c_str(), nullptr); diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index 57c0545..127bcf9 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx @@ -43,7 +43,8 @@ cmCPackGenerator::~cmCPackGenerator() this->MakefileMap = nullptr; } -void cmCPackGenerator::DisplayVerboseOutput(const char* msg, float progress) +void cmCPackGenerator::DisplayVerboseOutput(const std::string& msg, + float progress) { (void)progress; cmCPackLogger(cmCPackLog::LOG_VERBOSE, "" << msg << std::endl); @@ -278,7 +279,7 @@ int cmCPackGenerator::InstallProjectViaInstallCommands( std::string output; int retVal = 1; bool resB = cmSystemTools::RunSingleCommand( - ic.c_str(), &output, &output, &retVal, nullptr, this->GeneratorVerbose, + ic, &output, &output, &retVal, nullptr, this->GeneratorVerbose, cmDuration::zero()); if (!resB || retVal) { std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); @@ -387,8 +388,7 @@ int cmCPackGenerator::InstallProjectViaInstalledDirectories( } /* If it is not a symlink then do a plain copy */ else if (!(cmSystemTools::CopyFileIfDifferent(inFile, filePath) && - cmSystemTools::CopyFileTime(inFile.c_str(), - filePath.c_str()))) { + cmSystemTools::CopyFileTime(inFile, filePath))) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying file: " << inFile << " -> " << filePath << std::endl); @@ -647,8 +647,8 @@ int cmCPackGenerator::RunPreinstallTarget( std::string output; int retVal = 1; bool resB = cmSystemTools::RunSingleCommand( - buildCommand.c_str(), &output, &output, &retVal, - installDirectory.c_str(), this->GeneratorVerbose, cmDuration::zero()); + buildCommand, &output, &output, &retVal, installDirectory.c_str(), + this->GeneratorVerbose, cmDuration::zero()); if (!resB || retVal) { std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); tmpFile += "/PreinstallOutput.log"; @@ -689,7 +689,7 @@ int cmCPackGenerator::InstallCMakeProject( cm.SetHomeOutputDirectory(""); cm.GetCurrentSnapshot().SetDefaultDefinitions(); cm.AddCMakePaths(); - cm.SetProgressCallback([this](const char* msg, float prog) { + cm.SetProgressCallback([this](const std::string& msg, float prog) { this->DisplayVerboseOutput(msg, prog); }); cm.SetTrace(this->Trace); @@ -1245,7 +1245,8 @@ bool cmCPackGenerator::ConfigureString(const std::string& inString, return true; } -bool cmCPackGenerator::ConfigureFile(const char* inName, const char* outName, +bool cmCPackGenerator::ConfigureFile(const std::string& inName, + const std::string& outName, bool copyOnly /* = false */) { return this->MakefileMap->ConfigureFile(inName, outName, copyOnly, true, @@ -1254,9 +1255,8 @@ bool cmCPackGenerator::ConfigureFile(const char* inName, const char* outName, int cmCPackGenerator::CleanTemporaryDirectory() { - std::string tempInstallDirectoryWithPostfix = + std::string tempInstallDirectory = this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY"); - const char* tempInstallDirectory = tempInstallDirectoryWithPostfix.c_str(); if (cmsys::SystemTools::FileExists(tempInstallDirectory)) { cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Clean temporary : " << tempInstallDirectory << std::endl); diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h index 4755f94..3c06d41 100644 --- a/Source/CPack/cmCPackGenerator.h +++ b/Source/CPack/cmCPackGenerator.h @@ -96,7 +96,7 @@ public: void SetLogger(cmCPackLog* log) { this->Logger = log; } //! Display verbose information via logger - void DisplayVerboseOutput(const char* msg, float progress); + void DisplayVerboseOutput(const std::string& msg, float progress); bool ReadListFile(const char* moduleName); @@ -169,7 +169,8 @@ protected: virtual const char* GetPackagingInstallPrefix(); virtual std::string FindTemplate(const char* name); - virtual bool ConfigureFile(const char* inName, const char* outName, + virtual bool ConfigureFile(const std::string& inName, + const std::string& outName, bool copyOnly = false); virtual bool ConfigureString(const std::string& input, std::string& output); virtual int InitializeInternal(); diff --git a/Source/CPack/cmCPackGeneratorFactory.h b/Source/CPack/cmCPackGeneratorFactory.h index 7f633e4..972f0f7 100644 --- a/Source/CPack/cmCPackGeneratorFactory.h +++ b/Source/CPack/cmCPackGeneratorFactory.h @@ -22,6 +22,9 @@ public: cmCPackGeneratorFactory(); ~cmCPackGeneratorFactory(); + cmCPackGeneratorFactory(const cmCPackGeneratorFactory&) = delete; + cmCPackGeneratorFactory& operator=(const cmCPackGeneratorFactory&) = delete; + //! Get the generator cmCPackGenerator* NewGenerator(const std::string& name); void DeleteGenerator(cmCPackGenerator* gen); diff --git a/Source/CPack/cmCPackLog.h b/Source/CPack/cmCPackLog.h index 8e99221..65281e3 100644 --- a/Source/CPack/cmCPackLog.h +++ b/Source/CPack/cmCPackLog.h @@ -26,6 +26,9 @@ public: cmCPackLog(); ~cmCPackLog(); + cmCPackLog(const cmCPackLog&) = delete; + cmCPackLog& operator=(const cmCPackLog&) = delete; + enum __log_tags { NOTAG = 0, diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index 37ea66e..e2020c5 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -182,7 +182,7 @@ int cmCPackNSISGenerator::PackageFiles() this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", ""); this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", ""); this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", - "File /r \"${INST_DIR}\\*.*\""); + R"(File /r "${INST_DIR}\*.*")"); this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", ""); this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", ""); this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", ""); @@ -242,7 +242,7 @@ int cmCPackNSISGenerator::PackageFiles() } // Add this component to the various section lists. - sectionList += " !insertmacro \"${MacroName}\" \""; + sectionList += R"( !insertmacro "${MacroName}" ")"; sectionList += comp.first; sectionList += "\"\n"; selectedVarsList += "Var " + comp.first + "_selected\n"; @@ -292,9 +292,8 @@ int cmCPackNSISGenerator::PackageFiles() this->SetOption("CPACK_NSIS_DEFINES", defines.c_str()); } - this->ConfigureFile(nsisInInstallOptions.c_str(), - nsisInstallOptions.c_str()); - this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str()); + this->ConfigureFile(nsisInInstallOptions, nsisInstallOptions); + this->ConfigureFile(nsisInFileName, nsisFileName); std::string nsisCmd = "\""; nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM"); nsisCmd += "\" \"" + nsisFileName + "\""; @@ -302,8 +301,8 @@ int cmCPackNSISGenerator::PackageFiles() std::string output; int retVal = 1; bool res = cmSystemTools::RunSingleCommand( - nsisCmd.c_str(), &output, &output, &retVal, nullptr, - this->GeneratorVerbose, cmDuration::zero()); + nsisCmd, &output, &output, &retVal, nullptr, this->GeneratorVerbose, + cmDuration::zero()); if (!res || retVal) { cmGeneratedFileStream ofs(tmpFile); ofs << "# Run command: " << nsisCmd << std::endl @@ -407,8 +406,8 @@ int cmCPackNSISGenerator::InitializeInternal() std::string output; int retVal = 1; bool resS = cmSystemTools::RunSingleCommand( - nsisCmd.c_str(), &output, &output, &retVal, nullptr, - this->GeneratorVerbose, cmDuration::zero()); + nsisCmd, &output, &output, &retVal, nullptr, this->GeneratorVerbose, + cmDuration::zero()); cmsys::RegularExpression versionRex("v([0-9]+.[0-9]+)"); cmsys::RegularExpression versionRexCVS("v(.*)\\.cvs"); if (!resS || retVal || @@ -495,10 +494,10 @@ int cmCPackNSISGenerator::InitializeInternal() std::string execName = *it; ++it; std::string linkName = *it; - str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" << linkName - << ".lnk\" \"$INSTDIR\\" << cpackNsisExecutablesDirectory << "\\" + str << R"( CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\)" << linkName + << R"(.lnk" "$INSTDIR\)" << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\"" << std::endl; - deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName + deleteStr << R"( Delete "$SMPROGRAMS\$MUI_TEMP\)" << linkName << ".lnk\"" << std::endl; // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on // if so add a desktop link @@ -508,7 +507,7 @@ int cmCPackNSISGenerator::InitializeInternal() execName) != cpackPackageDesktopLinksVector.end()) { str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; str << " CreateShortCut \"$DESKTOP\\" << linkName - << ".lnk\" \"$INSTDIR\\" << cpackNsisExecutablesDirectory << "\\" + << R"(.lnk" "$INSTDIR\)" << cpackNsisExecutablesDirectory << "\\" << execName << ".exe\"" << std::endl; deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; deleteStr << " Delete \"$DESKTOP\\" << linkName << ".lnk\"" @@ -564,15 +563,15 @@ void cmCPackNSISGenerator::CreateMenuLinks(std::ostream& str, ++it; std::string linkName = *it; if (!url) { - str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" << linkName - << ".lnk\" \"$INSTDIR\\" << sourceName << "\"" << std::endl; - deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName + str << R"( CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\)" << linkName + << R"(.lnk" "$INSTDIR\)" << sourceName << "\"" << std::endl; + deleteStr << R"( Delete "$SMPROGRAMS\$MUI_TEMP\)" << linkName << ".lnk\"" << std::endl; } else { - str << " WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" << linkName - << ".url\" \"InternetShortcut\" \"URL\" \"" << sourceName << "\"" + str << R"( WriteINIStr "$SMPROGRAMS\$STARTMENU_FOLDER\)" << linkName + << R"(.url" "InternetShortcut" "URL" ")" << sourceName << "\"" << std::endl; - deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName + deleteStr << R"( Delete "$SMPROGRAMS\$MUI_TEMP\)" << linkName << ".url\"" << std::endl; } // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on @@ -582,7 +581,7 @@ void cmCPackNSISGenerator::CreateMenuLinks(std::ostream& str, if (this->IsSet(desktop)) { str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; str << " CreateShortCut \"$DESKTOP\\" << linkName - << ".lnk\" \"$INSTDIR\\" << sourceName << "\"" << std::endl; + << R"(.lnk" "$INSTDIR\)" << sourceName << "\"" << std::endl; deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; deleteStr << " Delete \"$DESKTOP\\" << linkName << ".lnk\"" << std::endl; @@ -749,7 +748,7 @@ std::string cmCPackNSISGenerator::CreateComponentDescription( std::string output; int retVal = -1; int res = cmSystemTools::RunSingleCommand( - cmd.c_str(), &output, &output, &retVal, dirName.c_str(), + cmd, &output, &output, &retVal, dirName.c_str(), cmSystemTools::OUTPUT_NONE, cmDuration::zero()); if (!res || retVal) { std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); diff --git a/Source/CPack/cmCPackOSXX11Generator.cxx b/Source/CPack/cmCPackOSXX11Generator.cxx index 486633c..90e0afe 100644 --- a/Source/CPack/cmCPackOSXX11Generator.cxx +++ b/Source/CPack/cmCPackOSXX11Generator.cxx @@ -83,7 +83,7 @@ int cmCPackOSXX11Generator::PackageFiles() return 0; } std::string destFileName = resourcesDirectory + "/" + iconFileName; - this->ConfigureFile(iconFile, destFileName.c_str(), true); + this->ConfigureFile(iconFile, destFileName, true); this->SetOptionIfNotSet("CPACK_APPLE_GUI_ICON", iconFileName.c_str()); } @@ -155,8 +155,8 @@ int cmCPackOSXX11Generator::PackageFiles() bool res = false; while (numTries > 0) { res = cmSystemTools::RunSingleCommand( - dmgCmd.str().c_str(), &output, &output, &retVal, nullptr, - this->GeneratorVerbose, cmDuration::zero()); + dmgCmd.str(), &output, &output, &retVal, nullptr, this->GeneratorVerbose, + cmDuration::zero()); if (res && !retVal) { numTries = -1; break; @@ -236,7 +236,7 @@ bool cmCPackOSXX11Generator::CopyCreateResourceFile(const std::string& name) cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " << (inFileName ? inFileName : "(NULL)") << " to " << destFileName << std::endl); - this->ConfigureFile(inFileName, destFileName.c_str()); + this->ConfigureFile(inFileName, destFileName); return true; } */ @@ -266,7 +266,7 @@ bool cmCPackOSXX11Generator::CopyResourcePlistFile( cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " << inFileName << " to " << destFileName << std::endl); - this->ConfigureFile(inFileName.c_str(), destFileName.c_str(), copyOnly); + this->ConfigureFile(inFileName, destFileName, copyOnly); return true; } diff --git a/Source/CPack/cmCPackPKGGenerator.cxx b/Source/CPack/cmCPackPKGGenerator.cxx index ae227aa..8c22c65 100644 --- a/Source/CPack/cmCPackPKGGenerator.cxx +++ b/Source/CPack/cmCPackPKGGenerator.cxx @@ -66,21 +66,17 @@ void cmCPackPKGGenerator::WriteDistributionFile(const char* metapackageFile) xout.StartElement("choices-outline"); // Emit the outline for the groups - std::map<std::string, cmCPackComponentGroup>::iterator groupIt; - for (groupIt = this->ComponentGroups.begin(); - groupIt != this->ComponentGroups.end(); ++groupIt) { - if (groupIt->second.ParentGroup == nullptr) { - CreateChoiceOutline(groupIt->second, xout); + for (auto const& group : this->ComponentGroups) { + if (group.second.ParentGroup == nullptr) { + CreateChoiceOutline(group.second, xout); } } // Emit the outline for the non-grouped components - std::map<std::string, cmCPackComponent>::iterator compIt; - for (compIt = this->Components.begin(); compIt != this->Components.end(); - ++compIt) { - if (!compIt->second.Group) { + for (auto const& comp : this->Components) { + if (!comp.second.Group) { xout.StartElement("line"); - xout.Attribute("choice", compIt->first + "Choice"); + xout.Attribute("choice", comp.first + "Choice"); xout.Content(""); // Avoid self-closing tag. xout.EndElement(); } @@ -94,13 +90,11 @@ void cmCPackPKGGenerator::WriteDistributionFile(const char* metapackageFile) xout.EndElement(); // choices-outline> // Create the actual choices - for (groupIt = this->ComponentGroups.begin(); - groupIt != this->ComponentGroups.end(); ++groupIt) { - CreateChoice(groupIt->second, xout); + for (auto const& group : this->ComponentGroups) { + CreateChoice(group.second, xout); } - for (compIt = this->Components.begin(); compIt != this->Components.end(); - ++compIt) { - CreateChoice(compIt->second, xout); + for (auto const& comp : this->Components) { + CreateChoice(comp.second, xout); } if (!this->PostFlightComponent.Name.empty()) { @@ -111,7 +105,7 @@ void cmCPackPKGGenerator::WriteDistributionFile(const char* metapackageFile) // Create the distribution.dist file in the metapackage to turn it // into a distribution package. - this->ConfigureFile(distributionTemplate.c_str(), distributionFile.c_str()); + this->ConfigureFile(distributionTemplate, distributionFile); } void cmCPackPKGGenerator::CreateChoiceOutline( @@ -119,17 +113,13 @@ void cmCPackPKGGenerator::CreateChoiceOutline( { xout.StartElement("line"); xout.Attribute("choice", group.Name + "Choice"); - std::vector<cmCPackComponentGroup*>::const_iterator groupIt; - for (groupIt = group.Subgroups.begin(); groupIt != group.Subgroups.end(); - ++groupIt) { - CreateChoiceOutline(**groupIt, xout); + for (cmCPackComponentGroup* subgroup : group.Subgroups) { + CreateChoiceOutline(*subgroup, xout); } - std::vector<cmCPackComponent*>::const_iterator compIt; - for (compIt = group.Components.begin(); compIt != group.Components.end(); - ++compIt) { + for (cmCPackComponent* comp : group.Components) { xout.StartElement("line"); - xout.Attribute("choice", (*compIt)->Name + "Choice"); + xout.Attribute("choice", comp->Name + "Choice"); xout.Content(""); // Avoid self-closing tag. xout.EndElement(); } @@ -238,11 +228,9 @@ void cmCPackPKGGenerator::AddDependencyAttributes( } visited.insert(&component); - std::vector<cmCPackComponent*>::const_iterator dependIt; - for (dependIt = component.Dependencies.begin(); - dependIt != component.Dependencies.end(); ++dependIt) { - out << " && choices['" << (*dependIt)->Name << "Choice'].selected"; - AddDependencyAttributes(**dependIt, visited, out); + for (cmCPackComponent* depend : component.Dependencies) { + out << " && choices['" << depend->Name << "Choice'].selected"; + AddDependencyAttributes(*depend, visited, out); } } @@ -255,11 +243,9 @@ void cmCPackPKGGenerator::AddReverseDependencyAttributes( } visited.insert(&component); - std::vector<cmCPackComponent*>::const_iterator dependIt; - for (dependIt = component.ReverseDependencies.begin(); - dependIt != component.ReverseDependencies.end(); ++dependIt) { - out << " || choices['" << (*dependIt)->Name << "Choice'].selected"; - AddReverseDependencyAttributes(**dependIt, visited, out); + for (cmCPackComponent* depend : component.ReverseDependencies) { + out << " || choices['" << depend->Name << "Choice'].selected"; + AddReverseDependencyAttributes(*depend, visited, out); } } @@ -308,7 +294,7 @@ bool cmCPackPKGGenerator::CopyCreateResourceFile(const std::string& name, cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " << (inFileName ? inFileName : "(NULL)") << " to " << destFileName << std::endl); - this->ConfigureFile(inFileName, destFileName.c_str()); + this->ConfigureFile(inFileName, destFileName); return true; } @@ -336,7 +322,7 @@ bool cmCPackPKGGenerator::CopyResourcePlistFile(const std::string& name, cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " << inFileName << " to " << destFileName << std::endl); - this->ConfigureFile(inFileName.c_str(), destFileName.c_str()); + this->ConfigureFile(inFileName, destFileName); return true; } diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx index 246178d..3d93c48 100644 --- a/Source/CPack/cmCPackPackageMakerGenerator.cxx +++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx @@ -295,8 +295,8 @@ int cmCPackPackageMakerGenerator::PackageFiles() bool res = false; while (numTries > 0) { res = cmSystemTools::RunSingleCommand( - dmgCmd.str().c_str(), &output, &output, &retVal, nullptr, - this->GeneratorVerbose, cmDuration::zero()); + dmgCmd.str(), &output, &output, &retVal, nullptr, this->GeneratorVerbose, + cmDuration::zero()); if (res && !retVal) { numTries = -1; break; diff --git a/Source/CPack/cmCPackProductBuildGenerator.cxx b/Source/CPack/cmCPackProductBuildGenerator.cxx index a556e0c..94b5b5f 100644 --- a/Source/CPack/cmCPackProductBuildGenerator.cxx +++ b/Source/CPack/cmCPackProductBuildGenerator.cxx @@ -145,8 +145,8 @@ bool cmCPackProductBuildGenerator::RunProductBuild(const std::string& command) std::string output; int retVal = 1; bool res = cmSystemTools::RunSingleCommand( - command.c_str(), &output, &output, &retVal, nullptr, - this->GeneratorVerbose, cmDuration::zero()); + command, &output, &output, &retVal, nullptr, this->GeneratorVerbose, + cmDuration::zero()); cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running command" << std::endl); if (!res || retVal) { cmGeneratedFileStream ofs(tmpFile); diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index 0413422..58b9e70 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx @@ -82,7 +82,7 @@ int cpackDefinitionArgument(const char* argument, const char* cValue, return 0; } std::string key = value.substr(0, pos); - value = value.c_str() + pos + 1; + value = value.substr(pos + 1); def->Map[key] = value; cmCPack_Log(def->Log, cmCPackLog::LOG_DEBUG, "Set CPack variable: " << key << " to \"" << value << "\"" @@ -90,7 +90,7 @@ int cpackDefinitionArgument(const char* argument, const char* cValue, return 1; } -static void cpackProgressCallback(const char* message, float /*unused*/) +static void cpackProgressCallback(const std::string& message, float /*unused*/) { std::cout << "-- " << message << std::endl; } @@ -98,6 +98,7 @@ static void cpackProgressCallback(const char* message, float /*unused*/) // this is CPack. int main(int argc, char const* const* argv) { + cmSystemTools::EnsureStdPipes(); #if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) // Replace streambuf so we can output Unicode to console cmsys::ConsoleBuf::Manager consoleOut(std::cout); diff --git a/Source/CTest/cmCTestBZR.cxx b/Source/CTest/cmCTestBZR.cxx index b154caf..83aeb64 100644 --- a/Source/CTest/cmCTestBZR.cxx +++ b/Source/CTest/cmCTestBZR.cxx @@ -365,7 +365,7 @@ bool cmCTestBZR::UpdateImpl() if (opts.empty()) { opts = this->CTest->GetCTestConfiguration("BZRUpdateOptions"); } - std::vector<std::string> args = cmSystemTools::ParseArguments(opts.c_str()); + std::vector<std::string> args = cmSystemTools::ParseArguments(opts); // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY) diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index 2fd4c7a..9ad9669 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -118,7 +118,7 @@ public: : CM(cm) { cmSystemTools::SetMessageCallback( - [&s](const char* msg, const char* /*unused*/) { + [&s](const std::string& msg, const char* /*unused*/) { s += msg; s += "\n"; }); @@ -126,9 +126,11 @@ public: cmSystemTools::SetStdoutCallback([&s](std::string const& m) { s += m; }); cmSystemTools::SetStderrCallback([&s](std::string const& m) { s += m; }); - this->CM.SetProgressCallback([&s](const char* msg, float /*unused*/) { - s += msg; - s += "\n"; + this->CM.SetProgressCallback([&s](const std::string& msg, float prog) { + if (prog < 0) { + s += msg; + s += "\n"; + } }); } @@ -139,6 +141,11 @@ public: cmSystemTools::SetStdoutCallback(nullptr); cmSystemTools::SetMessageCallback(nullptr); } + + cmCTestBuildAndTestCaptureRAII(const cmCTestBuildAndTestCaptureRAII&) = + delete; + cmCTestBuildAndTestCaptureRAII& operator=( + const cmCTestBuildAndTestCaptureRAII&) = delete; }; int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) @@ -250,7 +257,7 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) } int retVal = cm.GetGlobalGenerator()->Build( cmake::NO_BUILD_PARALLEL_LEVEL, this->SourceDir, this->BinaryDir, - this->BuildProject, tar, output, this->BuildMakeProgram, config, + this->BuildProject, { tar }, output, this->BuildMakeProgram, config, !this->BuildNoClean, false, false, remainingTime); out << output; // if the build failed then return diff --git a/Source/CTest/cmCTestBuildAndTestHandler.h b/Source/CTest/cmCTestBuildAndTestHandler.h index 5e6d0aa..2d47b15 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.h +++ b/Source/CTest/cmCTestBuildAndTestHandler.h @@ -44,7 +44,7 @@ public: void Initialize() override; protected: - ///! Run CMake and build a test and then run it as a single test. + //! Run CMake and build a test and then run it as a single test. int RunCMakeAndTest(std::string* output); int RunCMake(std::string* outstring, std::ostringstream& out, std::string& cmakeOutString, cmake* cm); diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index 32f7496..2eacaf1 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx @@ -4,7 +4,6 @@ #include "cmCTest.h" #include "cmCTestBuildHandler.h" -#include "cmCTestGenericHandler.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -39,12 +38,10 @@ cmCTestBuildCommand::~cmCTestBuildCommand() cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() { - cmCTestGenericHandler* handler = this->CTest->GetInitializedHandler("build"); - if (!handler) { - this->SetError("internal CTest error. Cannot instantiate build handler"); - return nullptr; - } - this->Handler = static_cast<cmCTestBuildHandler*>(handler); + cmCTestBuildHandler* handler = this->CTest->GetBuildHandler(); + handler->Initialize(); + + this->Handler = handler; const char* ctestBuildCommand = this->Makefile->GetDefinition("CTEST_BUILD_COMMAND"); diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index d934c00..1e17e1c 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -5,7 +5,7 @@ #include "cmAlgorithms.h" #include "cmCTest.h" #include "cmDuration.h" -#include "cmFileTimeComparison.h" +#include "cmFileTimeCache.h" #include "cmGeneratedFileStream.h" #include "cmMakefile.h" #include "cmProcessOutput.h" @@ -32,13 +32,13 @@ static const char* cmCTestErrorMatches[] = { "^Error: ", "^Error ", "[0-9] ERROR: ", - "^\"[^\"]+\", line [0-9]+: [^Ww]", + R"(^"[^"]+", line [0-9]+: [^Ww])", "^cc[^C]*CC: ERROR File = ([^,]+), Line = ([0-9]+)", "^ld([^:])*:([ \\t])*ERROR([^:])*:", - "^ild:([ \\t])*\\(undefined symbol\\)", + R"(^ild:([ \t])*\(undefined symbol\))", "([^ :]+) : (error|fatal error|catastrophic error)", "([^:]+): (Error:|error|undefined reference|multiply defined)", - "([^:]+)\\(([^\\)]+)\\) ?: (error|fatal error|catastrophic error)", + R"(([^:]+)\(([^\)]+)\) ?: (error|fatal error|catastrophic error))", "^fatal error C[0-9]+:", ": syntax error ", "^collect2: ld returned 1 exit status", @@ -50,14 +50,14 @@ static const char* cmCTestErrorMatches[] = { "^CMake Error.*:", ":[ \\t]cannot find", ":[ \\t]can't find", - ": \\*\\*\\* No rule to make target [`'].*\\'. Stop", - ": \\*\\*\\* No targets specified and no makefile found", + R"(: \*\*\* No rule to make target [`'].*\'. Stop)", + R"(: \*\*\* No targets specified and no makefile found)", ": Invalid loader fixup for symbol", ": Invalid fixups exist", ": Can't find library for", ": internal link edit command failed", ": Unrecognized option [`'].*\\'", - "\", line [0-9]+\\.[0-9]+: [0-9]+-[0-9]+ \\([^WI]\\)", + R"(", line [0-9]+\.[0-9]+: [0-9]+-[0-9]+ \([^WI]\))", "ld: 0706-006 Cannot find or open library file: -l ", "ild: \\(argument error\\) can't find library argument ::", "^could not be found and will not be loaded.", @@ -66,11 +66,11 @@ static const char* cmCTestErrorMatches[] = { "ld: 0711-993 Error occurred while writing to the output file:", "ld: fatal: ", "final link failed:", - "make: \\*\\*\\*.*Error", - "make\\[.*\\]: \\*\\*\\*.*Error", - "\\*\\*\\* Error code", + R"(make: \*\*\*.*Error)", + R"(make\[.*\]: \*\*\*.*Error)", + R"(\*\*\* Error code)", "nternal error:", - "Makefile:[0-9]+: \\*\\*\\* .* Stop\\.", + R"(Makefile:[0-9]+: \*\*\* .* Stop\.)", ": No such file or directory", ": Invalid argument", "^The project cannot be built\\.", @@ -101,19 +101,19 @@ static const char* cmCTestWarningMatches[] = { "^cc[^C]*CC: WARNING File = ([^,]+), Line = ([0-9]+)", "^ld([^:])*:([ \\t])*WARNING([^:])*:", "([^:]+): warning ([0-9]+):", - "^\"[^\"]+\", line [0-9]+: [Ww](arning|arnung)", + R"(^"[^"]+", line [0-9]+: [Ww](arning|arnung))", "([^:]+): warning[ \\t]*[0-9]+[ \\t]*:", "^(Warning|Warnung) ([0-9]+):", "^(Warning|Warnung)[ :]", "WARNING: ", "([^ :]+) : warning", "([^:]+): warning", - "\", line [0-9]+\\.[0-9]+: [0-9]+-[0-9]+ \\([WI]\\)", + R"(", line [0-9]+\.[0-9]+: [0-9]+-[0-9]+ \([WI]\))", "^cxx: Warning:", ".*file: .* has no symbols", "([^ :]+):([0-9]+): (Warning|Warnung)", "\\([0-9]*\\): remark #[0-9]*", - "\".*\", line [0-9]+: remark\\([0-9]*\\):", + R"(".*", line [0-9]+: remark\([0-9]*\):)", "cc-[0-9]* CC: REMARK File = .*, Line = [0-9]*", "^CMake Warning.*:", "^\\[WARNING\\]", @@ -121,9 +121,9 @@ static const char* cmCTestWarningMatches[] = { }; static const char* cmCTestWarningExceptions[] = { - "/usr/.*/X11/Xlib\\.h:[0-9]+: war.*: ANSI C\\+\\+ forbids declaration", - "/usr/.*/X11/Xutil\\.h:[0-9]+: war.*: ANSI C\\+\\+ forbids declaration", - "/usr/.*/X11/XResource\\.h:[0-9]+: war.*: ANSI C\\+\\+ forbids declaration", + R"(/usr/.*/X11/Xlib\.h:[0-9]+: war.*: ANSI C\+\+ forbids declaration)", + R"(/usr/.*/X11/Xutil\.h:[0-9]+: war.*: ANSI C\+\+ forbids declaration)", + R"(/usr/.*/X11/XResource\.h:[0-9]+: war.*: ANSI C\+\+ forbids declaration)", "WARNING 84 :", "WARNING 47 :", "makefile:", @@ -150,8 +150,8 @@ struct cmCTestBuildCompileErrorWarningRex static cmCTestBuildCompileErrorWarningRex cmCTestWarningErrorFileLine[] = { { "^Warning W[0-9]+ ([a-zA-Z.\\:/0-9_+ ~-]+) ([0-9]+):", 1, 2 }, { "^([a-zA-Z./0-9_+ ~-]+):([0-9]+):", 1, 2 }, - { "^([a-zA-Z.\\:/0-9_+ ~-]+)\\(([0-9]+)\\)", 1, 2 }, - { "^[0-9]+>([a-zA-Z.\\:/0-9_+ ~-]+)\\(([0-9]+)\\)", 1, 2 }, + { R"(^([a-zA-Z.\:/0-9_+ ~-]+)\(([0-9]+)\))", 1, 2 }, + { R"(^[0-9]+>([a-zA-Z.\:/0-9_+ ~-]+)\(([0-9]+)\))", 1, 2 }, { "^([a-zA-Z./0-9_+ ~-]+)\\(([0-9]+)\\)", 1, 2 }, { "\"([a-zA-Z./0-9_+ ~-]+)\", line ([0-9]+)", 1, 2 }, { "File = ([a-zA-Z./0-9_+ ~-]+), Line = ([0-9]+)", 1, 2 }, @@ -387,7 +387,7 @@ int cmCTestBuildHandler::ProcessHandler() std::string srcdirrep; for (cc = srcdir.size() - 2; cc > 0; cc--) { if (srcdir[cc] == '/') { - srcdirrep = srcdir.c_str() + cc; + srcdirrep = srcdir.substr(cc); srcdirrep = "/..." + srcdirrep; srcdir = srcdir.substr(0, cc + 1); break; @@ -401,7 +401,7 @@ int cmCTestBuildHandler::ProcessHandler() std::string bindirrep; for (cc = bindir.size() - 2; cc > 0; cc--) { if (bindir[cc] == '/') { - bindirrep = bindir.c_str() + cc; + bindirrep = bindir.substr(cc); bindirrep = "/..." + bindirrep; bindir = bindir.substr(0, cc + 1); break; @@ -418,8 +418,8 @@ int cmCTestBuildHandler::ProcessHandler() int retVal = 0; int res = cmsysProcess_State_Exited; if (!this->CTest->GetShowOnly()) { - res = this->RunMakeCommand(makeCommand.c_str(), &retVal, - buildDirectory.c_str(), 0, ofs); + res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0, + ofs); } else { cmCTestOptionalLog(this->CTest, DEBUG, "Build with command: " << makeCommand << std::endl, @@ -503,7 +503,7 @@ void cmCTestBuildHandler::GenerateXMLHeader(cmXMLWriter& xml) class cmCTestBuildHandler::FragmentCompare { public: - FragmentCompare(cmFileTimeComparison* ftc) + FragmentCompare(cmFileTimeCache* ftc) : FTC(ftc) { } @@ -513,14 +513,14 @@ public: // Order files by modification time. Use lexicographic order // among files with the same time. int result; - if (this->FTC->FileTimeCompare(l, r, &result) && result != 0) { + if (this->FTC->Compare(l, r, &result) && result != 0) { return result < 0; } return l < r; } private: - cmFileTimeComparison* FTC = nullptr; + cmFileTimeCache* FTC = nullptr; }; void cmCTestBuildHandler::GenerateXMLLaunched(cmXMLWriter& xml) @@ -530,7 +530,7 @@ void cmCTestBuildHandler::GenerateXMLLaunched(cmXMLWriter& xml) } // Sort XML fragments in chronological order. - cmFileTimeComparison ftc; + cmFileTimeCache ftc; FragmentCompare fragmentCompare(&ftc); typedef std::set<std::string, FragmentCompare> Fragments; Fragments fragments(fragmentCompare); @@ -680,6 +680,8 @@ class cmCTestBuildHandler::LaunchHelper public: LaunchHelper(cmCTestBuildHandler* handler); ~LaunchHelper(); + LaunchHelper(const LaunchHelper&) = delete; + LaunchHelper& operator=(const LaunchHelper&) = delete; private: cmCTestBuildHandler* Handler; @@ -764,9 +766,10 @@ void cmCTestBuildHandler::LaunchHelper::WriteScrapeMatchers( } } -int cmCTestBuildHandler::RunMakeCommand(const char* command, int* retVal, - const char* dir, int timeout, - std::ostream& ofs, Encoding encoding) +int cmCTestBuildHandler::RunMakeCommand(const std::string& command, + int* retVal, const char* dir, + int timeout, std::ostream& ofs, + Encoding encoding) { // First generate the command and arguments std::vector<std::string> args = cmSystemTools::ParseArguments(command); @@ -800,7 +803,7 @@ int cmCTestBuildHandler::RunMakeCommand(const char* command, int* retVal, // Now create process object cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, &*argv.begin()); + cmsysProcess_SetCommand(cp, argv.data()); cmsysProcess_SetWorkingDirectory(cp, dir); cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); cmsysProcess_SetTimeout(cp, timeout); @@ -978,7 +981,7 @@ void cmCTestBuildHandler::ProcessBuffer(const char* data, size_t length, this->CurrentProcessingLine.insert(this->CurrentProcessingLine.end(), queue->begin(), it); this->CurrentProcessingLine.push_back(0); - const char* line = &*this->CurrentProcessingLine.begin(); + const char* line = this->CurrentProcessingLine.data(); // Process the line int lineType = this->ProcessSingleLine(line); diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h index a9b121b..722c590 100644 --- a/Source/CTest/cmCTestBuildHandler.h +++ b/Source/CTest/cmCTestBuildHandler.h @@ -52,7 +52,7 @@ private: //! Run command specialized for make and configure. Returns process status // and retVal is return value or exception. - int RunMakeCommand(const char* command, int* retVal, const char* dir, + int RunMakeCommand(const std::string& command, int* retVal, const char* dir, int timeout, std::ostream& ofs, Encoding encoding = cmProcessOutput::Auto); diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx index 6e8f73f..9c03839 100644 --- a/Source/CTest/cmCTestCVS.cxx +++ b/Source/CTest/cmCTestCVS.cxx @@ -78,7 +78,7 @@ bool cmCTestCVS::UpdateImpl() opts = "-dP"; } } - std::vector<std::string> args = cmSystemTools::ParseArguments(opts.c_str()); + std::vector<std::string> args = cmSystemTools::ParseArguments(opts); // Specify the start time for nightly testing. if (this->CTest->GetTestModel() == cmCTest::NIGHTLY) { diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx index 7b5c3bc..74a932a 100644 --- a/Source/CTest/cmCTestConfigureCommand.cxx +++ b/Source/CTest/cmCTestConfigureCommand.cxx @@ -3,7 +3,7 @@ #include "cmCTestConfigureCommand.h" #include "cmCTest.h" -#include "cmCTestGenericHandler.h" +#include "cmCTestConfigureHandler.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmSystemTools.h" @@ -142,13 +142,8 @@ cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler() labelsForSubprojects, this->Quiet); } - cmCTestGenericHandler* handler = - this->CTest->GetInitializedHandler("configure"); - if (!handler) { - this->SetError( - "internal CTest error. Cannot instantiate configure handler"); - return nullptr; - } + cmCTestConfigureHandler* handler = this->CTest->GetConfigureHandler(); + handler->Initialize(); handler->SetQuiet(this->Quiet); return handler; } diff --git a/Source/CTest/cmCTestConfigureHandler.cxx b/Source/CTest/cmCTestConfigureHandler.cxx index 6b7601b..7e93189 100644 --- a/Source/CTest/cmCTestConfigureHandler.cxx +++ b/Source/CTest/cmCTestConfigureHandler.cxx @@ -61,7 +61,7 @@ int cmCTestConfigureHandler::ProcessHandler() cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Configure with command: " << cCommand << std::endl, this->Quiet); - res = this->CTest->RunMakeCommand(cCommand.c_str(), output, &retVal, + res = this->CTest->RunMakeCommand(cCommand, output, &retVal, buildDirectory.c_str(), cmDuration::zero(), ofs); diff --git a/Source/CTest/cmCTestCoverageCommand.cxx b/Source/CTest/cmCTestCoverageCommand.cxx index d2003ba..07aae76 100644 --- a/Source/CTest/cmCTestCoverageCommand.cxx +++ b/Source/CTest/cmCTestCoverageCommand.cxx @@ -19,12 +19,8 @@ cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler() this->CTest->SetCTestConfigurationFromCMakeVariable( this->Makefile, "CoverageExtraFlags", "CTEST_COVERAGE_EXTRA_FLAGS", this->Quiet); - cmCTestCoverageHandler* handler = static_cast<cmCTestCoverageHandler*>( - this->CTest->GetInitializedHandler("coverage")); - if (!handler) { - this->SetError("internal CTest error. Cannot instantiate test handler"); - return nullptr; - } + cmCTestCoverageHandler* handler = this->CTest->GetCoverageHandler(); + handler->Initialize(); // If a LABELS option was given, select only files with the labels. if (this->LabelsMentioned) { diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index 225383c..d76bd2a 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -52,6 +52,8 @@ public: } cmsysProcess_Delete(this->Process); } + cmCTestRunProcess(const cmCTestRunProcess&) = delete; + cmCTestRunProcess& operator=(const cmCTestRunProcess&) = delete; void SetCommand(const char* command) { this->CommandLineStrings.clear(); @@ -72,7 +74,7 @@ public: args.push_back(cl.c_str()); } args.push_back(nullptr); // null terminate - cmsysProcess_SetCommand(this->Process, &*args.begin()); + cmsysProcess_SetCommand(this->Process, args.data()); if (!this->WorkingDirectory.empty()) { cmsysProcess_SetWorkingDirectory(this->Process, this->WorkingDirectory.c_str()); @@ -223,7 +225,7 @@ bool cmCTestCoverageHandler::ShouldIDoCoverage(std::string const& file, checkDir = fBinDir; } std::string ndc = cmSystemTools::FileExistsInParentDirectories( - ".NoDartCoverage", fFile.c_str(), checkDir.c_str()); + ".NoDartCoverage", fFile, checkDir); if (!ndc.empty()) { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc << " so skip coverage of " << file @@ -254,8 +256,8 @@ bool cmCTestCoverageHandler::ShouldIDoCoverage(std::string const& file, return true; } - ndc = cmSystemTools::FileExistsInParentDirectories( - ".NoDartCoverage", fFile.c_str(), checkDir.c_str()); + ndc = cmSystemTools::FileExistsInParentDirectories(".NoDartCoverage", fFile, + checkDir); if (!ndc.empty()) { cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Found: " << ndc << " so skip coverage of: " << file @@ -786,6 +788,9 @@ struct cmCTestCoverageHandlerLocale cmSystemTools::UnsetEnv("LC_ALL"); } } + cmCTestCoverageHandlerLocale(const cmCTestCoverageHandlerLocale&) = delete; + cmCTestCoverageHandlerLocale& operator=( + const cmCTestCoverageHandlerLocale&) = delete; std::string lc_all; }; @@ -999,7 +1004,7 @@ int cmCTestCoverageHandler::HandleGCovCoverage( static_cast<void>(locale_C); std::vector<std::string> basecovargs = - cmSystemTools::ParseArguments(gcovExtraFlags.c_str()); + cmSystemTools::ParseArguments(gcovExtraFlags); basecovargs.insert(basecovargs.begin(), gcovCommand); basecovargs.emplace_back("-o"); @@ -1058,8 +1063,7 @@ int cmCTestCoverageHandler::HandleGCovCoverage( this->Quiet); std::vector<std::string> lines; - - cmSystemTools::Split(output.c_str(), lines); + cmsys::SystemTools::Split(output, lines); for (std::string const& line : lines) { std::string sourceFile; @@ -1373,7 +1377,7 @@ int cmCTestCoverageHandler::HandleLCovCoverage( static_cast<void>(locale_C); std::vector<std::string> covargs = - cmSystemTools::ParseArguments(lcovExtraFlags.c_str()); + cmSystemTools::ParseArguments(lcovExtraFlags); covargs.insert(covargs.begin(), lcovCommand); const std::string command = joinCommandLine(covargs); @@ -1435,8 +1439,7 @@ int cmCTestCoverageHandler::HandleLCovCoverage( this->Quiet); std::vector<std::string> lines; - - cmSystemTools::Split(output.c_str(), lines); + cmsys::SystemTools::Split(output, lines); for (std::string const& line : lines) { std::string sourceFile; diff --git a/Source/CTest/cmCTestCurl.h b/Source/CTest/cmCTestCurl.h index 86d9489..6186af8 100644 --- a/Source/CTest/cmCTestCurl.h +++ b/Source/CTest/cmCTestCurl.h @@ -16,6 +16,8 @@ class cmCTestCurl public: cmCTestCurl(cmCTest*); ~cmCTestCurl(); + cmCTestCurl(const cmCTestCurl&) = delete; + cmCTestCurl& operator=(const cmCTestCurl&) = delete; bool UploadFile(std::string const& local_file, std::string const& url, std::string const& fields, std::string& response); bool HttpRequest(std::string const& url, std::string const& fields, diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx index 210abe5..9d9761c 100644 --- a/Source/CTest/cmCTestGIT.cxx +++ b/Source/CTest/cmCTestGIT.cxx @@ -162,7 +162,7 @@ bool cmCTestGIT::UpdateByFetchAndReset() if (opts.empty()) { opts = this->CTest->GetCTestConfiguration("GITUpdateOptions"); } - std::vector<std::string> args = cmSystemTools::ParseArguments(opts.c_str()); + std::vector<std::string> args = cmSystemTools::ParseArguments(opts); for (std::string const& arg : args) { git_fetch.push_back(arg.c_str()); } @@ -547,7 +547,7 @@ private: { // Look for header fields that we need. if (cmHasLiteralPrefix(this->Line, "commit ")) { - this->Rev.Rev = this->Line.c_str() + 7; + this->Rev.Rev = this->Line.substr(7); } else if (cmHasLiteralPrefix(this->Line, "author ")) { Person author; this->ParsePerson(this->Line.c_str() + 7, author); diff --git a/Source/CTest/cmCTestHG.cxx b/Source/CTest/cmCTestHG.cxx index 6fb99d8..727c59c 100644 --- a/Source/CTest/cmCTestHG.cxx +++ b/Source/CTest/cmCTestHG.cxx @@ -144,7 +144,7 @@ bool cmCTestHG::UpdateImpl() if (opts.empty()) { opts = this->CTest->GetCTestConfiguration("HGUpdateOptions"); } - std::vector<std::string> args = cmSystemTools::ParseArguments(opts.c_str()); + std::vector<std::string> args = cmSystemTools::ParseArguments(opts); for (std::string const& arg : args) { hg_update.push_back(arg.c_str()); } diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index 57a14ef..adf9553 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -76,6 +76,8 @@ public: } } } + SaveRestoreErrorState(const SaveRestoreErrorState&) = delete; + SaveRestoreErrorState& operator=(const SaveRestoreErrorState&) = delete; private: bool InitialErrorState; diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx index 5e66e05..a96513e 100644 --- a/Source/CTest/cmCTestLaunch.cxx +++ b/Source/CTest/cmCTestLaunch.cxx @@ -308,7 +308,7 @@ void cmCTestLaunch::LoadLabels() if (line[0] == ' ') { // Label lines appear indented by one space. if (inTarget || inSource) { - this->Labels.insert(line.c_str() + 1); + this->Labels.insert(line.substr(1)); } } else if (!this->OptionSource.empty() && !inSource) { // Non-indented lines specify a source file name. The first one diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h index debbe59..107fd61 100644 --- a/Source/CTest/cmCTestLaunch.h +++ b/Source/CTest/cmCTestLaunch.h @@ -28,6 +28,9 @@ private: cmCTestLaunch(int argc, const char* const* argv); ~cmCTestLaunch(); + cmCTestLaunch(const cmCTestLaunch&) = delete; + cmCTestLaunch& operator=(const cmCTestLaunch&) = delete; + // Run the real command. int Run(); void RunChild(); diff --git a/Source/CTest/cmCTestMemCheckCommand.cxx b/Source/CTest/cmCTestMemCheckCommand.cxx index a5d5995..7dad1ce 100644 --- a/Source/CTest/cmCTestMemCheckCommand.cxx +++ b/Source/CTest/cmCTestMemCheckCommand.cxx @@ -7,7 +7,6 @@ #include <vector> #include "cmCTest.h" -#include "cmCTestGenericHandler.h" #include "cmCTestMemCheckHandler.h" #include "cmMakefile.h" @@ -20,8 +19,8 @@ cmCTestMemCheckCommand::cmCTestMemCheckCommand() cmCTestGenericHandler* cmCTestMemCheckCommand::InitializeActualHandler() { - cmCTestGenericHandler* handler = - this->CTest->GetInitializedHandler("memcheck"); + cmCTestMemCheckHandler* handler = this->CTest->GetMemCheckHandler(); + handler->Initialize(); this->CTest->SetCTestConfigurationFromCMakeVariable( this->Makefile, "MemoryCheckType", "CTEST_MEMORYCHECK_TYPE", this->Quiet); diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx index 8ba59d3..b09e7bb 100644 --- a/Source/CTest/cmCTestMemCheckHandler.cxx +++ b/Source/CTest/cmCTestMemCheckHandler.cxx @@ -520,7 +520,7 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() this->CTest->GetCTestConfiguration("ValgrindCommandOptions"); } this->MemoryTesterOptions = - cmSystemTools::ParseArguments(memoryTesterOptions.c_str()); + cmSystemTools::ParseArguments(memoryTesterOptions); this->MemoryTesterOutputFile = this->CTest->GetBinaryDir() + "/Testing/Temporary/MemoryChecker.??.log"; @@ -725,7 +725,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckSanitizerOutput( cmsys::RegularExpression leakWarning("(Direct|Indirect) leak of .*"); int defects = 0; std::vector<std::string> lines; - cmSystemTools::Split(str.c_str(), lines); + cmsys::SystemTools::Split(str, lines); std::ostringstream ostr; log.clear(); for (std::string const& l : lines) { @@ -755,7 +755,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckPurifyOutput( const std::string& str, std::string& log, std::vector<int>& results) { std::vector<std::string> lines; - cmSystemTools::Split(str.c_str(), lines); + cmsys::SystemTools::Split(str, lines); std::ostringstream ostr; log.clear(); @@ -798,7 +798,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput( const std::string& str, std::string& log, std::vector<int>& results) { std::vector<std::string> lines; - cmSystemTools::Split(str.c_str(), lines); + cmsys::SystemTools::Split(str, lines); bool unlimitedOutput = false; if (str.find("CTEST_FULL_OUTPUT") != std::string::npos || this->CustomMaximumFailedTestOutputSize == 0) { @@ -815,9 +815,9 @@ bool cmCTestMemCheckHandler::ProcessMemCheckValgrindOutput( cmsys::RegularExpression valgrindLine("^==[0-9][0-9]*=="); cmsys::RegularExpression vgFIM( - "== .*Invalid free\\(\\) / delete / delete\\[\\]"); + R"(== .*Invalid free\(\) / delete / delete\[\])"); cmsys::RegularExpression vgFMM( - "== .*Mismatched free\\(\\) / delete / delete \\[\\]"); + R"(== .*Mismatched free\(\) / delete / delete \[\])"); cmsys::RegularExpression vgMLK1( "== .*[0-9,]+ bytes in [0-9,]+ blocks are definitely lost" " in loss record [0-9,]+ of [0-9,]+"); @@ -937,7 +937,7 @@ bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput( log.clear(); auto sttime = std::chrono::steady_clock::now(); std::vector<std::string> lines; - cmSystemTools::Split(str.c_str(), lines); + cmsys::SystemTools::Split(str, lines); cmCTestOptionalLog(this->CTest, DEBUG, "Start test: " << lines.size() << std::endl, this->Quiet); std::vector<std::string>::size_type cc; diff --git a/Source/CTest/cmCTestMemCheckHandler.h b/Source/CTest/cmCTestMemCheckHandler.h index 8880dac..746d72c 100644 --- a/Source/CTest/cmCTestMemCheckHandler.h +++ b/Source/CTest/cmCTestMemCheckHandler.h @@ -114,7 +114,7 @@ private: // this type of checker void InitializeResultsVectors(); - ///! Initialize memory checking subsystem. + //! Initialize memory checking subsystem. bool InitializeMemoryChecking(); /** @@ -143,11 +143,11 @@ private: void PostProcessTest(cmCTestTestResult& res, int test); void PostProcessBoundsCheckerTest(cmCTestTestResult& res, int test); - ///! append MemoryTesterOutputFile to the test log + //! append MemoryTesterOutputFile to the test log void AppendMemTesterOutput(cmCTestTestHandler::cmCTestTestResult& res, std::string const& filename); - ///! generate the output filename for the given test index + //! generate the output filename for the given test index void TestOutputFileNames(int test, std::vector<std::string>& files); }; diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index 756ac6c..477161a 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -8,6 +8,7 @@ #include "cmCTestTestHandler.h" #include "cmDuration.h" #include "cmListFileCache.h" +#include "cmRange.h" #include "cmSystemTools.h" #include "cmWorkingDirectory.h" @@ -108,8 +109,8 @@ void cmCTestMultiProcessHandler::SetTestLoad(unsigned long load) fake_load_value)) { if (!cmSystemTools::StringToULong(fake_load_value.c_str(), &this->FakeLoadForTesting)) { - cmSystemTools::Error("Failed to parse fake load value: ", - fake_load_value.c_str()); + cmSystemTools::Error("Failed to parse fake load value: " + + fake_load_value); } } } @@ -651,9 +652,7 @@ void cmCTestMultiProcessHandler::CreateParallelTestCostList() // Reverse iterate over the different dependency levels (deepest first). // Sort tests within each level by COST and append them to the cost list. - for (std::list<TestSet>::reverse_iterator i = priorityStack.rbegin(); - i != priorityStack.rend(); ++i) { - TestSet const& currentSet = *i; + for (TestSet const& currentSet : cmReverseRange(priorityStack)) { TestComparator comp(this); TestList sortedCopy; diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx index 435be97..aa42810 100644 --- a/Source/CTest/cmCTestP4.cxx +++ b/Source/CTest/cmCTestP4.cxx @@ -5,6 +5,7 @@ #include "cmCTest.h" #include "cmCTestVC.h" #include "cmProcessTools.h" +#include "cmRange.h" #include "cmSystemTools.h" #include "cmsys/RegularExpression.hxx" @@ -193,7 +194,7 @@ public: { this->SetLog(&P4->Log, prefix); this->RegexHeader.compile("^Change ([0-9]+) by (.+)@(.+) on (.*)$"); - this->RegexDiff.compile("^\\.\\.\\. (.*)#[0-9]+ ([^ ]+)$"); + this->RegexDiff.compile(R"(^\.\.\. (.*)#[0-9]+ ([^ ]+)$)"); } private: @@ -323,8 +324,7 @@ void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions) // The CTEST_P4_OPTIONS variable adds additional Perforce command line // options before the main command std::string opts = this->CTest->GetCTestConfiguration("P4Options"); - std::vector<std::string> args = - cmSystemTools::ParseArguments(opts.c_str()); + std::vector<std::string> args = cmSystemTools::ParseArguments(opts); P4Options.insert(P4Options.end(), args.begin(), args.end()); } @@ -425,12 +425,11 @@ bool cmCTestP4::LoadRevisions() // p4 describe -s ...@1111111,2222222 std::vector<char const*> p4_describe; - for (std::vector<std::string>::reverse_iterator i = ChangeLists.rbegin(); - i != ChangeLists.rend(); ++i) { + for (std::string const& i : cmReverseRange(ChangeLists)) { SetP4Options(p4_describe); p4_describe.push_back("describe"); p4_describe.push_back("-s"); - p4_describe.push_back(i->c_str()); + p4_describe.push_back(i.c_str()); p4_describe.push_back(nullptr); DescribeParser outDescribe(this, "p4_describe-out> "); @@ -501,7 +500,7 @@ bool cmCTestP4::UpdateImpl() if (opts.empty()) { opts = this->CTest->GetCTestConfiguration("P4UpdateOptions"); } - std::vector<std::string> args = cmSystemTools::ParseArguments(opts.c_str()); + std::vector<std::string> args = cmSystemTools::ParseArguments(opts); for (std::string const& arg : args) { p4_sync.push_back(arg.c_str()); } diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 7f081ef..31976b9 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -9,11 +9,10 @@ #include "cmSystemTools.h" #include "cmWorkingDirectory.h" -#include "cm_zlib.h" -#include "cmsys/Base64.h" #include "cmsys/RegularExpression.hxx" #include <chrono> #include <cmAlgorithms.h> +#include <cstdint> #include <cstring> #include <iomanip> #include <ratio> @@ -31,9 +30,6 @@ cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler) this->TestResult.Status = cmCTestTestHandler::NOT_RUN; this->TestResult.TestCount = 0; this->TestResult.Properties = nullptr; - this->ProcessOutput.clear(); - this->CompressedOutput.clear(); - this->CompressionRatio = 2; this->NumberOfRunsLeft = 1; // default to 1 run of the test this->RunUntilFail = false; // default to run the test once this->RunAgain = false; // default to not having to run again @@ -68,73 +64,8 @@ void cmCTestRunTest::CheckOutput(std::string const& line) } } -// Streamed compression of test output. The compressed data -// is appended to this->CompressedOutput -void cmCTestRunTest::CompressOutput() -{ - int ret; - z_stream strm; - - unsigned char* in = reinterpret_cast<unsigned char*>( - const_cast<char*>(this->ProcessOutput.c_str())); - // zlib makes the guarantee that this is the maximum output size - int outSize = static_cast<int>( - static_cast<double>(this->ProcessOutput.size()) * 1.001 + 13.0); - unsigned char* out = new unsigned char[outSize]; - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - ret = deflateInit(&strm, -1); // default compression level - if (ret != Z_OK) { - delete[] out; - return; - } - - strm.avail_in = static_cast<uInt>(this->ProcessOutput.size()); - strm.next_in = in; - strm.avail_out = outSize; - strm.next_out = out; - ret = deflate(&strm, Z_FINISH); - - if (ret != Z_STREAM_END) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Error during output compression. Sending uncompressed output." - << std::endl); - delete[] out; - return; - } - - (void)deflateEnd(&strm); - - unsigned char* encoded_buffer = - new unsigned char[static_cast<int>(outSize * 1.5)]; - - size_t rlen = cmsysBase64_Encode(out, strm.total_out, encoded_buffer, 1); - - this->CompressedOutput.clear(); - for (size_t i = 0; i < rlen; i++) { - this->CompressedOutput += encoded_buffer[i]; - } - - if (strm.total_in) { - this->CompressionRatio = - static_cast<double>(strm.total_out) / static_cast<double>(strm.total_in); - } - - delete[] encoded_buffer; - delete[] out; -} - bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) { - if ((!this->TestHandler->MemCheck && - this->CTest->ShouldCompressTestOutput()) || - (this->TestHandler->MemCheck && - this->CTest->ShouldCompressTestOutput())) { - this->CompressOutput(); - } - this->WriteLogOutputTop(completed, total); std::string reason; bool passed = true; @@ -143,7 +74,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) if (res != cmProcess::State::Expired) { this->TimeoutIsForStopTime = false; } - int retVal = this->TestProcess->GetExitValue(); + std::int64_t retVal = this->TestProcess->GetExitValue(); bool forceFail = false; bool skipped = false; bool outputTestErrorsToConsole = false; @@ -201,14 +132,17 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) } else { this->TestResult.Status = cmCTestTestHandler::FAILED; outputStream << "***Failed " << reason; - outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; + outputTestErrorsToConsole = + this->CTest->GetOutputTestOutputOnTestFailure(); } } else if (res == cmProcess::State::Expired) { outputStream << "***Timeout "; this->TestResult.Status = cmCTestTestHandler::TIMEOUT; - outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; + outputTestErrorsToConsole = + this->CTest->GetOutputTestOutputOnTestFailure(); } else if (res == cmProcess::State::Exception) { - outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; + outputTestErrorsToConsole = + this->CTest->GetOutputTestOutputOnTestFailure(); outputStream << "***Exception: "; this->TestResult.ExceptionStatus = this->TestProcess->GetExitExceptionString(); @@ -335,10 +269,18 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) // if the test actually started and ran // record the results in TestResult if (started) { - bool compress = !this->TestHandler->MemCheck && - this->CompressionRatio < 1 && this->CTest->ShouldCompressTestOutput(); + std::string compressedOutput; + if (!this->TestHandler->MemCheck && + this->CTest->ShouldCompressTestOutput()) { + std::string str = this->ProcessOutput; + if (this->CTest->CompressString(str)) { + compressedOutput = std::move(str); + } + } + bool compress = !compressedOutput.empty() && + compressedOutput.length() < this->ProcessOutput.length(); this->TestResult.Output = - compress ? this->CompressedOutput : this->ProcessOutput; + compress ? compressedOutput : this->ProcessOutput; this->TestResult.CompressOutput = compress; this->TestResult.ReturnValue = this->TestProcess->GetExitValue(); if (!skipped) { @@ -493,32 +435,26 @@ bool cmCTestRunTest::StartTest(size_t completed, size_t total) this->ProcessOutput.clear(); + this->TestResult.Properties = this->TestProperties; + this->TestResult.ExecutionTime = cmDuration::zero(); + this->TestResult.CompressOutput = false; + this->TestResult.ReturnValue = -1; + this->TestResult.TestCount = this->TestProperties->Index; + this->TestResult.Name = this->TestProperties->Name; + this->TestResult.Path = this->TestProperties->Directory; + // Return immediately if test is disabled if (this->TestProperties->Disabled) { - this->TestResult.Properties = this->TestProperties; - this->TestResult.ExecutionTime = cmDuration::zero(); - this->TestResult.CompressOutput = false; - this->TestResult.ReturnValue = -1; this->TestResult.CompletionStatus = "Disabled"; this->TestResult.Status = cmCTestTestHandler::NOT_RUN; - this->TestResult.TestCount = this->TestProperties->Index; - this->TestResult.Name = this->TestProperties->Name; - this->TestResult.Path = this->TestProperties->Directory; this->TestProcess = cm::make_unique<cmProcess>(*this); this->TestResult.Output = "Disabled"; this->TestResult.FullCommandLine.clear(); return false; } - this->TestResult.Properties = this->TestProperties; - this->TestResult.ExecutionTime = cmDuration::zero(); - this->TestResult.CompressOutput = false; - this->TestResult.ReturnValue = -1; this->TestResult.CompletionStatus = "Failed to start"; this->TestResult.Status = cmCTestTestHandler::BAD_COMMAND; - this->TestResult.TestCount = this->TestProperties->Index; - this->TestResult.Name = this->TestProperties->Name; - this->TestResult.Path = this->TestProperties->Directory; // Check for failed fixture dependencies before we even look at the command // arguments because if we are not going to run the test, the command and @@ -696,9 +632,8 @@ bool cmCTestRunTest::ForkProcess(cmDuration testTimeOut, bool explicitTimeout, { this->TestProcess = cm::make_unique<cmProcess>(*this); this->TestProcess->SetId(this->Index); - this->TestProcess->SetWorkingDirectory( - this->TestProperties->Directory.c_str()); - this->TestProcess->SetCommand(this->ActualCommand.c_str()); + this->TestProcess->SetWorkingDirectory(this->TestProperties->Directory); + this->TestProcess->SetCommand(this->ActualCommand); this->TestProcess->SetCommandArguments(this->Arguments); // determine how much time we have diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h index 918d5fa..38cc417 100644 --- a/Source/CTest/cmCTestRunTest.h +++ b/Source/CTest/cmCTestRunTest.h @@ -58,9 +58,6 @@ public: // Read and store output. Returns true if it must be called again. void CheckOutput(std::string const& line); - // Compresses the output, writing to CompressedOutput - void CompressOutput(); - // launch the test process, return whether it started correctly bool StartTest(size_t completed, size_t total); // capture and report the test results @@ -105,8 +102,6 @@ private: cmCTest* CTest; std::unique_ptr<cmProcess> TestProcess; std::string ProcessOutput; - std::string CompressedOutput; - double CompressionRatio; // The test results cmCTestTestHandler::cmCTestTestResult TestResult; cmCTestMultiProcessHandler& MultiTestHandler; diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx index 3bf66ca..04749b7 100644 --- a/Source/CTest/cmCTestSVN.cxx +++ b/Source/CTest/cmCTestSVN.cxx @@ -242,7 +242,7 @@ bool cmCTestSVN::UpdateImpl() if (opts.empty()) { opts = this->CTest->GetCTestConfiguration("SVNUpdateOptions"); } - std::vector<std::string> args = cmSystemTools::ParseArguments(opts.c_str()); + std::vector<std::string> args = cmSystemTools::ParseArguments(opts); // Specify the start time for nightly testing. if (this->CTest->GetTestModel() == cmCTest::NIGHTLY) { @@ -277,7 +277,7 @@ bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters, std::string userOptions = this->CTest->GetCTestConfiguration("SVNOptions"); std::vector<std::string> parsedUserOptions = - cmSystemTools::ParseArguments(userOptions.c_str()); + cmSystemTools::ParseArguments(userOptions); for (std::string const& opt : parsedUserOptions) { args.push_back(opt.c_str()); } @@ -515,7 +515,7 @@ private: if (path.size() > this->SVN->SourceDirectory.size() && strncmp(path.c_str(), this->SVN->SourceDirectory.c_str(), this->SVN->SourceDirectory.size()) == 0) { - local_path = path.c_str() + this->SVN->SourceDirectory.size() + 1; + local_path = path.substr(this->SVN->SourceDirectory.size() + 1); } else { local_path = path; } @@ -554,7 +554,7 @@ std::string cmCTestSVN::SVNInfo::BuildLocalPath(std::string const& path) const // Add path with base prefix removed if (path.size() > this->Base.size() && strncmp(path.c_str(), this->Base.c_str(), this->Base.size()) == 0) { - local_path += (path.c_str() + this->Base.size()); + local_path += path.substr(this->Base.size()); } else { local_path += path; } diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 33b8b4a..43cfe16 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -199,7 +199,7 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg) // Now create process object cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, &*argv.begin()); + cmsysProcess_SetCommand(cp, argv.data()); // cmsysProcess_SetWorkingDirectory(cp, dir); cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); // cmsysProcess_SetTimeout(cp, timeout); @@ -288,11 +288,12 @@ void cmCTestScriptHandler::CreateCMake() this->ParentMakefile->GetRecursionDepth()); } - this->CMake->SetProgressCallback([this](const char* m, float /*unused*/) { - if (m && *m) { - cmCTestLog(this->CTest, HANDLER_OUTPUT, "-- " << m << std::endl); - } - }); + this->CMake->SetProgressCallback( + [this](const std::string& m, float /*unused*/) { + if (!m.empty()) { + cmCTestLog(this->CTest, HANDLER_OUTPUT, "-- " << m << std::endl); + } + }); this->AddCTestCommand("ctest_build", new cmCTestBuildCommand); this->AddCTestCommand("ctest_configure", new cmCTestConfigureCommand); @@ -330,7 +331,7 @@ int cmCTestScriptHandler::ReadInScript(const std::string& total_script_arg) } // make sure the file exists if (!cmSystemTools::FileExists(script)) { - cmSystemTools::Error("Cannot find file: ", script.c_str()); + cmSystemTools::Error("Cannot find file: " + script); return 1; } @@ -470,8 +471,8 @@ int cmCTestScriptHandler::ExtractVariables() msg += "\nCTEST_COMMAND = "; msg += (!this->CTestCmd.empty()) ? this->CTestCmd.c_str() : "(Null)"; cmSystemTools::Error( - "Some required settings in the configuration file were missing:\n", - msg.c_str()); + "Some required settings in the configuration file were missing:\n" + + msg); return 4; } @@ -607,12 +608,10 @@ int cmCTestScriptHandler::CheckOutSourceDir() cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run cvs: " << this->CVSCheckOut << std::endl); res = cmSystemTools::RunSingleCommand( - this->CVSCheckOut.c_str(), &output, &output, &retVal, - this->CTestRoot.c_str(), this->HandlerVerbose, - cmDuration::zero() /*this->TimeOut*/); + this->CVSCheckOut, &output, &output, &retVal, this->CTestRoot.c_str(), + this->HandlerVerbose, cmDuration::zero() /*this->TimeOut*/); if (!res || retVal != 0) { - cmSystemTools::Error("Unable to perform cvs checkout:\n", - output.c_str()); + cmSystemTools::Error("Unable to perform cvs checkout:\n" + output); return 6; } } @@ -675,11 +674,11 @@ int cmCTestScriptHandler::PerformExtraUpdates() cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run Update: " << fullCommand << std::endl); res = cmSystemTools::RunSingleCommand( - fullCommand.c_str(), &output, &output, &retVal, cvsArgs[0].c_str(), + fullCommand, &output, &output, &retVal, cvsArgs[0].c_str(), this->HandlerVerbose, cmDuration::zero() /*this->TimeOut*/); if (!res || retVal != 0) { - cmSystemTools::Error("Unable to perform extra updates:\n", eu.c_str(), - "\nWith output:\n", output.c_str()); + cmSystemTools::Error("Unable to perform extra updates:\n" + eu + + "\nWith output:\n" + output); return 0; } } @@ -721,8 +720,8 @@ int cmCTestScriptHandler::RunConfigurationDashboard() if (!cmSystemTools::FileExists(this->BinaryDir) && this->SourceDir != this->BinaryDir) { if (!cmSystemTools::MakeDirectory(this->BinaryDir)) { - cmSystemTools::Error("Unable to create the binary directory:\n", - this->BinaryDir.c_str()); + cmSystemTools::Error("Unable to create the binary directory:\n" + + this->BinaryDir); this->RestoreBackupDirectories(); return 7; } @@ -779,7 +778,7 @@ int cmCTestScriptHandler::RunConfigurationDashboard() cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run cmake command: " << command << std::endl); res = cmSystemTools::RunSingleCommand( - command.c_str(), &output, &output, &retVal, this->BinaryDir.c_str(), + command, &output, &output, &retVal, this->BinaryDir.c_str(), this->HandlerVerbose, cmDuration::zero() /*this->TimeOut*/); if (!this->CMOutFile.empty()) { @@ -818,7 +817,7 @@ int cmCTestScriptHandler::RunConfigurationDashboard() cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run ctest command: " << command << std::endl); res = cmSystemTools::RunSingleCommand( - command.c_str(), &output, &output, &retVal, this->BinaryDir.c_str(), + command, &output, &output, &retVal, this->BinaryDir.c_str(), this->HandlerVerbose, cmDuration::zero() /*this->TimeOut*/); // did something critical fail in ctest diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx index 00c0610..afc3e67 100644 --- a/Source/CTest/cmCTestSubmitCommand.cxx +++ b/Source/CTest/cmCTestSubmitCommand.cxx @@ -3,7 +3,6 @@ #include "cmCTestSubmitCommand.h" #include "cmCTest.h" -#include "cmCTestGenericHandler.h" #include "cmCTestSubmitHandler.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -13,6 +12,29 @@ class cmExecutionStatus; +cmCTestSubmitCommand::cmCTestSubmitCommand() +{ + this->PartsMentioned = false; + this->FilesMentioned = false; + this->InternalTest = false; + this->RetryCount = ""; + this->RetryDelay = ""; + this->CDashUpload = false; + this->Arguments[cts_BUILD_ID] = "BUILD_ID"; + this->Last = cts_LAST; +} + +/** + * This is a virtual constructor for the command. + */ +cmCommand* cmCTestSubmitCommand::Clone() +{ + cmCTestSubmitCommand* ni = new cmCTestSubmitCommand; + ni->CTest = this->CTest; + ni->CTestScriptHandler = this->CTestScriptHandler; + return ni; +} + cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() { const char* submitURL = !this->SubmitURL.empty() @@ -42,33 +64,23 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() this->Makefile->GetDefinition("CTEST_NOTES_FILES"); if (notesFilesVariable) { std::vector<std::string> notesFiles; - cmCTest::VectorOfStrings newNotesFiles; cmSystemTools::ExpandListArgument(notesFilesVariable, notesFiles); - newNotesFiles.insert(newNotesFiles.end(), notesFiles.begin(), - notesFiles.end()); - this->CTest->GenerateNotesFile(newNotesFiles); + this->CTest->GenerateNotesFile(notesFiles); } const char* extraFilesVariable = this->Makefile->GetDefinition("CTEST_EXTRA_SUBMIT_FILES"); if (extraFilesVariable) { std::vector<std::string> extraFiles; - cmCTest::VectorOfStrings newExtraFiles; cmSystemTools::ExpandListArgument(extraFilesVariable, extraFiles); - newExtraFiles.insert(newExtraFiles.end(), extraFiles.begin(), - extraFiles.end()); - if (!this->CTest->SubmitExtraFiles(newExtraFiles)) { + if (!this->CTest->SubmitExtraFiles(extraFiles)) { this->SetError("problem submitting extra files."); return nullptr; } } - cmCTestGenericHandler* handler = - this->CTest->GetInitializedHandler("submit"); - if (!handler) { - this->SetError("internal CTest error. Cannot instantiate submit handler"); - return nullptr; - } + cmCTestSubmitHandler* handler = this->CTest->GetSubmitHandler(); + handler->Initialize(); // If no FILES or PARTS given, *all* PARTS are submitted by default. // @@ -90,38 +102,30 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // But FILES with no PARTS mentioned should just submit the FILES // without any of the default parts. // - std::set<cmCTest::Part> noParts; - static_cast<cmCTestSubmitHandler*>(handler)->SelectParts(noParts); - - static_cast<cmCTestSubmitHandler*>(handler)->SelectFiles(this->Files); + handler->SelectParts(std::set<cmCTest::Part>()); + handler->SelectFiles(this->Files); } // If a PARTS option was given, select only the named parts for submission. // if (this->PartsMentioned) { - static_cast<cmCTestSubmitHandler*>(handler)->SelectParts(this->Parts); + handler->SelectParts(this->Parts); } // Pass along any HTTPHEADER to the handler if this option was given. if (!this->HttpHeaders.empty()) { - static_cast<cmCTestSubmitHandler*>(handler)->SetHttpHeaders( - this->HttpHeaders); + handler->SetHttpHeaders(this->HttpHeaders); } - static_cast<cmCTestSubmitHandler*>(handler)->SetOption( - "RetryDelay", this->RetryDelay.c_str()); - static_cast<cmCTestSubmitHandler*>(handler)->SetOption( - "RetryCount", this->RetryCount.c_str()); - static_cast<cmCTestSubmitHandler*>(handler)->SetOption( - "InternalTest", this->InternalTest ? "ON" : "OFF"); + handler->SetOption("RetryDelay", this->RetryDelay.c_str()); + handler->SetOption("RetryCount", this->RetryCount.c_str()); + handler->SetOption("InternalTest", this->InternalTest ? "ON" : "OFF"); handler->SetQuiet(this->Quiet); if (this->CDashUpload) { - static_cast<cmCTestSubmitHandler*>(handler)->SetOption( - "CDashUploadFile", this->CDashUploadFile.c_str()); - static_cast<cmCTestSubmitHandler*>(handler)->SetOption( - "CDashUploadType", this->CDashUploadType.c_str()); + handler->SetOption("CDashUploadFile", this->CDashUploadFile.c_str()); + handler->SetOption("CDashUploadType", this->CDashUploadType.c_str()); } return handler; } @@ -130,7 +134,15 @@ bool cmCTestSubmitCommand::InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) { this->CDashUpload = !args.empty() && args[0] == "CDASH_UPLOAD"; - return this->cmCTestHandlerCommand::InitialPass(args, status); + + bool ret = this->cmCTestHandlerCommand::InitialPass(args, status); + + if (this->Values[cts_BUILD_ID] && *this->Values[cts_BUILD_ID]) { + this->Makefile->AddDefinition(this->Values[cts_BUILD_ID], + this->CTest->GetBuildID().c_str()); + } + + return ret; } bool cmCTestSubmitCommand::CheckArgumentKeyword(std::string const& arg) diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h index 0caccd6..1e27046 100644 --- a/Source/CTest/cmCTestSubmitCommand.h +++ b/Source/CTest/cmCTestSubmitCommand.h @@ -25,26 +25,8 @@ class cmExecutionStatus; class cmCTestSubmitCommand : public cmCTestHandlerCommand { public: - cmCTestSubmitCommand() - { - this->PartsMentioned = false; - this->FilesMentioned = false; - this->InternalTest = false; - this->RetryCount = ""; - this->RetryDelay = ""; - this->CDashUpload = false; - } - - /** - * This is a virtual constructor for the command. - */ - cmCommand* Clone() override - { - cmCTestSubmitCommand* ni = new cmCTestSubmitCommand; - ni->CTest = this->CTest; - ni->CTestScriptHandler = this->CTestScriptHandler; - return ni; - } + cmCTestSubmitCommand(); + cmCommand* Clone() override; bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) override; @@ -75,11 +57,17 @@ protected: ArgumentDoingLast2 }; + enum + { + cts_BUILD_ID = ct_LAST, + cts_LAST + }; + bool PartsMentioned; std::set<cmCTest::Part> Parts; bool FilesMentioned; bool InternalTest; - cmCTest::SetOfStrings Files; + std::set<std::string> Files; std::string RetryCount; std::string RetryDelay; bool CDashUpload; diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index 87112da..2b54365 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -259,8 +259,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( upload_as += ctest_curl.Escape(this->CTest->GetCurrentTag()); upload_as += "-"; upload_as += ctest_curl.Escape(this->CTest->GetTestModelString()); - cmCTestScriptHandler* ch = static_cast<cmCTestScriptHandler*>( - this->CTest->GetHandler("script")); + cmCTestScriptHandler* ch = this->CTest->GetScriptHandler(); cmake* cm = ch->GetCMake(); if (cm) { const char* subproject = @@ -343,7 +342,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( if (!chunk.empty()) { cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) + << cmCTestLogWrite(chunk.data(), chunk.size()) << "]" << std::endl, this->Quiet); this->ParseResponse(chunk); @@ -352,7 +351,7 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( cmCTestOptionalLog( this->CTest, DEBUG, "CURL debug output: [" - << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]" + << cmCTestLogWrite(chunkDebug.data(), chunkDebug.size()) << "]" << std::endl, this->Quiet); } @@ -404,12 +403,11 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( res = ::curl_easy_perform(curl); if (!chunk.empty()) { - cmCTestOptionalLog( - this->CTest, DEBUG, - "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]" - << std::endl, - this->Quiet); + cmCTestOptionalLog(this->CTest, DEBUG, + "CURL output: [" + << cmCTestLogWrite(chunk.data(), chunk.size()) + << "]" << std::endl, + this->Quiet); this->ParseResponse(chunk); } @@ -433,11 +431,11 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP( // avoid deref of begin for zero size array if (!chunk.empty()) { *this->LogFile << " Curl output was: " - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) + << cmCTestLogWrite(chunk.data(), chunk.size()) << std::endl; cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: [" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]" + << cmCTestLogWrite(chunk.data(), chunk.size()) << "]" << std::endl); } ::curl_easy_cleanup(curl); @@ -486,7 +484,7 @@ void cmCTestSubmitHandler::ParseResponse( if (this->HasWarnings || this->HasErrors) { cmCTestLog(this->CTest, HANDLER_OUTPUT, " Server Response:\n" - << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "\n"); + << cmCTestLogWrite(chunk.data(), chunk.size()) << "\n"); } } @@ -559,8 +557,7 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, // has already been uploaded // TODO I added support for subproject. You would need to add // a "&subproject=subprojectname" to the first POST. - cmCTestScriptHandler* ch = - static_cast<cmCTestScriptHandler*>(this->CTest->GetHandler("script")); + cmCTestScriptHandler* ch = this->CTest->GetScriptHandler(); cmake* cm = ch->GetCMake(); const char* subproject = cm->GetState()->GetGlobalProperty("SubProject"); // TODO: Encode values for a URL instead of trusting caller. @@ -903,7 +900,7 @@ void cmCTestSubmitHandler::SelectParts(std::set<cmCTest::Part> const& parts) } } -void cmCTestSubmitHandler::SelectFiles(cmCTest::SetOfStrings const& files) +void cmCTestSubmitHandler::SelectFiles(std::set<std::string> const& files) { this->Files.insert(files.begin(), files.end()); } diff --git a/Source/CTest/cmCTestSubmitHandler.h b/Source/CTest/cmCTestSubmitHandler.h index 58f4f97..e0fed10 100644 --- a/Source/CTest/cmCTestSubmitHandler.h +++ b/Source/CTest/cmCTestSubmitHandler.h @@ -38,7 +38,7 @@ public: void SelectParts(std::set<cmCTest::Part> const& parts); /** Specify a set of files to submit. */ - void SelectFiles(cmCTest::SetOfStrings const& files); + void SelectFiles(std::set<std::string> const& files); // handle the cdash file upload protocol int HandleCDashUploadFile(std::string const& file, std::string const& type); @@ -74,7 +74,7 @@ private: bool SubmitPart[cmCTest::PartCount]; bool HasWarnings; bool HasErrors; - cmCTest::SetOfStrings Files; + std::set<std::string> Files; std::vector<std::string> HttpHeaders; }; diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx index 895ca12..cfd5e3d 100644 --- a/Source/CTest/cmCTestTestCommand.cxx +++ b/Source/CTest/cmCTestTestCommand.cxx @@ -4,6 +4,7 @@ #include "cmCTest.h" #include "cmCTestGenericHandler.h" +#include "cmCTestTestHandler.h" #include "cmDuration.h" #include "cmMakefile.h" #include "cmSystemTools.h" @@ -140,5 +141,7 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler() cmCTestGenericHandler* cmCTestTestCommand::InitializeActualHandler() { - return this->CTest->GetInitializedHandler("test"); + cmCTestTestHandler* handler = this->CTest->GetTestHandler(); + handler->Initialize(); + return handler; } diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index cf2652a..0ed56c8 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -1486,7 +1486,7 @@ int cmCTestTestHandler::ExecuteCommands(std::vector<std::string>& vec) int retVal = 0; cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Run command: " << it << std::endl, this->Quiet); - if (!cmSystemTools::RunSingleCommand(it.c_str(), nullptr, nullptr, &retVal, + if (!cmSystemTools::RunSingleCommand(it, nullptr, nullptr, &retVal, nullptr, cmSystemTools::OUTPUT_MERGE /*this->Verbose*/) || retVal != 0) { @@ -2265,8 +2265,8 @@ bool cmCTestTestHandler::SetTestsProperties( size_t pos = val.find_first_of('='); if (pos != std::string::npos) { std::string mKey = val.substr(0, pos); - const char* mVal = val.c_str() + pos + 1; - rt.Measurements[mKey] = mVal; + std::string mVal = val.substr(pos + 1); + rt.Measurements[mKey] = std::move(mVal); } else { rt.Measurements[val] = "1"; } diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index 0b557db..7f3f5e4 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -11,6 +11,7 @@ #include "cmsys/RegularExpression.hxx" #include <chrono> +#include <cstdint> #include <iosfwd> #include <map> #include <set> @@ -58,7 +59,7 @@ public: */ void PopulateCustomVectors(cmMakefile* mf) override; - ///! Control the use of the regular expresisons, call these methods to turn + //! Control the use of the regular expresisons, call these methods to turn /// them on void UseIncludeRegExp(); void UseExcludeRegExp(); @@ -77,7 +78,7 @@ public: this->CustomMaximumFailedTestOutputSize = n; } - ///! pass the -I argument down + //! pass the -I argument down void SetTestsToRunInformation(const char*); cmCTestTestHandler(); @@ -153,7 +154,7 @@ public: std::string Reason; std::string FullCommandLine; cmDuration ExecutionTime; - int ReturnValue; + std::int64_t ReturnValue; int Status; std::string ExceptionStatus; bool CompressOutput; diff --git a/Source/CTest/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx index 3d800f8..a2f1462 100644 --- a/Source/CTest/cmCTestUpdateCommand.cxx +++ b/Source/CTest/cmCTestUpdateCommand.cxx @@ -3,7 +3,7 @@ #include "cmCTestUpdateCommand.h" #include "cmCTest.h" -#include "cmCTestGenericHandler.h" +#include "cmCTestUpdateHandler.h" #include "cmMakefile.h" #include "cmSystemTools.h" @@ -74,12 +74,8 @@ cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler() this->CTest->SetCTestConfigurationFromCMakeVariable( this->Makefile, "P4Options", "CTEST_P4_OPTIONS", this->Quiet); - cmCTestGenericHandler* handler = - this->CTest->GetInitializedHandler("update"); - if (!handler) { - this->SetError("internal CTest error. Cannot instantiate update handler"); - return nullptr; - } + cmCTestUpdateHandler* handler = this->CTest->GetUpdateHandler(); + handler->Initialize(); handler->SetCommand(this); if (source_dir.empty()) { this->SetError("source directory not specified. Please use SOURCE tag"); diff --git a/Source/CTest/cmCTestUpdateHandler.cxx b/Source/CTest/cmCTestUpdateHandler.cxx index e3b7e9e..5cfc4a7 100644 --- a/Source/CTest/cmCTestUpdateHandler.cxx +++ b/Source/CTest/cmCTestUpdateHandler.cxx @@ -199,6 +199,10 @@ int cmCTestUpdateHandler::ProcessHandler() xml.Element("UpdateCommand", vc->GetUpdateCommandLine()); xml.Element("UpdateType", cmCTestUpdateHandlerUpdateToString(this->UpdateType)); + std::string changeId = this->CTest->GetCTestConfiguration("ChangeId"); + if (!changeId.empty()) { + xml.Element("ChangeId", changeId); + } bool loadedMods = vc->WriteXML(xml); diff --git a/Source/CTest/cmCTestUploadCommand.cxx b/Source/CTest/cmCTestUploadCommand.cxx index 2fe2cd3..59fbf37 100644 --- a/Source/CTest/cmCTestUploadCommand.cxx +++ b/Source/CTest/cmCTestUploadCommand.cxx @@ -6,7 +6,6 @@ #include <vector> #include "cmCTest.h" -#include "cmCTestGenericHandler.h" #include "cmCTestUploadHandler.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -14,14 +13,9 @@ cmCTestGenericHandler* cmCTestUploadCommand::InitializeHandler() { - cmCTestGenericHandler* handler = - this->CTest->GetInitializedHandler("upload"); - if (!handler) { - this->SetError("internal CTest error. Cannot instantiate upload handler"); - return nullptr; - } - static_cast<cmCTestUploadHandler*>(handler)->SetFiles(this->Files); - + cmCTestUploadHandler* handler = this->CTest->GetUploadHandler(); + handler->Initialize(); + handler->SetFiles(this->Files); handler->SetQuiet(this->Quiet); return handler; } diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h index 61bf1cc..0d3b06e 100644 --- a/Source/CTest/cmCTestUploadCommand.h +++ b/Source/CTest/cmCTestUploadCommand.h @@ -5,9 +5,9 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmCTest.h" #include "cmCTestHandlerCommand.h" +#include <set> #include <string> class cmCTestGenericHandler; @@ -22,8 +22,6 @@ class cmCommand; class cmCTestUploadCommand : public cmCTestHandlerCommand { public: - cmCTestUploadCommand() {} - /** * This is a virtual constructor for the command. */ @@ -55,7 +53,7 @@ protected: ArgumentDoingLast2 }; - cmCTest::SetOfStrings Files; + std::set<std::string> Files; }; #endif diff --git a/Source/CTest/cmCTestUploadHandler.cxx b/Source/CTest/cmCTestUploadHandler.cxx index 261ecab..9efdf70 100644 --- a/Source/CTest/cmCTestUploadHandler.cxx +++ b/Source/CTest/cmCTestUploadHandler.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestUploadHandler.h" +#include "cmCTest.h" #include "cmGeneratedFileStream.h" #include "cmVersion.h" #include "cmXMLWriter.h" @@ -20,7 +21,7 @@ void cmCTestUploadHandler::Initialize() this->Files.clear(); } -void cmCTestUploadHandler::SetFiles(const cmCTest::SetOfStrings& files) +void cmCTestUploadHandler::SetFiles(std::set<std::string> const& files) { this->Files = files; } @@ -50,7 +51,7 @@ int cmCTestUploadHandler::ProcessHandler() this->CTest->GetTestModelString()); xml.Attribute("Name", this->CTest->GetCTestConfiguration("Site")); xml.Attribute("Generator", - std::string("ctest") + cmVersion::GetCMakeVersion()); + std::string("ctest-") + cmVersion::GetCMakeVersion()); this->CTest->AddSiteProperties(xml); xml.StartElement("Upload"); diff --git a/Source/CTest/cmCTestUploadHandler.h b/Source/CTest/cmCTestUploadHandler.h index ff50574..4d8fab4 100644 --- a/Source/CTest/cmCTestUploadHandler.h +++ b/Source/CTest/cmCTestUploadHandler.h @@ -5,9 +5,11 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmCTest.h" #include "cmCTestGenericHandler.h" +#include <set> +#include <string> + /** \class cmCTestUploadHandler * \brief Helper class for CTest * @@ -20,7 +22,6 @@ public: typedef cmCTestGenericHandler Superclass; cmCTestUploadHandler(); - ~cmCTestUploadHandler() override {} /* * The main entry point for this class @@ -30,10 +31,10 @@ public: void Initialize() override; /** Specify a set of files to submit. */ - void SetFiles(cmCTest::SetOfStrings const& files); + void SetFiles(std::set<std::string> const& files); private: - cmCTest::SetOfStrings Files; + std::set<std::string> Files; }; #endif diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx index 70ef8df..a2c30bb 100644 --- a/Source/CTest/cmProcess.cxx +++ b/Source/CTest/cmProcess.cxx @@ -5,59 +5,19 @@ #include "cmCTest.h" #include "cmCTestRunTest.h" #include "cmCTestTestHandler.h" +#include "cmGetPipes.h" #include "cmsys/Process.h" -#include <fcntl.h> #include <iostream> #include <signal.h> #include <string> -#if !defined(_WIN32) -# include <unistd.h> +#if defined(_WIN32) +# include "cm_kwiml.h" #endif #include <utility> #define CM_PROCESS_BUF_SIZE 65536 -#if defined(_WIN32) && !defined(__CYGWIN__) -# include <io.h> - -static int cmProcessGetPipes(int* fds) -{ - SECURITY_ATTRIBUTES attr; - HANDLE readh, writeh; - attr.nLength = sizeof(attr); - attr.lpSecurityDescriptor = nullptr; - attr.bInheritHandle = FALSE; - if (!CreatePipe(&readh, &writeh, &attr, 0)) - return uv_translate_sys_error(GetLastError()); - fds[0] = _open_osfhandle((intptr_t)readh, 0); - fds[1] = _open_osfhandle((intptr_t)writeh, 0); - if (fds[0] == -1 || fds[1] == -1) { - CloseHandle(readh); - CloseHandle(writeh); - return uv_translate_sys_error(GetLastError()); - } - return 0; -} -#else -# include <errno.h> - -static int cmProcessGetPipes(int* fds) -{ - if (pipe(fds) == -1) { - return uv_translate_sys_error(errno); - } - - if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || - fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { - close(fds[0]); - close(fds[1]); - return uv_translate_sys_error(errno); - } - return 0; -} -#endif - cmProcess::cmProcess(cmCTestRunTest& runner) : Runner(runner) , Conv(cmProcessOutput::UTF8, CM_PROCESS_BUF_SIZE) @@ -71,7 +31,7 @@ cmProcess::cmProcess(cmCTestRunTest& runner) cmProcess::~cmProcess() = default; -void cmProcess::SetCommand(const char* command) +void cmProcess::SetCommand(std::string const& command) { this->Command = command; } @@ -81,6 +41,11 @@ void cmProcess::SetCommandArguments(std::vector<std::string> const& args) this->Arguments = args; } +void cmProcess::SetWorkingDirectory(std::string const& dir) +{ + this->WorkingDirectory = dir; +} + bool cmProcess::StartProcess(uv_loop_t& loop, std::vector<size_t>* affinity) { this->ProcessState = cmProcess::State::Error; @@ -113,7 +78,7 @@ bool cmProcess::StartProcess(uv_loop_t& loop, std::vector<size_t>* affinity) pipe_reader.init(loop, 0, this); int fds[2] = { -1, -1 }; - status = cmProcessGetPipes(fds); + status = cmGetPipes(fds); if (status != 0) { cmCTestLog(this->Runner.GetCTest(), ERROR_MESSAGE, "Error initializing pipe: " << uv_strerror(status) @@ -199,7 +164,7 @@ bool cmProcess::Buffer::GetLine(std::string& line) for (size_type sz = this->size(); this->Last != sz; ++this->Last) { if ((*this)[this->Last] == '\n' || (*this)[this->Last] == '\0') { // Extract the range first..last as a line. - const char* text = &*this->begin() + this->First; + const char* text = this->data() + this->First; size_type length = this->Last - this->First; while (length && text[length - 1] == '\r') { length--; @@ -229,7 +194,7 @@ bool cmProcess::Buffer::GetLast(std::string& line) { // Return the partial last line, if any. if (!this->empty()) { - line.assign(&*this->begin(), this->size()); + line.assign(this->data(), this->size()); this->First = this->Last = 0; this->clear(); return true; @@ -353,7 +318,7 @@ void cmProcess::OnExit(int64_t exit_status, int term_signal) } // Record exit information. - this->ExitValue = static_cast<int>(exit_status); + this->ExitValue = exit_status; this->Signal = term_signal; this->TotalTime = std::chrono::steady_clock::now() - this->StartTime; // Because of a processor clock scew the runtime may become slightly @@ -539,7 +504,8 @@ std::string cmProcess::GetExitExceptionString() case STATUS_NO_MEMORY: default: char buf[1024]; - _snprintf(buf, 1024, "Exit code 0x%x\n", this->ExitValue); + const char* fmt = "Exit code 0x%" KWIML_INT_PRIx64 "\n"; + _snprintf(buf, 1024, fmt, this->ExitValue); exception_str.assign(buf); } #else diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h index b2d87fa..a0a4b6b 100644 --- a/Source/CTest/cmProcess.h +++ b/Source/CTest/cmProcess.h @@ -28,10 +28,9 @@ class cmProcess public: explicit cmProcess(cmCTestRunTest& runner); ~cmProcess(); - const char* GetCommand() { return this->Command.c_str(); } - void SetCommand(const char* command); + void SetCommand(std::string const& command); void SetCommandArguments(std::vector<std::string> const& arg); - void SetWorkingDirectory(const char* dir) { this->WorkingDirectory = dir; } + void SetWorkingDirectory(std::string const& dir); void SetTimeout(cmDuration t) { this->Timeout = t; } void ChangeTimeout(cmDuration t); void ResetStartTime(); @@ -53,7 +52,7 @@ public: State GetProcessStatus(); int GetId() { return this->Id; } void SetId(int id) { this->Id = id; } - int GetExitValue() { return this->ExitValue; } + int64_t GetExitValue() { return this->ExitValue; } cmDuration GetTotalTime() { return this->TotalTime; } enum class Exception @@ -122,7 +121,7 @@ private: std::vector<std::string> Arguments; std::vector<const char*> ProcessArgs; int Id; - int ExitValue; + int64_t ExitValue; }; #endif diff --git a/Source/Checks/cm_cxx17_check.cpp b/Source/Checks/cm_cxx17_check.cpp index 9ea52c4..593d9b2 100644 --- a/Source/Checks/cm_cxx17_check.cpp +++ b/Source/Checks/cm_cxx17_check.cpp @@ -3,6 +3,10 @@ #include <memory> #include <unordered_map> +#ifdef _MSC_VER +# include <comdef.h> +#endif + int main() { int a[] = { 0, 1, 2 }; @@ -14,5 +18,14 @@ int main() auto ci = std::size(a); std::unique_ptr<int> u(new int(0)); + +#ifdef _MSC_VER + // clang-cl has problems instantiating this constructor in C++17 mode + // error: indirection requires pointer operand ('const _GUID' invalid) + // return *_IID; + IUnknownPtr ptr{}; + IDispatchPtr disp(ptr); +#endif + return *u + *ai + *(bi - 1) + (3 - static_cast<int>(ci)); } diff --git a/Source/CursesDialog/ccmake.cxx b/Source/CursesDialog/ccmake.cxx index f2982a6..7caed0c 100644 --- a/Source/CursesDialog/ccmake.cxx +++ b/Source/CursesDialog/ccmake.cxx @@ -67,6 +67,7 @@ void onsig(int /*unused*/) int main(int argc, char const* const* argv) { + cmSystemTools::EnsureStdPipes(); cmsys::Encoding::CommandLineArguments encoding_args = cmsys::Encoding::CommandLineArguments::Main(argc, argv); argc = encoding_args.argc(); @@ -150,7 +151,7 @@ int main(int argc, char const* const* argv) } cmSystemTools::SetMessageCallback( - [myform](const char* message, const char* title) { + [myform](const std::string& message, const char* title) { myform->AddError(message, title); }); diff --git a/Source/CursesDialog/cmCursesForm.h b/Source/CursesDialog/cmCursesForm.h index ddb67de..7842905 100644 --- a/Source/CursesDialog/cmCursesForm.h +++ b/Source/CursesDialog/cmCursesForm.h @@ -9,6 +9,8 @@ #include "cmsys/FStream.hxx" +#include <string> + class cmCursesForm { public: @@ -34,7 +36,7 @@ public: // Description: // During a CMake run, an error handle should add errors // to be displayed afterwards. - virtual void AddError(const char*, const char*) {} + virtual void AddError(const std::string&, const char*) {} // Description: // Turn debugging on. This will create ccmakelog.txt. diff --git a/Source/CursesDialog/cmCursesLongMessageForm.cxx b/Source/CursesDialog/cmCursesLongMessageForm.cxx index a41d051..4e887d6 100644 --- a/Source/CursesDialog/cmCursesLongMessageForm.cxx +++ b/Source/CursesDialog/cmCursesLongMessageForm.cxx @@ -19,9 +19,8 @@ cmCursesLongMessageForm::cmCursesLongMessageForm( std::vector<std::string> const& messages, const char* title) { // Append all messages into on big string - std::vector<std::string>::const_iterator it; - for (it = messages.begin(); it != messages.end(); it++) { - this->Messages += (*it); + for (std::string const& message : messages) { + this->Messages += message; // Add one blank line after each message this->Messages += "\n\n"; } diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx index 8ca7802..028e852 100644 --- a/Source/CursesDialog/cmCursesMainForm.cxx +++ b/Source/CursesDialog/cmCursesMainForm.cxx @@ -79,18 +79,11 @@ cmCursesMainForm::~cmCursesMainForm() // See if a cache entry is in the list of entries in the ui. bool cmCursesMainForm::LookForCacheEntry(const std::string& key) { - if (!this->Entries) { - return false; - } - - std::vector<cmCursesCacheEntryComposite*>::iterator it; - for (it = this->Entries->begin(); it != this->Entries->end(); ++it) { - if (key == (*it)->Key) { - return true; - } - } - - return false; + return this->Entries && + std::any_of(this->Entries->begin(), this->Entries->end(), + [&key](cmCursesCacheEntryComposite* entry) { + return key == entry->Key; + }); } // Create new cmCursesCacheEntryComposite entries from the cache @@ -107,10 +100,9 @@ void cmCursesMainForm::InitializeUI() // Count non-internal and non-static entries int count = 0; - for (std::vector<std::string>::const_iterator it = cacheKeys.begin(); - it != cacheKeys.end(); ++it) { + for (std::string const& key : cacheKeys) { cmStateEnums::CacheEntryType t = - this->CMakeInstance->GetState()->GetCacheEntryType(*it); + this->CMakeInstance->GetState()->GetCacheEntryType(key); if (t != cmStateEnums::INTERNAL && t != cmStateEnums::STATIC && t != cmStateEnums::UNINITIALIZED) { ++count; @@ -130,11 +122,9 @@ void cmCursesMainForm::InitializeUI() // Create the composites. // First add entries which are new - for (std::vector<std::string>::const_iterator it = cacheKeys.begin(); - it != cacheKeys.end(); ++it) { - std::string key = *it; + for (std::string const& key : cacheKeys) { cmStateEnums::CacheEntryType t = - this->CMakeInstance->GetState()->GetCacheEntryType(*it); + this->CMakeInstance->GetState()->GetCacheEntryType(key); if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC || t == cmStateEnums::UNINITIALIZED) { continue; @@ -148,11 +138,9 @@ void cmCursesMainForm::InitializeUI() } // then add entries which are old - for (std::vector<std::string>::const_iterator it = cacheKeys.begin(); - it != cacheKeys.end(); ++it) { - std::string key = *it; + for (std::string const& key : cacheKeys) { cmStateEnums::CacheEntryType t = - this->CMakeInstance->GetState()->GetCacheEntryType(*it); + this->CMakeInstance->GetState()->GetCacheEntryType(key); if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC || t == cmStateEnums::UNINITIALIZED) { continue; @@ -190,13 +178,12 @@ void cmCursesMainForm::RePost() } else { // If normal mode, count only non-advanced entries this->NumberOfVisibleEntries = 0; - std::vector<cmCursesCacheEntryComposite*>::iterator it; - for (it = this->Entries->begin(); it != this->Entries->end(); ++it) { + for (cmCursesCacheEntryComposite* entry : *this->Entries) { const char* existingValue = - this->CMakeInstance->GetState()->GetCacheEntryValue((*it)->GetValue()); + this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue()); bool advanced = this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool( - (*it)->GetValue(), "ADVANCED"); + entry->GetValue(), "ADVANCED"); if (!existingValue || (!this->AdvancedMode && advanced)) { continue; } @@ -217,27 +204,26 @@ void cmCursesMainForm::RePost() // Assign fields int j = 0; - std::vector<cmCursesCacheEntryComposite*>::iterator it; - for (it = this->Entries->begin(); it != this->Entries->end(); ++it) { + for (cmCursesCacheEntryComposite* entry : *this->Entries) { const char* existingValue = - this->CMakeInstance->GetState()->GetCacheEntryValue((*it)->GetValue()); + this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue()); bool advanced = this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool( - (*it)->GetValue(), "ADVANCED"); + entry->GetValue(), "ADVANCED"); if (!existingValue || (!this->AdvancedMode && advanced)) { continue; } - this->Fields[3 * j] = (*it)->Label->Field; - this->Fields[3 * j + 1] = (*it)->IsNewLabel->Field; - this->Fields[3 * j + 2] = (*it)->Entry->Field; + this->Fields[3 * j] = entry->Label->Field; + this->Fields[3 * j + 1] = entry->IsNewLabel->Field; + this->Fields[3 * j + 2] = entry->Entry->Field; j++; } // if no cache entries there should still be one dummy field if (j == 0) { - it = this->Entries->begin(); - this->Fields[0] = (*it)->Label->Field; - this->Fields[1] = (*it)->IsNewLabel->Field; - this->Fields[2] = (*it)->Entry->Field; + const auto& front = *this->Entries->front(); + this->Fields[0] = front.Label->Field; + this->Fields[1] = front.IsNewLabel->Field; + this->Fields[2] = front.Entry->Field; this->NumberOfVisibleEntries = 1; } // Has to be null terminated. @@ -278,13 +264,12 @@ void cmCursesMainForm::Render(int left, int top, int width, int height) } else { // If normal, display only non-advanced entries this->NumberOfVisibleEntries = 0; - std::vector<cmCursesCacheEntryComposite*>::iterator it; - for (it = this->Entries->begin(); it != this->Entries->end(); ++it) { + for (cmCursesCacheEntryComposite* entry : *this->Entries) { const char* existingValue = - this->CMakeInstance->GetState()->GetCacheEntryValue((*it)->GetValue()); + this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue()); bool advanced = this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool( - (*it)->GetValue(), "ADVANCED"); + entry->GetValue(), "ADVANCED"); if (!existingValue || (!this->AdvancedMode && advanced)) { continue; } @@ -297,13 +282,12 @@ void cmCursesMainForm::Render(int left, int top, int width, int height) if (height > 0) { bool isNewPage; int i = 0; - std::vector<cmCursesCacheEntryComposite*>::iterator it; - for (it = this->Entries->begin(); it != this->Entries->end(); ++it) { + for (cmCursesCacheEntryComposite* entry : *this->Entries) { const char* existingValue = - this->CMakeInstance->GetState()->GetCacheEntryValue((*it)->GetValue()); + this->CMakeInstance->GetState()->GetCacheEntryValue(entry->GetValue()); bool advanced = this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool( - (*it)->GetValue(), "ADVANCED"); + entry->GetValue(), "ADVANCED"); if (!existingValue || (!this->AdvancedMode && advanced)) { continue; } @@ -314,10 +298,10 @@ void cmCursesMainForm::Render(int left, int top, int width, int height) if (isNewPage) { this->NumberOfPages++; } - (*it)->Label->Move(left, top + row - 1, isNewPage); - (*it)->IsNewLabel->Move(left + 32, top + row - 1, false); - (*it)->Entry->Move(left + 33, top + row - 1, false); - (*it)->Entry->SetPage(this->NumberOfPages); + entry->Label->Move(left, top + row - 1, isNewPage); + entry->IsNewLabel->Move(left + 32, top + row - 1, false); + entry->Entry->Move(left + 33, top + row - 1, false); + entry->Entry->SetPage(this->NumberOfPages); i++; } } @@ -506,14 +490,14 @@ void cmCursesMainForm::UpdateStatusBar(const char* message) pos_form_cursor(this->Form); } -void cmCursesMainForm::UpdateProgress(const char* msg, float prog) +void cmCursesMainForm::UpdateProgress(const std::string& msg, float prog) { char tmp[1024]; const char* cmsg = tmp; if (prog >= 0) { - sprintf(tmp, "%s %i%%", msg, static_cast<int>(100 * prog)); + sprintf(tmp, "%s %i%%", msg.c_str(), static_cast<int>(100 * prog)); } else { - cmsg = msg; + cmsg = msg.c_str(); } this->UpdateStatusBar(cmsg); this->PrintKeys(1); @@ -533,7 +517,9 @@ int cmCursesMainForm::Configure(int noconfigure) touchwin(stdscr); refresh(); this->CMakeInstance->SetProgressCallback( - [this](const char* msg, float prog) { this->UpdateProgress(msg, prog); }); + [this](const std::string& msg, float prog) { + this->UpdateProgress(msg, prog); + }); // always save the current gui values to disk this->FillCacheManagerFromUI(); @@ -603,7 +589,9 @@ int cmCursesMainForm::Generate() touchwin(stdscr); refresh(); this->CMakeInstance->SetProgressCallback( - [this](const char* msg, float prog) { this->UpdateProgress(msg, prog); }); + [this](const std::string& msg, float prog) { + this->UpdateProgress(msg, prog); + }); // Get rid of previous errors this->Errors = std::vector<std::string>(); @@ -647,7 +635,8 @@ int cmCursesMainForm::Generate() return 0; } -void cmCursesMainForm::AddError(const char* message, const char* /*unused*/) +void cmCursesMainForm::AddError(const std::string& message, + const char* /*unused*/) { this->Errors.emplace_back(message); } @@ -658,28 +647,29 @@ void cmCursesMainForm::RemoveEntry(const char* value) return; } - std::vector<cmCursesCacheEntryComposite*>::iterator it; - for (it = this->Entries->begin(); it != this->Entries->end(); ++it) { - const char* val = (*it)->GetValue(); - if (val && !strcmp(value, val)) { - this->CMakeInstance->UnwatchUnusedCli(value); - this->Entries->erase(it); - break; - } + auto removeIt = + std::find_if(this->Entries->begin(), this->Entries->end(), + [value](cmCursesCacheEntryComposite* entry) -> bool { + const char* val = entry->GetValue(); + return val != nullptr && !strcmp(value, val); + }); + + if (removeIt != this->Entries->end()) { + this->CMakeInstance->UnwatchUnusedCli(value); + this->Entries->erase(removeIt); } } // copy from the list box to the cache manager void cmCursesMainForm::FillCacheManagerFromUI() { - size_t size = this->Entries->size(); - for (size_t i = 0; i < size; i++) { - std::string cacheKey = (*this->Entries)[i]->Key; + for (cmCursesCacheEntryComposite* entry : *this->Entries) { + const std::string& cacheKey = entry->Key; const char* existingValue = this->CMakeInstance->GetState()->GetCacheEntryValue(cacheKey); if (existingValue) { std::string oldValue = existingValue; - std::string newValue = (*this->Entries)[i]->Entry->GetValue(); + std::string newValue = entry->Entry->GetValue(); std::string fixedOldValue; std::string fixedNewValue; cmStateEnums::CacheEntryType t = @@ -975,17 +965,14 @@ void cmCursesMainForm::HandleInput() if (nextCur) { // make the next or prev. current field after deletion - nextCur = nullptr; - std::vector<cmCursesCacheEntryComposite*>::iterator it; - for (it = this->Entries->begin(); it != this->Entries->end(); - ++it) { - if (nextVal == (*it)->Key) { - nextCur = (*it)->Entry->Field; - } - } - - if (nextCur) { - set_current_field(this->Form, nextCur); + auto nextEntryIt = + std::find_if(this->Entries->begin(), this->Entries->end(), + [&nextVal](cmCursesCacheEntryComposite* entry) { + return nextVal == entry->Key; + }); + + if (nextEntryIt != this->Entries->end()) { + set_current_field(this->Form, (*nextEntryIt)->Entry->Field); } } } diff --git a/Source/CursesDialog/cmCursesMainForm.h b/Source/CursesDialog/cmCursesMainForm.h index cc6482f..d379975 100644 --- a/Source/CursesDialog/cmCursesMainForm.h +++ b/Source/CursesDialog/cmCursesMainForm.h @@ -81,7 +81,7 @@ public: * During a CMake run, an error handle should add errors * to be displayed afterwards. */ - void AddError(const char* message, const char* title) override; + void AddError(const std::string& message, const char* title) override; /** * Used to do a configure. If argument is specified, it does only the check @@ -102,7 +102,7 @@ public: /** * Progress callback */ - void UpdateProgress(const char* msg, float prog); + void UpdateProgress(const std::string& msg, float prog); protected: // Copy the cache values from the user interface to the actual diff --git a/Source/LexerParser/cmCommandArgumentParser.cxx b/Source/LexerParser/cmCommandArgumentParser.cxx index 68b9e6c..b965b32 100644 --- a/Source/LexerParser/cmCommandArgumentParser.cxx +++ b/Source/LexerParser/cmCommandArgumentParser.cxx @@ -513,7 +513,7 @@ static const yytype_uint8 yyrline[] = static const char *const yytname[] = { "$end", "error", "$undefined", "cal_ENVCURLY", "cal_NCURLY", - "cal_DCURLY", "\"$\"", "\"{\"", "\"}\"", "cal_NAME", "\"\\\\\"", + "cal_DCURLY", "\"$\"", "\"{\"", "\"}\"", "cal_NAME", R"("\\")", "cal_SYMBOL", "\"@\"", "cal_ERROR", "cal_ATNAME", "$accept", "Start", "GoalWithOptionalBackSlash", "Goal", "String", "OuterText", "Variable", "EnvVarName", "MultipleIds", "ID", YY_NULLPTR diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 9ce0323..cb89d19 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -2,9 +2,6 @@ # file Copyright.txt or https://cmake.org/licensing for details. project(QtDialog) -if(POLICY CMP0020) - cmake_policy(SET CMP0020 NEW) # Drop when CMake >= 2.8.11 required -endif() CMake_OPTIONAL_COMPONENT(cmake-gui) find_package(Qt5Widgets QUIET) if (Qt5Widgets_FOUND) diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index 8d9a50c..c9ebba8 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx @@ -55,6 +55,7 @@ Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin); int main(int argc, char** argv) { + cmSystemTools::EnsureStdPipes(); cmsys::Encoding::CommandLineArguments encoding_args = cmsys::Encoding::CommandLineArguments::Main(argc, argv); int argc2 = encoding_args.argc(); diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx index f28e1a8..364a378 100644 --- a/Source/QtDialog/FirstConfigure.cxx +++ b/Source/QtDialog/FirstConfigure.cxx @@ -95,33 +95,32 @@ void StartCompilerSetup::setGenerators( QStringList generator_list; - std::vector<cmake::GeneratorInfo>::const_iterator it; - for (it = gens.begin(); it != gens.end(); ++it) { - generator_list.append(QString::fromLocal8Bit(it->name.c_str())); + for (cmake::GeneratorInfo const& gen : gens) { + generator_list.append(QString::fromLocal8Bit(gen.name.c_str())); - if (it->supportsPlatform) { + if (gen.supportsPlatform) { this->GeneratorsSupportingPlatform.append( - QString::fromLocal8Bit(it->name.c_str())); + QString::fromLocal8Bit(gen.name.c_str())); this - ->GeneratorDefaultPlatform[QString::fromLocal8Bit(it->name.c_str())] = - QString::fromLocal8Bit(it->defaultPlatform.c_str()); + ->GeneratorDefaultPlatform[QString::fromLocal8Bit(gen.name.c_str())] = + QString::fromLocal8Bit(gen.defaultPlatform.c_str()); std::vector<std::string>::const_iterator platformIt = - it->supportedPlatforms.cbegin(); - while (platformIt != it->supportedPlatforms.cend()) { + gen.supportedPlatforms.cbegin(); + while (platformIt != gen.supportedPlatforms.cend()) { this->GeneratorSupportedPlatforms.insert( - QString::fromLocal8Bit(it->name.c_str()), + QString::fromLocal8Bit(gen.name.c_str()), QString::fromLocal8Bit((*platformIt).c_str())); platformIt++; } } - if (it->supportsToolset) { + if (gen.supportsToolset) { this->GeneratorsSupportingToolset.append( - QString::fromLocal8Bit(it->name.c_str())); + QString::fromLocal8Bit(gen.name.c_str())); } } diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index a073c30..f357f90 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -25,7 +25,7 @@ QCMake::QCMake(QObject* p) cmSystemTools::SetRunCommandHideConsole(true); cmSystemTools::SetMessageCallback( - [this](const char* msg, const char* title) { + [this](std::string const& msg, const char* title) { this->messageCallback(msg, title); }); cmSystemTools::SetStdoutCallback( @@ -37,7 +37,7 @@ QCMake::QCMake(QObject* p) this->CMakeInstance->SetCMakeEditCommand( cmSystemTools::GetCMakeGUICommand()); this->CMakeInstance->SetProgressCallback( - [this](const char* msg, float percent) { + [this](const std::string& msg, float percent) { this->progressCallback(msg, percent); }); @@ -48,9 +48,8 @@ QCMake::QCMake(QObject* p) this->CMakeInstance->GetRegisteredGenerators( generators, /*includeNamesWithPlatform=*/false); - std::vector<cmake::GeneratorInfo>::const_iterator it; - for (it = generators.begin(); it != generators.end(); ++it) { - this->AvailableGenerators.push_back(*it); + for (cmake::GeneratorInfo const& gen : generators) { + this->AvailableGenerators.push_back(gen); } } @@ -234,24 +233,23 @@ void QCMake::setProperties(const QCMakePropertyList& newProps) // set the value of properties cmState* state = this->CMakeInstance->GetState(); std::vector<std::string> cacheKeys = state->GetCacheEntryKeys(); - for (std::vector<std::string>::const_iterator it = cacheKeys.begin(); - it != cacheKeys.end(); ++it) { - cmStateEnums::CacheEntryType t = state->GetCacheEntryType(*it); + for (std::string const& key : cacheKeys) { + cmStateEnums::CacheEntryType t = state->GetCacheEntryType(key); if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC) { continue; } QCMakeProperty prop; - prop.Key = QString::fromLocal8Bit(it->c_str()); + prop.Key = QString::fromLocal8Bit(key.c_str()); int idx = props.indexOf(prop); if (idx == -1) { - toremove.append(QString::fromLocal8Bit(it->c_str())); + toremove.append(QString::fromLocal8Bit(key.c_str())); } else { prop = props[idx]; if (prop.Value.type() == QVariant::Bool) { - state->SetCacheEntryValue(*it, prop.Value.toBool() ? "ON" : "OFF"); + state->SetCacheEntryValue(key, prop.Value.toBool() ? "ON" : "OFF"); } else { - state->SetCacheEntryValue(*it, + state->SetCacheEntryValue(key, prop.Value.toString().toLocal8Bit().data()); } props.removeAt(idx); @@ -297,22 +295,21 @@ QCMakePropertyList QCMake::properties() const cmState* state = this->CMakeInstance->GetState(); std::vector<std::string> cacheKeys = state->GetCacheEntryKeys(); - for (std::vector<std::string>::const_iterator i = cacheKeys.begin(); - i != cacheKeys.end(); ++i) { - cmStateEnums::CacheEntryType t = state->GetCacheEntryType(*i); + for (std::string const& key : cacheKeys) { + cmStateEnums::CacheEntryType t = state->GetCacheEntryType(key); if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC || t == cmStateEnums::UNINITIALIZED) { continue; } - const char* cachedValue = state->GetCacheEntryValue(*i); + const char* cachedValue = state->GetCacheEntryValue(key); QCMakeProperty prop; - prop.Key = QString::fromLocal8Bit(i->c_str()); + prop.Key = QString::fromLocal8Bit(key.c_str()); prop.Help = - QString::fromLocal8Bit(state->GetCacheEntryProperty(*i, "HELPSTRING")); + QString::fromLocal8Bit(state->GetCacheEntryProperty(key, "HELPSTRING")); prop.Value = QString::fromLocal8Bit(cachedValue); - prop.Advanced = state->GetCacheEntryPropertyAsBool(*i, "ADVANCED"); + prop.Advanced = state->GetCacheEntryPropertyAsBool(key, "ADVANCED"); if (t == cmStateEnums::BOOL) { prop.Type = QCMakeProperty::BOOL; prop.Value = cmSystemTools::IsOn(cachedValue); @@ -323,7 +320,7 @@ QCMakePropertyList QCMake::properties() const } else if (t == cmStateEnums::STRING) { prop.Type = QCMakeProperty::STRING; const char* stringsProperty = - state->GetCacheEntryProperty(*i, "STRINGS"); + state->GetCacheEntryProperty(key, "STRINGS"); if (stringsProperty) { prop.Strings = QString::fromLocal8Bit(stringsProperty).split(";"); } @@ -349,19 +346,19 @@ bool QCMake::interruptCallback() #endif } -void QCMake::progressCallback(const char* msg, float percent) +void QCMake::progressCallback(const std::string& msg, float percent) { if (percent >= 0) { - emit this->progressChanged(QString::fromLocal8Bit(msg), percent); + emit this->progressChanged(QString::fromStdString(msg), percent); } else { - emit this->outputMessage(QString::fromLocal8Bit(msg)); + emit this->outputMessage(QString::fromStdString(msg)); } QCoreApplication::processEvents(); } -void QCMake::messageCallback(const char* msg, const char* /*title*/) +void QCMake::messageCallback(std::string const& msg, const char* /*title*/) { - emit this->errorMessage(QString::fromLocal8Bit(msg)); + emit this->errorMessage(QString::fromStdString(msg)); QCoreApplication::processEvents(); } diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index ef4d2a1..f2fd6d9 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h @@ -168,8 +168,8 @@ protected: cmake* CMakeInstance; bool interruptCallback(); - void progressCallback(const char* msg, float percent); - void messageCallback(const char* msg, const char* title); + void progressCallback(std::string const& msg, float percent); + void messageCallback(std::string const& msg, const char* title); void stdoutCallback(std::string const& msg); void stderrCallback(std::string const& msg); diff --git a/Source/cmAddDependenciesCommand.cxx b/Source/cmAddDependenciesCommand.cxx index 021bd29..4956a47 100644 --- a/Source/cmAddDependenciesCommand.cxx +++ b/Source/cmAddDependenciesCommand.cxx @@ -6,6 +6,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" #include "cmTarget.h" class cmExecutionStatus; @@ -27,10 +28,10 @@ bool cmAddDependenciesCommand::InitialPass( this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); } if (cmTarget* target = this->Makefile->FindTargetToUse(target_name)) { - std::vector<std::string>::const_iterator s = args.begin(); - ++s; // skip over target_name - for (; s != args.end(); ++s) { - target->AddUtility(*s, this->Makefile); + + // skip over target_name + for (std::string const& arg : cmMakeRange(args).advance(1)) { + target->AddUtility(arg, this->Makefile); } } else { std::ostringstream e; diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx index 5149333..adf4464 100644 --- a/Source/cmAddLibraryCommand.cxx +++ b/Source/cmAddLibraryCommand.cxx @@ -222,7 +222,9 @@ bool cmAddLibraryCommand::InitialPass(std::vector<std::string> const& args, aliasedType != cmStateEnums::STATIC_LIBRARY && aliasedType != cmStateEnums::MODULE_LIBRARY && aliasedType != cmStateEnums::OBJECT_LIBRARY && - aliasedType != cmStateEnums::INTERFACE_LIBRARY) { + aliasedType != cmStateEnums::INTERFACE_LIBRARY && + !(aliasedType == cmStateEnums::UNKNOWN_LIBRARY && + aliasedTarget->IsImported())) { std::ostringstream e; e << "cannot create ALIAS target \"" << libName << "\" because target \"" << aliasedName << "\" is not a library."; diff --git a/Source/cmAddSubDirectoryCommand.cxx b/Source/cmAddSubDirectoryCommand.cxx index 75e5aa4..7947188 100644 --- a/Source/cmAddSubDirectoryCommand.cxx +++ b/Source/cmAddSubDirectoryCommand.cxx @@ -6,6 +6,7 @@ #include <string.h> #include "cmMakefile.h" +#include "cmRange.h" #include "cmSystemTools.h" class cmExecutionStatus; @@ -26,15 +27,13 @@ bool cmAddSubDirectoryCommand::InitialPass( bool excludeFromAll = false; // process the rest of the arguments looking for optional args - std::vector<std::string>::const_iterator i = args.begin(); - ++i; - for (; i != args.end(); ++i) { - if (*i == "EXCLUDE_FROM_ALL") { + for (std::string const& arg : cmMakeRange(args).advance(1)) { + if (arg == "EXCLUDE_FROM_ALL") { excludeFromAll = true; continue; } if (binArg.empty()) { - binArg = *i; + binArg = arg; } else { this->SetError("called with incorrect number of arguments"); return false; diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h index d38b0d1..0980416 100644 --- a/Source/cmAlgorithms.h +++ b/Source/cmAlgorithms.h @@ -5,6 +5,8 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include "cmRange.h" + #include "cm_kwiml.h" #include <algorithm> #include <functional> @@ -156,57 +158,12 @@ private: }; } -template <typename const_iterator_> -struct cmRange -{ - typedef const_iterator_ const_iterator; - typedef typename std::iterator_traits<const_iterator>::value_type value_type; - typedef typename std::iterator_traits<const_iterator>::difference_type - difference_type; - cmRange(const_iterator begin_, const_iterator end_) - : Begin(begin_) - , End(end_) - { - } - const_iterator begin() const { return Begin; } - const_iterator end() const { return End; } - bool empty() const { return std::distance(Begin, End) == 0; } - difference_type size() const { return std::distance(Begin, End); } - cmRange& advance(KWIML_INT_intptr_t amount) - { - std::advance(Begin, amount); - return *this; - } - - cmRange& retreat(KWIML_INT_intptr_t amount) - { - std::advance(End, -amount); - return *this; - } - -private: - const_iterator Begin; - const_iterator End; -}; - typedef cmRange<std::vector<std::string>::const_iterator> cmStringRange; class cmListFileBacktrace; typedef cmRange<std::vector<cmListFileBacktrace>::const_iterator> cmBacktraceRange; -template <typename Iter1, typename Iter2> -cmRange<Iter1> cmMakeRange(Iter1 begin, Iter2 end) -{ - return cmRange<Iter1>(begin, end); -} - -template <typename Range> -cmRange<typename Range::const_iterator> cmMakeRange(Range const& range) -{ - return cmRange<typename Range::const_iterator>(range.begin(), range.end()); -} - template <typename Range> void cmDeleteAll(Range const& r) { @@ -276,27 +233,45 @@ typename Range::const_iterator cmRemoveMatching(Range& r, MatchRange const& m) ContainerAlgorithms::BinarySearcher<MatchRange>(m)); } -template <typename Range> -typename Range::const_iterator cmRemoveDuplicates(Range& r) +template <typename ForwardIterator> +ForwardIterator cmRemoveDuplicates(ForwardIterator first, ForwardIterator last) { - typedef typename Range::value_type T; - std::unordered_set<T> unique; - std::vector<size_t> indices; - size_t count = 0; - const typename Range::const_iterator end = r.end(); - for (typename Range::const_iterator it = r.begin(); it != end; - ++it, ++count) { - const typename std::unordered_set<T>::iterator occur = unique.find(*it); - if (occur == unique.end()) { - unique.insert(*it); - } else { - indices.push_back(count); + using Value = typename std::iterator_traits<ForwardIterator>::value_type; + using Hash = struct + { + std::size_t operator()(ForwardIterator it) const + { + return std::hash<Value>{}(*it); } + }; + + using Equal = struct + { + bool operator()(ForwardIterator it1, ForwardIterator it2) const + { + return *it1 == *it2; + } + }; + std::unordered_set<ForwardIterator, Hash, Equal> uniq; + + ForwardIterator result = first; + while (first != last) { + if (uniq.find(first) == uniq.end()) { + if (result != first) { + *result = std::move(*first); + } + uniq.insert(result); + ++result; + } + ++first; } - if (indices.empty()) { - return end; - } - return cmRemoveIndices(r, indices); + return result; +} + +template <typename Range> +typename Range::const_iterator cmRemoveDuplicates(Range& r) +{ + return cmRemoveDuplicates(r.begin(), r.end()); } template <typename Range> @@ -322,14 +297,6 @@ typename Range::const_iterator cmFindNot(Range const& r, T const& t) return std::find_if(r.begin(), r.end(), [&t](T const& i) { return i != t; }); } -template <typename Range> -cmRange<typename Range::const_reverse_iterator> cmReverseRange( - Range const& range) -{ - return cmRange<typename Range::const_reverse_iterator>(range.rbegin(), - range.rend()); -} - template <class Iter> std::reverse_iterator<Iter> cmMakeReverseIterator(Iter it) { diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx index 6e5109a..177ba02 100644 --- a/Source/cmArchiveWrite.cxx +++ b/Source/cmArchiveWrite.cxx @@ -54,6 +54,8 @@ public: { } ~Entry() { archive_entry_free(this->Object); } + Entry(const Entry&) = delete; + Entry& operator=(const Entry&) = delete; operator struct archive_entry*() { return this->Object; } }; @@ -177,12 +179,10 @@ cmArchiveWrite::~cmArchiveWrite() bool cmArchiveWrite::Add(std::string path, size_t skip, const char* prefix, bool recursive) { - if (this->Okay()) { - if (!path.empty() && path.back() == '/') { - path.erase(path.size() - 1); - } - this->AddPath(path.c_str(), skip, prefix, recursive); + if (!path.empty() && path.back() == '/') { + path.erase(path.size() - 1); } + this->AddPath(path.c_str(), skip, prefix, recursive); return this->Okay(); } @@ -218,6 +218,7 @@ bool cmArchiveWrite::AddPath(const char* path, size_t skip, const char* prefix, bool cmArchiveWrite::AddFile(const char* file, size_t skip, const char* prefix) { + this->Error = ""; // Skip the file if we have no name for it. This may happen on a // top-level directory, which does not need to be included anyway. if (skip >= strlen(file)) { @@ -239,7 +240,7 @@ bool cmArchiveWrite::AddFile(const char* file, size_t skip, const char* prefix) cm_archive_entry_copy_pathname(e, dest); if (archive_read_disk_entry_from_file(this->Disk, e, -1, nullptr) != ARCHIVE_OK) { - this->Error = "archive_read_disk_entry_from_file '"; + this->Error = "Unable to read from file '"; this->Error += file; this->Error += "': "; this->Error += cm_archive_error_string(this->Disk); diff --git a/Source/cmArchiveWrite.h b/Source/cmArchiveWrite.h index 6ecdd63..1f23dae 100644 --- a/Source/cmArchiveWrite.h +++ b/Source/cmArchiveWrite.h @@ -58,6 +58,9 @@ public: ~cmArchiveWrite(); + cmArchiveWrite(const cmArchiveWrite&) = delete; + cmArchiveWrite& operator=(const cmArchiveWrite&) = delete; + /** * Add a path (file or directory) to the archive. Directories are * added recursively. The "path" must be readable on disk, either diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx new file mode 100644 index 0000000..751d117 --- /dev/null +++ b/Source/cmArgumentParser.cxx @@ -0,0 +1,93 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmArgumentParser.h" + +#include <algorithm> +#include <type_traits> + +namespace ArgumentParser { + +auto ActionMap::Emplace(cm::string_view name, Action action) + -> std::pair<iterator, bool> +{ + auto const it = + std::lower_bound(this->begin(), this->end(), name, + [](value_type const& elem, cm::string_view const& k) { + return elem.first < k; + }); + return (it != this->end() && it->first == name) + ? std::make_pair(it, false) + : std::make_pair(this->emplace(it, name, std::move(action)), true); +} + +auto ActionMap::Find(cm::string_view name) const -> const_iterator +{ + auto const it = + std::lower_bound(this->begin(), this->end(), name, + [](value_type const& elem, cm::string_view const& k) { + return elem.first < k; + }); + return (it != this->end() && it->first == name) ? it : this->end(); +} + +void Instance::Bind(bool& val) +{ + val = true; + this->CurrentString = nullptr; + this->CurrentList = nullptr; + this->ExpectValue = false; +} + +void Instance::Bind(std::string& val) +{ + this->CurrentString = &val; + this->CurrentList = nullptr; + this->ExpectValue = true; +} + +void Instance::Bind(StringList& val) +{ + this->CurrentString = nullptr; + this->CurrentList = &val; + this->ExpectValue = true; +} + +void Instance::Bind(MultiStringList& val) +{ + this->CurrentString = nullptr; + this->CurrentList = (static_cast<void>(val.emplace_back()), &val.back()); + this->ExpectValue = false; +} + +void Instance::Consume(cm::string_view arg, void* result, + std::vector<std::string>* unparsedArguments, + std::vector<std::string>* keywordsMissingValue) +{ + auto const it = this->Bindings.Find(arg); + if (it != this->Bindings.end()) { + it->second(*this, result); + if (this->ExpectValue && keywordsMissingValue != nullptr) { + keywordsMissingValue->emplace_back(arg); + } + return; + } + + if (this->CurrentString != nullptr) { + this->CurrentString->assign(std::string(arg)); + this->CurrentString = nullptr; + this->CurrentList = nullptr; + } else if (this->CurrentList != nullptr) { + this->CurrentList->emplace_back(arg); + } else if (unparsedArguments != nullptr) { + unparsedArguments->emplace_back(arg); + } + + if (this->ExpectValue) { + if (keywordsMissingValue != nullptr) { + keywordsMissingValue->pop_back(); + } + this->ExpectValue = false; + } +} + +} // namespace ArgumentParser diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h new file mode 100644 index 0000000..6cfe946 --- /dev/null +++ b/Source/cmArgumentParser.h @@ -0,0 +1,143 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmArgumentParser_h +#define cmArgumentParser_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cm_static_string_view.hxx" +#include "cm_string_view.hxx" + +#include <cassert> +#include <functional> +#include <string> +#include <utility> +#include <vector> + +namespace ArgumentParser { + +using StringList = std::vector<std::string>; +using MultiStringList = std::vector<StringList>; + +class Instance; +using Action = std::function<void(Instance&, void*)>; + +// using ActionMap = cm::flat_map<cm::string_view, Action>; +class ActionMap : public std::vector<std::pair<cm::string_view, Action>> +{ +public: + std::pair<iterator, bool> Emplace(cm::string_view name, Action action); + const_iterator Find(cm::string_view name) const; +}; + +class Instance +{ +public: + Instance(ActionMap const& bindings) + : Bindings(bindings) + { + } + + void Bind(bool& val); + void Bind(std::string& val); + void Bind(StringList& val); + void Bind(MultiStringList& val); + + void Consume(cm::string_view arg, void* result, + std::vector<std::string>* unparsedArguments, + std::vector<std::string>* keywordsMissingValue); + +private: + ActionMap const& Bindings; + std::string* CurrentString = nullptr; + StringList* CurrentList = nullptr; + bool ExpectValue = false; +}; + +} // namespace ArgumentParser + +template <typename Result> +class cmArgumentParser +{ +public: + // I *think* this function could be made `constexpr` when the code is + // compiled as C++20. This would allow building a parser at compile time. + template <typename T> + cmArgumentParser& Bind(cm::static_string_view name, T Result::*member) + { + bool const inserted = + this->Bindings + .Emplace(name, + [member](ArgumentParser::Instance& instance, void* result) { + instance.Bind(static_cast<Result*>(result)->*member); + }) + .second; + assert(inserted), (void)inserted; + return *this; + } + + template <typename Range> + void Parse(Result& result, Range const& args, + std::vector<std::string>* unparsedArguments = nullptr, + std::vector<std::string>* keywordsMissingValue = nullptr) const + { + ArgumentParser::Instance instance(this->Bindings); + for (cm::string_view arg : args) { + instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue); + } + } + + template <typename Range> + Result Parse(Range const& args, + std::vector<std::string>* unparsedArguments = nullptr, + std::vector<std::string>* keywordsMissingValue = nullptr) const + { + Result result; + this->Parse(result, args, unparsedArguments, keywordsMissingValue); + return result; + } + +private: + ArgumentParser::ActionMap Bindings; +}; + +template <> +class cmArgumentParser<void> +{ +public: + template <typename T> + cmArgumentParser& Bind(cm::static_string_view name, T& ref) + { + bool const inserted = this->Bind(cm::string_view(name), ref); + assert(inserted), (void)inserted; + return *this; + } + + template <typename Range> + void Parse(Range const& args, + std::vector<std::string>* unparsedArguments = nullptr, + std::vector<std::string>* keywordsMissingValue = nullptr) const + { + ArgumentParser::Instance instance(this->Bindings); + for (cm::string_view arg : args) { + instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue); + } + } + +protected: + template <typename T> + bool Bind(cm::string_view name, T& ref) + { + return this->Bindings + .Emplace(name, + [&ref](ArgumentParser::Instance& instance, void*) { + instance.Bind(ref); + }) + .second; + } + +private: + ArgumentParser::ActionMap Bindings; +}; + +#endif diff --git a/Source/cmCMakeMinimumRequired.cxx b/Source/cmCMakeMinimumRequired.cxx index 4218d81..4b4bca2 100644 --- a/Source/cmCMakeMinimumRequired.cxx +++ b/Source/cmCMakeMinimumRequired.cxx @@ -55,7 +55,7 @@ bool cmCMakeMinimumRequired::InitialPass(std::vector<std::string> const& args, (version_min.empty() || version_max.empty())) { std::ostringstream e; e << "VERSION \"" << version_string - << "\" does not have a version on both sides of \"...\"."; + << R"(" does not have a version on both sides of "...".)"; this->SetError(e.str()); return false; } diff --git a/Source/cmCMakePolicyCommand.cxx b/Source/cmCMakePolicyCommand.cxx index ac30e1a..8da5ef7 100644 --- a/Source/cmCMakePolicyCommand.cxx +++ b/Source/cmCMakePolicyCommand.cxx @@ -176,7 +176,7 @@ bool cmCMakePolicyCommand::HandleVersionMode( (version_min.empty() || version_max.empty())) { std::ostringstream e; e << "VERSION \"" << version_string - << "\" does not have a version on both sides of \"...\"."; + << R"(" does not have a version on both sides of "...".)"; this->SetError(e.str()); return false; } diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx index c0088ac..5efc784 100644 --- a/Source/cmCPluginAPI.cxx +++ b/Source/cmCPluginAPI.cxx @@ -491,12 +491,16 @@ public: typedef std::map<cmSourceFile*, cmCPluginAPISourceFile*> derived; typedef derived::iterator iterator; typedef derived::value_type value_type; + cmCPluginAPISourceFileMap() = default; ~cmCPluginAPISourceFileMap() { for (auto const& i : *this) { delete i.second; } } + cmCPluginAPISourceFileMap(const cmCPluginAPISourceFileMap&) = delete; + cmCPluginAPISourceFileMap& operator=(const cmCPluginAPISourceFileMap&) = + delete; }; cmCPluginAPISourceFileMap cmCPluginAPISourceFiles; @@ -683,26 +687,24 @@ void CCONV cmSourceFileSetName(void* arg, const char* name, const char* dir, } // Next, try the various source extensions - for (std::vector<std::string>::const_iterator ext = sourceExts.begin(); - ext != sourceExts.end(); ++ext) { + for (std::string const& ext : sourceExts) { hname = pathname; hname += "."; - hname += *ext; + hname += ext; if (cmSystemTools::FileExists(hname)) { - sf->SourceExtension = *ext; + sf->SourceExtension = ext; sf->FullPath = hname; return; } } // Finally, try the various header extensions - for (std::vector<std::string>::const_iterator ext = headerExts.begin(); - ext != headerExts.end(); ++ext) { + for (std::string const& ext : headerExts) { hname = pathname; hname += "."; - hname += *ext; + hname += ext; if (cmSystemTools::FileExists(hname)) { - sf->SourceExtension = *ext; + sf->SourceExtension = ext; sf->FullPath = hname; return; } @@ -711,13 +713,11 @@ void CCONV cmSourceFileSetName(void* arg, const char* name, const char* dir, std::ostringstream e; e << "Cannot find source file \"" << pathname << "\""; e << "\n\nTried extensions"; - for (std::vector<std::string>::const_iterator ext = sourceExts.begin(); - ext != sourceExts.end(); ++ext) { - e << " ." << *ext; + for (std::string const& ext : sourceExts) { + e << " ." << ext; } - for (std::vector<std::string>::const_iterator ext = headerExts.begin(); - ext != headerExts.end(); ++ext) { - e << " ." << *ext; + for (std::string const& ext : headerExts) { + e << " ." << ext; } cmSystemTools::Error(e.str()); } diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 989c7ee..003ebdc 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -43,7 +43,6 @@ #include "cmCTestTestHandler.h" #include "cmCTestUpdateHandler.h" #include "cmCTestUploadHandler.h" -#include "cmCurl.h" #include "cmDynamicLoader.h" #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" @@ -62,12 +61,149 @@ # include <be/kernel/OS.h> /* disable_debugger() API. */ #endif -#define DEBUGOUT \ - std::cout << __LINE__ << " "; \ - std::cout -#define DEBUGERR \ - std::cerr << __LINE__ << " "; \ - std::cerr +struct cmCTest::Private +{ + /** Representation of one part. */ + struct PartInfo + { + void SetName(const std::string& name) { this->Name = name; } + const std::string& GetName() const { return this->Name; } + + void Enable() { this->Enabled = true; } + explicit operator bool() const { return this->Enabled; } + + std::vector<std::string> SubmitFiles; + + private: + bool Enabled = false; + std::string Name; + }; + + int RepeatTests = 1; // default to run each test once + bool RepeatUntilFail = false; + std::string ConfigType; + std::string ScheduleType; + std::chrono::system_clock::time_point StopTime; + bool TestProgressOutput = false; + bool Verbose = false; + bool ExtraVerbose = false; + bool ProduceXML = false; + bool LabelSummary = true; + bool SubprojectSummary = true; + bool UseHTTP10 = false; + bool PrintLabels = false; + bool Failover = false; + + bool FlushTestProgressLine = false; + + bool ForceNewCTestProcess = false; + + bool RunConfigurationScript = false; + + // these are helper classes + cmCTestBuildHandler BuildHandler; + cmCTestBuildAndTestHandler BuildAndTestHandler; + cmCTestCoverageHandler CoverageHandler; + cmCTestScriptHandler ScriptHandler; + cmCTestTestHandler TestHandler; + cmCTestUpdateHandler UpdateHandler; + cmCTestConfigureHandler ConfigureHandler; + cmCTestMemCheckHandler MemCheckHandler; + cmCTestSubmitHandler SubmitHandler; + cmCTestUploadHandler UploadHandler; + + std::vector<cmCTestGenericHandler*> GetTestingHandlers() + { + return { &this->BuildHandler, &this->BuildAndTestHandler, + &this->CoverageHandler, &this->ScriptHandler, + &this->TestHandler, &this->UpdateHandler, + &this->ConfigureHandler, &this->MemCheckHandler, + &this->SubmitHandler, &this->UploadHandler }; + } + + std::map<std::string, cmCTestGenericHandler*> GetNamedTestingHandlers() + { + return { { "build", &this->BuildHandler }, + { "buildtest", &this->BuildAndTestHandler }, + { "coverage", &this->CoverageHandler }, + { "script", &this->ScriptHandler }, + { "test", &this->TestHandler }, + { "update", &this->UpdateHandler }, + { "configure", &this->ConfigureHandler }, + { "memcheck", &this->MemCheckHandler }, + { "submit", &this->SubmitHandler }, + { "upload", &this->UploadHandler } }; + } + + bool ShowOnly = false; + bool OutputAsJson = false; + int OutputAsJsonVersion = 1; + + // TODO: The ctest configuration should be a hierarchy of + // configuration option sources: command-line, script, ini file. + // Then the ini file can get re-loaded whenever it changes without + // affecting any higher-precedence settings. + std::map<std::string, std::string> CTestConfiguration; + std::map<std::string, std::string> CTestConfigurationOverwrites; + + PartInfo Parts[PartCount]; + std::map<std::string, Part> PartMap; + + std::string CurrentTag; + bool TomorrowTag = false; + + int TestModel = cmCTest::EXPERIMENTAL; + std::string SpecificTrack; + + cmDuration TimeOut = cmDuration::zero(); + + cmDuration GlobalTimeout = cmDuration::zero(); + + int MaxTestNameWidth = 30; + + int ParallelLevel = 1; + bool ParallelLevelSetInCli = false; + + unsigned long TestLoad = 0; + + int CompatibilityMode; + + // information for the --build-and-test options + std::string BinaryDir; + + std::string NotesFiles; + + bool InteractiveDebugMode = true; + + bool ShortDateFormat = true; + + bool CompressXMLFiles = false; + bool CompressTestOutput = true; + + // By default we write output to the process output streams. + std::ostream* StreamOut = &std::cout; + std::ostream* StreamErr = &std::cerr; + + bool SuppressUpdatingCTestConfiguration = false; + + bool Debug = false; + bool ShowLineNumbers = false; + bool Quiet = false; + + std::string BuildID; + + std::vector<std::string> InitialCommandLineArguments; + + int SubmitIndex = 0; + + cmGeneratedFileStream* OutputLogFile = nullptr; + int OutputLogFileLastTag = -1; + + bool OutputTestOutputOnTestFailure = false; + bool OutputColorCode = cmCTest::ColoredOutputSupportedByConsole(); + + std::map<std::string, std::string> Definitions; +}; struct tm* cmCTest::GetNightlyTime(std::string const& str, bool tomorrowtag) { @@ -123,6 +259,11 @@ struct tm* cmCTest::GetNightlyTime(std::string const& str, bool tomorrowtag) return lctime; } +bool cmCTest::GetTomorrowTag() const +{ + return this->Impl->TomorrowTag; +} + std::string cmCTest::CleanString(const std::string& str) { std::string::size_type spos = str.find_first_not_of(" \n\t\r\f\v"); @@ -142,7 +283,7 @@ std::string cmCTest::CurrentTime() struct tm* t = localtime(¤ttime); // return ::CleanString(ctime(¤ttime)); char current_time[1024]; - if (this->ShortDateFormat) { + if (this->Impl->ShortDateFormat) { strftime(current_time, 1000, "%b %d %H:%M %Z", t); } else { strftime(current_time, 1000, "%a %b %d %H:%M:%S %Z %Y", t); @@ -160,88 +301,6 @@ std::string cmCTest::GetCostDataFile() return fname; } -#ifdef CMAKE_BUILD_WITH_CMAKE -static size_t HTTPResponseCallback(void* ptr, size_t size, size_t nmemb, - void* data) -{ - int realsize = static_cast<int>(size * nmemb); - - std::string* response = static_cast<std::string*>(data); - const char* chPtr = static_cast<char*>(ptr); - *response += chPtr; - - return realsize; -} - -int cmCTest::HTTPRequest(std::string url, HTTPMethod method, - std::string& response, std::string const& fields, - std::string const& putFile, int timeout) -{ - CURL* curl; - FILE* file; - ::curl_global_init(CURL_GLOBAL_ALL); - curl = ::curl_easy_init(); - cmCurlSetCAInfo(curl); - - // set request options based on method - switch (method) { - case cmCTest::HTTP_POST: - ::curl_easy_setopt(curl, CURLOPT_POST, 1); - ::curl_easy_setopt(curl, CURLOPT_POSTFIELDS, fields.c_str()); - break; - case cmCTest::HTTP_PUT: - if (!cmSystemTools::FileExists(putFile)) { - response = "Error: File "; - response += putFile + " does not exist.\n"; - return -1; - } - ::curl_easy_setopt(curl, CURLOPT_PUT, 1); - file = cmsys::SystemTools::Fopen(putFile, "rb"); - ::curl_easy_setopt(curl, CURLOPT_INFILE, file); - // fall through to append GET fields - CM_FALLTHROUGH; - case cmCTest::HTTP_GET: - if (!fields.empty()) { - url += "?" + fields; - } - break; - } - - ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); - ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - - // set response options - ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HTTPResponseCallback); - ::curl_easy_setopt(curl, CURLOPT_FILE, &response); - ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); - - CURLcode res = ::curl_easy_perform(curl); - - ::curl_easy_cleanup(curl); - ::curl_global_cleanup(); - - return static_cast<int>(res); -} -#endif - -std::string cmCTest::MakeURLSafe(const std::string& str) -{ - std::ostringstream ost; - char buffer[10]; - for (unsigned char ch : str) { - if ((ch > 126 || ch < 32 || ch == '&' || ch == '%' || ch == '+' || - ch == '=' || ch == '@') && - ch != 9) { - sprintf(buffer, "%02x;", static_cast<unsigned int>(ch)); - ost << buffer; - } else { - ost << ch; - } - } - return ost.str(); -} - std::string cmCTest::DecodeURL(const std::string& in) { std::string out; @@ -258,92 +317,39 @@ std::string cmCTest::DecodeURL(const std::string& in) } cmCTest::cmCTest() + : Impl(new Private) { - this->LabelSummary = true; - this->SubprojectSummary = true; - this->ParallelLevel = 1; - this->ParallelLevelSetInCli = false; - this->TestLoad = 0; - this->SubmitIndex = 0; - this->Failover = false; - this->ForceNewCTestProcess = false; - this->TomorrowTag = false; - this->TestProgressOutput = false; - this->FlushTestProgressLine = false; - this->Verbose = false; - - this->Debug = false; - this->ShowLineNumbers = false; - this->Quiet = false; - this->ExtraVerbose = false; - this->ProduceXML = false; - this->ShowOnly = false; - this->OutputAsJson = false; - this->OutputAsJsonVersion = 1; - this->RunConfigurationScript = false; - this->UseHTTP10 = false; - this->PrintLabels = false; - this->CompressTestOutput = true; - this->TestModel = cmCTest::EXPERIMENTAL; - this->MaxTestNameWidth = 30; - this->InteractiveDebugMode = true; - this->TimeOut = cmDuration::zero(); - this->GlobalTimeout = cmDuration::zero(); - this->CompressXMLFiles = false; - this->ScheduleType.clear(); - this->OutputLogFile = nullptr; - this->OutputLogFileLastTag = -1; - this->SuppressUpdatingCTestConfiguration = false; - this->BuildID = ""; - this->OutputTestOutputOnTestFailure = false; - this->OutputColorCode = cmCTest::ColoredOutputSupportedByConsole(); - this->RepeatTests = 1; // default to run each test once - this->RepeatUntilFail = false; - std::string envValue; if (cmSystemTools::GetEnv("CTEST_OUTPUT_ON_FAILURE", envValue)) { - this->OutputTestOutputOnTestFailure = !cmSystemTools::IsOff(envValue); + this->Impl->OutputTestOutputOnTestFailure = + !cmSystemTools::IsOff(envValue); } envValue.clear(); if (cmSystemTools::GetEnv("CTEST_PROGRESS_OUTPUT", envValue)) { - this->TestProgressOutput = !cmSystemTools::IsOff(envValue); - } - - this->InitStreams(); - - this->Parts[PartStart].SetName("Start"); - this->Parts[PartUpdate].SetName("Update"); - this->Parts[PartConfigure].SetName("Configure"); - this->Parts[PartBuild].SetName("Build"); - this->Parts[PartTest].SetName("Test"); - this->Parts[PartCoverage].SetName("Coverage"); - this->Parts[PartMemCheck].SetName("MemCheck"); - this->Parts[PartSubmit].SetName("Submit"); - this->Parts[PartNotes].SetName("Notes"); - this->Parts[PartExtraFiles].SetName("ExtraFiles"); - this->Parts[PartUpload].SetName("Upload"); - this->Parts[PartDone].SetName("Done"); + this->Impl->TestProgressOutput = !cmSystemTools::IsOff(envValue); + } + + this->Impl->Parts[PartStart].SetName("Start"); + this->Impl->Parts[PartUpdate].SetName("Update"); + this->Impl->Parts[PartConfigure].SetName("Configure"); + this->Impl->Parts[PartBuild].SetName("Build"); + this->Impl->Parts[PartTest].SetName("Test"); + this->Impl->Parts[PartCoverage].SetName("Coverage"); + this->Impl->Parts[PartMemCheck].SetName("MemCheck"); + this->Impl->Parts[PartSubmit].SetName("Submit"); + this->Impl->Parts[PartNotes].SetName("Notes"); + this->Impl->Parts[PartExtraFiles].SetName("ExtraFiles"); + this->Impl->Parts[PartUpload].SetName("Upload"); + this->Impl->Parts[PartDone].SetName("Done"); // Fill the part name-to-id map. for (Part p = PartStart; p != PartCount; p = Part(p + 1)) { - this->PartMap[cmSystemTools::LowerCase(this->Parts[p].GetName())] = p; + this->Impl + ->PartMap[cmSystemTools::LowerCase(this->Impl->Parts[p].GetName())] = p; } - this->ShortDateFormat = true; - - this->TestingHandlers["build"] = new cmCTestBuildHandler; - this->TestingHandlers["buildtest"] = new cmCTestBuildAndTestHandler; - this->TestingHandlers["coverage"] = new cmCTestCoverageHandler; - this->TestingHandlers["script"] = new cmCTestScriptHandler; - this->TestingHandlers["test"] = new cmCTestTestHandler; - this->TestingHandlers["update"] = new cmCTestUpdateHandler; - this->TestingHandlers["configure"] = new cmCTestConfigureHandler; - this->TestingHandlers["memcheck"] = new cmCTestMemCheckHandler; - this->TestingHandlers["submit"] = new cmCTestSubmitHandler; - this->TestingHandlers["upload"] = new cmCTestUploadHandler; - - for (auto& handler : this->TestingHandlers) { - handler.second->SetCTestInstance(this); + for (auto& handler : this->Impl->GetTestingHandlers()) { + handler->SetCTestInstance(this); } // Make sure we can capture the build tool output. @@ -352,31 +358,40 @@ cmCTest::cmCTest() cmCTest::~cmCTest() { - cmDeleteAll(this->TestingHandlers); - this->SetOutputLogFileName(nullptr); + delete this->Impl->OutputLogFile; +} + +int cmCTest::GetParallelLevel() const +{ + return this->Impl->ParallelLevel; } void cmCTest::SetParallelLevel(int level) { - this->ParallelLevel = level < 1 ? 1 : level; + this->Impl->ParallelLevel = level < 1 ? 1 : level; +} + +unsigned long cmCTest::GetTestLoad() const +{ + return this->Impl->TestLoad; } void cmCTest::SetTestLoad(unsigned long load) { - this->TestLoad = load; + this->Impl->TestLoad = load; } bool cmCTest::ShouldCompressTestOutput() { - return this->CompressTestOutput; + return this->Impl->CompressTestOutput; } cmCTest::Part cmCTest::GetPartFromName(const char* name) { // Look up by lower-case to make names case-insensitive. std::string lower_name = cmSystemTools::LowerCase(name); - PartMapType::const_iterator i = this->PartMap.find(lower_name); - if (i != this->PartMap.end()) { + auto const i = this->Impl->PartMap.find(lower_name); + if (i != this->Impl->PartMap.end()) { return i->second; } @@ -392,19 +407,19 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) } cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet); - if (!this->InteractiveDebugMode) { + if (!this->Impl->InteractiveDebugMode) { this->BlockTestErrorDiagnostics(); } else { cmSystemTools::PutEnv("CTEST_INTERACTIVE_DEBUG_MODE=1"); } - this->BinaryDir = binary_dir; - cmSystemTools::ConvertToUnixSlashes(this->BinaryDir); + this->Impl->BinaryDir = binary_dir; + cmSystemTools::ConvertToUnixSlashes(this->Impl->BinaryDir); this->UpdateCTestConfiguration(); cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet); - if (this->ProduceXML) { + if (this->Impl->ProduceXML) { cmCTestOptionalLog(this, DEBUG, "Here: " << __LINE__ << std::endl, quiet); cmCTestOptionalLog(this, OUTPUT, " Site: " @@ -415,7 +430,7 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) << std::endl, quiet); cmCTestOptionalLog(this, DEBUG, "Produce XML is on" << std::endl, quiet); - if (this->TestModel == cmCTest::NIGHTLY && + if (this->Impl->TestModel == cmCTest::NIGHTLY && this->GetCTestConfiguration("NightlyStartTime").empty()) { cmCTestOptionalLog( this, WARNING, @@ -435,17 +450,18 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) cm.GetCurrentSnapshot().SetDefaultDefinitions(); cmGlobalGenerator gg(&cm); cmMakefile mf(&gg, cm.GetCurrentSnapshot()); - if (!this->ReadCustomConfigurationFileTree(this->BinaryDir.c_str(), &mf)) { + if (!this->ReadCustomConfigurationFileTree(this->Impl->BinaryDir.c_str(), + &mf)) { cmCTestOptionalLog( this, DEBUG, "Cannot find custom configuration file tree" << std::endl, quiet); return 0; } - if (this->ProduceXML) { + if (this->Impl->ProduceXML) { // Verify "Testing" directory exists: // - std::string testingDir = this->BinaryDir + "/Testing"; + std::string testingDir = this->Impl->BinaryDir + "/Testing"; if (cmSystemTools::FileExists(testingDir)) { if (!cmSystemTools::FileIsDirectory(testingDir)) { cmCTestLog(this, ERROR_MESSAGE, @@ -475,7 +491,7 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) if (createNewTag) { time_t tctime = time(nullptr); - if (this->TomorrowTag) { + if (this->Impl->TomorrowTag) { tctime += (24 * 60 * 60); } struct tm* lctime = gmtime(&tctime); @@ -493,26 +509,28 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) } std::string track; if (cmSystemTools::GetLineFromStream(tfin, track) && - !this->Parts[PartStart] && !command) { - this->SpecificTrack = track; + !this->Impl->Parts[PartStart] && !command) { + this->Impl->SpecificTrack = track; } std::string model; if (cmSystemTools::GetLineFromStream(tfin, model) && - !this->Parts[PartStart] && !command) { - this->TestModel = GetTestModelFromString(model.c_str()); + !this->Impl->Parts[PartStart] && !command) { + this->Impl->TestModel = GetTestModelFromString(model.c_str()); } tfin.close(); } - if (tag.empty() || (nullptr != command) || this->Parts[PartStart]) { + if (tag.empty() || (nullptr != command) || + this->Impl->Parts[PartStart]) { cmCTestOptionalLog( this, DEBUG, "TestModel: " << this->GetTestModelString() << std::endl, quiet); - cmCTestOptionalLog( - this, DEBUG, "TestModel: " << this->TestModel << std::endl, quiet); - if (this->TestModel == cmCTest::NIGHTLY) { + cmCTestOptionalLog(this, DEBUG, + "TestModel: " << this->Impl->TestModel << std::endl, + quiet); + if (this->Impl->TestModel == cmCTest::NIGHTLY) { lctime = this->GetNightlyTime( this->GetCTestConfiguration("NightlyStartTime"), - this->TomorrowTag); + this->Impl->TomorrowTag); } char datestring[100]; sprintf(datestring, "%04d%02d%02d-%02d%02d", lctime->tm_year + 1900, @@ -523,7 +541,7 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) if (ofs) { ofs << tag << std::endl; ofs << this->GetTestModelString() << std::endl; - switch (this->TestModel) { + switch (this->Impl->TestModel) { case cmCTest::EXPERIMENTAL: ofs << "Experimental" << std::endl; break; @@ -565,7 +583,7 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) return 0; } - if (this->TestModel == cmCTest::UNKNOWN) { + if (this->Impl->TestModel == cmCTest::UNKNOWN) { if (model == cmCTest::UNKNOWN) { cmCTestLog(this, ERROR_MESSAGE, "TAG file does not contain model and " @@ -577,8 +595,8 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) this->SetTestModel(model); } - if (model != this->TestModel && model != cmCTest::UNKNOWN && - this->TestModel != cmCTest::UNKNOWN) { + if (model != this->Impl->TestModel && model != cmCTest::UNKNOWN && + this->Impl->TestModel != cmCTest::UNKNOWN) { cmCTestOptionalLog(this, WARNING, "Model given in TAG does not match " "model given in ctest_start()" @@ -586,14 +604,15 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) quiet); } - if (!this->SpecificTrack.empty() && track != this->SpecificTrack) { + if (!this->Impl->SpecificTrack.empty() && + track != this->Impl->SpecificTrack) { cmCTestOptionalLog(this, WARNING, "Track given in TAG does not match " "track given in ctest_start()" << std::endl, quiet); } else { - this->SpecificTrack = track; + this->Impl->SpecificTrack = track; } cmCTestOptionalLog(this, OUTPUT, @@ -603,7 +622,7 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) quiet); } - this->CurrentTag = tag; + this->Impl->CurrentTag = tag; } return 1; @@ -613,9 +632,9 @@ bool cmCTest::InitializeFromCommand(cmCTestStartCommand* command) { std::string src_dir = this->GetCTestConfiguration("SourceDirectory"); std::string bld_dir = this->GetCTestConfiguration("BuildDirectory"); - this->BuildID = ""; + this->Impl->BuildID = ""; for (Part p = PartStart; p != PartCount; p = Part(p + 1)) { - this->Parts[p].SubmitFiles.clear(); + this->Impl->Parts[p].SubmitFiles.clear(); } cmMakefile* mf = command->GetMakefile(); @@ -669,18 +688,18 @@ bool cmCTest::InitializeFromCommand(cmCTestStartCommand* command) bool cmCTest::UpdateCTestConfiguration() { - if (this->SuppressUpdatingCTestConfiguration) { + if (this->Impl->SuppressUpdatingCTestConfiguration) { return true; } - std::string fileName = this->BinaryDir + "/CTestConfiguration.ini"; + std::string fileName = this->Impl->BinaryDir + "/CTestConfiguration.ini"; if (!cmSystemTools::FileExists(fileName)) { - fileName = this->BinaryDir + "/DartConfiguration.tcl"; + fileName = this->Impl->BinaryDir + "/DartConfiguration.tcl"; } cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "UpdateCTestConfiguration from :" << fileName << "\n"); if (!cmSystemTools::FileExists(fileName)) { // No need to exit if we are not producing XML - if (this->ProduceXML) { + if (this->Impl->ProduceXML) { cmCTestLog(this, ERROR_MESSAGE, "Cannot find file: " << fileName << std::endl); return false; @@ -720,15 +739,15 @@ bool cmCTest::UpdateCTestConfiguration() } std::string key = line.substr(0, cpos); std::string value = cmCTest::CleanString(line.substr(cpos + 1)); - this->CTestConfiguration[key] = value; + this->Impl->CTestConfiguration[key] = value; } fin.close(); } if (!this->GetCTestConfiguration("BuildDirectory").empty()) { - this->BinaryDir = this->GetCTestConfiguration("BuildDirectory"); - cmSystemTools::ChangeDirectory(this->BinaryDir); + this->Impl->BinaryDir = this->GetCTestConfiguration("BuildDirectory"); + cmSystemTools::ChangeDirectory(this->Impl->BinaryDir); } - this->TimeOut = + this->Impl->TimeOut = std::chrono::seconds(atoi(this->GetCTestConfiguration("TimeOut").c_str())); std::string const& testLoad = this->GetCTestConfiguration("TestLoad"); if (!testLoad.empty()) { @@ -740,8 +759,8 @@ bool cmCTest::UpdateCTestConfiguration() "Invalid value for 'Test Load' : " << testLoad << std::endl); } } - if (this->ProduceXML) { - this->CompressXMLFiles = + if (this->Impl->ProduceXML) { + this->Impl->CompressXMLFiles = cmSystemTools::IsOn(this->GetCTestConfiguration("CompressSubmission")); } return true; @@ -760,21 +779,26 @@ void cmCTest::BlockTestErrorDiagnostics() void cmCTest::SetTestModel(int mode) { - this->InteractiveDebugMode = false; - this->TestModel = mode; + this->Impl->InteractiveDebugMode = false; + this->Impl->TestModel = mode; +} + +int cmCTest::GetTestModel() const +{ + return this->Impl->TestModel; } bool cmCTest::SetTest(const char* ttype, bool report) { if (cmSystemTools::LowerCase(ttype) == "all") { for (Part p = PartStart; p != PartCount; p = Part(p + 1)) { - this->Parts[p].Enable(); + this->Impl->Parts[p].Enable(); } return true; } Part p = this->GetPartFromName(ttype); if (p != PartCount) { - this->Parts[p].Enable(); + this->Impl->Parts[p].Enable(); return true; } if (report) { @@ -792,7 +816,7 @@ void cmCTest::Finalize() bool cmCTest::OpenOutputFile(const std::string& path, const std::string& name, cmGeneratedFileStream& stream, bool compress) { - std::string testingDir = this->BinaryDir + "/Testing"; + std::string testingDir = this->Impl->BinaryDir + "/Testing"; if (!path.empty()) { testingDir += "/" + path; } @@ -819,7 +843,7 @@ bool cmCTest::OpenOutputFile(const std::string& path, const std::string& name, return false; } if (compress) { - if (this->CompressXMLFiles) { + if (this->Impl->CompressXMLFiles) { stream.SetCompression(true); } } @@ -844,40 +868,59 @@ bool cmCTest::AddIfExists(Part part, const char* file) bool cmCTest::CTestFileExists(const std::string& filename) { - std::string testingDir = - this->BinaryDir + "/Testing/" + this->CurrentTag + "/" + filename; + std::string testingDir = this->Impl->BinaryDir + "/Testing/" + + this->Impl->CurrentTag + "/" + filename; return cmSystemTools::FileExists(testingDir); } -cmCTestGenericHandler* cmCTest::GetInitializedHandler(const char* handler) +cmCTestBuildHandler* cmCTest::GetBuildHandler() { - cmCTest::t_TestingHandlers::iterator it = - this->TestingHandlers.find(handler); - if (it == this->TestingHandlers.end()) { - return nullptr; - } - it->second->Initialize(); - return it->second; + return &this->Impl->BuildHandler; } -cmCTestGenericHandler* cmCTest::GetHandler(const char* handler) +cmCTestBuildAndTestHandler* cmCTest::GetBuildAndTestHandler() { - cmCTest::t_TestingHandlers::iterator it = - this->TestingHandlers.find(handler); - if (it == this->TestingHandlers.end()) { - return nullptr; - } - return it->second; + return &this->Impl->BuildAndTestHandler; } -int cmCTest::ExecuteHandler(const char* shandler) +cmCTestCoverageHandler* cmCTest::GetCoverageHandler() { - cmCTestGenericHandler* handler = this->GetHandler(shandler); - if (!handler) { - return -1; - } - handler->Initialize(); - return handler->ProcessHandler(); + return &this->Impl->CoverageHandler; +} + +cmCTestScriptHandler* cmCTest::GetScriptHandler() +{ + return &this->Impl->ScriptHandler; +} + +cmCTestTestHandler* cmCTest::GetTestHandler() +{ + return &this->Impl->TestHandler; +} + +cmCTestUpdateHandler* cmCTest::GetUpdateHandler() +{ + return &this->Impl->UpdateHandler; +} + +cmCTestConfigureHandler* cmCTest::GetConfigureHandler() +{ + return &this->Impl->ConfigureHandler; +} + +cmCTestMemCheckHandler* cmCTest::GetMemCheckHandler() +{ + return &this->Impl->MemCheckHandler; +} + +cmCTestSubmitHandler* cmCTest::GetSubmitHandler() +{ + return &this->Impl->SubmitHandler; +} + +cmCTestUploadHandler* cmCTest::GetUploadHandler() +{ + return &this->Impl->UploadHandler; } int cmCTest::ProcessSteps() @@ -887,11 +930,11 @@ int cmCTest::ProcessSteps() int update_count = 0; for (Part p = PartStart; notest && p != PartCount; p = Part(p + 1)) { - notest = !this->Parts[p]; + notest = !this->Impl->Parts[p]; } - if (this->Parts[PartUpdate] && + if (this->Impl->Parts[PartUpdate] && (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) { - cmCTestGenericHandler* uphandler = this->GetHandler("update"); + cmCTestUpdateHandler* uphandler = this->GetUpdateHandler(); uphandler->SetPersistentOption( "SourceDirectory", this->GetCTestConfiguration("SourceDirectory").c_str()); @@ -900,45 +943,45 @@ int cmCTest::ProcessSteps() res |= cmCTest::UPDATE_ERRORS; } } - if (this->TestModel == cmCTest::CONTINUOUS && !update_count) { + if (this->Impl->TestModel == cmCTest::CONTINUOUS && !update_count) { return 0; } - if (this->Parts[PartConfigure] && + if (this->Impl->Parts[PartConfigure] && (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) { - if (this->GetHandler("configure")->ProcessHandler() < 0) { + if (this->GetConfigureHandler()->ProcessHandler() < 0) { res |= cmCTest::CONFIGURE_ERRORS; } } - if (this->Parts[PartBuild] && + if (this->Impl->Parts[PartBuild] && (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) { this->UpdateCTestConfiguration(); - if (this->GetHandler("build")->ProcessHandler() < 0) { + if (this->GetBuildHandler()->ProcessHandler() < 0) { res |= cmCTest::BUILD_ERRORS; } } - if ((this->Parts[PartTest] || notest) && + if ((this->Impl->Parts[PartTest] || notest) && (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) { this->UpdateCTestConfiguration(); - if (this->GetHandler("test")->ProcessHandler() < 0) { + if (this->GetTestHandler()->ProcessHandler() < 0) { res |= cmCTest::TEST_ERRORS; } } - if (this->Parts[PartCoverage] && + if (this->Impl->Parts[PartCoverage] && (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) { this->UpdateCTestConfiguration(); - if (this->GetHandler("coverage")->ProcessHandler() < 0) { + if (this->GetCoverageHandler()->ProcessHandler() < 0) { res |= cmCTest::COVERAGE_ERRORS; } } - if (this->Parts[PartMemCheck] && + if (this->Impl->Parts[PartMemCheck] && (this->GetRemainingTimeAllowed() > std::chrono::minutes(2))) { this->UpdateCTestConfiguration(); - if (this->GetHandler("memcheck")->ProcessHandler() < 0) { + if (this->GetMemCheckHandler()->ProcessHandler() < 0) { res |= cmCTest::MEMORY_ERRORS; } } if (!notest) { - std::string notes_dir = this->BinaryDir + "/Testing/Notes"; + std::string notes_dir = this->Impl->BinaryDir + "/Testing/Notes"; if (cmSystemTools::FileIsDirectory(notes_dir)) { cmsys::Directory d; d.Load(notes_dir); @@ -948,24 +991,24 @@ int cmCTest::ProcessSteps() std::string fullname = notes_dir + "/" + file; if (cmSystemTools::FileExists(fullname) && !cmSystemTools::FileIsDirectory(fullname)) { - if (!this->NotesFiles.empty()) { - this->NotesFiles += ";"; + if (!this->Impl->NotesFiles.empty()) { + this->Impl->NotesFiles += ";"; } - this->NotesFiles += fullname; - this->Parts[PartNotes].Enable(); + this->Impl->NotesFiles += fullname; + this->Impl->Parts[PartNotes].Enable(); } } } } - if (this->Parts[PartNotes]) { + if (this->Impl->Parts[PartNotes]) { this->UpdateCTestConfiguration(); - if (!this->NotesFiles.empty()) { - this->GenerateNotesFile(this->NotesFiles.c_str()); + if (!this->Impl->NotesFiles.empty()) { + this->GenerateNotesFile(this->Impl->NotesFiles.c_str()); } } - if (this->Parts[PartSubmit]) { + if (this->Impl->Parts[PartSubmit]) { this->UpdateCTestConfiguration(); - if (this->GetHandler("submit")->ProcessHandler() < 0) { + if (this->GetSubmitHandler()->ProcessHandler() < 0) { res |= cmCTest::SUBMIT_ERRORS; } } @@ -977,10 +1020,10 @@ int cmCTest::ProcessSteps() std::string cmCTest::GetTestModelString() { - if (!this->SpecificTrack.empty()) { - return this->SpecificTrack; + if (!this->Impl->SpecificTrack.empty()) { + return this->Impl->SpecificTrack; } - switch (this->TestModel) { + switch (this->Impl->TestModel) { case cmCTest::NIGHTLY: return "Nightly"; case cmCTest::CONTINUOUS: @@ -1009,7 +1052,7 @@ int cmCTest::GetTestModelFromString(const char* str) //###################################################################### //###################################################################### -int cmCTest::RunMakeCommand(const char* command, std::string& output, +int cmCTest::RunMakeCommand(const std::string& command, std::string& output, int* retVal, const char* dir, cmDuration timeout, std::ostream& ofs, Encoding encoding) { @@ -1039,7 +1082,7 @@ int cmCTest::RunMakeCommand(const char* command, std::string& output, // Now create process object cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, &*argv.begin()); + cmsysProcess_SetCommand(cp, argv.data()); cmsysProcess_SetWorkingDirectory(cp, dir); cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); cmsysProcess_SetTimeout(cp, timeout.count()); @@ -1139,8 +1182,9 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, if (timeout != cmCTest::MaxDuration()) { timeout -= std::chrono::minutes(2); } - if (this->TimeOut > cmDuration::zero() && this->TimeOut < timeout) { - timeout = this->TimeOut; + if (this->Impl->TimeOut > cmDuration::zero() && + this->Impl->TimeOut < timeout) { + timeout = this->Impl->TimeOut; } if (testTimeOut > cmDuration::zero() && testTimeOut < this->GetRemainingTimeAllowed()) { @@ -1158,10 +1202,10 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, : std::to_string(cmDurationTo<unsigned int>(timeout))) << "\n"); if (cmSystemTools::SameFile(argv[0], cmSystemTools::GetCTestCommand()) && - !this->ForceNewCTestProcess) { + !this->Impl->ForceNewCTestProcess) { cmCTest inst; - inst.ConfigType = this->ConfigType; - inst.TimeOut = timeout; + inst.Impl->ConfigType = this->Impl->ConfigType; + inst.Impl->TimeOut = timeout; // Capture output of the child ctest. std::ostringstream oss; @@ -1222,7 +1266,7 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, } cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, &*argv.begin()); + cmsysProcess_SetCommand(cp, argv.data()); cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl); if (cmSystemTools::GetRunCommandHideConsole()) { cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); @@ -1258,7 +1302,7 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, cmsysProcess_WaitForExit(cp, nullptr); processOutput.DecodeText(tempOutput, tempOutput); if (output && tempOutput.begin() != tempOutput.end()) { - output->append(&*tempOutput.begin(), tempOutput.size()); + output->append(tempOutput.data(), tempOutput.size()); } cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "-- Process completed" << std::endl); @@ -1267,11 +1311,11 @@ int cmCTest::RunTest(std::vector<const char*> argv, std::string* output, if (result == cmsysProcess_State_Exited) { *retVal = cmsysProcess_GetExitValue(cp); - if (*retVal != 0 && this->OutputTestOutputOnTestFailure) { + if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) { OutputTestErrors(tempOutput); } } else if (result == cmsysProcess_State_Exception) { - if (this->OutputTestOutputOnTestFailure) { + if (this->Impl->OutputTestOutputOnTestFailure) { OutputTestErrors(tempOutput); } *retVal = cmsysProcess_GetExitException(cp); @@ -1330,7 +1374,7 @@ std::string cmCTest::SafeBuildIdField(const std::string& value) void cmCTest::StartXML(cmXMLWriter& xml, bool append) { - if (this->CurrentTag.empty()) { + if (this->Impl->CurrentTag.empty()) { cmCTestLog(this, ERROR_MESSAGE, "Current Tag empty, this may mean" " NightlStartTime was not set correctly." @@ -1346,7 +1390,7 @@ void cmCTest::StartXML(cmXMLWriter& xml, bool append) std::string buildname = cmCTest::SafeBuildIdField(this->GetCTestConfiguration("BuildName")); - std::string stamp = cmCTest::SafeBuildIdField(this->CurrentTag + "-" + + std::string stamp = cmCTest::SafeBuildIdField(this->Impl->CurrentTag + "-" + this->GetTestModelString()); std::string site = cmCTest::SafeBuildIdField(this->GetCTestConfiguration("Site")); @@ -1394,8 +1438,7 @@ void cmCTest::StartXML(cmXMLWriter& xml, bool append) void cmCTest::AddSiteProperties(cmXMLWriter& xml) { - cmCTestScriptHandler* ch = - static_cast<cmCTestScriptHandler*>(this->GetHandler("script")); + cmCTestScriptHandler* ch = this->GetScriptHandler(); cmake* cm = ch->GetCMake(); // if no CMake then this is the old style script and props like // this will not work anyway. @@ -1465,7 +1508,7 @@ void cmCTest::EndXML(cmXMLWriter& xml) } int cmCTest::GenerateCTestNotesOutput(cmXMLWriter& xml, - const cmCTest::VectorOfStrings& files) + std::vector<std::string> const& files) { std::string buildname = cmCTest::SafeBuildIdField(this->GetCTestConfiguration("BuildName")); @@ -1477,10 +1520,10 @@ int cmCTest::GenerateCTestNotesOutput(cmXMLWriter& xml, xml.StartElement("Site"); xml.Attribute("BuildName", buildname); xml.Attribute("BuildStamp", - this->CurrentTag + "-" + this->GetTestModelString()); + this->Impl->CurrentTag + "-" + this->GetTestModelString()); xml.Attribute("Name", this->GetCTestConfiguration("Site")); xml.Attribute("Generator", - std::string("ctest") + cmVersion::GetCMakeVersion()); + std::string("ctest-") + cmVersion::GetCMakeVersion()); this->AddSiteProperties(xml); xml.StartElement("Notes"); @@ -1515,10 +1558,10 @@ int cmCTest::GenerateCTestNotesOutput(cmXMLWriter& xml, return 1; } -int cmCTest::GenerateNotesFile(const VectorOfStrings& files) +int cmCTest::GenerateNotesFile(std::vector<std::string> const& files) { cmGeneratedFileStream ofs; - if (!this->OpenOutputFile(this->CurrentTag, "Notes.xml", ofs)) { + if (!this->OpenOutputFile(this->Impl->CurrentTag, "Notes.xml", ofs)) { cmCTestLog(this, ERROR_MESSAGE, "Cannot open notes file" << std::endl); return 1; } @@ -1533,11 +1576,10 @@ int cmCTest::GenerateNotesFile(const char* cfiles) return 1; } - VectorOfStrings files; - cmCTestLog(this, OUTPUT, "Create notes file" << std::endl); - files = cmSystemTools::SplitString(cfiles, ';'); + std::vector<std::string> const files = + cmSystemTools::SplitString(cfiles, ';'); if (files.empty()) { return 1; } @@ -1548,14 +1590,14 @@ int cmCTest::GenerateNotesFile(const char* cfiles) int cmCTest::GenerateDoneFile() { cmGeneratedFileStream ofs; - if (!this->OpenOutputFile(this->CurrentTag, "Done.xml", ofs)) { + if (!this->OpenOutputFile(this->Impl->CurrentTag, "Done.xml", ofs)) { cmCTestLog(this, ERROR_MESSAGE, "Cannot open done file" << std::endl); return 1; } cmXMLWriter xml(ofs); xml.StartDocument(); xml.StartElement("Done"); - xml.Element("buildId", this->BuildID); + xml.Element("buildId", this->Impl->BuildID); xml.Element("time", std::chrono::system_clock::now()); xml.EndElement(); // Done xml.EndDocument(); @@ -1604,7 +1646,7 @@ std::string cmCTest::Base64EncodeFile(std::string const& file) return std::string(&encoded_buffer[0], rlen); } -bool cmCTest::SubmitExtraFiles(const VectorOfStrings& files) +bool cmCTest::SubmitExtraFiles(std::vector<std::string> const& files) { for (std::string const& file : files) { if (!cmSystemTools::FileExists(file)) { @@ -1624,11 +1666,10 @@ bool cmCTest::SubmitExtraFiles(const char* cfiles) return true; } - VectorOfStrings files; - cmCTestLog(this, OUTPUT, "Submit extra files" << std::endl); - files = cmSystemTools::SplitString(cfiles, ';'); + std::vector<std::string> const files = + cmSystemTools::SplitString(cfiles, ';'); if (files.empty()) { return true; } @@ -1796,17 +1837,17 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, { std::string arg = args[i]; if (this->CheckArgument(arg, "-F")) { - this->Failover = true; + this->Impl->Failover = true; } if (this->CheckArgument(arg, "-j", "--parallel") && i < args.size() - 1) { i++; int plevel = atoi(args[i].c_str()); this->SetParallelLevel(plevel); - this->ParallelLevelSetInCli = true; + this->Impl->ParallelLevelSetInCli = true; } else if (arg.find("-j") == 0) { int plevel = atoi(arg.substr(2).c_str()); this->SetParallelLevel(plevel); - this->ParallelLevelSetInCli = true; + this->Impl->ParallelLevelSetInCli = true; } if (this->CheckArgument(arg, "--repeat-until-fail")) { if (i >= args.size() - 1) { @@ -1820,9 +1861,9 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, "'--repeat-until-fail' given non-integer value '" + args[i] + "'"; return false; } - this->RepeatTests = static_cast<int>(repeat); + this->Impl->RepeatTests = static_cast<int>(repeat); if (repeat > 1) { - this->RepeatUntilFail = true; + this->Impl->RepeatUntilFail = true; } } @@ -1838,21 +1879,21 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, } if (this->CheckArgument(arg, "--no-compress-output")) { - this->CompressTestOutput = false; + this->Impl->CompressTestOutput = false; } if (this->CheckArgument(arg, "--print-labels")) { - this->PrintLabels = true; + this->Impl->PrintLabels = true; } if (this->CheckArgument(arg, "--http1.0")) { - this->UseHTTP10 = true; + this->Impl->UseHTTP10 = true; } if (this->CheckArgument(arg, "--timeout") && i < args.size() - 1) { i++; auto timeout = cmDuration(atof(args[i].c_str())); - this->GlobalTimeout = timeout; + this->Impl->GlobalTimeout = timeout; } if (this->CheckArgument(arg, "--stop-time") && i < args.size() - 1) { @@ -1867,47 +1908,44 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, } if (this->CheckArgument(arg, "--debug")) { - this->Debug = true; - this->ShowLineNumbers = true; + this->Impl->Debug = true; + this->Impl->ShowLineNumbers = true; } if (this->CheckArgument(arg, "--track") && i < args.size() - 1) { i++; - this->SpecificTrack = args[i]; + this->Impl->SpecificTrack = args[i]; } if (this->CheckArgument(arg, "--show-line-numbers")) { - this->ShowLineNumbers = true; + this->Impl->ShowLineNumbers = true; } if (this->CheckArgument(arg, "--no-label-summary")) { - this->LabelSummary = false; + this->Impl->LabelSummary = false; } if (this->CheckArgument(arg, "--no-subproject-summary")) { - this->SubprojectSummary = false; + this->Impl->SubprojectSummary = false; } if (this->CheckArgument(arg, "-Q", "--quiet")) { - this->Quiet = true; + this->Impl->Quiet = true; } if (this->CheckArgument(arg, "--progress")) { - this->TestProgressOutput = true; + this->Impl->TestProgressOutput = true; } if (this->CheckArgument(arg, "-V", "--verbose")) { - this->Verbose = true; + this->Impl->Verbose = true; } if (this->CheckArgument(arg, "-VV", "--extra-verbose")) { - this->ExtraVerbose = true; - this->Verbose = true; + this->Impl->ExtraVerbose = true; + this->Impl->Verbose = true; } if (this->CheckArgument(arg, "--output-on-failure")) { - this->OutputTestOutputOnTestFailure = true; + this->Impl->OutputTestOutputOnTestFailure = true; } if (this->CheckArgument(arg, "--test-output-size-passed") && i < args.size() - 1) { i++; long outputSize; if (cmSystemTools::StringToLong(args[i].c_str(), &outputSize)) { - if (cmCTestTestHandler* pCTestTestHandler = - static_cast<cmCTestTestHandler*>(this->TestingHandlers["test"])) { - pCTestTestHandler->SetTestOutputSizePassed(int(outputSize)); - } + this->Impl->TestHandler.SetTestOutputSizePassed(int(outputSize)); } else { cmCTestLog(this, WARNING, "Invalid value for '--test-output-size-passed': " << args[i] @@ -1919,10 +1957,7 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, i++; long outputSize; if (cmSystemTools::StringToLong(args[i].c_str(), &outputSize)) { - if (cmCTestTestHandler* pCTestTestHandler = - static_cast<cmCTestTestHandler*>(this->TestingHandlers["test"])) { - pCTestTestHandler->SetTestOutputSizeFailed(int(outputSize)); - } + this->Impl->TestHandler.SetTestOutputSizeFailed(int(outputSize)); } else { cmCTestLog(this, WARNING, "Invalid value for '--test-output-size-failed': " << args[i] @@ -1930,10 +1965,10 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, } } if (this->CheckArgument(arg, "-N", "--show-only")) { - this->ShowOnly = true; + this->Impl->ShowOnly = true; } if (cmSystemTools::StringStartsWith(arg.c_str(), "--show-only=")) { - this->ShowOnly = true; + this->Impl->ShowOnly = true; // Check if a specific format is requested. Defaults to human readable // text. @@ -1941,9 +1976,9 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, std::string format = arg.substr(argWithFormat.length()); if (format == "json-v1") { // Force quiet mode so the only output is the json object model. - this->Quiet = true; - this->OutputAsJson = true; - this->OutputAsJsonVersion = 1; + this->Impl->Quiet = true; + this->Impl->OutputAsJson = true; + this->Impl->OutputAsJsonVersion = 1; } else if (format != "human") { errormsg = "'--show-only=' given unknown value '" + format + "'"; return false; @@ -1956,25 +1991,25 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, } if (this->CheckArgument(arg, "--tomorrow-tag")) { - this->TomorrowTag = true; + this->Impl->TomorrowTag = true; } if (this->CheckArgument(arg, "--force-new-ctest-process")) { - this->ForceNewCTestProcess = true; + this->Impl->ForceNewCTestProcess = true; } if (this->CheckArgument(arg, "-W", "--max-width") && i < args.size() - 1) { i++; - this->MaxTestNameWidth = atoi(args[i].c_str()); + this->Impl->MaxTestNameWidth = atoi(args[i].c_str()); } if (this->CheckArgument(arg, "--interactive-debug-mode") && i < args.size() - 1) { i++; - this->InteractiveDebugMode = cmSystemTools::IsOn(args[i]); + this->Impl->InteractiveDebugMode = cmSystemTools::IsOn(args[i]); } if (this->CheckArgument(arg, "--submit-index") && i < args.size() - 1) { i++; - this->SubmitIndex = atoi(args[i].c_str()); - if (this->SubmitIndex < 0) { - this->SubmitIndex = 0; + this->Impl->SubmitIndex = atoi(args[i].c_str()); + if (this->Impl->SubmitIndex < 0) { + this->Impl->SubmitIndex = 0; } } @@ -1983,7 +2018,7 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, this->AddCTestConfigurationOverwrite(args[i]); } if (this->CheckArgument(arg, "-A", "--add-notes") && i < args.size() - 1) { - this->ProduceXML = true; + this->Impl->ProduceXML = true; this->SetTest("Notes"); i++; this->SetNotesFiles(args[i].c_str()); @@ -1993,78 +2028,75 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, if (this->CheckArgument(arg, "-I", "--tests-information") && i < args.size() - 1) { i++; - this->GetHandler("test")->SetPersistentOption("TestsToRunInformation", - args[i].c_str()); - this->GetHandler("memcheck") - ->SetPersistentOption("TestsToRunInformation", args[i].c_str()); + this->GetTestHandler()->SetPersistentOption("TestsToRunInformation", + args[i].c_str()); + this->GetMemCheckHandler()->SetPersistentOption("TestsToRunInformation", + args[i].c_str()); } if (this->CheckArgument(arg, "-U", "--union")) { - this->GetHandler("test")->SetPersistentOption("UseUnion", "true"); - this->GetHandler("memcheck")->SetPersistentOption("UseUnion", "true"); + this->GetTestHandler()->SetPersistentOption("UseUnion", "true"); + this->GetMemCheckHandler()->SetPersistentOption("UseUnion", "true"); } if (this->CheckArgument(arg, "-R", "--tests-regex") && i < args.size() - 1) { i++; - this->GetHandler("test")->SetPersistentOption("IncludeRegularExpression", - args[i].c_str()); - this->GetHandler("memcheck") - ->SetPersistentOption("IncludeRegularExpression", args[i].c_str()); + this->GetTestHandler()->SetPersistentOption("IncludeRegularExpression", + args[i].c_str()); + this->GetMemCheckHandler()->SetPersistentOption("IncludeRegularExpression", + args[i].c_str()); } if (this->CheckArgument(arg, "-L", "--label-regex") && i < args.size() - 1) { i++; - this->GetHandler("test")->SetPersistentOption("LabelRegularExpression", - args[i].c_str()); - this->GetHandler("memcheck") - ->SetPersistentOption("LabelRegularExpression", args[i].c_str()); + this->GetTestHandler()->SetPersistentOption("LabelRegularExpression", + args[i].c_str()); + this->GetMemCheckHandler()->SetPersistentOption("LabelRegularExpression", + args[i].c_str()); } if (this->CheckArgument(arg, "-LE", "--label-exclude") && i < args.size() - 1) { i++; - this->GetHandler("test")->SetPersistentOption( + this->GetTestHandler()->SetPersistentOption( + "ExcludeLabelRegularExpression", args[i].c_str()); + this->GetMemCheckHandler()->SetPersistentOption( "ExcludeLabelRegularExpression", args[i].c_str()); - this->GetHandler("memcheck") - ->SetPersistentOption("ExcludeLabelRegularExpression", args[i].c_str()); } if (this->CheckArgument(arg, "-E", "--exclude-regex") && i < args.size() - 1) { i++; - this->GetHandler("test")->SetPersistentOption("ExcludeRegularExpression", - args[i].c_str()); - this->GetHandler("memcheck") - ->SetPersistentOption("ExcludeRegularExpression", args[i].c_str()); + this->GetTestHandler()->SetPersistentOption("ExcludeRegularExpression", + args[i].c_str()); + this->GetMemCheckHandler()->SetPersistentOption("ExcludeRegularExpression", + args[i].c_str()); } if (this->CheckArgument(arg, "-FA", "--fixture-exclude-any") && i < args.size() - 1) { i++; - this->GetHandler("test")->SetPersistentOption( + this->GetTestHandler()->SetPersistentOption( + "ExcludeFixtureRegularExpression", args[i].c_str()); + this->GetMemCheckHandler()->SetPersistentOption( "ExcludeFixtureRegularExpression", args[i].c_str()); - this->GetHandler("memcheck") - ->SetPersistentOption("ExcludeFixtureRegularExpression", - args[i].c_str()); } if (this->CheckArgument(arg, "-FS", "--fixture-exclude-setup") && i < args.size() - 1) { i++; - this->GetHandler("test")->SetPersistentOption( + this->GetTestHandler()->SetPersistentOption( + "ExcludeFixtureSetupRegularExpression", args[i].c_str()); + this->GetMemCheckHandler()->SetPersistentOption( "ExcludeFixtureSetupRegularExpression", args[i].c_str()); - this->GetHandler("memcheck") - ->SetPersistentOption("ExcludeFixtureSetupRegularExpression", - args[i].c_str()); } if (this->CheckArgument(arg, "-FC", "--fixture-exclude-cleanup") && i < args.size() - 1) { i++; - this->GetHandler("test")->SetPersistentOption( + this->GetTestHandler()->SetPersistentOption( + "ExcludeFixtureCleanupRegularExpression", args[i].c_str()); + this->GetMemCheckHandler()->SetPersistentOption( "ExcludeFixtureCleanupRegularExpression", args[i].c_str()); - this->GetHandler("memcheck") - ->SetPersistentOption("ExcludeFixtureCleanupRegularExpression", - args[i].c_str()); } if (this->CheckArgument(arg, "--rerun-failed")) { - this->GetHandler("test")->SetPersistentOption("RerunFailed", "true"); - this->GetHandler("memcheck")->SetPersistentOption("RerunFailed", "true"); + this->GetTestHandler()->SetPersistentOption("RerunFailed", "true"); + this->GetMemCheckHandler()->SetPersistentOption("RerunFailed", "true"); } return true; } @@ -2111,10 +2143,9 @@ void cmCTest::HandleScriptArguments(size_t& i, std::vector<std::string>& args, std::string arg = args[i]; if (this->CheckArgument(arg, "-SP", "--script-new-process") && i < args.size() - 1) { - this->RunConfigurationScript = true; + this->Impl->RunConfigurationScript = true; i++; - cmCTestScriptHandler* ch = - static_cast<cmCTestScriptHandler*>(this->GetHandler("script")); + cmCTestScriptHandler* ch = this->GetScriptHandler(); // -SR is an internal argument, -SP should be ignored when it is passed if (!SRArgumentSpecified) { ch->AddConfigurationScript(args[i].c_str(), false); @@ -2123,18 +2154,16 @@ void cmCTest::HandleScriptArguments(size_t& i, std::vector<std::string>& args, if (this->CheckArgument(arg, "-SR", "--script-run") && i < args.size() - 1) { SRArgumentSpecified = true; - this->RunConfigurationScript = true; + this->Impl->RunConfigurationScript = true; i++; - cmCTestScriptHandler* ch = - static_cast<cmCTestScriptHandler*>(this->GetHandler("script")); + cmCTestScriptHandler* ch = this->GetScriptHandler(); ch->AddConfigurationScript(args[i].c_str(), true); } if (this->CheckArgument(arg, "-S", "--script") && i < args.size() - 1) { - this->RunConfigurationScript = true; + this->Impl->RunConfigurationScript = true; i++; - cmCTestScriptHandler* ch = - static_cast<cmCTestScriptHandler*>(this->GetHandler("script")); + cmCTestScriptHandler* ch = this->GetScriptHandler(); // -SR is an internal argument, -S should be ignored when it is passed if (!SRArgumentSpecified) { ch->AddConfigurationScript(args[i].c_str(), true); @@ -2149,7 +2178,7 @@ bool cmCTest::AddVariableDefinition(const std::string& arg) cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED; if (cmake::ParseCacheEntry(arg, name, value, type)) { - this->Definitions[name] = value; + this->Impl->Definitions[name] = value; return true; } @@ -2165,8 +2194,8 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) bool SRArgumentSpecified = false; // copy the command line - this->InitialCommandLineArguments.insert( - this->InitialCommandLineArguments.end(), args.begin(), args.end()); + this->Impl->InitialCommandLineArguments.insert( + this->Impl->InitialCommandLineArguments.end(), args.begin(), args.end()); // process the command line arguments for (size_t i = 1; i < args.size(); ++i) { @@ -2183,7 +2212,7 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) // --dashboard: handle a request for a dashboard std::string arg = args[i]; if (this->CheckArgument(arg, "-D", "--dashboard") && i < args.size() - 1) { - this->ProduceXML = true; + this->Impl->ProduceXML = true; i++; std::string targ = args[i]; // AddTestsForDashboard parses the dashboard type and converts it @@ -2219,7 +2248,7 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) // --extra-submit if (this->CheckArgument(arg, "--extra-submit") && i < args.size() - 1) { - this->ProduceXML = true; + this->Impl->ProduceXML = true; this->SetTest("Submit"); i++; if (!this->SubmitExtraFiles(args[i].c_str())) { @@ -2234,14 +2263,14 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) // --schedule-random if (this->CheckArgument(arg, "--schedule-random")) { - this->ScheduleType = "Random"; + this->Impl->ScheduleType = "Random"; } // pass the argument to all the handlers as well, but i may no longer be // set to what it was originally so I'm not sure this is working as // intended - for (auto& handler : this->TestingHandlers) { - if (!handler.second->ProcessCommandLineArguments(arg, i, args)) { + for (auto& handler : this->Impl->GetTestingHandlers()) { + if (!handler->ProcessCommandLineArguments(arg, i, args)) { cmCTestLog(this, ERROR_MESSAGE, "Problem parsing command line arguments within a handler"); return 0; @@ -2250,7 +2279,7 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) } // the close of the for argument loop // handle CTEST_PARALLEL_LEVEL environment variable - if (!this->ParallelLevelSetInCli) { + if (!this->Impl->ParallelLevelSetInCli) { std::string parallel; if (cmSystemTools::GetEnv("CTEST_PARALLEL_LEVEL", parallel)) { int plevel = atoi(parallel.c_str()); @@ -2260,10 +2289,10 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) // TestProgressOutput only supported if console supports it and not logging // to a file - this->TestProgressOutput = this->TestProgressOutput && - !this->OutputLogFile && this->ProgressOutputSupportedByConsole(); + this->Impl->TestProgressOutput = this->Impl->TestProgressOutput && + !this->Impl->OutputLogFile && this->ProgressOutputSupportedByConsole(); #ifdef _WIN32 - if (this->TestProgressOutput) { + if (this->Impl->TestProgressOutput) { // Disable output line buffering so we can print content without // a newline. std::setvbuf(stdout, nullptr, _IONBF, 0); @@ -2290,7 +2319,7 @@ bool cmCTest::HandleTestActionArgument(const char* ctestExec, size_t& i, std::string arg = args[i]; if (this->CheckArgument(arg, "-T", "--test-action") && (i < args.size() - 1)) { - this->ProduceXML = true; + this->Impl->ProduceXML = true; i++; if (!this->SetTest(args[i].c_str(), false)) { success = false; @@ -2350,16 +2379,16 @@ int cmCTest::ExecuteTests() { int res; // call process directory - if (this->RunConfigurationScript) { - if (this->ExtraVerbose) { + if (this->Impl->RunConfigurationScript) { + if (this->Impl->ExtraVerbose) { cmCTestLog(this, OUTPUT, "* Extra verbosity turned on" << std::endl); } - for (auto& handler : this->TestingHandlers) { - handler.second->SetVerbose(this->ExtraVerbose); - handler.second->SetSubmitIndex(this->SubmitIndex); + for (auto& handler : this->Impl->GetTestingHandlers()) { + handler->SetVerbose(this->Impl->ExtraVerbose); + handler->SetSubmitIndex(this->Impl->SubmitIndex); } - this->GetHandler("script")->SetVerbose(this->Verbose); - res = this->GetHandler("script")->ProcessHandler(); + this->GetScriptHandler()->SetVerbose(this->Impl->Verbose); + res = this->GetScriptHandler()->ProcessHandler(); if (res != 0) { cmCTestLog(this, DEBUG, "running script failing returning: " << res << std::endl); @@ -2368,11 +2397,11 @@ int cmCTest::ExecuteTests() } else { // What is this? -V seems to be the same as -VV, // and Verbose is always on in this case - this->ExtraVerbose = this->Verbose; - this->Verbose = true; - for (auto& handler : this->TestingHandlers) { - handler.second->SetVerbose(this->Verbose); - handler.second->SetSubmitIndex(this->SubmitIndex); + this->Impl->ExtraVerbose = this->Impl->Verbose; + this->Impl->Verbose = true; + for (auto& handler : this->Impl->GetTestingHandlers()) { + handler->SetVerbose(this->Impl->Verbose); + handler->SetSubmitIndex(this->Impl->SubmitIndex); } std::string cwd = cmSystemTools::GetCurrentWorkingDirectory(); if (!this->Initialize(cwd.c_str(), nullptr)) { @@ -2393,9 +2422,8 @@ int cmCTest::ExecuteTests() int cmCTest::RunCMakeAndTest(std::string* output) { - this->Verbose = true; - cmCTestBuildAndTestHandler* handler = - static_cast<cmCTestBuildAndTestHandler*>(this->GetHandler("buildtest")); + this->Impl->Verbose = true; + cmCTestBuildAndTestHandler* handler = this->GetBuildAndTestHandler(); int retv = handler->ProcessHandler(); *output = handler->GetOutput(); #ifdef CMAKE_BUILD_WITH_CMAKE @@ -2413,7 +2441,12 @@ void cmCTest::SetNotesFiles(const char* notes) if (!notes) { return; } - this->NotesFiles = notes; + this->Impl->NotesFiles = notes; +} + +std::chrono::system_clock::time_point cmCTest::GetStopTime() const +{ + return this->Impl->StopTime; } void cmCTest::SetStopTime(std::string const& time_str) @@ -2443,21 +2476,29 @@ void cmCTest::SetStopTime(std::string const& time_str) time_t stop_time = curl_getdate(buf, ¤t_time); if (stop_time == -1) { - this->StopTime = std::chrono::system_clock::time_point(); + this->Impl->StopTime = std::chrono::system_clock::time_point(); return; } - this->StopTime = std::chrono::system_clock::from_time_t(stop_time); + this->Impl->StopTime = std::chrono::system_clock::from_time_t(stop_time); if (stop_time < current_time) { - this->StopTime += std::chrono::hours(24); + this->Impl->StopTime += std::chrono::hours(24); } } +std::string cmCTest::GetScheduleType() const +{ + return this->Impl->ScheduleType; +} + +void cmCTest::SetScheduleType(std::string const& type) +{ + this->Impl->ScheduleType = type; +} + int cmCTest::ReadCustomConfigurationFileTree(const char* dir, cmMakefile* mf) { bool found = false; - VectorOfStrings dirs; - VectorOfStrings ndirs; cmCTestLog(this, DEBUG, "* Read custom CTest configuration directory: " << dir << std::endl); @@ -2505,7 +2546,7 @@ int cmCTest::ReadCustomConfigurationFileTree(const char* dir, cmMakefile* mf) } if (found) { - for (auto& handler : this->TestingHandlers) { + for (auto& handler : this->Impl->GetNamedTestingHandlers()) { cmCTestLog(this, DEBUG, "* Read custom CTest configuration vectors for handler: " << handler.first << " (" << handler.second << ")" @@ -2596,16 +2637,16 @@ std::string cmCTest::GetShortPathToFile(const char* cfname) std::string cmCTest::GetCTestConfiguration(const std::string& name) { - if (this->CTestConfigurationOverwrites.find(name) != - this->CTestConfigurationOverwrites.end()) { - return this->CTestConfigurationOverwrites[name]; + if (this->Impl->CTestConfigurationOverwrites.find(name) != + this->Impl->CTestConfigurationOverwrites.end()) { + return this->Impl->CTestConfigurationOverwrites[name]; } - return this->CTestConfiguration[name]; + return this->Impl->CTestConfiguration[name]; } void cmCTest::EmptyCTestConfiguration() { - this->CTestConfiguration.clear(); + this->Impl->CTestConfiguration.clear(); } void cmCTest::SetCTestConfiguration(const char* name, const char* value, @@ -2620,10 +2661,10 @@ void cmCTest::SetCTestConfiguration(const char* name, const char* value, return; } if (!value) { - this->CTestConfiguration.erase(name); + this->Impl->CTestConfiguration.erase(name); return; } - this->CTestConfiguration[name] = value; + this->Impl->CTestConfiguration[name] = value; } std::string cmCTest::GetSubmitURL() @@ -2654,69 +2695,190 @@ std::string cmCTest::GetSubmitURL() std::string cmCTest::GetCurrentTag() { - return this->CurrentTag; + return this->Impl->CurrentTag; } std::string cmCTest::GetBinaryDir() { - return this->BinaryDir; + return this->Impl->BinaryDir; } std::string const& cmCTest::GetConfigType() { - return this->ConfigType; + return this->Impl->ConfigType; +} + +cmDuration cmCTest::GetTimeOut() const +{ + return this->Impl->TimeOut; +} + +void cmCTest::SetTimeOut(cmDuration t) +{ + this->Impl->TimeOut = t; +} + +cmDuration cmCTest::GetGlobalTimeout() const +{ + return this->Impl->GlobalTimeout; } bool cmCTest::GetShowOnly() { - return this->ShowOnly; + return this->Impl->ShowOnly; } bool cmCTest::GetOutputAsJson() { - return this->OutputAsJson; + return this->Impl->OutputAsJson; } int cmCTest::GetOutputAsJsonVersion() { - return this->OutputAsJsonVersion; + return this->Impl->OutputAsJsonVersion; +} + +bool cmCTest::ShouldUseHTTP10() const +{ + return this->Impl->UseHTTP10; +} + +bool cmCTest::ShouldPrintLabels() const +{ + return this->Impl->PrintLabels; } int cmCTest::GetMaxTestNameWidth() const { - return this->MaxTestNameWidth; + return this->Impl->MaxTestNameWidth; +} + +void cmCTest::SetMaxTestNameWidth(int w) +{ + this->Impl->MaxTestNameWidth = w; } void cmCTest::SetProduceXML(bool v) { - this->ProduceXML = v; + this->Impl->ProduceXML = v; } bool cmCTest::GetProduceXML() { - return this->ProduceXML; + return this->Impl->ProduceXML; +} + +std::vector<std::string>& cmCTest::GetInitialCommandLineArguments() +{ + return this->Impl->InitialCommandLineArguments; } const char* cmCTest::GetSpecificTrack() { - if (this->SpecificTrack.empty()) { + if (this->Impl->SpecificTrack.empty()) { return nullptr; } - return this->SpecificTrack.c_str(); + return this->Impl->SpecificTrack.c_str(); } void cmCTest::SetSpecificTrack(const char* track) { if (!track) { - this->SpecificTrack.clear(); + this->Impl->SpecificTrack.clear(); return; } - this->SpecificTrack = track; + this->Impl->SpecificTrack = track; +} + +void cmCTest::SetFailover(bool failover) +{ + this->Impl->Failover = failover; +} + +bool cmCTest::GetFailover() const +{ + return this->Impl->Failover; +} + +bool cmCTest::GetTestProgressOutput() const +{ + return this->Impl->TestProgressOutput; +} + +bool cmCTest::GetVerbose() const +{ + return this->Impl->Verbose; +} + +bool cmCTest::GetExtraVerbose() const +{ + return this->Impl->ExtraVerbose; +} + +void cmCTest::SetStreams(std::ostream* out, std::ostream* err) +{ + this->Impl->StreamOut = out; + this->Impl->StreamErr = err; +} + +bool cmCTest::GetLabelSummary() const +{ + return this->Impl->LabelSummary; +} + +bool cmCTest::GetSubprojectSummary() const +{ + return this->Impl->SubprojectSummary; +} + +bool cmCTest::GetOutputTestOutputOnTestFailure() const +{ + return this->Impl->OutputTestOutputOnTestFailure; +} + +const std::map<std::string, std::string>& cmCTest::GetDefinitions() const +{ + return this->Impl->Definitions; +} + +int cmCTest::GetTestRepeat() const +{ + return this->Impl->RepeatTests; +} + +bool cmCTest::GetRepeatUntilFail() const +{ + return this->Impl->RepeatUntilFail; +} + +void cmCTest::SetBuildID(const std::string& id) +{ + this->Impl->BuildID = id; +} + +std::string cmCTest::GetBuildID() const +{ + return this->Impl->BuildID; } void cmCTest::AddSubmitFile(Part part, const char* name) { - this->Parts[part].SubmitFiles.emplace_back(name); + this->Impl->Parts[part].SubmitFiles.emplace_back(name); +} + +std::vector<std::string> const& cmCTest::GetSubmitFiles(Part part) const +{ + return this->Impl->Parts[part].SubmitFiles; +} + +void cmCTest::ClearSubmitFiles(Part part) +{ + this->Impl->Parts[part].SubmitFiles.clear(); +} + +void cmCTest::SetSuppressUpdatingCTestConfiguration(bool val) +{ + this->Impl->SuppressUpdatingCTestConfiguration = val; } void cmCTest::AddCTestConfigurationOverwrite(const std::string& overStr) @@ -2732,14 +2894,14 @@ void cmCTest::AddCTestConfigurationOverwrite(const std::string& overStr) } std::string key = overStr.substr(0, epos); std::string value = overStr.substr(epos + 1); - this->CTestConfigurationOverwrites[key] = value; + this->Impl->CTestConfigurationOverwrites[key] = value; } void cmCTest::SetConfigType(const char* ct) { - this->ConfigType = ct ? ct : ""; - cmSystemTools::ReplaceString(this->ConfigType, ".\\", ""); - std::string confTypeEnv = "CMAKE_CONFIG_TYPE=" + this->ConfigType; + this->Impl->ConfigType = ct ? ct : ""; + cmSystemTools::ReplaceString(this->Impl->ConfigType, ".\\", ""); + std::string confTypeEnv = "CMAKE_CONFIG_TYPE=" + this->Impl->ConfigType; cmSystemTools::PutEnv(confTypeEnv); } @@ -2776,7 +2938,7 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, stdErr->clear(); cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, &*argv.begin()); + cmsysProcess_SetCommand(cp, argv.data()); cmsysProcess_SetWorkingDirectory(cp, dir); if (cmSystemTools::GetRunCommandHideConsole()) { cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); @@ -2805,12 +2967,12 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, done = true; } if ((res == cmsysProcess_Pipe_STDOUT || res == cmsysProcess_Pipe_STDERR) && - this->ExtraVerbose) { + this->Impl->ExtraVerbose) { processOutput.DecodeText(data, length, strdata); cmSystemTools::Stdout(strdata); } } - if (this->ExtraVerbose) { + if (this->Impl->ExtraVerbose) { processOutput.DecodeText(std::string(), strdata); if (!strdata.empty()) { cmSystemTools::Stdout(strdata); @@ -2820,11 +2982,11 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, cmsysProcess_WaitForExit(cp, nullptr); if (!tempOutput.empty()) { processOutput.DecodeText(tempOutput, tempOutput); - stdOut->append(&*tempOutput.begin(), tempOutput.size()); + stdOut->append(tempOutput.data(), tempOutput.size()); } if (!tempError.empty()) { processOutput.DecodeText(tempError, tempError); - stdErr->append(&*tempError.begin(), tempError.size()); + stdErr->append(tempError.data(), tempError.size()); } bool result = true; @@ -2859,12 +3021,12 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, void cmCTest::SetOutputLogFileName(const char* name) { - if (this->OutputLogFile) { - delete this->OutputLogFile; - this->OutputLogFile = nullptr; + if (this->Impl->OutputLogFile) { + delete this->Impl->OutputLogFile; + this->Impl->OutputLogFile = nullptr; } if (name) { - this->OutputLogFile = new cmGeneratedFileStream(name); + this->Impl->OutputLogFile = new cmGeneratedFileStream(name); } } @@ -2880,18 +3042,11 @@ static const char* cmCTestStringLogType[] = { "DEBUG", #define cmCTestLogOutputFileLine(stream) \ do { \ - if (this->ShowLineNumbers) { \ + if (this->Impl->ShowLineNumbers) { \ (stream) << std::endl << file << ":" << line << " "; \ } \ } while (false) -void cmCTest::InitStreams() -{ - // By default we write output to the process output streams. - this->StreamOut = &std::cout; - this->StreamErr = &std::cerr; -} - void cmCTest::Log(int logType, const char* file, int line, const char* msg, bool suppress) { @@ -2902,53 +3057,53 @@ void cmCTest::Log(int logType, const char* file, int line, const char* msg, return; } if (logType == cmCTest::HANDLER_PROGRESS_OUTPUT && - (this->Debug || this->ExtraVerbose)) { + (this->Impl->Debug || this->Impl->ExtraVerbose)) { return; } - if (this->OutputLogFile) { + if (this->Impl->OutputLogFile) { bool display = true; - if (logType == cmCTest::DEBUG && !this->Debug) { + if (logType == cmCTest::DEBUG && !this->Impl->Debug) { display = false; } - if (logType == cmCTest::HANDLER_VERBOSE_OUTPUT && !this->Debug && - !this->ExtraVerbose) { + if (logType == cmCTest::HANDLER_VERBOSE_OUTPUT && !this->Impl->Debug && + !this->Impl->ExtraVerbose) { display = false; } if (display) { - cmCTestLogOutputFileLine(*this->OutputLogFile); - if (logType != this->OutputLogFileLastTag) { - *this->OutputLogFile << "["; + cmCTestLogOutputFileLine(*this->Impl->OutputLogFile); + if (logType != this->Impl->OutputLogFileLastTag) { + *this->Impl->OutputLogFile << "["; if (logType >= OTHER || logType < 0) { - *this->OutputLogFile << "OTHER"; + *this->Impl->OutputLogFile << "OTHER"; } else { - *this->OutputLogFile << cmCTestStringLogType[logType]; + *this->Impl->OutputLogFile << cmCTestStringLogType[logType]; } - *this->OutputLogFile << "] " << std::endl << std::flush; + *this->Impl->OutputLogFile << "] " << std::endl << std::flush; } - *this->OutputLogFile << msg << std::flush; - if (logType != this->OutputLogFileLastTag) { - *this->OutputLogFile << std::endl << std::flush; - this->OutputLogFileLastTag = logType; + *this->Impl->OutputLogFile << msg << std::flush; + if (logType != this->Impl->OutputLogFileLastTag) { + *this->Impl->OutputLogFile << std::endl << std::flush; + this->Impl->OutputLogFileLastTag = logType; } } } - if (!this->Quiet) { - std::ostream& out = *this->StreamOut; - std::ostream& err = *this->StreamErr; + if (!this->Impl->Quiet) { + std::ostream& out = *this->Impl->StreamOut; + std::ostream& err = *this->Impl->StreamErr; if (logType == HANDLER_TEST_PROGRESS_OUTPUT) { - if (this->TestProgressOutput) { + if (this->Impl->TestProgressOutput) { cmCTestLogOutputFileLine(out); - if (this->FlushTestProgressLine) { + if (this->Impl->FlushTestProgressLine) { printf("\r"); - this->FlushTestProgressLine = false; + this->Impl->FlushTestProgressLine = false; out.flush(); } std::string msg_str{ msg }; auto const lineBreakIt = msg_str.find('\n'); if (lineBreakIt != std::string::npos) { - this->FlushTestProgressLine = true; + this->Impl->FlushTestProgressLine = true; msg_str.erase(std::remove(msg_str.begin(), msg_str.end(), '\n'), msg_str.end()); } @@ -2965,7 +3120,7 @@ void cmCTest::Log(int logType, const char* file, int line, const char* msg, switch (logType) { case DEBUG: - if (this->Debug) { + if (this->Impl->Debug) { cmCTestLogOutputFileLine(out); out << msg; out.flush(); @@ -2973,14 +3128,14 @@ void cmCTest::Log(int logType, const char* file, int line, const char* msg, break; case OUTPUT: case HANDLER_OUTPUT: - if (this->Debug || this->Verbose) { + if (this->Impl->Debug || this->Impl->Verbose) { cmCTestLogOutputFileLine(out); out << msg; out.flush(); } break; case HANDLER_VERBOSE_OUTPUT: - if (this->Debug || this->ExtraVerbose) { + if (this->Impl->Debug || this->Impl->ExtraVerbose) { cmCTestLogOutputFileLine(out); out << msg; out.flush(); @@ -3007,7 +3162,7 @@ void cmCTest::Log(int logType, const char* file, int line, const char* msg, std::string cmCTest::GetColorCode(Color color) const { - if (this->OutputColorCode) { + if (this->Impl->OutputColorCode) { #if defined(_WIN32) // Not supported on Windows static_cast<void>(color); @@ -3021,14 +3176,7 @@ std::string cmCTest::GetColorCode(Color color) const cmDuration cmCTest::GetRemainingTimeAllowed() { - if (!this->GetHandler("script")) { - return cmCTest::MaxDuration(); - } - - cmCTestScriptHandler* ch = - static_cast<cmCTestScriptHandler*>(this->GetHandler("script")); - - return ch->GetRemainingTimeAllowed(); + return this->GetScriptHandler()->GetRemainingTimeAllowed(); } cmDuration cmCTest::MaxDuration() @@ -3038,17 +3186,14 @@ cmDuration cmCTest::MaxDuration() void cmCTest::SetRunCurrentScript(bool value) { - cmCTestScriptHandler* ch = - static_cast<cmCTestScriptHandler*>(this->GetHandler("script")); - - ch->SetRunCurrentScript(value); + this->GetScriptHandler()->SetRunCurrentScript(value); } void cmCTest::OutputTestErrors(std::vector<char> const& process_output) { std::string test_outputs("\n*** Test Failed:\n"); if (!process_output.empty()) { - test_outputs.append(&*process_output.begin(), process_output.size()); + test_outputs.append(process_output.data(), process_output.size()); } cmCTestLog(this, HANDLER_OUTPUT, test_outputs << std::endl << std::flush); } diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 92a02c3..d300c33 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -10,13 +10,22 @@ #include <chrono> #include <map> -#include <set> +#include <memory> // IWYU pragma: keep #include <sstream> #include <string> #include <time.h> #include <vector> -class cmCTestGenericHandler; +class cmCTestBuildHandler; +class cmCTestBuildAndTestHandler; +class cmCTestCoverageHandler; +class cmCTestScriptHandler; +class cmCTestTestHandler; +class cmCTestUpdateHandler; +class cmCTestConfigureHandler; +class cmCTestMemCheckHandler; +class cmCTestSubmitHandler; +class cmCTestUploadHandler; class cmCTestStartCommand; class cmGeneratedFileStream; class cmMakefile; @@ -31,9 +40,6 @@ class cmXMLWriter; */ class cmCTest { - friend class cmCTestRunTest; - friend class cmCTestMultiProcessHandler; - public: typedef cmProcessOutput::Encoding Encoding; /** Enumerate parts of the testing and submission process. */ @@ -54,44 +60,10 @@ public: PartCount // Update names in constructor when adding a part }; - /** Representation of one part. */ - struct PartInfo - { - void SetName(const std::string& name) { this->Name = name; } - const std::string& GetName() const { return this->Name; } - - void Enable() { this->Enabled = true; } - explicit operator bool() const { return this->Enabled; } - - std::vector<std::string> SubmitFiles; - - private: - bool Enabled = false; - std::string Name; - }; -#ifdef CMAKE_BUILD_WITH_CMAKE - enum HTTPMethod - { - HTTP_GET, - HTTP_POST, - HTTP_PUT - }; - - /** - * Perform an HTTP request. - */ - static int HTTPRequest(std::string url, HTTPMethod method, - std::string& response, std::string const& fields = "", - std::string const& putFile = "", int timeout = 0); -#endif - /** Get a testing part id from its string name. Returns PartCount if the string does not name a valid part. */ Part GetPartFromName(const char* name); - typedef std::vector<std::string> VectorOfStrings; - typedef std::set<std::string> SetOfStrings; - /** Process Command line arguments */ int Run(std::vector<std::string>&, std::string* output = nullptr); @@ -128,7 +100,7 @@ public: /** * Is the tomorrow tag set? */ - bool GetTomorrowTag() { return this->TomorrowTag; } + bool GetTomorrowTag() const; /** * Try to run tests of the project @@ -137,16 +109,16 @@ public: /** what is the configuration type, e.g. Debug, Release etc. */ std::string const& GetConfigType(); - cmDuration GetTimeOut() { return this->TimeOut; } - void SetTimeOut(cmDuration t) { this->TimeOut = t; } + cmDuration GetTimeOut() const; + void SetTimeOut(cmDuration t); - cmDuration GetGlobalTimeout() { return this->GlobalTimeout; } + cmDuration GetGlobalTimeout() const; /** how many test to run at the same time */ - int GetParallelLevel() { return this->ParallelLevel; } + int GetParallelLevel() const; void SetParallelLevel(int); - unsigned long GetTestLoad() { return this->TestLoad; } + unsigned long GetTestLoad() const; void SetTestLoad(unsigned long); /** @@ -164,7 +136,7 @@ public: * Set the cmake test mode (experimental, nightly, continuous). */ void SetTestModel(int mode); - int GetTestModel() { return this->TestModel; } + int GetTestModel() const; std::string GetTestModelString(); static int GetTestModelFromString(const char* str); @@ -182,6 +154,9 @@ public: cmCTest(); ~cmCTest(); + cmCTest(const cmCTest&) = delete; + cmCTest& operator=(const cmCTest&) = delete; + /** Set the notes files to be created. */ void SetNotesFiles(const char* notes); @@ -219,26 +194,23 @@ public: int GetOutputAsJsonVersion(); - bool ShouldUseHTTP10() { return this->UseHTTP10; } + bool ShouldUseHTTP10() const; - bool ShouldPrintLabels() { return this->PrintLabels; } + bool ShouldPrintLabels() const; bool ShouldCompressTestOutput(); bool CompressString(std::string& str); - std::chrono::system_clock::time_point GetStopTime() - { - return this->StopTime; - } + std::chrono::system_clock::time_point GetStopTime() const; void SetStopTime(std::string const& time); /** Used for parallel ctest job scheduling */ - std::string GetScheduleType() { return this->ScheduleType; } - void SetScheduleType(std::string const& type) { this->ScheduleType = type; } + std::string GetScheduleType() const; + void SetScheduleType(std::string const& type); /** The max output width */ int GetMaxTestNameWidth() const; - void SetMaxTestNameWidth(int w) { this->MaxTestNameWidth = w; } + void SetMaxTestNameWidth(int w); /** * Run a single executable command and put the stdout and stderr @@ -277,8 +249,9 @@ public: * Run command specialized for make and configure. Returns process status * and retVal is return value or exception. */ - int RunMakeCommand(const char* command, std::string& output, int* retVal, - const char* dir, cmDuration timeout, std::ostream& ofs, + int RunMakeCommand(const std::string& command, std::string& output, + int* retVal, const char* dir, cmDuration timeout, + std::ostream& ofs, Encoding encoding = cmProcessOutput::Auto); /** Return the current tag */ @@ -331,16 +304,18 @@ public: Encoding encoding = cmProcessOutput::Auto); /** - * Execute handler and return its result. If the handler fails, it returns - * negative value. - */ - int ExecuteHandler(const char* handler); - - /** * Get the handler object */ - cmCTestGenericHandler* GetHandler(const char* handler); - cmCTestGenericHandler* GetInitializedHandler(const char* handler); + cmCTestBuildHandler* GetBuildHandler(); + cmCTestBuildAndTestHandler* GetBuildAndTestHandler(); + cmCTestCoverageHandler* GetCoverageHandler(); + cmCTestScriptHandler* GetScriptHandler(); + cmCTestTestHandler* GetTestHandler(); + cmCTestUpdateHandler* GetUpdateHandler(); + cmCTestConfigureHandler* GetConfigureHandler(); + cmCTestMemCheckHandler* GetMemCheckHandler(); + cmCTestSubmitHandler* GetSubmitHandler(); + cmCTestUploadHandler* GetUploadHandler(); /** * Set the CTest variable from CMake variable @@ -350,9 +325,6 @@ public: const std::string& cmake_var, bool suppress = false); - /** Make string safe to be sent as a URL */ - static std::string MakeURLSafe(const std::string&); - /** Decode a URL to the original string. */ static std::string DecodeURL(const std::string&); @@ -360,10 +332,7 @@ public: * Should ctect configuration be updated. When using new style ctest * script, this should be true. */ - void SetSuppressUpdatingCTestConfiguration(bool val) - { - this->SuppressUpdatingCTestConfiguration = val; - } + void SetSuppressUpdatingCTestConfiguration(bool val); /** * Add overwrite to ctest configuration. @@ -373,14 +342,14 @@ public: void AddCTestConfigurationOverwrite(const std::string& encstr); /** Create XML file that contains all the notes specified */ - int GenerateNotesFile(const VectorOfStrings& files); + int GenerateNotesFile(std::vector<std::string> const& files); /** Create XML file to indicate that build is complete */ int GenerateDoneFile(); /** Submit extra files to the server */ bool SubmitExtraFiles(const char* files); - bool SubmitExtraFiles(const VectorOfStrings& files); + bool SubmitExtraFiles(std::vector<std::string> const& files); /** Set the output log file name */ void SetOutputLogFileName(const char* name); @@ -420,62 +389,52 @@ public: std::string GetColorCode(Color color) const; /** The Build ID is assigned by CDash */ - void SetBuildID(const std::string& id) { this->BuildID = id; } - std::string GetBuildID() { return this->BuildID; } + void SetBuildID(const std::string& id); + std::string GetBuildID() const; /** Add file to be submitted */ void AddSubmitFile(Part part, const char* name); - std::vector<std::string> const& GetSubmitFiles(Part part) - { - return this->Parts[part].SubmitFiles; - } - void ClearSubmitFiles(Part part) { this->Parts[part].SubmitFiles.clear(); } + std::vector<std::string> const& GetSubmitFiles(Part part) const; + void ClearSubmitFiles(Part part); /** * Read the custom configuration files and apply them to the current ctest */ int ReadCustomConfigurationFileTree(const char* dir, cmMakefile* mf); - std::vector<std::string>& GetInitialCommandLineArguments() - { - return this->InitialCommandLineArguments; - } + std::vector<std::string>& GetInitialCommandLineArguments(); /** Set the track to submit to */ void SetSpecificTrack(const char* track); const char* GetSpecificTrack(); - void SetFailover(bool failover) { this->Failover = failover; } - bool GetFailover() { return this->Failover; } + void SetFailover(bool failover); + bool GetFailover() const; - bool GetTestProgressOutput() const { return this->TestProgressOutput; } + bool GetTestProgressOutput() const; - bool GetVerbose() { return this->Verbose; } - bool GetExtraVerbose() { return this->ExtraVerbose; } + bool GetVerbose() const; + bool GetExtraVerbose() const; /** Direct process output to given streams. */ - void SetStreams(std::ostream* out, std::ostream* err) - { - this->StreamOut = out; - this->StreamErr = err; - } + void SetStreams(std::ostream* out, std::ostream* err); + void AddSiteProperties(cmXMLWriter& xml); - bool GetLabelSummary() { return this->LabelSummary; } - bool GetSubprojectSummary() { return this->SubprojectSummary; } + bool GetLabelSummary() const; + bool GetSubprojectSummary() const; std::string GetCostDataFile(); - const std::map<std::string, std::string>& GetDefinitions() - { - return this->Definitions; - } + bool GetOutputTestOutputOnTestFailure() const; + + const std::map<std::string, std::string>& GetDefinitions() const; /** Return the number of times a test should be run */ - int GetTestRepeat() { return this->RepeatTests; } + int GetTestRepeat() const; /** Return true if test should run until fail */ - bool GetRepeatUntilFail() { return this->RepeatUntilFail; } + bool GetRepeatUntilFail() const; void GenerateSubprojectsOutput(cmXMLWriter& xml); std::vector<std::string> GetLabelsForSubprojects(); @@ -483,85 +442,8 @@ public: void SetRunCurrentScript(bool value); private: - int RepeatTests; - bool RepeatUntilFail; - std::string ConfigType; - std::string ScheduleType; - std::chrono::system_clock::time_point StopTime; - bool TestProgressOutput; - bool Verbose; - bool ExtraVerbose; - bool ProduceXML; - bool LabelSummary; - bool SubprojectSummary; - bool UseHTTP10; - bool PrintLabels; - bool Failover; - - bool FlushTestProgressLine; - - bool ForceNewCTestProcess; - - bool RunConfigurationScript; - int GenerateNotesFile(const char* files); - // these are helper classes - typedef std::map<std::string, cmCTestGenericHandler*> t_TestingHandlers; - t_TestingHandlers TestingHandlers; - - bool ShowOnly; - bool OutputAsJson; - int OutputAsJsonVersion; - - /** Map of configuration properties */ - typedef std::map<std::string, std::string> CTestConfigurationMap; - - // TODO: The ctest configuration should be a hierarchy of - // configuration option sources: command-line, script, ini file. - // Then the ini file can get re-loaded whenever it changes without - // affecting any higher-precedence settings. - CTestConfigurationMap CTestConfiguration; - CTestConfigurationMap CTestConfigurationOverwrites; - PartInfo Parts[PartCount]; - typedef std::map<std::string, Part> PartMapType; - PartMapType PartMap; - - std::string CurrentTag; - bool TomorrowTag; - - int TestModel; - std::string SpecificTrack; - - cmDuration TimeOut; - - cmDuration GlobalTimeout; - - int MaxTestNameWidth; - - int ParallelLevel; - bool ParallelLevelSetInCli; - - unsigned long TestLoad; - - int CompatibilityMode; - - // information for the --build-and-test options - std::string BinaryDir; - - std::string NotesFiles; - - bool InteractiveDebugMode; - - bool ShortDateFormat; - - bool CompressXMLFiles; - bool CompressTestOutput; - - void InitStreams(); - std::ostream* StreamOut; - std::ostream* StreamErr; - void BlockTestErrorDiagnostics(); /** @@ -606,7 +488,8 @@ private: bool UpdateCTestConfiguration(); /** Create note from files. */ - int GenerateCTestNotesOutput(cmXMLWriter& xml, const VectorOfStrings& files); + int GenerateCTestNotesOutput(cmXMLWriter& xml, + std::vector<std::string> const& files); /** Check if the argument is the one specified */ bool CheckArgument(const std::string& arg, const char* varg1, @@ -626,25 +509,8 @@ private: int RunCMakeAndTest(std::string* output); int ExecuteTests(); - bool SuppressUpdatingCTestConfiguration; - - bool Debug; - bool ShowLineNumbers; - bool Quiet; - - std::string BuildID; - - std::vector<std::string> InitialCommandLineArguments; - - int SubmitIndex; - - cmGeneratedFileStream* OutputLogFile; - int OutputLogFileLastTag; - - bool OutputTestOutputOnTestFailure; - bool OutputColorCode; - - std::map<std::string, std::string> Definitions; + struct Private; + std::unique_ptr<Private> Impl; }; class cmCTestLogWrite diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx index 2728f0f..6116de0 100644 --- a/Source/cmCacheManager.cxx +++ b/Source/cmCacheManager.cxx @@ -239,8 +239,7 @@ bool cmCacheManager::SaveCache(const std::string& path, cmMessenger* messenger) cmGeneratedFileStream fout(cacheFile); fout.SetCopyIfDifferent(true); if (!fout) { - cmSystemTools::Error("Unable to open cache file for save. ", - cacheFile.c_str()); + cmSystemTools::Error("Unable to open cache file for save. " + cacheFile); cmSystemTools::ReportLastSystemError(""); return false; } @@ -364,8 +363,8 @@ bool cmCacheManager::SaveCache(const std::string& path, cmMessenger* messenger) checkCacheFile += "/cmake.check_cache"; cmsys::ofstream checkCache(checkCacheFile.c_str()); if (!checkCache) { - cmSystemTools::Error("Unable to open check cache file for write. ", - checkCacheFile.c_str()); + cmSystemTools::Error("Unable to open check cache file for write. " + + checkCacheFile); return false; } checkCache << "# This file is generated by cmake for dependency checking " diff --git a/Source/cmCacheManager.h b/Source/cmCacheManager.h index 0c70ed2..65f22f7 100644 --- a/Source/cmCacheManager.h +++ b/Source/cmCacheManager.h @@ -93,33 +93,33 @@ public: CacheEntry& GetEntry() { return this->Position->second; } }; - ///! return an iterator to iterate through the cache map + //! return an iterator to iterate through the cache map cmCacheManager::CacheIterator NewIterator() { return CacheIterator(*this); } - ///! Load a cache for given makefile. Loads from path/CMakeCache.txt. + //! Load a cache for given makefile. Loads from path/CMakeCache.txt. bool LoadCache(const std::string& path, bool internal, std::set<std::string>& excludes, std::set<std::string>& includes); - ///! Save cache for given makefile. Saves to output path/CMakeCache.txt + //! Save cache for given makefile. Saves to output path/CMakeCache.txt bool SaveCache(const std::string& path, cmMessenger* messenger); - ///! Delete the cache given + //! Delete the cache given bool DeleteCache(const std::string& path); - ///! Print the cache to a stream + //! Print the cache to a stream void PrintCache(std::ostream&) const; - ///! Get the iterator for an entry with a given key. + //! Get the iterator for an entry with a given key. cmCacheManager::CacheIterator GetCacheIterator(const char* key = nullptr); - ///! Remove an entry from the cache + //! Remove an entry from the cache void RemoveCacheEntry(const std::string& key); - ///! Get the number of entries in the cache + //! Get the number of entries in the cache int GetSize() { return static_cast<int>(this->Cache.size()); } - ///! Get a value from the cache given a key + //! Get a value from the cache given a key const std::string* GetInitializedCacheValue(const std::string& key) const; const char* GetCacheEntryValue(const std::string& key) @@ -197,14 +197,14 @@ public: unsigned int GetCacheMinorVersion() const { return this->CacheMinorVersion; } protected: - ///! Add an entry into the cache + //! Add an entry into the cache void AddCacheEntry(const std::string& key, const char* value, const char* helpString, cmStateEnums::CacheEntryType type); - ///! Get a cache entry object for a key + //! Get a cache entry object for a key CacheEntry* GetCacheEntry(const std::string& key); - ///! Clean out the CMakeFiles directory if no CMakeCache.txt + //! Clean out the CMakeFiles directory if no CMakeCache.txt void CleanCMakeFiles(const std::string& path); // Cache version info diff --git a/Source/cmCallVisualStudioMacro.cxx b/Source/cmCallVisualStudioMacro.cxx index ee5feee..2f6cf64 100644 --- a/Source/cmCallVisualStudioMacro.cxx +++ b/Source/cmCallVisualStudioMacro.cxx @@ -35,8 +35,8 @@ static bool LogErrorsAsMessages; # endif # endif -///! Use ReportHRESULT to make a cmSystemTools::Message after calling -///! a COM method that may have failed. +//! Use ReportHRESULT to make a cmSystemTools::Message after calling +//! a COM method that may have failed. # define ReportHRESULT(hr, context) \ if (FAILED(hr)) { \ if (LogErrorsAsMessages) { \ @@ -50,7 +50,7 @@ static bool LogErrorsAsMessages; } \ } -///! Using the given instance of Visual Studio, call the named macro +//! Using the given instance of Visual Studio, call the named macro HRESULT InstanceCallMacro(IDispatch* vsIDE, const std::string& macro, const std::string& args) { @@ -133,7 +133,7 @@ HRESULT InstanceCallMacro(IDispatch* vsIDE, const std::string& macro, return hr; } -///! Get the Solution object from the IDE object +//! Get the Solution object from the IDE object HRESULT GetSolutionObject(IDispatch* vsIDE, IDispatchPtr& vsSolution) { HRESULT hr = E_POINTER; @@ -176,7 +176,7 @@ HRESULT GetSolutionObject(IDispatch* vsIDE, IDispatchPtr& vsSolution) return hr; } -///! Get the FullName property from the Solution object +//! Get the FullName property from the Solution object HRESULT GetSolutionFullName(IDispatch* vsSolution, std::string& fullName) { HRESULT hr = E_POINTER; @@ -220,7 +220,7 @@ HRESULT GetSolutionFullName(IDispatch* vsSolution, std::string& fullName) return hr; } -///! Get the FullName property from the Solution object, given the IDE object +//! Get the FullName property from the Solution object, given the IDE object HRESULT GetIDESolutionFullName(IDispatch* vsIDE, std::string& fullName) { IDispatchPtr vsSolution; @@ -235,8 +235,8 @@ HRESULT GetIDESolutionFullName(IDispatch* vsIDE, std::string& fullName) return hr; } -///! Get all running objects from the Windows running object table. -///! Save them in a map by their display names. +//! Get all running objects from the Windows running object table. +//! Save them in a map by their display names. HRESULT GetRunningInstances(std::map<std::string, IUnknownPtr>& mrot) { // mrot == Map of the Running Object Table @@ -292,8 +292,8 @@ HRESULT GetRunningInstances(std::map<std::string, IUnknownPtr>& mrot) return hr; } -///! Do the two file names refer to the same Visual Studio solution? Or are -///! we perhaps looking for any and all solutions? +//! Do the two file names refer to the same Visual Studio solution? Or are +//! we perhaps looking for any and all solutions? bool FilesSameSolution(const std::string& slnFile, const std::string& slnName) { if (slnFile == "ALL" || slnName == "ALL") { @@ -310,9 +310,9 @@ bool FilesSameSolution(const std::string& slnFile, const std::string& slnName) return s1 == s2; } -///! Find instances of Visual Studio with the given solution file -///! open. Pass "ALL" for slnFile to gather all running instances -///! of Visual Studio. +//! Find instances of Visual Studio with the given solution file +//! open. Pass "ALL" for slnFile to gather all running instances +//! of Visual Studio. HRESULT FindVisualStudioInstances(const std::string& slnFile, std::vector<IDispatchPtr>& instances) { @@ -384,8 +384,8 @@ int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances( return count; } -///! Get all running objects from the Windows running object table. -///! Save them in a map by their display names. +//! Get all running objects from the Windows running object table. +//! Save them in a map by their display names. int cmCallVisualStudioMacro::CallMacro(const std::string& slnFile, const std::string& macro, const std::string& args, diff --git a/Source/cmCallVisualStudioMacro.h b/Source/cmCallVisualStudioMacro.h index fdc9e66..9b5b3a8 100644 --- a/Source/cmCallVisualStudioMacro.h +++ b/Source/cmCallVisualStudioMacro.h @@ -16,16 +16,16 @@ class cmCallVisualStudioMacro { public: - ///! Call the named macro in instances of Visual Studio with the - ///! given solution file open. Pass "ALL" for slnFile to call the - ///! macro in each Visual Studio instance. + //! Call the named macro in instances of Visual Studio with the + //! given solution file open. Pass "ALL" for slnFile to call the + //! macro in each Visual Studio instance. static int CallMacro(const std::string& slnFile, const std::string& macro, const std::string& args, const bool logErrorsAsMessages); - ///! Count the number of running instances of Visual Studio with the - ///! given solution file open. Pass "ALL" for slnFile to count all - ///! running Visual Studio instances. + //! Count the number of running instances of Visual Studio with the + //! given solution file open. Pass "ALL" for slnFile to count all + //! running Visual Studio instances. static int GetNumberOfRunningVisualStudioInstances( const std::string& slnFile); diff --git a/Source/cmCommandArgumentsHelper.cxx b/Source/cmCommandArgumentsHelper.cxx deleted file mode 100644 index 968b17c..0000000 --- a/Source/cmCommandArgumentsHelper.cxx +++ /dev/null @@ -1,233 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmCommandArgumentsHelper.h" - -cmCommandArgument::cmCommandArgument(cmCommandArgumentsHelper* args, - const char* key, - cmCommandArgumentGroup* group) - : Key(key) - , Group(group) - , WasActive(false) - , ArgumentsBeforeEmpty(true) - , CurrentIndex(0) -{ - if (args != nullptr) { - args->AddArgument(this); - } - - if (this->Group != nullptr) { - this->Group->ContainedArguments.push_back(this); - } -} - -void cmCommandArgument::Reset() -{ - this->WasActive = false; - this->CurrentIndex = 0; - this->DoReset(); -} - -void cmCommandArgument::Follows(const cmCommandArgument* arg) -{ - this->ArgumentsBeforeEmpty = false; - this->ArgumentsBefore.insert(arg); -} - -void cmCommandArgument::FollowsGroup(const cmCommandArgumentGroup* group) -{ - if (group != nullptr) { - this->ArgumentsBeforeEmpty = false; - this->ArgumentsBefore.insert(group->ContainedArguments.begin(), - group->ContainedArguments.end()); - } -} - -bool cmCommandArgument::MayFollow(const cmCommandArgument* current) const -{ - if (this->ArgumentsBeforeEmpty) { - return true; - } - return this->ArgumentsBefore.find(current) != this->ArgumentsBefore.end(); -} - -bool cmCommandArgument::KeyMatches(const std::string& key) const -{ - if ((this->Key == nullptr) || (this->Key[0] == '\0')) { - return true; - } - return (key == this->Key); -} - -void cmCommandArgument::ApplyOwnGroup() -{ - if (this->Group != nullptr) { - for (cmCommandArgument* cargs : this->Group->ContainedArguments) { - if (cargs != this) { - this->ArgumentsBefore.insert(cargs); - } - } - } -} - -void cmCommandArgument::Activate() -{ - this->WasActive = true; - this->CurrentIndex = 0; -} - -bool cmCommandArgument::Consume(const std::string& arg) -{ - bool res = this->DoConsume(arg, this->CurrentIndex); - this->CurrentIndex++; - return res; -} - -cmCAStringVector::cmCAStringVector(cmCommandArgumentsHelper* args, - const char* key, - cmCommandArgumentGroup* group) - : cmCommandArgument(args, key, group) - , Ignore(nullptr) -{ - if ((key == nullptr) || (*key == 0)) { - this->DataStart = 0; - } else { - this->DataStart = 1; - } -} - -bool cmCAStringVector::DoConsume(const std::string& arg, unsigned int index) -{ - if (index >= this->DataStart) { - if ((this->Ignore == nullptr) || (arg != this->Ignore)) { - this->Vector.push_back(arg); - } - } - - return false; -} - -void cmCAStringVector::DoReset() -{ - this->Vector.clear(); -} - -cmCAString::cmCAString(cmCommandArgumentsHelper* args, const char* key, - cmCommandArgumentGroup* group) - : cmCommandArgument(args, key, group) -{ - if ((key == nullptr) || (*key == 0)) { - this->DataStart = 0; - } else { - this->DataStart = 1; - } -} - -bool cmCAString::DoConsume(const std::string& arg, unsigned int index) -{ - if (index == this->DataStart) { - this->String = arg; - } - - return index >= this->DataStart; -} - -void cmCAString::DoReset() -{ - this->String.clear(); -} - -cmCAEnabler::cmCAEnabler(cmCommandArgumentsHelper* args, const char* key, - cmCommandArgumentGroup* group) - : cmCommandArgument(args, key, group) - , Enabled(false) -{ -} - -bool cmCAEnabler::DoConsume(const std::string&, unsigned int index) -{ - if (index == 0) { - this->Enabled = true; - } - return true; -} - -void cmCAEnabler::DoReset() -{ - this->Enabled = false; -} - -cmCADisabler::cmCADisabler(cmCommandArgumentsHelper* args, const char* key, - cmCommandArgumentGroup* group) - : cmCommandArgument(args, key, group) - , Enabled(true) -{ -} - -bool cmCADisabler::DoConsume(const std::string&, unsigned int index) -{ - if (index == 0) { - this->Enabled = false; - } - return true; -} - -void cmCADisabler::DoReset() -{ - this->Enabled = true; -} - -void cmCommandArgumentGroup::Follows(const cmCommandArgument* arg) -{ - for (cmCommandArgument* ca : this->ContainedArguments) { - ca->Follows(arg); - } -} - -void cmCommandArgumentGroup::FollowsGroup(const cmCommandArgumentGroup* group) -{ - for (cmCommandArgument* ca : this->ContainedArguments) { - ca->FollowsGroup(group); - } -} - -void cmCommandArgumentsHelper::Parse(const std::vector<std::string>* args, - std::vector<std::string>* unconsumedArgs) -{ - if (args == nullptr) { - return; - } - - for (cmCommandArgument* ca : this->Arguments) { - ca->ApplyOwnGroup(); - ca->Reset(); - } - - cmCommandArgument* activeArgument = nullptr; - const cmCommandArgument* previousArgument = nullptr; - for (std::string const& it : *args) { - for (cmCommandArgument* ca : this->Arguments) { - if (ca->KeyMatches(it) && (ca->MayFollow(previousArgument))) { - activeArgument = ca; - activeArgument->Activate(); - break; - } - } - - if (activeArgument) { - bool argDone = activeArgument->Consume(it); - previousArgument = activeArgument; - if (argDone) { - activeArgument = nullptr; - } - } else { - if (unconsumedArgs != nullptr) { - unconsumedArgs->push_back(it); - } - } - } -} - -void cmCommandArgumentsHelper::AddArgument(cmCommandArgument* arg) -{ - this->Arguments.push_back(arg); -} diff --git a/Source/cmCommandArgumentsHelper.h b/Source/cmCommandArgumentsHelper.h deleted file mode 100644 index dc934be..0000000 --- a/Source/cmCommandArgumentsHelper.h +++ /dev/null @@ -1,194 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCommandArgumentsHelper_h -#define cmCommandArgumentsHelper_h - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <set> -#include <string> -#include <vector> - -class cmCommandArgumentGroup; -class cmCommandArgumentsHelper; - -/* cmCommandArgumentsHelper, cmCommandArgumentGroup and cmCommandArgument (i.e. -its derived classes cmCAXXX can be used to simplify the processing of -arguments to cmake commands. Maybe they can also be used to generate -documentation. - -For every argument supported by a command one cmCommandArgument is created -and added to cmCommandArgumentsHelper. cmCommand has a cmCommandArgumentsHelper -as member variable so this should be used. - -The order of the arguments is defined using the Follows(arg) method. It says -that this argument follows immediateley the given argument. It can be used -with multiple arguments if the argument can follow after different arguments. - -Arguments can be arranged in groups using cmCommandArgumentGroup. Every -member of a group can follow any other member of the group. These groups -can also be used to define the order. - -Once all arguments and groups are set up, cmCommandArgumentsHelper::Parse() -is called and afterwards the values of the arguments can be evaluated. - -For an example see cmExportCommand.cxx. -*/ -class cmCommandArgument -{ -public: - cmCommandArgument(cmCommandArgumentsHelper* args, const char* key, - cmCommandArgumentGroup* group = nullptr); - virtual ~cmCommandArgument() = default; - - /// this argument may follow after arg. 0 means it comes first. - void Follows(const cmCommandArgument* arg); - - /// this argument may follow after any of the arguments in the given group - void FollowsGroup(const cmCommandArgumentGroup* group); - - /// Returns true if the argument was found in the argument list - bool WasFound() const { return this->WasActive; } - - // The following methods are only called from - // cmCommandArgumentsHelper::Parse(), but making this a friend would - // give it access to everything - - /// Make the current argument the currently active argument - void Activate(); - /// Consume the current string - bool Consume(const std::string& arg); - - /// Return true if this argument may follow after the given argument. - bool MayFollow(const cmCommandArgument* current) const; - - /** Returns true if the given key matches the key for this argument. - If this argument has an empty key everything matches. */ - bool KeyMatches(const std::string& key) const; - - /// Make this argument follow all members of the own group - void ApplyOwnGroup(); - - /// Reset argument, so it's back to its initial state - void Reset(); - -private: - const char* Key; - std::set<const cmCommandArgument*> ArgumentsBefore; - cmCommandArgumentGroup* Group; - bool WasActive; - bool ArgumentsBeforeEmpty; - unsigned int CurrentIndex; - - virtual bool DoConsume(const std::string& arg, unsigned int index) = 0; - virtual void DoReset() = 0; -}; - -/** cmCAStringVector is to be used for arguments which can consist of more -than one string, e.g. the FILES argument in INSTALL(FILES f1 f2 f3 ...). */ -class cmCAStringVector : public cmCommandArgument -{ -public: - cmCAStringVector(cmCommandArgumentsHelper* args, const char* key, - cmCommandArgumentGroup* group = nullptr); - - /// Return the vector of strings - const std::vector<std::string>& GetVector() const { return this->Vector; } - - /** Is there a keyword which should be skipped in - the arguments (e.g. ARGS for ADD_CUSTOM_COMMAND) ? */ - void SetIgnore(const char* ignore) { this->Ignore = ignore; } - -private: - std::vector<std::string> Vector; - unsigned int DataStart; - const char* Ignore; - bool DoConsume(const std::string& arg, unsigned int index) override; - void DoReset() override; -}; - -/** cmCAString is to be used for arguments which consist of one value, -e.g. the executable name in ADD_EXECUTABLE(). */ -class cmCAString : public cmCommandArgument -{ -public: - cmCAString(cmCommandArgumentsHelper* args, const char* key, - cmCommandArgumentGroup* group = nullptr); - - /// Return the string - const std::string& GetString() const { return this->String; } - const char* GetCString() const { return this->String.c_str(); } - -private: - std::string String; - unsigned int DataStart; - bool DoConsume(const std::string& arg, unsigned int index) override; - void DoReset() override; -}; - -/** cmCAEnabler is to be used for options which are off by default and can be -enabled using a special argument, e.g. EXCLUDE_FROM_ALL in ADD_EXECUTABLE(). */ -class cmCAEnabler : public cmCommandArgument -{ -public: - cmCAEnabler(cmCommandArgumentsHelper* args, const char* key, - cmCommandArgumentGroup* group = nullptr); - - /// Has it been enabled ? - bool IsEnabled() const { return this->Enabled; } - -private: - bool Enabled; - bool DoConsume(const std::string& arg, unsigned int index) override; - void DoReset() override; -}; - -/** cmCADisable is to be used for options which are on by default and can be -disabled using a special argument.*/ -class cmCADisabler : public cmCommandArgument -{ -public: - cmCADisabler(cmCommandArgumentsHelper* args, const char* key, - cmCommandArgumentGroup* group = nullptr); - - /// Is it still enabled ? - bool IsEnabled() const { return this->Enabled; } - -private: - bool Enabled; - bool DoConsume(const std::string& arg, unsigned int index) override; - void DoReset() override; -}; - -/** Group of arguments, needed for ordering. E.g. WIN32, EXCLUDE_FROM_ALL and -MACSOX_BUNDLE from ADD_EXECUTABLE() are a group. -*/ -class cmCommandArgumentGroup -{ - friend class cmCommandArgument; - -public: - /// All members of this group may follow the given argument - void Follows(const cmCommandArgument* arg); - - /// All members of this group may follow all members of the given group - void FollowsGroup(const cmCommandArgumentGroup* group); - -private: - std::vector<cmCommandArgument*> ContainedArguments; -}; - -class cmCommandArgumentsHelper -{ -public: - /// Parse the argument list - void Parse(const std::vector<std::string>* args, - std::vector<std::string>* unconsumedArgs); - /// Add an argument. - void AddArgument(cmCommandArgument* arg); - -private: - std::vector<cmCommandArgument*> Arguments; -}; - -#endif diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index 4717cf6..186deb6 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -9,6 +9,7 @@ #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmRange.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" @@ -256,11 +257,7 @@ cmComputeLinkDepends::Compute() // Iterate in reverse order so we can keep only the last occurrence // of a shared library. std::set<int> emmitted; - for (std::vector<int>::const_reverse_iterator - li = this->FinalLinkOrder.rbegin(), - le = this->FinalLinkOrder.rend(); - li != le; ++li) { - int i = *li; + for (int i : cmReverseRange(this->FinalLinkOrder)) { LinkEntry const& e = this->EntryList[i]; cmGeneratorTarget const* t = e.Target; // Entries that we know the linker will re-use do not need to be repeated. @@ -586,11 +583,10 @@ void cmComputeLinkDepends::InferDependencies() } // Intersect the sets for this item. - DependSetList::const_iterator i = sets->begin(); - DependSet common = *i; - for (++i; i != sets->end(); ++i) { + DependSet common = sets->front(); + for (DependSet const& i : cmMakeRange(*sets).advance(1)) { DependSet intersection; - std::set_intersection(common.begin(), common.end(), i->begin(), i->end(), + std::set_intersection(common.begin(), common.end(), i.begin(), i.end(), std::inserter(intersection, intersection.begin())); common = intersection; } @@ -708,9 +704,8 @@ void cmComputeLinkDepends::VisitComponent(unsigned int c) // Run in reverse order so the topological order will preserve the // original order where there are no constraints. EdgeList const& nl = this->CCG->GetComponentGraphEdges(c); - for (EdgeList::const_reverse_iterator ni = nl.rbegin(); ni != nl.rend(); - ++ni) { - this->VisitComponent(*ni); + for (cmGraphEdge const& edge : cmReverseRange(nl)) { + this->VisitComponent(edge); } // Assign an ordering id to this component. diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h index 252f874..dfaaf8b 100644 --- a/Source/cmComputeLinkDepends.h +++ b/Source/cmComputeLinkDepends.h @@ -31,6 +31,9 @@ public: const std::string& config); ~cmComputeLinkDepends(); + cmComputeLinkDepends(const cmComputeLinkDepends&) = delete; + cmComputeLinkDepends& operator=(const cmComputeLinkDepends&) = delete; + // Basic information about each link item. struct LinkEntry { diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 3d61665..44d8615 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -457,8 +457,8 @@ bool cmComputeLinkInformation::Compute() // We require a link language for the target. if (this->LinkLanguage.empty()) { cmSystemTools::Error( - "CMake can not determine linker language for target: ", - this->Target->GetName().c_str()); + "CMake can not determine linker language for target: " + + this->Target->GetName()); return false; } @@ -1266,7 +1266,7 @@ void cmComputeLinkInformation::AddFrameworkItem(std::string const& item) void cmComputeLinkInformation::AddDirectoryItem(std::string const& item) { if (this->Makefile->IsOn("APPLE") && - cmSystemTools::IsPathToFramework(item.c_str())) { + cmSystemTools::IsPathToFramework(item)) { this->AddFrameworkItem(item); } else { this->DropDirectoryItem(item); diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx index 9b7b02f..01d4c07 100644 --- a/Source/cmComputeTargetDepends.cxx +++ b/Source/cmComputeTargetDepends.cxx @@ -11,6 +11,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStateTypes.h" @@ -549,10 +550,9 @@ bool cmComputeTargetDepends::ComputeFinalDepends( int head = -1; std::set<int> emitted; NodeList const& nl = components[c]; - for (NodeList::const_reverse_iterator ni = nl.rbegin(); ni != nl.rend(); - ++ni) { + for (int ni : cmReverseRange(nl)) { std::set<int> visited; - if (!this->IntraComponent(cmap, c, *ni, &head, emitted, visited)) { + if (!this->IntraComponent(cmap, c, ni, &head, emitted, visited)) { // Cycle in add_dependencies within component! this->ComplainAboutBadComponent(ccg, c, true); return false; diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx index 94ea529..303b147 100644 --- a/Source/cmConditionEvaluator.cxx +++ b/Source/cmConditionEvaluator.cxx @@ -130,8 +130,8 @@ bool cmConditionEvaluator::IsTrue( return false; } - return this->GetBooleanValueWithAutoDereference(*(newArgs.begin()), - errorString, status, true); + return this->GetBooleanValueWithAutoDereference(newArgs.front(), errorString, + status, true); } //========================================================================= diff --git a/Source/cmConfigureFileCommand.cxx b/Source/cmConfigureFileCommand.cxx index 8224a0f..0917d11 100644 --- a/Source/cmConfigureFileCommand.cxx +++ b/Source/cmConfigureFileCommand.cxx @@ -102,7 +102,7 @@ bool cmConfigureFileCommand::InitialPass(std::vector<std::string> const& args, int cmConfigureFileCommand::ConfigureFile() { - return this->Makefile->ConfigureFile( - this->InputFile.c_str(), this->OutputFile.c_str(), this->CopyOnly, - this->AtOnly, this->EscapeQuotes, this->NewLineStyle); + return this->Makefile->ConfigureFile(this->InputFile, this->OutputFile, + this->CopyOnly, this->AtOnly, + this->EscapeQuotes, this->NewLineStyle); } diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 3892011..2f62db1 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -20,6 +20,7 @@ #include "cmSystemTools.h" #include "cmTarget.h" #include "cmVersion.h" +#include "cm_static_string_view.hxx" #include "cmake.h" static std::string const kCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN = @@ -42,6 +43,8 @@ static std::string const kCMAKE_LINK_SEARCH_END_STATIC = "CMAKE_LINK_SEARCH_END_STATIC"; static std::string const kCMAKE_LINK_SEARCH_START_STATIC = "CMAKE_LINK_SEARCH_START_STATIC"; +static std::string const kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT = + "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT"; static std::string const kCMAKE_OSX_ARCHITECTURES = "CMAKE_OSX_ARCHITECTURES"; static std::string const kCMAKE_OSX_DEPLOYMENT_TARGET = "CMAKE_OSX_DEPLOYMENT_TARGET"; @@ -60,7 +63,8 @@ static std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED"; /* GHS Multi platform variables */ static std::set<std::string> ghs_platform_vars{ "GHS_TARGET_PLATFORM", "GHS_PRIMARY_TARGET", "GHS_TOOLSET_ROOT", - "GHS_OS_ROOT", "GHS_OS_DIR", "GHS_BSP_NAME" + "GHS_OS_ROOT", "GHS_OS_DIR", "GHS_BSP_NAME", + "GHS_OS_DIR_OPTION" }; static void writeProperty(FILE* fout, std::string const& targetName, @@ -499,6 +503,13 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def); } + /* Set MSVC runtime library policy to match our selection. */ + if (const char* msvcRuntimeLibraryDefault = + this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) { + fprintf(fout, "cmake_policy(SET CMP0091 %s)\n", + *msvcRuntimeLibraryDefault ? "NEW" : "OLD"); + } + std::string projectLangs; for (std::string const& li : testLangs) { projectLangs += " " + li; @@ -659,6 +670,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, vars.insert(kCMAKE_SYSROOT_COMPILE); vars.insert(kCMAKE_SYSROOT_LINK); vars.insert(kCMAKE_WARN_DEPRECATED); + vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s); if (const char* varListStr = this->Makefile->GetDefinition( kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) { @@ -896,7 +908,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, // Forward the GHS variables to the inner project cache. for (std::string const& var : ghs_platform_vars) { if (const char* val = this->Makefile->GetDefinition(var)) { - std::string flag = "-D" + var + "=" + val; + std::string flag = "-D" + var + "=" + "'" + val + "'"; cmakeFlags.push_back(std::move(flag)); } } @@ -965,8 +977,8 @@ void cmCoreTryCompile::CleanupFiles(std::string const& binDir) if (binDir.find("CMakeTmp") == std::string::npos) { cmSystemTools::Error( "TRY_COMPILE attempt to remove -rf directory that does not contain " - "CMakeTmp:", - binDir.c_str()); + "CMakeTmp:" + + binDir); return; } @@ -1040,7 +1052,14 @@ void cmCoreTryCompile::FindOutputFile(const std::string& targetName, } searchDirs.emplace_back("/Debug"); #if defined(__APPLE__) - std::string app = "/Debug/" + targetName + ".app"; + std::string app = "/" + targetName + ".app"; + if (config && config[0]) { + std::string tmp = "/"; + tmp += config + app; + searchDirs.push_back(std::move(tmp)); + } + std::string tmp = "/Debug" + app; + searchDirs.emplace_back(std::move(tmp)); searchDirs.push_back(std::move(app)); #endif searchDirs.emplace_back("/Development"); diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx index 69532e6..b78493f 100644 --- a/Source/cmCreateTestSourceList.cxx +++ b/Source/cmCreateTestSourceList.cxx @@ -136,8 +136,7 @@ bool cmCreateTestSourceList::InitialPass(std::vector<std::string> const& args, this->Makefile->AddDefinition("CMAKE_FUNCTION_TABLE_ENTIRES", functionMapCode.c_str()); bool res = true; - if (!this->Makefile->ConfigureFile(configFile.c_str(), driver.c_str(), false, - true, false)) { + if (!this->Makefile->ConfigureFile(configFile, driver, false, true, false)) { res = false; } diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h index 9684cff..7fd60c0 100644 --- a/Source/cmCustomCommandGenerator.h +++ b/Source/cmCustomCommandGenerator.h @@ -32,6 +32,9 @@ public: cmCustomCommandGenerator(cmCustomCommand const& cc, std::string config, cmLocalGenerator* lg); ~cmCustomCommandGenerator(); + cmCustomCommandGenerator(const cmCustomCommandGenerator&) = delete; + cmCustomCommandGenerator& operator=(const cmCustomCommandGenerator&) = + delete; cmCustomCommand const& GetCC() const { return this->CC; } unsigned int GetNumberOfCommands() const; std::string GetCommand(unsigned int c) const; diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx index 7d1d316..ed76dbf 100644 --- a/Source/cmDepends.cxx +++ b/Source/cmDepends.cxx @@ -2,7 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmDepends.h" -#include "cmFileTimeComparison.h" +#include "cmFileTime.h" +#include "cmFileTimeCache.h" #include "cmGeneratedFileStream.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" @@ -10,46 +11,39 @@ #include "cmsys/FStream.hxx" #include <sstream> -#include <string.h> #include <utility> cmDepends::cmDepends(cmLocalGenerator* lg, std::string targetDir) : LocalGenerator(lg) , TargetDirectory(std::move(targetDir)) - , Dependee(new char[MaxPath]) - , Depender(new char[MaxPath]) { } -cmDepends::~cmDepends() -{ - delete[] this->Dependee; - delete[] this->Depender; -} +cmDepends::~cmDepends() = default; bool cmDepends::Write(std::ostream& makeDepends, std::ostream& internalDepends) { - // Lookup the set of sources to scan. - std::string srcLang = "CMAKE_DEPENDS_CHECK_"; - srcLang += this->Language; - cmMakefile* mf = this->LocalGenerator->GetMakefile(); - std::string const& srcStr = mf->GetSafeDefinition(srcLang); - std::vector<std::string> pairs; - cmSystemTools::ExpandListArgument(srcStr, pairs); - std::map<std::string, std::set<std::string>> dependencies; - for (std::vector<std::string>::iterator si = pairs.begin(); - si != pairs.end();) { - // Get the source and object file. - std::string const& src = *si++; - if (si == pairs.end()) { - break; + { + // Lookup the set of sources to scan. + std::vector<std::string> pairs; + { + std::string const srcLang = "CMAKE_DEPENDS_CHECK_" + this->Language; + cmMakefile* mf = this->LocalGenerator->GetMakefile(); + cmSystemTools::ExpandListArgument(mf->GetSafeDefinition(srcLang), pairs); + } + for (std::vector<std::string>::iterator si = pairs.begin(); + si != pairs.end();) { + // Get the source and object file. + std::string const& src = *si++; + if (si == pairs.end()) { + break; + } + std::string const& obj = *si++; + dependencies[obj].insert(src); } - std::string const& obj = *si++; - dependencies[obj].insert(src); } for (auto const& d : dependencies) { - // Write the dependencies for this pair. if (!this->WriteDependencies(d.second, d.first, makeDepends, internalDepends)) { @@ -67,7 +61,7 @@ bool cmDepends::Finalize(std::ostream& /*unused*/, std::ostream& /*unused*/) bool cmDepends::Check(const std::string& makeFile, const std::string& internalFile, - std::map<std::string, DependencyVector>& validDeps) + DependencyMap& validDeps) { // Check whether dependencies must be regenerated. bool okay = true; @@ -76,9 +70,9 @@ bool cmDepends::Check(const std::string& makeFile, // Clear all dependencies so they will be regenerated. this->Clear(makeFile); cmSystemTools::RemoveFile(internalFile); + this->FileTimeCache->Remove(internalFile); okay = false; } - return okay; } @@ -107,48 +101,62 @@ bool cmDepends::WriteDependencies(const std::set<std::string>& /*unused*/, return false; } -bool cmDepends::CheckDependencies( - std::istream& internalDepends, const std::string& internalDependsFileName, - std::map<std::string, DependencyVector>& validDeps) +bool cmDepends::CheckDependencies(std::istream& internalDepends, + const std::string& internalDependsFileName, + DependencyMap& validDeps) { + // Read internal depends file time + cmFileTime internalDependsTime; + if (!this->FileTimeCache->Load(internalDependsFileName, + internalDependsTime)) { + return false; + } + // Parse dependencies from the stream. If any dependee is missing // or newer than the depender then dependencies should be // regenerated. bool okay = true; bool dependerExists = false; - DependencyVector* currentDependencies = nullptr; - while (internalDepends.getline(this->Dependee, this->MaxPath)) { - if (this->Dependee[0] == 0 || this->Dependee[0] == '#' || - this->Dependee[0] == '\r') { + std::string line; + line.reserve(1024); + std::string depender; + std::string dependee; + cmFileTime dependerTime; + cmFileTime dependeeTime; + std::vector<std::string>* currentDependencies = nullptr; + + while (std::getline(internalDepends, line)) { + // Check if this an empty or a comment line + if (line.empty() || line.front() == '#') { continue; } - size_t len = internalDepends.gcount() - 1; - if (this->Dependee[len - 1] == '\r') { - len--; - this->Dependee[len] = 0; + // Drop carriage return character at the end + if (line.back() == '\r') { + line.pop_back(); + if (line.empty()) { + continue; + } } - if (this->Dependee[0] != ' ') { - memcpy(this->Depender, this->Dependee, len + 1); - // Calling FileExists() for the depender here saves in many cases 50% - // of the calls to FileExists() further down in the loop. E.g. for - // kdelibs/khtml this reduces the number of calls from 184k down to 92k, - // or the time for cmake -E cmake_depends from 0.3 s down to 0.21 s. - dependerExists = cmSystemTools::FileExists(this->Depender); + // Check if this a depender line + if (line.front() != ' ') { + depender = line; + dependerExists = this->FileTimeCache->Load(depender, dependerTime); // If we erase validDeps[this->Depender] by overwriting it with an empty // vector, we lose dependencies for dependers that have multiple // entries. No need to initialize the entry, std::map will do so on first // access. - currentDependencies = &validDeps[this->Depender]; + currentDependencies = &validDeps[depender]; continue; } - /* - // Parse the dependency line. - if(!this->ParseDependency(line.c_str())) - { - continue; - } - */ + + // This is a dependee line + dependee = line.substr(1); + + // Add dependee to depender's list + if (currentDependencies != nullptr) { + currentDependencies->push_back(dependee); + } // Dependencies must be regenerated // * if the dependee does not exist @@ -156,13 +164,8 @@ bool cmDepends::CheckDependencies( // * if the depender does not exist, but the dependee is newer than the // depends file bool regenerate = false; - const std::string dependee(this->Dependee + 1); - const std::string depender(this->Depender); - if (currentDependencies != nullptr) { - currentDependencies->push_back(dependee); - } - - if (!cmSystemTools::FileExists(dependee)) { + bool dependeeExists = this->FileTimeCache->Load(dependee, dependeeTime); + if (!dependeeExists) { // The dependee does not exist. regenerate = true; @@ -173,45 +176,38 @@ bool cmDepends::CheckDependencies( << depender << "\"." << std::endl; cmSystemTools::Stdout(msg.str()); } - } else { - if (dependerExists) { - // The dependee and depender both exist. Compare file times. - int result = 0; - if ((!this->FileComparison->FileTimeCompare(depender, dependee, - &result) || - result < 0)) { - // The depender is older than the dependee. - regenerate = true; - - // Print verbose output. - if (this->Verbose) { - std::ostringstream msg; - msg << "Dependee \"" << dependee << "\" is newer than depender \"" - << depender << "\"." << std::endl; - cmSystemTools::Stdout(msg.str()); - } + } else if (dependerExists) { + // The dependee and depender both exist. Compare file times. + if (dependerTime.Older(dependeeTime)) { + // The depender is older than the dependee. + regenerate = true; + + // Print verbose output. + if (this->Verbose) { + std::ostringstream msg; + msg << "Dependee \"" << dependee << "\" is newer than depender \"" + << depender << "\"." << std::endl; + cmSystemTools::Stdout(msg.str()); } - } else { - // The dependee exists, but the depender doesn't. Regenerate if the - // internalDepends file is older than the dependee. - int result = 0; - if ((!this->FileComparison->FileTimeCompare(internalDependsFileName, - dependee, &result) || - result < 0)) { - // The depends-file is older than the dependee. - regenerate = true; - - // Print verbose output. - if (this->Verbose) { - std::ostringstream msg; - msg << "Dependee \"" << dependee - << "\" is newer than depends file \"" - << internalDependsFileName << "\"." << std::endl; - cmSystemTools::Stdout(msg.str()); - } + } + } else { + // The dependee exists, but the depender doesn't. Regenerate if the + // internalDepends file is older than the dependee. + if (internalDependsTime.Older(dependeeTime)) { + // The depends-file is older than the dependee. + regenerate = true; + + // Print verbose output. + if (this->Verbose) { + std::ostringstream msg; + msg << "Dependee \"" << dependee + << "\" is newer than depends file \"" << internalDependsFileName + << "\"." << std::endl; + cmSystemTools::Stdout(msg.str()); } } } + if (regenerate) { // Dependencies must be regenerated. okay = false; @@ -219,13 +215,14 @@ bool cmDepends::CheckDependencies( // Remove the information of this depender from the map, it needs // to be rescanned if (currentDependencies != nullptr) { - validDeps.erase(this->Depender); + validDeps.erase(depender); currentDependencies = nullptr; } // Remove the depender to be sure it is rebuilt. if (dependerExists) { cmSystemTools::RemoveFile(depender); + this->FileTimeCache->Remove(depender); dependerExists = false; } } diff --git a/Source/cmDepends.h b/Source/cmDepends.h index 20c91ca..b7475f0 100644 --- a/Source/cmDepends.h +++ b/Source/cmDepends.h @@ -8,11 +8,10 @@ #include <iosfwd> #include <map> #include <set> -#include <stddef.h> #include <string> #include <vector> -class cmFileTimeComparison; +class cmFileTimeCache; class cmLocalGenerator; /** \class cmDepends @@ -25,6 +24,9 @@ class cmLocalGenerator; class cmDepends { public: + typedef std::map<std::string, std::vector<std::string>> DependencyMap; + +public: /** Instances need to know the build directory name and the relative path from the build directory to the target file. */ cmDepends(cmLocalGenerator* lg = nullptr, std::string targetDir = ""); @@ -56,26 +58,19 @@ public: /** Write dependencies for the target file. */ bool Write(std::ostream& makeDepends, std::ostream& internalDepends); - class DependencyVector : public std::vector<std::string> - { - }; - /** Check dependencies for the target file. Returns true if dependencies are okay and false if they must be generated. If they must be generated Clear has already been called to wipe out the old dependencies. Dependencies which are still valid will be stored in validDeps. */ bool Check(const std::string& makeFile, const std::string& internalFile, - std::map<std::string, DependencyVector>& validDeps); + DependencyMap& validDeps); /** Clear dependencies for the target file so they will be regenerated. */ void Clear(const std::string& file); /** Set the file comparison object */ - void SetFileComparison(cmFileTimeComparison* fc) - { - this->FileComparison = fc; - } + void SetFileTimeCache(cmFileTimeCache* fc) { this->FileTimeCache = fc; } protected: // Write dependencies for the target file to the given stream. @@ -88,9 +83,9 @@ protected: // Check dependencies for the target file in the given stream. // Return false if dependencies must be regenerated and true // otherwise. - virtual bool CheckDependencies( - std::istream& internalDepends, const std::string& internalDependsFileName, - std::map<std::string, DependencyVector>& validDeps); + virtual bool CheckDependencies(std::istream& internalDepends, + const std::string& internalDependsFileName, + DependencyMap& validDeps); // Finalize the dependency information for the target. virtual bool Finalize(std::ostream& makeDepends, @@ -101,17 +96,13 @@ protected: // Flag for verbose output. bool Verbose = false; - cmFileTimeComparison* FileComparison = nullptr; + cmFileTimeCache* FileTimeCache = nullptr; std::string Language; // The full path to the target's build directory. std::string TargetDirectory; - size_t MaxPath = 16384; - char* Dependee; - char* Depender; - // The include file search path. std::vector<std::string> IncludePath; diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx index b1630f9..dc49c18 100644 --- a/Source/cmDependsC.cxx +++ b/Source/cmDependsC.cxx @@ -6,7 +6,7 @@ #include <utility> #include "cmAlgorithms.h" -#include "cmFileTimeComparison.h" +#include "cmFileTime.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmSystemTools.h" @@ -21,9 +21,8 @@ cmDependsC::cmDependsC() = default; -cmDependsC::cmDependsC( - cmLocalGenerator* lg, const std::string& targetDir, const std::string& lang, - const std::map<std::string, DependencyVector>* validDeps) +cmDependsC::cmDependsC(cmLocalGenerator* lg, const std::string& targetDir, + const std::string& lang, const DependencyMap* validDeps) : cmDepends(lg, targetDir) , ValidDeps(validDeps) { @@ -102,8 +101,7 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources, this->LocalGenerator->MaybeConvertToRelativePath(binDir, obj); if (this->ValidDeps != nullptr) { - std::map<std::string, DependencyVector>::const_iterator tmpIt = - this->ValidDeps->find(obj_i); + auto const tmpIt = this->ValidDeps->find(obj_i); if (tmpIt != this->ValidDeps->end()) { dependencies.insert(tmpIt->second.begin(), tmpIt->second.end()); haveDeps = true; @@ -123,12 +121,6 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources, } std::set<std::string> scanned; - - // Use reserve to allocate enough memory for tempPathStr - // so that during the loops no memory is allocated or freed - std::string tempPathStr; - tempPathStr.reserve(4 * 1024); - while (!this->Unscanned.empty()) { // Get the next file to scan. UnscannedEntry current = this->Unscanned.front(); @@ -147,22 +139,21 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources, // the source containing the include statement. fullName = current.QuotedLocation; } else { - std::map<std::string, std::string>::iterator headerLocationIt = + auto headerLocationIt = this->HeaderLocationCache.find(current.FileName); if (headerLocationIt != this->HeaderLocationCache.end()) { fullName = headerLocationIt->second; } else { - for (std::string const& i : this->IncludePath) { + for (std::string const& iPath : this->IncludePath) { // Construct the name of the file as if it were in the current // include directory. Avoid using a leading "./". - - tempPathStr = - cmSystemTools::CollapseCombinedPath(i, current.FileName); + std::string tmpPath = + cmSystemTools::CollapseFullPath(current.FileName, iPath); // Look for the file in this location. - if (cmSystemTools::FileExists(tempPathStr, true)) { - fullName = tempPathStr; - HeaderLocationCache[current.FileName] = fullName; + if (cmSystemTools::FileExists(tmpPath, true)) { + fullName = tmpPath; + this->HeaderLocationCache[current.FileName] = std::move(tmpPath); break; } } @@ -173,8 +164,7 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources, // regex. if (fullName.empty() && this->IncludeRegexComplain.find(current.FileName)) { - cmSystemTools::Error("Cannot find file \"", current.FileName.c_str(), - "\"."); + cmSystemTools::Error("Cannot find file \"" + current.FileName + "\"."); return false; } @@ -184,8 +174,7 @@ bool cmDependsC::WriteDependencies(const std::set<std::string>& sources, scanned.insert(fullName); // Check whether this file is already in the cache - std::map<std::string, cmIncludeLines*>::iterator fileIt = - this->FileCache.find(fullName); + auto fileIt = this->FileCache.find(fullName); if (fileIt != this->FileCache.end()) { fileIt->second->Used = true; dependencies.insert(fullName); @@ -257,6 +246,8 @@ void cmDependsC::ReadCacheFile() cmIncludeLines* cacheEntry = nullptr; bool haveFileName = false; + cmFileTime cacheFileTime; + bool const cacheFileTimeGood = cacheFileTime.Load(this->CacheFileName); while (cmSystemTools::GetLineFromStream(fin, line)) { if (line.empty()) { cacheEntry = nullptr; @@ -266,11 +257,12 @@ void cmDependsC::ReadCacheFile() // the first line after an empty line is the name of the parsed file if (!haveFileName) { haveFileName = true; - int newer = 0; - cmFileTimeComparison comp; - bool res = comp.FileTimeCompare(this->CacheFileName, line, &newer); - if (res && newer == 1) // cache is newer than the parsed file + cmFileTime fileTime; + bool const res = cacheFileTimeGood && fileTime.Load(line); + bool const newer = res && cacheFileTime.Newer(fileTime); + + if (res && newer) // cache is newer than the parsed file { cacheEntry = new cmIncludeLines; this->FileCache[line] = cacheEntry; @@ -368,7 +360,7 @@ void cmDependsC::Scan(std::istream& is, const std::string& directory, // must check for the file in the directory containing the // file we are scanning. entry.QuotedLocation = - cmSystemTools::CollapseCombinedPath(directory, entry.FileName); + cmSystemTools::CollapseFullPath(entry.FileName, directory); } // Queue the file if it has not yet been encountered and it diff --git a/Source/cmDependsC.h b/Source/cmDependsC.h index eee5ae1..3fc839e 100644 --- a/Source/cmDependsC.h +++ b/Source/cmDependsC.h @@ -27,8 +27,7 @@ public: relative path from the build directory to the target file. */ cmDependsC(); cmDependsC(cmLocalGenerator* lg, const std::string& targetDir, - const std::string& lang, - const std::map<std::string, DependencyVector>* validDeps); + const std::string& lang, const DependencyMap* validDeps); /** Virtual destructor to cleanup subclasses properly. */ ~cmDependsC() override; @@ -81,7 +80,7 @@ public: }; protected: - const std::map<std::string, DependencyVector>* ValidDeps = nullptr; + const DependencyMap* ValidDeps = nullptr; std::set<std::string> Encountered; std::queue<UnscannedEntry> Unscanned; diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx index 3f036a9..178e18b 100644 --- a/Source/cmDependsFortran.cxx +++ b/Source/cmDependsFortran.cxx @@ -295,7 +295,7 @@ void cmDependsFortran::MatchRemoteModules(std::istream& fin, // They do not include the ".mod" extension. mod += ".mod"; } - this->ConsiderModule(mod.c_str() + 1, stampDir); + this->ConsiderModule(mod.substr(1), stampDir); } } else if (line == "provides") { doing_provides = true; diff --git a/Source/cmDependsJava.cxx b/Source/cmDependsJava.cxx index 2485e15..b17b2ba 100644 --- a/Source/cmDependsJava.cxx +++ b/Source/cmDependsJava.cxx @@ -24,8 +24,7 @@ bool cmDependsJava::WriteDependencies(const std::set<std::string>& sources, bool cmDependsJava::CheckDependencies( std::istream& /*internalDepends*/, - const std::string& /*internalDependsFileName*/, - std::map<std::string, DependencyVector>& /*validDeps*/) + const std::string& /*internalDependsFileName*/, DependencyMap& /*validDeps*/) { return true; } diff --git a/Source/cmDependsJava.h b/Source/cmDependsJava.h index 109ef13..dd671a1 100644 --- a/Source/cmDependsJava.h +++ b/Source/cmDependsJava.h @@ -8,7 +8,6 @@ #include "cmDepends.h" #include <iosfwd> -#include <map> #include <set> #include <string> @@ -33,9 +32,9 @@ protected: bool WriteDependencies(const std::set<std::string>& sources, const std::string& file, std::ostream& makeDepends, std::ostream& internalDepends) override; - bool CheckDependencies( - std::istream& internalDepends, const std::string& internalDependsFileName, - std::map<std::string, DependencyVector>& validDeps) override; + bool CheckDependencies(std::istream& internalDepends, + const std::string& internalDependsFileName, + DependencyMap& validDeps) override; }; #endif diff --git a/Source/cmDependsJavaParserHelper.cxx b/Source/cmDependsJavaParserHelper.cxx index 792db48..12d875d 100644 --- a/Source/cmDependsJavaParserHelper.cxx +++ b/Source/cmDependsJavaParserHelper.cxx @@ -5,6 +5,7 @@ #include "cmDependsJavaLexer.h" #include "cmSystemTools.h" +#include "cm_string_view.hxx" #include "cmsys/FStream.hxx" #include <iostream> #include <stdio.h> @@ -298,14 +299,10 @@ void cmDependsJavaParserHelper::Error(const char* str) unsigned long pos = static_cast<unsigned long>(this->InputBufferPos); fprintf(stderr, "JPError: %s (%lu / Line: %d)\n", str, pos, this->CurrentLine); - int cc; - std::cerr << "String: ["; - for (cc = 0; - cc < 30 && *(this->InputBuffer.c_str() + this->InputBufferPos + cc); - cc++) { - std::cerr << *(this->InputBuffer.c_str() + this->InputBufferPos + cc); - } - std::cerr << "]" << std::endl; + std::cerr << "String: [" + << cm::string_view{ this->InputBuffer }.substr( + this->InputBufferPos, 30) + << "]" << std::endl; } void cmDependsJavaParserHelper::UpdateCombine(const char* str1, diff --git a/Source/cmDependsJavaParserHelper.h b/Source/cmDependsJavaParserHelper.h index 0b445d9..a673b5b 100644 --- a/Source/cmDependsJavaParserHelper.h +++ b/Source/cmDependsJavaParserHelper.h @@ -24,6 +24,10 @@ public: cmDependsJavaParserHelper(); ~cmDependsJavaParserHelper(); + cmDependsJavaParserHelper(const cmDependsJavaParserHelper&) = delete; + cmDependsJavaParserHelper& operator=(const cmDependsJavaParserHelper&) = + delete; + int ParseString(const char* str, int verb); int ParseFile(const char* file); diff --git a/Source/cmELF.h b/Source/cmELF.h index c8a91e4..987f0c3 100644 --- a/Source/cmELF.h +++ b/Source/cmELF.h @@ -28,6 +28,9 @@ public: /** Destruct. */ ~cmELF(); + cmELF(const cmELF&) = delete; + cmELF& operator=(const cmELF&) = delete; + /** Get the error message if any. */ std::string const& GetErrorMessage() const { return this->ErrorMessage; } diff --git a/Source/cmExecProgramCommand.cxx b/Source/cmExecProgramCommand.cxx index 75a7786..36651af 100644 --- a/Source/cmExecProgramCommand.cxx +++ b/Source/cmExecProgramCommand.cxx @@ -67,7 +67,7 @@ bool cmExecProgramCommand::InitialPass(std::vector<std::string> const& args, std::string command; if (!arguments.empty()) { - command = cmSystemTools::ConvertToRunCommandPath(args[0].c_str()); + command = cmSystemTools::ConvertToRunCommandPath(args[0]); command += " "; command += arguments; } else { @@ -152,7 +152,7 @@ bool cmExecProgramCommand::RunCommand(const char* command, std::string& output, if (!cmSystemTools::FileExists(cmd)) { shortCmd = cmd; } else if (!cmSystemTools::GetShortPath(cmd.c_str(), shortCmd)) { - cmSystemTools::Error("GetShortPath failed for ", cmd.c_str()); + cmSystemTools::Error("GetShortPath failed for " + cmd); return false; } shortCmd += " "; diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 8c67cdb..0d9859e 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -2,13 +2,17 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExecuteProcessCommand.h" +#include "cm_static_string_view.hxx" #include "cmsys/Process.h" +#include <algorithm> #include <ctype.h> /* isspace */ -#include <sstream> +#include <iostream> #include <stdio.h> #include "cmAlgorithms.h" +#include "cmArgumentParser.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmProcessOutput.h" #include "cmSystemTools.h" @@ -32,173 +36,108 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, this->SetError("called with incorrect number of arguments"); return false; } - std::vector<std::vector<const char*>> cmds; - std::string arguments; - bool doing_command = false; - size_t command_index = 0; - bool output_quiet = false; - bool error_quiet = false; - bool output_strip_trailing_whitespace = false; - bool error_strip_trailing_whitespace = false; - std::string timeout_string; - std::string input_file; - std::string output_file; - std::string error_file; - std::string output_variable; - std::string error_variable; - std::string result_variable; - std::string results_variable; - std::string working_directory; - cmProcessOutput::Encoding encoding = cmProcessOutput::None; - for (size_t i = 0; i < args.size(); ++i) { - if (args[i] == "COMMAND") { - doing_command = true; - command_index = cmds.size(); - cmds.emplace_back(); - } else if (args[i] == "OUTPUT_VARIABLE") { - doing_command = false; - if (++i < args.size()) { - output_variable = args[i]; - } else { - this->SetError(" called with no value for OUTPUT_VARIABLE."); - return false; - } - } else if (args[i] == "ERROR_VARIABLE") { - doing_command = false; - if (++i < args.size()) { - error_variable = args[i]; - } else { - this->SetError(" called with no value for ERROR_VARIABLE."); - return false; - } - } else if (args[i] == "RESULT_VARIABLE") { - doing_command = false; - if (++i < args.size()) { - result_variable = args[i]; - } else { - this->SetError(" called with no value for RESULT_VARIABLE."); - return false; - } - } else if (args[i] == "RESULTS_VARIABLE") { - doing_command = false; - if (++i < args.size()) { - results_variable = args[i]; - } else { - this->SetError(" called with no value for RESULTS_VARIABLE."); - return false; - } - } else if (args[i] == "WORKING_DIRECTORY") { - doing_command = false; - if (++i < args.size()) { - working_directory = args[i]; - } else { - this->SetError(" called with no value for WORKING_DIRECTORY."); - return false; - } - } else if (args[i] == "INPUT_FILE") { - doing_command = false; - if (++i < args.size()) { - input_file = args[i]; - } else { - this->SetError(" called with no value for INPUT_FILE."); - return false; - } - } else if (args[i] == "OUTPUT_FILE") { - doing_command = false; - if (++i < args.size()) { - output_file = args[i]; - } else { - this->SetError(" called with no value for OUTPUT_FILE."); - return false; - } - } else if (args[i] == "ERROR_FILE") { - doing_command = false; - if (++i < args.size()) { - error_file = args[i]; - } else { - this->SetError(" called with no value for ERROR_FILE."); - return false; - } - } else if (args[i] == "TIMEOUT") { - doing_command = false; - if (++i < args.size()) { - timeout_string = args[i]; - } else { - this->SetError(" called with no value for TIMEOUT."); - return false; - } - } else if (args[i] == "OUTPUT_QUIET") { - doing_command = false; - output_quiet = true; - } else if (args[i] == "ERROR_QUIET") { - doing_command = false; - error_quiet = true; - } else if (args[i] == "OUTPUT_STRIP_TRAILING_WHITESPACE") { - doing_command = false; - output_strip_trailing_whitespace = true; - } else if (args[i] == "ERROR_STRIP_TRAILING_WHITESPACE") { - doing_command = false; - error_strip_trailing_whitespace = true; - } else if (args[i] == "ENCODING") { - doing_command = false; - if (++i < args.size()) { - encoding = cmProcessOutput::FindEncoding(args[i]); - } else { - this->SetError(" called with no value for ENCODING."); - return false; - } - } else if (doing_command) { - cmds[command_index].push_back(args[i].c_str()); - } else { - std::ostringstream e; - e << " given unknown argument \"" << args[i] << "\"."; - this->SetError(e.str()); - return false; - } + + struct Arguments + { + std::vector<std::vector<std::string>> Commands; + std::string OutputVariable; + std::string ErrorVariable; + std::string ResultVariable; + std::string ResultsVariable; + std::string WorkingDirectory; + std::string InputFile; + std::string OutputFile; + std::string ErrorFile; + std::string Timeout; + std::string CommandEcho; + bool OutputQuiet = false; + bool ErrorQuiet = false; + bool OutputStripTrailingWhitespace = false; + bool ErrorStripTrailingWhitespace = false; + std::string Encoding; + }; + + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("COMMAND"_s, &Arguments::Commands) + .Bind("COMMAND_ECHO"_s, &Arguments::CommandEcho) + .Bind("OUTPUT_VARIABLE"_s, &Arguments::OutputVariable) + .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable) + .Bind("RESULT_VARIABLE"_s, &Arguments::ResultVariable) + .Bind("RESULTS_VARIABLE"_s, &Arguments::ResultsVariable) + .Bind("WORKING_DIRECTORY"_s, &Arguments::WorkingDirectory) + .Bind("INPUT_FILE"_s, &Arguments::InputFile) + .Bind("OUTPUT_FILE"_s, &Arguments::OutputFile) + .Bind("ERROR_FILE"_s, &Arguments::ErrorFile) + .Bind("TIMEOUT"_s, &Arguments::Timeout) + .Bind("OUTPUT_QUIET"_s, &Arguments::OutputQuiet) + .Bind("ERROR_QUIET"_s, &Arguments::ErrorQuiet) + .Bind("OUTPUT_STRIP_TRAILING_WHITESPACE"_s, + &Arguments::OutputStripTrailingWhitespace) + .Bind("ERROR_STRIP_TRAILING_WHITESPACE"_s, + &Arguments::ErrorStripTrailingWhitespace) + .Bind("ENCODING"_s, &Arguments::Encoding); + + std::vector<std::string> unparsedArguments; + std::vector<std::string> keywordsMissingValue; + Arguments const arguments = + parser.Parse(args, &unparsedArguments, &keywordsMissingValue); + + if (!keywordsMissingValue.empty()) { + this->SetError(" called with no value for " + + keywordsMissingValue.front() + "."); + return false; + } + if (!unparsedArguments.empty()) { + this->SetError(" given unknown argument \"" + unparsedArguments.front() + + "\"."); + return false; } - if (!this->Makefile->CanIWriteThisFile(output_file)) { - std::string e = "attempted to output into a file: " + output_file + - " into a source directory."; - this->SetError(e); + if (!this->Makefile->CanIWriteThisFile(arguments.OutputFile)) { + this->SetError("attempted to output into a file: " + arguments.OutputFile + + " into a source directory."); cmSystemTools::SetFatalErrorOccured(); return false; } // Check for commands given. - if (cmds.empty()) { + if (arguments.Commands.empty()) { this->SetError(" called with no COMMAND argument."); return false; } - for (auto& cmd : cmds) { + for (std::vector<std::string> const& cmd : arguments.Commands) { if (cmd.empty()) { this->SetError(" given COMMAND argument with no value."); return false; } - // Add the null terminating pointer to the command argument list. - cmd.push_back(nullptr); } // Parse the timeout string. double timeout = -1; - if (!timeout_string.empty()) { - if (sscanf(timeout_string.c_str(), "%lg", &timeout) != 1) { + if (!arguments.Timeout.empty()) { + if (sscanf(arguments.Timeout.c_str(), "%lg", &timeout) != 1) { this->SetError(" called with TIMEOUT value that could not be parsed."); return false; } } - // Create a process instance. - cmsysProcess* cp = cmsysProcess_New(); + std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr( + cmsysProcess_New(), cmsysProcess_Delete); + cmsysProcess* cp = cp_ptr.get(); // Set the command sequence. - for (auto const& cmd : cmds) { - cmsysProcess_AddCommand(cp, &*cmd.begin()); + for (std::vector<std::string> const& cmd : arguments.Commands) { + std::vector<const char*> argv(cmd.size() + 1); + std::transform(cmd.begin(), cmd.end(), argv.begin(), + [](std::string const& s) { return s.c_str(); }); + argv.back() = nullptr; + cmsysProcess_AddCommand(cp, argv.data()); } // Set the process working directory. - if (!working_directory.empty()) { - cmsysProcess_SetWorkingDirectory(cp, working_directory.c_str()); + if (!arguments.WorkingDirectory.empty()) { + cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str()); } // Always hide the process window. @@ -206,22 +145,24 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, // Check the output variables. bool merge_output = false; - if (!input_file.empty()) { - cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN, input_file.c_str()); + if (!arguments.InputFile.empty()) { + cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN, + arguments.InputFile.c_str()); } - if (!output_file.empty()) { + if (!arguments.OutputFile.empty()) { cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT, - output_file.c_str()); + arguments.OutputFile.c_str()); } - if (!error_file.empty()) { - if (error_file == output_file) { + if (!arguments.ErrorFile.empty()) { + if (arguments.ErrorFile == arguments.OutputFile) { merge_output = true; } else { cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR, - error_file.c_str()); + arguments.ErrorFile.c_str()); } } - if (!output_variable.empty() && output_variable == error_variable) { + if (!arguments.OutputVariable.empty() && + arguments.OutputVariable == arguments.ErrorVariable) { merge_output = true; } if (merge_output) { @@ -233,6 +174,51 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, cmsysProcess_SetTimeout(cp, timeout); } + bool echo_stdout = false; + bool echo_stderr = false; + bool echo_output_from_variable = true; + std::string echo_output = + this->Makefile->GetSafeDefinition("CMAKE_EXECUTE_PROCESS_COMMAND_ECHO"); + if (!arguments.CommandEcho.empty()) { + echo_output_from_variable = false; + echo_output = arguments.CommandEcho; + } + + if (!echo_output.empty()) { + if (echo_output == "STDERR") { + echo_stderr = true; + } else if (echo_output == "STDOUT") { + echo_stdout = true; + } else if (echo_output != "NONE") { + std::string error; + if (echo_output_from_variable) { + error = "CMAKE_EXECUTE_PROCESS_COMMAND_ECHO set to '"; + } else { + error = " called with '"; + } + error += echo_output; + error += "' expected STDERR|STDOUT|NONE"; + if (!echo_output_from_variable) { + error += " for COMMAND_ECHO."; + } + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, error); + return true; + } + } + if (echo_stdout || echo_stderr) { + std::string command; + for (auto& cmd : arguments.Commands) { + command += "'"; + command += cmJoin(cmd, "' '"); + command += "'"; + command += "\n"; + } + if (echo_stdout) { + std::cout << command; + } else if (echo_stderr) { + std::cerr << command; + } + } // Start the process. cmsysProcess_Execute(cp); @@ -242,19 +228,20 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, int length; char* data; int p; - cmProcessOutput processOutput(encoding); + cmProcessOutput processOutput( + cmProcessOutput::FindEncoding(arguments.Encoding)); std::string strdata; while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) { // Put the output in the right place. - if (p == cmsysProcess_Pipe_STDOUT && !output_quiet) { - if (output_variable.empty()) { + if (p == cmsysProcess_Pipe_STDOUT && !arguments.OutputQuiet) { + if (arguments.OutputVariable.empty()) { processOutput.DecodeText(data, length, strdata, 1); cmSystemTools::Stdout(strdata); } else { cmExecuteProcessCommandAppend(tempOutput, data, length); } - } else if (p == cmsysProcess_Pipe_STDERR && !error_quiet) { - if (error_variable.empty()) { + } else if (p == cmsysProcess_Pipe_STDERR && !arguments.ErrorQuiet) { + if (arguments.ErrorVariable.empty()) { processOutput.DecodeText(data, length, strdata, 2); cmSystemTools::Stderr(strdata); } else { @@ -262,13 +249,13 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, } } } - if (!output_quiet && output_variable.empty()) { + if (!arguments.OutputQuiet && arguments.OutputVariable.empty()) { processOutput.DecodeText(std::string(), strdata, 1); if (!strdata.empty()) { cmSystemTools::Stdout(strdata); } } - if (!error_quiet && error_variable.empty()) { + if (!arguments.ErrorQuiet && arguments.ErrorVariable.empty()) { processOutput.DecodeText(std::string(), strdata, 2); if (!strdata.empty()) { cmSystemTools::Stderr(strdata); @@ -281,46 +268,49 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, processOutput.DecodeText(tempError, tempError); // Fix the text in the output strings. - cmExecuteProcessCommandFixText(tempOutput, output_strip_trailing_whitespace); - cmExecuteProcessCommandFixText(tempError, error_strip_trailing_whitespace); + cmExecuteProcessCommandFixText(tempOutput, + arguments.OutputStripTrailingWhitespace); + cmExecuteProcessCommandFixText(tempError, + arguments.ErrorStripTrailingWhitespace); // Store the output obtained. - if (!output_variable.empty() && !tempOutput.empty()) { - this->Makefile->AddDefinition(output_variable, &*tempOutput.begin()); + if (!arguments.OutputVariable.empty() && !tempOutput.empty()) { + this->Makefile->AddDefinition(arguments.OutputVariable, tempOutput.data()); } - if (!merge_output && !error_variable.empty() && !tempError.empty()) { - this->Makefile->AddDefinition(error_variable, &*tempError.begin()); + if (!merge_output && !arguments.ErrorVariable.empty() && + !tempError.empty()) { + this->Makefile->AddDefinition(arguments.ErrorVariable, tempError.data()); } // Store the result of running the process. - if (!result_variable.empty()) { + if (!arguments.ResultVariable.empty()) { switch (cmsysProcess_GetState(cp)) { case cmsysProcess_State_Exited: { int v = cmsysProcess_GetExitValue(cp); char buf[16]; sprintf(buf, "%d", v); - this->Makefile->AddDefinition(result_variable, buf); + this->Makefile->AddDefinition(arguments.ResultVariable, buf); } break; case cmsysProcess_State_Exception: - this->Makefile->AddDefinition(result_variable, + this->Makefile->AddDefinition(arguments.ResultVariable, cmsysProcess_GetExceptionString(cp)); break; case cmsysProcess_State_Error: - this->Makefile->AddDefinition(result_variable, + this->Makefile->AddDefinition(arguments.ResultVariable, cmsysProcess_GetErrorString(cp)); break; case cmsysProcess_State_Expired: - this->Makefile->AddDefinition(result_variable, + this->Makefile->AddDefinition(arguments.ResultVariable, "Process terminated due to timeout"); break; } } // Store the result of running the processes. - if (!results_variable.empty()) { + if (!arguments.ResultsVariable.empty()) { switch (cmsysProcess_GetState(cp)) { case cmsysProcess_State_Exited: { std::vector<std::string> res; - for (size_t i = 0; i < cmds.size(); ++i) { + for (size_t i = 0; i < arguments.Commands.size(); ++i) { switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) { case kwsysProcess_StateByIndex_Exited: { int exitCode = @@ -339,27 +329,24 @@ bool cmExecuteProcessCommand::InitialPass(std::vector<std::string> const& args, break; } } - this->Makefile->AddDefinition(results_variable, + this->Makefile->AddDefinition(arguments.ResultsVariable, cmJoin(res, ";").c_str()); } break; case cmsysProcess_State_Exception: - this->Makefile->AddDefinition(results_variable, + this->Makefile->AddDefinition(arguments.ResultsVariable, cmsysProcess_GetExceptionString(cp)); break; case cmsysProcess_State_Error: - this->Makefile->AddDefinition(results_variable, + this->Makefile->AddDefinition(arguments.ResultsVariable, cmsysProcess_GetErrorString(cp)); break; case cmsysProcess_State_Expired: - this->Makefile->AddDefinition(results_variable, + this->Makefile->AddDefinition(arguments.ResultsVariable, "Process terminated due to timeout"); break; } } - // Delete the process instance. - cmsysProcess_Delete(cp); - return true; } diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 722831a..5b611c0 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -2,10 +2,13 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmExportCommand.h" +#include "cm_static_string_view.hxx" #include "cmsys/RegularExpression.hxx" #include <map> #include <sstream> +#include <utility> +#include "cmArgumentParser.h" #include "cmExportBuildAndroidMKGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmExportSetMap.h" @@ -13,10 +16,12 @@ #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" +class cmExportSet; class cmExecutionStatus; #if defined(__HAIKU__) @@ -24,19 +29,6 @@ class cmExecutionStatus; # include <StorageDefs.h> #endif -cmExportCommand::cmExportCommand() - : Targets(&Helper, "TARGETS") - , Append(&Helper, "APPEND", &ArgumentGroup) - , ExportSetName(&Helper, "EXPORT", &ArgumentGroup) - , Namespace(&Helper, "NAMESPACE", &ArgumentGroup) - , Filename(&Helper, "FILE", &ArgumentGroup) - , ExportOld(&Helper, "EXPORT_LINK_INTERFACE_LIBRARIES", &ArgumentGroup) - , AndroidMKFile(&Helper, "ANDROID_MK") -{ - this->ExportSet = nullptr; -} - -// cmExportCommand bool cmExportCommand::InitialPass(std::vector<std::string> const& args, cmExecutionStatus&) { @@ -48,45 +40,62 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, if (args[0] == "PACKAGE") { return this->HandlePackage(args); } + + struct Arguments + { + std::string ExportSetName; + std::vector<std::string> Targets; + std::string Namespace; + std::string Filename; + std::string AndroidMKFile; + bool Append = false; + bool ExportOld = false; + }; + + auto parser = cmArgumentParser<Arguments>{} + .Bind("NAMESPACE"_s, &Arguments::Namespace) + .Bind("FILE"_s, &Arguments::Filename); + if (args[0] == "EXPORT") { - this->ExportSetName.Follows(nullptr); - this->ArgumentGroup.Follows(&this->ExportSetName); + parser.Bind("EXPORT"_s, &Arguments::ExportSetName); } else { - this->Targets.Follows(nullptr); - this->ArgumentGroup.Follows(&this->Targets); + parser.Bind("TARGETS"_s, &Arguments::Targets); + parser.Bind("ANDROID_MK"_s, &Arguments::AndroidMKFile); + parser.Bind("APPEND"_s, &Arguments::Append); + parser.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, &Arguments::ExportOld); } std::vector<std::string> unknownArgs; - this->Helper.Parse(&args, &unknownArgs); + Arguments const arguments = parser.Parse(args, &unknownArgs); if (!unknownArgs.empty()) { - this->SetError("Unknown arguments."); + this->SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); return false; } std::string fname; bool android = false; - if (this->AndroidMKFile.WasFound()) { - fname = this->AndroidMKFile.GetString(); + if (!arguments.AndroidMKFile.empty()) { + fname = arguments.AndroidMKFile; android = true; } - if (!this->Filename.WasFound() && fname.empty()) { + if (arguments.Filename.empty() && fname.empty()) { if (args[0] != "EXPORT") { this->SetError("FILE <filename> option missing."); return false; } - fname = this->ExportSetName.GetString() + ".cmake"; + fname = arguments.ExportSetName + ".cmake"; } else if (fname.empty()) { // Make sure the file has a .cmake extension. - if (cmSystemTools::GetFilenameLastExtension(this->Filename.GetCString()) != + if (cmSystemTools::GetFilenameLastExtension(arguments.Filename) != ".cmake") { std::ostringstream e; - e << "FILE option given filename \"" << this->Filename.GetString() + e << "FILE option given filename \"" << arguments.Filename << "\" which does not have an extension of \".cmake\".\n"; this->SetError(e.str()); return false; } - fname = this->Filename.GetString(); + fname = arguments.Filename; } // Get the file to write. @@ -108,33 +117,19 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator(); + cmExportSet* ExportSet = nullptr; if (args[0] == "EXPORT") { - if (this->Append.IsEnabled()) { - std::ostringstream e; - e << "EXPORT signature does not recognise the APPEND option."; - this->SetError(e.str()); - return false; - } - - if (this->ExportOld.IsEnabled()) { - std::ostringstream e; - e << "EXPORT signature does not recognise the " - "EXPORT_LINK_INTERFACE_LIBRARIES option."; - this->SetError(e.str()); - return false; - } - cmExportSetMap& setMap = gg->GetExportSets(); - std::string setName = this->ExportSetName.GetString(); - if (setMap.find(setName) == setMap.end()) { + auto const it = setMap.find(arguments.ExportSetName); + if (it == setMap.end()) { std::ostringstream e; - e << "Export set \"" << setName << "\" not found."; + e << "Export set \"" << arguments.ExportSetName << "\" not found."; this->SetError(e.str()); return false; } - this->ExportSet = setMap[setName]; - } else if (this->Targets.WasFound()) { - for (std::string const& currentTarget : this->Targets.GetVector()) { + ExportSet = it->second; + } else if (!arguments.Targets.empty()) { + for (std::string const& currentTarget : arguments.Targets) { if (this->Makefile->IsAlias(currentTarget)) { std::ostringstream e; e << "given ALIAS target \"" << currentTarget @@ -158,7 +153,7 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, } targets.push_back(currentTarget); } - if (this->Append.IsEnabled()) { + if (arguments.Append) { if (cmExportBuildFileGenerator* ebfg = gg->GetExportedTargetsFile(fname)) { ebfg->AppendTargets(targets); @@ -178,15 +173,15 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, ebfg = new cmExportBuildFileGenerator; } ebfg->SetExportFile(fname.c_str()); - ebfg->SetNamespace(this->Namespace.GetCString()); - ebfg->SetAppendMode(this->Append.IsEnabled()); - if (this->ExportSet) { - ebfg->SetExportSet(this->ExportSet); + ebfg->SetNamespace(arguments.Namespace); + ebfg->SetAppendMode(arguments.Append); + if (ExportSet != nullptr) { + ebfg->SetExportSet(ExportSet); } else { ebfg->SetTargets(targets); } this->Makefile->AddExportBuildFileGenerator(ebfg); - ebfg->SetExportOld(this->ExportOld.IsEnabled()); + ebfg->SetExportOld(arguments.ExportOld); // Compute the set of configurations exported. std::vector<std::string> configurationTypes; @@ -197,7 +192,7 @@ bool cmExportCommand::InitialPass(std::vector<std::string> const& args, for (std::string const& ct : configurationTypes) { ebfg->AddConfiguration(ct); } - if (this->ExportSet) { + if (ExportSet != nullptr) { gg->AddBuildExportExportSet(ebfg); } else { gg->AddBuildExportSet(ebfg); @@ -243,10 +238,23 @@ bool cmExportCommand::HandlePackage(std::vector<std::string> const& args) return false; } - // If the CMAKE_EXPORT_NO_PACKAGE_REGISTRY variable is set the command - // export(PACKAGE) does nothing. - if (this->Makefile->IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) { - return true; + // CMP0090 decides both the default and what variable changes it. + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0090)) { + case cmPolicies::WARN: + case cmPolicies::OLD: + // Default is to export, but can be disabled. + if (this->Makefile->IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) { + return true; + } + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Default is to not export, but can be enabled. + if (!this->Makefile->IsOn("CMAKE_EXPORT_PACKAGE_REGISTRY")) { + return true; + } + break; } // We store the current build directory in the registry as a value diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h index a5c6751..99f9932 100644 --- a/Source/cmExportCommand.h +++ b/Source/cmExportCommand.h @@ -9,21 +9,12 @@ #include <vector> #include "cmCommand.h" -#include "cmCommandArgumentsHelper.h" class cmExecutionStatus; -class cmExportSet; -/** \class cmExportLibraryDependenciesCommand - * \brief Add a test to the lists of tests to run. - * - * cmExportLibraryDependenciesCommand adds a test to the list of tests to run - * - */ class cmExportCommand : public cmCommand { public: - cmExportCommand(); /** * This is a virtual constructor for the command. */ @@ -37,21 +28,6 @@ public: cmExecutionStatus& status) override; private: - cmCommandArgumentsHelper Helper; - cmCommandArgumentGroup ArgumentGroup; - cmCAStringVector Targets; - cmCAEnabler Append; - cmCAString ExportSetName; - cmCAString Namespace; - cmCAString Filename; - cmCAEnabler ExportOld; - cmCAString AndroidMKFile; - - cmExportSet* ExportSet; - - friend class cmExportBuildFileGenerator; - std::string ErrorMessage; - bool HandlePackage(std::vector<std::string> const& args); void StorePackageRegistryWin(std::string const& package, const char* content, const char* hash); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index c8f743a..a12e0c4 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -70,8 +70,9 @@ bool cmExportFileGenerator::GenerateImportFile() std::unique_ptr<cmsys::ofstream> foutPtr; if (this->AppendMode) { // Open for append. + auto openmodeApp = std::ios::app; foutPtr = cm::make_unique<cmsys::ofstream>(this->MainImportFile.c_str(), - std::ios::app); + openmodeApp); } else { // Generate atomically and with copy-if-different. std::unique_ptr<cmGeneratedFileStream> ap( diff --git a/Source/cmExportLibraryDependenciesCommand.cxx b/Source/cmExportLibraryDependenciesCommand.cxx index b4b2962..0a0646c 100644 --- a/Source/cmExportLibraryDependenciesCommand.cxx +++ b/Source/cmExportLibraryDependenciesCommand.cxx @@ -50,8 +50,9 @@ void cmExportLibraryDependenciesCommand::ConstFinalPass() const // Use copy-if-different if not appending. std::unique_ptr<cmsys::ofstream> foutPtr; if (this->Append) { + const auto openmodeApp = std::ios::app; foutPtr = - cm::make_unique<cmsys::ofstream>(this->Filename.c_str(), std::ios::app); + cm::make_unique<cmsys::ofstream>(this->Filename.c_str(), openmodeApp); } else { std::unique_ptr<cmGeneratedFileStream> ap( new cmGeneratedFileStream(this->Filename, true)); @@ -61,7 +62,7 @@ void cmExportLibraryDependenciesCommand::ConstFinalPass() const std::ostream& fout = *foutPtr; if (!fout) { - cmSystemTools::Error("Error Writing ", this->Filename.c_str()); + cmSystemTools::Error("Error Writing " + this->Filename); cmSystemTools::ReportLastSystemError(""); return; } diff --git a/Source/cmExportSet.h b/Source/cmExportSet.h index 0ef306f..d654c12 100644 --- a/Source/cmExportSet.h +++ b/Source/cmExportSet.h @@ -25,6 +25,9 @@ public: /// Destructor ~cmExportSet(); + cmExportSet(const cmExportSet&) = delete; + cmExportSet& operator=(const cmExportSet&) = delete; + void Compute(cmLocalGenerator* lg); void AddTargetExport(cmTargetExport* tgt); diff --git a/Source/cmExportSetMap.cxx b/Source/cmExportSetMap.cxx index 0740828..293e80c 100644 --- a/Source/cmExportSetMap.cxx +++ b/Source/cmExportSetMap.cxx @@ -23,6 +23,8 @@ void cmExportSetMap::clear() this->derived::clear(); } +cmExportSetMap::cmExportSetMap() = default; + cmExportSetMap::~cmExportSetMap() { this->clear(); diff --git a/Source/cmExportSetMap.h b/Source/cmExportSetMap.h index 0f71c79..3853732 100644 --- a/Source/cmExportSetMap.h +++ b/Source/cmExportSetMap.h @@ -25,8 +25,13 @@ public: void clear(); + cmExportSetMap(); + /// Overloaded destructor deletes all member export sets. ~cmExportSetMap(); + + cmExportSetMap(const cmExportSetMap&) = delete; + cmExportSetMap& operator=(const cmExportSetMap&) = delete; }; #endif diff --git a/Source/cmExternalMakefileProjectGenerator.h b/Source/cmExternalMakefileProjectGenerator.h index 4438f28..a472a06 100644 --- a/Source/cmExternalMakefileProjectGenerator.h +++ b/Source/cmExternalMakefileProjectGenerator.h @@ -31,13 +31,13 @@ public: virtual void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*, bool optional); - ///! set the global generator which will generate the makefiles + //! set the global generator which will generate the makefiles virtual void SetGlobalGenerator(cmGlobalGenerator* generator) { this->GlobalGenerator = generator; } - ///! Return the list of global generators supported by this extra generator + //! Return the list of global generators supported by this extra generator const std::vector<std::string>& GetSupportedGlobalGenerators() const { return this->SupportedGlobalGenerators; @@ -49,7 +49,7 @@ public: static std::string CreateFullGeneratorName( const std::string& globalGenerator, const std::string& extraGenerator); - ///! Generate the project files, the Makefiles have already been generated + //! Generate the project files, the Makefiles have already been generated virtual void Generate() = 0; void SetName(const std::string& n) { Name = n; } @@ -59,9 +59,9 @@ public: bool dryRun); protected: - ///! Contains the names of the global generators support by this generator. + //! Contains the names of the global generators support by this generator. std::vector<std::string> SupportedGlobalGenerators; - ///! the global generator which creates the makefiles + //! the global generator which creates the makefiles const cmGlobalGenerator* GlobalGenerator = nullptr; std::string Name; diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx index e408de3..93ff8f4 100644 --- a/Source/cmExtraCodeBlocksGenerator.cxx +++ b/Source/cmExtraCodeBlocksGenerator.cxx @@ -13,6 +13,7 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmStateTypes.h" #include "cmSystemTools.h" @@ -247,8 +248,8 @@ void cmExtraCodeBlocksGenerator::CreateNewProjectFile( // figure out the compiler std::string compiler = this->GetCBCompilerId(mf); - std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); - const std::string makeArgs = + const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string& makeArgs = mf->GetSafeDefinition("CMAKE_CODEBLOCKS_MAKE_ARGUMENTS"); cmXMLWriter xml(fout); @@ -589,10 +590,9 @@ void cmExtraCodeBlocksGenerator::AppendTarget( std::vector<std::string>::const_iterator end = cmRemoveDuplicates(allIncludeDirs); - for (std::vector<std::string>::const_iterator i = allIncludeDirs.begin(); - i != end; ++i) { + for (std::string const& str : cmMakeRange(allIncludeDirs.cbegin(), end)) { xml.StartElement("Add"); - xml.Attribute("directory", *i); + xml.Attribute("directory", str); xml.EndElement(); } diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx index 0773edc..6fe8c14 100644 --- a/Source/cmExtraCodeLiteGenerator.cxx +++ b/Source/cmExtraCodeLiteGenerator.cxx @@ -628,8 +628,8 @@ std::string cmExtraCodeLiteGenerator::GetConfigurationName( std::string cmExtraCodeLiteGenerator::GetBuildCommand( const cmMakefile* mf, const std::string& targetName) const { - std::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR"); - std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string& generator = mf->GetSafeDefinition("CMAKE_GENERATOR"); + const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); std::string buildCommand = make; // Default std::ostringstream ss; if (generator == "NMake Makefiles" || generator == "Ninja") { @@ -669,8 +669,8 @@ std::string cmExtraCodeLiteGenerator::GetSingleFileBuildCommand( const cmMakefile* mf) const { std::string buildCommand; - std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); - std::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR"); + const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string& generator = mf->GetSafeDefinition("CMAKE_GENERATOR"); if (generator == "Unix Makefiles" || generator == "MinGW Makefiles") { std::ostringstream ss; #if defined(_WIN32) diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx index e05f74b..aece3bc 100644 --- a/Source/cmExtraEclipseCDT4Generator.cxx +++ b/Source/cmExtraEclipseCDT4Generator.cxx @@ -881,8 +881,8 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const xml.Attribute("moduleId", "org.eclipse.cdt.make.core.buildtargets"); xml.StartElement("buildTargets"); emmited.clear(); - const std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); - const std::string makeArgs = + const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string& makeArgs = mf->GetSafeDefinition("CMAKE_ECLIPSE_MAKE_ARGUMENTS"); cmGlobalGenerator* generator = @@ -1079,7 +1079,8 @@ void cmExtraEclipseCDT4Generator::AppendStorageScanners( cmXMLWriter& xml, const cmMakefile& makefile) { // we need the "make" and the C (or C++) compiler which are used, Alex - std::string make = makefile.GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string& make = + makefile.GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); std::string compiler = makefile.GetSafeDefinition("CMAKE_C_COMPILER"); std::string arg1 = makefile.GetSafeDefinition("CMAKE_C_COMPILER_ARG1"); if (compiler.empty()) { diff --git a/Source/cmExtraKateGenerator.cxx b/Source/cmExtraKateGenerator.cxx index 23ba6b7..877f109 100644 --- a/Source/cmExtraKateGenerator.cxx +++ b/Source/cmExtraKateGenerator.cxx @@ -75,8 +75,8 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator* lg, cmGeneratedFileStream& fout) const { cmMakefile const* mf = lg->GetMakefile(); - const std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); - const std::string makeArgs = + const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string& makeArgs = mf->GetSafeDefinition("CMAKE_KATE_MAKE_ARGUMENTS"); std::string const& homeOutputDir = lg->GetBinaryDirectory(); @@ -196,7 +196,7 @@ void cmExtraKateGenerator::AppendTarget(cmGeneratedFileStream& fout, { static char JsonSep = ' '; - fout << "\t\t\t" << JsonSep << "{\"name\":\"" << target + fout << "\t\t\t" << JsonSep << R"({"name":")" << target << "\", " "\"build_cmd\":\"" << make << " -C \\\"" << (this->UseNinja ? homeOutputDir : path) diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx index 739a177..71c8fcd 100644 --- a/Source/cmExtraSublimeTextGenerator.cxx +++ b/Source/cmExtraSublimeTextGenerator.cxx @@ -168,7 +168,7 @@ void cmExtraSublimeTextGenerator::AppendAllTargets( const std::vector<cmLocalGenerator*>& lgs, const cmMakefile* mf, cmGeneratedFileStream& fout, MapSourceFileFlags& sourceFileFlags) { - std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); std::string compiler; if (!lgs.empty()) { this->AppendTarget(fout, "all", lgs[0], nullptr, make.c_str(), mf, @@ -263,7 +263,7 @@ void cmExtraSublimeTextGenerator::AppendTarget( // Regular expression to extract compiler flags from a string // https://gist.github.com/3944250 const char* regexString = - "(^|[ ])-[DIOUWfgs][^= ]+(=\\\"[^\"]+\\\"|=[^\"][^ ]+)?"; + R"((^|[ ])-[DIOUWfgs][^= ]+(=\"[^"]+\"|=[^"][^ ]+)?)"; flagRegex.compile(regexString); std::string workString = flagsString + " " + definesString + " " + includesString; @@ -315,12 +315,12 @@ std::string cmExtraSublimeTextGenerator::BuildMakeCommand( std::string generator = this->GlobalGenerator->GetName(); if (generator == "NMake Makefiles") { std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile); - command += ", \"/NOLOGO\", \"/f\", \""; + command += R"(, "/NOLOGO", "/f", ")"; command += makefileName + "\""; command += ", \"" + target + "\""; } else if (generator == "Ninja") { std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile); - command += ", \"-f\", \""; + command += R"(, "-f", ")"; command += makefileName + "\""; command += ", \"" + target + "\""; } else { @@ -332,7 +332,7 @@ std::string cmExtraSublimeTextGenerator::BuildMakeCommand( } else { makefileName = cmSystemTools::ConvertToOutputPath(makefile); } - command += ", \"-f\", \""; + command += R"(, "-f", ")"; command += makefileName + "\""; command += ", \"" + target + "\""; } diff --git a/Source/cmFLTKWrapUICommand.cxx b/Source/cmFLTKWrapUICommand.cxx index 4b14d26..89629c7 100644 --- a/Source/cmFLTKWrapUICommand.cxx +++ b/Source/cmFLTKWrapUICommand.cxx @@ -6,6 +6,7 @@ #include "cmCustomCommandLines.h" #include "cmMakefile.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmSystemTools.h" @@ -40,18 +41,17 @@ bool cmFLTKWrapUICommand::InitialPass(std::vector<std::string> const& args, this->Makefile->AddIncludeDirectories(outputDirectories); } - for (std::vector<std::string>::const_iterator i = (args.begin() + 1); - i != args.end(); i++) { - cmSourceFile* curr = this->Makefile->GetSource(*i); + for (std::string const& arg : cmMakeRange(args).advance(1)) { + cmSourceFile* curr = this->Makefile->GetSource(arg); // if we should use the source GUI // to generate .cxx and .h files if (!curr || !curr->GetPropertyAsBool("WRAP_EXCLUDE")) { std::string outName = outputDirectory; outName += "/"; - outName += cmSystemTools::GetFilenameWithoutExtension(*i); + outName += cmSystemTools::GetFilenameWithoutExtension(arg); std::string hname = outName; hname += ".h"; - std::string origname = cdir + "/" + *i; + std::string origname = cdir + "/" + arg; // add starting depends std::vector<std::string> depends; depends.push_back(origname); diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 0f911c1..f5ec9fe 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -3,7 +3,7 @@ #include "cmFileCommand.h" #include "cm_kwiml.h" -#include "cmsys/Directory.hxx" +#include "cm_static_string_view.hxx" #include "cmsys/FStream.hxx" #include "cmsys/Glob.hxx" #include "cmsys/RegularExpression.hxx" @@ -16,24 +16,23 @@ #include <sstream> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <utility> #include <vector> #include "cmAlgorithms.h" -#include "cmCommandArgumentsHelper.h" +#include "cmArgumentParser.h" #include "cmCryptoHash.h" -#include "cmFSPermissions.h" +#include "cmFileCopier.h" +#include "cmFileInstaller.h" #include "cmFileLockPool.h" -#include "cmFileTimeComparison.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" #include "cmHexFileConverter.h" -#include "cmInstallType.h" #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmSystemTools.h" #include "cmTimestamp.h" #include "cm_sys_stat.h" @@ -55,8 +54,6 @@ class cmSystemToolsFileTime; -using namespace cmFSPermissions; - #if defined(_WIN32) // libcurl doesn't support file:// urls for unicode filenames on Windows. // Convert string from UTF-8 to ACP if this is a file:// URL. @@ -223,7 +220,7 @@ bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args, bool writable = false; // Set permissions to writable - if (cmSystemTools::GetPermissions(fileName.c_str(), mode)) { + if (cmSystemTools::GetPermissions(fileName, mode)) { #if defined(_MSC_VER) || defined(__MINGW32__) writable = (mode & S_IWRITE) != 0; mode_t newMode = mode | S_IWRITE; @@ -232,7 +229,7 @@ bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args, mode_t newMode = mode | S_IWUSR | S_IWGRP; #endif if (!writable) { - cmSystemTools::SetPermissions(fileName.c_str(), newMode); + cmSystemTools::SetPermissions(fileName, newMode); } } // If GetPermissions fails, pretend like it is ok. File open will fail if @@ -259,7 +256,7 @@ bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args, } file.close(); if (mode && !writable) { - cmSystemTools::SetPermissions(fileName.c_str(), mode); + cmSystemTools::SetPermissions(fileName, mode); } return true; } @@ -272,36 +269,34 @@ bool cmFileCommand::HandleReadCommand(std::vector<std::string> const& args) return false; } - cmCommandArgumentsHelper argHelper; - cmCommandArgumentGroup group; + std::string const& fileNameArg = args[1]; + std::string const& variable = args[2]; + + struct Arguments + { + std::string Offset; + std::string Limit; + bool Hex = false; + }; - cmCAString readArg(&argHelper, "READ"); - cmCAString fileNameArg(&argHelper, nullptr); - cmCAString resultArg(&argHelper, nullptr); + static auto const parser = cmArgumentParser<Arguments>{} + .Bind("OFFSET"_s, &Arguments::Offset) + .Bind("LIMIT"_s, &Arguments::Limit) + .Bind("HEX"_s, &Arguments::Hex); - cmCAString offsetArg(&argHelper, "OFFSET", &group); - cmCAString limitArg(&argHelper, "LIMIT", &group); - cmCAEnabler hexOutputArg(&argHelper, "HEX", &group); - readArg.Follows(nullptr); - fileNameArg.Follows(&readArg); - resultArg.Follows(&fileNameArg); - group.Follows(&resultArg); - argHelper.Parse(&args, nullptr); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3)); - std::string fileName = fileNameArg.GetString(); + std::string fileName = fileNameArg; if (!cmsys::SystemTools::FileIsFullPath(fileName)) { fileName = this->Makefile->GetCurrentSourceDirectory(); - fileName += "/" + fileNameArg.GetString(); + fileName += "/" + fileNameArg; } - std::string variable = resultArg.GetString(); - // Open the specified file. #if defined(_WIN32) || defined(__CYGWIN__) - cmsys::ifstream file( - fileName.c_str(), - std::ios::in | - (hexOutputArg.IsEnabled() ? std::ios::binary : std::ios::in)); + cmsys::ifstream file(fileName.c_str(), + arguments.Hex ? (std::ios::binary | std::ios::in) + : std::ios::in); #else cmsys::ifstream file(fileName.c_str()); #endif @@ -317,21 +312,21 @@ bool cmFileCommand::HandleReadCommand(std::vector<std::string> const& args) // is there a limit? long sizeLimit = -1; - if (!limitArg.GetString().empty()) { - sizeLimit = atoi(limitArg.GetCString()); + if (!arguments.Limit.empty()) { + sizeLimit = atoi(arguments.Limit.c_str()); } // is there an offset? long offset = 0; - if (!offsetArg.GetString().empty()) { - offset = atoi(offsetArg.GetCString()); + if (!arguments.Offset.empty()) { + offset = atoi(arguments.Offset.c_str()); } file.seekg(offset, std::ios::beg); // explicit ios::beg for IBM VisualAge 6 std::string output; - if (hexOutputArg.IsEnabled()) { + if (arguments.Hex) { // Convert part of the file into hex code char c; while ((sizeLimit != 0) && (file.get(c))) { @@ -393,7 +388,7 @@ bool cmFileCommand::HandleHashCommand(std::vector<std::string> const& args) #else std::ostringstream e; e << args[0] << " not available during bootstrap"; - this->SetError(e.str().c_str()); + this->SetError(e.str()); return false; #endif } @@ -523,7 +518,7 @@ bool cmFileCommand::HandleStringsCommand(std::vector<std::string> const& args) maxlen = len; arg_mode = arg_none; } else if (arg_mode == arg_regex) { - if (!regex.compile(args[i].c_str())) { + if (!regex.compile(args[i])) { std::ostringstream e; e << "STRINGS option REGEX value \"" << args[i] << "\" could not be compiled."; @@ -947,16 +942,14 @@ bool cmFileCommand::HandleMakeDirectoryCommand( // File command has at least one argument assert(args.size() > 1); - std::vector<std::string>::const_iterator i = args.begin(); - - i++; // Get rid of subcommand - std::string expr; - for (; i != args.end(); ++i) { - const std::string* cdir = &(*i); - if (!cmsys::SystemTools::FileIsFullPath(*i)) { + for (std::string const& arg : + cmMakeRange(args).advance(1)) // Get rid of subcommand + { + const std::string* cdir = &arg; + if (!cmsys::SystemTools::FileIsFullPath(arg)) { expr = this->Makefile->GetCurrentSourceDirectory(); - expr += "/" + *i; + expr += "/" + arg; cdir = &expr; } if (!this->Makefile->CanIWriteThisFile(*cdir)) { @@ -981,15 +974,13 @@ bool cmFileCommand::HandleTouchCommand(std::vector<std::string> const& args, // File command has at least one argument assert(args.size() > 1); - std::vector<std::string>::const_iterator i = args.begin(); - - i++; // Get rid of subcommand - - for (; i != args.end(); ++i) { - std::string tfile = *i; + for (std::string const& arg : + cmMakeRange(args).advance(1)) // Get rid of subcommand + { + std::string tfile = arg; if (!cmsys::SystemTools::FileIsFullPath(tfile)) { tfile = this->Makefile->GetCurrentSourceDirectory(); - tfile += "/" + *i; + tfile += "/" + arg; } if (!this->Makefile->CanIWriteThisFile(tfile)) { std::string e = @@ -1061,1088 +1052,17 @@ bool cmFileCommand::HandleDifferentCommand( return true; } -// File installation helper class. -struct cmFileCopier -{ - cmFileCopier(cmFileCommand* command, const char* name = "COPY") - : FileCommand(command) - , Makefile(command->GetMakefile()) - , Name(name) - , Always(false) - , MatchlessFiles(true) - , FilePermissions(0) - , DirPermissions(0) - , CurrentMatchRule(nullptr) - , UseGivenPermissionsFile(false) - , UseGivenPermissionsDir(false) - , UseSourcePermissions(true) - , Doing(DoingNone) - { - } - virtual ~cmFileCopier() = default; - - bool Run(std::vector<std::string> const& args); - -protected: - cmFileCommand* FileCommand; - cmMakefile* Makefile; - const char* Name; - bool Always; - cmFileTimeComparison FileTimes; - - // Whether to install a file not matching any expression. - bool MatchlessFiles; - - // Permissions for files and directories installed by this object. - mode_t FilePermissions; - mode_t DirPermissions; - - // Properties set by pattern and regex match rules. - struct MatchProperties - { - bool Exclude = false; - mode_t Permissions = 0; - }; - struct MatchRule - { - cmsys::RegularExpression Regex; - MatchProperties Properties; - std::string RegexString; - MatchRule(std::string const& regex) - : Regex(regex.c_str()) - , RegexString(regex) - { - } - }; - std::vector<MatchRule> MatchRules; - - // Get the properties from rules matching this input file. - MatchProperties CollectMatchProperties(const char* file) - { -// Match rules are case-insensitive on some platforms. -#if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__) - std::string lower = cmSystemTools::LowerCase(file); - const char* file_to_match = lower.c_str(); -#else - const char* file_to_match = file; -#endif - - // Collect properties from all matching rules. - bool matched = false; - MatchProperties result; - for (MatchRule& mr : this->MatchRules) { - if (mr.Regex.find(file_to_match)) { - matched = true; - result.Exclude |= mr.Properties.Exclude; - result.Permissions |= mr.Properties.Permissions; - } - } - if (!matched && !this->MatchlessFiles) { - result.Exclude = !cmSystemTools::FileIsDirectory(file); - } - return result; - } - - bool SetPermissions(const char* toFile, mode_t permissions) - { - if (permissions) { -#ifdef WIN32 - if (Makefile->IsOn("CMAKE_CROSSCOMPILING")) { - // Store the mode in an NTFS alternate stream. - std::string mode_t_adt_filename = - std::string(toFile) + ":cmake_mode_t"; - - // Writing to an NTFS alternate stream changes the modification - // time, so we need to save and restore its original value. - cmSystemToolsFileTime* file_time_orig = cmSystemTools::FileTimeNew(); - cmSystemTools::FileTimeGet(toFile, file_time_orig); - - cmsys::ofstream permissionStream(mode_t_adt_filename.c_str()); - - if (permissionStream) { - permissionStream << std::oct << permissions << std::endl; - } - - permissionStream.close(); - - cmSystemTools::FileTimeSet(toFile, file_time_orig); - - cmSystemTools::FileTimeDelete(file_time_orig); - } -#endif - - if (!cmSystemTools::SetPermissions(toFile, permissions)) { - std::ostringstream e; - e << this->Name << " cannot set permissions on \"" << toFile << "\""; - this->FileCommand->SetError(e.str()); - return false; - } - } - return true; - } - - // Translate an argument to a permissions bit. - bool CheckPermissions(std::string const& arg, mode_t& permissions) - { - if (!cmFSPermissions::stringToModeT(arg, permissions)) { - std::ostringstream e; - e << this->Name << " given invalid permission \"" << arg << "\"."; - this->FileCommand->SetError(e.str()); - return false; - } - return true; - } - - bool InstallSymlink(const char* fromFile, const char* toFile); - bool InstallFile(const char* fromFile, const char* toFile, - MatchProperties match_properties); - bool InstallDirectory(const char* source, const char* destination, - MatchProperties match_properties); - virtual bool Install(const char* fromFile, const char* toFile); - virtual std::string const& ToName(std::string const& fromName) - { - return fromName; - } - - enum Type - { - TypeFile, - TypeDir, - TypeLink - }; - virtual void ReportCopy(const char*, Type, bool) {} - virtual bool ReportMissing(const char* fromFile) - { - // The input file does not exist and installation is not optional. - std::ostringstream e; - e << this->Name << " cannot find \"" << fromFile << "\"."; - this->FileCommand->SetError(e.str()); - return false; - } - - MatchRule* CurrentMatchRule; - bool UseGivenPermissionsFile; - bool UseGivenPermissionsDir; - bool UseSourcePermissions; - std::string Destination; - std::string FilesFromDir; - std::vector<std::string> Files; - int Doing; - - virtual bool Parse(std::vector<std::string> const& args); - enum - { - DoingNone, - DoingError, - DoingDestination, - DoingFilesFromDir, - DoingFiles, - DoingPattern, - DoingRegex, - DoingPermissionsFile, - DoingPermissionsDir, - DoingPermissionsMatch, - DoingLast1 - }; - virtual bool CheckKeyword(std::string const& arg); - virtual bool CheckValue(std::string const& arg); - - void NotBeforeMatch(std::string const& arg) - { - std::ostringstream e; - e << "option " << arg << " may not appear before PATTERN or REGEX."; - this->FileCommand->SetError(e.str()); - this->Doing = DoingError; - } - void NotAfterMatch(std::string const& arg) - { - std::ostringstream e; - e << "option " << arg << " may not appear after PATTERN or REGEX."; - this->FileCommand->SetError(e.str()); - this->Doing = DoingError; - } - virtual void DefaultFilePermissions() - { - // Use read/write permissions. - this->FilePermissions = 0; - this->FilePermissions |= mode_owner_read; - this->FilePermissions |= mode_owner_write; - this->FilePermissions |= mode_group_read; - this->FilePermissions |= mode_world_read; - } - virtual void DefaultDirectoryPermissions() - { - // Use read/write/executable permissions. - this->DirPermissions = 0; - this->DirPermissions |= mode_owner_read; - this->DirPermissions |= mode_owner_write; - this->DirPermissions |= mode_owner_execute; - this->DirPermissions |= mode_group_read; - this->DirPermissions |= mode_group_execute; - this->DirPermissions |= mode_world_read; - this->DirPermissions |= mode_world_execute; - } - - bool GetDefaultDirectoryPermissions(mode_t** mode) - { - // check if default dir creation permissions were set - const char* default_dir_install_permissions = - this->Makefile->GetDefinition( - "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS"); - if (default_dir_install_permissions && *default_dir_install_permissions) { - std::vector<std::string> items; - cmSystemTools::ExpandListArgument(default_dir_install_permissions, - items); - for (const auto& arg : items) { - if (!this->CheckPermissions(arg, **mode)) { - std::ostringstream e; - e << this->FileCommand->GetError() - << " Set with CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS " - "variable."; - this->FileCommand->SetError(e.str()); - return false; - } - } - } else { - *mode = nullptr; - } - - return true; - } -}; - -bool cmFileCopier::Parse(std::vector<std::string> const& args) -{ - this->Doing = DoingFiles; - for (unsigned int i = 1; i < args.size(); ++i) { - // Check this argument. - if (!this->CheckKeyword(args[i]) && !this->CheckValue(args[i])) { - std::ostringstream e; - e << "called with unknown argument \"" << args[i] << "\"."; - this->FileCommand->SetError(e.str()); - return false; - } - - // Quit if an argument is invalid. - if (this->Doing == DoingError) { - return false; - } - } - - // Require a destination. - if (this->Destination.empty()) { - std::ostringstream e; - e << this->Name << " given no DESTINATION"; - this->FileCommand->SetError(e.str()); - return false; - } - - // If file permissions were not specified set default permissions. - if (!this->UseGivenPermissionsFile && !this->UseSourcePermissions) { - this->DefaultFilePermissions(); - } - - // If directory permissions were not specified set default permissions. - if (!this->UseGivenPermissionsDir && !this->UseSourcePermissions) { - this->DefaultDirectoryPermissions(); - } - - return true; -} - -bool cmFileCopier::CheckKeyword(std::string const& arg) -{ - if (arg == "DESTINATION") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingDestination; - } - } else if (arg == "FILES_FROM_DIR") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingFilesFromDir; - } - } else if (arg == "PATTERN") { - this->Doing = DoingPattern; - } else if (arg == "REGEX") { - this->Doing = DoingRegex; - } else if (arg == "EXCLUDE") { - // Add this property to the current match rule. - if (this->CurrentMatchRule) { - this->CurrentMatchRule->Properties.Exclude = true; - this->Doing = DoingNone; - } else { - this->NotBeforeMatch(arg); - } - } else if (arg == "PERMISSIONS") { - if (this->CurrentMatchRule) { - this->Doing = DoingPermissionsMatch; - } else { - this->NotBeforeMatch(arg); - } - } else if (arg == "FILE_PERMISSIONS") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingPermissionsFile; - this->UseGivenPermissionsFile = true; - } - } else if (arg == "DIRECTORY_PERMISSIONS") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingPermissionsDir; - this->UseGivenPermissionsDir = true; - } - } else if (arg == "USE_SOURCE_PERMISSIONS") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingNone; - this->UseSourcePermissions = true; - } - } else if (arg == "NO_SOURCE_PERMISSIONS") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingNone; - this->UseSourcePermissions = false; - } - } else if (arg == "FILES_MATCHING") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingNone; - this->MatchlessFiles = false; - } - } else { - return false; - } - return true; -} - -bool cmFileCopier::CheckValue(std::string const& arg) -{ - switch (this->Doing) { - case DoingFiles: - this->Files.push_back(arg); - break; - case DoingDestination: - if (arg.empty() || cmSystemTools::FileIsFullPath(arg)) { - this->Destination = arg; - } else { - this->Destination = this->Makefile->GetCurrentBinaryDirectory(); - this->Destination += "/" + arg; - } - this->Doing = DoingNone; - break; - case DoingFilesFromDir: - if (cmSystemTools::FileIsFullPath(arg)) { - this->FilesFromDir = arg; - } else { - this->FilesFromDir = this->Makefile->GetCurrentSourceDirectory(); - this->FilesFromDir += "/" + arg; - } - cmSystemTools::ConvertToUnixSlashes(this->FilesFromDir); - this->Doing = DoingNone; - break; - case DoingPattern: { - // Convert the pattern to a regular expression. Require a - // leading slash and trailing end-of-string in the matched - // string to make sure the pattern matches only whole file - // names. - std::string regex = "/"; - regex += cmsys::Glob::PatternToRegex(arg, false); - regex += "$"; - this->MatchRules.emplace_back(regex); - this->CurrentMatchRule = &*(this->MatchRules.end() - 1); - if (this->CurrentMatchRule->Regex.is_valid()) { - this->Doing = DoingNone; - } else { - std::ostringstream e; - e << "could not compile PATTERN \"" << arg << "\"."; - this->FileCommand->SetError(e.str()); - this->Doing = DoingError; - } - } break; - case DoingRegex: - this->MatchRules.emplace_back(arg); - this->CurrentMatchRule = &*(this->MatchRules.end() - 1); - if (this->CurrentMatchRule->Regex.is_valid()) { - this->Doing = DoingNone; - } else { - std::ostringstream e; - e << "could not compile REGEX \"" << arg << "\"."; - this->FileCommand->SetError(e.str()); - this->Doing = DoingError; - } - break; - case DoingPermissionsFile: - if (!this->CheckPermissions(arg, this->FilePermissions)) { - this->Doing = DoingError; - } - break; - case DoingPermissionsDir: - if (!this->CheckPermissions(arg, this->DirPermissions)) { - this->Doing = DoingError; - } - break; - case DoingPermissionsMatch: - if (!this->CheckPermissions( - arg, this->CurrentMatchRule->Properties.Permissions)) { - this->Doing = DoingError; - } - break; - default: - return false; - } - return true; -} - -bool cmFileCopier::Run(std::vector<std::string> const& args) -{ - if (!this->Parse(args)) { - return false; - } - - for (std::string const& f : this->Files) { - std::string file; - if (!f.empty() && !cmSystemTools::FileIsFullPath(f)) { - if (!this->FilesFromDir.empty()) { - file = this->FilesFromDir; - } else { - file = this->Makefile->GetCurrentSourceDirectory(); - } - file += "/"; - file += f; - } else if (!this->FilesFromDir.empty()) { - this->FileCommand->SetError("option FILES_FROM_DIR requires all files " - "to be specified as relative paths."); - return false; - } else { - file = f; - } - - // Split the input file into its directory and name components. - std::vector<std::string> fromPathComponents; - cmSystemTools::SplitPath(file, fromPathComponents); - std::string fromName = *(fromPathComponents.end() - 1); - std::string fromDir = cmSystemTools::JoinPath( - fromPathComponents.begin(), fromPathComponents.end() - 1); - - // Compute the full path to the destination file. - std::string toFile = this->Destination; - if (!this->FilesFromDir.empty()) { - std::string dir = cmSystemTools::GetFilenamePath(f); - if (!dir.empty()) { - toFile += "/"; - toFile += dir; - } - } - std::string const& toName = this->ToName(fromName); - if (!toName.empty()) { - toFile += "/"; - toFile += toName; - } - - // Construct the full path to the source file. The file name may - // have been changed above. - std::string fromFile = fromDir; - if (!fromName.empty()) { - fromFile += "/"; - fromFile += fromName; - } - - if (!this->Install(fromFile.c_str(), toFile.c_str())) { - return false; - } - } - return true; -} - -bool cmFileCopier::Install(const char* fromFile, const char* toFile) -{ - if (!*fromFile) { - std::ostringstream e; - e << "INSTALL encountered an empty string input file name."; - this->FileCommand->SetError(e.str()); - return false; - } - - // Collect any properties matching this file name. - MatchProperties match_properties = this->CollectMatchProperties(fromFile); - - // Skip the file if it is excluded. - if (match_properties.Exclude) { - return true; - } - - if (cmSystemTools::SameFile(fromFile, toFile)) { - return true; - } - if (cmSystemTools::FileIsSymlink(fromFile)) { - return this->InstallSymlink(fromFile, toFile); - } - if (cmSystemTools::FileIsDirectory(fromFile)) { - return this->InstallDirectory(fromFile, toFile, match_properties); - } - if (cmSystemTools::FileExists(fromFile)) { - return this->InstallFile(fromFile, toFile, match_properties); - } - return this->ReportMissing(fromFile); -} - -bool cmFileCopier::InstallSymlink(const char* fromFile, const char* toFile) -{ - // Read the original symlink. - std::string symlinkTarget; - if (!cmSystemTools::ReadSymlink(fromFile, symlinkTarget)) { - std::ostringstream e; - e << this->Name << " cannot read symlink \"" << fromFile - << "\" to duplicate at \"" << toFile << "\"."; - this->FileCommand->SetError(e.str()); - return false; - } - - // Compare the symlink value to that at the destination if not - // always installing. - bool copy = true; - if (!this->Always) { - std::string oldSymlinkTarget; - if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) { - if (symlinkTarget == oldSymlinkTarget) { - copy = false; - } - } - } - - // Inform the user about this file installation. - this->ReportCopy(toFile, TypeLink, copy); - - if (copy) { - // Remove the destination file so we can always create the symlink. - cmSystemTools::RemoveFile(toFile); - - // Create destination directory if it doesn't exist - cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile)); - - // Create the symlink. - if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) { - std::ostringstream e; - e << this->Name << " cannot duplicate symlink \"" << fromFile - << "\" at \"" << toFile << "\"."; - this->FileCommand->SetError(e.str()); - return false; - } - } - - return true; -} - -bool cmFileCopier::InstallFile(const char* fromFile, const char* toFile, - MatchProperties match_properties) -{ - // Determine whether we will copy the file. - bool copy = true; - if (!this->Always) { - // If both files exist with the same time do not copy. - if (!this->FileTimes.FileTimesDiffer(fromFile, toFile)) { - copy = false; - } - } - - // Inform the user about this file installation. - this->ReportCopy(toFile, TypeFile, copy); - - // Copy the file. - if (copy && !cmSystemTools::CopyAFile(fromFile, toFile, true)) { - std::ostringstream e; - e << this->Name << " cannot copy file \"" << fromFile << "\" to \"" - << toFile << "\"."; - this->FileCommand->SetError(e.str()); - return false; - } - - // Set the file modification time of the destination file. - if (copy && !this->Always) { - // Add write permission so we can set the file time. - // Permissions are set unconditionally below anyway. - mode_t perm = 0; - if (cmSystemTools::GetPermissions(toFile, perm)) { - cmSystemTools::SetPermissions(toFile, perm | mode_owner_write); - } - if (!cmSystemTools::CopyFileTime(fromFile, toFile)) { - std::ostringstream e; - e << this->Name << " cannot set modification time on \"" << toFile - << "\""; - this->FileCommand->SetError(e.str()); - return false; - } - } - - // Set permissions of the destination file. - mode_t permissions = - (match_properties.Permissions ? match_properties.Permissions - : this->FilePermissions); - if (!permissions) { - // No permissions were explicitly provided but the user requested - // that the source file permissions be used. - cmSystemTools::GetPermissions(fromFile, permissions); - } - return this->SetPermissions(toFile, permissions); -} - -bool cmFileCopier::InstallDirectory(const char* source, - const char* destination, - MatchProperties match_properties) -{ - // Inform the user about this directory installation. - this->ReportCopy(destination, TypeDir, - !cmSystemTools::FileIsDirectory(destination)); - - // check if default dir creation permissions were set - mode_t default_dir_mode_v = 0; - mode_t* default_dir_mode = &default_dir_mode_v; - if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) { - return false; - } - - // Make sure the destination directory exists. - if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) { - std::ostringstream e; - e << this->Name << " cannot make directory \"" << destination - << "\": " << cmSystemTools::GetLastSystemError(); - this->FileCommand->SetError(e.str()); - return false; - } - - // Compute the requested permissions for the destination directory. - mode_t permissions = - (match_properties.Permissions ? match_properties.Permissions - : this->DirPermissions); - if (!permissions) { - // No permissions were explicitly provided but the user requested - // that the source directory permissions be used. - cmSystemTools::GetPermissions(source, permissions); - } - - // Compute the set of permissions required on this directory to - // recursively install files and subdirectories safely. - mode_t required_permissions = - mode_owner_read | mode_owner_write | mode_owner_execute; - - // If the required permissions are specified it is safe to set the - // final permissions now. Otherwise we must add the required - // permissions temporarily during file installation. - mode_t permissions_before = 0; - mode_t permissions_after = 0; - if ((permissions & required_permissions) == required_permissions) { - permissions_before = permissions; - } else { - permissions_before = permissions | required_permissions; - permissions_after = permissions; - } - - // Set the required permissions of the destination directory. - if (!this->SetPermissions(destination, permissions_before)) { - return false; - } - - // Load the directory contents to traverse it recursively. - cmsys::Directory dir; - if (source && *source) { - dir.Load(source); - } - unsigned long numFiles = static_cast<unsigned long>(dir.GetNumberOfFiles()); - for (unsigned long fileNum = 0; fileNum < numFiles; ++fileNum) { - if (!(strcmp(dir.GetFile(fileNum), ".") == 0 || - strcmp(dir.GetFile(fileNum), "..") == 0)) { - std::string fromPath = source; - fromPath += "/"; - fromPath += dir.GetFile(fileNum); - std::string toPath = destination; - toPath += "/"; - toPath += dir.GetFile(fileNum); - if (!this->Install(fromPath.c_str(), toPath.c_str())) { - return false; - } - } - } - - // Set the requested permissions of the destination directory. - return this->SetPermissions(destination, permissions_after); -} - bool cmFileCommand::HandleCopyCommand(std::vector<std::string> const& args) { cmFileCopier copier(this); return copier.Run(args); } -struct cmFileInstaller : public cmFileCopier -{ - cmFileInstaller(cmFileCommand* command) - : cmFileCopier(command, "INSTALL") - , InstallType(cmInstallType_FILES) - , Optional(false) - , MessageAlways(false) - , MessageLazy(false) - , MessageNever(false) - , DestDirLength(0) - { - // Installation does not use source permissions by default. - this->UseSourcePermissions = false; - // Check whether to copy files always or only if they have changed. - std::string install_always; - if (cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS", install_always)) { - this->Always = cmSystemTools::IsOn(install_always); - } - // Get the current manifest. - this->Manifest = - this->Makefile->GetSafeDefinition("CMAKE_INSTALL_MANIFEST_FILES"); - } - ~cmFileInstaller() override - { - // Save the updated install manifest. - this->Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES", - this->Manifest.c_str()); - } - -protected: - cmInstallType InstallType; - bool Optional; - bool MessageAlways; - bool MessageLazy; - bool MessageNever; - int DestDirLength; - std::string Rename; - - std::string Manifest; - void ManifestAppend(std::string const& file) - { - if (!this->Manifest.empty()) { - this->Manifest += ";"; - } - this->Manifest += file.substr(this->DestDirLength); - } - - std::string const& ToName(std::string const& fromName) override - { - return this->Rename.empty() ? fromName : this->Rename; - } - - void ReportCopy(const char* toFile, Type type, bool copy) override - { - if (!this->MessageNever && (copy || !this->MessageLazy)) { - std::string message = (copy ? "Installing: " : "Up-to-date: "); - message += toFile; - this->Makefile->DisplayStatus(message.c_str(), -1); - } - if (type != TypeDir) { - // Add the file to the manifest. - this->ManifestAppend(toFile); - } - } - bool ReportMissing(const char* fromFile) override - { - return (this->Optional || this->cmFileCopier::ReportMissing(fromFile)); - } - bool Install(const char* fromFile, const char* toFile) override - { - // Support installing from empty source to make a directory. - if (this->InstallType == cmInstallType_DIRECTORY && !*fromFile) { - return this->InstallDirectory(fromFile, toFile, MatchProperties()); - } - return this->cmFileCopier::Install(fromFile, toFile); - } - - bool Parse(std::vector<std::string> const& args) override; - enum - { - DoingType = DoingLast1, - DoingRename, - DoingLast2 - }; - bool CheckKeyword(std::string const& arg) override; - bool CheckValue(std::string const& arg) override; - void DefaultFilePermissions() override - { - this->cmFileCopier::DefaultFilePermissions(); - // Add execute permissions based on the target type. - switch (this->InstallType) { - case cmInstallType_SHARED_LIBRARY: - case cmInstallType_MODULE_LIBRARY: - if (this->Makefile->IsOn("CMAKE_INSTALL_SO_NO_EXE")) { - break; - } - CM_FALLTHROUGH; - case cmInstallType_EXECUTABLE: - case cmInstallType_PROGRAMS: - this->FilePermissions |= mode_owner_execute; - this->FilePermissions |= mode_group_execute; - this->FilePermissions |= mode_world_execute; - break; - default: - break; - } - } - bool GetTargetTypeFromString(const std::string& stype); - bool HandleInstallDestination(); -}; - -bool cmFileInstaller::Parse(std::vector<std::string> const& args) -{ - if (!this->cmFileCopier::Parse(args)) { - return false; - } - - if (!this->Rename.empty()) { - if (!this->FilesFromDir.empty()) { - this->FileCommand->SetError("INSTALL option RENAME may not be " - "combined with FILES_FROM_DIR."); - return false; - } - if (this->InstallType != cmInstallType_FILES && - this->InstallType != cmInstallType_PROGRAMS) { - this->FileCommand->SetError("INSTALL option RENAME may be used " - "only with FILES or PROGRAMS."); - return false; - } - if (this->Files.size() > 1) { - this->FileCommand->SetError("INSTALL option RENAME may be used " - "only with one file."); - return false; - } - } - - if (!this->HandleInstallDestination()) { - return false; - } - - if (((this->MessageAlways ? 1 : 0) + (this->MessageLazy ? 1 : 0) + - (this->MessageNever ? 1 : 0)) > 1) { - this->FileCommand->SetError("INSTALL options MESSAGE_ALWAYS, " - "MESSAGE_LAZY, and MESSAGE_NEVER " - "are mutually exclusive."); - return false; - } - - return true; -} - -bool cmFileInstaller::CheckKeyword(std::string const& arg) -{ - if (arg == "TYPE") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingType; - } - } else if (arg == "FILES") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingFiles; - } - } else if (arg == "RENAME") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingRename; - } - } else if (arg == "OPTIONAL") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingNone; - this->Optional = true; - } - } else if (arg == "MESSAGE_ALWAYS") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingNone; - this->MessageAlways = true; - } - } else if (arg == "MESSAGE_LAZY") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingNone; - this->MessageLazy = true; - } - } else if (arg == "MESSAGE_NEVER") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - this->Doing = DoingNone; - this->MessageNever = true; - } - } else if (arg == "PERMISSIONS") { - if (this->CurrentMatchRule) { - this->Doing = DoingPermissionsMatch; - } else { - // file(INSTALL) aliases PERMISSIONS to FILE_PERMISSIONS - this->Doing = DoingPermissionsFile; - this->UseGivenPermissionsFile = true; - } - } else if (arg == "DIR_PERMISSIONS") { - if (this->CurrentMatchRule) { - this->NotAfterMatch(arg); - } else { - // file(INSTALL) aliases DIR_PERMISSIONS to DIRECTORY_PERMISSIONS - this->Doing = DoingPermissionsDir; - this->UseGivenPermissionsDir = true; - } - } else if (arg == "COMPONENTS" || arg == "CONFIGURATIONS" || - arg == "PROPERTIES") { - std::ostringstream e; - e << "INSTALL called with old-style " << arg << " argument. " - << "This script was generated with an older version of CMake. " - << "Re-run this cmake version on your build tree."; - this->FileCommand->SetError(e.str()); - this->Doing = DoingError; - } else { - return this->cmFileCopier::CheckKeyword(arg); - } - return true; -} - -bool cmFileInstaller::CheckValue(std::string const& arg) -{ - switch (this->Doing) { - case DoingType: - if (!this->GetTargetTypeFromString(arg)) { - this->Doing = DoingError; - } - break; - case DoingRename: - this->Rename = arg; - break; - default: - return this->cmFileCopier::CheckValue(arg); - } - return true; -} - -bool cmFileInstaller::GetTargetTypeFromString(const std::string& stype) -{ - if (stype == "EXECUTABLE") { - this->InstallType = cmInstallType_EXECUTABLE; - } else if (stype == "FILE") { - this->InstallType = cmInstallType_FILES; - } else if (stype == "PROGRAM") { - this->InstallType = cmInstallType_PROGRAMS; - } else if (stype == "STATIC_LIBRARY") { - this->InstallType = cmInstallType_STATIC_LIBRARY; - } else if (stype == "SHARED_LIBRARY") { - this->InstallType = cmInstallType_SHARED_LIBRARY; - } else if (stype == "MODULE") { - this->InstallType = cmInstallType_MODULE_LIBRARY; - } else if (stype == "DIRECTORY") { - this->InstallType = cmInstallType_DIRECTORY; - } else { - std::ostringstream e; - e << "Option TYPE given unknown value \"" << stype << "\"."; - this->FileCommand->SetError(e.str()); - return false; - } - return true; -} - -bool cmFileInstaller::HandleInstallDestination() -{ - std::string& destination = this->Destination; - - // allow for / to be a valid destination - if (destination.size() < 2 && destination != "/") { - this->FileCommand->SetError("called with inappropriate arguments. " - "No DESTINATION provided or ."); - return false; - } - - std::string sdestdir; - if (cmSystemTools::GetEnv("DESTDIR", sdestdir) && !sdestdir.empty()) { - cmSystemTools::ConvertToUnixSlashes(sdestdir); - char ch1 = destination[0]; - char ch2 = destination[1]; - char ch3 = 0; - if (destination.size() > 2) { - ch3 = destination[2]; - } - int skip = 0; - if (ch1 != '/') { - int relative = 0; - if (((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')) && - ch2 == ':') { - // Assume windows - // let's do some destdir magic: - skip = 2; - if (ch3 != '/') { - relative = 1; - } - } else { - relative = 1; - } - if (relative) { - // This is relative path on unix or windows. Since we are doing - // destdir, this case does not make sense. - this->FileCommand->SetError( - "called with relative DESTINATION. This " - "does not make sense when using DESTDIR. Specify " - "absolute path or remove DESTDIR environment variable."); - return false; - } - } else { - if (ch2 == '/') { - // looks like a network path. - std::string message = - "called with network path DESTINATION. This " - "does not make sense when using DESTDIR. Specify local " - "absolute path or remove DESTDIR environment variable." - "\nDESTINATION=\n"; - message += destination; - this->FileCommand->SetError(message); - return false; - } - } - destination = sdestdir + (destination.c_str() + skip); - this->DestDirLength = int(sdestdir.size()); - } - - // check if default dir creation permissions were set - mode_t default_dir_mode_v = 0; - mode_t* default_dir_mode = &default_dir_mode_v; - if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) { - return false; - } - - if (this->InstallType != cmInstallType_DIRECTORY) { - if (!cmSystemTools::FileExists(destination)) { - if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) { - std::string errstring = "cannot create directory: " + destination + - ". Maybe need administrative privileges."; - this->FileCommand->SetError(errstring); - return false; - } - } - if (!cmSystemTools::FileIsDirectory(destination)) { - std::string errstring = - "INSTALL destination: " + destination + " is not a directory."; - this->FileCommand->SetError(errstring); - return false; - } - } - return true; -} - bool cmFileCommand::HandleRPathChangeCommand( std::vector<std::string> const& args) { // Evaluate arguments. - const char* file = nullptr; + std::string file; const char* oldRPath = nullptr; const char* newRPath = nullptr; enum Doing @@ -2161,7 +1081,7 @@ bool cmFileCommand::HandleRPathChangeCommand( } else if (args[i] == "FILE") { doing = DoingFile; } else if (doing == DoingFile) { - file = args[i].c_str(); + file = args[i]; doing = DoingNone; } else if (doing == DoingOld) { oldRPath = args[i].c_str(); @@ -2176,7 +1096,7 @@ bool cmFileCommand::HandleRPathChangeCommand( return false; } } - if (!file) { + if (file.empty()) { this->SetError("RPATH_CHANGE not given FILE option."); return false; } @@ -2218,7 +1138,7 @@ bool cmFileCommand::HandleRPathChangeCommand( message += "\" to \""; message += newRPath; message += "\""; - this->Makefile->DisplayStatus(message.c_str(), -1); + this->Makefile->DisplayStatus(message, -1); } if (have_ft) { cmSystemTools::FileTimeSet(file, ft); @@ -2232,7 +1152,7 @@ bool cmFileCommand::HandleRPathRemoveCommand( std::vector<std::string> const& args) { // Evaluate arguments. - const char* file = nullptr; + std::string file; enum Doing { DoingNone, @@ -2243,7 +1163,7 @@ bool cmFileCommand::HandleRPathRemoveCommand( if (args[i] == "FILE") { doing = DoingFile; } else if (doing == DoingFile) { - file = args[i].c_str(); + file = args[i]; doing = DoingNone; } else { std::ostringstream e; @@ -2252,7 +1172,7 @@ bool cmFileCommand::HandleRPathRemoveCommand( return false; } } - if (!file) { + if (file.empty()) { this->SetError("RPATH_REMOVE not given FILE option."); return false; } @@ -2282,7 +1202,7 @@ bool cmFileCommand::HandleRPathRemoveCommand( std::string message = "Removed runtime path from \""; message += file; message += "\""; - this->Makefile->DisplayStatus(message.c_str(), -1); + this->Makefile->DisplayStatus(message, -1); } if (have_ft) { cmSystemTools::FileTimeSet(file, ft); @@ -2296,7 +1216,7 @@ bool cmFileCommand::HandleRPathCheckCommand( std::vector<std::string> const& args) { // Evaluate arguments. - const char* file = nullptr; + std::string file; const char* rpath = nullptr; enum Doing { @@ -2311,7 +1231,7 @@ bool cmFileCommand::HandleRPathCheckCommand( } else if (args[i] == "FILE") { doing = DoingFile; } else if (doing == DoingFile) { - file = args[i].c_str(); + file = args[i]; doing = DoingNone; } else if (doing == DoingRPath) { rpath = args[i].c_str(); @@ -2323,7 +1243,7 @@ bool cmFileCommand::HandleRPathCheckCommand( return false; } } - if (!file) { + if (file.empty()) { this->SetError("RPATH_CHECK not given FILE option."); return false; } @@ -2351,55 +1271,54 @@ bool cmFileCommand::HandleReadElfCommand(std::vector<std::string> const& args) return false; } - cmCommandArgumentsHelper argHelper; - cmCommandArgumentGroup group; + std::string const& fileNameArg = args[1]; - cmCAString readArg(&argHelper, "READ_ELF"); - cmCAString fileNameArg(&argHelper, nullptr); - - cmCAString rpathArg(&argHelper, "RPATH", &group); - cmCAString runpathArg(&argHelper, "RUNPATH", &group); - cmCAString errorArg(&argHelper, "CAPTURE_ERROR", &group); + struct Arguments + { + std::string RPath; + std::string RunPath; + std::string Error; + }; - readArg.Follows(nullptr); - fileNameArg.Follows(&readArg); - group.Follows(&fileNameArg); - argHelper.Parse(&args, nullptr); + static auto const parser = cmArgumentParser<Arguments>{} + .Bind("RPATH"_s, &Arguments::RPath) + .Bind("RUNPATH"_s, &Arguments::RunPath) + .Bind("CAPTURE_ERROR"_s, &Arguments::Error); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2)); - if (!cmSystemTools::FileExists(fileNameArg.GetString(), true)) { + if (!cmSystemTools::FileExists(fileNameArg, true)) { std::ostringstream e; - e << "READ_ELF given FILE \"" << fileNameArg.GetString() - << "\" that does not exist."; + e << "READ_ELF given FILE \"" << fileNameArg << "\" that does not exist."; this->SetError(e.str()); return false; } #if defined(CMAKE_USE_ELF_PARSER) - cmELF elf(fileNameArg.GetCString()); + cmELF elf(fileNameArg.c_str()); - if (!rpathArg.GetString().empty()) { + if (!arguments.RPath.empty()) { if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) { std::string rpath(se_rpath->Value); std::replace(rpath.begin(), rpath.end(), ':', ';'); - this->Makefile->AddDefinition(rpathArg.GetString(), rpath.c_str()); + this->Makefile->AddDefinition(arguments.RPath, rpath.c_str()); } } - if (!runpathArg.GetString().empty()) { + if (!arguments.RunPath.empty()) { if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) { std::string runpath(se_runpath->Value); std::replace(runpath.begin(), runpath.end(), ':', ';'); - this->Makefile->AddDefinition(runpathArg.GetString(), runpath.c_str()); + this->Makefile->AddDefinition(arguments.RunPath, runpath.c_str()); } } return true; #else std::string error = "ELF parser not available on this platform."; - if (errorArg.GetString().empty()) { + if (arguments.Error.empty()) { this->SetError(error); return false; } - this->Makefile->AddDefinition(errorArg.GetString(), error.c_str()); + this->Makefile->AddDefinition(arguments.Error, error.c_str()); return true; #endif } @@ -2481,14 +1400,14 @@ bool cmFileCommand::HandleRemove(std::vector<std::string> const& args, { std::string message; - std::vector<std::string>::const_iterator i = args.begin(); - i++; // Get rid of subcommand - for (; i != args.end(); ++i) { - std::string fileName = *i; + for (std::string const& arg : + cmMakeRange(args).advance(1)) // Get rid of subcommand + { + std::string fileName = arg; if (!cmsys::SystemTools::FileIsFullPath(fileName)) { fileName = this->Makefile->GetCurrentSourceDirectory(); - fileName += "/" + *i; + fileName += "/" + arg; } if (cmSystemTools::FileIsDirectory(fileName) && @@ -2501,44 +1420,43 @@ bool cmFileCommand::HandleRemove(std::vector<std::string> const& args, return true; } +namespace { +std::string ToNativePath(const std::string& path) +{ + const auto& outPath = cmSystemTools::ConvertToOutputPath(path); + if (outPath.size() > 1 && outPath.front() == '\"' && + outPath.back() == '\"') { + return outPath.substr(1, outPath.size() - 2); + } + return outPath; +} + +std::string ToCMakePath(const std::string& path) +{ + auto temp = path; + cmSystemTools::ConvertToUnixSlashes(temp); + return temp; +} +} + bool cmFileCommand::HandleCMakePathCommand( std::vector<std::string> const& args, bool nativePath) { - std::vector<std::string>::const_iterator i = args.begin(); if (args.size() != 3) { this->SetError("FILE([TO_CMAKE_PATH|TO_NATIVE_PATH] path result) must be " "called with exactly three arguments."); return false; } - i++; // Get rid of subcommand #if defined(_WIN32) && !defined(__CYGWIN__) char pathSep = ';'; #else char pathSep = ':'; #endif - std::vector<std::string> path = cmSystemTools::SplitString(*i, pathSep); - i++; - const char* var = i->c_str(); - std::string value; - for (std::vector<std::string>::iterator j = path.begin(); j != path.end(); - ++j) { - if (j != path.begin()) { - value += ";"; - } - if (!nativePath) { - cmSystemTools::ConvertToUnixSlashes(*j); - } else { - *j = cmSystemTools::ConvertToOutputPath(*j); - // remove double quotes in the path - std::string& s = *j; + std::vector<std::string> path = cmSystemTools::SplitString(args[1], pathSep); - if (s.size() > 1 && s.front() == '\"' && s.back() == '\"') { - s = s.substr(1, s.size() - 2); - } - } - value += *j; - } - this->Makefile->AddDefinition(var, value.c_str()); + std::string value = cmJoin( + cmMakeRange(path).transform(nativePath ? ToNativePath : ToCMakePath), ";"); + this->Makefile->AddDefinition(args[2], value.c_str()); return true; } @@ -2651,7 +1569,7 @@ int cmFileDownloadProgressCallback(void* clientp, double dltotal, double dlnow, if (helper->UpdatePercentage(dlnow, dltotal, status)) { cmFileCommand* fc = helper->GetFileCommand(); cmMakefile* mf = fc->GetMakefile(); - mf->DisplayStatus(status.c_str(), -1); + mf->DisplayStatus(status, -1); } return 0; @@ -2669,7 +1587,7 @@ int cmFileUploadProgressCallback(void* clientp, double dltotal, double dlnow, if (helper->UpdatePercentage(ulnow, ultotal, status)) { cmFileCommand* fc = helper->GetFileCommand(); cmMakefile* mf = fc->GetMakefile(); - mf->DisplayStatus(status.c_str(), -1); + mf->DisplayStatus(status, -1); } return 0; @@ -2693,6 +1611,9 @@ public: } } + cURLEasyGuard(const cURLEasyGuard&) = delete; + cURLEasyGuard& operator=(const cURLEasyGuard&) = delete; + void release() { this->Easy = nullptr; } private: @@ -3067,7 +1988,7 @@ bool cmFileCommand::HandleDownloadCommand(std::vector<std::string> const& args) if (!logVar.empty()) { chunkDebug.push_back(0); - this->Makefile->AddDefinition(logVar, &*chunkDebug.begin()); + this->Makefile->AddDefinition(logVar, chunkDebug.data()); } return true; @@ -3326,14 +2247,14 @@ bool cmFileCommand::HandleUploadCommand(std::vector<std::string> const& args) if (!chunkResponse.empty()) { chunkResponse.push_back(0); log += "Response:\n"; - log += &*chunkResponse.begin(); + log += chunkResponse.data(); log += "\n"; } if (!chunkDebug.empty()) { chunkDebug.push_back(0); log += "Debug:\n"; - log += &*chunkDebug.begin(); + log += chunkDebug.data(); log += "\n"; } @@ -3674,44 +2595,39 @@ bool cmFileCommand::HandleCreateLinkCommand( return false; } - cmCommandArgumentsHelper argHelper; - cmCommandArgumentGroup group; - - cmCAString linkArg(&argHelper, "CREATE_LINK"); - cmCAString fileArg(&argHelper, nullptr); - cmCAString newFileArg(&argHelper, nullptr); + std::string const& fileName = args[1]; + std::string const& newFileName = args[2]; - cmCAString resultArg(&argHelper, "RESULT", &group); - cmCAEnabler copyOnErrorArg(&argHelper, "COPY_ON_ERROR", &group); - cmCAEnabler symbolicArg(&argHelper, "SYMBOLIC", &group); + struct Arguments + { + std::string Result; + bool CopyOnError = false; + bool Symbolic = false; + }; - linkArg.Follows(nullptr); - fileArg.Follows(&linkArg); - newFileArg.Follows(&fileArg); - group.Follows(&newFileArg); + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("RESULT"_s, &Arguments::Result) + .Bind("COPY_ON_ERROR"_s, &Arguments::CopyOnError) + .Bind("SYMBOLIC"_s, &Arguments::Symbolic); std::vector<std::string> unconsumedArgs; - argHelper.Parse(&args, &unconsumedArgs); + Arguments const arguments = + parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs); if (!unconsumedArgs.empty()) { this->SetError("unknown argument: \"" + unconsumedArgs.front() + '\"'); return false; } - std::string fileName = fileArg.GetString(); - std::string newFileName = newFileArg.GetString(); - - // Output variable for storing the result. - const std::string& resultVar = resultArg.GetString(); - // The system error message generated in the operation. std::string result; // Check if the paths are distinct. if (fileName == newFileName) { result = "CREATE_LINK cannot use same file and newfile"; - if (!resultVar.empty()) { - this->Makefile->AddDefinition(resultVar, result.c_str()); + if (!arguments.Result.empty()) { + this->Makefile->AddDefinition(arguments.Result, result.c_str()); return true; } this->SetError(result); @@ -3719,10 +2635,10 @@ bool cmFileCommand::HandleCreateLinkCommand( } // Hard link requires original file to exist. - if (!symbolicArg.IsEnabled() && !cmSystemTools::FileExists(fileName)) { + if (!arguments.Symbolic && !cmSystemTools::FileExists(fileName)) { result = "Cannot hard link \'" + fileName + "\' as it does not exist."; - if (!resultVar.empty()) { - this->Makefile->AddDefinition(resultVar, result.c_str()); + if (!arguments.Result.empty()) { + this->Makefile->AddDefinition(arguments.Result, result.c_str()); return true; } this->SetError(result); @@ -3738,8 +2654,8 @@ bool cmFileCommand::HandleCreateLinkCommand( << "' because existing path cannot be removed: " << cmSystemTools::GetLastSystemError() << "\n"; - if (!resultVar.empty()) { - this->Makefile->AddDefinition(resultVar, e.str().c_str()); + if (!arguments.Result.empty()) { + this->Makefile->AddDefinition(arguments.Result, e.str().c_str()); return true; } this->SetError(e.str()); @@ -3750,15 +2666,15 @@ bool cmFileCommand::HandleCreateLinkCommand( bool completed = false; // Check if the command requires a symbolic link. - if (symbolicArg.IsEnabled()) { + if (arguments.Symbolic) { completed = cmSystemTools::CreateSymlink(fileName, newFileName, &result); } else { completed = cmSystemTools::CreateLink(fileName, newFileName, &result); } // Check if copy-on-error is enabled in the arguments. - if (!completed && copyOnErrorArg.IsEnabled()) { - completed = cmSystemTools::cmCopyFile(fileName, newFileName); + if (!completed && arguments.CopyOnError) { + completed = cmsys::SystemTools::CopyFileAlways(fileName, newFileName); if (!completed) { result = "Copy failed: " + cmSystemTools::GetLastSystemError(); } @@ -3767,14 +2683,14 @@ bool cmFileCommand::HandleCreateLinkCommand( // Check if the operation was successful. if (completed) { result = "0"; - } else if (resultVar.empty()) { + } else if (arguments.Result.empty()) { // The operation failed and the result is not reported in a variable. this->SetError(result); return false; } - if (!resultVar.empty()) { - this->Makefile->AddDefinition(resultVar, result.c_str()); + if (!arguments.Result.empty()) { + this->Makefile->AddDefinition(arguments.Result, result.c_str()); } return true; diff --git a/Source/cmFileCopier.cxx b/Source/cmFileCopier.cxx new file mode 100644 index 0000000..8913e6d --- /dev/null +++ b/Source/cmFileCopier.cxx @@ -0,0 +1,660 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmFileCopier.h" + +#include "cmFSPermissions.h" +#include "cmFileCommand.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmsys/Directory.hxx" +#include "cmsys/Glob.hxx" + +#ifdef _WIN32 +# include "cmsys/FStream.hxx" +#endif + +#include <sstream> +#include <string.h> + +using namespace cmFSPermissions; + +cmFileCopier::cmFileCopier(cmFileCommand* command, const char* name) + : FileCommand(command) + , Makefile(command->GetMakefile()) + , Name(name) + , Always(false) + , MatchlessFiles(true) + , FilePermissions(0) + , DirPermissions(0) + , CurrentMatchRule(nullptr) + , UseGivenPermissionsFile(false) + , UseGivenPermissionsDir(false) + , UseSourcePermissions(true) + , Doing(DoingNone) +{ +} + +cmFileCopier::~cmFileCopier() = default; + +cmFileCopier::MatchProperties cmFileCopier::CollectMatchProperties( + const std::string& file) +{ + // Match rules are case-insensitive on some platforms. +#if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__) + const std::string file_to_match = cmSystemTools::LowerCase(file); +#else + const std::string& file_to_match = file; +#endif + + // Collect properties from all matching rules. + bool matched = false; + MatchProperties result; + for (MatchRule& mr : this->MatchRules) { + if (mr.Regex.find(file_to_match)) { + matched = true; + result.Exclude |= mr.Properties.Exclude; + result.Permissions |= mr.Properties.Permissions; + } + } + if (!matched && !this->MatchlessFiles) { + result.Exclude = !cmSystemTools::FileIsDirectory(file); + } + return result; +} + +bool cmFileCopier::SetPermissions(const std::string& toFile, + mode_t permissions) +{ + if (permissions) { +#ifdef WIN32 + if (Makefile->IsOn("CMAKE_CROSSCOMPILING")) { + // Store the mode in an NTFS alternate stream. + std::string mode_t_adt_filename = toFile + ":cmake_mode_t"; + + // Writing to an NTFS alternate stream changes the modification + // time, so we need to save and restore its original value. + cmSystemToolsFileTime* file_time_orig = cmSystemTools::FileTimeNew(); + cmSystemTools::FileTimeGet(toFile, file_time_orig); + + cmsys::ofstream permissionStream(mode_t_adt_filename.c_str()); + + if (permissionStream) { + permissionStream << std::oct << permissions << std::endl; + } + + permissionStream.close(); + + cmSystemTools::FileTimeSet(toFile, file_time_orig); + + cmSystemTools::FileTimeDelete(file_time_orig); + } +#endif + + if (!cmSystemTools::SetPermissions(toFile, permissions)) { + std::ostringstream e; + e << this->Name << " cannot set permissions on \"" << toFile << "\""; + this->FileCommand->SetError(e.str()); + return false; + } + } + return true; +} + +// Translate an argument to a permissions bit. +bool cmFileCopier::CheckPermissions(std::string const& arg, + mode_t& permissions) +{ + if (!cmFSPermissions::stringToModeT(arg, permissions)) { + std::ostringstream e; + e << this->Name << " given invalid permission \"" << arg << "\"."; + this->FileCommand->SetError(e.str()); + return false; + } + return true; +} + +std::string const& cmFileCopier::ToName(std::string const& fromName) +{ + return fromName; +} + +bool cmFileCopier::ReportMissing(const std::string& fromFile) +{ + // The input file does not exist and installation is not optional. + std::ostringstream e; + e << this->Name << " cannot find \"" << fromFile << "\"."; + this->FileCommand->SetError(e.str()); + return false; +} + +void cmFileCopier::NotBeforeMatch(std::string const& arg) +{ + std::ostringstream e; + e << "option " << arg << " may not appear before PATTERN or REGEX."; + this->FileCommand->SetError(e.str()); + this->Doing = DoingError; +} + +void cmFileCopier::NotAfterMatch(std::string const& arg) +{ + std::ostringstream e; + e << "option " << arg << " may not appear after PATTERN or REGEX."; + this->FileCommand->SetError(e.str()); + this->Doing = DoingError; +} + +void cmFileCopier::DefaultFilePermissions() +{ + // Use read/write permissions. + this->FilePermissions = 0; + this->FilePermissions |= mode_owner_read; + this->FilePermissions |= mode_owner_write; + this->FilePermissions |= mode_group_read; + this->FilePermissions |= mode_world_read; +} + +void cmFileCopier::DefaultDirectoryPermissions() +{ + // Use read/write/executable permissions. + this->DirPermissions = 0; + this->DirPermissions |= mode_owner_read; + this->DirPermissions |= mode_owner_write; + this->DirPermissions |= mode_owner_execute; + this->DirPermissions |= mode_group_read; + this->DirPermissions |= mode_group_execute; + this->DirPermissions |= mode_world_read; + this->DirPermissions |= mode_world_execute; +} + +bool cmFileCopier::GetDefaultDirectoryPermissions(mode_t** mode) +{ + // check if default dir creation permissions were set + const char* default_dir_install_permissions = this->Makefile->GetDefinition( + "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS"); + if (default_dir_install_permissions && *default_dir_install_permissions) { + std::vector<std::string> items; + cmSystemTools::ExpandListArgument(default_dir_install_permissions, items); + for (const auto& arg : items) { + if (!this->CheckPermissions(arg, **mode)) { + std::ostringstream e; + e << this->FileCommand->GetError() + << " Set with CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS " + "variable."; + this->FileCommand->SetError(e.str()); + return false; + } + } + } else { + *mode = nullptr; + } + + return true; +} + +bool cmFileCopier::Parse(std::vector<std::string> const& args) +{ + this->Doing = DoingFiles; + for (unsigned int i = 1; i < args.size(); ++i) { + // Check this argument. + if (!this->CheckKeyword(args[i]) && !this->CheckValue(args[i])) { + std::ostringstream e; + e << "called with unknown argument \"" << args[i] << "\"."; + this->FileCommand->SetError(e.str()); + return false; + } + + // Quit if an argument is invalid. + if (this->Doing == DoingError) { + return false; + } + } + + // Require a destination. + if (this->Destination.empty()) { + std::ostringstream e; + e << this->Name << " given no DESTINATION"; + this->FileCommand->SetError(e.str()); + return false; + } + + // If file permissions were not specified set default permissions. + if (!this->UseGivenPermissionsFile && !this->UseSourcePermissions) { + this->DefaultFilePermissions(); + } + + // If directory permissions were not specified set default permissions. + if (!this->UseGivenPermissionsDir && !this->UseSourcePermissions) { + this->DefaultDirectoryPermissions(); + } + + return true; +} + +bool cmFileCopier::CheckKeyword(std::string const& arg) +{ + if (arg == "DESTINATION") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingDestination; + } + } else if (arg == "FILES_FROM_DIR") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingFilesFromDir; + } + } else if (arg == "PATTERN") { + this->Doing = DoingPattern; + } else if (arg == "REGEX") { + this->Doing = DoingRegex; + } else if (arg == "EXCLUDE") { + // Add this property to the current match rule. + if (this->CurrentMatchRule) { + this->CurrentMatchRule->Properties.Exclude = true; + this->Doing = DoingNone; + } else { + this->NotBeforeMatch(arg); + } + } else if (arg == "PERMISSIONS") { + if (this->CurrentMatchRule) { + this->Doing = DoingPermissionsMatch; + } else { + this->NotBeforeMatch(arg); + } + } else if (arg == "FILE_PERMISSIONS") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingPermissionsFile; + this->UseGivenPermissionsFile = true; + } + } else if (arg == "DIRECTORY_PERMISSIONS") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingPermissionsDir; + this->UseGivenPermissionsDir = true; + } + } else if (arg == "USE_SOURCE_PERMISSIONS") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingNone; + this->UseSourcePermissions = true; + } + } else if (arg == "NO_SOURCE_PERMISSIONS") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingNone; + this->UseSourcePermissions = false; + } + } else if (arg == "FILES_MATCHING") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingNone; + this->MatchlessFiles = false; + } + } else { + return false; + } + return true; +} + +bool cmFileCopier::CheckValue(std::string const& arg) +{ + switch (this->Doing) { + case DoingFiles: + this->Files.push_back(arg); + break; + case DoingDestination: + if (arg.empty() || cmSystemTools::FileIsFullPath(arg)) { + this->Destination = arg; + } else { + this->Destination = this->Makefile->GetCurrentBinaryDirectory(); + this->Destination += "/" + arg; + } + this->Doing = DoingNone; + break; + case DoingFilesFromDir: + if (cmSystemTools::FileIsFullPath(arg)) { + this->FilesFromDir = arg; + } else { + this->FilesFromDir = this->Makefile->GetCurrentSourceDirectory(); + this->FilesFromDir += "/" + arg; + } + cmSystemTools::ConvertToUnixSlashes(this->FilesFromDir); + this->Doing = DoingNone; + break; + case DoingPattern: { + // Convert the pattern to a regular expression. Require a + // leading slash and trailing end-of-string in the matched + // string to make sure the pattern matches only whole file + // names. + std::string regex = "/"; + regex += cmsys::Glob::PatternToRegex(arg, false); + regex += "$"; + this->MatchRules.emplace_back(regex); + this->CurrentMatchRule = &*(this->MatchRules.end() - 1); + if (this->CurrentMatchRule->Regex.is_valid()) { + this->Doing = DoingNone; + } else { + std::ostringstream e; + e << "could not compile PATTERN \"" << arg << "\"."; + this->FileCommand->SetError(e.str()); + this->Doing = DoingError; + } + } break; + case DoingRegex: + this->MatchRules.emplace_back(arg); + this->CurrentMatchRule = &*(this->MatchRules.end() - 1); + if (this->CurrentMatchRule->Regex.is_valid()) { + this->Doing = DoingNone; + } else { + std::ostringstream e; + e << "could not compile REGEX \"" << arg << "\"."; + this->FileCommand->SetError(e.str()); + this->Doing = DoingError; + } + break; + case DoingPermissionsFile: + if (!this->CheckPermissions(arg, this->FilePermissions)) { + this->Doing = DoingError; + } + break; + case DoingPermissionsDir: + if (!this->CheckPermissions(arg, this->DirPermissions)) { + this->Doing = DoingError; + } + break; + case DoingPermissionsMatch: + if (!this->CheckPermissions( + arg, this->CurrentMatchRule->Properties.Permissions)) { + this->Doing = DoingError; + } + break; + default: + return false; + } + return true; +} + +bool cmFileCopier::Run(std::vector<std::string> const& args) +{ + if (!this->Parse(args)) { + return false; + } + + for (std::string const& f : this->Files) { + std::string file; + if (!f.empty() && !cmSystemTools::FileIsFullPath(f)) { + if (!this->FilesFromDir.empty()) { + file = this->FilesFromDir; + } else { + file = this->Makefile->GetCurrentSourceDirectory(); + } + file += "/"; + file += f; + } else if (!this->FilesFromDir.empty()) { + this->FileCommand->SetError("option FILES_FROM_DIR requires all files " + "to be specified as relative paths."); + return false; + } else { + file = f; + } + + // Split the input file into its directory and name components. + std::vector<std::string> fromPathComponents; + cmSystemTools::SplitPath(file, fromPathComponents); + std::string fromName = *(fromPathComponents.end() - 1); + std::string fromDir = cmSystemTools::JoinPath( + fromPathComponents.begin(), fromPathComponents.end() - 1); + + // Compute the full path to the destination file. + std::string toFile = this->Destination; + if (!this->FilesFromDir.empty()) { + std::string dir = cmSystemTools::GetFilenamePath(f); + if (!dir.empty()) { + toFile += "/"; + toFile += dir; + } + } + std::string const& toName = this->ToName(fromName); + if (!toName.empty()) { + toFile += "/"; + toFile += toName; + } + + // Construct the full path to the source file. The file name may + // have been changed above. + std::string fromFile = fromDir; + if (!fromName.empty()) { + fromFile += "/"; + fromFile += fromName; + } + + if (!this->Install(fromFile, toFile)) { + return false; + } + } + return true; +} + +bool cmFileCopier::Install(const std::string& fromFile, + const std::string& toFile) +{ + if (fromFile.empty()) { + std::ostringstream e; + e << "INSTALL encountered an empty string input file name."; + this->FileCommand->SetError(e.str()); + return false; + } + + // Collect any properties matching this file name. + MatchProperties match_properties = this->CollectMatchProperties(fromFile); + + // Skip the file if it is excluded. + if (match_properties.Exclude) { + return true; + } + + if (cmSystemTools::SameFile(fromFile, toFile)) { + return true; + } + if (cmSystemTools::FileIsSymlink(fromFile)) { + return this->InstallSymlink(fromFile, toFile); + } + if (cmSystemTools::FileIsDirectory(fromFile)) { + return this->InstallDirectory(fromFile, toFile, match_properties); + } + if (cmSystemTools::FileExists(fromFile)) { + return this->InstallFile(fromFile, toFile, match_properties); + } + return this->ReportMissing(fromFile); +} + +bool cmFileCopier::InstallSymlink(const std::string& fromFile, + const std::string& toFile) +{ + // Read the original symlink. + std::string symlinkTarget; + if (!cmSystemTools::ReadSymlink(fromFile, symlinkTarget)) { + std::ostringstream e; + e << this->Name << " cannot read symlink \"" << fromFile + << "\" to duplicate at \"" << toFile << "\"."; + this->FileCommand->SetError(e.str()); + return false; + } + + // Compare the symlink value to that at the destination if not + // always installing. + bool copy = true; + if (!this->Always) { + std::string oldSymlinkTarget; + if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) { + if (symlinkTarget == oldSymlinkTarget) { + copy = false; + } + } + } + + // Inform the user about this file installation. + this->ReportCopy(toFile, TypeLink, copy); + + if (copy) { + // Remove the destination file so we can always create the symlink. + cmSystemTools::RemoveFile(toFile); + + // Create destination directory if it doesn't exist + cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile)); + + // Create the symlink. + if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) { + std::ostringstream e; + e << this->Name << " cannot duplicate symlink \"" << fromFile + << "\" at \"" << toFile << "\"."; + this->FileCommand->SetError(e.str()); + return false; + } + } + + return true; +} + +bool cmFileCopier::InstallFile(const std::string& fromFile, + const std::string& toFile, + MatchProperties match_properties) +{ + // Determine whether we will copy the file. + bool copy = true; + if (!this->Always) { + // If both files exist with the same time do not copy. + if (!this->FileTimes.DifferS(fromFile, toFile)) { + copy = false; + } + } + + // Inform the user about this file installation. + this->ReportCopy(toFile, TypeFile, copy); + + // Copy the file. + if (copy && !cmSystemTools::CopyAFile(fromFile, toFile, true)) { + std::ostringstream e; + e << this->Name << " cannot copy file \"" << fromFile << "\" to \"" + << toFile << "\"."; + this->FileCommand->SetError(e.str()); + return false; + } + + // Set the file modification time of the destination file. + if (copy && !this->Always) { + // Add write permission so we can set the file time. + // Permissions are set unconditionally below anyway. + mode_t perm = 0; + if (cmSystemTools::GetPermissions(toFile, perm)) { + cmSystemTools::SetPermissions(toFile, perm | mode_owner_write); + } + if (!cmSystemTools::CopyFileTime(fromFile, toFile)) { + std::ostringstream e; + e << this->Name << " cannot set modification time on \"" << toFile + << "\""; + this->FileCommand->SetError(e.str()); + return false; + } + } + + // Set permissions of the destination file. + mode_t permissions = + (match_properties.Permissions ? match_properties.Permissions + : this->FilePermissions); + if (!permissions) { + // No permissions were explicitly provided but the user requested + // that the source file permissions be used. + cmSystemTools::GetPermissions(fromFile, permissions); + } + return this->SetPermissions(toFile, permissions); +} + +bool cmFileCopier::InstallDirectory(const std::string& source, + const std::string& destination, + MatchProperties match_properties) +{ + // Inform the user about this directory installation. + this->ReportCopy(destination, TypeDir, + !cmSystemTools::FileIsDirectory(destination)); + + // check if default dir creation permissions were set + mode_t default_dir_mode_v = 0; + mode_t* default_dir_mode = &default_dir_mode_v; + if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) { + return false; + } + + // Make sure the destination directory exists. + if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) { + std::ostringstream e; + e << this->Name << " cannot make directory \"" << destination + << "\": " << cmSystemTools::GetLastSystemError(); + this->FileCommand->SetError(e.str()); + return false; + } + + // Compute the requested permissions for the destination directory. + mode_t permissions = + (match_properties.Permissions ? match_properties.Permissions + : this->DirPermissions); + if (!permissions) { + // No permissions were explicitly provided but the user requested + // that the source directory permissions be used. + cmSystemTools::GetPermissions(source, permissions); + } + + // Compute the set of permissions required on this directory to + // recursively install files and subdirectories safely. + mode_t required_permissions = + mode_owner_read | mode_owner_write | mode_owner_execute; + + // If the required permissions are specified it is safe to set the + // final permissions now. Otherwise we must add the required + // permissions temporarily during file installation. + mode_t permissions_before = 0; + mode_t permissions_after = 0; + if ((permissions & required_permissions) == required_permissions) { + permissions_before = permissions; + } else { + permissions_before = permissions | required_permissions; + permissions_after = permissions; + } + + // Set the required permissions of the destination directory. + if (!this->SetPermissions(destination, permissions_before)) { + return false; + } + + // Load the directory contents to traverse it recursively. + cmsys::Directory dir; + if (!source.empty()) { + dir.Load(source); + } + unsigned long numFiles = static_cast<unsigned long>(dir.GetNumberOfFiles()); + for (unsigned long fileNum = 0; fileNum < numFiles; ++fileNum) { + if (!(strcmp(dir.GetFile(fileNum), ".") == 0 || + strcmp(dir.GetFile(fileNum), "..") == 0)) { + std::string fromPath = source; + fromPath += "/"; + fromPath += dir.GetFile(fileNum); + std::string toPath = destination; + toPath += "/"; + toPath += dir.GetFile(fileNum); + if (!this->Install(fromPath, toPath)) { + return false; + } + } + } + + // Set the requested permissions of the destination directory. + return this->SetPermissions(destination, permissions_after); +} diff --git a/Source/cmFileCopier.h b/Source/cmFileCopier.h new file mode 100644 index 0000000..003b8f6 --- /dev/null +++ b/Source/cmFileCopier.h @@ -0,0 +1,120 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmFileCopier_h +#define cmFileCopier_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmFileTimeCache.h" +#include "cm_sys_stat.h" +#include "cmsys/RegularExpression.hxx" + +#include <string> +#include <vector> + +class cmFileCommand; +class cmMakefile; + +// File installation helper class. +struct cmFileCopier +{ + cmFileCopier(cmFileCommand* command, const char* name = "COPY"); + virtual ~cmFileCopier(); + + bool Run(std::vector<std::string> const& args); + +protected: + cmFileCommand* FileCommand; + cmMakefile* Makefile; + const char* Name; + bool Always; + cmFileTimeCache FileTimes; + + // Whether to install a file not matching any expression. + bool MatchlessFiles; + + // Permissions for files and directories installed by this object. + mode_t FilePermissions; + mode_t DirPermissions; + + // Properties set by pattern and regex match rules. + struct MatchProperties + { + bool Exclude = false; + mode_t Permissions = 0; + }; + struct MatchRule + { + cmsys::RegularExpression Regex; + MatchProperties Properties; + std::string RegexString; + MatchRule(std::string const& regex) + : Regex(regex) + , RegexString(regex) + { + } + }; + std::vector<MatchRule> MatchRules; + + // Get the properties from rules matching this input file. + MatchProperties CollectMatchProperties(const std::string& file); + + bool SetPermissions(const std::string& toFile, mode_t permissions); + + // Translate an argument to a permissions bit. + bool CheckPermissions(std::string const& arg, mode_t& permissions); + + bool InstallSymlink(const std::string& fromFile, const std::string& toFile); + bool InstallFile(const std::string& fromFile, const std::string& toFile, + MatchProperties match_properties); + bool InstallDirectory(const std::string& source, + const std::string& destination, + MatchProperties match_properties); + virtual bool Install(const std::string& fromFile, const std::string& toFile); + virtual std::string const& ToName(std::string const& fromName); + + enum Type + { + TypeFile, + TypeDir, + TypeLink + }; + virtual void ReportCopy(const std::string&, Type, bool) {} + virtual bool ReportMissing(const std::string& fromFile); + + MatchRule* CurrentMatchRule; + bool UseGivenPermissionsFile; + bool UseGivenPermissionsDir; + bool UseSourcePermissions; + std::string Destination; + std::string FilesFromDir; + std::vector<std::string> Files; + int Doing; + + virtual bool Parse(std::vector<std::string> const& args); + enum + { + DoingNone, + DoingError, + DoingDestination, + DoingFilesFromDir, + DoingFiles, + DoingPattern, + DoingRegex, + DoingPermissionsFile, + DoingPermissionsDir, + DoingPermissionsMatch, + DoingLast1 + }; + virtual bool CheckKeyword(std::string const& arg); + virtual bool CheckValue(std::string const& arg); + + void NotBeforeMatch(std::string const& arg); + void NotAfterMatch(std::string const& arg); + virtual void DefaultFilePermissions(); + virtual void DefaultDirectoryPermissions(); + + bool GetDefaultDirectoryPermissions(mode_t** mode); +}; + +#endif diff --git a/Source/cmFileInstaller.cxx b/Source/cmFileInstaller.cxx new file mode 100644 index 0000000..d4f76fd --- /dev/null +++ b/Source/cmFileInstaller.cxx @@ -0,0 +1,350 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmFileInstaller.h" + +#include "cmFSPermissions.h" +#include "cmFileCommand.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" + +#include "cm_sys_stat.h" + +#include <sstream> + +using namespace cmFSPermissions; + +cmFileInstaller::cmFileInstaller(cmFileCommand* command) + : cmFileCopier(command, "INSTALL") + , InstallType(cmInstallType_FILES) + , Optional(false) + , MessageAlways(false) + , MessageLazy(false) + , MessageNever(false) + , DestDirLength(0) +{ + // Installation does not use source permissions by default. + this->UseSourcePermissions = false; + // Check whether to copy files always or only if they have changed. + std::string install_always; + if (cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS", install_always)) { + this->Always = cmSystemTools::IsOn(install_always); + } + // Get the current manifest. + this->Manifest = + this->Makefile->GetSafeDefinition("CMAKE_INSTALL_MANIFEST_FILES"); +} +cmFileInstaller::~cmFileInstaller() +{ + // Save the updated install manifest. + this->Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES", + this->Manifest.c_str()); +} + +void cmFileInstaller::ManifestAppend(std::string const& file) +{ + if (!this->Manifest.empty()) { + this->Manifest += ";"; + } + this->Manifest += file.substr(this->DestDirLength); +} + +std::string const& cmFileInstaller::ToName(std::string const& fromName) +{ + return this->Rename.empty() ? fromName : this->Rename; +} + +void cmFileInstaller::ReportCopy(const std::string& toFile, Type type, + bool copy) +{ + if (!this->MessageNever && (copy || !this->MessageLazy)) { + std::string message = (copy ? "Installing: " : "Up-to-date: "); + message += toFile; + this->Makefile->DisplayStatus(message, -1); + } + if (type != TypeDir) { + // Add the file to the manifest. + this->ManifestAppend(toFile); + } +} +bool cmFileInstaller::ReportMissing(const std::string& fromFile) +{ + return (this->Optional || this->cmFileCopier::ReportMissing(fromFile)); +} +bool cmFileInstaller::Install(const std::string& fromFile, + const std::string& toFile) +{ + // Support installing from empty source to make a directory. + if (this->InstallType == cmInstallType_DIRECTORY && fromFile.empty()) { + return this->InstallDirectory(fromFile, toFile, MatchProperties()); + } + return this->cmFileCopier::Install(fromFile, toFile); +} + +void cmFileInstaller::DefaultFilePermissions() +{ + this->cmFileCopier::DefaultFilePermissions(); + // Add execute permissions based on the target type. + switch (this->InstallType) { + case cmInstallType_SHARED_LIBRARY: + case cmInstallType_MODULE_LIBRARY: + if (this->Makefile->IsOn("CMAKE_INSTALL_SO_NO_EXE")) { + break; + } + CM_FALLTHROUGH; + case cmInstallType_EXECUTABLE: + case cmInstallType_PROGRAMS: + this->FilePermissions |= mode_owner_execute; + this->FilePermissions |= mode_group_execute; + this->FilePermissions |= mode_world_execute; + break; + default: + break; + } +} + +bool cmFileInstaller::Parse(std::vector<std::string> const& args) +{ + if (!this->cmFileCopier::Parse(args)) { + return false; + } + + if (!this->Rename.empty()) { + if (!this->FilesFromDir.empty()) { + this->FileCommand->SetError("INSTALL option RENAME may not be " + "combined with FILES_FROM_DIR."); + return false; + } + if (this->InstallType != cmInstallType_FILES && + this->InstallType != cmInstallType_PROGRAMS) { + this->FileCommand->SetError("INSTALL option RENAME may be used " + "only with FILES or PROGRAMS."); + return false; + } + if (this->Files.size() > 1) { + this->FileCommand->SetError("INSTALL option RENAME may be used " + "only with one file."); + return false; + } + } + + if (!this->HandleInstallDestination()) { + return false; + } + + if (((this->MessageAlways ? 1 : 0) + (this->MessageLazy ? 1 : 0) + + (this->MessageNever ? 1 : 0)) > 1) { + this->FileCommand->SetError("INSTALL options MESSAGE_ALWAYS, " + "MESSAGE_LAZY, and MESSAGE_NEVER " + "are mutually exclusive."); + return false; + } + + return true; +} + +bool cmFileInstaller::CheckKeyword(std::string const& arg) +{ + if (arg == "TYPE") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingType; + } + } else if (arg == "FILES") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingFiles; + } + } else if (arg == "RENAME") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingRename; + } + } else if (arg == "OPTIONAL") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingNone; + this->Optional = true; + } + } else if (arg == "MESSAGE_ALWAYS") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingNone; + this->MessageAlways = true; + } + } else if (arg == "MESSAGE_LAZY") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingNone; + this->MessageLazy = true; + } + } else if (arg == "MESSAGE_NEVER") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + this->Doing = DoingNone; + this->MessageNever = true; + } + } else if (arg == "PERMISSIONS") { + if (this->CurrentMatchRule) { + this->Doing = DoingPermissionsMatch; + } else { + // file(INSTALL) aliases PERMISSIONS to FILE_PERMISSIONS + this->Doing = DoingPermissionsFile; + this->UseGivenPermissionsFile = true; + } + } else if (arg == "DIR_PERMISSIONS") { + if (this->CurrentMatchRule) { + this->NotAfterMatch(arg); + } else { + // file(INSTALL) aliases DIR_PERMISSIONS to DIRECTORY_PERMISSIONS + this->Doing = DoingPermissionsDir; + this->UseGivenPermissionsDir = true; + } + } else if (arg == "COMPONENTS" || arg == "CONFIGURATIONS" || + arg == "PROPERTIES") { + std::ostringstream e; + e << "INSTALL called with old-style " << arg << " argument. " + << "This script was generated with an older version of CMake. " + << "Re-run this cmake version on your build tree."; + this->FileCommand->SetError(e.str()); + this->Doing = DoingError; + } else { + return this->cmFileCopier::CheckKeyword(arg); + } + return true; +} + +bool cmFileInstaller::CheckValue(std::string const& arg) +{ + switch (this->Doing) { + case DoingType: + if (!this->GetTargetTypeFromString(arg)) { + this->Doing = DoingError; + } + break; + case DoingRename: + this->Rename = arg; + break; + default: + return this->cmFileCopier::CheckValue(arg); + } + return true; +} + +bool cmFileInstaller::GetTargetTypeFromString(const std::string& stype) +{ + if (stype == "EXECUTABLE") { + this->InstallType = cmInstallType_EXECUTABLE; + } else if (stype == "FILE") { + this->InstallType = cmInstallType_FILES; + } else if (stype == "PROGRAM") { + this->InstallType = cmInstallType_PROGRAMS; + } else if (stype == "STATIC_LIBRARY") { + this->InstallType = cmInstallType_STATIC_LIBRARY; + } else if (stype == "SHARED_LIBRARY") { + this->InstallType = cmInstallType_SHARED_LIBRARY; + } else if (stype == "MODULE") { + this->InstallType = cmInstallType_MODULE_LIBRARY; + } else if (stype == "DIRECTORY") { + this->InstallType = cmInstallType_DIRECTORY; + } else { + std::ostringstream e; + e << "Option TYPE given unknown value \"" << stype << "\"."; + this->FileCommand->SetError(e.str()); + return false; + } + return true; +} + +bool cmFileInstaller::HandleInstallDestination() +{ + std::string& destination = this->Destination; + + // allow for / to be a valid destination + if (destination.size() < 2 && destination != "/") { + this->FileCommand->SetError("called with inappropriate arguments. " + "No DESTINATION provided or ."); + return false; + } + + std::string sdestdir; + if (cmSystemTools::GetEnv("DESTDIR", sdestdir) && !sdestdir.empty()) { + cmSystemTools::ConvertToUnixSlashes(sdestdir); + char ch1 = destination[0]; + char ch2 = destination[1]; + char ch3 = 0; + if (destination.size() > 2) { + ch3 = destination[2]; + } + int skip = 0; + if (ch1 != '/') { + int relative = 0; + if (((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')) && + ch2 == ':') { + // Assume windows + // let's do some destdir magic: + skip = 2; + if (ch3 != '/') { + relative = 1; + } + } else { + relative = 1; + } + if (relative) { + // This is relative path on unix or windows. Since we are doing + // destdir, this case does not make sense. + this->FileCommand->SetError( + "called with relative DESTINATION. This " + "does not make sense when using DESTDIR. Specify " + "absolute path or remove DESTDIR environment variable."); + return false; + } + } else { + if (ch2 == '/') { + // looks like a network path. + std::string message = + "called with network path DESTINATION. This " + "does not make sense when using DESTDIR. Specify local " + "absolute path or remove DESTDIR environment variable." + "\nDESTINATION=\n"; + message += destination; + this->FileCommand->SetError(message); + return false; + } + } + destination = sdestdir + destination.substr(skip); + this->DestDirLength = int(sdestdir.size()); + } + + // check if default dir creation permissions were set + mode_t default_dir_mode_v = 0; + mode_t* default_dir_mode = &default_dir_mode_v; + if (!this->GetDefaultDirectoryPermissions(&default_dir_mode)) { + return false; + } + + if (this->InstallType != cmInstallType_DIRECTORY) { + if (!cmSystemTools::FileExists(destination)) { + if (!cmSystemTools::MakeDirectory(destination, default_dir_mode)) { + std::string errstring = "cannot create directory: " + destination + + ". Maybe need administrative privileges."; + this->FileCommand->SetError(errstring); + return false; + } + } + if (!cmSystemTools::FileIsDirectory(destination)) { + std::string errstring = + "INSTALL destination: " + destination + " is not a directory."; + this->FileCommand->SetError(errstring); + return false; + } + } + return true; +} diff --git a/Source/cmFileInstaller.h b/Source/cmFileInstaller.h new file mode 100644 index 0000000..312529a --- /dev/null +++ b/Source/cmFileInstaller.h @@ -0,0 +1,55 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmFileInstaller_h +#define cmFileInstaller_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmFileCopier.h" + +#include "cmInstallType.h" + +#include <string> +#include <vector> + +class cmFileCommand; + +struct cmFileInstaller : public cmFileCopier +{ + cmFileInstaller(cmFileCommand* command); + ~cmFileInstaller() override; + +protected: + cmInstallType InstallType; + bool Optional; + bool MessageAlways; + bool MessageLazy; + bool MessageNever; + int DestDirLength; + std::string Rename; + + std::string Manifest; + void ManifestAppend(std::string const& file); + + std::string const& ToName(std::string const& fromName) override; + + void ReportCopy(const std::string& toFile, Type type, bool copy) override; + bool ReportMissing(const std::string& fromFile) override; + bool Install(const std::string& fromFile, + const std::string& toFile) override; + + bool Parse(std::vector<std::string> const& args) override; + enum + { + DoingType = DoingLast1, + DoingRename, + DoingLast2 + }; + bool CheckKeyword(std::string const& arg) override; + bool CheckValue(std::string const& arg) override; + void DefaultFilePermissions() override; + bool GetTargetTypeFromString(const std::string& stype); + bool HandleInstallDestination(); +}; + +#endif diff --git a/Source/cmFilePathChecksum.cxx b/Source/cmFilePathChecksum.cxx index 2cffa7c..47a223a 100644 --- a/Source/cmFilePathChecksum.cxx +++ b/Source/cmFilePathChecksum.cxx @@ -74,7 +74,7 @@ std::string cmFilePathChecksum::get(std::string const& filePath) const cmCryptoHash(cmCryptoHash::AlgoSHA256).ByteHashString(relSeed + relPath); // Convert binary checksum to string - return cmBase32Encoder().encodeString(&hashBytes.front(), hashBytes.size(), + return cmBase32Encoder().encodeString(hashBytes.data(), hashBytes.size(), false); } diff --git a/Source/cmFileTime.cxx b/Source/cmFileTime.cxx new file mode 100644 index 0000000..253457f --- /dev/null +++ b/Source/cmFileTime.cxx @@ -0,0 +1,49 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileTime.h" + +#include <string> +#include <time.h> + +// Use a platform-specific API to get file times efficiently. +#if !defined(_WIN32) || defined(__CYGWIN__) +# include "cm_sys_stat.h" +#else +# include "cmsys/Encoding.hxx" +# include <windows.h> +#endif + +bool cmFileTime::Load(std::string const& fileName) +{ +#if !defined(_WIN32) || defined(__CYGWIN__) + // POSIX version. Use the stat function. + struct stat fst; + if (::stat(fileName.c_str(), &fst) != 0) { + return false; + } +# if CMake_STAT_HAS_ST_MTIM + // Nanosecond resolution + this->NS = fst.st_mtim.tv_sec * NsPerS + fst.st_mtim.tv_nsec; +# elif CMake_STAT_HAS_ST_MTIMESPEC + // Nanosecond resolution + this->NS = fst.st_mtimespec.tv_sec * NsPerS + fst.st_mtimespec.tv_nsec; +# else + // Second resolution + this->NS = fst.st_mtime * NsPerS; +# endif +#else + // Windows version. Get the modification time from extended file attributes. + WIN32_FILE_ATTRIBUTE_DATA fdata; + if (!GetFileAttributesExW(cmsys::Encoding::ToWide(fileName).c_str(), + GetFileExInfoStandard, &fdata)) { + return false; + } + + // Copy the file time to the output location. + this->NS = (static_cast<NSC>(fdata.ftLastWriteTime.dwHighDateTime) << 32) | + static_cast<NSC>(fdata.ftLastWriteTime.dwLowDateTime); + // The file time resolution is 100 ns. + this->NS *= 100; +#endif + return true; +} diff --git a/Source/cmFileTime.h b/Source/cmFileTime.h new file mode 100644 index 0000000..d4de4e0 --- /dev/null +++ b/Source/cmFileTime.h @@ -0,0 +1,130 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmFileTime_h +#define cmFileTime_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> + +/** \class cmFileTime + * \brief Abstract file modification time with support for comparison with + * other file modification times. + */ +class cmFileTime +{ +public: + typedef long long NSC; + static constexpr NSC NsPerS = 1000000000; + + cmFileTime() = default; + ~cmFileTime() = default; + + /** + * @brief Loads the file time of fileName from the file system + * @return true on success + */ + bool Load(std::string const& fileName); + + /** + * @brief Return true if this is older than ftm + */ + bool Older(cmFileTime const& ftm) const { return (this->NS - ftm.NS) < 0; } + + /** + * @brief Return true if this is newer than ftm + */ + bool Newer(cmFileTime const& ftm) const { return (ftm.NS - this->NS) < 0; } + + /** + * @brief Return true if this is the same as ftm + */ + bool Equal(cmFileTime const& ftm) const { return this->NS == ftm.NS; } + + /** + * @brief Return true if this is not the same as ftm + */ + bool Differ(cmFileTime const& ftm) const { return this->NS != ftm.NS; } + + /** + * @brief Compare file modification times. + * @return -1, 0, +1 for this older, same, or newer than ftm. + */ + int Compare(cmFileTime const& ftm) const + { + NSC const diff = this->NS - ftm.NS; + if (diff == 0) { + return 0; + } + return (diff < 0) ? -1 : 1; + } + + // -- Comparison in second resolution + + /** + * @brief Return true if this is at least a second older than ftm + */ + bool OlderS(cmFileTime const& ftm) const + { + return (ftm.NS - this->NS) >= cmFileTime::NsPerS; + } + + /** + * @brief Return true if this is at least a second newer than ftm + */ + bool NewerS(cmFileTime const& ftm) const + { + return (this->NS - ftm.NS) >= cmFileTime::NsPerS; + } + + /** + * @brief Return true if this is within the same second as ftm + */ + bool EqualS(cmFileTime const& ftm) const + { + NSC diff = this->NS - ftm.NS; + if (diff < 0) { + diff = -diff; + } + return (diff < cmFileTime::NsPerS); + } + + /** + * @brief Return true if this is older or newer than ftm by at least a second + */ + bool DifferS(cmFileTime const& ftm) const + { + NSC diff = this->NS - ftm.NS; + if (diff < 0) { + diff = -diff; + } + return (diff >= cmFileTime::NsPerS); + } + + /** + * @brief Compare file modification times. + * @return -1: this at least a second older, 0: this within the same second + * as ftm, +1: this at least a second newer than ftm. + */ + int CompareS(cmFileTime const& ftm) const + { + NSC const diff = this->NS - ftm.NS; + if (diff <= -cmFileTime::NsPerS) { + return -1; + } + if (diff >= cmFileTime::NsPerS) { + return 1; + } + return 0; + } + + /** + * @brief The file modification time in nanoseconds + */ + NSC GetNS() const { return this->NS; } + +private: + NSC NS = 0; +}; + +#endif diff --git a/Source/cmFileTimeCache.cxx b/Source/cmFileTimeCache.cxx new file mode 100644 index 0000000..24d6bf6 --- /dev/null +++ b/Source/cmFileTimeCache.cxx @@ -0,0 +1,62 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileTimeCache.h" + +#include <string> +#include <unordered_map> +#include <utility> + +cmFileTimeCache::cmFileTimeCache() = default; + +cmFileTimeCache::~cmFileTimeCache() = default; + +bool cmFileTimeCache::Load(std::string const& fileName, cmFileTime& fileTime) +{ + // Use the stored time if available. + { + auto fit = this->Cache.find(fileName); + if (fit != this->Cache.end()) { + fileTime = fit->second; + return true; + } + } + // Read file time from OS + if (!fileTime.Load(fileName)) { + return false; + } + // Store file time in cache + this->Cache[fileName] = fileTime; + return true; +} + +bool cmFileTimeCache::Remove(std::string const& fileName) +{ + return (this->Cache.erase(fileName) != 0); +} + +bool cmFileTimeCache::Compare(std::string const& f1, std::string const& f2, + int* result) +{ + // Get the modification time for each file. + cmFileTime ft1, ft2; + if (this->Load(f1, ft1) && this->Load(f2, ft2)) { + // Compare the two modification times. + *result = ft1.Compare(ft2); + return true; + } + // No comparison available. Default to the same time. + *result = 0; + return false; +} + +bool cmFileTimeCache::DifferS(std::string const& f1, std::string const& f2) +{ + // Get the modification time for each file. + cmFileTime ft1, ft2; + if (this->Load(f1, ft1) && this->Load(f2, ft2)) { + // Compare the two modification times. + return ft1.DifferS(ft2); + } + // No comparison available. Default to different times. + return true; +} diff --git a/Source/cmFileTimeCache.h b/Source/cmFileTimeCache.h new file mode 100644 index 0000000..4f1a3a2 --- /dev/null +++ b/Source/cmFileTimeCache.h @@ -0,0 +1,56 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmFileTimeCache_h +#define cmFileTimeCache_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmFileTime.h" // IWYU pragma: keep +#include <string> +#include <unordered_map> + +/** \class cmFileTimeCache + * \brief Caches file modification times in an internal map for fast lookups. + */ +class cmFileTimeCache +{ +public: + cmFileTimeCache(); + ~cmFileTimeCache(); + + cmFileTimeCache(const cmFileTimeCache&) = delete; + cmFileTimeCache& operator=(const cmFileTimeCache&) = delete; + + /** + * @brief Loads the file time from the cache or the file system. + * @return true on success + */ + bool Load(std::string const& fileName, cmFileTime& fileTime); + + /** + * @brief Removes a file time from the cache + * @return true if the file was found in the cache and removed + */ + bool Remove(std::string const& fileName); + + /** + * @brief Compare file modification times. + * @return true for successful comparison and false for error. + * + * When true is returned, result has -1, 0, +1 for + * f1 older, same, or newer than f2. + */ + bool Compare(std::string const& f1, std::string const& f2, int* result); + + /** + * @brief Compare file modification times. + * @return true unless both files exist and have modification times less + * than 1 second apart. + */ + bool DifferS(std::string const& f1, std::string const& f2); + +private: + std::unordered_map<std::string, cmFileTime> Cache; +}; + +#endif diff --git a/Source/cmFileTimeComparison.cxx b/Source/cmFileTimeComparison.cxx deleted file mode 100644 index 8b3911e..0000000 --- a/Source/cmFileTimeComparison.cxx +++ /dev/null @@ -1,233 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmFileTimeComparison.h" - -#include <string> -#include <time.h> -#include <unordered_map> -#include <utility> - -// Use a platform-specific API to get file times efficiently. -#if !defined(_WIN32) || defined(__CYGWIN__) -# include "cm_sys_stat.h" -# define cmFileTimeComparison_Type struct stat -#else -# include "cmsys/Encoding.hxx" -# include <windows.h> -# define cmFileTimeComparison_Type FILETIME -#endif - -class cmFileTimeComparisonInternal -{ -public: - // Internal comparison method. - inline bool FileTimeCompare(const std::string& f1, const std::string& f2, - int* result); - - bool FileTimesDiffer(const std::string& f1, const std::string& f2); - -private: - typedef std::unordered_map<std::string, cmFileTimeComparison_Type> - FileStatsMap; - FileStatsMap Files; - - // Internal methods to lookup and compare modification times. - inline bool Stat(const std::string& fname, cmFileTimeComparison_Type* st); - inline int Compare(cmFileTimeComparison_Type* st1, - cmFileTimeComparison_Type* st2); - inline bool TimesDiffer(cmFileTimeComparison_Type* st1, - cmFileTimeComparison_Type* st2); -}; - -bool cmFileTimeComparisonInternal::Stat(const std::string& fname, - cmFileTimeComparison_Type* st) -{ - // Use the stored time if available. - cmFileTimeComparisonInternal::FileStatsMap::iterator fit = - this->Files.find(fname); - if (fit != this->Files.end()) { - *st = fit->second; - return true; - } - -#if !defined(_WIN32) || defined(__CYGWIN__) - // POSIX version. Use the stat function. - int res = ::stat(fname.c_str(), st); - if (res != 0) { - return false; - } -#else - // Windows version. Get the modification time from extended file - // attributes. - WIN32_FILE_ATTRIBUTE_DATA fdata; - if (!GetFileAttributesExW(cmsys::Encoding::ToWide(fname).c_str(), - GetFileExInfoStandard, &fdata)) { - return false; - } - - // Copy the file time to the output location. - *st = fdata.ftLastWriteTime; -#endif - - // Store the time for future use. - this->Files[fname] = *st; - return true; -} - -cmFileTimeComparison::cmFileTimeComparison() -{ - this->Internals = new cmFileTimeComparisonInternal; -} - -cmFileTimeComparison::~cmFileTimeComparison() -{ - delete this->Internals; -} - -bool cmFileTimeComparison::FileTimeCompare(const std::string& f1, - const std::string& f2, int* result) -{ - return this->Internals->FileTimeCompare(f1, f2, result); -} - -bool cmFileTimeComparison::FileTimesDiffer(const std::string& f1, - const std::string& f2) -{ - return this->Internals->FileTimesDiffer(f1, f2); -} - -int cmFileTimeComparisonInternal::Compare(cmFileTimeComparison_Type* s1, - cmFileTimeComparison_Type* s2) -{ -#if !defined(_WIN32) || defined(__CYGWIN__) -# if CMake_STAT_HAS_ST_MTIM - // Compare using nanosecond resolution. - if (s1->st_mtim.tv_sec < s2->st_mtim.tv_sec) { - return -1; - } - if (s1->st_mtim.tv_sec > s2->st_mtim.tv_sec) { - return 1; - } - if (s1->st_mtim.tv_nsec < s2->st_mtim.tv_nsec) { - return -1; - } - if (s1->st_mtim.tv_nsec > s2->st_mtim.tv_nsec) { - return 1; - } -# elif CMake_STAT_HAS_ST_MTIMESPEC - // Compare using nanosecond resolution. - if (s1->st_mtimespec.tv_sec < s2->st_mtimespec.tv_sec) { - return -1; - } - if (s1->st_mtimespec.tv_sec > s2->st_mtimespec.tv_sec) { - return 1; - } - if (s1->st_mtimespec.tv_nsec < s2->st_mtimespec.tv_nsec) { - return -1; - } - if (s1->st_mtimespec.tv_nsec > s2->st_mtimespec.tv_nsec) { - return 1; - } -# else - // Compare using 1 second resolution. - if (s1->st_mtime < s2->st_mtime) { - return -1; - } - if (s1->st_mtime > s2->st_mtime) { - return 1; - } -# endif - // Files have the same time. - return 0; -#else - // Compare using system-provided function. - return (int)CompareFileTime(s1, s2); -#endif -} - -bool cmFileTimeComparisonInternal::TimesDiffer(cmFileTimeComparison_Type* s1, - cmFileTimeComparison_Type* s2) -{ -#if !defined(_WIN32) || defined(__CYGWIN__) -# if CMake_STAT_HAS_ST_MTIM - // Times are integers in units of 1ns. - long long bil = 1000000000; - long long t1 = s1->st_mtim.tv_sec * bil + s1->st_mtim.tv_nsec; - long long t2 = s2->st_mtim.tv_sec * bil + s2->st_mtim.tv_nsec; - if (t1 < t2) { - return (t2 - t1) >= bil; - } - if (t2 < t1) { - return (t1 - t2) >= bil; - } - return false; -# elif CMake_STAT_HAS_ST_MTIMESPEC - // Times are integers in units of 1ns. - long long bil = 1000000000; - long long t1 = s1->st_mtimespec.tv_sec * bil + s1->st_mtimespec.tv_nsec; - long long t2 = s2->st_mtimespec.tv_sec * bil + s2->st_mtimespec.tv_nsec; - if (t1 < t2) { - return (t2 - t1) >= bil; - } - if (t2 < t1) { - return (t1 - t2) >= bil; - } - return false; -# else - // Times are integers in units of 1s. - if (s1->st_mtime < s2->st_mtime) { - return (s2->st_mtime - s1->st_mtime) >= 1; - } - if (s1->st_mtime > s2->st_mtime) { - return (s1->st_mtime - s2->st_mtime) >= 1; - } - return false; -# endif -#else - // Times are integers in units of 100ns. - LARGE_INTEGER t1; - LARGE_INTEGER t2; - t1.LowPart = s1->dwLowDateTime; - t1.HighPart = s1->dwHighDateTime; - t2.LowPart = s2->dwLowDateTime; - t2.HighPart = s2->dwHighDateTime; - if (t1.QuadPart < t2.QuadPart) { - return (t2.QuadPart - t1.QuadPart) >= static_cast<LONGLONG>(10000000); - } else if (t2.QuadPart < t1.QuadPart) { - return (t1.QuadPart - t2.QuadPart) >= static_cast<LONGLONG>(10000000); - } else { - return false; - } -#endif -} - -bool cmFileTimeComparisonInternal::FileTimeCompare(const std::string& f1, - const std::string& f2, - int* result) -{ - // Get the modification time for each file. - cmFileTimeComparison_Type s1; - cmFileTimeComparison_Type s2; - if (this->Stat(f1, &s1) && this->Stat(f2, &s2)) { - // Compare the two modification times. - *result = this->Compare(&s1, &s2); - return true; - } - // No comparison available. Default to the same time. - *result = 0; - return false; -} - -bool cmFileTimeComparisonInternal::FileTimesDiffer(const std::string& f1, - const std::string& f2) -{ - // Get the modification time for each file. - cmFileTimeComparison_Type s1; - cmFileTimeComparison_Type s2; - if (this->Stat(f1, &s1) && this->Stat(f2, &s2)) { - // Compare the two modification times. - return this->TimesDiffer(&s1, &s2); - } - // No comparison available. Default to different times. - return true; -} diff --git a/Source/cmFileTimeComparison.h b/Source/cmFileTimeComparison.h deleted file mode 100644 index 5f74e34..0000000 --- a/Source/cmFileTimeComparison.h +++ /dev/null @@ -1,42 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileTimeComparison_h -#define cmFileTimeComparison_h - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <string> - -class cmFileTimeComparisonInternal; - -/** \class cmFileTimeComparison - * \brief Helper class for comparing file modification times. - * - * Compare file modification times or test if file modification times differ. - */ -class cmFileTimeComparison -{ -public: - cmFileTimeComparison(); - ~cmFileTimeComparison(); - - /** - * Compare file modification times. - * Return true for successful comparison and false for error. - * When true is returned, result has -1, 0, +1 for - * f1 older, same, or newer than f2. - */ - bool FileTimeCompare(const std::string& f1, const std::string& f2, - int* result); - - /** - * Compare file modification times. Return true unless both files - * exist and have modification times less than 1 second apart. - */ - bool FileTimesDiffer(const std::string& f1, const std::string& f2); - -protected: - cmFileTimeComparisonInternal* Internals; -}; - -#endif diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 425546a..2e5e29c 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -4,12 +4,12 @@ #include <deque> #include <iostream> -#include <iterator> #include <map> #include <stddef.h> #include "cmAlgorithms.h" #include "cmMakefile.h" +#include "cmRange.h" #include "cmSearchPath.h" #include "cmState.h" #include "cmStateTypes.h" @@ -205,11 +205,9 @@ void cmFindBase::FillPackageRootPath() cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRoot]; // Add the PACKAGE_ROOT_PATH from each enclosing find_package call. - for (std::deque<std::vector<std::string>>::const_reverse_iterator pkgPaths = - this->Makefile->FindPackageRootPathStack.rbegin(); - pkgPaths != this->Makefile->FindPackageRootPathStack.rend(); - ++pkgPaths) { - paths.AddPrefixPaths(*pkgPaths); + for (std::vector<std::string> const& pkgPaths : + cmReverseRange(this->Makefile->FindPackageRootPathStack)) { + paths.AddPrefixPaths(pkgPaths); } paths.AddSuffixes(this->SearchPathSuffixes); diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx index 78be64e..9aaa000 100644 --- a/Source/cmFindCommon.cxx +++ b/Source/cmFindCommon.cxx @@ -310,7 +310,7 @@ void cmFindCommon::AddPathSuffix(std::string const& arg) void AddTrailingSlash(std::string& s) { - if (!s.empty() && *s.rbegin() != '/') { + if (!s.empty() && s.back() != '/') { s += '/'; } } diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index c2e0712..7ebd211 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -23,6 +23,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmSearchPath.h" #include "cmState.h" #include "cmStateTypes.h" @@ -466,7 +467,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args, // Allocate a PACKAGE_ROOT_PATH for the current find_package call. this->Makefile->FindPackageRootPathStack.emplace_back(); std::vector<std::string>& rootPaths = - *this->Makefile->FindPackageRootPathStack.rbegin(); + this->Makefile->FindPackageRootPathStack.back(); // Add root paths from <PackageName>_ROOT CMake and environment variables, // subject to CMP0074. @@ -827,10 +828,10 @@ bool cmFindPackageCommand::HandlePackageMode() << " requested version \"" << this->Version << "\".\n" << "The following configuration files were considered but not " "accepted:\n"; - for (std::vector<ConfigFileInfo>::const_iterator i = - this->ConsideredConfigs.begin(); - i != duplicate_end; ++i) { - e << " " << i->filename << ", version: " << i->version << "\n"; + + for (ConfigFileInfo const& info : + cmMakeRange(this->ConsideredConfigs.cbegin(), duplicate_end)) { + e << " " << info.filename << ", version: " << info.version << "\n"; } } else { std::string requestedVersionString; @@ -911,7 +912,7 @@ bool cmFindPackageCommand::HandlePackageMode() std::ostringstream aw; aw << "Could NOT find " << this->Name << " (missing: " << this->Name << "_DIR)"; - this->Makefile->DisplayStatus(aw.str().c_str(), -1); + this->Makefile->DisplayStatus(aw.str(), -1); } } @@ -1372,6 +1373,9 @@ public: cmSystemTools::RemoveFile(this->File); } } + cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete; + cmFindPackageCommandHoldFile& operator=( + const cmFindPackageCommandHoldFile&) = delete; void Release() { this->File = nullptr; } }; diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx index e359def..08003eb 100644 --- a/Source/cmForEachCommand.cxx +++ b/Source/cmForEachCommand.cxx @@ -11,6 +11,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" #include "cmSystemTools.h" cmForEachFunctionBlocker::cmForEachFunctionBlocker(cmMakefile* mf) @@ -48,12 +49,10 @@ bool cmForEachFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, if (mf.GetDefinition(this->Args[0])) { oldDef = mf.GetDefinition(this->Args[0]); } - std::vector<std::string>::const_iterator j = this->Args.begin(); - ++j; - for (; j != this->Args.end(); ++j) { + for (std::string const& arg : cmMakeRange(this->Args).advance(1)) { // set the variable to the loop value - mf.AddDefinition(this->Args[0], j->c_str()); + mf.AddDefinition(this->Args[0], arg.c_str()); // Invoke all the functions that were collected in the block. cmExecutionStatus status; for (cmListFileFunction const& func : this->Functions) { diff --git a/Source/cmFortranParser.h b/Source/cmFortranParser.h index 0762340..6a33be5 100644 --- a/Source/cmFortranParser.h +++ b/Source/cmFortranParser.h @@ -141,6 +141,9 @@ struct cmFortranParser_s std::set<std::string> defines, cmFortranSourceInfo& info); ~cmFortranParser_s(); + cmFortranParser_s(const cmFortranParser_s&) = delete; + cmFortranParser_s& operator=(const cmFortranParser_s&) = delete; + bool FindIncludeFile(const char* dir, const char* includeName, std::string& fileName); diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx index 264a338..9d75b72 100644 --- a/Source/cmFunctionCommand.cxx +++ b/Source/cmFunctionCommand.cxx @@ -8,6 +8,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmState.h" // define the class for function commands diff --git a/Source/cmGeneratedFileStream.cxx b/Source/cmGeneratedFileStream.cxx index 4fe1587..2f47788 100644 --- a/Source/cmGeneratedFileStream.cxx +++ b/Source/cmGeneratedFileStream.cxx @@ -29,8 +29,7 @@ cmGeneratedFileStream::cmGeneratedFileStream(std::string const& name, { // Check if the file opened. if (!*this && !quiet) { - cmSystemTools::Error("Cannot open file for write: ", - this->TempName.c_str()); + cmSystemTools::Error("Cannot open file for write: " + this->TempName); cmSystemTools::ReportLastSystemError(""); } #ifdef CMAKE_BUILD_WITH_CMAKE @@ -68,8 +67,7 @@ cmGeneratedFileStream& cmGeneratedFileStream::Open(std::string const& name, // Check if the file opened. if (!*this && !quiet) { - cmSystemTools::Error("Cannot open file for write: ", - this->TempName.c_str()); + cmSystemTools::Error("Cannot open file for write: " + this->TempName); cmSystemTools::ReportLastSystemError(""); } return *this; diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx index f9a6d64..268de6f 100644 --- a/Source/cmGeneratorExpressionEvaluator.cxx +++ b/Source/cmGeneratorExpressionEvaluator.cxx @@ -34,20 +34,16 @@ std::string GeneratorExpressionContent::ProcessArbitraryContent( std::vector<cmGeneratorExpressionEvaluator*>>::const_iterator pend = this->ParamChildren.end(); for (; pit != pend; ++pit) { - std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it = - pit->begin(); - const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end = - pit->end(); - for (; it != end; ++it) { + for (cmGeneratorExpressionEvaluator* pExprEval : *pit) { if (node->RequiresLiteralInput()) { - if ((*it)->GetType() != cmGeneratorExpressionEvaluator::Text) { + if (pExprEval->GetType() != cmGeneratorExpressionEvaluator::Text) { reportError(context, this->GetOriginalExpression(), "$<" + identifier + "> expression requires literal input."); return std::string(); } } - result += (*it)->Evaluate(context, dagChecker); + result += pExprEval->Evaluate(context, dagChecker); if (context->HadError) { return std::string(); } @@ -70,12 +66,9 @@ std::string GeneratorExpressionContent::Evaluate( { std::string identifier; { - std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it = - this->IdentifierChildren.begin(); - const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end = - this->IdentifierChildren.end(); - for (; it != end; ++it) { - identifier += (*it)->Evaluate(context, dagChecker); + for (cmGeneratorExpressionEvaluator* pExprEval : + this->IdentifierChildren) { + identifier += pExprEval->Evaluate(context, dagChecker); if (context->HadError) { return std::string(); } @@ -138,12 +131,8 @@ std::string GeneratorExpressionContent::EvaluateParameters( return std::string(); } std::string parameter; - std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it = - pit->begin(); - const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end = - pit->end(); - for (; it != end; ++it) { - parameter += (*it)->Evaluate(context, dagChecker); + for (cmGeneratorExpressionEvaluator* pExprEval : *pit) { + parameter += pExprEval->Evaluate(context, dagChecker); if (context->HadError) { return std::string(); } diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 70c80c9..8c6fb34 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -15,16 +15,23 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" +#include "cmRange.h" +#include "cmState.h" +#include "cmStateSnapshot.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cm_static_string_view.hxx" +#include "cm_string_view.hxx" #include "cmake.h" #include "cmsys/RegularExpression.hxx" #include "cmsys/String.h" + #include <algorithm> #include <assert.h> #include <errno.h> +#include <iterator> #include <map> #include <memory> // IWYU pragma: keep #include <set> @@ -92,38 +99,42 @@ static const struct OneNode buildInterfaceNode; static const struct ZeroNode installInterfaceNode; -#define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \ - static const struct OP##Node : public cmGeneratorExpressionNode \ - { \ - OP##Node() {} /* NOLINT(modernize-use-equals-default) */ \ - virtual int NumExpectedParameters() const { return OneOrMoreParameters; } \ - \ - std::string Evaluate(const std::vector<std::string>& parameters, \ - cmGeneratorExpressionContext* context, \ - const GeneratorExpressionContent* content, \ - cmGeneratorExpressionDAGChecker*) const \ - { \ - std::vector<std::string>::const_iterator it = parameters.begin(); \ - const std::vector<std::string>::const_iterator end = parameters.end(); \ - for (; it != end; ++it) { \ - if (*it == #FAILURE_VALUE) { \ - return #FAILURE_VALUE; \ - } \ - if (*it != #SUCCESS_VALUE) { \ - reportError(context, content->GetOriginalExpression(), \ - "Parameters to $<" #OP \ - "> must resolve to either '0' or '1'."); \ - return std::string(); \ - } \ - } \ - return #SUCCESS_VALUE; \ - } \ - } OPNAME; - -BOOLEAN_OP_NODE(andNode, AND, 1, 0) -BOOLEAN_OP_NODE(orNode, OR, 0, 1) - -#undef BOOLEAN_OP_NODE +struct BooleanOpNode : public cmGeneratorExpressionNode +{ + BooleanOpNode(const char* op_, const char* successVal_, + const char* failureVal_) + : op(op_) + , successVal(successVal_) + , failureVal(failureVal_) + { + } + + int NumExpectedParameters() const override { return OneOrMoreParameters; } + + std::string Evaluate(const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker*) const override + { + for (std::string const& param : parameters) { + if (param == this->failureVal) { + return this->failureVal; + } + if (param != this->successVal) { + std::ostringstream e; + e << "Parameters to $<" << this->op; + e << "> must resolve to either '0' or '1'."; + reportError(context, content->GetOriginalExpression(), e.str()); + return std::string(); + } + } + return this->successVal; + } + + const char *const op, *const successVal, *const failureVal; +}; + +static const BooleanOpNode andNode("AND", "1", "0"), orNode("OR", "0", "1"); static const struct NotNode : public cmGeneratorExpressionNode { @@ -135,13 +146,13 @@ static const struct NotNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - if (*parameters.begin() != "0" && *parameters.begin() != "1") { + if (parameters.front() != "0" && parameters.front() != "1") { reportError( context, content->GetOriginalExpression(), "$<NOT> parameter must resolve to exactly one '0' or '1' value."); return std::string(); } - return *parameters.begin() == "0" ? "1" : "0"; + return parameters.front() == "0" ? "1" : "0"; } } notNode; @@ -157,7 +168,7 @@ static const struct BoolNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - return !cmSystemTools::IsOff(*parameters.begin()) ? "1" : "0"; + return !cmSystemTools::IsOff(parameters.front()) ? "1" : "0"; } } boolNode; @@ -194,7 +205,7 @@ static const struct StrEqualNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - return *parameters.begin() == parameters[1] ? "1" : "0"; + return parameters.front() == parameters[1] ? "1" : "0"; } } strEqualNode; @@ -210,69 +221,44 @@ static const struct EqualNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - char* pEnd; - - int base = 0; - bool flipSign = false; - - const char* lhs = parameters[0].c_str(); - if (cmHasLiteralPrefix(lhs, "0b") || cmHasLiteralPrefix(lhs, "0B")) { - base = 2; - lhs += 2; - } - if (cmHasLiteralPrefix(lhs, "-0b") || cmHasLiteralPrefix(lhs, "-0B")) { - base = 2; - lhs += 3; - flipSign = true; - } - if (cmHasLiteralPrefix(lhs, "+0b") || cmHasLiteralPrefix(lhs, "+0B")) { - base = 2; - lhs += 3; - } - - long lnum = strtol(lhs, &pEnd, base); - if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) { - reportError(context, content->GetOriginalExpression(), - "$<EQUAL> parameter " + parameters[0] + - " is not a valid integer."); - return std::string(); - } - - if (flipSign) { - lnum = -lnum; + long numbers[2]; + for (int i = 0; i < 2; ++i) { + if (!ParameterToLong(parameters[i].c_str(), &numbers[i])) { + reportError(context, content->GetOriginalExpression(), + "$<EQUAL> parameter " + parameters[i] + + " is not a valid integer."); + return {}; + } } + return numbers[0] == numbers[1] ? "1" : "0"; + } - base = 0; - flipSign = false; + static bool ParameterToLong(const char* param, long* outResult) + { + const char isNegative = param[0] == '-'; - const char* rhs = parameters[1].c_str(); - if (cmHasLiteralPrefix(rhs, "0b") || cmHasLiteralPrefix(rhs, "0B")) { - base = 2; - rhs += 2; - } - if (cmHasLiteralPrefix(rhs, "-0b") || cmHasLiteralPrefix(rhs, "-0B")) { + int base = 0; + if (cmHasLiteralPrefix(param, "0b") || cmHasLiteralPrefix(param, "0B")) { base = 2; - rhs += 3; - flipSign = true; - } - if (cmHasLiteralPrefix(rhs, "+0b") || cmHasLiteralPrefix(rhs, "+0B")) { + param += 2; + } else if (cmHasLiteralPrefix(param, "-0b") || + cmHasLiteralPrefix(param, "-0B") || + cmHasLiteralPrefix(param, "+0b") || + cmHasLiteralPrefix(param, "+0B")) { base = 2; - rhs += 3; + param += 3; } - long rnum = strtol(rhs, &pEnd, base); - if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) { - reportError(context, content->GetOriginalExpression(), - "$<EQUAL> parameter " + parameters[1] + - " is not a valid integer."); - return std::string(); + char* pEnd; + long result = strtol(param, &pEnd, base); + if (pEnd == param || *pEnd != '\0' || errno == ERANGE) { + return false; } - - if (flipSign) { - rnum = -rnum; + if (isNegative && result > 0) { + result *= -1; } - - return lnum == rnum ? "1" : "0"; + *outResult = result; + return true; } } equalNode; @@ -326,6 +312,79 @@ static const struct InListNode : public cmGeneratorExpressionNode } } inListNode; +static const struct FilterNode : public cmGeneratorExpressionNode +{ + FilterNode() {} // NOLINT(modernize-use-equals-default) + + int NumExpectedParameters() const override { return 3; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + { + if (parameters.size() != 3) { + reportError(context, content->GetOriginalExpression(), + "$<FILTER:...> expression requires three parameters"); + return {}; + } + + if (parameters[1] != "INCLUDE" && parameters[1] != "EXCLUDE") { + reportError( + context, content->GetOriginalExpression(), + "$<FILTER:...> second parameter must be either INCLUDE or EXCLUDE"); + return {}; + } + + const bool exclude = parameters[1] == "EXCLUDE"; + + cmsys::RegularExpression re; + if (!re.compile(parameters[2])) { + reportError(context, content->GetOriginalExpression(), + "$<FILTER:...> failed to compile regex"); + return {}; + } + + std::vector<std::string> values, result; + cmSystemTools::ExpandListArgument(parameters.front(), values, true); + + std::copy_if(values.cbegin(), values.cend(), std::back_inserter(result), + [&re, exclude](std::string const& input) { + return exclude ^ re.find(input); + }); + return cmJoin(cmMakeRange(result.cbegin(), result.cend()), ";"); + } +} filterNode; + +static const struct RemoveDuplicatesNode : public cmGeneratorExpressionNode +{ + RemoveDuplicatesNode() {} // NOLINT(modernize-use-equals-default) + + int NumExpectedParameters() const override { return 1; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + { + if (parameters.size() != 1) { + reportError( + context, content->GetOriginalExpression(), + "$<REMOVE_DUPLICATES:...> expression requires one parameter"); + } + + std::vector<std::string> values; + cmSystemTools::ExpandListArgument(parameters.front(), values, true); + + auto valuesEnd = cmRemoveDuplicates(values); + auto valuesBegin = values.cbegin(); + return cmJoin(cmMakeRange(valuesBegin, valuesEnd), ";"); + } + +} removeDuplicatesNode; + static const struct TargetExistsNode : public cmGeneratorExpressionNode { TargetExistsNode() {} // NOLINT(modernize-use-equals-default) @@ -546,9 +605,10 @@ static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode } } makeCIdentifierNode; -static const struct Angle_RNode : public cmGeneratorExpressionNode +template <char C> +struct CharacterNode : public cmGeneratorExpressionNode { - Angle_RNode() {} // NOLINT(modernize-use-equals-default) + CharacterNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 0; } @@ -558,47 +618,39 @@ static const struct Angle_RNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - return ">"; + return { C }; } -} angle_rNode; +}; +static const CharacterNode<'>'> angle_rNode; +static const CharacterNode<','> commaNode; +static const CharacterNode<';'> semicolonNode; -static const struct CommaNode : public cmGeneratorExpressionNode +struct CompilerIdNode : public cmGeneratorExpressionNode { - CommaNode() {} // NOLINT(modernize-use-equals-default) - - int NumExpectedParameters() const override { return 0; } - - std::string Evaluate( - const std::vector<std::string>& /*parameters*/, - cmGeneratorExpressionContext* /*context*/, - const GeneratorExpressionContent* /*content*/, - cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + CompilerIdNode(const char* compilerLang) + : CompilerLanguage(compilerLang) { - return ","; } -} commaNode; - -static const struct SemicolonNode : public cmGeneratorExpressionNode -{ - SemicolonNode() {} // NOLINT(modernize-use-equals-default) - int NumExpectedParameters() const override { return 0; } + int NumExpectedParameters() const override { return OneOrZeroParameters; } std::string Evaluate( - const std::vector<std::string>& /*parameters*/, - cmGeneratorExpressionContext* /*context*/, - const GeneratorExpressionContent* /*content*/, - cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagChecker) const override { - return ";"; + if (!context->HeadTarget) { + std::ostringstream e; + e << "$<" << this->CompilerLanguage + << "_COMPILER_ID> may only be used with binary targets. It may " + "not be used with add_custom_command or add_custom_target."; + reportError(context, content->GetOriginalExpression(), e.str()); + return {}; + } + return this->EvaluateWithLanguage(parameters, context, content, dagChecker, + this->CompilerLanguage); } -} semicolonNode; - -struct CompilerIdNode : public cmGeneratorExpressionNode -{ - CompilerIdNode() {} // NOLINT(modernize-use-equals-default) - - int NumExpectedParameters() const override { return OneOrZeroParameters; } std::string EvaluateWithLanguage(const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, @@ -613,7 +665,7 @@ struct CompilerIdNode : public cmGeneratorExpressionNode return compilerId; } static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$"); - if (!compilerIdValidator.find(*parameters.begin())) { + if (!compilerIdValidator.find(parameters.front())) { reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); return std::string(); @@ -622,11 +674,11 @@ struct CompilerIdNode : public cmGeneratorExpressionNode return parameters.front().empty() ? "1" : "0"; } - if (strcmp(parameters.begin()->c_str(), compilerId.c_str()) == 0) { + if (strcmp(parameters.front().c_str(), compilerId.c_str()) == 0) { return "1"; } - if (cmsysString_strcasecmp(parameters.begin()->c_str(), + if (cmsysString_strcasecmp(parameters.front().c_str(), compilerId.c_str()) == 0) { switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) { case cmPolicies::WARN: { @@ -646,55 +698,21 @@ struct CompilerIdNode : public cmGeneratorExpressionNode } return "0"; } -}; -static const struct CCompilerIdNode : public CompilerIdNode -{ - CCompilerIdNode() {} // NOLINT(modernize-use-equals-default) + const char* const CompilerLanguage; +}; - std::string Evaluate( - const std::vector<std::string>& parameters, - cmGeneratorExpressionContext* context, - const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* dagChecker) const override - { - if (!context->HeadTarget) { - reportError( - context, content->GetOriginalExpression(), - "$<C_COMPILER_ID> may only be used with binary targets. It may " - "not be used with add_custom_command or add_custom_target."); - return std::string(); - } - return this->EvaluateWithLanguage(parameters, context, content, dagChecker, - "C"); - } -} cCompilerIdNode; +static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"), + cudaCompilerIdNode("CUDA"), fortranCompilerIdNode("Fortran"); -static const struct CXXCompilerIdNode : public CompilerIdNode +struct CompilerVersionNode : public cmGeneratorExpressionNode { - CXXCompilerIdNode() {} // NOLINT(modernize-use-equals-default) - - std::string Evaluate( - const std::vector<std::string>& parameters, - cmGeneratorExpressionContext* context, - const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* dagChecker) const override + CompilerVersionNode(const char* compilerLang) + : CompilerLanguage(compilerLang) { - if (!context->HeadTarget) { - reportError( - context, content->GetOriginalExpression(), - "$<CXX_COMPILER_ID> may only be used with binary targets. It may " - "not be used with add_custom_command or add_custom_target."); - return std::string(); - } - return this->EvaluateWithLanguage(parameters, context, content, dagChecker, - "CXX"); } -} cxxCompilerIdNode; -static const struct FortranCompilerIdNode : public CompilerIdNode -{ - FortranCompilerIdNode() {} // NOLINT(modernize-use-equals-default) + int NumExpectedParameters() const override { return OneOrZeroParameters; } std::string Evaluate( const std::vector<std::string>& parameters, @@ -703,22 +721,16 @@ static const struct FortranCompilerIdNode : public CompilerIdNode cmGeneratorExpressionDAGChecker* dagChecker) const override { if (!context->HeadTarget) { - reportError( - context, content->GetOriginalExpression(), - "$<Fortran_COMPILER_ID> may only be used with binary targets. It may " - "not be used with add_custom_command or add_custom_target."); - return std::string(); + std::ostringstream e; + e << "$<" << this->CompilerLanguage + << "_COMPILER_VERSION> may only be used with binary targets. It " + "may not be used with add_custom_command or add_custom_target."; + reportError(context, content->GetOriginalExpression(), e.str()); + return {}; } return this->EvaluateWithLanguage(parameters, context, content, dagChecker, - "Fortran"); + this->CompilerLanguage); } -} fortranCompilerIdNode; - -struct CompilerVersionNode : public cmGeneratorExpressionNode -{ - CompilerVersionNode() {} // NOLINT(modernize-use-equals-default) - - int NumExpectedParameters() const override { return OneOrZeroParameters; } std::string EvaluateWithLanguage(const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, @@ -734,88 +746,28 @@ struct CompilerVersionNode : public cmGeneratorExpressionNode } static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$"); - if (!compilerIdValidator.find(*parameters.begin())) { + if (!compilerIdValidator.find(parameters.front())) { reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); - return std::string(); + return {}; } if (compilerVersion.empty()) { return parameters.front().empty() ? "1" : "0"; } return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, - parameters.begin()->c_str(), + parameters.front().c_str(), compilerVersion.c_str()) ? "1" : "0"; } -}; - -static const struct CCompilerVersionNode : public CompilerVersionNode -{ - CCompilerVersionNode() {} // NOLINT(modernize-use-equals-default) - std::string Evaluate( - const std::vector<std::string>& parameters, - cmGeneratorExpressionContext* context, - const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* dagChecker) const override - { - if (!context->HeadTarget) { - reportError( - context, content->GetOriginalExpression(), - "$<C_COMPILER_VERSION> may only be used with binary targets. It " - "may not be used with add_custom_command or add_custom_target."); - return std::string(); - } - return this->EvaluateWithLanguage(parameters, context, content, dagChecker, - "C"); - } -} cCompilerVersionNode; - -static const struct CxxCompilerVersionNode : public CompilerVersionNode -{ - CxxCompilerVersionNode() {} // NOLINT(modernize-use-equals-default) - - std::string Evaluate( - const std::vector<std::string>& parameters, - cmGeneratorExpressionContext* context, - const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* dagChecker) const override - { - if (!context->HeadTarget) { - reportError( - context, content->GetOriginalExpression(), - "$<CXX_COMPILER_VERSION> may only be used with binary targets. It " - "may not be used with add_custom_command or add_custom_target."); - return std::string(); - } - return this->EvaluateWithLanguage(parameters, context, content, dagChecker, - "CXX"); - } -} cxxCompilerVersionNode; + const char* const CompilerLanguage; +}; -static const struct FortranCompilerVersionNode : public CompilerVersionNode -{ - FortranCompilerVersionNode() {} // NOLINT(modernize-use-equals-default) - - std::string Evaluate( - const std::vector<std::string>& parameters, - cmGeneratorExpressionContext* context, - const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* dagChecker) const override - { - if (!context->HeadTarget) { - reportError( - context, content->GetOriginalExpression(), - "$<Fortran_COMPILER_VERSION> may only be used with binary targets. " - "It may not be used with add_custom_command or add_custom_target."); - return std::string(); - } - return this->EvaluateWithLanguage(parameters, context, content, dagChecker, - "Fortran"); - } -} fortranCompilerVersionNode; +static const CompilerVersionNode cCompilerVersionNode("C"), + cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"), + fortranCompilerVersionNode("Fortran"); struct PlatformIdNode : public cmGeneratorExpressionNode { @@ -839,16 +791,17 @@ struct PlatformIdNode : public cmGeneratorExpressionNode return parameters.front().empty() ? "1" : "0"; } - if (*parameters.begin() == platformId) { + if (parameters.front() == platformId) { return "1"; } return "0"; } } platformIdNode; -static const struct VersionGreaterNode : public cmGeneratorExpressionNode +template <cmSystemTools::CompareOp Op> +struct VersionNode : public cmGeneratorExpressionNode { - VersionGreaterNode() {} // NOLINT(modernize-use-equals-default) + VersionNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 2; } @@ -858,93 +811,18 @@ static const struct VersionGreaterNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER, - parameters.front().c_str(), + return cmSystemTools::VersionCompare(Op, parameters.front().c_str(), parameters[1].c_str()) ? "1" : "0"; } -} versionGreaterNode; - -static const struct VersionGreaterEqNode : public cmGeneratorExpressionNode -{ - VersionGreaterEqNode() {} // NOLINT(modernize-use-equals-default) - - int NumExpectedParameters() const override { return 2; } - - std::string Evaluate( - const std::vector<std::string>& parameters, - cmGeneratorExpressionContext* /*context*/, - const GeneratorExpressionContent* /*content*/, - cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override - { - return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, - parameters.front().c_str(), - parameters[1].c_str()) - ? "1" - : "0"; - } -} versionGreaterEqNode; - -static const struct VersionLessNode : public cmGeneratorExpressionNode -{ - VersionLessNode() {} // NOLINT(modernize-use-equals-default) - - int NumExpectedParameters() const override { return 2; } - - std::string Evaluate( - const std::vector<std::string>& parameters, - cmGeneratorExpressionContext* /*context*/, - const GeneratorExpressionContent* /*content*/, - cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override - { - return cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, - parameters.front().c_str(), - parameters[1].c_str()) - ? "1" - : "0"; - } -} versionLessNode; - -static const struct VersionLessEqNode : public cmGeneratorExpressionNode -{ - VersionLessEqNode() {} // NOLINT(modernize-use-equals-default) - - int NumExpectedParameters() const override { return 2; } - - std::string Evaluate( - const std::vector<std::string>& parameters, - cmGeneratorExpressionContext* /*context*/, - const GeneratorExpressionContent* /*content*/, - cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override - { - return cmSystemTools::VersionCompare(cmSystemTools::OP_LESS_EQUAL, - parameters.front().c_str(), - parameters[1].c_str()) - ? "1" - : "0"; - } -} versionLessEqNode; - -static const struct VersionEqualNode : public cmGeneratorExpressionNode -{ - VersionEqualNode() {} // NOLINT(modernize-use-equals-default) +}; - int NumExpectedParameters() const override { return 2; } - - std::string Evaluate( - const std::vector<std::string>& parameters, - cmGeneratorExpressionContext* /*context*/, - const GeneratorExpressionContent* /*content*/, - cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override - { - return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, - parameters.front().c_str(), - parameters[1].c_str()) - ? "1" - : "0"; - } -} versionEqualNode; +static const VersionNode<cmSystemTools::OP_GREATER> versionGreaterNode; +static const VersionNode<cmSystemTools::OP_GREATER_EQUAL> versionGreaterEqNode; +static const VersionNode<cmSystemTools::OP_LESS> versionLessNode; +static const VersionNode<cmSystemTools::OP_LESS_EQUAL> versionLessEqNode; +static const VersionNode<cmSystemTools::OP_EQUAL> versionEqualNode; static const struct LinkOnlyNode : public cmGeneratorExpressionNode { @@ -1001,7 +879,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode return configurationNode.Evaluate(parameters, context, content, nullptr); } static cmsys::RegularExpression configValidator("^[A-Za-z0-9_]*$"); - if (!configValidator.find(*parameters.begin())) { + if (!configValidator.find(parameters.front())) { reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); return std::string(); @@ -1011,7 +889,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode return parameters.front().empty() ? "1" : "0"; } - if (cmsysString_strcasecmp(parameters.begin()->c_str(), + if (cmsysString_strcasecmp(parameters.front().c_str(), context->Config.c_str()) == 0) { return "1"; } @@ -1151,73 +1029,54 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode // This node handles errors on parameter count itself. int NumExpectedParameters() const override { return OneOrMoreParameters; } + static const char* GetErrorText(std::string const& targetName, + std::string const& propertyName) + { + static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$"); + if (targetName.empty() && propertyName.empty()) { + return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " + "target name and property name."; + } + if (targetName.empty()) { + return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " + "target name."; + } + if (!cmGeneratorExpression::IsValidTargetName(targetName)) { + if (!propertyNameValidator.find(propertyName)) { + return "Target name and property name not supported."; + } + return "Target name not supported."; + } + return nullptr; + } + std::string Evaluate( const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagCheckerParent) const override { - if (parameters.size() != 1 && parameters.size() != 2) { - reportError( - context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:...> expression requires one or two parameters"); - return std::string(); - } static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$"); - cmGeneratorTarget const* target = context->HeadTarget; - std::string propertyName = *parameters.begin(); - - if (parameters.size() == 1) { - context->HadHeadSensitiveCondition = true; - } - if (!target && parameters.size() == 1) { - reportError( - context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:prop> may only be used with binary targets. " - "It may not be used with add_custom_command or add_custom_target. " - "Specify the target to read a property from using the " - "$<TARGET_PROPERTY:tgt,prop> signature instead."); - return std::string(); - } + cmGeneratorTarget const* target = nullptr; + std::string targetName, propertyName; if (parameters.size() == 2) { - if (parameters.begin()->empty() && parameters[1].empty()) { - reportError( - context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " - "target name and property name."); - return std::string(); - } - if (parameters.begin()->empty()) { - reportError( - context, content->GetOriginalExpression(), - "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " - "target name."); - return std::string(); - } - - std::string targetName = parameters.front(); + targetName = parameters[0]; propertyName = parameters[1]; - if (!cmGeneratorExpression::IsValidTargetName(targetName)) { - if (!propertyNameValidator.find(propertyName)) { - ::reportError(context, content->GetOriginalExpression(), - "Target name and property name not supported."); - return std::string(); - } - ::reportError(context, content->GetOriginalExpression(), - "Target name not supported."); + + if (const char* e = GetErrorText(targetName, propertyName)) { + reportError(context, content->GetOriginalExpression(), e); return std::string(); } - static const std::string propALIASED_TARGET = "ALIASED_TARGET"; - if (propertyName == propALIASED_TARGET) { + if (propertyName == "ALIASED_TARGET"_s) { if (context->LG->GetMakefile()->IsAlias(targetName)) { if (cmGeneratorTarget* tgt = context->LG->FindGeneratorTargetToUse(targetName)) { return tgt->GetName(); } } - return ""; + return std::string(); } target = context->LG->FindGeneratorTargetToUse(targetName); @@ -1228,15 +1087,34 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode return std::string(); } context->AllTargets.insert(target); - } - if (target == context->HeadTarget) { + } else if (parameters.size() == 1) { + target = context->HeadTarget; + propertyName = parameters[0]; + // Keep track of the properties seen while processing. // The evaluation of the LINK_LIBRARIES generator expressions // will check this to ensure that properties have one consistent // value for all evaluations. context->SeenTargetProperties.insert(propertyName); + + context->HadHeadSensitiveCondition = true; + if (!target) { + reportError( + context, content->GetOriginalExpression(), + "$<TARGET_PROPERTY:prop> may only be used with binary targets. " + "It may not be used with add_custom_command or add_custom_target. " + "Specify the target to read a property from using the " + "$<TARGET_PROPERTY:tgt,prop> signature instead."); + return std::string(); + } + } else { + reportError( + context, content->GetOriginalExpression(), + "$<TARGET_PROPERTY:...> expression requires one or two parameters"); + return std::string(); } + if (propertyName == "SOURCES") { context->SourceSensitiveTargets.insert(target); } @@ -1481,10 +1359,16 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode reportError(context, content->GetOriginalExpression(), e.str()); return std::string(); } - if (gt->GetType() != cmStateEnums::OBJECT_LIBRARY) { + cmStateEnums::TargetType type = gt->GetType(); + if (type != cmStateEnums::EXECUTABLE && + type != cmStateEnums::STATIC_LIBRARY && + type != cmStateEnums::SHARED_LIBRARY && + type != cmStateEnums::MODULE_LIBRARY && + type != cmStateEnums::OBJECT_LIBRARY) { std::ostringstream e; e << "Objects of target \"" << tgtName - << "\" referenced but is not an OBJECT library."; + << "\" referenced but is not an allowed library types (EXECUTABLE, " + << "STATIC, SHARED, MODULE, OBJECT)."; reportError(context, content->GetOriginalExpression(), e.str()); return std::string(); } @@ -1950,39 +1834,37 @@ struct TargetFilesystemArtifactResultGetter<ArtifactPathTag> static std::string Get(const std::string& result) { return result; } }; -template <typename ArtifactT, typename ComponentT> -struct TargetFilesystemArtifact : public cmGeneratorExpressionNode +struct TargetArtifactBase : public cmGeneratorExpressionNode { - TargetFilesystemArtifact() {} // NOLINT(modernize-use-equals-default) - - int NumExpectedParameters() const override { return 1; } + TargetArtifactBase() {} // NOLINT(modernize-use-equals-default) - std::string Evaluate( +protected: + cmGeneratorTarget* GetTarget( const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* dagChecker) const override + cmGeneratorExpressionDAGChecker* dagChecker) const { // Lookup the referenced target. - std::string name = *parameters.begin(); + std::string name = parameters.front(); if (!cmGeneratorExpression::IsValidTargetName(name)) { ::reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); - return std::string(); + return nullptr; } cmGeneratorTarget* target = context->LG->FindGeneratorTargetToUse(name); if (!target) { ::reportError(context, content->GetOriginalExpression(), "No target \"" + name + "\""); - return std::string(); + return nullptr; } if (target->GetType() >= cmStateEnums::OBJECT_LIBRARY && target->GetType() != cmStateEnums::UNKNOWN_LIBRARY) { ::reportError(context, content->GetOriginalExpression(), "Target \"" + name + "\" is not an executable or library."); - return std::string(); + return nullptr; } if (dagChecker && (dagChecker->EvaluatingLinkLibraries(target) || @@ -1991,6 +1873,29 @@ struct TargetFilesystemArtifact : public cmGeneratorExpressionNode ::reportError(context, content->GetOriginalExpression(), "Expressions which require the linker language may not " "be used while evaluating link libraries"); + return nullptr; + } + + return target; + } +}; + +template <typename ArtifactT, typename ComponentT> +struct TargetFilesystemArtifact : public TargetArtifactBase +{ + TargetFilesystemArtifact() {} // NOLINT(modernize-use-equals-default) + + int NumExpectedParameters() const override { return 1; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagChecker) const override + { + cmGeneratorTarget* target = + this->GetTarget(parameters, context, content, dagChecker); + if (!target) { return std::string(); } context->DependTargets.insert(target); @@ -2037,6 +1942,242 @@ static const TargetFilesystemArtifact<ArtifactBundleContentDirTag, ArtifactPathTag> targetBundleContentDirNode; +// +// To retrieve base name for various artifacts +// +template <typename ArtifactT> +struct TargetOutputNameArtifactResultGetter +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content); +}; + +template <> +struct TargetOutputNameArtifactResultGetter<ArtifactNameTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* /*unused*/) + { + return target->GetOutputName(context->Config, + cmStateEnums::RuntimeBinaryArtifact) + + target->GetFilePostfix(context->Config); + } +}; + +template <> +struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + // The file used to link to the target (.so, .lib, .a). + if (!target->IsLinkable()) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_LINKER_FILE_BASE_NAME is allowed only for " + "libraries and executables with ENABLE_EXPORTS."); + return std::string(); + } + cmStateEnums::ArtifactType artifact = + target->HasImportLibrary(context->Config) + ? cmStateEnums::ImportLibraryArtifact + : cmStateEnums::RuntimeBinaryArtifact; + return target->GetOutputName(context->Config, artifact) + + target->GetFilePostfix(context->Config); + } +}; + +template <> +struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + if (target->IsImported()) { + ::reportError( + context, content->GetOriginalExpression(), + "TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets."); + return std::string(); + } + + std::string language = target->GetLinkerLanguage(context->Config); + + std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB"; + + if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) { + ::reportError( + context, content->GetOriginalExpression(), + "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker."); + return std::string(); + } + + cmStateEnums::TargetType targetType = target->GetType(); + + if (targetType != cmStateEnums::SHARED_LIBRARY && + targetType != cmStateEnums::MODULE_LIBRARY && + targetType != cmStateEnums::EXECUTABLE) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_PDB_FILE_BASE_NAME is allowed only for " + "targets with linker created artifacts."); + return std::string(); + } + + return target->GetPDBOutputName(context->Config) + + target->GetFilePostfix(context->Config); + } +}; + +template <typename ArtifactT> +struct TargetFileBaseNameArtifact : public TargetArtifactBase +{ + TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default) + + int NumExpectedParameters() const override { return 1; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagChecker) const override + { + cmGeneratorTarget* target = + this->GetTarget(parameters, context, content, dagChecker); + if (!target) { + return std::string(); + } + + std::string result = TargetOutputNameArtifactResultGetter<ArtifactT>::Get( + target, context, content); + if (context->HadError) { + return std::string(); + } + return result; + } +}; + +static const TargetFileBaseNameArtifact<ArtifactNameTag> + targetFileBaseNameNode; +static const TargetFileBaseNameArtifact<ArtifactLinkerTag> + targetLinkerFileBaseNameNode; +static const TargetFileBaseNameArtifact<ArtifactPdbTag> + targetPdbFileBaseNameNode; + +class ArtifactFilePrefixTag; +class ArtifactLinkerFilePrefixTag; +class ArtifactFileSuffixTag; +class ArtifactLinkerFileSuffixTag; + +template <typename ArtifactT> +struct TargetFileArtifactResultGetter +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content); +}; + +template <> +struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent*) + { + return target->GetFilePrefix(context->Config); + } +}; +template <> +struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + if (!target->IsLinkable()) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_LINKER_PREFIX is allowed only for libraries and " + "executables with ENABLE_EXPORTS."); + return std::string(); + } + + cmStateEnums::ArtifactType artifact = + target->HasImportLibrary(context->Config) + ? cmStateEnums::ImportLibraryArtifact + : cmStateEnums::RuntimeBinaryArtifact; + + return target->GetFilePrefix(context->Config, artifact); + } +}; +template <> +struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent*) + { + return target->GetFileSuffix(context->Config); + } +}; +template <> +struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag> +{ + static std::string Get(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content) + { + if (!target->IsLinkable()) { + ::reportError(context, content->GetOriginalExpression(), + "TARGET_LINKER_SUFFIX is allowed only for libraries and " + "executables with ENABLE_EXPORTS."); + return std::string(); + } + + cmStateEnums::ArtifactType artifact = + target->HasImportLibrary(context->Config) + ? cmStateEnums::ImportLibraryArtifact + : cmStateEnums::RuntimeBinaryArtifact; + + return target->GetFileSuffix(context->Config, artifact); + } +}; + +template <typename ArtifactT> +struct TargetFileArtifact : public TargetArtifactBase +{ + TargetFileArtifact() {} // NOLINT(modernize-use-equals-default) + + int NumExpectedParameters() const override { return 1; } + + std::string Evaluate( + const std::vector<std::string>& parameters, + cmGeneratorExpressionContext* context, + const GeneratorExpressionContent* content, + cmGeneratorExpressionDAGChecker* dagChecker) const override + { + cmGeneratorTarget* target = + this->GetTarget(parameters, context, content, dagChecker); + if (!target) { + return std::string(); + } + + std::string result = + TargetFileArtifactResultGetter<ArtifactT>::Get(target, context, content); + if (context->HadError) { + return std::string(); + } + return result; + } +}; + +static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode; +static const TargetFileArtifact<ArtifactLinkerFilePrefixTag> + targetLinkerFilePrefixNode; +static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode; +static const TargetFileArtifact<ArtifactLinkerFileSuffixTag> + targetLinkerFileSuffixNode; + static const struct ShellPathNode : public cmGeneratorExpressionNode { ShellPathNode() {} // NOLINT(modernize-use-equals-default) @@ -2047,88 +2188,114 @@ static const struct ShellPathNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { - if (!cmSystemTools::FileIsFullPath(parameters.front())) { + std::vector<std::string> listIn; + cmSystemTools::ExpandListArgument(parameters.front(), listIn); + if (listIn.empty()) { reportError(context, content->GetOriginalExpression(), - "\"" + parameters.front() + "\" is not an absolute path."); + "\"\" is not an absolute path."); return std::string(); } - cmOutputConverter converter(context->LG->GetStateSnapshot()); - return converter.ConvertDirectorySeparatorsForShell(parameters.front()); + cmStateSnapshot snapshot = context->LG->GetStateSnapshot(); + cmOutputConverter converter(snapshot); + const char* separator = snapshot.GetState()->UseWindowsShell() ? ";" : ":"; + std::vector<std::string> listOut; + listOut.reserve(listIn.size()); + for (auto const& in : listIn) { + if (!cmSystemTools::FileIsFullPath(in)) { + reportError(context, content->GetOriginalExpression(), + "\"" + in + "\" is not an absolute path."); + return std::string(); + } + listOut.emplace_back(converter.ConvertDirectorySeparatorsForShell(in)); + } + return cmJoin(listOut, separator); } } shellPathNode; const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( const std::string& identifier) { - typedef std::map<std::string, const cmGeneratorExpressionNode*> NodeMap; - static NodeMap nodeMap; - if (nodeMap.empty()) { - nodeMap["0"] = &zeroNode; - nodeMap["1"] = &oneNode; - nodeMap["AND"] = &andNode; - nodeMap["OR"] = &orNode; - nodeMap["NOT"] = ¬Node; - nodeMap["C_COMPILER_ID"] = &cCompilerIdNode; - nodeMap["CXX_COMPILER_ID"] = &cxxCompilerIdNode; - nodeMap["Fortran_COMPILER_ID"] = &fortranCompilerIdNode; - nodeMap["VERSION_GREATER"] = &versionGreaterNode; - nodeMap["VERSION_GREATER_EQUAL"] = &versionGreaterEqNode; - nodeMap["VERSION_LESS"] = &versionLessNode; - nodeMap["VERSION_LESS_EQUAL"] = &versionLessEqNode; - nodeMap["VERSION_EQUAL"] = &versionEqualNode; - nodeMap["C_COMPILER_VERSION"] = &cCompilerVersionNode; - nodeMap["CXX_COMPILER_VERSION"] = &cxxCompilerVersionNode; - nodeMap["Fortran_COMPILER_VERSION"] = &fortranCompilerVersionNode; - nodeMap["PLATFORM_ID"] = &platformIdNode; - nodeMap["COMPILE_FEATURES"] = &compileFeaturesNode; - nodeMap["CONFIGURATION"] = &configurationNode; - nodeMap["CONFIG"] = &configurationTestNode; - nodeMap["TARGET_FILE"] = &targetNodeGroup.File; - nodeMap["TARGET_LINKER_FILE"] = &targetLinkerNodeGroup.File; - nodeMap["TARGET_SONAME_FILE"] = &targetSoNameNodeGroup.File; - nodeMap["TARGET_PDB_FILE"] = &targetPdbNodeGroup.File; - nodeMap["TARGET_FILE_NAME"] = &targetNodeGroup.FileName; - nodeMap["TARGET_LINKER_FILE_NAME"] = &targetLinkerNodeGroup.FileName; - nodeMap["TARGET_SONAME_FILE_NAME"] = &targetSoNameNodeGroup.FileName; - nodeMap["TARGET_PDB_FILE_NAME"] = &targetPdbNodeGroup.FileName; - nodeMap["TARGET_FILE_DIR"] = &targetNodeGroup.FileDir; - nodeMap["TARGET_LINKER_FILE_DIR"] = &targetLinkerNodeGroup.FileDir; - nodeMap["TARGET_SONAME_FILE_DIR"] = &targetSoNameNodeGroup.FileDir; - nodeMap["TARGET_PDB_FILE_DIR"] = &targetPdbNodeGroup.FileDir; - nodeMap["TARGET_BUNDLE_DIR"] = &targetBundleDirNode; - nodeMap["TARGET_BUNDLE_CONTENT_DIR"] = &targetBundleContentDirNode; - nodeMap["STREQUAL"] = &strEqualNode; - nodeMap["EQUAL"] = &equalNode; - nodeMap["IN_LIST"] = &inListNode; - nodeMap["LOWER_CASE"] = &lowerCaseNode; - nodeMap["UPPER_CASE"] = &upperCaseNode; - nodeMap["MAKE_C_IDENTIFIER"] = &makeCIdentifierNode; - nodeMap["BOOL"] = &boolNode; - nodeMap["IF"] = &ifNode; - nodeMap["ANGLE-R"] = &angle_rNode; - nodeMap["COMMA"] = &commaNode; - nodeMap["SEMICOLON"] = &semicolonNode; - nodeMap["TARGET_PROPERTY"] = &targetPropertyNode; - nodeMap["TARGET_NAME"] = &targetNameNode; - nodeMap["TARGET_OBJECTS"] = &targetObjectsNode; - nodeMap["TARGET_POLICY"] = &targetPolicyNode; - nodeMap["TARGET_EXISTS"] = &targetExistsNode; - nodeMap["TARGET_NAME_IF_EXISTS"] = &targetNameIfExistsNode; - nodeMap["TARGET_GENEX_EVAL"] = &targetGenexEvalNode; - nodeMap["GENEX_EVAL"] = &genexEvalNode; - nodeMap["BUILD_INTERFACE"] = &buildInterfaceNode; - nodeMap["INSTALL_INTERFACE"] = &installInterfaceNode; - nodeMap["INSTALL_PREFIX"] = &installPrefixNode; - nodeMap["JOIN"] = &joinNode; - nodeMap["LINK_ONLY"] = &linkOnlyNode; - nodeMap["COMPILE_LANGUAGE"] = &languageNode; - nodeMap["SHELL_PATH"] = &shellPathNode; - } - NodeMap::const_iterator i = nodeMap.find(identifier); - if (i == nodeMap.end()) { - return nullptr; + static std::map<std::string, cmGeneratorExpressionNode const*> const nodeMap{ + { "0", &zeroNode }, + { "1", &oneNode }, + { "AND", &andNode }, + { "OR", &orNode }, + { "NOT", ¬Node }, + { "C_COMPILER_ID", &cCompilerIdNode }, + { "CXX_COMPILER_ID", &cxxCompilerIdNode }, + { "CUDA_COMPILER_ID", &cudaCompilerIdNode }, + { "Fortran_COMPILER_ID", &fortranCompilerIdNode }, + { "VERSION_GREATER", &versionGreaterNode }, + { "VERSION_GREATER_EQUAL", &versionGreaterEqNode }, + { "VERSION_LESS", &versionLessNode }, + { "VERSION_LESS_EQUAL", &versionLessEqNode }, + { "VERSION_EQUAL", &versionEqualNode }, + { "C_COMPILER_VERSION", &cCompilerVersionNode }, + { "CXX_COMPILER_VERSION", &cxxCompilerVersionNode }, + { "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode }, + { "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode }, + { "PLATFORM_ID", &platformIdNode }, + { "COMPILE_FEATURES", &compileFeaturesNode }, + { "CONFIGURATION", &configurationNode }, + { "CONFIG", &configurationTestNode }, + { "TARGET_FILE", &targetNodeGroup.File }, + { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File }, + { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File }, + { "TARGET_PDB_FILE", &targetPdbNodeGroup.File }, + { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode }, + { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode }, + { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode }, + { "TARGET_FILE_PREFIX", &targetFilePrefixNode }, + { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode }, + { "TARGET_FILE_SUFFIX", &targetFileSuffixNode }, + { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode }, + { "TARGET_FILE_NAME", &targetNodeGroup.FileName }, + { "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName }, + { "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName }, + { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName }, + { "TARGET_FILE_DIR", &targetNodeGroup.FileDir }, + { "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir }, + { "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir }, + { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir }, + { "TARGET_BUNDLE_DIR", &targetBundleDirNode }, + { "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode }, + { "STREQUAL", &strEqualNode }, + { "EQUAL", &equalNode }, + { "IN_LIST", &inListNode }, + { "FILTER", &filterNode }, + { "REMOVE_DUPLICATES", &removeDuplicatesNode }, + { "LOWER_CASE", &lowerCaseNode }, + { "UPPER_CASE", &upperCaseNode }, + { "MAKE_C_IDENTIFIER", &makeCIdentifierNode }, + { "BOOL", &boolNode }, + { "IF", &ifNode }, + { "ANGLE-R", &angle_rNode }, + { "COMMA", &commaNode }, + { "SEMICOLON", &semicolonNode }, + { "TARGET_PROPERTY", &targetPropertyNode }, + { "TARGET_NAME", &targetNameNode }, + { "TARGET_OBJECTS", &targetObjectsNode }, + { "TARGET_POLICY", &targetPolicyNode }, + { "TARGET_EXISTS", &targetExistsNode }, + { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode }, + { "TARGET_GENEX_EVAL", &targetGenexEvalNode }, + { "GENEX_EVAL", &genexEvalNode }, + { "BUILD_INTERFACE", &buildInterfaceNode }, + { "INSTALL_INTERFACE", &installInterfaceNode }, + { "INSTALL_PREFIX", &installPrefixNode }, + { "JOIN", &joinNode }, + { "LINK_ONLY", &linkOnlyNode }, + { "COMPILE_LANGUAGE", &languageNode }, + { "SHELL_PATH", &shellPathNode } + }; + + { + auto itr = nodeMap.find(identifier); + if (itr != nodeMap.end()) { + return itr->second; + } } - return i->second; + return nullptr; } void reportError(cmGeneratorExpressionContext* context, diff --git a/Source/cmGeneratorExpressionParser.cxx b/Source/cmGeneratorExpressionParser.cxx index 949a86d..7aa3314 100644 --- a/Source/cmGeneratorExpressionParser.cxx +++ b/Source/cmGeneratorExpressionParser.cxx @@ -47,11 +47,11 @@ static void extendResult( if (!result.empty() && (*(result.end() - 1))->GetType() == cmGeneratorExpressionEvaluator::Text && - (*contents.begin())->GetType() == cmGeneratorExpressionEvaluator::Text) { + contents.front()->GetType() == cmGeneratorExpressionEvaluator::Text) { TextContent* textContent = static_cast<TextContent*>(*(result.end() - 1)); textContent->Extend( - static_cast<TextContent*>(*contents.begin())->GetLength()); - delete *contents.begin(); + static_cast<TextContent*>(contents.front())->GetLength()); + delete contents.front(); result.insert(result.end(), contents.begin() + 1, contents.end()); } else { result.insert(result.end(), contents.begin(), contents.end()); diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 25349d4..036a07d 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -28,6 +28,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPropertyMap.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmState.h" @@ -64,20 +65,137 @@ const char* cmTargetPropertyComputer::ComputeLocation<cmGeneratorTarget>( class cmGeneratorTarget::TargetPropertyEntry { +protected: static cmLinkImplItem NoLinkImplItem; public: - TargetPropertyEntry(std::unique_ptr<cmCompiledGeneratorExpression> cge, - cmLinkImplItem const& item = NoLinkImplItem) - : ge(std::move(cge)) - , LinkImplItem(item) + TargetPropertyEntry(cmLinkImplItem const& item) + : LinkImplItem(item) { } - const std::unique_ptr<cmCompiledGeneratorExpression> ge; + virtual ~TargetPropertyEntry() = default; + + virtual const std::string& Evaluate( + cmLocalGenerator* lg, const std::string& config, bool quiet = false, + cmGeneratorTarget const* headTarget = nullptr, + cmGeneratorTarget const* currentTarget = nullptr, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr, + std::string const& language = std::string()) const = 0; + virtual const std::string& Evaluate( + cmLocalGenerator* lg, const std::string& config, bool quiet, + cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker, + std::string const& language = std::string()) const = 0; + + virtual cmListFileBacktrace GetBacktrace() const = 0; + virtual std::string const& GetInput() const = 0; + virtual bool GetHadContextSensitiveCondition() const { return false; } + cmLinkImplItem const& LinkImplItem; + +private: + cmListFileBacktrace Backtrace; }; cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem; +class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry +{ +public: + TargetPropertyEntryGenex(std::unique_ptr<cmCompiledGeneratorExpression> cge, + cmLinkImplItem const& item = NoLinkImplItem) + : cmGeneratorTarget::TargetPropertyEntry(item) + , ge(std::move(cge)) + { + } + + const std::string& Evaluate( + cmLocalGenerator* lg, const std::string& config, bool quiet = false, + cmGeneratorTarget const* headTarget = nullptr, + cmGeneratorTarget const* currentTarget = nullptr, + cmGeneratorExpressionDAGChecker* dagChecker = nullptr, + std::string const& language = std::string()) const override + { + return this->ge->Evaluate(lg, config, quiet, headTarget, currentTarget, + dagChecker, language); + } + const std::string& Evaluate( + cmLocalGenerator* lg, const std::string& config, bool quiet, + cmGeneratorTarget const* headTarget, + cmGeneratorExpressionDAGChecker* dagChecker, + std::string const& language = std::string()) const override + { + return this->ge->Evaluate(lg, config, quiet, headTarget, dagChecker, + language); + } + + cmListFileBacktrace GetBacktrace() const override + { + return this->ge->GetBacktrace(); + } + + std::string const& GetInput() const override { return this->ge->GetInput(); } + + bool GetHadContextSensitiveCondition() const override + { + return this->ge->GetHadContextSensitiveCondition(); + } + +private: + const std::unique_ptr<cmCompiledGeneratorExpression> ge; +}; + +class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry +{ +public: + TargetPropertyEntryString(std::string propertyValue, + cmListFileBacktrace backtrace, + cmLinkImplItem const& item = NoLinkImplItem) + : cmGeneratorTarget::TargetPropertyEntry(item) + , PropertyValue(std::move(propertyValue)) + , Backtrace(std::move(backtrace)) + { + } + + const std::string& Evaluate(cmLocalGenerator*, const std::string&, bool, + cmGeneratorTarget const*, + cmGeneratorTarget const*, + cmGeneratorExpressionDAGChecker*, + std::string const&) const override + { + return this->PropertyValue; + } + const std::string& Evaluate(cmLocalGenerator*, const std::string&, bool, + cmGeneratorTarget const*, + cmGeneratorExpressionDAGChecker*, + std::string const&) const override + { + return this->PropertyValue; + } + + cmListFileBacktrace GetBacktrace() const override { return this->Backtrace; } + std::string const& GetInput() const override { return this->PropertyValue; } + +private: + std::string PropertyValue; + cmListFileBacktrace Backtrace; +}; + +cmGeneratorTarget::TargetPropertyEntry* CreateTargetPropertyEntry( + const std::string& propertyValue, + cmListFileBacktrace backtrace = cmListFileBacktrace(), + bool evaluateForBuildsystem = false) +{ + if (cmGeneratorExpression::Find(propertyValue) != std::string::npos) { + cmGeneratorExpression ge(std::move(backtrace)); + std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(propertyValue); + cge->SetEvaluateForBuildsystem(evaluateForBuildsystem); + return new TargetPropertyEntryGenex(std::move(cge)); + } + + return new TargetPropertyEntryString(propertyValue, backtrace); +} + void CreatePropertyGeneratorExpressions( cmStringRange entries, cmBacktraceRange backtraces, std::vector<cmGeneratorTarget::TargetPropertyEntry*>& items, @@ -86,11 +204,8 @@ void CreatePropertyGeneratorExpressions( std::vector<cmListFileBacktrace>::const_iterator btIt = backtraces.begin(); for (std::vector<std::string>::const_iterator it = entries.begin(); it != entries.end(); ++it, ++btIt) { - cmGeneratorExpression ge(*btIt); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*it); - cge->SetEvaluateForBuildsystem(evaluateForBuildsystem); items.push_back( - new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); + CreateTargetPropertyEntry(*it, *btIt, evaluateForBuildsystem)); } } @@ -147,7 +262,7 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) this->DLLPlatform = !this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty(); - this->PolicyMap = t->PolicyMap; + this->PolicyMap = t->GetPolicyMap(); } cmGeneratorTarget::~cmGeneratorTarget() @@ -166,7 +281,7 @@ const char* cmGeneratorTarget::GetSourcesProperty() const { std::vector<std::string> values; for (TargetPropertyEntry* se : this->SourceEntries) { - values.push_back(se->ge->GetInput()); + values.push_back(se->GetInput()); } static std::string value; value.clear(); @@ -349,6 +464,150 @@ std::string cmGeneratorTarget::GetOutputName( return i->second; } +std::string cmGeneratorTarget::GetFilePrefix( + const std::string& config, cmStateEnums::ArtifactType artifact) const +{ + if (this->IsImported()) { + const char* prefix = this->GetFilePrefixInternal(artifact); + + return prefix ? prefix : std::string(); + } + + std::string prefix, suffix, base; + this->GetFullNameInternal(config, artifact, prefix, base, suffix); + return prefix; +} +std::string cmGeneratorTarget::GetFileSuffix( + const std::string& config, cmStateEnums::ArtifactType artifact) const +{ + if (this->IsImported()) { + const char* suffix = this->GetFileSuffixInternal(artifact); + + return suffix ? suffix : std::string(); + } + + std::string prefix, suffix, base; + this->GetFullNameInternal(config, artifact, prefix, base, suffix); + return suffix; +} + +std::string cmGeneratorTarget::GetFilePostfix(const std::string& config) const +{ + const char* postfix = nullptr; + if (!config.empty()) { + std::string configProp = cmSystemTools::UpperCase(config); + configProp += "_POSTFIX"; + postfix = this->GetProperty(configProp); + // Mac application bundles and frameworks have no postfix. + if (!this->IsImported() && postfix && + (this->IsAppBundleOnApple() || this->IsFrameworkOnApple())) { + postfix = nullptr; + } + } + return postfix ? postfix : std::string(); +} + +const char* cmGeneratorTarget::GetFilePrefixInternal( + cmStateEnums::ArtifactType artifact, const std::string& language) const +{ + // no prefix for non-main target types. + if (this->GetType() != cmStateEnums::STATIC_LIBRARY && + this->GetType() != cmStateEnums::SHARED_LIBRARY && + this->GetType() != cmStateEnums::MODULE_LIBRARY && + this->GetType() != cmStateEnums::EXECUTABLE) { + return nullptr; + } + + const bool isImportedLibraryArtifact = + (artifact == cmStateEnums::ImportLibraryArtifact); + + // Return an empty prefix for the import library if this platform + // does not support import libraries. + if (isImportedLibraryArtifact && + !this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) { + return nullptr; + } + + // The implib option is only allowed for shared libraries, module + // libraries, and executables. + if (this->GetType() != cmStateEnums::SHARED_LIBRARY && + this->GetType() != cmStateEnums::MODULE_LIBRARY && + this->GetType() != cmStateEnums::EXECUTABLE) { + artifact = cmStateEnums::RuntimeBinaryArtifact; + } + + // Compute prefix value. + const char* targetPrefix = + (isImportedLibraryArtifact ? this->GetProperty("IMPORT_PREFIX") + : this->GetProperty("PREFIX")); + + if (!targetPrefix) { + const char* prefixVar = this->Target->GetPrefixVariableInternal(artifact); + if (!language.empty() && prefixVar && *prefixVar) { + std::string langPrefix = prefixVar + std::string("_") + language; + targetPrefix = this->Makefile->GetDefinition(langPrefix); + } + + // if there is no prefix on the target nor specific language + // use the cmake definition. + if (!targetPrefix && prefixVar) { + targetPrefix = this->Makefile->GetDefinition(prefixVar); + } + } + + return targetPrefix; +} +const char* cmGeneratorTarget::GetFileSuffixInternal( + cmStateEnums::ArtifactType artifact, const std::string& language) const +{ + // no suffix for non-main target types. + if (this->GetType() != cmStateEnums::STATIC_LIBRARY && + this->GetType() != cmStateEnums::SHARED_LIBRARY && + this->GetType() != cmStateEnums::MODULE_LIBRARY && + this->GetType() != cmStateEnums::EXECUTABLE) { + return nullptr; + } + + const bool isImportedLibraryArtifact = + (artifact == cmStateEnums::ImportLibraryArtifact); + + // Return an empty suffix for the import library if this platform + // does not support import libraries. + if (isImportedLibraryArtifact && + !this->Makefile->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) { + return nullptr; + } + + // The implib option is only allowed for shared libraries, module + // libraries, and executables. + if (this->GetType() != cmStateEnums::SHARED_LIBRARY && + this->GetType() != cmStateEnums::MODULE_LIBRARY && + this->GetType() != cmStateEnums::EXECUTABLE) { + artifact = cmStateEnums::RuntimeBinaryArtifact; + } + + // Compute suffix value. + const char* targetSuffix = + (isImportedLibraryArtifact ? this->GetProperty("IMPORT_SUFFIX") + : this->GetProperty("SUFFIX")); + + if (!targetSuffix) { + const char* suffixVar = this->Target->GetSuffixVariableInternal(artifact); + if (!language.empty() && suffixVar && *suffixVar) { + std::string langSuffix = suffixVar + std::string("_") + language; + targetSuffix = this->Makefile->GetDefinition(langSuffix); + } + + // if there is no suffix on the target nor specific language + // use the cmake definition. + if (!targetSuffix && suffixVar) { + targetSuffix = this->Makefile->GetDefinition(suffixVar); + } + } + + return targetSuffix; +} + void cmGeneratorTarget::ClearSourcesCache() { this->KindedSourcesMap.clear(); @@ -358,13 +617,9 @@ void cmGeneratorTarget::ClearSourcesCache() void cmGeneratorTarget::AddSourceCommon(const std::string& src, bool before) { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - cmGeneratorExpression ge(lfbt); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(src); - cge->SetEvaluateForBuildsystem(true); - this->SourceEntries.insert(before ? this->SourceEntries.begin() - : this->SourceEntries.end(), - new TargetPropertyEntry(std::move(cge))); + this->SourceEntries.insert( + before ? this->SourceEntries.begin() : this->SourceEntries.end(), + CreateTargetPropertyEntry(src, this->Makefile->GetBacktrace(), true)); this->ClearSourcesCache(); } @@ -386,14 +641,10 @@ void cmGeneratorTarget::AddIncludeDirectory(const std::string& src, bool before) { this->Target->InsertInclude(src, this->Makefile->GetBacktrace(), before); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - cmGeneratorExpression ge(lfbt); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(src); - cge->SetEvaluateForBuildsystem(true); this->IncludeDirectoriesEntries.insert( before ? this->IncludeDirectoriesEntries.begin() : this->IncludeDirectoriesEntries.end(), - new TargetPropertyEntry(std::move(cge))); + CreateTargetPropertyEntry(src, this->Makefile->GetBacktrace(), true)); } std::vector<cmSourceFile*> const* cmGeneratorTarget::GetSourceDepends( @@ -853,8 +1104,7 @@ static void AddInterfaceEntries( cmGeneratorExpression ge(lib.Backtrace); std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex); cge->SetEvaluateForBuildsystem(true); - entries.push_back( - new cmGeneratorTarget::TargetPropertyEntry(std::move(cge), lib)); + entries.push_back(new TargetPropertyEntryGenex(std::move(cge), lib)); } } } @@ -876,8 +1126,7 @@ static void AddObjectEntries( cmGeneratorExpression ge(lib.Backtrace); std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex); cge->SetEvaluateForBuildsystem(true); - entries.push_back( - new cmGeneratorTarget::TargetPropertyEntry(std::move(cge), lib)); + entries.push_back(new TargetPropertyEntryGenex(std::move(cge), lib)); } } } @@ -899,12 +1148,12 @@ static bool processSources( cmLinkImplItem const& item = entry->LinkImplItem; std::string const& targetName = item.AsStr(); std::vector<std::string> entrySources; - cmSystemTools::ExpandListArgument( - entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, tgt, - dagChecker), - entrySources); + cmSystemTools::ExpandListArgument(entry->Evaluate(tgt->GetLocalGenerator(), + config, false, tgt, tgt, + dagChecker), + entrySources); - if (entry->ge->GetHadContextSensitiveCondition()) { + if (entry->GetHadContextSensitiveCondition()) { contextDependent = true; } @@ -939,7 +1188,7 @@ static bool processSources( std::string usedSources; for (std::string const& src : entrySources) { if (uniqueSrcs.insert(src).second) { - srcs.emplace_back(src, entry->ge->GetBacktrace()); + srcs.emplace_back(src, entry->GetBacktrace()); if (debugSources) { usedSources += " * " + src + "\n"; } @@ -950,7 +1199,7 @@ static bool processSources( MessageType::LOG, std::string("Used sources for target ") + tgt->GetName() + ":\n" + usedSources, - entry->ge->GetBacktrace()); + entry->GetBacktrace()); } } return contextDependent; @@ -1606,13 +1855,7 @@ std::string cmGeneratorTarget::GetSOName(const std::string& config) const return ""; } // Compute the soname that will be built. - std::string name; - std::string soName; - std::string realName; - std::string impName; - std::string pdbName; - this->GetLibraryNames(name, soName, realName, impName, pdbName, config); - return soName; + return this->GetLibraryNames(config).SharedObject; } static bool shouldAddFullLevel(cmGeneratorTarget::BundleDirectoryLevel level) @@ -2378,7 +2621,7 @@ void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) std::set<cmGeneratorTarget*> targets; for (cmCustomCommandLine const& cCmdLine : cc.GetCommandLines()) { - std::string const& command = *cCmdLine.begin(); + std::string const& command = cCmdLine.front(); // Check for a target with this name. if (cmGeneratorTarget* t = this->LocalGenerator->FindGeneratorTargetToUse(command)) { @@ -2534,10 +2777,10 @@ static void processIncludeDirectories( bool const fromImported = item.Target && item.Target->IsImported(); bool const checkCMP0027 = item.FromGenex; std::vector<std::string> entryIncludes; - cmSystemTools::ExpandListArgument( - entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, - dagChecker, language), - entryIncludes); + cmSystemTools::ExpandListArgument(entry->Evaluate(tgt->GetLocalGenerator(), + config, false, tgt, + dagChecker, language), + entryIncludes); std::string usedIncludes; for (std::string& entryInclude : entryIncludes) { @@ -2615,7 +2858,7 @@ static void processIncludeDirectories( std::string inc = entryInclude; if (uniqueIncludes.insert(inc).second) { - includes.emplace_back(inc, entry->ge->GetBacktrace()); + includes.emplace_back(inc, entry->GetBacktrace()); if (debugIncludes) { usedIncludes += " * " + inc + "\n"; } @@ -2626,7 +2869,7 @@ static void processIncludeDirectories( MessageType::LOG, std::string("Used includes for target ") + tgt->GetName() + ":\n" + usedIncludes, - entry->ge->GetBacktrace()); + entry->GetBacktrace()); } } } @@ -2678,11 +2921,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories( libDir = frameworkCheck.match(1); - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(libDir.c_str()); linkInterfaceIncludeDirectoriesEntries.push_back( - new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); + CreateTargetPropertyEntry(libDir)); } } @@ -2712,10 +2952,10 @@ static void processOptionsInternal( { for (cmGeneratorTarget::TargetPropertyEntry* entry : entries) { std::vector<std::string> entryOptions; - cmSystemTools::ExpandListArgument( - entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, - dagChecker, language), - entryOptions); + cmSystemTools::ExpandListArgument(entry->Evaluate(tgt->GetLocalGenerator(), + config, false, tgt, + dagChecker, language), + entryOptions); std::string usedOptions; for (std::string const& opt : entryOptions) { if (uniqueOptions.insert(opt).second) { @@ -2724,10 +2964,10 @@ static void processOptionsInternal( std::vector<std::string> tmp; cmSystemTools::ParseUnixCommandLine(opt.c_str() + 6, tmp); for (std::string& o : tmp) { - options.emplace_back(std::move(o), entry->ge->GetBacktrace()); + options.emplace_back(std::move(o), entry->GetBacktrace()); } } else { - options.emplace_back(opt, entry->ge->GetBacktrace()); + options.emplace_back(opt, entry->GetBacktrace()); } if (debugOptions) { usedOptions += " * " + opt + "\n"; @@ -2739,7 +2979,7 @@ static void processOptionsInternal( MessageType::LOG, std::string("Used ") + logName + std::string(" for target ") + tgt->GetName() + ":\n" + usedOptions, - entry->ge->GetBacktrace()); + entry->GetBacktrace()); } } } @@ -2943,11 +3183,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetCompileDefinitions( CM_FALLTHROUGH; } case cmPolicies::OLD: { - cmGeneratorExpression ge; - std::unique_ptr<cmCompiledGeneratorExpression> cge = - ge.Parse(configProp); linkInterfaceCompileDefinitionsEntries.push_back( - new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); + CreateTargetPropertyEntry(configProp)); } break; case cmPolicies::NEW: case cmPolicies::REQUIRED_ALWAYS: @@ -3173,12 +3410,9 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetStaticLibraryLinkOptions( if (const char* linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) { std::vector<std::string> options; - cmGeneratorExpression ge; cmSystemTools::ExpandListArgument(linkOptions, options); for (const auto& option : options) { - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(option); - entries.push_back( - new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); + entries.push_back(CreateTargetPropertyEntry(option)); } } processStaticLibraryLinkOptions(this, entries, result, uniqueOptions, @@ -3202,10 +3436,10 @@ void processLinkDirectories( std::string const& targetName = item.AsStr(); std::vector<std::string> entryDirectories; - cmSystemTools::ExpandListArgument( - entry->ge->Evaluate(tgt->GetLocalGenerator(), config, false, tgt, - dagChecker, language), - entryDirectories); + cmSystemTools::ExpandListArgument(entry->Evaluate(tgt->GetLocalGenerator(), + config, false, tgt, + dagChecker, language), + entryDirectories); std::string usedDirectories; for (std::string& entryDirectory : entryDirectories) { @@ -3261,7 +3495,7 @@ void processLinkDirectories( MessageType::LOG, std::string("Used link directories for target ") + tgt->GetName() + ":\n" + usedDirectories, - entry->ge->GetBacktrace()); + entry->GetBacktrace()); } } } @@ -3358,12 +3592,9 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends( if (const char* linkDepends = this->GetProperty("LINK_DEPENDS")) { std::vector<std::string> depends; - cmGeneratorExpression ge; cmSystemTools::ExpandListArgument(linkDepends, depends); for (const auto& depend : depends) { - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(depend); - linkDependsEntries.push_back( - new cmGeneratorTarget::TargetPropertyEntry(std::move(cge))); + linkDependsEntries.push_back(CreateTargetPropertyEntry(depend)); } } AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", @@ -3383,17 +3614,13 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const cmGlobalGenerator* gg = this->LocalGenerator->GetGlobalGenerator(); // Get the names. - std::string name; - std::string soName; - std::string realName; - std::string impName; - std::string pdbName; + cmGeneratorTarget::Names targetNames; if (this->GetType() == cmStateEnums::EXECUTABLE) { - this->GetExecutableNames(name, realName, impName, pdbName, config); + targetNames = this->GetExecutableNames(config); } else if (this->GetType() == cmStateEnums::STATIC_LIBRARY || this->GetType() == cmStateEnums::SHARED_LIBRARY || this->GetType() == cmStateEnums::MODULE_LIBRARY) { - this->GetLibraryNames(name, soName, realName, impName, pdbName, config); + targetNames = this->GetLibraryNames(config); } else { return; } @@ -3404,34 +3631,34 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const // Add each name. std::string f; - if (!name.empty()) { + if (!targetNames.Output.empty()) { f = dir; f += "/"; - f += name; + f += targetNames.Output; gg->AddToManifest(f); } - if (!soName.empty()) { + if (!targetNames.SharedObject.empty()) { f = dir; f += "/"; - f += soName; + f += targetNames.SharedObject; gg->AddToManifest(f); } - if (!realName.empty()) { + if (!targetNames.Real.empty()) { f = dir; f += "/"; - f += realName; + f += targetNames.Real; gg->AddToManifest(f); } - if (!pdbName.empty()) { + if (!targetNames.PDB.empty()) { f = dir; f += "/"; - f += pdbName; + f += targetNames.PDB; gg->AddToManifest(f); } - if (!impName.empty()) { + if (!targetNames.ImportLibrary.empty()) { f = this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact); f += "/"; - f += impName; + f += targetNames.ImportLibrary; gg->AddToManifest(f); } } @@ -3509,29 +3736,17 @@ std::string cmGeneratorTarget::NormalGetRealName( if (this->GetType() == cmStateEnums::EXECUTABLE) { // Compute the real name that will be built. - std::string name; - std::string realName; - std::string impName; - std::string pdbName; - this->GetExecutableNames(name, realName, impName, pdbName, config); - return realName; + return this->GetExecutableNames(config).Real; } // Compute the real name that will be built. - std::string name; - std::string soName; - std::string realName; - std::string impName; - std::string pdbName; - this->GetLibraryNames(name, soName, realName, impName, pdbName, config); - return realName; -} - -void cmGeneratorTarget::GetLibraryNames(std::string& name, std::string& soName, - std::string& realName, - std::string& impName, - std::string& pdbName, - const std::string& config) const + return this->GetLibraryNames(config).Real; +} + +cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames( + const std::string& config) const { + cmGeneratorTarget::Names targetNames; + // This should not be called for imported targets. // TODO: Split cmTarget into a class hierarchy to get compile-time // enforcement of the limited imported target API. @@ -3539,7 +3754,6 @@ void cmGeneratorTarget::GetLibraryNames(std::string& name, std::string& soName, std::string msg = "GetLibraryNames called on imported target: "; msg += this->GetName(); this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg); - return; } // Check for library version properties. @@ -3565,50 +3779,51 @@ void cmGeneratorTarget::GetLibraryNames(std::string& name, std::string& soName, // Get the components of the library name. std::string prefix; - std::string base; std::string suffix; this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact, - prefix, base, suffix); + prefix, targetNames.Base, suffix); // The library name. - name = prefix + base + suffix; + targetNames.Output = prefix + targetNames.Base + suffix; if (this->IsFrameworkOnApple()) { - realName = prefix; + targetNames.Real = prefix; if (!this->Makefile->PlatformIsAppleEmbedded()) { - realName += "Versions/"; - realName += this->GetFrameworkVersion(); - realName += "/"; + targetNames.Real += "Versions/"; + targetNames.Real += this->GetFrameworkVersion(); + targetNames.Real += "/"; } - realName += base; - soName = realName; + targetNames.Real += targetNames.Base; + targetNames.SharedObject = targetNames.Real; } else { // The library's soname. - this->ComputeVersionedName(soName, prefix, base, suffix, name, soversion); + this->ComputeVersionedName(targetNames.SharedObject, prefix, + targetNames.Base, suffix, targetNames.Output, + soversion); // The library's real name on disk. - this->ComputeVersionedName(realName, prefix, base, suffix, name, version); + this->ComputeVersionedName(targetNames.Real, prefix, targetNames.Base, + suffix, targetNames.Output, version); } // The import library name. if (this->GetType() == cmStateEnums::SHARED_LIBRARY || this->GetType() == cmStateEnums::MODULE_LIBRARY) { - impName = + targetNames.ImportLibrary = this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact); - } else { - impName.clear(); } // The program database file name. - pdbName = this->GetPDBName(config); + targetNames.PDB = this->GetPDBName(config); + + return targetNames; } -void cmGeneratorTarget::GetExecutableNames(std::string& name, - std::string& realName, - std::string& impName, - std::string& pdbName, - const std::string& config) const +cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames( + const std::string& config) const { + cmGeneratorTarget::Names targetNames; + // This should not be called for imported targets. // TODO: Split cmTarget into a class hierarchy to get compile-time // enforcement of the limited imported target API. @@ -3633,34 +3848,35 @@ void cmGeneratorTarget::GetExecutableNames(std::string& name, // Get the components of the executable name. std::string prefix; - std::string base; std::string suffix; this->GetFullNameInternal(config, cmStateEnums::RuntimeBinaryArtifact, - prefix, base, suffix); + prefix, targetNames.Base, suffix); // The executable name. - name = prefix + base + suffix; + targetNames.Output = prefix + targetNames.Base + suffix; // The executable's real name on disk. #if defined(__CYGWIN__) - realName = prefix + base; + targetNames.Real = prefix + targetNames.Base; #else - realName = name; + targetNames.Real = targetNames.Output; #endif if (version) { - realName += "-"; - realName += version; + targetNames.Real += "-"; + targetNames.Real += version; } #if defined(__CYGWIN__) - realName += suffix; + targetNames.Real += suffix; #endif // The import library name. - impName = + targetNames.ImportLibrary = this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact); // The program database file name. - pdbName = this->GetPDBName(config); + targetNames.PDB = this->GetPDBName(config); + + return targetNames; } std::string cmGeneratorTarget::GetFullNameInternal( @@ -3716,6 +3932,11 @@ void cmGeneratorTarget::GetFullNameInternal( return; } + // retrieve prefix and suffix + std::string ll = this->GetLinkerLanguage(config); + const char* targetPrefix = this->GetFilePrefixInternal(artifact, ll); + const char* targetSuffix = this->GetFileSuffixInternal(artifact, ll); + // The implib option is only allowed for shared libraries, module // libraries, and executables. if (this->GetType() != cmStateEnums::SHARED_LIBRARY && @@ -3725,47 +3946,7 @@ void cmGeneratorTarget::GetFullNameInternal( } // Compute the full name for main target types. - const char* targetPrefix = - (isImportedLibraryArtifact ? this->GetProperty("IMPORT_PREFIX") - : this->GetProperty("PREFIX")); - const char* targetSuffix = - (isImportedLibraryArtifact ? this->GetProperty("IMPORT_SUFFIX") - : this->GetProperty("SUFFIX")); - const char* configPostfix = nullptr; - if (!config.empty()) { - std::string configProp = cmSystemTools::UpperCase(config); - configProp += "_POSTFIX"; - configPostfix = this->GetProperty(configProp); - // Mac application bundles and frameworks have no postfix. - if (configPostfix && - (this->IsAppBundleOnApple() || this->IsFrameworkOnApple())) { - configPostfix = nullptr; - } - } - const char* prefixVar = this->Target->GetPrefixVariableInternal(artifact); - const char* suffixVar = this->Target->GetSuffixVariableInternal(artifact); - - // Check for language-specific default prefix and suffix. - std::string ll = this->GetLinkerLanguage(config); - if (!ll.empty()) { - if (!targetSuffix && suffixVar && *suffixVar) { - std::string langSuff = suffixVar + std::string("_") + ll; - targetSuffix = this->Makefile->GetDefinition(langSuff); - } - if (!targetPrefix && prefixVar && *prefixVar) { - std::string langPrefix = prefixVar + std::string("_") + ll; - targetPrefix = this->Makefile->GetDefinition(langPrefix); - } - } - - // if there is no prefix on the target use the cmake definition - if (!targetPrefix && prefixVar) { - targetPrefix = this->Makefile->GetSafeDefinition(prefixVar).c_str(); - } - // if there is no suffix on the target use the cmake definition - if (!targetSuffix && suffixVar) { - targetSuffix = this->Makefile->GetSafeDefinition(suffixVar).c_str(); - } + const std::string configPostfix = this->GetFilePostfix(config); // frameworks have directory prefix but no suffix std::string fw_prefix; @@ -3790,7 +3971,7 @@ void cmGeneratorTarget::GetFullNameInternal( outBase += this->GetOutputName(config, artifact); // Append the per-configuration postfix. - outBase += configPostfix ? configPostfix : ""; + outBase += configPostfix; // Name shared libraries with their version number on some platforms. if (const char* soversion = this->GetProperty("SOVERSION")) { @@ -3812,6 +3993,31 @@ std::string cmGeneratorTarget::GetLinkerLanguage( return this->GetLinkClosure(config)->LinkerLanguage; } +std::string cmGeneratorTarget::GetPDBOutputName( + const std::string& config) const +{ + std::string base = + this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact); + + std::vector<std::string> props; + std::string configUpper = cmSystemTools::UpperCase(config); + if (!configUpper.empty()) { + // PDB_NAME_<CONFIG> + props.push_back("PDB_NAME_" + configUpper); + } + + // PDB_NAME + props.emplace_back("PDB_NAME"); + + for (std::string const& p : props) { + if (const char* outName = this->GetProperty(p)) { + base = outName; + break; + } + } + return base; +} + std::string cmGeneratorTarget::GetPDBName(const std::string& config) const { std::string prefix; diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 59d38af..0e0ee6a 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -69,9 +69,9 @@ public: std::string GetExportName() const; std::vector<std::string> GetPropertyKeys() const; - ///! Might return a nullptr if the property is not set or invalid + //! Might return a nullptr if the property is not set or invalid const char* GetProperty(const std::string& prop) const; - ///! Always returns a valid pointer + //! Always returns a valid pointer const char* GetSafeProperty(const std::string& prop) const; bool GetPropertyAsBool(const std::string& prop) const; void GetSourceFiles(std::vector<cmSourceFile*>& files, @@ -503,6 +503,9 @@ public: OutputInfo const* GetOutputInfo(const std::string& config) const; + // Get the target PDB base name. + std::string GetPDBOutputName(const std::string& config) const; + /** Get the name of the pdb file for the target. */ std::string GetPDBName(const std::string& config = "") const; @@ -531,6 +534,18 @@ public: std::string GetOutputName(const std::string& config, cmStateEnums::ArtifactType artifact) const; + /** Get target file prefix */ + std::string GetFilePrefix(const std::string& config, + cmStateEnums::ArtifactType artifact = + cmStateEnums::RuntimeBinaryArtifact) const; + /** Get target file prefix */ + std::string GetFileSuffix(const std::string& config, + cmStateEnums::ArtifactType artifact = + cmStateEnums::RuntimeBinaryArtifact) const; + + /** Get target file postfix */ + std::string GetFilePostfix(const std::string& config) const; + /** Clears cached meta data for local and external source files. * The meta data will be recomputed on demand. */ @@ -568,19 +583,25 @@ public: void GetAutoUicOptions(std::vector<std::string>& result, const std::string& config) const; + struct Names + { + std::string Base; + std::string Output; + std::string Real; + std::string ImportLibrary; + std::string PDB; + std::string SharedObject; + }; + /** Get the names of the executable needed to generate a build rule that takes into account executable version numbers. This should be called only on an executable target. */ - void GetExecutableNames(std::string& name, std::string& realName, - std::string& impName, std::string& pdbName, - const std::string& config) const; + Names GetExecutableNames(const std::string& config) const; /** Get the names of the library needed to generate a build rule that takes into account shared library version numbers. This should be called only on a library target. */ - void GetLibraryNames(std::string& name, std::string& soName, - std::string& realName, std::string& impName, - std::string& pdbName, const std::string& config) const; + Names GetLibraryNames(const std::string& config) const; /** * Compute whether this target must be relinked before installing. @@ -596,7 +617,7 @@ public: pdb output directory is given. */ std::string GetPDBDirectory(const std::string& config) const; - ///! Return the preferred linker language for this target + //! Return the preferred linker language for this target std::string GetLinkerLanguage(const std::string& config) const; /** Does this target have a GNU implib to convert to MS format? */ @@ -719,6 +740,11 @@ private: mutable std::map<std::string, bool> DebugCompatiblePropertiesDone; + const char* GetFilePrefixInternal(cmStateEnums::ArtifactType artifact, + const std::string& language = "") const; + const char* GetFileSuffixInternal(cmStateEnums::ArtifactType artifact, + const std::string& language = "") const; + std::string GetFullNameInternal(const std::string& config, cmStateEnums::ArtifactType artifact) const; void GetFullNameInternal(const std::string& config, diff --git a/Source/cmGetPipes.cxx b/Source/cmGetPipes.cxx new file mode 100644 index 0000000..ad323f7 --- /dev/null +++ b/Source/cmGetPipes.cxx @@ -0,0 +1,48 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmGetPipes.h" + +#include "cm_uv.h" + +#include <fcntl.h> + +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <io.h> + +int cmGetPipes(int* fds) +{ + SECURITY_ATTRIBUTES attr; + HANDLE readh, writeh; + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = nullptr; + attr.bInheritHandle = FALSE; + if (!CreatePipe(&readh, &writeh, &attr, 0)) + return uv_translate_sys_error(GetLastError()); + fds[0] = _open_osfhandle((intptr_t)readh, 0); + fds[1] = _open_osfhandle((intptr_t)writeh, 0); + if (fds[0] == -1 || fds[1] == -1) { + CloseHandle(readh); + CloseHandle(writeh); + return uv_translate_sys_error(GetLastError()); + } + return 0; +} +#else +# include <errno.h> +# include <unistd.h> + +int cmGetPipes(int* fds) +{ + if (pipe(fds) == -1) { + return uv_translate_sys_error(errno); + } + + if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || + fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { + close(fds[0]); + close(fds[1]); + return uv_translate_sys_error(errno); + } + return 0; +} +#endif diff --git a/Source/cmGetPipes.h b/Source/cmGetPipes.h new file mode 100644 index 0000000..2a46b51 --- /dev/null +++ b/Source/cmGetPipes.h @@ -0,0 +1,8 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmGetPipes_h +#define cmGetPipes_h + +int cmGetPipes(int* fds); + +#endif diff --git a/Source/cmGhsMultiGpj.cxx b/Source/cmGhsMultiGpj.cxx index c1f0742..da27971 100644 --- a/Source/cmGhsMultiGpj.cxx +++ b/Source/cmGhsMultiGpj.cxx @@ -2,16 +2,17 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGhsMultiGpj.h" -#include "cmGeneratedFileStream.h" +#include <ostream> static const char* GHS_TAG[] = { "[INTEGRITY Application]", "[Library]", "[Project]", "[Program]", "[Reference]", - "[Subproject]" }; + "[Subproject]", + "[Custom Target]" }; -const char* GhsMultiGpj::GetGpjTag(Types const gpjType) +const char* GhsMultiGpj::GetGpjTag(Types gpjType) { char const* tag; switch (gpjType) { @@ -21,6 +22,7 @@ const char* GhsMultiGpj::GetGpjTag(Types const gpjType) case PROGRAM: case REFERENCE: case SUBPROJECT: + case CUSTOM_TARGET: tag = GHS_TAG[gpjType]; break; default: @@ -29,7 +31,7 @@ const char* GhsMultiGpj::GetGpjTag(Types const gpjType) return tag; } -void GhsMultiGpj::WriteGpjTag(Types const gpjType, std::ostream& fout) +void GhsMultiGpj::WriteGpjTag(Types gpjType, std::ostream& fout) { char const* tag; tag = GhsMultiGpj::GetGpjTag(gpjType); diff --git a/Source/cmGhsMultiGpj.h b/Source/cmGhsMultiGpj.h index 6d59225..e588150 100644 --- a/Source/cmGhsMultiGpj.h +++ b/Source/cmGhsMultiGpj.h @@ -6,8 +6,6 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <iosfwd> -class cmGeneratedFileStream; - class GhsMultiGpj { public: @@ -18,12 +16,13 @@ public: PROJECT, PROGRAM, REFERENCE, - SUBPROJECT + SUBPROJECT, + CUSTOM_TARGET }; - static void WriteGpjTag(Types const gpjType, std::ostream& fout); + static void WriteGpjTag(Types gpjType, std::ostream& fout); - static const char* GetGpjTag(Types const gpjType); + static const char* GetGpjTag(Types gpjType); }; #endif // ! cmGhsMultiGpjType_h diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index 5fe350c..b80da72 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -2,23 +2,41 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGhsMultiTargetGenerator.h" -#include "cmComputeLinkInformation.h" +#include "cmCustomCommand.h" +#include "cmCustomCommandGenerator.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalGhsMultiGenerator.h" #include "cmLinkLineComputer.h" +#include "cmLocalGenerator.h" #include "cmLocalGhsMultiGenerator.h" #include "cmMakefile.h" +#include "cmOutputConverter.h" #include "cmSourceFile.h" +#include "cmSourceFileLocation.h" #include "cmSourceGroup.h" +#include "cmStateDirectory.h" +#include "cmStateSnapshot.h" +#include "cmStateTypes.h" +#include "cmSystemTools.h" #include "cmTarget.h" +#include <algorithm> +#include <ostream> +#include <set> +#include <utility> + cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target) : GeneratorTarget(target) , LocalGenerator( static_cast<cmLocalGhsMultiGenerator*>(target->GetLocalGenerator())) , Makefile(target->Target->GetMakefile()) , Name(target->GetName()) +#ifdef _WIN32 + , CmdWindowsShell(true) +#else + , CmdWindowsShell(false) +#endif { // Store the configuration name that is being used if (const char* config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) { @@ -30,9 +48,7 @@ cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target) } } -cmGhsMultiTargetGenerator::~cmGhsMultiTargetGenerator() -{ -} +cmGhsMultiTargetGenerator::~cmGhsMultiTargetGenerator() = default; void cmGhsMultiTargetGenerator::Generate() { @@ -40,12 +56,8 @@ void cmGhsMultiTargetGenerator::Generate() switch (this->GeneratorTarget->GetType()) { case cmStateEnums::EXECUTABLE: { // Get the name of the executable to generate. - std::string targetName; - std::string targetNameImport; - std::string targetNamePDB; - this->GeneratorTarget->GetExecutableNames( - targetName, this->TargetNameReal, targetNameImport, targetNamePDB, - this->ConfigName); + this->TargetNameReal = + this->GeneratorTarget->GetExecutableNames(this->ConfigName).Real; if (cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()) { this->TagType = GhsMultiGpj::INTERGRITY_APPLICATION; } else { @@ -54,13 +66,8 @@ void cmGhsMultiTargetGenerator::Generate() break; } case cmStateEnums::STATIC_LIBRARY: { - std::string targetName; - std::string targetNameSO; - std::string targetNameImport; - std::string targetNamePDB; - this->GeneratorTarget->GetLibraryNames( - targetName, targetNameSO, this->TargetNameReal, targetNameImport, - targetNamePDB, this->ConfigName); + this->TargetNameReal = + this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real; this->TagType = GhsMultiGpj::LIBRARY; break; } @@ -71,13 +78,8 @@ void cmGhsMultiTargetGenerator::Generate() return; } case cmStateEnums::OBJECT_LIBRARY: { - std::string targetName; - std::string targetNameSO; - std::string targetNameImport; - std::string targetNamePDB; - this->GeneratorTarget->GetLibraryNames( - targetName, targetNameSO, this->TargetNameReal, targetNameImport, - targetNamePDB, this->ConfigName); + this->TargetNameReal = + this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real; this->TagType = GhsMultiGpj::SUBPROJECT; break; } @@ -88,10 +90,19 @@ void cmGhsMultiTargetGenerator::Generate() return; } case cmStateEnums::UTILITY: { - std::string msg = "add_custom_target(<name> ...) not supported: "; - msg += this->Name; - cmSystemTools::Message(msg); - return; + this->TargetNameReal = this->GeneratorTarget->GetName(); + this->TagType = GhsMultiGpj::CUSTOM_TARGET; + break; + } + case cmStateEnums::GLOBAL_TARGET: { + this->TargetNameReal = this->GeneratorTarget->GetName(); + if (this->TargetNameReal == + this->GetGlobalGenerator()->GetInstallTargetName()) { + this->TagType = GhsMultiGpj::CUSTOM_TARGET; + } else { + return; + } + break; } default: return; @@ -108,29 +119,29 @@ void cmGhsMultiTargetGenerator::Generate() void cmGhsMultiTargetGenerator::GenerateTarget() { - // Open the filestream in copy-if-different mode. - std::string fname = this->LocalGenerator->GetCurrentBinaryDirectory(); - fname += "/"; - fname += this->Name; - fname += cmGlobalGhsMultiGenerator::FILE_EXTENSION; - cmGeneratedFileStream fout(fname.c_str()); + // Open the target file in copy-if-different mode. + std::string fproj = this->LocalGenerator->GetCurrentBinaryDirectory(); + fproj += "/"; + fproj += this->Name; + fproj += cmGlobalGhsMultiGenerator::FILE_EXTENSION; + cmGeneratedFileStream fout(fproj); fout.SetCopyIfDifferent(true); this->GetGlobalGenerator()->WriteFileHeader(fout); GhsMultiGpj::WriteGpjTag(this->TagType, fout); - const std::string language( - this->GeneratorTarget->GetLinkerLanguage(this->ConfigName)); - - this->WriteTargetSpecifics(fout, this->ConfigName); - this->SetCompilerFlags(this->ConfigName, language); - this->WriteCompilerFlags(fout, this->ConfigName, language); - this->WriteCompilerDefinitions(fout, this->ConfigName, language); - this->WriteIncludes(fout, this->ConfigName, language); - this->WriteTargetLinkLine(fout, this->ConfigName); - this->WriteCustomCommands(fout); + if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { + const std::string language( + this->GeneratorTarget->GetLinkerLanguage(this->ConfigName)); + this->WriteTargetSpecifics(fout, this->ConfigName); + this->SetCompilerFlags(this->ConfigName, language); + this->WriteCompilerFlags(fout, this->ConfigName, language); + this->WriteCompilerDefinitions(fout, this->ConfigName, language); + this->WriteIncludes(fout, this->ConfigName, language); + this->WriteTargetLinkLine(fout, this->ConfigName); + this->WriteBuildEvents(fout); + } this->WriteSources(fout); - this->WriteReferences(fout); fout.Close(); } @@ -224,7 +235,7 @@ void cmGhsMultiTargetGenerator::WriteCompilerFlags(std::ostream& fout, if (flagsByLangI != this->FlagsByLanguage.end()) { if (!flagsByLangI->second.empty()) { std::vector<std::string> ghsCompFlags = - cmSystemTools::ParseArguments(flagsByLangI->second.c_str()); + cmSystemTools::ParseArguments(flagsByLangI->second); for (auto& f : ghsCompFlags) { fout << " " << f << std::endl; } @@ -238,10 +249,8 @@ void cmGhsMultiTargetGenerator::WriteCompilerDefinitions( std::vector<std::string> compileDefinitions; this->GeneratorTarget->GetCompileDefinitions(compileDefinitions, config, language); - for (std::vector<std::string>::const_iterator cdI = - compileDefinitions.begin(); - cdI != compileDefinitions.end(); ++cdI) { - fout << " -D" << (*cdI) << std::endl; + for (std::string const& compileDefinition : compileDefinitions) { + fout << " -D" << compileDefinition << std::endl; } } @@ -253,9 +262,8 @@ void cmGhsMultiTargetGenerator::WriteIncludes(std::ostream& fout, this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, language, config); - for (std::vector<std::string>::const_iterator includes_i = includes.begin(); - includes_i != includes.end(); ++includes_i) { - fout << " -I\"" << *includes_i << "\"" << std::endl; + for (std::string const& include : includes) { + fout << " -I\"" << include << "\"" << std::endl; } } @@ -282,16 +290,14 @@ void cmGhsMultiTargetGenerator::WriteTargetLinkLine(std::ostream& fout, frameworkPath, linkPath, this->GeneratorTarget); // write out link options - std::vector<std::string> lopts = - cmSystemTools::ParseArguments(linkFlags.c_str()); + std::vector<std::string> lopts = cmSystemTools::ParseArguments(linkFlags); for (auto& l : lopts) { fout << " " << l << std::endl; } // write out link search paths // must be quoted for paths that contain spaces - std::vector<std::string> lpath = - cmSystemTools::ParseArguments(linkPath.c_str()); + std::vector<std::string> lpath = cmSystemTools::ParseArguments(linkPath); for (auto& l : lpath) { fout << " -L\"" << l << "\"" << std::endl; } @@ -301,68 +307,161 @@ void cmGhsMultiTargetGenerator::WriteTargetLinkLine(std::ostream& fout, std::string cbd = this->LocalGenerator->GetCurrentBinaryDirectory(); std::vector<std::string> llibs = - cmSystemTools::ParseArguments(linkLibraries.c_str()); + cmSystemTools::ParseArguments(linkLibraries); for (auto& l : llibs) { if (l.compare(0, 2, "-l") == 0) { fout << " \"" << l << "\"" << std::endl; } else { - std::string rl = cmSystemTools::CollapseCombinedPath(cbd, l); + std::string rl = cmSystemTools::CollapseFullPath(l, cbd); fout << " -l\"" << rl << "\"" << std::endl; } } } -void cmGhsMultiTargetGenerator::WriteCustomCommands(std::ostream& fout) +void cmGhsMultiTargetGenerator::WriteBuildEvents(std::ostream& fout) { - WriteCustomCommandsHelper(fout, this->GeneratorTarget->GetPreBuildCommands(), - cmTarget::PRE_BUILD); - WriteCustomCommandsHelper( - fout, this->GeneratorTarget->GetPostBuildCommands(), cmTarget::POST_BUILD); + this->WriteBuildEventsHelper( + fout, this->GeneratorTarget->GetPreBuildCommands(), + std::string("prebuild"), std::string("preexecShell")); + + if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { + this->WriteBuildEventsHelper( + fout, this->GeneratorTarget->GetPreLinkCommands(), + std::string("prelink"), std::string("preexecShell")); + } + + this->WriteBuildEventsHelper( + fout, this->GeneratorTarget->GetPostBuildCommands(), + std::string("postbuild"), std::string("postexecShell")); +} + +void cmGhsMultiTargetGenerator::WriteBuildEventsHelper( + std::ostream& fout, const std::vector<cmCustomCommand>& ccv, + std::string const& name, std::string const& cmd) +{ + int cmdcount = 0; + + for (cmCustomCommand const& cc : ccv) { + cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator); + // Open the filestream for this custom command + std::string fname = this->LocalGenerator->GetCurrentBinaryDirectory(); + fname += + "/" + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + fname += "/" + this->Name + "_" + name; + fname += std::to_string(cmdcount++); + fname += this->CmdWindowsShell ? ".bat" : ".sh"; + cmGeneratedFileStream f(fname); + f.SetCopyIfDifferent(true); + this->WriteCustomCommandsHelper(f, ccg); + f.Close(); + if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { + fout << " :" << cmd << "=\"" << fname << "\"" << std::endl; + } else { + fout << fname << std::endl; + fout << " :outputName=\"" << fname << ".rule\"" << std::endl; + } + for (auto& byp : ccg.GetByproducts()) { + fout << " :extraOutputFile=\"" << byp << "\"" << std::endl; + } + } } void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper( - std::ostream& fout, std::vector<cmCustomCommand> const& commandsSet, - cmTarget::CustomCommandType const commandType) + std::ostream& fout, cmCustomCommandGenerator const& ccg) { - for (std::vector<cmCustomCommand>::const_iterator commandsSetI = - commandsSet.begin(); - commandsSetI != commandsSet.end(); ++commandsSetI) { - cmCustomCommandLines const& commands = commandsSetI->GetCommandLines(); - for (cmCustomCommandLines::const_iterator commandI = commands.begin(); - commandI != commands.end(); ++commandI) { - switch (commandType) { - case cmTarget::PRE_BUILD: - fout << " :preexecShellSafe="; - break; - case cmTarget::POST_BUILD: - fout << " :postexecShellSafe="; - break; - default: - assert("Only pre and post are supported"); - } - cmCustomCommandLine const& command = *commandI; - for (cmCustomCommandLine::const_iterator commandLineI = command.begin(); - commandLineI != command.end(); ++commandLineI) { - std::string subCommandE = - this->LocalGenerator->EscapeForShell(*commandLineI, true); - if (!command.empty()) { - fout << (command.begin() == commandLineI ? "'" : " "); - // Need to double escape backslashes - cmSystemTools::ReplaceString(subCommandE, "\\", "\\\\"); + std::vector<std::string> cmdLines; + + // if the command specified a working directory use it. + std::string dir = this->LocalGenerator->GetCurrentBinaryDirectory(); + std::string currentBinDir = dir; + std::string workingDir = ccg.GetWorkingDirectory(); + if (!workingDir.empty()) { + dir = workingDir; + } + + // Line to check for error between commands. +#ifdef _WIN32 + std::string check_error = "if %errorlevel% neq 0 exit /b %errorlevel%"; +#else + std::string check_error = "if [[ $? -ne 0 ]]; then exit 1; fi"; +#endif + +#ifdef _WIN32 + cmdLines.push_back("@echo off"); +#endif + // Echo the custom command's comment text. + const char* comment = ccg.GetComment(); + if (comment && *comment) { + std::string echocmd = "echo "; + echocmd += comment; + cmdLines.push_back(std::move(echocmd)); + } + + // Switch to working directory + std::string cdCmd; +#ifdef _WIN32 + std::string cdStr = "cd /D "; +#else + std::string cdStr = "cd "; +#endif + cdCmd = cdStr + + this->LocalGenerator->ConvertToOutputFormat(dir, cmOutputConverter::SHELL); + cmdLines.push_back(std::move(cdCmd)); + + for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) { + // Build the command line in a single string. + std::string cmd = ccg.GetCommand(c); + if (!cmd.empty()) { + // Use "call " before any invocations of .bat or .cmd files + // invoked as custom commands in the WindowsShell. + // + bool useCall = false; + + if (this->CmdWindowsShell) { + std::string suffix; + if (cmd.size() > 4) { + suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4)); + if (suffix == ".bat" || suffix == ".cmd") { + useCall = true; + } } - fout << subCommandE; } - if (!command.empty()) { - fout << "'" << std::endl; + + cmSystemTools::ReplaceString(cmd, "/./", "/"); + // Convert the command to a relative path only if the current + // working directory will be the start-output directory. + bool had_slash = cmd.find('/') != std::string::npos; + if (workingDir.empty()) { + cmd = + this->LocalGenerator->MaybeConvertToRelativePath(currentBinDir, cmd); + } + bool has_slash = cmd.find('/') != std::string::npos; + if (had_slash && !has_slash) { + // This command was specified as a path to a file in the + // current directory. Add a leading "./" so it can run + // without the current directory being in the search path. + cmd = "./" + cmd; } + cmd = this->LocalGenerator->ConvertToOutputFormat( + cmd, cmOutputConverter::SHELL); + if (useCall) { + cmd = "call " + cmd; + } + ccg.AppendArguments(c, cmd); + cmdLines.push_back(std::move(cmd)); } } + + // push back the custom commands + for (auto const& c : cmdLines) { + fout << c << std::endl; + fout << check_error << std::endl; + } } -void cmGhsMultiTargetGenerator::WriteSourceProperty(std::ostream& fout, - const cmSourceFile* sf, - std::string propName, - std::string propFlag) +void cmGhsMultiTargetGenerator::WriteSourceProperty( + std::ostream& fout, const cmSourceFile* sf, std::string const& propName, + std::string const& propFlag) { const char* prop = sf->GetProperty(propName); if (prop) { @@ -393,12 +492,12 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) this->Makefile->FindSourceGroup(sf->GetFullPath(), sourceGroups); std::string gn = sourceGroup->GetFullName(); groupFiles[gn].push_back(sf); - groupNames.insert(gn); + groupNames.insert(std::move(gn)); } /* list of known groups and the order they are displayed in a project file */ const std::vector<std::string> standardGroups = { - "Header Files", "Source Files", "CMake Rules", + "CMake Rules", "Header Files", "Source Files", "Object Files", "Object Libraries", "Resources" }; @@ -416,11 +515,19 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) groupFilesList[i] = *n; i += 1; groupNames.erase(gn); + } else if (this->TagType == GhsMultiGpj::CUSTOM_TARGET && + gn == "CMake Rules") { + /* make sure that rules folder always exists in case of custom targets + * that have no custom commands except for pre or post build events. + */ + groupFilesList.resize(groupFilesList.size() + 1); + groupFilesList[i] = gn; + i += 1; } } { /* catch-all group - is last item */ - std::string gn = ""; + std::string gn; auto n = groupNames.find(gn); if (n != groupNames.end()) { groupFilesList.back() = *n; @@ -446,7 +553,7 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) /* write files into the proper project file * -- groups go into main project file - * unless FOLDER property or variable is set. + * unless NO_SOURCE_GROUP_FILE property or variable is set. */ for (auto& sg : groupFilesList) { std::ostream* fout; @@ -469,7 +576,7 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) std::string fpath = this->LocalGenerator->GetCurrentBinaryDirectory(); fpath += "/"; fpath += lpath; - cmGeneratedFileStream* f = new cmGeneratedFileStream(fpath.c_str()); + cmGeneratedFileStream* f = new cmGeneratedFileStream(fpath); f->SetCopyIfDifferent(true); gfiles.push_back(f); fout = f; @@ -485,33 +592,98 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) } else { *fout << "{comment} " << sg << std::endl; } + } else if (sg.empty()) { + *fout << "{comment} Others" << std::endl; } - /* output rule for each source file */ - for (const cmSourceFile* si : groupFiles[sg]) { - - // Convert filename to native system - // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on - // windows when opening some files from the search window. - std::string fname(si->GetFullPath()); - cmSystemTools::ConvertToOutputSlashes(fname); - *fout << fname << std::endl; + if (sg != "CMake Rules") { + /* output rule for each source file */ + for (const cmSourceFile* si : groupFiles[sg]) { + bool compile = true; + // Convert filename to native system + // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on + // windows when opening some files from the search window. + std::string fname(si->GetFullPath()); + cmSystemTools::ConvertToOutputSlashes(fname); + + /* For custom targets list any associated sources, + * comment out source code to prevent it from being + * compiled when processing this target. + * Otherwise, comment out any custom command (main) dependencies that + * are listed as source files to prevent them from being considered + * part of the build. + */ + std::string comment; + if ((this->TagType == GhsMultiGpj::CUSTOM_TARGET && + !si->GetLanguage().empty()) || + si->GetCustomCommand()) { + comment = "{comment} "; + compile = false; + } - if ("ld" != si->GetExtension() && "int" != si->GetExtension() && - "bsp" != si->GetExtension()) { - this->WriteObjectLangOverride(*fout, si); + *fout << comment << fname << std::endl; + if (compile) { + if ("ld" != si->GetExtension() && "int" != si->GetExtension() && + "bsp" != si->GetExtension()) { + WriteObjectLangOverride(*fout, si); + } + + this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); + this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); + this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); + + /* to avoid clutter in the GUI only print out the objectName if it + * has been renamed */ + std::string objectName = this->GeneratorTarget->GetObjectName(si); + if (!objectName.empty() && + this->GeneratorTarget->HasExplicitObjectName(si)) { + *fout << " -o " << objectName << std::endl; + } + } } - - this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); - this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); - this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); - - /* to avoid clutter in the gui only print out the objectName if it has - * been renamed */ - std::string objectName = this->GeneratorTarget->GetObjectName(si); - if (!objectName.empty() && - this->GeneratorTarget->HasExplicitObjectName(si)) { - *fout << " -o " << objectName << std::endl; + } else { + std::vector<cmSourceFile const*> customCommands; + if (ComputeCustomCommandOrder(customCommands)) { + std::string message = "The custom commands for target [" + + this->GeneratorTarget->GetName() + "] had a cycle.\n"; + cmSystemTools::Error(message); + } else { + /* Custom targets do not have a dependency on SOURCES files. + * Therefore the dependency list may include SOURCES files after the + * custom target. Because nothing can depend on the custom target just + * move it to the last item. + */ + for (auto sf = customCommands.begin(); sf != customCommands.end(); + ++sf) { + if (((*sf)->GetLocation()).GetName() == this->Name + ".rule") { + std::rotate(sf, sf + 1, customCommands.end()); + break; + } + } + int cmdcount = 0; + for (auto& sf : customCommands) { + const cmCustomCommand* cc = sf->GetCustomCommand(); + cmCustomCommandGenerator ccg(*cc, this->ConfigName, + this->LocalGenerator); + + // Open the filestream for this custom command + std::string fname = + this->LocalGenerator->GetCurrentBinaryDirectory(); + fname += "/" + + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + fname += "/" + this->Name + "_cc"; + fname += std::to_string(cmdcount++) + "_"; + fname += (sf->GetLocation()).GetName(); + fname += this->CmdWindowsShell ? ".bat" : ".sh"; + cmGeneratedFileStream f(fname); + f.SetCopyIfDifferent(true); + this->WriteCustomCommandsHelper(f, ccg); + f.Close(); + this->WriteCustomCommandLine(*fout, fname, ccg); + } + } + if (this->TagType == GhsMultiGpj::CUSTOM_TARGET) { + this->WriteBuildEvents(*fout); } } } @@ -521,62 +693,107 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) } } +void cmGhsMultiTargetGenerator::WriteCustomCommandLine( + std::ostream& fout, std::string& fname, cmCustomCommandGenerator const& ccg) +{ + /* NOTE: Customization Files are not well documented. Testing showed + * that ":outputName=file" can only be used once per script. The + * script will only run if ":outputName=file" is missing or just run + * once if ":outputName=file" is not specified. If there are + * multiple outputs then the script needs to be listed multiple times + * for each output. Otherwise it won't rerun the script if one of + * the outputs is manually deleted. + */ + bool specifyExtra = true; + for (auto& out : ccg.GetOutputs()) { + fout << fname << std::endl; + fout << " :outputName=\"" << out << "\"" << std::endl; + if (specifyExtra) { + for (auto& byp : ccg.GetByproducts()) { + fout << " :extraOutputFile=\"" << byp << "\"" << std::endl; + } + for (auto& dep : ccg.GetDepends()) { + fout << " :depends=\"" << dep << "\"" << std::endl; + } + specifyExtra = false; + } + } +} + void cmGhsMultiTargetGenerator::WriteObjectLangOverride( std::ostream& fout, const cmSourceFile* sourceFile) { const char* rawLangProp = sourceFile->GetProperty("LANGUAGE"); - if (NULL != rawLangProp) { + if (nullptr != rawLangProp) { std::string sourceLangProp(rawLangProp); - std::string extension(sourceFile->GetExtension()); + std::string const& extension = sourceFile->GetExtension(); if ("CXX" == sourceLangProp && ("c" == extension || "C" == extension)) { fout << " -dotciscxx" << std::endl; } } } -void cmGhsMultiTargetGenerator::WriteReferences(std::ostream& fout) +bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp() { - // This only applies to INTEGRITY Applications - if (this->TagType != GhsMultiGpj::INTERGRITY_APPLICATION) { - return; + const char* p = this->GeneratorTarget->GetProperty("ghs_integrity_app"); + if (p) { + return cmSystemTools::IsOn( + this->GeneratorTarget->GetProperty("ghs_integrity_app")); } + std::vector<cmSourceFile*> sources; + this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName); + for (auto& sf : sources) { + if ("int" == sf->GetExtension()) { + return true; + } + } + return false; +} - // Get the targets that this one depends upon - cmTargetDependSet unordered = - this->GetGlobalGenerator()->GetTargetDirectDepends(this->GeneratorTarget); - cmGlobalGhsMultiGenerator::OrderedTargetDependSet ordered(unordered, - this->Name); - for (auto& t : ordered) { - std::string tname = t->GetName(); - std::string tpath = t->LocalGenerator->GetCurrentBinaryDirectory(); - std::string rootpath = this->LocalGenerator->GetCurrentBinaryDirectory(); - std::string outpath = - this->LocalGenerator->MaybeConvertToRelativePath(rootpath, tpath) + "/" + - tname + "REF" + cmGlobalGhsMultiGenerator::FILE_EXTENSION; +bool cmGhsMultiTargetGenerator::ComputeCustomCommandOrder( + std::vector<cmSourceFile const*>& order) +{ + std::set<cmSourceFile const*> temp; + std::set<cmSourceFile const*> perm; - fout << outpath; - fout << " "; - GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fout); + // Collect all custom commands for this target + std::vector<cmSourceFile const*> customCommands; + this->GeneratorTarget->GetCustomCommands(customCommands, this->ConfigName); - // Tell the global generator that a refernce project needs to be created - t->Target->SetProperty("GHS_REFERENCE_PROJECT", "ON"); + for (cmSourceFile const* si : customCommands) { + bool r = VisitCustomCommand(temp, perm, order, si); + if (r) { + return r; + } } + return false; } -bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp(void) +bool cmGhsMultiTargetGenerator::VisitCustomCommand( + std::set<cmSourceFile const*>& temp, std::set<cmSourceFile const*>& perm, + std::vector<cmSourceFile const*>& order, cmSourceFile const* si) { - const char* p = this->GeneratorTarget->GetProperty("ghs_integrity_app"); - if (p) { - return cmSystemTools::IsOn( - this->GeneratorTarget->GetProperty("ghs_integrity_app")); - } else { - std::vector<cmSourceFile*> sources; - this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName); - for (auto& sf : sources) { - if ("int" == sf->GetExtension()) { - return true; + /* check if permanent mark is set*/ + if (perm.find(si) == perm.end()) { + /* set temporary mark; check if revisit*/ + if (temp.insert(si).second) { + for (auto& di : si->GetCustomCommand()->GetDepends()) { + cmSourceFile const* sf = this->GeneratorTarget->GetLocalGenerator() + ->GetMakefile() + ->GetSourceFileWithOutput(di); + /* if sf exists then visit */ + if (sf && this->VisitCustomCommand(temp, perm, order, sf)) { + return true; + } } + /* mark as complete; insert into beginning of list*/ + perm.insert(si); + order.push_back(si); + return false; } - return false; + /* revisiting item - not a DAG */ + return true; } + /* already complete */ + return false; } diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h index a241cc6..a131567 100644 --- a/Source/cmGhsMultiTargetGenerator.h +++ b/Source/cmGhsMultiTargetGenerator.h @@ -5,10 +5,14 @@ #include "cmGhsMultiGpj.h" -#include "cmTarget.h" +#include <iosfwd> +#include <map> +#include <set> +#include <string> +#include <vector> class cmCustomCommand; -class cmGeneratedFileStream; +class cmCustomCommandGenerator; class cmGeneratorTarget; class cmGlobalGhsMultiGenerator; class cmLocalGhsMultiGenerator; @@ -45,18 +49,27 @@ private: void WriteIncludes(std::ostream& fout, const std::string& config, const std::string& language); void WriteTargetLinkLine(std::ostream& fout, std::string const& config); - void WriteCustomCommands(std::ostream& fout); - void WriteCustomCommandsHelper( - std::ostream& fout, std::vector<cmCustomCommand> const& commandsSet, - cmTarget::CustomCommandType commandType); + void WriteBuildEvents(std::ostream& fout); + void WriteBuildEventsHelper(std::ostream& fout, + const std::vector<cmCustomCommand>& ccv, + std::string const& name, std::string const& cmd); + void WriteCustomCommandsHelper(std::ostream& fout, + cmCustomCommandGenerator const& ccg); + void WriteCustomCommandLine(std::ostream& fout, std::string& fname, + cmCustomCommandGenerator const& ccg); + bool ComputeCustomCommandOrder(std::vector<cmSourceFile const*>& order); + bool VisitCustomCommand(std::set<cmSourceFile const*>& temp, + std::set<cmSourceFile const*>& perm, + std::vector<cmSourceFile const*>& order, + cmSourceFile const* sf); void WriteSources(std::ostream& fout_proj); void WriteSourceProperty(std::ostream& fout, const cmSourceFile* sf, - std::string propName, std::string propFlag); - void WriteReferences(std::ostream& fout); + std::string const& propName, + std::string const& propFlag); static void WriteObjectLangOverride(std::ostream& fout, const cmSourceFile* sourceFile); - bool DetermineIfIntegrityApp(void); + bool DetermineIfIntegrityApp(); cmGeneratorTarget* GeneratorTarget; cmLocalGhsMultiGenerator* LocalGenerator; cmMakefile* Makefile; @@ -66,7 +79,8 @@ private: std::string TargetNameReal; GhsMultiGpj::Types TagType; std::string const Name; - std::string ConfigName; /* CMAKE_BUILD_TYPE */ + std::string ConfigName; /* CMAKE_BUILD_TYPE */ + bool const CmdWindowsShell; /* custom commands run in cmd.exe or /bin/sh */ }; #endif // ! cmGhsMultiTargetGenerator_h diff --git a/Source/cmGlobVerificationManager.cxx b/Source/cmGlobVerificationManager.cxx index 5fd890e..9fb4170 100644 --- a/Source/cmGlobVerificationManager.cxx +++ b/Source/cmGlobVerificationManager.cxx @@ -25,8 +25,8 @@ bool cmGlobVerificationManager::SaveVerificationScript(const std::string& path) cmGeneratedFileStream verifyScriptFile(scriptFile); verifyScriptFile.SetCopyIfDifferent(true); if (!verifyScriptFile) { - cmSystemTools::Error("Unable to open verification script file for save. ", - scriptFile.c_str()); + cmSystemTools::Error("Unable to open verification script file for save. " + + scriptFile); cmSystemTools::ReportLastSystemError(""); return false; } @@ -71,8 +71,8 @@ bool cmGlobVerificationManager::SaveVerificationScript(const std::string& path) cmsys::ofstream verifyStampFile(stampFile.c_str()); if (!verifyStampFile) { - cmSystemTools::Error("Unable to open verification stamp file for write. ", - stampFile.c_str()); + cmSystemTools::Error("Unable to open verification stamp file for write. " + + stampFile); return false; } verifyStampFile << "# This file is generated by CMake for checking of the " diff --git a/Source/cmGlobVerificationManager.h b/Source/cmGlobVerificationManager.h index f7146be..48606d3 100644 --- a/Source/cmGlobVerificationManager.h +++ b/Source/cmGlobVerificationManager.h @@ -22,11 +22,11 @@ class cmGlobVerificationManager { protected: - ///! Save verification script for given makefile. - ///! Saves to output <path>/<CMakeFilesDirectory>/VerifyGlobs.cmake + //! Save verification script for given makefile. + //! Saves to output <path>/<CMakeFilesDirectory>/VerifyGlobs.cmake bool SaveVerificationScript(const std::string& path); - ///! Add an entry into the glob cache + //! Add an entry into the glob cache void AddCacheEntry(bool recurse, bool listDirectories, bool followSymlinks, const std::string& relative, const std::string& expression, @@ -34,13 +34,13 @@ protected: const std::string& variable, const cmListFileBacktrace& bt); - ///! Clear the glob cache for state reset. + //! Clear the glob cache for state reset. void Reset(); - ///! Check targets should be written in generated build system. + //! Check targets should be written in generated build system. bool DoWriteVerifyTarget() const; - ///! Get the paths to the generated script and stamp files + //! Get the paths to the generated script and stamp files std::string const& GetVerifyScript() const { return this->VerifyScript; } std::string const& GetVerifyStamp() const { return this->VerifyStamp; } diff --git a/Source/cmGlobalBorlandMakefileGenerator.cxx b/Source/cmGlobalBorlandMakefileGenerator.cxx index c2eb583..51d681d 100644 --- a/Source/cmGlobalBorlandMakefileGenerator.cxx +++ b/Source/cmGlobalBorlandMakefileGenerator.cxx @@ -34,7 +34,7 @@ void cmGlobalBorlandMakefileGenerator::EnableLanguage( this->cmGlobalUnixMakefileGenerator3::EnableLanguage(l, mf, optional); } -///! Create a local generator appropriate to this Global Generator +//! Create a local generator appropriate to this Global Generator cmLocalGenerator* cmGlobalBorlandMakefileGenerator::CreateLocalGenerator( cmMakefile* mf) { @@ -53,15 +53,16 @@ void cmGlobalBorlandMakefileGenerator::GetDocumentation( entry.Brief = "Generates Borland makefiles."; } -void cmGlobalBorlandMakefileGenerator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& projectName, const std::string& projectDir, - const std::string& targetName, const std::string& config, bool fast, - int /*jobs*/, bool verbose, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalBorlandMakefileGenerator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int /*jobs*/, bool verbose, + std::vector<std::string> const& makeOptions) { - this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( - makeCommand, makeProgram, projectName, projectDir, targetName, config, - fast, cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions); + return this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( + makeProgram, projectName, projectDir, targetNames, config, fast, + cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions); } void cmGlobalBorlandMakefileGenerator::PrintBuildCommandAdvice( diff --git a/Source/cmGlobalBorlandMakefileGenerator.h b/Source/cmGlobalBorlandMakefileGenerator.h index ca04b7b..ee7de70 100644 --- a/Source/cmGlobalBorlandMakefileGenerator.h +++ b/Source/cmGlobalBorlandMakefileGenerator.h @@ -22,7 +22,7 @@ public: cmGlobalBorlandMakefileGenerator>(); } - ///! Get the name for the generator. + //! Get the name for the generator. std::string GetName() const override { return cmGlobalBorlandMakefileGenerator::GetActualName(); @@ -32,7 +32,7 @@ public: /** Get the documentation entry for this generator. */ static void GetDocumentation(cmDocumentationEntry& entry); - ///! Create a local generator appropriate to this Global Generator + //! Create a local generator appropriate to this Global Generator cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override; /** @@ -46,15 +46,12 @@ public: bool AllowDeleteOnError() const override { return false; } protected: - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override; }; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 386a3f7..8223dd1 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -7,6 +7,7 @@ #include <algorithm> #include <assert.h> #include <cstring> +#include <initializer_list> #include <iterator> #include <sstream> #include <stdio.h> @@ -34,6 +35,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStateDirectory.h" @@ -211,7 +213,7 @@ void cmGlobalGenerator::ResolveLanguageCompiler(const std::string& lang, if (!mf->GetDefinition(langComp)) { if (!optional) { - cmSystemTools::Error(langComp.c_str(), " not set, after EnableLanguage"); + cmSystemTools::Error(langComp + " not set, after EnableLanguage"); } return; } @@ -270,8 +272,7 @@ void cmGlobalGenerator::AddBuildExportExportSet( bool cmGlobalGenerator::GenerateImportFile(const std::string& file) { - std::map<std::string, cmExportBuildFileGenerator*>::iterator it = - this->BuildExportSets.find(file); + auto const it = this->BuildExportSets.find(file); if (it != this->BuildExportSets.end()) { bool result = it->second->GenerateImportFile(); @@ -297,10 +298,7 @@ bool cmGlobalGenerator::CheckTargetsForMissingSources() const { bool failed = false; for (cmLocalGenerator* localGen : this->LocalGenerators) { - const std::vector<cmGeneratorTarget*>& targets = - localGen->GetGeneratorTargets(); - - for (cmGeneratorTarget* target : targets) { + for (cmGeneratorTarget* target : localGen->GetGeneratorTargets()) { if (target->GetType() == cmStateEnums::TargetType::GLOBAL_TARGET || target->GetType() == cmStateEnums::TargetType::INTERFACE_LIBRARY || target->GetType() == cmStateEnums::TargetType::UTILITY || @@ -314,9 +312,11 @@ bool cmGlobalGenerator::CheckTargetsForMissingSources() const if (configs.empty()) { target->GetSourceFiles(srcs, ""); } else { - for (std::vector<std::string>::const_iterator ci = configs.begin(); - ci != configs.end() && srcs.empty(); ++ci) { - target->GetSourceFiles(srcs, *ci); + for (std::string const& config : configs) { + target->GetSourceFiles(srcs, config); + if (srcs.empty()) { + break; + } } } if (srcs.empty()) { @@ -334,8 +334,7 @@ bool cmGlobalGenerator::CheckTargetsForMissingSources() const bool cmGlobalGenerator::IsExportedTargetsFile( const std::string& filename) const { - const std::map<std::string, cmExportBuildFileGenerator*>::const_iterator it = - this->BuildExportSets.find(filename); + auto const it = this->BuildExportSets.find(filename); if (it == this->BuildExportSets.end()) { return false; } @@ -635,8 +634,7 @@ void cmGlobalGenerator::EnableLanguage( // to avoid duplicate compiler tests. if (cmSystemTools::FileExists(fpath)) { if (!mf->ReadListFile(fpath)) { - cmSystemTools::Error("Could not find cmake module file: ", - fpath.c_str()); + cmSystemTools::Error("Could not find cmake module file: " + fpath); } // if this file was found then the language was already determined // to be working @@ -661,8 +659,8 @@ void cmGlobalGenerator::EnableLanguage( determineCompiler += "Compiler.cmake"; std::string determineFile = mf->GetModulesFile(determineCompiler); if (!mf->ReadListFile(determineFile)) { - cmSystemTools::Error("Could not find cmake module file: ", - determineCompiler.c_str()); + cmSystemTools::Error("Could not find cmake module file: " + + determineCompiler); } if (cmSystemTools::GetFatalErrorOccured()) { return; @@ -680,8 +678,9 @@ void cmGlobalGenerator::EnableLanguage( std::string compilerEnv = "CMAKE_"; compilerEnv += lang; compilerEnv += "_COMPILER_ENV_VAR"; - std::string envVar = mf->GetRequiredDefinition(compilerEnv); - std::string envVarValue = mf->GetRequiredDefinition(compilerName); + const std::string& envVar = mf->GetRequiredDefinition(compilerEnv); + const std::string& envVarValue = + mf->GetRequiredDefinition(compilerName); std::string env = envVar; env += "="; env += envVarValue; @@ -695,8 +694,7 @@ void cmGlobalGenerator::EnableLanguage( fpath += lang; fpath += "Compiler.cmake"; if (!mf->ReadListFile(fpath)) { - cmSystemTools::Error("Could not find cmake module file: ", - fpath.c_str()); + cmSystemTools::Error("Could not find cmake module file: " + fpath); } this->SetLanguageEnabledFlag(lang, mf); needSetLanguageEnabledMaps[lang] = true; @@ -788,11 +786,10 @@ void cmGlobalGenerator::EnableLanguage( fpath += "Information.cmake"; std::string informationFile = mf->GetModulesFile(fpath); if (informationFile.empty()) { - cmSystemTools::Error("Could not find cmake module file: ", - fpath.c_str()); + cmSystemTools::Error("Could not find cmake module file: " + fpath); } else if (!mf->ReadListFile(informationFile)) { - cmSystemTools::Error("Could not process cmake module file: ", - informationFile.c_str()); + cmSystemTools::Error("Could not process cmake module file: " + + informationFile); } } if (needSetLanguageEnabledMaps[lang]) { @@ -812,8 +809,8 @@ void cmGlobalGenerator::EnableLanguage( testLang += "Compiler.cmake"; std::string ifpath = mf->GetModulesFile(testLang); if (!mf->ReadListFile(ifpath)) { - cmSystemTools::Error("Could not find cmake module file: ", - testLang.c_str()); + cmSystemTools::Error("Could not find cmake module file: " + + testLang); } std::string compilerWorks = "CMAKE_"; compilerWorks += lang; @@ -898,7 +895,7 @@ void cmGlobalGenerator::CheckCompilerIdCompatibility( /* clang-format off */ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0025) << "\n" "Converting " << lang << - " compiler id \"AppleClang\" to \"Clang\" for compatibility." + R"( compiler id "AppleClang" to "Clang" for compatibility.)" ; /* clang-format on */ mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); @@ -928,7 +925,7 @@ void cmGlobalGenerator::CheckCompilerIdCompatibility( /* clang-format off */ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0047) << "\n" "Converting " << lang << - " compiler id \"QCC\" to \"GNU\" for compatibility." + R"( compiler id "QCC" to "GNU" for compatibility.)" ; /* clang-format on */ mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); @@ -954,6 +951,36 @@ void cmGlobalGenerator::CheckCompilerIdCompatibility( break; } } + + if (strcmp(compilerId, "XLClang") == 0) { + switch (mf->GetPolicyStatus(cmPolicies::CMP0089)) { + case cmPolicies::WARN: + if (!this->CMakeInstance->GetIsInTryCompile() && + mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0089")) { + std::ostringstream w; + /* clang-format off */ + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0089) << "\n" + "Converting " << lang << + R"( compiler id "XLClang" to "XL" for compatibility.)" + ; + /* clang-format on */ + mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior is to convert XLClang to XL. + mf->AddDefinition(compilerIdVar, "XL"); + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + mf->IssueMessage( + MessageType::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0089)); + case cmPolicies::NEW: + // NEW behavior is to keep AppleClang. + break; + } + } } std::string cmGlobalGenerator::GetLanguageOutputExtension( @@ -961,9 +988,7 @@ std::string cmGlobalGenerator::GetLanguageOutputExtension( { const std::string& lang = source.GetLanguage(); if (!lang.empty()) { - std::map<std::string, std::string>::const_iterator it = - this->LanguageToOutputExtension.find(lang); - + auto const it = this->LanguageToOutputExtension.find(lang); if (it != this->LanguageToOutputExtension.end()) { return it->second; } @@ -988,8 +1013,7 @@ std::string cmGlobalGenerator::GetLanguageFromExtension(const char* ext) const if (ext && *ext == '.') { ++ext; } - std::map<std::string, std::string>::const_iterator it = - this->ExtensionToLanguage.find(ext); + auto const it = this->ExtensionToLanguage.find(ext); if (it != this->ExtensionToLanguage.end()) { return it->second; } @@ -1182,14 +1206,16 @@ void cmGlobalGenerator::Configure() this->ConfigureDoneCMP0026AndCMP0024 = true; // Put a copy of each global target in every directory. - std::vector<GlobalTargetInfo> globalTargets; - this->CreateDefaultGlobalTargets(globalTargets); - - for (cmMakefile* mf : this->Makefiles) { - cmTargets* targets = &(mf->GetTargets()); - for (GlobalTargetInfo const& globalTarget : globalTargets) { - targets->insert(cmTargets::value_type( - globalTarget.Name, this->CreateGlobalTarget(globalTarget, mf))); + { + std::vector<GlobalTargetInfo> globalTargets; + this->CreateDefaultGlobalTargets(globalTargets); + + for (cmMakefile* mf : this->Makefiles) { + auto& targets = mf->GetTargets(); + for (GlobalTargetInfo const& globalTarget : globalTargets) { + targets.emplace(globalTarget.Name, + this->CreateGlobalTarget(globalTarget, mf)); + } } } @@ -1222,7 +1248,7 @@ void cmGlobalGenerator::Configure() } else { msg << "Configuring done"; } - this->CMakeInstance->UpdateProgress(msg.str().c_str(), -1); + this->CMakeInstance->UpdateProgress(msg.str(), -1); } } @@ -1238,7 +1264,7 @@ void cmGlobalGenerator::CreateImportedGenerationObjects( std::vector<const cmGeneratorTarget*>& exports) { this->CreateGenerationObjects(ImportedOnly); - std::vector<cmMakefile*>::iterator mfit = + auto const mfit = std::find(this->Makefiles.begin(), this->Makefiles.end(), mf); cmLocalGenerator* lg = this->LocalGenerators[std::distance(this->Makefiles.begin(), mfit)]; @@ -1253,8 +1279,7 @@ void cmGlobalGenerator::CreateImportedGenerationObjects( cmExportBuildFileGenerator* cmGlobalGenerator::GetExportedTargetsFile( const std::string& filename) const { - std::map<std::string, cmExportBuildFileGenerator*>::const_iterator it = - this->BuildExportSets.find(filename); + auto const it = this->BuildExportSets.find(filename); return it == this->BuildExportSets.end() ? nullptr : it->second; } @@ -1468,8 +1493,7 @@ bool cmGlobalGenerator::ComputeTargetDepends() if (!ctd.Compute()) { return false; } - std::vector<cmGeneratorTarget const*> const& targets = ctd.GetTargets(); - for (cmGeneratorTarget const* target : targets) { + for (cmGeneratorTarget const* target : ctd.GetTargets()) { ctd.GetTargetDirectDepends(target, this->TargetDependencies[target]); } return true; @@ -1509,8 +1533,7 @@ void cmGlobalGenerator::FinalizeTargetCompileInfo() const cmBacktraceRange noconfig_compile_definitions_bts = mf->GetCompileDefinitionsBacktraces(); - cmTargets& targets = mf->GetTargets(); - for (auto& target : targets) { + for (auto& target : mf->GetTargets()) { cmTarget* t = &target.second; if (t->GetType() == cmStateEnums::GLOBAL_TARGET) { continue; @@ -1522,12 +1545,12 @@ void cmGlobalGenerator::FinalizeTargetCompileInfo() continue; } - cmBacktraceRange::const_iterator btIt = - noconfig_compile_definitions_bts.begin(); - for (cmStringRange::const_iterator it = - noconfig_compile_definitions.begin(); - it != noconfig_compile_definitions.end(); ++it, ++btIt) { - t->InsertCompileDefinition(*it, *btIt); + { + auto btIt = noconfig_compile_definitions_bts.begin(); + auto it = noconfig_compile_definitions.begin(); + for (; it != noconfig_compile_definitions.end(); ++it, ++btIt) { + t->InsertCompileDefinition(*it, *btIt); + } } cmPolicies::PolicyStatus polSt = @@ -1567,17 +1590,14 @@ void cmGlobalGenerator::CreateGeneratorTargets( std::map<cmTarget*, cmGeneratorTarget*> const& importedMap) { if (targetTypes == AllTargets) { - cmTargets& targets = mf->GetTargets(); - for (auto& target : targets) { + for (auto& target : mf->GetTargets()) { cmTarget* t = &target.second; cmGeneratorTarget* gt = new cmGeneratorTarget(t, lg); lg->AddGeneratorTarget(gt); } } - std::vector<cmTarget*> itgts = mf->GetImportedTargets(); - - for (cmTarget* t : itgts) { + for (cmTarget* t : mf->GetImportedTargets()) { lg->AddImportedGeneratorTarget(importedMap.find(t)->second); } } @@ -1638,14 +1658,11 @@ void cmGlobalGenerator::CheckTargetProperties() cmState* state = this->GetCMakeInstance()->GetState(); for (unsigned int i = 0; i < this->Makefiles.size(); ++i) { this->Makefiles[i]->ConfigureFinalPass(); - cmTargets& targets = this->Makefiles[i]->GetTargets(); - for (auto const& target : targets) { + for (auto const& target : this->Makefiles[i]->GetTargets()) { if (target.second.GetType() == cmStateEnums::INTERFACE_LIBRARY) { continue; } - const cmTarget::LinkLibraryVectorType& libs = - target.second.GetOriginalLinkLibraries(); - for (auto const& lib : libs) { + for (auto const& lib : target.second.GetOriginalLinkLibraries()) { if (lib.first.size() > 9 && cmSystemTools::IsNOTFOUND(lib.first.c_str())) { std::string varName = lib.first.substr(0, lib.first.size() - 9); @@ -1702,8 +1719,8 @@ void cmGlobalGenerator::CheckTargetProperties() cmSystemTools::Error("The following variables are used in this project, " "but they are set to NOTFOUND.\n" "Please set them or make sure they are set and " - "tested correctly in the CMake files:\n", - notFoundVars.c_str()); + "tested correctly in the CMake files:\n" + + notFoundVars); } } @@ -1731,20 +1748,9 @@ int cmGlobalGenerator::TryCompile(int jobs, const std::string& srcdir, this->FirstTimeProgress); } - std::string newTarget; + std::vector<std::string> newTarget = {}; if (!target.empty()) { - newTarget += target; -#if 0 -# if defined(_WIN32) || defined(__CYGWIN__) - std::string tmp = target; - // if the target does not already end in . something - // then assume .exe - if(tmp.size() < 4 || tmp[tmp.size()-4] != '.') - { - newTarget += ".exe"; - } -# endif // WIN32 -#endif + newTarget = { target }; } std::string config = mf->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"); @@ -1752,14 +1758,16 @@ int cmGlobalGenerator::TryCompile(int jobs, const std::string& srcdir, config, false, fast, false, this->TryCompileTimeout); } -void cmGlobalGenerator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& /*unused*/, +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalGenerator::GenerateBuildCommand( const std::string& /*unused*/, const std::string& /*unused*/, - const std::string& /*unused*/, const std::string& /*unused*/, - bool /*unused*/, int /*unused*/, bool /*unused*/, - std::vector<std::string> const& /*unused*/) + const std::string& /*unused*/, std::vector<std::string> const& /*unused*/, + const std::string& /*unused*/, bool /*unused*/, int /*unused*/, + bool /*unused*/, std::vector<std::string> const& /*unused*/) { - makeCommand.add("cmGlobalGenerator::GenerateBuildCommand not implemented"); + GeneratedMakeCommand makeCommand; + makeCommand.Add("cmGlobalGenerator::GenerateBuildCommand not implemented"); + return { std::move(makeCommand) }; } void cmGlobalGenerator::PrintBuildCommandAdvice(std::ostream& /*os*/, @@ -1769,15 +1777,13 @@ void cmGlobalGenerator::PrintBuildCommandAdvice(std::ostream& /*os*/, // they do not support certain build command line options } -int cmGlobalGenerator::Build(int jobs, const std::string& /*unused*/, - const std::string& bindir, - const std::string& projectName, - const std::string& target, std::string& output, - const std::string& makeCommandCSTR, - const std::string& config, bool clean, bool fast, - bool verbose, cmDuration timeout, - cmSystemTools::OutputOption outputflag, - std::vector<std::string> const& nativeOptions) +int cmGlobalGenerator::Build( + int jobs, const std::string& /*unused*/, const std::string& bindir, + const std::string& projectName, const std::vector<std::string>& targets, + std::string& output, const std::string& makeCommandCSTR, + const std::string& config, bool clean, bool fast, bool verbose, + cmDuration timeout, cmSystemTools::OutputOption outputflag, + std::vector<std::string> const& nativeOptions) { bool hideconsole = cmSystemTools::GetRunCommandHideConsole(); @@ -1798,32 +1804,37 @@ int cmGlobalGenerator::Build(int jobs, const std::string& /*unused*/, return 1; } - int retVal; + int retVal = 0; cmSystemTools::SetRunCommandHideConsole(true); std::string outputBuffer; std::string* outputPtr = &outputBuffer; - GeneratedMakeCommand makeCommand; - this->GenerateBuildCommand(makeCommand, makeCommandCSTR, projectName, bindir, - target, config, fast, jobs, verbose, - nativeOptions); + std::vector<GeneratedMakeCommand> makeCommand = + this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir, targets, + config, fast, jobs, verbose, nativeOptions); // Workaround to convince some commands to produce output. if (outputflag == cmSystemTools::OUTPUT_PASSTHROUGH && - makeCommand.RequiresOutputForward) { + makeCommand.back().RequiresOutputForward) { outputflag = cmSystemTools::OUTPUT_FORWARD; } // should we do a clean first? if (clean) { - GeneratedMakeCommand cleanCommand; - this->GenerateBuildCommand(cleanCommand, makeCommandCSTR, projectName, - bindir, "clean", config, fast, jobs, verbose); + std::vector<GeneratedMakeCommand> cleanCommand = + this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir, + { "clean" }, config, fast, jobs, verbose); output += "\nRun Clean Command:"; - output += cleanCommand.printable(); + output += cleanCommand.front().Printable(); output += "\n"; - - if (!cmSystemTools::RunSingleCommand(cleanCommand.PrimaryCommand, + if (cleanCommand.size() != 1) { + this->GetCMakeInstance()->IssueMessage(MessageType::INTERNAL_ERROR, + "The generator did not produce " + "exactly one command for the " + "'clean' target"); + return 1; + } + if (!cmSystemTools::RunSingleCommand(cleanCommand.front().PrimaryCommand, outputPtr, outputPtr, &retVal, nullptr, outputflag, timeout)) { cmSystemTools::SetRunCommandHideConsole(hideconsole); @@ -1837,33 +1848,34 @@ int cmGlobalGenerator::Build(int jobs, const std::string& /*unused*/, } // now build - std::string makeCommandStr = makeCommand.printable(); + std::string makeCommandStr; output += "\nRun Build Command(s):"; - output += makeCommandStr; - output += "\n"; - if (!cmSystemTools::RunSingleCommand(makeCommand.PrimaryCommand, outputPtr, - outputPtr, &retVal, nullptr, outputflag, - timeout)) { - cmSystemTools::SetRunCommandHideConsole(hideconsole); - cmSystemTools::Error( - "Generator: execution of make failed. Make command was: ", - makeCommandStr.c_str()); - output += *outputPtr; - output += "\nGenerator: execution of make failed. Make command was: " + - makeCommandStr + "\n"; + for (auto command = makeCommand.begin(); command != makeCommand.end(); + ++command) { + makeCommandStr = command->Printable(); + if (command != makeCommand.end()) { + makeCommandStr += " && "; + } - return 1; - } - output += *outputPtr; - cmSystemTools::SetRunCommandHideConsole(hideconsole); + output += makeCommandStr; + if (!cmSystemTools::RunSingleCommand(command->PrimaryCommand, outputPtr, + outputPtr, &retVal, nullptr, + outputflag, timeout)) { + cmSystemTools::SetRunCommandHideConsole(hideconsole); + cmSystemTools::Error( + "Generator: execution of make failed. Make command was: " + + makeCommandStr); + output += *outputPtr; + output += "\nGenerator: execution of make failed. Make command was: " + + makeCommandStr + "\n"; - // The SGI MipsPro 7.3 compiler does not return an error code when - // the source has a #error in it! This is a work-around for such - // compilers. - if ((retVal == 0) && (output.find("#error") != std::string::npos)) { - retVal = 1; + return 1; + } + output += *outputPtr; } + output += "\n"; + cmSystemTools::SetRunCommandHideConsole(hideconsole); // The OpenWatcom tools do not return an error code when a link // library is not found! @@ -2043,8 +2055,7 @@ void cmGlobalGenerator::GetEnabledLanguages( int cmGlobalGenerator::GetLinkerPreference(const std::string& lang) const { - std::map<std::string, int>::const_iterator it = - this->LanguageToLinkerPreference.find(lang); + auto const it = this->LanguageToLinkerPreference.find(lang); if (it != this->LanguageToLinkerPreference.end()) { return it->second; } @@ -2071,9 +2082,9 @@ void cmGlobalGenerator::FillProjectMap() cmMakefile* cmGlobalGenerator::FindMakefile(const std::string& start_dir) const { - MakefileMap::const_iterator i = this->MakefileSearchIndex.find(start_dir); - if (i != this->MakefileSearchIndex.end()) { - return i->second; + auto const it = this->MakefileSearchIndex.find(start_dir); + if (it != this->MakefileSearchIndex.end()) { + return it->second; } return nullptr; } @@ -2081,10 +2092,9 @@ cmMakefile* cmGlobalGenerator::FindMakefile(const std::string& start_dir) const cmLocalGenerator* cmGlobalGenerator::FindLocalGenerator( cmDirectoryId const& id) const { - LocalGeneratorMap::const_iterator i = - this->LocalGeneratorSearchIndex.find(id.String); - if (i != this->LocalGeneratorSearchIndex.end()) { - return i->second; + auto const it = this->LocalGeneratorSearchIndex.find(id.String); + if (it != this->LocalGeneratorSearchIndex.end()) { + return it->second; } return nullptr; } @@ -2114,17 +2124,24 @@ void cmGlobalGenerator::IndexGeneratorTarget(cmGeneratorTarget* gt) } } +static char const hexDigits[] = "0123456789abcdef"; + std::string cmGlobalGenerator::IndexGeneratorTargetUniquely( cmGeneratorTarget const* gt) { // Use the pointer value to uniquely identify the target instance. - // Use a "T" prefix to indicate that this identifier is for a target. + // Use a ":" prefix to avoid conflict with project-defined targets. // We must satisfy cmGeneratorExpression::IsValidTargetName so use no // other special characters. - char buf[64]; - sprintf(buf, "::T%p", - static_cast<void const*>(gt)); // cast avoids format warning - std::string id = gt->GetName() + buf; + char buf[1 + sizeof(gt) * 2]; + char* b = buf; + *b++ = ':'; + for (size_t i = 0; i < sizeof(gt); ++i) { + unsigned char const c = reinterpret_cast<unsigned char const*>(>)[i]; + *b++ = hexDigits[(c & 0xf0) >> 4]; + *b++ = hexDigits[(c & 0x0f)]; + } + std::string id(buf, sizeof(buf)); // We internally index pointers to non-const generator targets // but our callers only have pointers to const generator targets. // They will give up non-const privileges when looking up anyway. @@ -2151,9 +2168,9 @@ void cmGlobalGenerator::IndexLocalGenerator(cmLocalGenerator* lg) cmTarget* cmGlobalGenerator::FindTargetImpl(std::string const& name) const { - TargetMap::const_iterator i = this->TargetSearchIndex.find(name); - if (i != this->TargetSearchIndex.end()) { - return i->second; + auto const it = this->TargetSearchIndex.find(name); + if (it != this->TargetSearchIndex.end()) { + return it->second; } return nullptr; } @@ -2161,10 +2178,9 @@ cmTarget* cmGlobalGenerator::FindTargetImpl(std::string const& name) const cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTargetImpl( std::string const& name) const { - GeneratorTargetMap::const_iterator i = - this->GeneratorTargetSearchIndex.find(name); - if (i != this->GeneratorTargetSearchIndex.end()) { - return i->second; + auto const it = this->GeneratorTargetSearchIndex.find(name); + if (it != this->GeneratorTargetSearchIndex.end()) { + return it->second; } return nullptr; } @@ -2173,8 +2189,7 @@ cmTarget* cmGlobalGenerator::FindTarget(const std::string& name, bool excludeAliases) const { if (!excludeAliases) { - std::map<std::string, std::string>::const_iterator ai = - this->AliasTargets.find(name); + auto const ai = this->AliasTargets.find(name); if (ai != this->AliasTargets.end()) { return this->FindTargetImpl(ai->second); } @@ -2185,8 +2200,7 @@ cmTarget* cmGlobalGenerator::FindTarget(const std::string& name, cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTarget( const std::string& name) const { - std::map<std::string, std::string>::const_iterator ai = - this->AliasTargets.find(name); + auto const ai = this->AliasTargets.find(name); if (ai != this->AliasTargets.end()) { return this->FindGeneratorTargetImpl(ai->second); } @@ -2196,7 +2210,7 @@ cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTarget( bool cmGlobalGenerator::NameResolvesToFramework( const std::string& libname) const { - if (cmSystemTools::IsPathToFramework(libname.c_str())) { + if (cmSystemTools::IsPathToFramework(libname)) { return true; } @@ -2277,10 +2291,9 @@ void cmGlobalGenerator::AddGlobalTarget_Package( return; } - const char* reservedTargets[] = { "package", "PACKAGE" }; - for (const char* const* tn = cm::cbegin(reservedTargets); - tn != cm::cend(reservedTargets); ++tn) { - if (!this->CheckCMP0037(*tn, "when CPack packaging is enabled")) { + static const auto reservedTargets = { "package", "PACKAGE" }; + for (auto const& target : reservedTargets) { + if (!this->CheckCMP0037(target, "when CPack packaging is enabled")) { return; } } @@ -2327,10 +2340,10 @@ void cmGlobalGenerator::AddGlobalTarget_PackageSource( return; } - const char* reservedTargets[] = { "package_source" }; - for (const char* const* tn = cm::cbegin(reservedTargets); - tn != cm::cend(reservedTargets); ++tn) { - if (!this->CheckCMP0037(*tn, "when CPack source packaging is enabled")) { + static const auto reservedTargets = { "package_source" }; + for (auto const& target : reservedTargets) { + if (!this->CheckCMP0037(target, + "when CPack source packaging is enabled")) { return; } } @@ -2357,10 +2370,9 @@ void cmGlobalGenerator::AddGlobalTarget_Test( return; } - const char* reservedTargets[] = { "test", "RUN_TESTS" }; - for (const char* const* tn = cm::cbegin(reservedTargets); - tn != cm::cend(reservedTargets); ++tn) { - if (!this->CheckCMP0037(*tn, "when CTest testing is enabled")) { + static const auto reservedTargets = { "test", "RUN_TESTS" }; + for (auto const& target : reservedTargets) { + if (!this->CheckCMP0037(target, "when CTest testing is enabled")) { return; } } @@ -2619,8 +2631,7 @@ bool cmGlobalGenerator::ShouldStripResourcePath(cmMakefile* mf) const std::string cmGlobalGenerator::GetSharedLibFlagsForLanguage( std::string const& l) const { - std::map<std::string, std::string>::const_iterator it = - this->LanguageToOriginalSharedLibFlags.find(l); + auto const it = this->LanguageToOriginalSharedLibFlags.find(l); if (it != this->LanguageToOriginalSharedLibFlags.end()) { return it->second; } @@ -2698,11 +2709,8 @@ void cmGlobalGenerator::GetTargetSets(TargetDependSet& projectTargets, if (this->IsExcluded(root, generator)) { continue; } - // Get the targets in the makefile - const std::vector<cmGeneratorTarget*>& tgts = - generator->GetGeneratorTargets(); - // loop over all the targets - for (cmGeneratorTarget* target : tgts) { + // loop over all the generator targets in the makefile + for (cmGeneratorTarget* target : generator->GetGeneratorTargets()) { if (this->IsRootOnlyTarget(target) && target->GetLocalGenerator() != root) { continue; @@ -2728,8 +2736,7 @@ void cmGlobalGenerator::AddTargetDepends(cmGeneratorTarget const* target, if (projectTargets.insert(target).second) { // This is the first time we have encountered the target. // Recursively follow its dependencies. - TargetDependSet const& ts = this->GetTargetDirectDepends(target); - for (auto const& t : ts) { + for (auto const& t : this->GetTargetDirectDepends(target)) { this->AddTargetDepends(t, projectTargets); } } @@ -2841,8 +2848,7 @@ void cmGlobalGenerator::CheckRuleHashes(std::string const& pfile, fname = line.substr(33); // Look for a hash for this file's rule. - std::map<std::string, RuleHash>::const_iterator rhi = - this->RuleHashes.find(fname); + auto const rhi = this->RuleHashes.find(fname); if (rhi != this->RuleHashes.end()) { // Compare the rule hash in the file to that we were given. if (strncmp(line.c_str(), rhi->second.Data, 32) != 0) { @@ -2892,8 +2898,7 @@ void cmGlobalGenerator::WriteSummary() cmGeneratedFileStream fout(fname); for (cmLocalGenerator* lg : this->LocalGenerators) { - const std::vector<cmGeneratorTarget*>& tgts = lg->GetGeneratorTargets(); - for (cmGeneratorTarget* tgt : tgts) { + for (cmGeneratorTarget* tgt : lg->GetGeneratorTargets()) { if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { continue; } @@ -2981,12 +2986,9 @@ void cmGlobalGenerator::WriteSummary(cmGeneratorTarget* target) for (std::string const& c : configs) { target->GetSourceFiles(sources, c); } - std::vector<cmSourceFile*>::const_iterator sourcesEnd = - cmRemoveDuplicates(sources); - for (std::vector<cmSourceFile*>::const_iterator si = sources.begin(); - si != sourcesEnd; ++si) { + auto const sourcesEnd = cmRemoveDuplicates(sources); + for (cmSourceFile* sf : cmMakeRange(sources.cbegin(), sourcesEnd)) { Json::Value& lj_source = lj_sources.append(Json::objectValue); - cmSourceFile* sf = *si; std::string const& sfp = sf->GetFullPath(); fout << sfp << "\n"; lj_source["file"] = sfp; diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index ac01326..88c586a 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -13,6 +13,7 @@ #include <utility> #include <vector> +#include "cmAlgorithms.h" #include "cmCustomCommandLines.h" #include "cmDuration.h" #include "cmExportSetMap.h" @@ -56,33 +57,20 @@ struct GeneratedMakeCommand { // Add each argument as a separate element to the vector template <typename... T> - void add(T&&... args) + void Add(T&&... args) { // iterate the args and append each one AppendStrs(PrimaryCommand, std::forward<T>(args)...); } // Add each value in the iterators as a separate element to the vector - void add(std::vector<std::string>::const_iterator start, + void Add(std::vector<std::string>::const_iterator start, std::vector<std::string>::const_iterator end) { PrimaryCommand.insert(PrimaryCommand.end(), start, end); } - std::string printable() const - { - std::size_t size = PrimaryCommand.size(); - for (auto&& i : PrimaryCommand) { - size += i.size(); - } - std::string buffer; - buffer.reserve(size); - for (auto&& i : PrimaryCommand) { - buffer.append(i); - buffer.append(1, ' '); - } - return buffer; - } + std::string Printable() const { return cmJoin(PrimaryCommand, " "); } std::vector<std::string> PrimaryCommand; bool RequiresOutputForward = false; @@ -98,13 +86,13 @@ struct GeneratedMakeCommand class cmGlobalGenerator { public: - ///! Free any memory allocated with the GlobalGenerator + //! Free any memory allocated with the GlobalGenerator cmGlobalGenerator(cmake* cm); virtual ~cmGlobalGenerator(); virtual cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf); - ///! Get the name for this generator + //! Get the name for this generator virtual std::string GetName() const { return "Generic"; } /** Check whether the given name matches the current generator. */ @@ -216,10 +204,10 @@ public: */ int Build( int jobs, const std::string& srcdir, const std::string& bindir, - const std::string& projectName, const std::string& targetName, - std::string& output, const std::string& makeProgram, - const std::string& config, bool clean, bool fast, bool verbose, - cmDuration timeout, + const std::string& projectName, + std::vector<std::string> const& targetNames, std::string& output, + const std::string& makeProgram, const std::string& config, bool clean, + bool fast, bool verbose, cmDuration timeout, cmSystemTools::OutputOption outputflag = cmSystemTools::OUTPUT_NONE, std::vector<std::string> const& nativeOptions = std::vector<std::string>()); @@ -234,11 +222,10 @@ public: { }; - virtual void GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& projectName, const std::string& projectDir, - const std::string& targetName, const std::string& config, bool fast, - int jobs, bool verbose, + virtual std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, std::vector<std::string> const& makeOptions = std::vector<std::string>()); virtual void PrintBuildCommandAdvice(std::ostream& os, int jobs) const; @@ -249,7 +236,7 @@ public: const std::string& native, bool ignoreErrors); - ///! Get the CMake instance + //! Get the CMake instance cmake* GetCMakeInstance() const { return this->CMakeInstance; } void SetConfiguredFilesPath(cmGlobalGenerator* gen); @@ -274,7 +261,7 @@ public: void AddMakefile(cmMakefile* mf); - ///! Set an generator for an "external makefile based project" + //! Set an generator for an "external makefile based project" void SetExternalMakefileProjectGenerator( cmExternalMakefileProjectGenerator* extraGenerator); @@ -303,19 +290,19 @@ public: bool GetForceUnixPaths() const { return this->ForceUnixPaths; } bool GetToolSupportsColor() const { return this->ToolSupportsColor; } - ///! return the language for the given extension + //! return the language for the given extension std::string GetLanguageFromExtension(const char* ext) const; - ///! is an extension to be ignored + //! is an extension to be ignored bool IgnoreFile(const char* ext) const; - ///! What is the preference for linkers and this language (None or Preferred) + //! What is the preference for linkers and this language (None or Preferred) int GetLinkerPreference(const std::string& lang) const; - ///! What is the object file extension for a given source file? + //! What is the object file extension for a given source file? std::string GetLanguageOutputExtension(cmSourceFile const&) const; - ///! What is the configurations directory variable called? + //! What is the configurations directory variable called? virtual const char* GetCMakeCFGIntDir() const { return "."; } - ///! expand CFGIntDir for a configuration + //! expand CFGIntDir for a configuration virtual std::string ExpandCFGIntDir(const std::string& str, const std::string& config) const; @@ -331,7 +318,7 @@ public: */ virtual bool FindMakeProgram(cmMakefile*); - ///! Find a target by name by searching the local generators. + //! Find a target by name by searching the local generators. cmTarget* FindTarget(const std::string& name, bool excludeAliases = false) const; diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index 45fa052..b69dea0 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -2,22 +2,33 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGlobalGhsMultiGenerator.h" -#include "cmsys/SystemTools.hxx" - -#include "cmAlgorithms.h" #include "cmDocumentationEntry.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" -#include "cmGhsMultiTargetGenerator.h" +#include "cmGhsMultiGpj.h" +#include "cmLocalGenerator.h" #include "cmLocalGhsMultiGenerator.h" #include "cmMakefile.h" #include "cmState.h" +#include "cmStateTypes.h" +#include "cmSystemTools.h" #include "cmVersion.h" #include "cmake.h" +#include <algorithm> +#include <map> +#include <ostream> +#include <string.h> +#include <utility> + const char* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj"; +#ifdef __linux__ +const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild"; +const char* cmGlobalGhsMultiGenerator::DEFAULT_TOOLSET_ROOT = "/usr/ghs"; +#elif defined(_WIN32) const char* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe"; const char* cmGlobalGhsMultiGenerator::DEFAULT_TOOLSET_ROOT = "C:/ghs"; +#endif cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm) : cmGlobalGenerator(cm) @@ -25,9 +36,7 @@ cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm) cm->GetState()->SetGhsMultiIDE(true); } -cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() -{ -} +cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() = default; cmLocalGenerator* cmGlobalGhsMultiGenerator::CreateLocalGenerator( cmMakefile* mf) @@ -64,7 +73,8 @@ bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts, /* no toolset was found */ if (tsp.empty()) { return false; - } else if (ts.empty()) { + } + if (ts.empty()) { std::string message; message = "Green Hills MULTI: -T <toolset> not specified; defaulting to \""; @@ -86,7 +96,7 @@ bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts, const char* prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM"); /* check if the toolset changed from last generate */ - if (prevTool != NULL && (gbuild != prevTool)) { + if (prevTool != nullptr && (gbuild != prevTool)) { std::string message = "toolset build tool: "; message += gbuild; message += "\nDoes not match the previously used build tool: "; @@ -95,13 +105,12 @@ bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts, "directory or choose a different binary directory."; cmSystemTools::Error(message); return false; - } else { - /* store the toolset that is being used for this build */ - mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild.c_str(), - "build program to use", cmStateEnums::INTERNAL, - true); } + /* store the toolset that is being used for this build */ + mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild.c_str(), + "build program to use", cmStateEnums::INTERNAL, true); + mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp.c_str()); return true; @@ -110,10 +119,11 @@ bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts, bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p, cmMakefile* mf) { - if (p == "") { + std::string arch; + if (p.empty()) { cmSystemTools::Message( "Green Hills MULTI: -A <arch> not specified; defaulting to \"arm\""); - std::string arch = "arm"; + arch = "arm"; /* store the platform name for later use * -- already done if -A<arch> was specified @@ -121,19 +131,51 @@ bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p, mf->AddCacheDefinition("CMAKE_GENERATOR_PLATFORM", arch.c_str(), "Name of generator platform.", cmStateEnums::INTERNAL); + } else { + arch = p; } - const char* tgtPlatform = mf->GetDefinition("GHS_TARGET_PLATFORM"); - if (tgtPlatform == nullptr) { - cmSystemTools::Message("Green Hills MULTI: GHS_TARGET_PLATFORM not " - "specified; defaulting to \"integrity\""); - tgtPlatform = "integrity"; + /* check if OS location has been updated by platform scripts */ + std::string platform = mf->GetSafeDefinition("GHS_TARGET_PLATFORM"); + std::string osdir = mf->GetSafeDefinition("GHS_OS_DIR"); + if (cmSystemTools::IsOff(osdir.c_str()) && + platform.find("integrity") != std::string::npos) { + if (!this->CMakeInstance->GetIsInTryCompile()) { + /* required OS location is not found */ + std::string m = + "Green Hills MULTI: GHS_OS_DIR not specified; No OS found in \""; + m += mf->GetSafeDefinition("GHS_OS_ROOT"); + m += "\""; + cmSystemTools::Message(m); + } + osdir = "GHS_OS_DIR-NOT-SPECIFIED"; + } else if (!this->CMakeInstance->GetIsInTryCompile() && + cmSystemTools::IsOff(this->OsDir) && + !cmSystemTools::IsOff(osdir)) { + /* OS location was updated by auto-selection */ + std::string m = "Green Hills MULTI: GHS_OS_DIR not specified; found \""; + m += osdir; + m += "\""; + cmSystemTools::Message(m); } + this->OsDir = osdir; - /* store the platform name for later use */ - mf->AddCacheDefinition("GHS_TARGET_PLATFORM", tgtPlatform, - "Name of GHS target platform.", - cmStateEnums::INTERNAL); + // Determine GHS_BSP_NAME + std::string bspName = mf->GetSafeDefinition("GHS_BSP_NAME"); + + if (cmSystemTools::IsOff(bspName.c_str()) && + platform.find("integrity") != std::string::npos) { + bspName = "sim" + arch; + /* write back the calculate name for next time */ + mf->AddCacheDefinition("GHS_BSP_NAME", bspName.c_str(), + "Name of GHS target platform.", + cmStateEnums::STRING, true); + std::string m = + "Green Hills MULTI: GHS_BSP_NAME not specified; defaulting to \""; + m += bspName; + m += "\""; + cmSystemTools::Message(m); + } return true; } @@ -144,6 +186,21 @@ void cmGlobalGhsMultiGenerator::EnableLanguage( mf->AddDefinition("CMAKE_SYSTEM_NAME", "GHS-MULTI"); mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files + + const char* tgtPlatform = mf->GetDefinition("GHS_TARGET_PLATFORM"); + if (!tgtPlatform) { + cmSystemTools::Message("Green Hills MULTI: GHS_TARGET_PLATFORM not " + "specified; defaulting to \"integrity\""); + tgtPlatform = "integrity"; + } + + /* store the platform name for later use */ + mf->AddCacheDefinition("GHS_TARGET_PLATFORM", tgtPlatform, + "Name of GHS target platform.", cmStateEnums::STRING); + + /* store original OS location */ + this->OsDir = mf->GetSafeDefinition("GHS_OS_DIR"); + this->cmGlobalGenerator::EnableLanguage(l, mf, optional); } @@ -185,8 +242,7 @@ void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsd, } } else { std::string tryPath; - /* CollapseCombinedPath will check if ts is an absolute path */ - tryPath = cmSystemTools::CollapseCombinedPath(tsd, ts); + tryPath = cmSystemTools::CollapseFullPath(ts, tsd); if (!cmSystemTools::FileExists(tryPath)) { std::string msg = "GHS toolset \"" + tryPath + "\" not found."; cmSystemTools::Error(msg); @@ -202,139 +258,246 @@ void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout) fout << "#!gbuild" << std::endl; fout << "#" << std::endl << "# CMAKE generated file: DO NOT EDIT!" << std::endl - << "# Generated by \"" << this->GetActualName() << "\"" + << "# Generated by \"" << GetActualName() << "\"" << " Generator, CMake Version " << cmVersion::GetMajorVersion() << "." << cmVersion::GetMinorVersion() << std::endl << "#" << std::endl << std::endl; } -void cmGlobalGhsMultiGenerator::WriteTopLevelProject( - std::ostream& fout, cmLocalGenerator* root, - std::vector<cmLocalGenerator*>& generators) +void cmGlobalGhsMultiGenerator::WriteCustomRuleBOD(std::ostream& fout) { - WriteFileHeader(fout); + fout << "Commands {\n" + " Custom_Rule_Command {\n" + " name = \"Custom Rule Command\"\n" + " exec = \""; +#ifdef _WIN32 + fout << "cmd.exe"; +#else + fout << "/bin/sh"; +#endif + fout << "\"\n" + " options = {\"SpecialOptions\"}\n" + " }\n" + "}\n"; + + fout << "\n\n"; + fout << "FileTypes {\n" + " CmakeRule {\n" + " name = \"Custom Rule\"\n" + " action = \"&Run\"\n" + " extensions = {\""; +#ifdef _WIN32 + fout << "bat"; +#else + fout << "sh"; +#endif + fout << "\"}\n" + " grepable = false\n" + " command = \"Custom Rule Command\"\n" + " commandLine = \"$COMMAND "; +#ifdef _WIN32 + fout << "/c"; +#endif + fout << " $INPUTFILE\"\n" + " progress = \"Processing Custom Rule\"\n" + " promoteToFirstPass = true\n" + " outputType = \"None\"\n" + " color = \"#800080\"\n" + " }\n" + "}\n"; +} + +void cmGlobalGhsMultiGenerator::WriteCustomTargetBOD(std::ostream& fout) +{ + fout << "FileTypes {\n" + " CmakeTarget {\n" + " name = \"Custom Target\"\n" + " action = \"&Execute\"\n" + " grepable = false\n" + " outputType = \"None\"\n" + " color = \"#800080\"\n" + " }\n" + "}\n"; +} - this->WriteMacros(fout); - this->WriteHighLevelDirectives(fout); +void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout, + cmLocalGenerator* root) +{ + this->WriteFileHeader(fout); + this->WriteMacros(fout, root); + this->WriteHighLevelDirectives(root, fout); GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout); fout << "# Top Level Project File" << std::endl; // Specify BSP option if supplied by user - // -- not all platforms require this entry in the project file - // integrity platforms require this field; use default if needed - std::string platform; - if (const char* p = - this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM")) { - platform = p; - } - - std::string bspName; - if (char const* bspCache = - this->GetCMakeInstance()->GetCacheDefinition("GHS_BSP_NAME")) { - bspName = bspCache; - this->GetCMakeInstance()->MarkCliAsUsed("GHS_BSP_NAME"); - } else { - bspName = "IGNORE"; - } - - if (platform.find("integrity") != std::string::npos && - cmSystemTools::IsOff(bspName.c_str())) { - const char* a = - this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM"); - bspName = "sim"; - bspName += (a ? a : ""); - } - - if (!cmSystemTools::IsOff(bspName.c_str())) { + const char* bspName = + this->GetCMakeInstance()->GetCacheDefinition("GHS_BSP_NAME"); + if (!cmSystemTools::IsOff(bspName)) { fout << " -bsp " << bspName << std::endl; } // Specify OS DIR if supplied by user // -- not all platforms require this entry in the project file - std::string osDir; - std::string osDirOption; - if (char const* osDirCache = - this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR")) { - osDir = osDirCache; + if (!cmSystemTools::IsOff(this->OsDir.c_str())) { + const char* osDirOption = + this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR_OPTION"); + std::replace(this->OsDir.begin(), this->OsDir.end(), '\\', '/'); + fout << " "; + if (cmSystemTools::IsOff(osDirOption)) { + fout << ""; + } else { + fout << osDirOption; + } + fout << "\"" << this->OsDir << "\"" << std::endl; } +} - if (char const* osDirOptionCache = - this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR_OPTION")) { - osDirOption = osDirOptionCache; +void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout, + std::string& all_target) +{ + fout << "CMakeFiles/" << all_target << " [Project]" << std::endl; + // All known targets + for (cmGeneratorTarget const* target : this->ProjectTargets) { + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY || + target->GetType() == cmStateEnums::MODULE_LIBRARY || + target->GetType() == cmStateEnums::SHARED_LIBRARY || + (target->GetType() == cmStateEnums::GLOBAL_TARGET && + target->GetName() != GetInstallTargetName())) { + continue; + } + fout << "CMakeFiles/" << target->GetName() + ".tgt" + FILE_EXTENSION + << " [Project]" << std::endl; } +} - if (!cmSystemTools::IsOff(osDir.c_str()) || - platform.find("integrity") != std::string::npos) { - std::replace(osDir.begin(), osDir.end(), '\\', '/'); - fout << " " << osDirOption << "\"" << osDir << "\"" << std::endl; +void cmGlobalGhsMultiGenerator::WriteProjectLine( + std::ostream& fout, cmGeneratorTarget const* target, cmLocalGenerator* root, + std::string& rootBinaryDir) +{ + const char* projName = target->GetProperty("GENERATOR_FILE_NAME"); + const char* projType = target->GetProperty("GENERATOR_FILE_NAME_EXT"); + if (projName && projType) { + cmLocalGenerator* lg = target->GetLocalGenerator(); + std::string dir = lg->GetCurrentBinaryDirectory(); + dir = root->MaybeConvertToRelativePath(rootBinaryDir, dir); + if (dir == ".") { + dir.clear(); + } else { + if (dir.back() != '/') { + dir += "/"; + } + } + + std::string projFile = dir + projName + FILE_EXTENSION; + fout << projFile; + fout << " " << projType << std::endl; + } else { + /* Should never happen */ + std::string message = + "The project file for target [" + target->GetName() + "] is missing.\n"; + cmSystemTools::Error(message); + fout << "{comment} " << target->GetName() << " [missing project file]\n"; } +} + +void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root) +{ + std::string rootBinaryDir = root->GetCurrentBinaryDirectory(); + rootBinaryDir += "/CMakeFiles"; + + // All known targets + for (cmGeneratorTarget const* target : this->ProjectTargets) { + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY || + target->GetType() == cmStateEnums::MODULE_LIBRARY || + target->GetType() == cmStateEnums::SHARED_LIBRARY || + (target->GetType() == cmStateEnums::GLOBAL_TARGET && + target->GetName() != GetInstallTargetName())) { + continue; + } - WriteSubProjects(fout, root, generators); + // create target build file + std::string name = target->GetName() + ".tgt" + FILE_EXTENSION; + std::string fname = rootBinaryDir + "/" + name; + cmGeneratedFileStream fbld(fname); + fbld.SetCopyIfDifferent(true); + this->WriteFileHeader(fbld); + GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld); + std::vector<cmGeneratorTarget const*> build; + if (ComputeTargetBuildOrder(target, build)) { + std::string message = "The inter-target dependency graph for target [" + + target->GetName() + "] had a cycle.\n"; + cmSystemTools::Error(message); + } else { + for (auto& tgt : build) { + WriteProjectLine(fbld, tgt, root, rootBinaryDir); + } + } + fbld.Close(); + } } -void cmGlobalGhsMultiGenerator::WriteSubProjects( - std::ostream& fout, cmLocalGenerator* root, - std::vector<cmLocalGenerator*>& generators) +void cmGlobalGhsMultiGenerator::WriteAllTarget( + cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators, + std::string& all_target) { + this->ProjectTargets.clear(); + + // create target build file + all_target = root->GetProjectName() + "." + this->GetAllTargetName() + + ".tgt" + FILE_EXTENSION; + std::string fname = + root->GetCurrentBinaryDirectory() + "/CMakeFiles/" + all_target; + cmGeneratedFileStream fbld(fname); + fbld.SetCopyIfDifferent(true); + this->WriteFileHeader(fbld); + GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld); + // Collect all targets under this root generator and the transitive // closure of their dependencies. TargetDependSet projectTargets; TargetDependSet originalTargets; this->GetTargetSets(projectTargets, originalTargets, root, generators); - OrderedTargetDependSet orderedProjectTargets(projectTargets, ""); - - // write out all the sub-projects - std::string rootBinaryDir = root->GetCurrentBinaryDirectory(); - for (cmGeneratorTarget const* target : orderedProjectTargets) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + OrderedTargetDependSet sortedProjectTargets(projectTargets, ""); + std::vector<cmGeneratorTarget const*> defaultTargets; + for (cmGeneratorTarget const* t : sortedProjectTargets) { + /* save list of all targets in sorted order */ + this->ProjectTargets.push_back(t); + } + for (cmGeneratorTarget const* t : sortedProjectTargets) { + if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) { continue; } - - const char* projName = target->GetProperty("GENERATOR_FILE_NAME"); - const char* projType = target->GetProperty("GENERATOR_FILE_NAME_EXT"); - if (projName && projType) { - cmLocalGenerator* lg = target->GetLocalGenerator(); - std::string dir = lg->GetCurrentBinaryDirectory(); - dir = root->MaybeConvertToRelativePath(rootBinaryDir, dir.c_str()); - if (dir == ".") { - dir.clear(); - } else { - if (dir.back() != '/') { - dir += "/"; - } - } - - if (cmSystemTools::IsOn(target->GetProperty("EXCLUDE_FROM_ALL"))) { - fout << "{comment} "; - } - std::string projFile = dir + projName + FILE_EXTENSION; - fout << projFile; - fout << " " << projType << std::endl; - - if (cmSystemTools::IsOn(target->GetProperty("GHS_REFERENCE_PROJECT"))) { - // create reference project - std::string fname = dir; - fname += target->GetName(); - fname += "REF"; - fname += FILE_EXTENSION; - - cmGeneratedFileStream fref(fname.c_str()); - fref.SetCopyIfDifferent(true); - - this->WriteFileHeader(fref); - GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fref); - fref << " :reference=" << projFile << std::endl; - - fref.Close(); + if (!cmSystemTools::IsOn(t->GetProperty("EXCLUDE_FROM_ALL"))) { + defaultTargets.push_back(t); + } + } + std::vector<cmGeneratorTarget const*> build; + if (ComputeTargetBuildOrder(defaultTargets, build)) { + std::string message = "The inter-target dependency graph for project [" + + root->GetProjectName() + "] had a cycle.\n"; + cmSystemTools::Error(message); + } else { + // determine the targets for ALL target + std::string rootBinaryDir = root->GetCurrentBinaryDirectory(); + rootBinaryDir += "/CMakeFiles"; + for (cmGeneratorTarget const* target : build) { + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY || + target->GetType() == cmStateEnums::MODULE_LIBRARY || + target->GetType() == cmStateEnums::SHARED_LIBRARY) { + continue; } + this->WriteProjectLine(fbld, target, root, rootBinaryDir); } } + fbld.Close(); } void cmGlobalGhsMultiGenerator::Generate() { + std::string fname; + // first do the superclass method this->cmGlobalGenerator::Generate(); @@ -342,11 +505,32 @@ void cmGlobalGhsMultiGenerator::Generate() for (auto& it : this->ProjectMap) { this->OutputTopLevelProject(it.second[0], it.second); } + + // create custom rule BOD file + fname = this->GetCMakeInstance()->GetHomeOutputDirectory() + + "/CMakeFiles/custom_rule.bod"; + cmGeneratedFileStream frule(fname); + frule.SetCopyIfDifferent(true); + this->WriteFileHeader(frule); + this->WriteCustomRuleBOD(frule); + frule.Close(); + + // create custom target BOD file + fname = this->GetCMakeInstance()->GetHomeOutputDirectory() + + "/CMakeFiles/custom_target.bod"; + cmGeneratedFileStream ftarget(fname); + ftarget.SetCopyIfDifferent(true); + this->WriteFileHeader(ftarget); + this->WriteCustomTargetBOD(ftarget); + ftarget.Close(); } void cmGlobalGhsMultiGenerator::OutputTopLevelProject( cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators) { + std::string fname; + std::string all_target; + if (generators.empty()) { return; } @@ -355,86 +539,106 @@ void cmGlobalGhsMultiGenerator::OutputTopLevelProject( * with target projects. This avoid the issue where the project has * the same name as the executable target. */ - std::string fname = root->GetCurrentBinaryDirectory(); + fname = root->GetCurrentBinaryDirectory(); fname += "/"; fname += root->GetProjectName(); fname += ".top"; fname += FILE_EXTENSION; - cmGeneratedFileStream fout(fname.c_str()); - fout.SetCopyIfDifferent(true); + cmGeneratedFileStream top(fname); + top.SetCopyIfDifferent(true); + this->WriteTopLevelProject(top, root); - this->WriteTopLevelProject(fout, root, generators); + this->WriteAllTarget(root, generators, all_target); + this->WriteTargets(root); - fout.Close(); + this->WriteSubProjects(top, all_target); + top.Close(); } -void cmGlobalGhsMultiGenerator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& projectName, const std::string& projectDir, - const std::string& targetName, const std::string& /*config*/, bool /*fast*/, - int jobs, bool /*verbose*/, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalGhsMultiGenerator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& /*config*/, bool /*fast*/, int jobs, bool /*verbose*/, + std::vector<std::string> const& makeOptions) { - const char* gbuild = - this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM"); - makeCommand.add(this->SelectMakeProgram(makeProgram, (std::string)gbuild)); + GeneratedMakeCommand makeCommand = {}; + std::string gbuild; + if (const char* gbuildCached = + this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM")) { + gbuild = gbuildCached; + } + makeCommand.Add(this->SelectMakeProgram(makeProgram, gbuild)); if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { - makeCommand.add("-parallel"); + makeCommand.Add("-parallel"); if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { - makeCommand.add(std::to_string(jobs)); + makeCommand.Add(std::to_string(jobs)); } } - makeCommand.add(makeOptions.begin(), makeOptions.end()); + makeCommand.Add(makeOptions.begin(), makeOptions.end()); /* determine which top-project file to use */ std::string proj = projectName + ".top" + FILE_EXTENSION; std::vector<std::string> files; cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files); if (!files.empty()) { + /* if multiple top-projects are found in build directory + * then prefer projectName top-project. + */ auto p = std::find(files.begin(), files.end(), proj); if (p == files.end()) { proj = files.at(0); } } - makeCommand.add("-top", proj); - if (!targetName.empty()) { - if (targetName == "clean") { - makeCommand.add("-clean"); + makeCommand.Add("-top", proj); + if (!targetNames.empty()) { + if (std::find(targetNames.begin(), targetNames.end(), "clean") != + targetNames.end()) { + makeCommand.Add("-clean"); } else { - if (targetName.compare(targetName.size() - 4, 4, ".gpj") == 0) { - makeCommand.add(targetName); - } else { - makeCommand.add(targetName + ".gpj"); + for (const auto& tname : targetNames) { + if (!tname.empty()) { + makeCommand.Add(tname + ".tgt.gpj"); + } } } + } else { + /* transform name to default build */; + std::string all = proj; + all.replace(all.end() - 7, all.end(), + std::string(this->GetAllTargetName()) + ".tgt.gpj"); + makeCommand.Add(all); } + return { makeCommand }; } -void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout) +void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout, + cmLocalGenerator* root) { + fout << "macro PROJ_NAME=" << root->GetProjectName() << std::endl; char const* ghsGpjMacros = this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS"); - if (NULL != ghsGpjMacros) { + if (nullptr != ghsGpjMacros) { std::vector<std::string> expandedList; cmSystemTools::ExpandListArgument(std::string(ghsGpjMacros), expandedList); - for (std::vector<std::string>::const_iterator expandedListI = - expandedList.begin(); - expandedListI != expandedList.end(); ++expandedListI) { - fout << "macro " << *expandedListI << std::endl; + for (std::string const& arg : expandedList) { + fout << "macro " << arg << std::endl; } } } -void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout) +void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives( + cmLocalGenerator* root, std::ostream& fout) { /* set primary target */ std::string tgt; const char* t = this->GetCMakeInstance()->GetCacheDefinition("GHS_PRIMARY_TARGET"); - if (t) { + if (t && *t != '\0') { tgt = t; this->GetCMakeInstance()->MarkCliAsUsed("GHS_PRIMARY_TARGET"); } else { @@ -449,16 +653,20 @@ void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout) } fout << "primaryTarget=" << tgt << std::endl; + fout << "customization=" << root->GetBinaryDirectory() + << "/CMakeFiles/custom_rule.bod" << std::endl; + fout << "customization=" << root->GetBinaryDirectory() + << "/CMakeFiles/custom_target.bod" << std::endl; char const* const customization = this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION"); - if (NULL != customization && strlen(customization) > 0) { - fout << "customization=" << trimQuotes(customization) << std::endl; + if (nullptr != customization && strlen(customization) > 0) { + fout << "customization=" << this->TrimQuotes(customization) << std::endl; this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION"); } } -std::string cmGlobalGhsMultiGenerator::trimQuotes(std::string const& str) +std::string cmGlobalGhsMultiGenerator::TrimQuotes(std::string const& str) { std::string result; result.reserve(str.size()); @@ -491,3 +699,56 @@ cmGlobalGhsMultiGenerator::OrderedTargetDependSet::OrderedTargetDependSet( { this->insert(targets.begin(), targets.end()); } + +bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( + cmGeneratorTarget const* tgt, std::vector<cmGeneratorTarget const*>& build) +{ + std::vector<cmGeneratorTarget const*> t{ tgt }; + return ComputeTargetBuildOrder(t, build); +} + +bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( + std::vector<cmGeneratorTarget const*>& tgt, + std::vector<cmGeneratorTarget const*>& build) +{ + std::set<cmGeneratorTarget const*> temp; + std::set<cmGeneratorTarget const*> perm; + + for (auto ti : tgt) { + bool r = VisitTarget(temp, perm, build, ti); + if (r) { + return r; + } + } + return false; +} + +bool cmGlobalGhsMultiGenerator::VisitTarget( + std::set<cmGeneratorTarget const*>& temp, + std::set<cmGeneratorTarget const*>& perm, + std::vector<cmGeneratorTarget const*>& order, cmGeneratorTarget const* ti) +{ + /* check if permanent mark is set*/ + if (perm.find(ti) == perm.end()) { + /* set temporary mark; check if revisit*/ + if (temp.insert(ti).second) { + /* sort targets lexicographically to ensure that nodes are always visited + * in the same order */ + OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti), + ""); + for (auto& di : sortedTargets) { + if (this->VisitTarget(temp, perm, order, di)) { + return true; + } + } + /* mark as complete; insert into beginning of list*/ + perm.insert(ti); + order.push_back(ti); + return false; + } + /* revisiting item - not a DAG */ + return true; + } + /* already complete */ + return false; +} diff --git a/Source/cmGlobalGhsMultiGenerator.h b/Source/cmGlobalGhsMultiGenerator.h index bc2b199..98358c7 100644 --- a/Source/cmGlobalGhsMultiGenerator.h +++ b/Source/cmGlobalGhsMultiGenerator.h @@ -5,10 +5,20 @@ #include "cmGlobalGenerator.h" -#include "cmGhsMultiGpj.h" #include "cmGlobalGeneratorFactory.h" +#include "cmTargetDepend.h" -class cmGeneratedFileStream; +#include <iosfwd> +#include <set> +#include <string> +#include <utility> +#include <vector> + +class cmGeneratorTarget; +class cmLocalGenerator; +class cmMakefile; +class cmake; +struct cmDocumentationEntry; class cmGlobalGhsMultiGenerator : public cmGlobalGenerator { @@ -17,21 +27,21 @@ public: static const char* FILE_EXTENSION; cmGlobalGhsMultiGenerator(cmake* cm); - ~cmGlobalGhsMultiGenerator(); + ~cmGlobalGhsMultiGenerator() override; static cmGlobalGeneratorFactory* NewFactory() { return new cmGlobalGeneratorSimpleFactory<cmGlobalGhsMultiGenerator>(); } - ///! create the correct local generator + //! create the correct local generator cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override; /// @return the name of this generator. static std::string GetActualName() { return "Green Hills MULTI"; } - ///! Get the name for this generator - std::string GetName() const override { return this->GetActualName(); } + //! Get the name for this generator + std::string GetName() const override { return GetActualName(); } /// Overloaded methods. @see cmGlobalGenerator::GetDocumentation() static void GetDocumentation(cmDocumentationEntry& entry); @@ -68,7 +78,54 @@ public: // Write the common disclaimer text at the top of each build file. void WriteFileHeader(std::ostream& fout); - // Target dependency sorting + const char* GetInstallTargetName() const override { return "install"; } + +protected: + void Generate() override; + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; + +private: + void GetToolset(cmMakefile* mf, std::string& tsd, const std::string& ts); + + /* top-level project */ + void OutputTopLevelProject(cmLocalGenerator* root, + std::vector<cmLocalGenerator*>& generators); + void WriteTopLevelProject(std::ostream& fout, cmLocalGenerator* root); + void WriteMacros(std::ostream& fout, cmLocalGenerator* root); + void WriteHighLevelDirectives(cmLocalGenerator* root, std::ostream& fout); + void WriteSubProjects(std::ostream& fout, std::string& all_target); + void WriteTargets(cmLocalGenerator* root); + void WriteProjectLine(std::ostream& fout, cmGeneratorTarget const* target, + cmLocalGenerator* root, std::string& rootBinaryDir); + void WriteCustomRuleBOD(std::ostream& fout); + void WriteCustomTargetBOD(std::ostream& fout); + void WriteAllTarget(cmLocalGenerator* root, + std::vector<cmLocalGenerator*>& generators, + std::string& all_target); + + std::string TrimQuotes(std::string const& str); + + std::string OsDir; + static const char* DEFAULT_BUILD_PROGRAM; + static const char* DEFAULT_TOOLSET_ROOT; + + bool ComputeTargetBuildOrder(cmGeneratorTarget const* tgt, + std::vector<cmGeneratorTarget const*>& build); + bool ComputeTargetBuildOrder(std::vector<cmGeneratorTarget const*>& tgt, + std::vector<cmGeneratorTarget const*>& build); + bool VisitTarget(std::set<cmGeneratorTarget const*>& temp, + std::set<cmGeneratorTarget const*>& perm, + std::vector<cmGeneratorTarget const*>& order, + cmGeneratorTarget const* ti); + + std::vector<cmGeneratorTarget const*> ProjectTargets; + + // Target sorting class TargetSet : public std::set<cmGeneratorTarget const*> { }; @@ -77,44 +134,14 @@ public: std::string First; public: - TargetCompare(std::string const& first) - : First(first) + TargetCompare(std::string first) + : First(std::move(first)) { } bool operator()(cmGeneratorTarget const* l, cmGeneratorTarget const* r) const; }; class OrderedTargetDependSet; - -protected: - void Generate() override; - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; - -private: - void GetToolset(cmMakefile* mf, std::string& tsd, const std::string& ts); - - /* top-level project */ - void OutputTopLevelProject(cmLocalGenerator* root, - std::vector<cmLocalGenerator*>& generators); - void WriteTopLevelProject(std::ostream& fout, cmLocalGenerator* root, - std::vector<cmLocalGenerator*>& generators); - void WriteMacros(std::ostream& fout); - void WriteHighLevelDirectives(std::ostream& fout); - void WriteSubProjects(std::ostream& fout, cmLocalGenerator* root, - std::vector<cmLocalGenerator*>& generators); - - std::string trimQuotes(std::string const& str); - - static const char* DEFAULT_BUILD_PROGRAM; - static const char* DEFAULT_TOOLSET_ROOT; }; class cmGlobalGhsMultiGenerator::OrderedTargetDependSet diff --git a/Source/cmGlobalJOMMakefileGenerator.cxx b/Source/cmGlobalJOMMakefileGenerator.cxx index 2b7f486..43c9666 100644 --- a/Source/cmGlobalJOMMakefileGenerator.cxx +++ b/Source/cmGlobalJOMMakefileGenerator.cxx @@ -54,11 +54,12 @@ void cmGlobalJOMMakefileGenerator::PrintCompilerAdvice( this->cmGlobalUnixMakefileGenerator3::PrintCompilerAdvice(os, lang, envVar); } -void cmGlobalJOMMakefileGenerator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& projectName, const std::string& projectDir, - const std::string& targetName, const std::string& config, bool fast, - int jobs, bool verbose, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalJOMMakefileGenerator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions) { std::vector<std::string> jomMakeOptions; @@ -75,7 +76,7 @@ void cmGlobalJOMMakefileGenerator::GenerateBuildCommand( jobs = cmake::NO_BUILD_PARALLEL_LEVEL; } - cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( - makeCommand, makeProgram, projectName, projectDir, targetName, config, - fast, jobs, verbose, jomMakeOptions); + return cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( + makeProgram, projectName, projectDir, targetNames, config, fast, jobs, + verbose, jomMakeOptions); } diff --git a/Source/cmGlobalJOMMakefileGenerator.h b/Source/cmGlobalJOMMakefileGenerator.h index aa8b5fb..df3aec9 100644 --- a/Source/cmGlobalJOMMakefileGenerator.h +++ b/Source/cmGlobalJOMMakefileGenerator.h @@ -20,7 +20,7 @@ public: { return new cmGlobalGeneratorSimpleFactory<cmGlobalJOMMakefileGenerator>(); } - ///! Get the name for the generator. + //! Get the name for the generator. std::string GetName() const override { return cmGlobalJOMMakefileGenerator::GetActualName(); @@ -40,15 +40,12 @@ public: bool optional) override; protected: - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; private: void PrintCompilerAdvice(std::ostream& os, std::string const& lang, diff --git a/Source/cmGlobalMSYSMakefileGenerator.cxx b/Source/cmGlobalMSYSMakefileGenerator.cxx index 3c24556..606febe 100644 --- a/Source/cmGlobalMSYSMakefileGenerator.cxx +++ b/Source/cmGlobalMSYSMakefileGenerator.cxx @@ -45,7 +45,8 @@ void cmGlobalMSYSMakefileGenerator::EnableLanguage( std::vector<std::string> const& l, cmMakefile* mf, bool optional) { this->FindMakeProgram(mf); - std::string makeProgram = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string& makeProgram = + mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); std::vector<std::string> locations; std::string makeloc = cmSystemTools::GetProgramPath(makeProgram.c_str()); locations.push_back(this->FindMinGW(makeloc)); diff --git a/Source/cmGlobalMSYSMakefileGenerator.h b/Source/cmGlobalMSYSMakefileGenerator.h index 23dbc5e..d6e4847 100644 --- a/Source/cmGlobalMSYSMakefileGenerator.h +++ b/Source/cmGlobalMSYSMakefileGenerator.h @@ -19,7 +19,7 @@ public: return new cmGlobalGeneratorSimpleFactory<cmGlobalMSYSMakefileGenerator>(); } - ///! Get the name for the generator. + //! Get the name for the generator. virtual std::string GetName() const { return cmGlobalMSYSMakefileGenerator::GetActualName(); diff --git a/Source/cmGlobalMinGWMakefileGenerator.cxx b/Source/cmGlobalMinGWMakefileGenerator.cxx index c6d46e9..e218b4b 100644 --- a/Source/cmGlobalMinGWMakefileGenerator.cxx +++ b/Source/cmGlobalMinGWMakefileGenerator.cxx @@ -23,7 +23,8 @@ void cmGlobalMinGWMakefileGenerator::EnableLanguage( std::vector<std::string> const& l, cmMakefile* mf, bool optional) { this->FindMakeProgram(mf); - std::string makeProgram = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); + const std::string& makeProgram = + mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); std::vector<std::string> locations; locations.push_back(cmSystemTools::GetProgramPath(makeProgram)); locations.push_back("/mingw/bin"); diff --git a/Source/cmGlobalMinGWMakefileGenerator.h b/Source/cmGlobalMinGWMakefileGenerator.h index a994c92..15297e3 100644 --- a/Source/cmGlobalMinGWMakefileGenerator.h +++ b/Source/cmGlobalMinGWMakefileGenerator.h @@ -19,7 +19,7 @@ public: return new cmGlobalGeneratorSimpleFactory< cmGlobalMinGWMakefileGenerator>(); } - ///! Get the name for the generator. + //! Get the name for the generator. virtual std::string GetName() const { return cmGlobalMinGWMakefileGenerator::GetActualName(); diff --git a/Source/cmGlobalNMakeMakefileGenerator.cxx b/Source/cmGlobalNMakeMakefileGenerator.cxx index ffe95f9..a4838bc 100644 --- a/Source/cmGlobalNMakeMakefileGenerator.cxx +++ b/Source/cmGlobalNMakeMakefileGenerator.cxx @@ -54,11 +54,12 @@ void cmGlobalNMakeMakefileGenerator::PrintCompilerAdvice( this->cmGlobalUnixMakefileGenerator3::PrintCompilerAdvice(os, lang, envVar); } -void cmGlobalNMakeMakefileGenerator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& projectName, const std::string& projectDir, - const std::string& targetName, const std::string& config, bool fast, - int /*jobs*/, bool verbose, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalNMakeMakefileGenerator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int /*jobs*/, bool verbose, + std::vector<std::string> const& makeOptions) { std::vector<std::string> nmakeMakeOptions; @@ -68,9 +69,9 @@ void cmGlobalNMakeMakefileGenerator::GenerateBuildCommand( nmakeMakeOptions.insert(nmakeMakeOptions.end(), makeOptions.begin(), makeOptions.end()); - this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( - makeCommand, makeProgram, projectName, projectDir, targetName, config, - fast, cmake::NO_BUILD_PARALLEL_LEVEL, verbose, nmakeMakeOptions); + return this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( + makeProgram, projectName, projectDir, targetNames, config, fast, + cmake::NO_BUILD_PARALLEL_LEVEL, verbose, nmakeMakeOptions); } void cmGlobalNMakeMakefileGenerator::PrintBuildCommandAdvice(std::ostream& os, diff --git a/Source/cmGlobalNMakeMakefileGenerator.h b/Source/cmGlobalNMakeMakefileGenerator.h index 06c48e2..2fdf1ce 100644 --- a/Source/cmGlobalNMakeMakefileGenerator.h +++ b/Source/cmGlobalNMakeMakefileGenerator.h @@ -21,7 +21,7 @@ public: return new cmGlobalGeneratorSimpleFactory< cmGlobalNMakeMakefileGenerator>(); } - ///! Get the name for the generator. + //! Get the name for the generator. std::string GetName() const override { return cmGlobalNMakeMakefileGenerator::GetActualName(); @@ -45,15 +45,12 @@ public: bool optional) override; protected: - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 920f639..4fa6ee6 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -26,6 +26,7 @@ #include "cmMessageType.h" #include "cmNinjaLinkLineComputer.h" #include "cmOutputConverter.h" +#include "cmRange.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" @@ -136,16 +137,16 @@ void cmGlobalNinjaGenerator::WriteBuild( // Make sure there is a rule. if (rule.empty()) { cmSystemTools::Error("No rule for WriteBuildStatement! called " - "with comment: ", - comment.c_str()); + "with comment: " + + comment); return; } // Make sure there is at least one output file. if (outputs.empty()) { cmSystemTools::Error("No output files for WriteBuildStatement! called " - "with comment: ", - comment.c_str()); + "with comment: " + + comment); return; } @@ -334,16 +335,16 @@ void cmGlobalNinjaGenerator::WriteRule( // Make sure the rule has a name. if (name.empty()) { cmSystemTools::Error("No name given for WriteRuleStatement! called " - "with comment: ", - comment.c_str()); + "with comment: " + + comment); return; } // Make sure a command is given. if (command.empty()) { cmSystemTools::Error("No command given for WriteRuleStatement! called " - "with comment: ", - comment.c_str()); + "with comment: " + + comment); return; } @@ -376,7 +377,7 @@ void cmGlobalNinjaGenerator::WriteRule( if (!rspfile.empty()) { if (rspcontent.empty()) { - cmSystemTools::Error("No rspfile_content given!", comment.c_str()); + cmSystemTools::Error("No rspfile_content given!" + comment); return; } cmGlobalNinjaGenerator::Indent(os, 1); @@ -407,8 +408,8 @@ void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os, // Make sure we have a name. if (name.empty()) { cmSystemTools::Error("No name given for WriteVariable! called " - "with comment: ", - comment.c_str()); + "with comment: " + + comment); return; } @@ -676,31 +677,37 @@ void cmGlobalNinjaGenerator::EnableLanguage( // cmGlobalXCodeGenerator // Called by: // cmGlobalGenerator::Build() -void cmGlobalNinjaGenerator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& /*projectName*/, const std::string& /*projectDir*/, - const std::string& targetName, const std::string& /*config*/, bool /*fast*/, - int jobs, bool verbose, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalNinjaGenerator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& /*projectName*/, + const std::string& /*projectDir*/, + std::vector<std::string> const& targetNames, const std::string& /*config*/, + bool /*fast*/, int jobs, bool verbose, + std::vector<std::string> const& makeOptions) { - makeCommand.add(this->SelectMakeProgram(makeProgram)); + GeneratedMakeCommand makeCommand; + makeCommand.Add(this->SelectMakeProgram(makeProgram)); if (verbose) { - makeCommand.add("-v"); + makeCommand.Add("-v"); } if ((jobs != cmake::NO_BUILD_PARALLEL_LEVEL) && (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL)) { - makeCommand.add("-j", std::to_string(jobs)); + makeCommand.Add("-j", std::to_string(jobs)); } - makeCommand.add(makeOptions.begin(), makeOptions.end()); - if (!targetName.empty()) { - if (targetName == "clean") { - makeCommand.add("-t", "clean"); - } else { - makeCommand.add(targetName); + makeCommand.Add(makeOptions.begin(), makeOptions.end()); + for (const auto& tname : targetNames) { + if (!tname.empty()) { + if (tname == "clean") { + makeCommand.Add("-t", "clean"); + } else { + makeCommand.Add(tname); + } } } + return { std::move(makeCommand) }; } // Non-virtual public methods. @@ -899,11 +906,11 @@ void cmGlobalNinjaGenerator::AddCXXCompileCommand( /* clang-format off */ *this->CompileCommandsStream << "\n{\n" - << " \"directory\": \"" + << R"( "directory": ")" << cmGlobalGenerator::EscapeJSON(buildFileDir) << "\",\n" - << " \"command\": \"" + << R"( "command": ")" << cmGlobalGenerator::EscapeJSON(commandLine) << "\",\n" - << " \"file\": \"" + << R"( "file": ")" << cmGlobalGenerator::EscapeJSON(sourceFileName) << "\"\n" << "}"; /* clang-format on */ @@ -1573,12 +1580,13 @@ Compilation of source files within a target is split into the following steps: command = gfortran -cpp $DEFINES $INCLUDES $FLAGS -E $in -o $out && cmake -E cmake_ninja_depends \ --tdi=FortranDependInfo.json --pp=$out --dep=$DEP_FILE \ - --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE + --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE \ + --lang=Fortran - build src.f90-pp.f90 | src.f90-pp.f90.ddi: Fortran_PREPROCESS src.f90 + build src.f90-pp.f90 | src.f90.o.ddi: Fortran_PREPROCESS src.f90 OBJ_FILE = src.f90.o - DEP_FILE = src.f90-pp.f90.d - DYNDEP_INTERMEDIATE_FILE = src.f90-pp.f90.ddi + DEP_FILE = src.f90.o.d + DYNDEP_INTERMEDIATE_FILE = src.f90.o.ddi The ``cmake -E cmake_ninja_depends`` tool reads the preprocessed output and generates the ninja depfile for preprocessor dependencies. It also @@ -1592,9 +1600,9 @@ Compilation of source files within a target is split into the following steps: rule Fortran_DYNDEP command = cmake -E cmake_ninja_dyndep \ - --tdi=FortranDependInfo.json --dd=$out $in + --tdi=FortranDependInfo.json --lang=Fortran --dd=$out $in - build Fortran.dd: Fortran_DYNDEP src1.f90-pp.f90.ddi src2.f90-pp.f90.ddi + build Fortran.dd: Fortran_DYNDEP src1.f90.o.ddi src2.f90.o.ddi The ``cmake -E cmake_ninja_dyndep`` tool reads the "ddi" files from all sources in the target and the ``FortranModules.json`` files from targets @@ -1632,6 +1640,19 @@ Compilation of source files within a target is split into the following steps: (because the latter consumes the module). */ +struct cmSourceInfo +{ + // Set of provided and required modules. + std::set<std::string> Provides; + std::set<std::string> Requires; + + // Set of files included in the translation unit. + std::set<std::string> Includes; +}; + +static std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran( + std::string const& arg_tdi, std::string const& arg_pp); + int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, std::vector<std::string>::const_iterator argEnd) { @@ -1640,8 +1661,8 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, std::string arg_dep; std::string arg_obj; std::string arg_ddi; - for (std::vector<std::string>::const_iterator a = argBeg; a != argEnd; ++a) { - std::string const& arg = *a; + std::string arg_lang; + for (std::string const& arg : cmMakeRange(argBeg, argEnd)) { if (cmHasLiteralPrefix(arg, "--tdi=")) { arg_tdi = arg.substr(6); } else if (cmHasLiteralPrefix(arg, "--pp=")) { @@ -1652,9 +1673,10 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, arg_obj = arg.substr(6); } else if (cmHasLiteralPrefix(arg, "--ddi=")) { arg_ddi = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--lang=")) { + arg_lang = arg.substr(7); } else { - cmSystemTools::Error("-E cmake_ninja_depends unknown argument: ", - arg.c_str()); + cmSystemTools::Error("-E cmake_ninja_depends unknown argument: " + arg); return 1; } } @@ -1678,7 +1700,61 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, cmSystemTools::Error("-E cmake_ninja_depends requires value for --ddi="); return 1; } + if (arg_lang.empty()) { + cmSystemTools::Error("-E cmake_ninja_depends requires value for --lang="); + return 1; + } + + std::unique_ptr<cmSourceInfo> info; + if (arg_lang == "Fortran") { + info = cmcmd_cmake_ninja_depends_fortran(arg_tdi, arg_pp); + } else { + cmSystemTools::Error("-E cmake_ninja_depends does not understand the " + + arg_lang + " language"); + return 1; + } + + if (!info) { + // The error message is already expected to have been output. + return 1; + } + + { + cmGeneratedFileStream depfile(arg_dep); + depfile << cmSystemTools::ConvertToUnixOutputPath(arg_pp) << ":"; + for (std::string const& include : info->Includes) { + depfile << " \\\n " << cmSystemTools::ConvertToUnixOutputPath(include); + } + depfile << "\n"; + } + Json::Value ddi(Json::objectValue); + ddi["object"] = arg_obj; + + Json::Value& ddi_provides = ddi["provides"] = Json::arrayValue; + for (std::string const& provide : info->Provides) { + ddi_provides.append(provide); + } + Json::Value& ddi_requires = ddi["requires"] = Json::arrayValue; + for (std::string const& r : info->Requires) { + // Require modules not provided in the same source. + if (!info->Provides.count(r)) { + ddi_requires.append(r); + } + } + + cmGeneratedFileStream ddif(arg_ddi); + ddif << ddi; + if (!ddif) { + cmSystemTools::Error("-E cmake_ninja_depends failed to write " + arg_ddi); + return 1; + } + return 0; +} + +std::unique_ptr<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran( + std::string const& arg_tdi, std::string const& arg_pp) +{ cmFortranCompiler fc; std::vector<std::string> includes; { @@ -1688,10 +1764,9 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary); Json::Reader reader; if (!reader.parse(tdif, tdio, false)) { - cmSystemTools::Error("-E cmake_ninja_depends failed to parse ", - arg_tdi.c_str(), - reader.getFormattedErrorMessages().c_str()); - return 1; + cmSystemTools::Error("-E cmake_ninja_depends failed to parse " + + arg_tdi + reader.getFormattedErrorMessages()); + return nullptr; } } @@ -1712,54 +1787,26 @@ int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, fc.SModExt = tdi_submodule_ext.asString(); } - cmFortranSourceInfo info; + cmFortranSourceInfo finfo; std::set<std::string> defines; - cmFortranParser parser(fc, includes, defines, info); + cmFortranParser parser(fc, includes, defines, finfo); if (!cmFortranParser_FilePush(&parser, arg_pp.c_str())) { - cmSystemTools::Error("-E cmake_ninja_depends failed to open ", - arg_pp.c_str()); - return 1; + cmSystemTools::Error("-E cmake_ninja_depends failed to open " + arg_pp); + return nullptr; } if (cmFortran_yyparse(parser.Scanner) != 0) { // Failed to parse the file. - return 1; + return nullptr; } - { - cmGeneratedFileStream depfile(arg_dep); - depfile << cmSystemTools::ConvertToUnixOutputPath(arg_pp) << ":"; - for (std::string const& include : info.Includes) { - depfile << " \\\n " << cmSystemTools::ConvertToUnixOutputPath(include); - } - depfile << "\n"; - } - - Json::Value ddi(Json::objectValue); - ddi["object"] = arg_obj; - - Json::Value& ddi_provides = ddi["provides"] = Json::arrayValue; - for (std::string const& provide : info.Provides) { - ddi_provides.append(provide); - } - Json::Value& ddi_requires = ddi["requires"] = Json::arrayValue; - for (std::string const& r : info.Requires) { - // Require modules not provided in the same source. - if (!info.Provides.count(r)) { - ddi_requires.append(r); - } - } - - cmGeneratedFileStream ddif(arg_ddi); - ddif << ddi; - if (!ddif) { - cmSystemTools::Error("-E cmake_ninja_depends failed to write ", - arg_ddi.c_str()); - return 1; - } - return 0; + auto info = cm::make_unique<cmSourceInfo>(); + info->Provides = finfo.Provides; + info->Requires = finfo.Requires; + info->Includes = finfo.Includes; + return info; } -struct cmFortranObjectInfo +struct cmDyndepObjectInfo { std::string Object; std::vector<std::string> Provides; @@ -1771,7 +1818,8 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( std::string const& dir_cur_src, std::string const& dir_cur_bld, std::string const& arg_dd, std::vector<std::string> const& arg_ddis, std::string const& module_dir, - std::vector<std::string> const& linked_target_dirs) + std::vector<std::string> const& linked_target_dirs, + std::string const& arg_lang) { // Setup path conversions. { @@ -1788,7 +1836,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( this->LocalGenerators.push_back(lgd.release()); } - std::vector<cmFortranObjectInfo> objects; + std::vector<cmDyndepObjectInfo> objects; for (std::string const& arg_ddi : arg_ddis) { // Load the ddi file and compute the module file paths it provides. Json::Value ddio; @@ -1796,13 +1844,12 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( cmsys::ifstream ddif(arg_ddi.c_str(), std::ios::in | std::ios::binary); Json::Reader reader; if (!reader.parse(ddif, ddio, false)) { - cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse ", - arg_ddi.c_str(), - reader.getFormattedErrorMessages().c_str()); + cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse " + arg_ddi + + reader.getFormattedErrorMessages()); return false; } - cmFortranObjectInfo info; + cmDyndepObjectInfo info; info.Object = ddi["object"].asString(); Json::Value const& ddi_provides = ddi["provides"]; if (ddi_provides.isArray()) { @@ -1824,14 +1871,15 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( // Populate the module map with those provided by linked targets first. for (std::string const& linked_target_dir : linked_target_dirs) { - std::string const ltmn = linked_target_dir + "/FortranModules.json"; + std::string const ltmn = + linked_target_dir + "/" + arg_lang + "Modules.json"; Json::Value ltm; cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary); Json::Reader reader; if (ltmf && !reader.parse(ltmf, ltm, false)) { - cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse ", - linked_target_dir.c_str(), - reader.getFormattedErrorMessages().c_str()); + cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse " + + linked_target_dir + + reader.getFormattedErrorMessages()); return false; } if (ltm.isObject()) { @@ -1845,7 +1893,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( // We do this after loading the modules provided by linked targets // in case we have one of the same name that must be preferred. Json::Value tm = Json::objectValue; - for (cmFortranObjectInfo const& object : objects) { + for (cmDyndepObjectInfo const& object : objects) { for (std::string const& p : object.Provides) { std::string const mod = module_dir + p; mod_files[p] = mod; @@ -1856,7 +1904,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( cmGeneratedFileStream ddf(arg_dd); ddf << "ninja_dyndep_version = 1.0\n"; - for (cmFortranObjectInfo const& object : objects) { + for (cmDyndepObjectInfo const& object : objects) { std::string const ddComment; std::string const ddRule = "dyndep"; cmNinjaDeps ddOutputs; @@ -1887,7 +1935,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( // Store the map of modules provided by this target in a file for // use by dependents that reference this target in linked-target-dirs. std::string const target_mods_file = - cmSystemTools::GetFilenamePath(arg_dd) + "/FortranModules.json"; + cmSystemTools::GetFilenamePath(arg_dd) + "/" + arg_lang + "Modules.json"; cmGeneratedFileStream tmf(target_mods_file); tmf << tm; @@ -1901,19 +1949,21 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, cmSystemTools::HandleResponseFile(argBeg, argEnd); std::string arg_dd; + std::string arg_lang; std::string arg_tdi; std::vector<std::string> arg_ddis; for (std::string const& arg : arg_full) { if (cmHasLiteralPrefix(arg, "--tdi=")) { arg_tdi = arg.substr(6); + } else if (cmHasLiteralPrefix(arg, "--lang=")) { + arg_lang = arg.substr(7); } else if (cmHasLiteralPrefix(arg, "--dd=")) { arg_dd = arg.substr(5); } else if (!cmHasLiteralPrefix(arg, "--") && cmHasLiteralSuffix(arg, ".ddi")) { arg_ddis.push_back(arg); } else { - cmSystemTools::Error("-E cmake_ninja_dyndep unknown argument: ", - arg.c_str()); + cmSystemTools::Error("-E cmake_ninja_dyndep unknown argument: " + arg); return 1; } } @@ -1921,6 +1971,10 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --tdi="); return 1; } + if (arg_lang.empty()) { + cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --lang="); + return 1; + } if (arg_dd.empty()) { cmSystemTools::Error("-E cmake_ninja_dyndep requires value for --dd="); return 1; @@ -1932,9 +1986,8 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, cmsys::ifstream tdif(arg_tdi.c_str(), std::ios::in | std::ios::binary); Json::Reader reader; if (!reader.parse(tdif, tdio, false)) { - cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse ", - arg_tdi.c_str(), - reader.getFormattedErrorMessages().c_str()); + cmSystemTools::Error("-E cmake_ninja_dyndep failed to parse " + arg_tdi + + reader.getFormattedErrorMessages()); return 1; } } @@ -1944,7 +1997,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, std::string const dir_top_bld = tdi["dir-top-bld"].asString(); std::string const dir_top_src = tdi["dir-top-src"].asString(); std::string module_dir = tdi["module-dir"].asString(); - if (!module_dir.empty()) { + if (!module_dir.empty() && !cmHasLiteralSuffix(module_dir, "/")) { module_dir += "/"; } std::vector<std::string> linked_target_dirs; @@ -1962,8 +2015,8 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, static_cast<cmGlobalNinjaGenerator*>(cm.CreateGlobalGenerator("Ninja"))); if (!ggd || !ggd->WriteDyndepFile(dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, - arg_dd, arg_ddis, module_dir, - linked_target_dirs)) { + arg_dd, arg_ddis, module_dir, linked_target_dirs, + arg_lang)) { return 1; } return 0; diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index c619e67..efd1d8f 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -200,15 +200,12 @@ public: void EnableLanguage(std::vector<std::string> const& languages, cmMakefile* mf, bool optional) override; - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; // Setup target names const char* GetAllTargetName() const override { return "all"; } @@ -365,7 +362,8 @@ public: std::string const& arg_dd, std::vector<std::string> const& arg_ddis, std::string const& module_dir, - std::vector<std::string> const& linked_target_dirs); + std::vector<std::string> const& linked_target_dirs, + std::string const& arg_lang); protected: void Generate() override; diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index dac6ea6..65d816e 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -57,7 +57,7 @@ void cmGlobalUnixMakefileGenerator3::EnableLanguage( } } -///! Create a local generator appropriate to this Global Generator +//! Create a local generator appropriate to this Global Generator cmLocalGenerator* cmGlobalUnixMakefileGenerator3::CreateLocalGenerator( cmMakefile* mf) { @@ -177,13 +177,13 @@ void cmGlobalUnixMakefileGenerator3::AddCXXCompileCommand( *this->CommandDatabase << "," << std::endl; } *this->CommandDatabase << "{" << std::endl - << " \"directory\": \"" + << R"( "directory": ")" << cmGlobalGenerator::EscapeJSON(workingDirectory) << "\"," << std::endl - << " \"command\": \"" + << R"( "command": ")" << cmGlobalGenerator::EscapeJSON(compileCommand) << "\"," << std::endl - << " \"file\": \"" + << R"( "file": ")" << cmGlobalGenerator::EscapeJSON(sourceFile) << "\"" << std::endl << "}"; @@ -494,11 +494,13 @@ void cmGlobalUnixMakefileGenerator3::WriteDirectoryRules2( this->WriteDirectoryRule2(ruleFileStream, lg, "preinstall", true, true); } -void cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& /*projectName*/, const std::string& /*projectDir*/, - const std::string& targetName, const std::string& /*config*/, bool fast, - int jobs, bool verbose, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( + const std::string& makeProgram, const std::string& /*projectName*/, + const std::string& /*projectDir*/, + std::vector<std::string> const& targetNames, const std::string& /*config*/, + bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions) { std::unique_ptr<cmMakefile> mfu; cmMakefile* mf; @@ -515,34 +517,38 @@ void cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( mf = mfu.get(); } + GeneratedMakeCommand makeCommand; + // Make it possible to set verbosity also from command line if (verbose) { - makeCommand.add(cmSystemTools::GetCMakeCommand()); - makeCommand.add("-E"); - makeCommand.add("env"); - makeCommand.add("VERBOSE=1"); + makeCommand.Add(cmSystemTools::GetCMakeCommand()); + makeCommand.Add("-E"); + makeCommand.Add("env"); + makeCommand.Add("VERBOSE=1"); } - makeCommand.add(this->SelectMakeProgram(makeProgram)); + makeCommand.Add(this->SelectMakeProgram(makeProgram)); if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { - makeCommand.add("-j"); + makeCommand.Add("-j"); if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { - makeCommand.add(std::to_string(jobs)); + makeCommand.Add(std::to_string(jobs)); } } - makeCommand.add(makeOptions.begin(), makeOptions.end()); - if (!targetName.empty()) { - std::string tname = targetName; - if (fast) { - tname += "/fast"; + makeCommand.Add(makeOptions.begin(), makeOptions.end()); + for (auto tname : targetNames) { + if (!tname.empty()) { + if (fast) { + tname += "/fast"; + } + tname = + mf->GetStateSnapshot().GetDirectory().ConvertToRelPathIfNotContained( + mf->GetState()->GetBinaryDirectory(), tname); + cmSystemTools::ConvertToOutputSlashes(tname); + makeCommand.Add(std::move(tname)); } - tname = - mf->GetStateSnapshot().GetDirectory().ConvertToRelPathIfNotContained( - mf->GetState()->GetBinaryDirectory(), tname); - cmSystemTools::ConvertToOutputSlashes(tname); - makeCommand.add(std::move(tname)); } + return { std::move(makeCommand) }; } void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules( diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h index 8a80acc..e919d38 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.h +++ b/Source/cmGlobalUnixMakefileGenerator3.h @@ -67,7 +67,7 @@ public: cmGlobalUnixMakefileGenerator3>(); } - ///! Get the name for the generator. + //! Get the name for the generator. std::string GetName() const override { return cmGlobalUnixMakefileGenerator3::GetActualName(); @@ -127,15 +127,12 @@ public: std::string GetEmptyRuleHackDepends() { return this->EmptyRuleHackDepends; } // change the build command for speed - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; /** Record per-target progress information. */ void RecordTargetProgress(cmMakefileTargetGenerator* tg); diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index d8b2e89..4fa89d0 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -509,7 +509,7 @@ std::string cmGlobalVisualStudio10Generator::SelectWindowsCEToolset() const return ""; } -///! Create a local generator appropriate to this Global Generator +//! Create a local generator appropriate to this Global Generator cmLocalGenerator* cmGlobalVisualStudio10Generator::CreateLocalGenerator( cmMakefile* mf) { @@ -878,12 +878,14 @@ bool cmGlobalVisualStudio10Generator::FindVCTargetsPath(cmMakefile* mf) return true; } -void cmGlobalVisualStudio10Generator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& projectName, const std::string& projectDir, - const std::string& targetName, const std::string& config, bool fast, - int jobs, bool verbose, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalVisualStudio10Generator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions) { + std::vector<GeneratedMakeCommand> makeCommands; // Select the caller- or user-preferred make program, else MSBuild. std::string makeProgramSelected = this->SelectMakeProgram(makeProgram, this->GetMSBuildCommand()); @@ -895,7 +897,7 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand( makeProgramLower.find("vcexpress") != std::string::npos); // Workaround to convince VCExpress.exe to produce output. - makeCommand.RequiresOutputForward = + const bool requiresOutputForward = (makeProgramLower.find("vcexpress") != std::string::npos); // MSBuild is preferred (and required for VS Express), but if the .sln has @@ -913,10 +915,11 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand( if (parser.ParseFile(slnFile, slnData, cmVisualStudioSlnParser::DataGroupProjects)) { std::vector<cmSlnProjectEntry> slnProjects = slnData.GetProjects(); - for (std::vector<cmSlnProjectEntry>::const_iterator i = - slnProjects.cbegin(); - !useDevEnv && i != slnProjects.cend(); ++i) { - std::string proj = i->GetRelativePath(); + for (cmSlnProjectEntry const& project : slnProjects) { + if (useDevEnv) { + break; + } + std::string proj = project.GetRelativePath(); if (proj.size() > 7 && proj.substr(proj.size() - 7) == ".vfproj") { useDevEnv = true; } @@ -925,62 +928,71 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand( } if (useDevEnv) { // Use devenv to build solutions containing Intel Fortran projects. - cmGlobalVisualStudio7Generator::GenerateBuildCommand( - makeCommand, makeProgram, projectName, projectDir, targetName, config, - fast, jobs, verbose, makeOptions); - return; - } + return cmGlobalVisualStudio7Generator::GenerateBuildCommand( + makeProgram, projectName, projectDir, targetNames, config, fast, jobs, + verbose, makeOptions); + } + + std::vector<std::string> realTargetNames = targetNames; + if (targetNames.empty() || + ((targetNames.size() == 1) && targetNames.front().empty())) { + realTargetNames = { "ALL_BUILD" }; + } + for (const auto& tname : realTargetNames) { + // msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug + // /target:ALL_BUILD + // /m + if (tname.empty()) { + continue; + } - makeCommand.add(makeProgramSelected); + GeneratedMakeCommand makeCommand; + makeCommand.RequiresOutputForward = requiresOutputForward; + makeCommand.Add(makeProgramSelected); - std::string realTarget = targetName; - // msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug /target:ALL_BUILD - // /m - if (realTarget.empty()) { - realTarget = "ALL_BUILD"; - } - if (realTarget == "clean") { - makeCommand.add(std::string(projectName) + ".sln"); - makeCommand.add("/t:Clean"); - } else { - std::string targetProject(realTarget); - targetProject += ".vcxproj"; - if (targetProject.find('/') == std::string::npos) { - // it might be in a subdir - if (cmSlnProjectEntry const* proj = - slnData.GetProjectByName(realTarget)) { - targetProject = proj->GetRelativePath(); - cmSystemTools::ConvertToUnixSlashes(targetProject); + if (tname == "clean") { + makeCommand.Add(std::string(projectName) + ".sln"); + makeCommand.Add("/t:Clean"); + } else { + std::string targetProject(tname); + targetProject += ".vcxproj"; + if (targetProject.find('/') == std::string::npos) { + // it might be in a subdir + if (cmSlnProjectEntry const* proj = slnData.GetProjectByName(tname)) { + targetProject = proj->GetRelativePath(); + cmSystemTools::ConvertToUnixSlashes(targetProject); + } } + makeCommand.Add(std::move(targetProject)); } - makeCommand.add(std::move(targetProject)); - } - std::string configArg = "/p:Configuration="; - if (!config.empty()) { - configArg += config; - } else { - configArg += "Debug"; - } - makeCommand.add(configArg); - makeCommand.add(std::string("/p:Platform=") + this->GetPlatformName()); - makeCommand.add(std::string("/p:VisualStudioVersion=") + - this->GetIDEVersion()); - - if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { - if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { - makeCommand.add("/m"); + std::string configArg = "/p:Configuration="; + if (!config.empty()) { + configArg += config; } else { - makeCommand.add(std::string("/m:") + std::to_string(jobs)); + configArg += "Debug"; + } + makeCommand.Add(configArg); + makeCommand.Add(std::string("/p:Platform=") + this->GetPlatformName()); + makeCommand.Add(std::string("/p:VisualStudioVersion=") + + this->GetIDEVersion()); + + if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { + if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { + makeCommand.Add("/m"); + } else { + makeCommand.Add(std::string("/m:") + std::to_string(jobs)); + } + // Having msbuild.exe and cl.exe using multiple jobs is discouraged + makeCommand.Add("/p:CL_MPCount=1"); } - // Having msbuild.exe and cl.exe using multiple jobs is discouraged - makeCommand.add("/p:CL_MPCount=1"); - } - - // Respect the verbosity: 'n' normal will show build commands - // 'm' minimal only the build step's title - makeCommand.add(std::string("/v:") + ((verbose) ? "n" : "m")); - makeCommand.add(makeOptions.begin(), makeOptions.end()); + // Respect the verbosity: 'n' normal will show build commands + // 'm' minimal only the build step's title + makeCommand.Add(std::string("/v:") + ((verbose) ? "n" : "m")); + makeCommand.Add(makeOptions.begin(), makeOptions.end()); + makeCommands.emplace_back(std::move(makeCommand)); + } + return makeCommands; } bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf) @@ -1005,7 +1017,7 @@ bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf) winSDK_7_1)) { std::ostringstream m; m << "Found Windows SDK v7.1: " << winSDK_7_1; - mf->DisplayStatus(m.str().c_str(), -1); + mf->DisplayStatus(m.str(), -1); this->DefaultPlatformToolset = "Windows7.1SDK"; return true; } else { diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index 3ef7abf..2f532a6 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -22,17 +22,14 @@ public: bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override; bool SetGeneratorToolset(std::string const& ts, cmMakefile* mf) override; - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; - - ///! create the correct local generator + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; + + //! create the correct local generator cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override; /** diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx index 4eb78ba..4b74ef1 100644 --- a/Source/cmGlobalVisualStudio11Generator.cxx +++ b/Source/cmGlobalVisualStudio11Generator.cxx @@ -252,15 +252,10 @@ cmGlobalVisualStudio11Generator::GetInstalledWindowsCESDKs() return ret; } -bool cmGlobalVisualStudio11Generator::NeedsDeploy( - cmStateEnums::TargetType type) const +bool cmGlobalVisualStudio11Generator::TargetSystemSupportsDeployment() const { - if ((type == cmStateEnums::EXECUTABLE || - type == cmStateEnums::SHARED_LIBRARY) && - (this->SystemIsWindowsPhone || this->SystemIsWindowsStore)) { - return true; - } - return cmGlobalVisualStudio10Generator::NeedsDeploy(type); + return this->SystemIsWindowsPhone || this->SystemIsWindowsStore || + cmGlobalVisualStudio10Generator::TargetSystemSupportsDeployment(); } bool cmGlobalVisualStudio11Generator::IsWindowsDesktopToolsetInstalled() const diff --git a/Source/cmGlobalVisualStudio11Generator.h b/Source/cmGlobalVisualStudio11Generator.h index 8b4c8b7..f8cce18 100644 --- a/Source/cmGlobalVisualStudio11Generator.h +++ b/Source/cmGlobalVisualStudio11Generator.h @@ -45,8 +45,8 @@ protected: bool UseFolderProperty() const override; static std::set<std::string> GetInstalledWindowsCESDKs(); - /** Return true if the configuration needs to be deployed */ - bool NeedsDeploy(cmStateEnums::TargetType type) const override; + /** Return true if target system supports debugging deployment. */ + bool TargetSystemSupportsDeployment() const override; private: class Factory; diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx index 2025867..6509b56 100644 --- a/Source/cmGlobalVisualStudio14Generator.cxx +++ b/Source/cmGlobalVisualStudio14Generator.cxx @@ -179,7 +179,7 @@ void cmGlobalVisualStudio14Generator::SetWindowsTargetPlatformVersion( std::ostringstream e; e << "Selecting Windows SDK version " << this->WindowsTargetPlatformVersion << " to target Windows " << this->SystemVersion << "."; - mf->DisplayStatus(e.str().c_str(), -1); + mf->DisplayStatus(e.str(), -1); } mf->AddDefinition("CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION", this->WindowsTargetPlatformVersion.c_str()); diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index d457f60..c31dbf4 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -101,7 +101,7 @@ void cmGlobalVisualStudio7Generator::EnableLanguage( } // Create list of configurations requested by user's cache, if any. - this->cmGlobalGenerator::EnableLanguage(lang, mf, optional); + this->cmGlobalVisualStudioGenerator::EnableLanguage(lang, mf, optional); // if this environment variable is set, then copy it to // a static cache entry. It will be used by @@ -190,11 +190,14 @@ const char* cmGlobalVisualStudio7Generator::ExternalProjectType( } return "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942"; } -void cmGlobalVisualStudio7Generator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& projectName, const std::string& /*projectDir*/, - const std::string& targetName, const std::string& config, bool /*fast*/, - int /*jobs*/, bool /*verbose*/, std::vector<std::string> const& makeOptions) + +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalVisualStudio7Generator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& /*projectDir*/, + std::vector<std::string> const& targetNames, const std::string& config, + bool /*fast*/, int /*jobs*/, bool /*verbose*/, + std::vector<std::string> const& makeOptions) { // Select the caller- or user-preferred make program, else devenv. std::string makeProgramSelected = @@ -210,27 +213,42 @@ void cmGlobalVisualStudio7Generator::GenerateBuildCommand( } // Workaround to convince VCExpress.exe to produce output. - makeCommand.RequiresOutputForward = + const bool requiresOutputForward = (makeProgramLower.find("vcexpress") != std::string::npos); + std::vector<GeneratedMakeCommand> makeCommands; - makeCommand.add(makeProgramSelected); - - makeCommand.add(std::string(projectName) + ".sln"); - std::string realTarget = targetName; - bool clean = false; - if (realTarget == "clean") { - clean = true; - realTarget = "ALL_BUILD"; + std::vector<std::string> realTargetNames = targetNames; + if (targetNames.empty() || + ((targetNames.size() == 1) && targetNames.front().empty())) { + realTargetNames = { "ALL_BUILD" }; } - - makeCommand.add((clean ? "/clean" : "/build")); - makeCommand.add((config.empty() ? "Debug" : config)); - makeCommand.add("/project"); - makeCommand.add((realTarget.empty() ? "ALL_BUILD" : realTarget)); - makeCommand.add(makeOptions.begin(), makeOptions.end()); + for (const auto& tname : realTargetNames) { + std::string realTarget; + if (!tname.empty()) { + realTarget = tname; + } else { + continue; + } + bool clean = false; + if (realTarget == "clean") { + clean = true; + realTarget = "ALL_BUILD"; + } + GeneratedMakeCommand makeCommand; + makeCommand.RequiresOutputForward = requiresOutputForward; + makeCommand.Add(makeProgramSelected); + makeCommand.Add(std::string(projectName) + ".sln"); + makeCommand.Add((clean ? "/clean" : "/build")); + makeCommand.Add((config.empty() ? "Debug" : config)); + makeCommand.Add("/project"); + makeCommand.Add(realTarget); + makeCommand.Add(makeOptions.begin(), makeOptions.end()); + makeCommands.emplace_back(std::move(makeCommand)); + } + return makeCommands; } -///! Create a local generator appropriate to this Global Generator +//! Create a local generator appropriate to this Global Generator cmLocalGenerator* cmGlobalVisualStudio7Generator::CreateLocalGenerator( cmMakefile* mf) { diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index 3f1c173..1e76383 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -20,7 +20,7 @@ class cmGlobalVisualStudio7Generator : public cmGlobalVisualStudioGenerator public: ~cmGlobalVisualStudio7Generator(); - ///! Create a local generator appropriate to this Global Generator + //! Create a local generator appropriate to this Global Generator cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override; #if defined(CMAKE_BUILD_WITH_CMAKE) @@ -52,22 +52,19 @@ public: * Try running cmake and building a file. This is used for dynamically * loaded commands, not as part of the usual build process. */ - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; /** * Generate the DSW workspace file. */ virtual void OutputSLNFile(); - ///! Lookup a stored GUID or compute one deterministically. + //! Lookup a stored GUID or compute one deterministically. std::string GetGUID(std::string const& name); /** Append the subdirectory for the given configuration. */ @@ -76,7 +73,7 @@ public: const std::string& suffix, std::string& dir) override; - ///! What is the configurations directory variable called? + //! What is the configurations directory variable called? const char* GetCMakeCFGIntDir() const override { return "$(ConfigurationName)"; diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index f6db018..21abdf7 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -205,7 +205,7 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget() "Checking Build System", no_working_directory, true, false)) { gt->AddSource(file->GetFullPath()); } else { - cmSystemTools::Error("Error adding rule for ", stamps[0].c_str()); + cmSystemTools::Error("Error adding rule for " + stamps[0]); } } @@ -273,7 +273,7 @@ void cmGlobalVisualStudio8Generator::WriteProjectConfigurations( : this->GetPlatformName()) << "\n"; } - if (this->NeedsDeploy(target.GetType())) { + if (this->NeedsDeploy(target, dstConfig)) { fout << "\t\t{" << guid << "}." << i << "|" << this->GetPlatformName() << ".Deploy.0 = " << dstConfig << "|" << (!platformMapping.empty() ? platformMapping @@ -284,11 +284,32 @@ void cmGlobalVisualStudio8Generator::WriteProjectConfigurations( } bool cmGlobalVisualStudio8Generator::NeedsDeploy( - cmStateEnums::TargetType type) const + cmGeneratorTarget const& target, const char* config) const { - bool needsDeploy = - (type == cmStateEnums::EXECUTABLE || type == cmStateEnums::SHARED_LIBRARY); - return this->TargetsWindowsCE() && needsDeploy; + cmStateEnums::TargetType type = target.GetType(); + bool noDeploy = DeployInhibited(target, config); + return !noDeploy && + (type == cmStateEnums::EXECUTABLE || + type == cmStateEnums::SHARED_LIBRARY) && + this->TargetSystemSupportsDeployment(); +} + +bool cmGlobalVisualStudio8Generator::DeployInhibited( + cmGeneratorTarget const& target, const char* config) const +{ + bool rVal = false; + if (const char* propStr = target.GetProperty("VS_NO_SOLUTION_DEPLOY")) { + cmGeneratorExpression ge; + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(propStr); + std::string prop = cge->Evaluate(target.LocalGenerator, config); + rVal = cmSystemTools::IsOn(prop); + } + return rVal; +} + +bool cmGlobalVisualStudio8Generator::TargetSystemSupportsDeployment() const +{ + return this->TargetsWindowsCE(); } bool cmGlobalVisualStudio8Generator::ComputeTargetDepends() diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h index 8719bf3..352bc3c 100644 --- a/Source/cmGlobalVisualStudio8Generator.h +++ b/Source/cmGlobalVisualStudio8Generator.h @@ -13,7 +13,7 @@ class cmGlobalVisualStudio8Generator : public cmGlobalVisualStudio71Generator { public: - ///! Get the name for the generator. + //! Get the name for the generator. std::string GetName() const override { return this->Name; } /** Get the name of the main stamp list file. */ @@ -54,7 +54,15 @@ protected: bool AddCheckTarget(); /** Return true if the configuration needs to be deployed */ - virtual bool NeedsDeploy(cmStateEnums::TargetType type) const; + virtual bool NeedsDeploy(cmGeneratorTarget const& target, + const char* config) const; + + /** Returns true if deployment has been disabled in cmake file. */ + bool DeployInhibited(cmGeneratorTarget const& target, + const char* config) const; + + /** Returns true if the target system support debugging deployment. */ + virtual bool TargetSystemSupportsDeployment() const; static cmIDEFlagTable const* GetExtraFlagTableVS8(); void WriteSolutionConfigurations( diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 639dc22..cd0355f 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -12,6 +12,7 @@ #include "cmAlgorithms.h" #include "cmCallVisualStudioMacro.h" +#include "cmCustomCommand.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmLocalVisualStudioGenerator.h" @@ -52,6 +53,14 @@ void cmGlobalVisualStudioGenerator::SetVersion(VSVersion v) this->Version = v; } +void cmGlobalVisualStudioGenerator::EnableLanguage( + std::vector<std::string> const& lang, cmMakefile* mf, bool optional) +{ + mf->AddDefinition("CMAKE_VS_PLATFORM_NAME_DEFAULT", + this->DefaultPlatformName.c_str()); + this->cmGlobalGenerator::EnableLanguage(lang, mf, optional); +} + bool cmGlobalVisualStudioGenerator::SetGeneratorPlatform(std::string const& p, cmMakefile* mf) { @@ -194,9 +203,7 @@ void cmGlobalVisualStudioGenerator::AddExtraIDETargets() // Now make all targets depend on the ALL_BUILD target for (cmLocalGenerator const* i : gen) { - std::vector<cmGeneratorTarget*> const& targets = - i->GetGeneratorTargets(); - for (cmGeneratorTarget* tgt : targets) { + for (cmGeneratorTarget* tgt : i->GetGeneratorTargets()) { if (tgt->GetType() == cmStateEnums::GLOBAL_TARGET || tgt->IsImported()) { continue; @@ -309,17 +316,7 @@ void cmGlobalVisualStudioGenerator::CallVisualStudioMacro( std::vector<std::string> filenames; this->GetFilesReplacedDuringGenerate(filenames); if (!filenames.empty()) { - // Convert vector to semi-colon delimited string of filenames: - std::string projects; - std::vector<std::string>::iterator it = filenames.begin(); - if (it != filenames.end()) { - projects = *it; - ++it; - } - for (; it != filenames.end(); ++it) { - projects += ";"; - projects += *it; - } + std::string projects = cmJoin(filenames, ";"); cmCallVisualStudioMacro::CallMacro( topLevelSlnName, CMAKE_VSMACROS_RELOAD_MACRONAME, projects, this->GetCMakeInstance()->GetDebugOutput()); @@ -359,7 +356,7 @@ void cmGlobalVisualStudioGenerator::FillLinkClosure( cmGlobalVisualStudioGenerator::TargetSet const& cmGlobalVisualStudioGenerator::GetTargetLinkClosure(cmGeneratorTarget* target) { - TargetSetMap::iterator i = this->TargetLinkClosure.find(target); + auto i = this->TargetLinkClosure.find(target); if (i == this->TargetLinkClosure.end()) { TargetSetMap::value_type entry(target, TargetSet()); i = this->TargetLinkClosure.insert(entry).first; @@ -393,11 +390,8 @@ bool cmGlobalVisualStudioGenerator::ComputeTargetDepends() return false; } for (auto const& it : this->ProjectMap) { - std::vector<cmLocalGenerator*> const& gen = it.second; - for (const cmLocalGenerator* i : gen) { - std::vector<cmGeneratorTarget*> const& targets = - i->GetGeneratorTargets(); - for (cmGeneratorTarget* ti : targets) { + for (const cmLocalGenerator* i : it.second) { + for (cmGeneratorTarget* ti : i->GetGeneratorTargets()) { this->ComputeVSTargetDepends(ti); } } @@ -449,8 +443,7 @@ void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends( std::set<cmGeneratorTarget const*> linkDepends; if (target->GetType() != cmStateEnums::STATIC_LIBRARY) { for (cmTargetDepend const& di : depends) { - cmTargetDepend dep = di; - if (dep.IsLink()) { + if (di.IsLink()) { this->FollowLinkDepends(di, linkDepends); } } @@ -459,8 +452,7 @@ void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends( // Collect explicit util dependencies (add_dependencies). std::set<cmGeneratorTarget const*> utilDepends; for (cmTargetDepend const& di : depends) { - cmTargetDepend dep = di; - if (dep.IsUtil()) { + if (di.IsUtil()) { this->FollowLinkDepends(di, utilDepends); } } @@ -504,7 +496,7 @@ bool cmGlobalVisualStudioGenerator::FindMakeProgram(cmMakefile* mf) std::string cmGlobalVisualStudioGenerator::GetUtilityDepend( cmGeneratorTarget const* target) { - UtilityDependsMap::iterator i = this->UtilityDepends.find(target); + auto i = this->UtilityDepends.find(target); if (i == this->UtilityDepends.end()) { std::string name = this->WriteUtilityDepend(target); UtilityDependsMap::value_type entry(target, name); @@ -922,7 +914,7 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand( cmdl.push_back(objs_file); cmGeneratedFileStream fout(objs_file.c_str()); if (!fout) { - cmSystemTools::Error("could not open ", objs_file.c_str()); + cmSystemTools::Error("could not open " + objs_file); return; } @@ -930,11 +922,10 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand( std::vector<std::string> objs; for (cmSourceFile const* it : objectSources) { // Find the object file name corresponding to this source file. - std::map<cmSourceFile const*, std::string>::const_iterator map_it = - mapping.find(it); // It must exist because we populated the mapping just above. - assert(!map_it->second.empty()); - std::string objFile = obj_dir + map_it->second; + const auto& v = mapping[it]; + assert(!v.empty()); + std::string objFile = obj_dir + v; objs.push_back(objFile); } std::vector<cmSourceFile const*> externalObjectSources; diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 039191c..cbab329 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -50,6 +50,9 @@ public: /** Is the installed VS an Express edition? */ bool IsExpressEdition() const { return this->ExpressEdition; } + void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*, + bool optional) override; + bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override; /** diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index f52abd0..2ba1aff 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx @@ -211,8 +211,8 @@ class cmGlobalVisualStudioVersionedGenerator::Factory16 : public cmGlobalGeneratorFactory { public: - virtual cmGlobalGenerator* CreateGlobalGenerator(const std::string& name, - cmake* cm) const + cmGlobalGenerator* CreateGlobalGenerator(const std::string& name, + cmake* cm) const override { std::string genName; const char* p = cmVS16GenName(name, genName); @@ -226,7 +226,7 @@ public: return 0; } - virtual void GetDocumentation(cmDocumentationEntry& entry) const + void GetDocumentation(cmDocumentationEntry& entry) const override { entry.Name = std::string(vs16generatorName); entry.Brief = "Generates Visual Studio 2019 project files. " diff --git a/Source/cmGlobalWatcomWMakeGenerator.cxx b/Source/cmGlobalWatcomWMakeGenerator.cxx index c02c471..8a27384 100644 --- a/Source/cmGlobalWatcomWMakeGenerator.cxx +++ b/Source/cmGlobalWatcomWMakeGenerator.cxx @@ -3,6 +3,7 @@ #include "cmGlobalWatcomWMakeGenerator.h" #include "cmDocumentationEntry.h" +#include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmState.h" #include "cmake.h" @@ -50,15 +51,16 @@ void cmGlobalWatcomWMakeGenerator::GetDocumentation( entry.Brief = "Generates Watcom WMake makefiles."; } -void cmGlobalWatcomWMakeGenerator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& projectName, const std::string& projectDir, - const std::string& targetName, const std::string& config, bool fast, - int /*jobs*/, bool verbose, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalWatcomWMakeGenerator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int /*jobs*/, bool verbose, + std::vector<std::string> const& makeOptions) { - this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( - makeCommand, makeProgram, projectName, projectDir, targetName, config, - fast, cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions); + return this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand( + makeProgram, projectName, projectDir, targetNames, config, fast, + cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions); } void cmGlobalWatcomWMakeGenerator::PrintBuildCommandAdvice(std::ostream& os, diff --git a/Source/cmGlobalWatcomWMakeGenerator.h b/Source/cmGlobalWatcomWMakeGenerator.h index 6680b19..3ca5e7d 100644 --- a/Source/cmGlobalWatcomWMakeGenerator.h +++ b/Source/cmGlobalWatcomWMakeGenerator.h @@ -29,7 +29,7 @@ public: { return new cmGlobalGeneratorSimpleFactory<cmGlobalWatcomWMakeGenerator>(); } - ///! Get the name for the generator. + //! Get the name for the generator. std::string GetName() const override { return cmGlobalWatcomWMakeGenerator::GetActualName(); @@ -50,15 +50,12 @@ public: bool AllowDeleteOnError() const override { return false; } protected: - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override; }; diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 57de60e..db673bb 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -12,6 +12,7 @@ #include "cmAlgorithms.h" #include "cmComputeLinkInformation.h" +#include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmDocumentationEntry.h" #include "cmGeneratedFileStream.h" @@ -217,7 +218,7 @@ cmGlobalGenerator* cmGlobalXCodeGenerator::Factory::CreateGlobalGenerator( sscanf(version_string.c_str(), "%u.%u", &v[0], &v[1]); unsigned int version_number = 10 * v[0] + v[1]; - if (version_number < 30) { + if (version_number < 50) { cm->IssueMessage(MessageType::FATAL_ERROR, "Xcode " + version_string + " not supported."); return nullptr; @@ -256,15 +257,11 @@ std::string const& cmGlobalXCodeGenerator::GetXcodeBuildCommand() std::string cmGlobalXCodeGenerator::FindXcodeBuildCommand() { - if (this->XcodeVersion >= 40) { - std::string makeProgram = cmSystemTools::FindProgram("xcodebuild"); - if (makeProgram.empty()) { - makeProgram = "xcodebuild"; - } - return makeProgram; + std::string makeProgram = cmSystemTools::FindProgram("xcodebuild"); + if (makeProgram.empty()) { + makeProgram = "xcodebuild"; } - // Use cmakexbuild wrapper to suppress environment dump from output. - return cmSystemTools::GetCMakeCommand() + "xbuild"; + return makeProgram; } bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts, @@ -338,49 +335,61 @@ bool cmGlobalXCodeGenerator::Open(const std::string& bindir, return ret; } -void cmGlobalXCodeGenerator::GenerateBuildCommand( - GeneratedMakeCommand& makeCommand, const std::string& makeProgram, - const std::string& projectName, const std::string& /*projectDir*/, - const std::string& targetName, const std::string& config, bool /*fast*/, - int jobs, bool /*verbose*/, std::vector<std::string> const& makeOptions) +std::vector<cmGlobalGenerator::GeneratedMakeCommand> +cmGlobalXCodeGenerator::GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& /*projectDir*/, + std::vector<std::string> const& targetNames, const std::string& config, + bool /*fast*/, int jobs, bool /*verbose*/, + std::vector<std::string> const& makeOptions) { + GeneratedMakeCommand makeCommand; // now build the test - makeCommand.add( + makeCommand.Add( this->SelectMakeProgram(makeProgram, this->GetXcodeBuildCommand())); if (!projectName.empty()) { - makeCommand.add("-project"); + makeCommand.Add("-project"); std::string projectArg = projectName; projectArg += ".xcode"; projectArg += "proj"; - makeCommand.add(projectArg); + makeCommand.Add(projectArg); } - - bool clean = false; - std::string realTarget = targetName; - if (realTarget == "clean") { - clean = true; - realTarget = "ALL_BUILD"; + if (std::find(targetNames.begin(), targetNames.end(), "clean") != + targetNames.end()) { + makeCommand.Add("clean"); + makeCommand.Add("-target", "ALL_BUILD"); + } else { + makeCommand.Add("build"); + if (targetNames.empty() || + ((targetNames.size() == 1) && targetNames.front().empty())) { + makeCommand.Add("-target", "ALL_BUILD"); + } else { + for (const auto& tname : targetNames) { + if (!tname.empty()) { + makeCommand.Add("-target", tname); + } + } + } } - makeCommand.add((clean ? "clean" : "build")); - makeCommand.add("-target", (realTarget.empty() ? "ALL_BUILD" : realTarget)); - makeCommand.add("-configuration", (config.empty() ? "Debug" : config)); + makeCommand.Add("-configuration", (config.empty() ? "Debug" : config)); if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { - makeCommand.add("-jobs"); + makeCommand.Add("-jobs"); if (jobs != cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { - makeCommand.add(std::to_string(jobs)); + makeCommand.Add(std::to_string(jobs)); } } if (this->XcodeVersion >= 70) { - makeCommand.add("-hideShellScriptEnvironment"); + makeCommand.Add("-hideShellScriptEnvironment"); } - makeCommand.add(makeOptions.begin(), makeOptions.end()); + makeCommand.Add(makeOptions.begin(), makeOptions.end()); + return { std::move(makeCommand) }; } -///! Create a local generator appropriate to this Global Generator +//! Create a local generator appropriate to this Global Generator cmLocalGenerator* cmGlobalXCodeGenerator::CreateLocalGenerator(cmMakefile* mf) { return new cmLocalXCodeGenerator(this, mf); @@ -550,12 +559,7 @@ void cmGlobalXCodeGenerator::AddExtraTargets( // run the depend check makefile as a post build rule // this will make sure that when the next target is built // things are up-to-date - if (target->GetType() == cmStateEnums::OBJECT_LIBRARY || - (this->XcodeVersion < 50 && - (target->GetType() == cmStateEnums::EXECUTABLE || - target->GetType() == cmStateEnums::STATIC_LIBRARY || - target->GetType() == cmStateEnums::SHARED_LIBRARY || - target->GetType() == cmStateEnums::MODULE_LIBRARY))) { + if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) { makeHelper.back() = // fill placeholder this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)"); cmCustomCommandLines commandLines; @@ -1019,7 +1023,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( this->CreateString(fileType)); // Store the file path relative to the top of the source tree. - std::string path = this->RelativeToSource(fullpath.c_str()); + std::string path = this->RelativeToSource(fullpath); std::string name = cmSystemTools::GetFilenameName(path); const char* sourceTree = (cmSystemTools::FileIsFullPath(path.c_str()) ? "<absolute>" @@ -1190,23 +1194,6 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( } } - if (this->XcodeVersion < 50) { - // Add object library contents as external objects. (Equivalent to - // the externalObjFiles above, except each one is not a cmSourceFile - // within the target.) - std::vector<cmSourceFile const*> objs; - gtgt->GetExternalObjects(objs, ""); - for (auto sourceFile : objs) { - if (sourceFile->GetObjectLibrary().empty()) { - continue; - } - std::string const& obj = sourceFile->GetFullPath(); - cmXCodeObject* xsf = - this->CreateXCodeSourceFileFromPath(obj, gtgt, "", nullptr); - externalObjFiles.push_back(xsf); - } - } - // some build phases only apply to bundles and/or frameworks bool isFrameworkTarget = gtgt->IsFrameworkOnApple(); bool isBundleTarget = gtgt->GetPropertyAsBool("MACOSX_BUNDLE"); @@ -1820,6 +1807,10 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, this->CurrentLocalGenerator->AddLanguageFlags(flags, gtgt, lang, configName); + if (gtgt->IsIPOEnabled(lang, configName)) { + this->CurrentLocalGenerator->AppendFeatureOptions(flags, lang, "IPO"); + } + // Add shared-library flags if needed. this->CurrentLocalGenerator->AddCMP0018Flags(flags, gtgt, lang, configName); @@ -2009,7 +2000,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, // so let it replace the framework name. This avoids creating // a per-configuration Info.plist file. this->CurrentLocalGenerator->GenerateFrameworkInfoPList( - gtgt, "$(EXECUTABLE_NAME)", plist.c_str()); + gtgt, "$(EXECUTABLE_NAME)", plist); buildSettings->AddAttribute("INFOPLIST_FILE", this->CreateString(plist)); buildSettings->AddAttribute("MACH_O_TYPE", @@ -2050,7 +2041,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, // a per-configuration Info.plist file. The cfbundle plist // is very similar to the application bundle plist this->CurrentLocalGenerator->GenerateAppleInfoPList( - gtgt, "$(EXECUTABLE_NAME)", plist.c_str()); + gtgt, "$(EXECUTABLE_NAME)", plist); buildSettings->AddAttribute("INFOPLIST_FILE", this->CreateString(plist)); } else { @@ -2084,7 +2075,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, // so let it replace the framework name. This avoids creating // a per-configuration Info.plist file. this->CurrentLocalGenerator->GenerateFrameworkInfoPList( - gtgt, "$(EXECUTABLE_NAME)", plist.c_str()); + gtgt, "$(EXECUTABLE_NAME)", plist); buildSettings->AddAttribute("INFOPLIST_FILE", this->CreateString(plist)); } else { @@ -2122,7 +2113,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, // so let it replace the executable name. This avoids creating // a per-configuration Info.plist file. this->CurrentLocalGenerator->GenerateAppleInfoPList( - gtgt, "$(EXECUTABLE_NAME)", plist.c_str()); + gtgt, "$(EXECUTABLE_NAME)", plist); buildSettings->AddAttribute("INFOPLIST_FILE", this->CreateString(plist)); } @@ -2130,9 +2121,6 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, default: break; } - if (this->XcodeVersion < 40) { - buildSettings->AddAttribute("PREBINDING", this->CreateString("NO")); - } BuildObjectListOrString dirs(this, true); BuildObjectListOrString fdirs(this, true); @@ -2782,8 +2770,7 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) // Loop over configuration types and set per-configuration info. for (auto const& configName : this->CurrentConfigurationTypes) { - // Get the current configuration name. - if (this->XcodeVersion >= 50) { + { // Add object library contents as link flags. std::string linkObjs; const char* sep = ""; @@ -2893,7 +2880,7 @@ bool cmGlobalXCodeGenerator::CreateGroups( // Put cmSourceFile instances in proper groups: for (auto const& si : gtgt->GetAllConfigSources()) { cmSourceFile const* sf = si.Source; - if (this->XcodeVersion >= 50 && !sf->GetObjectLibrary().empty()) { + if (!sf->GetObjectLibrary().empty()) { // Object library files go on the link line instead. continue; } @@ -3081,20 +3068,12 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( v << std::setfill('0') << std::setw(4) << XcodeVersion * 10; group->AddAttribute("LastUpgradeCheck", this->CreateString(v.str())); this->RootObject->AddAttribute("attributes", group); - if (this->XcodeVersion >= 32) { - this->RootObject->AddAttribute("compatibilityVersion", - this->CreateString("Xcode 3.2")); - } else if (this->XcodeVersion >= 31) { - this->RootObject->AddAttribute("compatibilityVersion", - this->CreateString("Xcode 3.1")); - } else { - this->RootObject->AddAttribute("compatibilityVersion", - this->CreateString("Xcode 3.0")); - } + this->RootObject->AddAttribute("compatibilityVersion", + this->CreateString("Xcode 3.2")); // Point Xcode at the top of the source tree. { std::string pdir = - this->RelativeToBinary(root->GetCurrentSourceDirectory().c_str()); + this->RelativeToBinary(root->GetCurrentSourceDirectory()); this->RootObject->AddAttribute("projectDirPath", this->CreateString(pdir)); this->RootObject->AddAttribute("projectRoot", this->CreateString("")); } @@ -3433,10 +3412,8 @@ void cmGlobalXCodeGenerator::OutputXCodeProject( } this->WriteXCodePBXProj(fout, root, generators); - if (this->IsGeneratingScheme(root)) { - this->OutputXCodeSharedSchemes(xcodeDir); - } - this->OutputXCodeWorkspaceSettings(xcodeDir, root); + bool hasGeneratedSchemes = this->OutputXCodeSharedSchemes(xcodeDir, root); + this->OutputXCodeWorkspaceSettings(xcodeDir, hasGeneratedSchemes); this->ClearXCodeObjects(); @@ -3446,17 +3423,8 @@ void cmGlobalXCodeGenerator::OutputXCodeProject( root->GetBinaryDirectory()); } -bool cmGlobalXCodeGenerator::IsGeneratingScheme(cmLocalGenerator* root) const -{ - // Since the lowest available Xcode version for testing was 6.4, - // I'm setting this as a limit then - return this->XcodeVersion >= 64 && - (root->GetMakefile()->GetCMakeInstance()->GetIsInTryCompile() || - root->GetMakefile()->IsOn("CMAKE_XCODE_GENERATE_SCHEME")); -} - -void cmGlobalXCodeGenerator::OutputXCodeSharedSchemes( - const std::string& xcProjDir) +bool cmGlobalXCodeGenerator::OutputXCodeSharedSchemes( + const std::string& xcProjDir, cmLocalGenerator* root) { // collect all tests for the targets std::map<std::string, cmXCodeScheme::TestObjects> testables; @@ -3480,21 +3448,33 @@ void cmGlobalXCodeGenerator::OutputXCodeSharedSchemes( } // generate scheme - for (auto obj : this->XCodeObjects) { - if (obj->GetType() == cmXCodeObject::OBJECT && - (obj->GetIsA() == cmXCodeObject::PBXNativeTarget || - obj->GetIsA() == cmXCodeObject::PBXAggregateTarget)) { - const std::string& targetName = obj->GetTarget()->GetName(); - cmXCodeScheme schm(obj, testables[targetName], - this->CurrentConfigurationTypes, this->XcodeVersion); - schm.WriteXCodeSharedScheme(xcProjDir, - this->RelativeToSource(xcProjDir.c_str())); + bool ret = false; + + // Since the lowest available Xcode version for testing was 6.4, + // I'm setting this as a limit then + if (this->XcodeVersion >= 64) { + for (auto obj : this->XCodeObjects) { + if (obj->GetType() == cmXCodeObject::OBJECT && + (obj->GetIsA() == cmXCodeObject::PBXNativeTarget || + obj->GetIsA() == cmXCodeObject::PBXAggregateTarget) && + (root->GetMakefile()->GetCMakeInstance()->GetIsInTryCompile() || + obj->GetTarget()->GetPropertyAsBool("XCODE_GENERATE_SCHEME"))) { + const std::string& targetName = obj->GetTarget()->GetName(); + cmXCodeScheme schm(obj, testables[targetName], + this->CurrentConfigurationTypes, + this->XcodeVersion); + schm.WriteXCodeSharedScheme(xcProjDir, + this->RelativeToSource(xcProjDir)); + ret = true; + } } } + + return ret; } void cmGlobalXCodeGenerator::OutputXCodeWorkspaceSettings( - const std::string& xcProjDir, cmLocalGenerator* root) + const std::string& xcProjDir, bool hasGeneratedSchemes) { std::string xcodeSharedDataDir = xcProjDir; xcodeSharedDataDir += "/project.xcworkspace/xcshareddata"; @@ -3520,7 +3500,7 @@ void cmGlobalXCodeGenerator::OutputXCodeWorkspaceSettings( xout.Element("key", "BuildSystemType"); xout.Element("string", "Original"); } - if (this->IsGeneratingScheme(root)) { + if (hasGeneratedSchemes) { xout.Element("key", "IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded"); xout.Element("false"); @@ -3545,13 +3525,7 @@ void cmGlobalXCodeGenerator::WriteXCodePBXProj(std::ostream& fout, cmXCodeObject::Indent(1, fout); fout << "};\n"; cmXCodeObject::Indent(1, fout); - if (this->XcodeVersion >= 32) { - fout << "objectVersion = 46;\n"; - } else if (this->XcodeVersion >= 31) { - fout << "objectVersion = 45;\n"; - } else { - fout << "objectVersion = 44;\n"; - } + fout << "objectVersion = 46;\n"; cmXCode21Object::PrintList(this->XCodeObjects, fout); cmXCodeObject::Indent(1, fout); fout << "rootObject = " << this->RootObject->GetId() @@ -3596,7 +3570,7 @@ std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake( return cmSystemTools::ConvertToOutputPath(p); } -std::string cmGlobalXCodeGenerator::RelativeToSource(const char* p) +std::string cmGlobalXCodeGenerator::RelativeToSource(const std::string& p) { // We force conversion because Xcode breakpoints do not work unless // they are in a file named relative to the source tree. @@ -3604,7 +3578,7 @@ std::string cmGlobalXCodeGenerator::RelativeToSource(const char* p) cmSystemTools::JoinPath(this->ProjectSourceDirectoryComponents), p); } -std::string cmGlobalXCodeGenerator::RelativeToBinary(const char* p) +std::string cmGlobalXCodeGenerator::RelativeToBinary(const std::string& p) { return this->CurrentLocalGenerator->MaybeConvertToRelativePath( cmSystemTools::JoinPath(this->ProjectOutputDirectoryComponents), p); @@ -3719,11 +3693,7 @@ void cmGlobalXCodeGenerator::AppendFlag(std::string& flags, // Flag value with escaped quotes and backslashes. for (auto c : flag) { if (c == '\'') { - if (this->XcodeVersion >= 40) { - flags += "'\\''"; - } else { - flags += "\\'"; - } + flags += "'\\''"; } else if (c == '\\') { flags += "\\\\"; } else { diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index e1e412d..71446f9 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -36,7 +36,7 @@ public: unsigned int version_number); static cmGlobalGeneratorFactory* NewFactory(); - ///! Get the name for the generator. + //! Get the name for the generator. std::string GetName() const override { return cmGlobalXCodeGenerator::GetActualName(); @@ -46,7 +46,7 @@ public: /** Get the documentation entry for this generator. */ static void GetDocumentation(cmDocumentationEntry& entry); - ///! Create a local generator appropriate to this Global Generator + //! Create a local generator appropriate to this Global Generator cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override; /** @@ -66,15 +66,12 @@ public: * Try running cmake and building a file. This is used for dynalically * loaded commands, not as part of the usual build process. */ - void GenerateBuildCommand(GeneratedMakeCommand& makeCommand, - const std::string& makeProgram, - const std::string& projectName, - const std::string& projectDir, - const std::string& targetName, - const std::string& config, bool fast, int jobs, - bool verbose, - std::vector<std::string> const& makeOptions = - std::vector<std::string>()) override; + std::vector<GeneratedMakeCommand> GenerateBuildCommand( + const std::string& makeProgram, const std::string& projectName, + const std::string& projectDir, std::vector<std::string> const& targetNames, + const std::string& config, bool fast, int jobs, bool verbose, + std::vector<std::string> const& makeOptions = + std::vector<std::string>()) override; /** Append the subdirectory for the given configuration. */ void AppendDirectoryForConfig(const std::string& prefix, @@ -84,9 +81,9 @@ public: bool FindMakeProgram(cmMakefile*) override; - ///! What is the configurations directory variable called? + //! What is the configurations directory variable called? const char* GetCMakeCFGIntDir() const override; - ///! expand CFGIntDir + //! expand CFGIntDir std::string ExpandCFGIntDir(const std::string& str, const std::string& config) const override; @@ -122,8 +119,8 @@ private: const std::string& name); bool CreateGroups(std::vector<cmLocalGenerator*>& generators); std::string XCodeEscapePath(const std::string& p); - std::string RelativeToSource(const char* p); - std::string RelativeToBinary(const char* p); + std::string RelativeToSource(const std::string& p); + std::string RelativeToBinary(const std::string& p); std::string ConvertToRelativeForMake(std::string const& p); void CreateCustomCommands(cmXCodeObject* buildPhases, cmXCodeObject* sourceBuildPhase, @@ -189,11 +186,12 @@ private: std::vector<cmLocalGenerator*>& generators); void OutputXCodeProject(cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators); - bool IsGeneratingScheme(cmLocalGenerator* root) const; // Write shared scheme files for all the native targets - void OutputXCodeSharedSchemes(const std::string& xcProjDir); + // return true if any were written + bool OutputXCodeSharedSchemes(const std::string& xcProjDir, + cmLocalGenerator* root); void OutputXCodeWorkspaceSettings(const std::string& xcProjDir, - cmLocalGenerator* root); + bool hasGeneratedSchemes); void WriteXCodePBXProj(std::ostream& fout, cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators); cmXCodeObject* CreateXCodeFileReferenceFromPath(const std::string& fullpath, diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 20d1a31..dba4bbb 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmInstallCommand.h" +#include "cm_static_string_view.hxx" #include "cmsys/Glob.hxx" #include <set> #include <sstream> @@ -9,7 +10,7 @@ #include <utility> #include "cmAlgorithms.h" -#include "cmCommandArgumentsHelper.h" +#include "cmArgumentParser.h" #include "cmExportSet.h" #include "cmExportSetMap.h" #include "cmGeneratorExpression.h" @@ -219,49 +220,51 @@ bool cmInstallCommand::HandleScriptMode(std::vector<std::string> const& args) return true; } -/*struct InstallPart -{ - InstallPart(cmCommandArgumentsHelper* helper, const char* key, - cmCommandArgumentGroup* group); - cmCAStringVector argVector; - cmInstallCommandArguments args; -};*/ - bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) { // This is the TARGETS mode. std::vector<cmTarget*> targets; - cmCommandArgumentsHelper argHelper; - cmCommandArgumentGroup group; - cmCAStringVector genericArgVector(&argHelper, nullptr); - cmCAStringVector archiveArgVector(&argHelper, "ARCHIVE", &group); - cmCAStringVector libraryArgVector(&argHelper, "LIBRARY", &group); - cmCAStringVector runtimeArgVector(&argHelper, "RUNTIME", &group); - cmCAStringVector objectArgVector(&argHelper, "OBJECTS", &group); - cmCAStringVector frameworkArgVector(&argHelper, "FRAMEWORK", &group); - cmCAStringVector bundleArgVector(&argHelper, "BUNDLE", &group); - cmCAStringVector includesArgVector(&argHelper, "INCLUDES", &group); - cmCAStringVector privateHeaderArgVector(&argHelper, "PRIVATE_HEADER", - &group); - cmCAStringVector publicHeaderArgVector(&argHelper, "PUBLIC_HEADER", &group); - cmCAStringVector resourceArgVector(&argHelper, "RESOURCE", &group); - genericArgVector.Follows(nullptr); - group.Follows(&genericArgVector); - - argHelper.Parse(&args, nullptr); + struct ArgVectors + { + std::vector<std::string> Archive; + std::vector<std::string> Library; + std::vector<std::string> Runtime; + std::vector<std::string> Object; + std::vector<std::string> Framework; + std::vector<std::string> Bundle; + std::vector<std::string> Includes; + std::vector<std::string> PrivateHeader; + std::vector<std::string> PublicHeader; + std::vector<std::string> Resource; + }; + + static auto const argHelper = + cmArgumentParser<ArgVectors>{} + .Bind("ARCHIVE"_s, &ArgVectors::Archive) + .Bind("LIBRARY"_s, &ArgVectors::Library) + .Bind("RUNTIME"_s, &ArgVectors::Runtime) + .Bind("OBJECTS"_s, &ArgVectors::Object) + .Bind("FRAMEWORK"_s, &ArgVectors::Framework) + .Bind("BUNDLE"_s, &ArgVectors::Bundle) + .Bind("INCLUDES"_s, &ArgVectors::Includes) + .Bind("PRIVATE_HEADER"_s, &ArgVectors::PrivateHeader) + .Bind("PUBLIC_HEADER"_s, &ArgVectors::PublicHeader) + .Bind("RESOURCE"_s, &ArgVectors::Resource); + + std::vector<std::string> genericArgVector; + ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector); // now parse the generic args (i.e. the ones not specialized on LIBRARY/ // ARCHIVE, RUNTIME etc. (see above) // These generic args also contain the targets and the export stuff + std::vector<std::string> targetList; + std::string exports; std::vector<std::string> unknownArgs; cmInstallCommandArguments genericArgs(this->DefaultComponentName); - cmCAStringVector targetList(&genericArgs.Parser, "TARGETS"); - cmCAString exports(&genericArgs.Parser, "EXPORT", - &genericArgs.ArgumentGroup); - targetList.Follows(nullptr); - genericArgs.ArgumentGroup.Follows(&targetList); - genericArgs.Parse(&genericArgVector.GetVector(), &unknownArgs); + genericArgs.Bind("TARGETS"_s, targetList); + genericArgs.Bind("EXPORT"_s, exports); + genericArgs.Parse(genericArgVector, &unknownArgs); bool success = genericArgs.Finalize(); cmInstallCommandArguments archiveArgs(this->DefaultComponentName); @@ -277,16 +280,16 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME, ARCHIVE etc. - archiveArgs.Parse(&archiveArgVector.GetVector(), &unknownArgs); - libraryArgs.Parse(&libraryArgVector.GetVector(), &unknownArgs); - runtimeArgs.Parse(&runtimeArgVector.GetVector(), &unknownArgs); - objectArgs.Parse(&objectArgVector.GetVector(), &unknownArgs); - frameworkArgs.Parse(&frameworkArgVector.GetVector(), &unknownArgs); - bundleArgs.Parse(&bundleArgVector.GetVector(), &unknownArgs); - privateHeaderArgs.Parse(&privateHeaderArgVector.GetVector(), &unknownArgs); - publicHeaderArgs.Parse(&publicHeaderArgVector.GetVector(), &unknownArgs); - resourceArgs.Parse(&resourceArgVector.GetVector(), &unknownArgs); - includesArgs.Parse(&includesArgVector.GetVector(), &unknownArgs); + archiveArgs.Parse(argVectors.Archive, &unknownArgs); + libraryArgs.Parse(argVectors.Library, &unknownArgs); + runtimeArgs.Parse(argVectors.Runtime, &unknownArgs); + objectArgs.Parse(argVectors.Object, &unknownArgs); + frameworkArgs.Parse(argVectors.Framework, &unknownArgs); + bundleArgs.Parse(argVectors.Bundle, &unknownArgs); + privateHeaderArgs.Parse(argVectors.PrivateHeader, &unknownArgs); + publicHeaderArgs.Parse(argVectors.PublicHeader, &unknownArgs); + resourceArgs.Parse(argVectors.Resource, &unknownArgs); + includesArgs.Parse(&argVectors.Includes, &unknownArgs); if (!unknownArgs.empty()) { // Unknown argument. @@ -382,7 +385,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) } // Check if there is something to do. - if (targetList.GetVector().empty()) { + if (targetList.empty()) { return true; } @@ -390,7 +393,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) bool dll_platform = !this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty(); - for (std::string const& tgt : targetList.GetVector()) { + for (std::string const& tgt : targetList) { if (this->Makefile->IsAlias(tgt)) { std::ostringstream e; @@ -663,8 +666,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // generators for them. bool createInstallGeneratorsForTargetFileSets = true; - if (target.IsFrameworkOnApple() || - target.GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (target.IsFrameworkOnApple()) { createInstallGeneratorsForTargetFileSets = false; } @@ -748,7 +750,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) // Add this install rule to an export if one was specified and // this is not a namelink-only rule. - if (!exports.GetString().empty() && !namelinkOnly) { + if (!exports.empty() && !namelinkOnly) { cmTargetExport* te = new cmTargetExport; te->TargetName = target.GetName(); te->ArchiveGenerator = archiveGenerator; @@ -759,7 +761,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args) te->RuntimeGenerator = runtimeGenerator; te->ObjectsGenerator = objectGenerator; this->Makefile->GetGlobalGenerator() - ->GetExportSets()[exports.GetString()] + ->GetExportSets()[exports] ->AddTargetExport(te); te->InterfaceIncludeDirectories = @@ -818,11 +820,10 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args) // This is the FILES mode. bool programs = (args[0] == "PROGRAMS"); cmInstallCommandArguments ica(this->DefaultComponentName); - cmCAStringVector files(&ica.Parser, programs ? "PROGRAMS" : "FILES"); - files.Follows(nullptr); - ica.ArgumentGroup.Follows(&files); + std::vector<std::string> files; + ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files); std::vector<std::string> unknownArgs; - ica.Parse(&args, &unknownArgs); + ica.Parse(args, &unknownArgs); if (!unknownArgs.empty()) { // Unknown argument. @@ -840,7 +841,7 @@ bool cmInstallCommand::HandleFilesMode(std::vector<std::string> const& args) return false; } - const std::vector<std::string>& filesVector = files.GetVector(); + const std::vector<std::string>& filesVector = files; // Check if there is something to do. if (filesVector.empty()) { @@ -1271,16 +1272,19 @@ bool cmInstallCommand::HandleExportAndroidMKMode( #ifdef CMAKE_BUILD_WITH_CMAKE // This is the EXPORT mode. cmInstallCommandArguments ica(this->DefaultComponentName); - cmCAString exp(&ica.Parser, "EXPORT_ANDROID_MK"); - cmCAString name_space(&ica.Parser, "NAMESPACE", &ica.ArgumentGroup); - cmCAEnabler exportOld(&ica.Parser, "EXPORT_LINK_INTERFACE_LIBRARIES", - &ica.ArgumentGroup); - cmCAString filename(&ica.Parser, "FILE", &ica.ArgumentGroup); - exp.Follows(nullptr); - - ica.ArgumentGroup.Follows(&exp); + + std::string exp; + std::string name_space; + bool exportOld = false; + std::string filename; + + ica.Bind("EXPORT_ANDROID_MK"_s, exp); + ica.Bind("NAMESPACE"_s, name_space); + ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld); + ica.Bind("FILE"_s, filename); + std::vector<std::string> unknownArgs; - ica.Parse(&args, &unknownArgs); + ica.Parse(args, &unknownArgs); if (!unknownArgs.empty()) { // Unknown argument. @@ -1304,7 +1308,7 @@ bool cmInstallCommand::HandleExportAndroidMKMode( } // Check the file name. - std::string fname = filename.GetString(); + std::string fname = filename; if (fname.find_first_of(":/\\") != std::string::npos) { std::ostringstream e; e << args[0] << " given invalid export file name \"" << fname << "\". " @@ -1325,7 +1329,7 @@ bool cmInstallCommand::HandleExportAndroidMKMode( } if (fname.find_first_of(":/\\") != std::string::npos) { std::ostringstream e; - e << args[0] << " given export name \"" << exp.GetString() << "\". " + e << args[0] << " given export name \"" << exp << "\". " << "This name cannot be safely converted to a file name. " << "Specify a different export name or use the FILE option to set " << "a file name explicitly."; @@ -1338,7 +1342,7 @@ bool cmInstallCommand::HandleExportAndroidMKMode( } cmExportSet* exportSet = - this->Makefile->GetGlobalGenerator()->GetExportSets()[exp.GetString()]; + this->Makefile->GetGlobalGenerator()->GetExportSets()[exp]; cmInstallGenerator::MessageLevel message = cmInstallGenerator::SelectMessageLevel(this->Makefile); @@ -1347,8 +1351,8 @@ bool cmInstallCommand::HandleExportAndroidMKMode( cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator( exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(), ica.GetConfigurations(), ica.GetComponent().c_str(), message, - ica.GetExcludeFromAll(), fname.c_str(), name_space.GetCString(), - exportOld.IsEnabled(), true); + ica.GetExcludeFromAll(), fname.c_str(), name_space.c_str(), exportOld, + true); this->Makefile->AddInstallGenerator(exportGenerator); return true; @@ -1363,16 +1367,19 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) { // This is the EXPORT mode. cmInstallCommandArguments ica(this->DefaultComponentName); - cmCAString exp(&ica.Parser, "EXPORT"); - cmCAString name_space(&ica.Parser, "NAMESPACE", &ica.ArgumentGroup); - cmCAEnabler exportOld(&ica.Parser, "EXPORT_LINK_INTERFACE_LIBRARIES", - &ica.ArgumentGroup); - cmCAString filename(&ica.Parser, "FILE", &ica.ArgumentGroup); - exp.Follows(nullptr); - - ica.ArgumentGroup.Follows(&exp); + + std::string exp; + std::string name_space; + bool exportOld = false; + std::string filename; + + ica.Bind("EXPORT"_s, exp); + ica.Bind("NAMESPACE"_s, name_space); + ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld); + ica.Bind("FILE"_s, filename); + std::vector<std::string> unknownArgs; - ica.Parse(&args, &unknownArgs); + ica.Parse(args, &unknownArgs); if (!unknownArgs.empty()) { // Unknown argument. @@ -1396,7 +1403,7 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) } // Check the file name. - std::string fname = filename.GetString(); + std::string fname = filename; if (fname.find_first_of(":/\\") != std::string::npos) { std::ostringstream e; e << args[0] << " given invalid export file name \"" << fname << "\". " @@ -1418,12 +1425,12 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) // Construct the file name. if (fname.empty()) { - fname = exp.GetString(); + fname = exp; fname += ".cmake"; if (fname.find_first_of(":/\\") != std::string::npos) { std::ostringstream e; - e << args[0] << " given export name \"" << exp.GetString() << "\". " + e << args[0] << " given export name \"" << exp << "\". " << "This name cannot be safely converted to a file name. " << "Specify a different export name or use the FILE option to set " << "a file name explicitly."; @@ -1433,8 +1440,8 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) } cmExportSet* exportSet = - this->Makefile->GetGlobalGenerator()->GetExportSets()[exp.GetString()]; - if (exportOld.IsEnabled()) { + this->Makefile->GetGlobalGenerator()->GetExportSets()[exp]; + if (exportOld) { for (cmTargetExport* te : *exportSet->GetTargetExports()) { cmTarget* tgt = this->Makefile->GetGlobalGenerator()->FindTarget(te->TargetName); @@ -1461,8 +1468,8 @@ bool cmInstallCommand::HandleExportMode(std::vector<std::string> const& args) cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator( exportSet, ica.GetDestination().c_str(), ica.GetPermissions().c_str(), ica.GetConfigurations(), ica.GetComponent().c_str(), message, - ica.GetExcludeFromAll(), fname.c_str(), name_space.GetCString(), - exportOld.IsEnabled(), false); + ica.GetExcludeFromAll(), fname.c_str(), name_space.c_str(), exportOld, + false); this->Makefile->AddInstallGenerator(exportGenerator); return true; diff --git a/Source/cmInstallCommandArguments.cxx b/Source/cmInstallCommandArguments.cxx index 155f055..8b33782 100644 --- a/Source/cmInstallCommandArguments.cxx +++ b/Source/cmInstallCommandArguments.cxx @@ -2,7 +2,9 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmInstallCommandArguments.h" +#include "cmRange.h" #include "cmSystemTools.h" +#include "cm_static_string_view.hxx" #include <utility> @@ -17,20 +19,19 @@ const std::string cmInstallCommandArguments::EmptyString; cmInstallCommandArguments::cmInstallCommandArguments( std::string defaultComponent) - : Destination(&Parser, "DESTINATION", &ArgumentGroup) - , Component(&Parser, "COMPONENT", &ArgumentGroup) - , NamelinkComponent(&Parser, "NAMELINK_COMPONENT", &ArgumentGroup) - , ExcludeFromAll(&Parser, "EXCLUDE_FROM_ALL", &ArgumentGroup) - , Rename(&Parser, "RENAME", &ArgumentGroup) - , Permissions(&Parser, "PERMISSIONS", &ArgumentGroup) - , Configurations(&Parser, "CONFIGURATIONS", &ArgumentGroup) - , Optional(&Parser, "OPTIONAL", &ArgumentGroup) - , NamelinkOnly(&Parser, "NAMELINK_ONLY", &ArgumentGroup) - , NamelinkSkip(&Parser, "NAMELINK_SKIP", &ArgumentGroup) - , Type(&Parser, "TYPE", &ArgumentGroup) - , GenericArguments(nullptr) - , DefaultComponentName(std::move(defaultComponent)) + : DefaultComponentName(std::move(defaultComponent)) { + this->Bind("DESTINATION"_s, this->Destination); + this->Bind("COMPONENT"_s, this->Component); + this->Bind("NAMELINK_COMPONENT"_s, this->NamelinkComponent); + this->Bind("EXCLUDE_FROM_ALL"_s, this->ExcludeFromAll); + this->Bind("RENAME"_s, this->Rename); + this->Bind("PERMISSIONS"_s, this->Permissions); + this->Bind("CONFIGURATIONS"_s, this->Configurations); + this->Bind("OPTIONAL"_s, this->Optional); + this->Bind("NAMELINK_ONLY"_s, this->NamelinkOnly); + this->Bind("NAMELINK_SKIP"_s, this->NamelinkSkip); + this->Bind("TYPE"_s, this->Type); } const std::string& cmInstallCommandArguments::GetDestination() const @@ -46,8 +47,8 @@ const std::string& cmInstallCommandArguments::GetDestination() const const std::string& cmInstallCommandArguments::GetComponent() const { - if (!this->Component.GetString().empty()) { - return this->Component.GetString(); + if (!this->Component.empty()) { + return this->Component; } if (this->GenericArguments != nullptr) { return this->GenericArguments->GetComponent(); @@ -61,16 +62,16 @@ const std::string& cmInstallCommandArguments::GetComponent() const const std::string& cmInstallCommandArguments::GetNamelinkComponent() const { - if (!this->NamelinkComponent.GetString().empty()) { - return this->NamelinkComponent.GetString(); + if (!this->NamelinkComponent.empty()) { + return this->NamelinkComponent; } return this->GetComponent(); } const std::string& cmInstallCommandArguments::GetRename() const { - if (!this->Rename.GetString().empty()) { - return this->Rename.GetString(); + if (!this->Rename.empty()) { + return this->Rename; } if (this->GenericArguments != nullptr) { return this->GenericArguments->GetRename(); @@ -91,7 +92,7 @@ const std::string& cmInstallCommandArguments::GetPermissions() const bool cmInstallCommandArguments::GetOptional() const { - if (this->Optional.IsEnabled()) { + if (this->Optional) { return true; } if (this->GenericArguments != nullptr) { @@ -102,7 +103,7 @@ bool cmInstallCommandArguments::GetOptional() const bool cmInstallCommandArguments::GetExcludeFromAll() const { - if (this->ExcludeFromAll.IsEnabled()) { + if (this->ExcludeFromAll) { return true; } if (this->GenericArguments != nullptr) { @@ -113,7 +114,7 @@ bool cmInstallCommandArguments::GetExcludeFromAll() const bool cmInstallCommandArguments::GetNamelinkOnly() const { - if (this->NamelinkOnly.IsEnabled()) { + if (this->NamelinkOnly) { return true; } if (this->GenericArguments != nullptr) { @@ -124,7 +125,7 @@ bool cmInstallCommandArguments::GetNamelinkOnly() const bool cmInstallCommandArguments::GetNamelinkSkip() const { - if (this->NamelinkSkip.IsEnabled()) { + if (this->NamelinkSkip) { return true; } if (this->GenericArguments != nullptr) { @@ -135,7 +136,7 @@ bool cmInstallCommandArguments::GetNamelinkSkip() const bool cmInstallCommandArguments::HasNamelinkComponent() const { - if (!this->NamelinkComponent.GetString().empty()) { + if (!this->NamelinkComponent.empty()) { return true; } if (this->GenericArguments != nullptr) { @@ -146,19 +147,19 @@ bool cmInstallCommandArguments::HasNamelinkComponent() const const std::string& cmInstallCommandArguments::GetType() const { - return this->Type.GetString(); + return this->Type; } const std::vector<std::string>& cmInstallCommandArguments::GetConfigurations() const { - if (!this->Configurations.GetVector().empty()) { - return this->Configurations.GetVector(); + if (!this->Configurations.empty()) { + return this->Configurations; } if (this->GenericArguments != nullptr) { return this->GenericArguments->GetConfigurations(); } - return this->Configurations.GetVector(); + return this->Configurations; } bool cmInstallCommandArguments::Finalize() @@ -166,21 +167,15 @@ bool cmInstallCommandArguments::Finalize() if (!this->CheckPermissions()) { return false; } - this->DestinationString = this->Destination.GetString(); + this->DestinationString = this->Destination; cmSystemTools::ConvertToUnixSlashes(this->DestinationString); return true; } -void cmInstallCommandArguments::Parse(const std::vector<std::string>* args, - std::vector<std::string>* unconsumedArgs) -{ - this->Parser.Parse(args, unconsumedArgs); -} - bool cmInstallCommandArguments::CheckPermissions() { this->PermissionsString.clear(); - for (std::string const& perm : this->Permissions.GetVector()) { + for (std::string const& perm : this->Permissions) { if (!cmInstallCommandArguments::CheckPermissions( perm, this->PermissionsString)) { return false; @@ -220,10 +215,7 @@ void cmInstallCommandIncludesArgument::Parse( if (args->empty()) { return; } - std::vector<std::string>::const_iterator it = args->begin(); - ++it; - for (; it != args->end(); ++it) { - std::string dir = *it; + for (std::string dir : cmMakeRange(*args).advance(1)) { cmSystemTools::ConvertToUnixSlashes(dir); this->IncludeDirs.push_back(std::move(dir)); } diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 9c0d417..5d2ee0a 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -8,9 +8,9 @@ #include <string> #include <vector> -#include "cmCommandArgumentsHelper.h" +#include "cmArgumentParser.h" -class cmInstallCommandArguments +class cmInstallCommandArguments : public cmArgumentParser<void> { public: cmInstallCommandArguments(std::string defaultComponent); @@ -18,8 +18,6 @@ public: { this->GenericArguments = args; } - void Parse(const std::vector<std::string>* args, - std::vector<std::string>* unconsumedArgs); // Compute destination path.and check permissions bool Finalize(); @@ -37,30 +35,25 @@ public: bool HasNamelinkComponent() const; const std::string& GetType() const; - // once HandleDirectoryMode() is also switched to using - // cmInstallCommandArguments then these two functions can become non-static - // private member functions without arguments static bool CheckPermissions(const std::string& onePerm, std::string& perm); - cmCommandArgumentsHelper Parser; - cmCommandArgumentGroup ArgumentGroup; private: - cmCAString Destination; - cmCAString Component; - cmCAString NamelinkComponent; - cmCAEnabler ExcludeFromAll; - cmCAString Rename; - cmCAStringVector Permissions; - cmCAStringVector Configurations; - cmCAEnabler Optional; - cmCAEnabler NamelinkOnly; - cmCAEnabler NamelinkSkip; - cmCAString Type; + std::string Destination; + std::string Component; + std::string NamelinkComponent; + bool ExcludeFromAll = false; + std::string Rename; + std::vector<std::string> Permissions; + std::vector<std::string> Configurations; + bool Optional = false; + bool NamelinkOnly = false; + bool NamelinkSkip = false; + std::string Type; std::string DestinationString; std::string PermissionsString; - cmInstallCommandArguments* GenericArguments; + cmInstallCommandArguments* GenericArguments = nullptr; static const char* PermissionsTable[]; static const std::string EmptyString; std::string DefaultComponentName; diff --git a/Source/cmInstallDirectoryGenerator.cxx b/Source/cmInstallDirectoryGenerator.cxx index a88c7af..62ce9f2 100644 --- a/Source/cmInstallDirectoryGenerator.cxx +++ b/Source/cmInstallDirectoryGenerator.cxx @@ -31,10 +31,12 @@ cmInstallDirectoryGenerator::cmInstallDirectoryGenerator( } // We need per-config actions if any directories have generator expressions. - for (std::vector<std::string>::const_iterator i = dirs.begin(); - !this->ActionsPerConfig && i != dirs.end(); ++i) { - if (cmGeneratorExpression::Find(*i) != std::string::npos) { - this->ActionsPerConfig = true; + if (!this->ActionsPerConfig) { + for (std::string const& dir : dirs) { + if (cmGeneratorExpression::Find(dir) != std::string::npos) { + this->ActionsPerConfig = true; + break; + } } } } diff --git a/Source/cmInstallExportAndroidMKGenerator.cxx b/Source/cmInstallExportAndroidMKGenerator.cxx index 85b7021..186b9df 100644 --- a/Source/cmInstallExportAndroidMKGenerator.cxx +++ b/Source/cmInstallExportAndroidMKGenerator.cxx @@ -67,10 +67,8 @@ void cmInstallExportAndroidMKGenerator::GenerateScript(std::ostream& os) this->EFGen->AddConfiguration(""); } } else { - for (std::vector<std::string>::const_iterator ci = - this->ConfigurationTypes->begin(); - ci != this->ConfigurationTypes->end(); ++ci) { - this->EFGen->AddConfiguration(*ci); + for (std::string const& config : this->ConfigurationTypes) { + this->EFGen->AddConfiguration(config); } } this->EFGen->GenerateImportFile(); @@ -88,11 +86,9 @@ void cmInstallExportAndroidMKGenerator::GenerateScriptConfigs( // Now create a configuration-specific install rule for the import // file of each configuration. std::vector<std::string> files; - for (std::map<std::string, std::string>::const_iterator i = - this->EFGen->GetConfigImportFiles().begin(); - i != this->EFGen->GetConfigImportFiles().end(); ++i) { - files.push_back(i->second); - std::string config_test = this->CreateConfigTest(i->first); + for (auto const& pair : this->EFGen->GetConfigImportFiles()) { + files.push_back(pair.second); + std::string config_test = this->CreateConfigTest(pair.first); os << indent << "if(" << config_test << ")\n"; this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files, false, this->FilePermissions.c_str(), nullptr, diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index 47b9785..73a37cb 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -202,7 +202,7 @@ void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os, os << indentNN << "file(GLOB OLD_CONFIG_FILES \"" << installedDir << this->EFGen->GetConfigImportFileGlob() << "\")\n"; os << indentNN << "if(OLD_CONFIG_FILES)\n"; - os << indentNNN << "message(STATUS \"Old export file \\\"" << installedFile + os << indentNNN << R"(message(STATUS "Old export file \")" << installedFile << "\\\" will be replaced. Removing files [${OLD_CONFIG_FILES}].\")\n"; os << indentNNN << "file(REMOVE ${OLD_CONFIG_FILES})\n"; os << indentNN << "endif()\n"; diff --git a/Source/cmInstallFilesCommand.cxx b/Source/cmInstallFilesCommand.cxx index 4dde18f..b068e46 100644 --- a/Source/cmInstallFilesCommand.cxx +++ b/Source/cmInstallFilesCommand.cxx @@ -7,6 +7,7 @@ #include "cmInstallFilesGenerator.h" #include "cmInstallGenerator.h" #include "cmMakefile.h" +#include "cmRange.h" #include "cmSystemTools.h" class cmExecutionStatus; @@ -27,10 +28,9 @@ bool cmInstallFilesCommand::InitialPass(std::vector<std::string> const& args, if ((args.size() > 1) && (args[1] == "FILES")) { this->IsFilesForm = true; - for (std::vector<std::string>::const_iterator s = args.begin() + 2; - s != args.end(); ++s) { + for (std::string const& arg : cmMakeRange(args).advance(2)) { // Find the source location for each file listed. - this->Files.push_back(this->FindInstallSource(s->c_str())); + this->Files.push_back(this->FindInstallSource(arg.c_str())); } this->CreateInstallGenerator(); } else { diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx index 07094cb..9eb8ad4 100644 --- a/Source/cmInstallFilesGenerator.cxx +++ b/Source/cmInstallFilesGenerator.cxx @@ -29,11 +29,13 @@ cmInstallFilesGenerator::cmInstallFilesGenerator( this->ActionsPerConfig = true; } - // We need per-config actions if any files have generator expressions. - for (std::vector<std::string>::const_iterator i = files.begin(); - !this->ActionsPerConfig && i != files.end(); ++i) { - if (cmGeneratorExpression::Find(*i) != std::string::npos) { - this->ActionsPerConfig = true; + // We need per-config actions if any directories have generator expressions. + if (!this->ActionsPerConfig) { + for (std::string const& file : files) { + if (cmGeneratorExpression::Find(file) != std::string::npos) { + this->ActionsPerConfig = true; + break; + } } } } diff --git a/Source/cmInstallGenerator.cxx b/Source/cmInstallGenerator.cxx index d139190..2ffca30 100644 --- a/Source/cmInstallGenerator.cxx +++ b/Source/cmInstallGenerator.cxx @@ -69,17 +69,18 @@ void cmInstallGenerator::AddInstallRule( if (cmSystemTools::FileIsFullPath(dest)) { os << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n"; os << indent << " \""; - for (std::vector<std::string>::const_iterator fi = files.begin(); - fi != files.end(); ++fi) { - if (fi != files.begin()) { + bool firstIteration = true; + for (std::string const& file : files) { + if (!firstIteration) { os << ";"; } os << dest << "/"; if (rename && *rename) { os << rename; } else { - os << cmSystemTools::GetFilenameName(*fi); + os << cmSystemTools::GetFilenameName(file); } + firstIteration = false; } os << "\")\n"; os << indent << "if(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n"; @@ -141,7 +142,7 @@ void cmInstallGenerator::AddInstallRule( std::string cmInstallGenerator::CreateComponentTest(const char* component, bool exclude_from_all) { - std::string result = "\"x${CMAKE_INSTALL_COMPONENT}x\" STREQUAL \"x"; + std::string result = R"("x${CMAKE_INSTALL_COMPONENT}x" STREQUAL "x)"; result += component; result += "x\""; if (!exclude_from_all) { diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 9d3a6bb..4fefe23 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -107,19 +107,15 @@ void cmInstallTargetGenerator::GenerateScriptForConfig( // There is a bug in cmInstallCommand if this fails. assert(this->NamelinkMode == NamelinkModeNone); - std::string targetName; - std::string targetNameReal; - std::string targetNameImport; - std::string targetNamePDB; - this->Target->GetExecutableNames(targetName, targetNameReal, - targetNameImport, targetNamePDB, config); + cmGeneratorTarget::Names targetNames = + this->Target->GetExecutableNames(config); if (this->ImportLibrary) { - std::string from1 = fromDirConfig + targetNameImport; - std::string to1 = toDir + targetNameImport; + std::string from1 = fromDirConfig + targetNames.ImportLibrary; + std::string to1 = toDir + targetNames.ImportLibrary; filesFrom.push_back(std::move(from1)); filesTo.push_back(std::move(to1)); std::string targetNameImportLib; - if (this->Target->GetImplibGNUtoMS(config, targetNameImport, + if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, targetNameImportLib)) { filesFrom.push_back(fromDirConfig + targetNameImportLib); filesTo.push_back(toDir + targetNameImportLib); @@ -128,8 +124,8 @@ void cmInstallTargetGenerator::GenerateScriptForConfig( // An import library looks like a static library. type = cmInstallType_STATIC_LIBRARY; } else { - std::string from1 = fromDirConfig + targetName; - std::string to1 = toDir + targetName; + std::string from1 = fromDirConfig + targetNames.Output; + std::string to1 = toDir + targetNames.Output; // Handle OSX Bundles. if (this->Target->IsAppBundleOnApple()) { @@ -154,12 +150,12 @@ void cmInstallTargetGenerator::GenerateScriptForConfig( if (!mf->PlatformIsAppleEmbedded()) { to1 += "Contents/MacOS/"; } - to1 += targetName; + to1 += targetNames.Output; } else { // Tweaks apply to the real file, so list it first. - if (targetNameReal != targetName) { - std::string from2 = fromDirConfig + targetNameReal; - std::string to2 = toDir += targetNameReal; + if (targetNames.Real != targetNames.Output) { + std::string from2 = fromDirConfig + targetNames.Real; + std::string to2 = toDir += targetNames.Real; filesFrom.push_back(std::move(from2)); filesTo.push_back(std::move(to2)); } @@ -169,23 +165,18 @@ void cmInstallTargetGenerator::GenerateScriptForConfig( filesTo.push_back(std::move(to1)); } } else { - std::string targetName; - std::string targetNameSO; - std::string targetNameReal; - std::string targetNameImport; - std::string targetNamePDB; - this->Target->GetLibraryNames(targetName, targetNameSO, targetNameReal, - targetNameImport, targetNamePDB, config); + cmGeneratorTarget::Names targetNames = + this->Target->GetLibraryNames(config); if (this->ImportLibrary) { // There is a bug in cmInstallCommand if this fails. assert(this->NamelinkMode == NamelinkModeNone); - std::string from1 = fromDirConfig + targetNameImport; - std::string to1 = toDir + targetNameImport; + std::string from1 = fromDirConfig + targetNames.ImportLibrary; + std::string to1 = toDir + targetNames.ImportLibrary; filesFrom.push_back(std::move(from1)); filesTo.push_back(std::move(to1)); std::string targetNameImportLib; - if (this->Target->GetImplibGNUtoMS(config, targetNameImport, + if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, targetNameImportLib)) { filesFrom.push_back(fromDirConfig + targetNameImportLib); filesTo.push_back(toDir + targetNameImportLib); @@ -227,11 +218,11 @@ void cmInstallTargetGenerator::GenerateScriptForConfig( type = cmInstallType_DIRECTORY; literal_args += " USE_SOURCE_PERMISSIONS"; - std::string from1 = fromDirConfig + targetName; + std::string from1 = fromDirConfig + targetNames.Output; from1 = cmSystemTools::GetFilenamePath(from1); // Tweaks apply to the binary inside the bundle. - std::string to1 = toDir + targetNameReal; + std::string to1 = toDir + targetNames.Real; filesFrom.push_back(std::move(from1)); filesTo.push_back(std::move(to1)); @@ -240,10 +231,11 @@ void cmInstallTargetGenerator::GenerateScriptForConfig( type = cmInstallType_DIRECTORY; literal_args += " USE_SOURCE_PERMISSIONS"; - std::string targetNameBase = targetName.substr(0, targetName.find('/')); + std::string targetNameBase = + targetNames.Output.substr(0, targetNames.Output.find('/')); std::string from1 = fromDirConfig + targetNameBase; - std::string to1 = toDir + targetName; + std::string to1 = toDir + targetNames.Output; filesFrom.push_back(std::move(from1)); filesTo.push_back(std::move(to1)); @@ -251,25 +243,26 @@ void cmInstallTargetGenerator::GenerateScriptForConfig( bool haveNamelink = false; // Library link name. - std::string fromName = fromDirConfig + targetName; - std::string toName = toDir + targetName; + std::string fromName = fromDirConfig + targetNames.Output; + std::string toName = toDir + targetNames.Output; // Library interface name. std::string fromSOName; std::string toSOName; - if (targetNameSO != targetName) { + if (targetNames.SharedObject != targetNames.Output) { haveNamelink = true; - fromSOName = fromDirConfig + targetNameSO; - toSOName = toDir + targetNameSO; + fromSOName = fromDirConfig + targetNames.SharedObject; + toSOName = toDir + targetNames.SharedObject; } // Library implementation name. std::string fromRealName; std::string toRealName; - if (targetNameReal != targetName && targetNameReal != targetNameSO) { + if (targetNames.Real != targetNames.Output && + targetNames.Real != targetNames.SharedObject) { haveNamelink = true; - fromRealName = fromDirConfig + targetNameReal; - toRealName = toDir + targetNameReal; + fromRealName = fromDirConfig + targetNames.Real; + toRealName = toDir + targetNames.Real; } // Add the names based on the current namelink mode. @@ -400,48 +393,37 @@ std::string cmInstallTargetGenerator::GetInstallFilename( std::string fname; // Compute the name of the library. if (target->GetType() == cmStateEnums::EXECUTABLE) { - std::string targetName; - std::string targetNameReal; - std::string targetNameImport; - std::string targetNamePDB; - target->GetExecutableNames(targetName, targetNameReal, targetNameImport, - targetNamePDB, config); + cmGeneratorTarget::Names targetNames = target->GetExecutableNames(config); if (nameType == NameImplib) { // Use the import library name. - if (!target->GetImplibGNUtoMS(config, targetNameImport, fname, + if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname, "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) { - fname = targetNameImport; + fname = targetNames.ImportLibrary; } } else if (nameType == NameReal) { // Use the canonical name. - fname = targetNameReal; + fname = targetNames.Real; } else { // Use the canonical name. - fname = targetName; + fname = targetNames.Output; } } else { - std::string targetName; - std::string targetNameSO; - std::string targetNameReal; - std::string targetNameImport; - std::string targetNamePDB; - target->GetLibraryNames(targetName, targetNameSO, targetNameReal, - targetNameImport, targetNamePDB, config); + cmGeneratorTarget::Names targetNames = target->GetLibraryNames(config); if (nameType == NameImplib) { // Use the import library name. - if (!target->GetImplibGNUtoMS(config, targetNameImport, fname, + if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname, "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) { - fname = targetNameImport; + fname = targetNames.ImportLibrary; } } else if (nameType == NameSO) { // Use the soname. - fname = targetNameSO; + fname = targetNames.SharedObject; } else if (nameType == NameReal) { // Use the real name. - fname = targetNameReal; + fname = targetNames.Real; } else { // Use the canonical name. - fname = targetName; + fname = targetNames.Output; } } @@ -809,7 +791,7 @@ void cmInstallTargetGenerator::AddRanlibRule(std::ostream& os, Indent indent, return; } - std::string ranlib = + const std::string& ranlib = this->Target->Target->GetMakefile()->GetRequiredDefinition("CMAKE_RANLIB"); if (ranlib.empty()) { return; diff --git a/Source/cmInstallTargetsCommand.cxx b/Source/cmInstallTargetsCommand.cxx index d721ca0..7e67d4e 100644 --- a/Source/cmInstallTargetsCommand.cxx +++ b/Source/cmInstallTargetsCommand.cxx @@ -40,8 +40,8 @@ bool cmInstallTargetsCommand::InitialPass(std::vector<std::string> const& args, } else { cmTargets::iterator ti = tgts.find(*s); if (ti != tgts.end()) { - ti->second.SetInstallPath(args[0].c_str()); - ti->second.SetRuntimeInstallPath(runtime_dir.c_str()); + ti->second.SetInstallPath(args[0]); + ti->second.SetRuntimeInstallPath(runtime_dir); ti->second.SetHaveInstallRule(true); } else { std::string str = "Cannot find target: \"" + *s + "\" to install."; diff --git a/Source/cmInstalledFile.h b/Source/cmInstalledFile.h index 070b954..b7d602e 100644 --- a/Source/cmInstalledFile.h +++ b/Source/cmInstalledFile.h @@ -32,6 +32,9 @@ public: Property(); ~Property(); + Property(const Property&) = delete; + Property& operator=(const Property&) = delete; + ExpressionVectorType ValueExpressions; }; @@ -41,6 +44,9 @@ public: ~cmInstalledFile(); + cmInstalledFile(const cmInstalledFile&) = delete; + cmInstalledFile& operator=(const cmInstalledFile&) = delete; + void RemoveProperty(const std::string& prop); void SetProperty(cmMakefile const* mf, const std::string& prop, diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx index 297babf..5474afa 100644 --- a/Source/cmListCommand.cxx +++ b/Source/cmListCommand.cxx @@ -19,6 +19,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmStringReplaceHelper.h" #include "cmSystemTools.h" @@ -42,6 +43,15 @@ bool cmListCommand::InitialPass(std::vector<std::string> const& args, if (subCommand == "APPEND") { return this->HandleAppendCommand(args); } + if (subCommand == "PREPEND") { + return this->HandlePrependCommand(args); + } + if (subCommand == "POP_BACK") { + return this->HandlePopBackCommand(args); + } + if (subCommand == "POP_FRONT") { + return this->HandlePopFrontCommand(args); + } if (subCommand == "FIND") { return this->HandleFindCommand(args); } @@ -222,20 +232,141 @@ bool cmListCommand::HandleAppendCommand(std::vector<std::string> const& args) return true; } - const std::string& listName = args[1]; + std::string const& listName = args[1]; // expand the variable std::string listString; this->GetListString(listString, listName); - if (!listString.empty() && !args.empty()) { - listString += ";"; + // If `listString` or `args` is empty, no need to append `;`, + // then index is going to be `1` and points to the end-of-string ";" + auto const offset = + std::string::size_type(listString.empty() || args.empty()); + listString += &";"[offset] + cmJoin(cmMakeRange(args).advance(2), ";"); + + this->Makefile->AddDefinition(listName, listString.c_str()); + return true; +} + +bool cmListCommand::HandlePrependCommand(std::vector<std::string> const& args) +{ + assert(args.size() >= 2); + + // Skip if nothing to prepend. + if (args.size() < 3) { + return true; } - listString += cmJoin(cmMakeRange(args).advance(2), ";"); + + std::string const& listName = args[1]; + // expand the variable + std::string listString; + this->GetListString(listString, listName); + + // If `listString` or `args` is empty, no need to append `;`, + // then `offset` is going to be `1` and points to the end-of-string ";" + auto const offset = + std::string::size_type(listString.empty() || args.empty()); + listString.insert(0, + cmJoin(cmMakeRange(args).advance(2), ";") + &";"[offset]); this->Makefile->AddDefinition(listName, listString.c_str()); return true; } +bool cmListCommand::HandlePopBackCommand(std::vector<std::string> const& args) +{ + assert(args.size() >= 2); + + auto ai = args.cbegin(); + ++ai; // Skip subcommand name + std::string const& listName = *ai++; + std::vector<std::string> varArgsExpanded; + if (!this->GetList(varArgsExpanded, listName)) { + // Can't get the list definition... undefine any vars given after. + for (; ai != args.cend(); ++ai) { + this->Makefile->RemoveDefinition(*ai); + } + return true; + } + + if (!varArgsExpanded.empty()) { + if (ai == args.cend()) { + // No variables are given... Just remove one element. + varArgsExpanded.pop_back(); + } else { + // Ok, assign elements to be removed to the given variables + for (; !varArgsExpanded.empty() && ai != args.cend(); ++ai) { + assert(!ai->empty()); + this->Makefile->AddDefinition(*ai, varArgsExpanded.back().c_str()); + varArgsExpanded.pop_back(); + } + // Undefine the rest variables if the list gets empty earlier... + for (; ai != args.cend(); ++ai) { + this->Makefile->RemoveDefinition(*ai); + } + } + + this->Makefile->AddDefinition(listName, + cmJoin(varArgsExpanded, ";").c_str()); + + } else if (ai != + args.cend()) { // The list is empty, but some args were given + // Need to *undefine* 'em all, cuz there are no items to assign... + for (; ai != args.cend(); ++ai) { + this->Makefile->RemoveDefinition(*ai); + } + } + + return true; +} + +bool cmListCommand::HandlePopFrontCommand(std::vector<std::string> const& args) +{ + assert(args.size() >= 2); + + auto ai = args.cbegin(); + ++ai; // Skip subcommand name + std::string const& listName = *ai++; + std::vector<std::string> varArgsExpanded; + if (!this->GetList(varArgsExpanded, listName)) { + // Can't get the list definition... undefine any vars given after. + for (; ai != args.cend(); ++ai) { + this->Makefile->RemoveDefinition(*ai); + } + return true; + } + + if (!varArgsExpanded.empty()) { + if (ai == args.cend()) { + // No variables are given... Just remove one element. + varArgsExpanded.erase(varArgsExpanded.begin()); + } else { + // Ok, assign elements to be removed to the given variables + auto vi = varArgsExpanded.begin(); + for (; vi != varArgsExpanded.end() && ai != args.cend(); ++ai, ++vi) { + assert(!ai->empty()); + this->Makefile->AddDefinition(*ai, varArgsExpanded.front().c_str()); + } + varArgsExpanded.erase(varArgsExpanded.begin(), vi); + // Undefine the rest variables if the list gets empty earlier... + for (; ai != args.cend(); ++ai) { + this->Makefile->RemoveDefinition(*ai); + } + } + + this->Makefile->AddDefinition(listName, + cmJoin(varArgsExpanded, ";").c_str()); + + } else if (ai != + args.cend()) { // The list is empty, but some args were given + // Need to *undefine* 'em all, cuz there are no items to assign... + for (; ai != args.cend(); ++ai) { + this->Makefile->RemoveDefinition(*ai); + } + } + + return true; +} + bool cmListCommand::HandleFindCommand(std::vector<std::string> const& args) { if (args.size() != 4) { diff --git a/Source/cmListCommand.h b/Source/cmListCommand.h index 76a9856..ea3d643 100644 --- a/Source/cmListCommand.h +++ b/Source/cmListCommand.h @@ -35,6 +35,9 @@ protected: bool HandleLengthCommand(std::vector<std::string> const& args); bool HandleGetCommand(std::vector<std::string> const& args); bool HandleAppendCommand(std::vector<std::string> const& args); + bool HandlePrependCommand(std::vector<std::string> const& args); + bool HandlePopBackCommand(std::vector<std::string> const& args); + bool HandlePopFrontCommand(std::vector<std::string> const& args); bool HandleFindCommand(std::vector<std::string> const& args); bool HandleInsertCommand(std::vector<std::string> const& args); bool HandleJoinCommand(std::vector<std::string> const& args); diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index f855119..f99caed 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -27,6 +27,8 @@ struct cmListFileParser cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt, cmMessenger* messenger, const char* filename); ~cmListFileParser(); + cmListFileParser(const cmListFileParser&) = delete; + cmListFileParser& operator=(const cmListFileParser&) = delete; void IssueFileOpenError(std::string const& text) const; void IssueError(std::string const& text) const; bool ParseFile(); diff --git a/Source/cmLoadCommandCommand.cxx b/Source/cmLoadCommandCommand.cxx index cd71518..69751b6 100644 --- a/Source/cmLoadCommandCommand.cxx +++ b/Source/cmLoadCommandCommand.cxx @@ -33,7 +33,7 @@ public: this->info.CAPI = &cmStaticCAPI; } - ///! clean up any memory allocated by the plugin + //! clean up any memory allocated by the plugin ~cmLoadedCommand() override; /** diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 7e15234..8b51834 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -6,6 +6,7 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommandGenerator.h" #include "cmGeneratedFileStream.h" +#include "cmGeneratorExpression.h" #include "cmGeneratorExpressionEvaluationFile.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -34,6 +35,7 @@ #include "cmsys/RegularExpression.hxx" #include <algorithm> #include <assert.h> +#include <initializer_list> #include <iterator> #include <sstream> #include <stdio.h> @@ -51,25 +53,23 @@ // replaced in the form <var> with GetSafeDefinition(var). // ${LANG} is replaced in the variable first with all enabled // languages. -static const char* ruleReplaceVars[] = { - "CMAKE_${LANG}_COMPILER", - "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS", - "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS", - "CMAKE_SHARED_MODULE_${LANG}_FLAGS", - "CMAKE_SHARED_LIBRARY_${LANG}_FLAGS", - "CMAKE_${LANG}_LINK_FLAGS", - "CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG", - "CMAKE_${LANG}_ARCHIVE", - "CMAKE_AR", - "CMAKE_CURRENT_SOURCE_DIR", - "CMAKE_CURRENT_BINARY_DIR", - "CMAKE_RANLIB", - "CMAKE_LINKER", - "CMAKE_MT", - "CMAKE_CUDA_HOST_COMPILER", - "CMAKE_CUDA_HOST_LINK_LAUNCHER", - "CMAKE_CL_SHOWINCLUDES_PREFIX" -}; +static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER", + "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS", + "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS", + "CMAKE_SHARED_MODULE_${LANG}_FLAGS", + "CMAKE_SHARED_LIBRARY_${LANG}_FLAGS", + "CMAKE_${LANG}_LINK_FLAGS", + "CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG", + "CMAKE_${LANG}_ARCHIVE", + "CMAKE_AR", + "CMAKE_CURRENT_SOURCE_DIR", + "CMAKE_CURRENT_BINARY_DIR", + "CMAKE_RANLIB", + "CMAKE_LINKER", + "CMAKE_MT", + "CMAKE_CUDA_HOST_COMPILER", + "CMAKE_CUDA_HOST_LINK_LAUNCHER", + "CMAKE_CL_SHOWINCLUDES_PREFIX" }; cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile) : cmOutputConverter(makefile->GetStateSnapshot()) @@ -138,15 +138,13 @@ cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile) this->VariableMappings[compilerOptionSysroot] = this->Makefile->GetSafeDefinition(compilerOptionSysroot); - for (const char* const* replaceIter = cm::cbegin(ruleReplaceVars); - replaceIter != cm::cend(ruleReplaceVars); ++replaceIter) { - std::string actualReplace = *replaceIter; - if (actualReplace.find("${LANG}") != std::string::npos) { - cmSystemTools::ReplaceString(actualReplace, "${LANG}", lang); + for (std::string replaceVar : ruleReplaceVars) { + if (replaceVar.find("${LANG}") != std::string::npos) { + cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang); } - this->VariableMappings[actualReplace] = - this->Makefile->GetSafeDefinition(actualReplace); + this->VariableMappings[replaceVar] = + this->Makefile->GetSafeDefinition(replaceVar); } } } @@ -369,8 +367,8 @@ void cmLocalGenerator::ProcessEvaluationFiles( std::back_inserter(intersection)); if (!intersection.empty()) { cmSystemTools::Error("Files to be generated by multiple different " - "commands: ", - cmWrap('"', intersection, '"', " ").c_str()); + "commands: " + + cmWrap('"', intersection, '"', " ")); return; } @@ -460,7 +458,7 @@ void cmLocalGenerator::GenerateInstallRules() << "if(NOT DEFINED CMAKE_INSTALL_PREFIX)" << std::endl << " set(CMAKE_INSTALL_PREFIX \"" << prefix << "\")" << std::endl << "endif()" << std::endl - << "string(REGEX REPLACE \"/$\" \"\" CMAKE_INSTALL_PREFIX " + << R"(string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX )" << "\"${CMAKE_INSTALL_PREFIX}\")" << std::endl << std::endl; @@ -779,7 +777,7 @@ std::string cmLocalGenerator::GetIncludeFlags( #endif for (std::string const& i : includes) { if (fwSearchFlag && *fwSearchFlag && this->Makefile->IsOn("APPLE") && - cmSystemTools::IsPathToFramework(i.c_str())) { + cmSystemTools::IsPathToFramework(i)) { std::string frameworkDir = i; frameworkDir += "/../"; frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir); @@ -1182,8 +1180,8 @@ void cmLocalGenerator::GetTargetFlags( } if (linkLanguage.empty()) { cmSystemTools::Error( - "CMake can not determine linker language for target: ", - target->GetName().c_str()); + "CMake can not determine linker language for target: " + + target->GetName()); return; } this->AddLanguageFlagsForLinking(flags, target, linkLanguage, buildType); @@ -1261,6 +1259,10 @@ void cmLocalGenerator::GetTargetCompileFlags(cmGeneratorTarget* target, // Add language-specific flags. this->AddLanguageFlags(flags, target, lang, config); + if (target->IsIPOEnabled(lang, config)) { + this->AppendFeatureOptions(flags, lang, "IPO"); + } + this->AddArchitectureFlags(flags, target, lang, config); if (lang == "Fortran") { @@ -1382,9 +1384,9 @@ void cmLocalGenerator::OutputLinkLibraries( std::string linkLanguage = cli.GetLinkLanguage(); - std::string libPathFlag = + const std::string& libPathFlag = this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); - std::string libPathTerminator = + const std::string& libPathTerminator = this->Makefile->GetSafeDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); // Add standard libraries for this language. @@ -1518,8 +1520,39 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, flagsVar += "_FLAGS"; this->AddConfigVariableFlags(flags, flagsVar, config); - if (target->IsIPOEnabled(lang, config)) { - this->AppendFeatureOptions(flags, lang, "IPO"); + // Add MSVC runtime library flags. This is activated by the presence + // of a default selection whether or not it is overridden by a property. + const char* msvcRuntimeLibraryDefault = + this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT"); + if (msvcRuntimeLibraryDefault && *msvcRuntimeLibraryDefault) { + const char* msvcRuntimeLibraryValue = + target->GetProperty("MSVC_RUNTIME_LIBRARY"); + if (!msvcRuntimeLibraryValue) { + msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault; + } + cmGeneratorExpression ge; + std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(msvcRuntimeLibraryValue); + std::string const msvcRuntimeLibrary = + cge->Evaluate(this, config, false, target); + if (!msvcRuntimeLibrary.empty()) { + if (const char* msvcRuntimeLibraryOptions = + this->Makefile->GetDefinition( + "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" + + msvcRuntimeLibrary)) { + this->AppendCompileOptions(flags, msvcRuntimeLibraryOptions); + } else if ((this->Makefile->GetSafeDefinition( + "CMAKE_" + lang + "_COMPILER_ID") == "MSVC" || + this->Makefile->GetSafeDefinition( + "CMAKE_" + lang + "_SIMULATE_ID") == "MSVC") && + !cmSystemTools::GetErrorOccuredFlag()) { + // The compiler uses the MSVC ABI so it needs a known runtime library. + this->IssueMessage(MessageType::FATAL_ERROR, + "MSVC_RUNTIME_LIBRARY value '" + + msvcRuntimeLibrary + "' not known for this " + + lang + " compiler."); + } + } } } @@ -1537,6 +1570,10 @@ void cmLocalGenerator::AddLanguageFlagsForLinking( } this->AddLanguageFlags(flags, target, lang, config); + + if (target->IsIPOEnabled(lang, config)) { + this->AppendFeatureOptions(flags, lang, "IPO"); + } } cmGeneratorTarget* cmLocalGenerator::FindGeneratorTargetToUse( @@ -2241,11 +2278,8 @@ void cmLocalGenerator::JoinDefines(const std::set<std::string>& defines, dflag = df; } } - - std::set<std::string>::const_iterator defineIt = defines.begin(); - const std::set<std::string>::const_iterator defineEnd = defines.end(); const char* itemSeparator = definesString.empty() ? "" : " "; - for (; defineIt != defineEnd; ++defineIt) { + for (std::string const& define : defines) { // Append the definition with proper escaping. std::string def = dflag; if (this->GetState()->UseWatcomWMake()) { @@ -2259,7 +2293,7 @@ void cmLocalGenerator::JoinDefines(const std::set<std::string>& defines, // command line without any escapes. However we still have to // get the '$' and '#' characters through WMake as '$$' and // '$#'. - for (const char* c = defineIt->c_str(); *c; ++c) { + for (const char* c = define.c_str(); *c; ++c) { if (*c == '$' || *c == '#') { def += '$'; } @@ -2268,11 +2302,11 @@ void cmLocalGenerator::JoinDefines(const std::set<std::string>& defines, } else { // Make the definition appear properly on the command line. Use // -DNAME="value" instead of -D"NAME=value" for historical reasons. - std::string::size_type eq = defineIt->find("="); - def += defineIt->substr(0, eq); + std::string::size_type eq = define.find('='); + def += define.substr(0, eq); if (eq != std::string::npos) { def += "="; - def += this->EscapeForShell(defineIt->c_str() + eq + 1, true); + def += this->EscapeForShell(define.substr(eq + 1), true); } } definesString += itemSeparator; @@ -2829,7 +2863,7 @@ static void cmLGInfoProp(cmMakefile* mf, cmGeneratorTarget* target, void cmLocalGenerator::GenerateAppleInfoPList(cmGeneratorTarget* target, const std::string& targetName, - const char* fname) + const std::string& fname) { // Find the Info.plist template. const char* in = target->GetProperty("MACOSX_BUNDLE_INFO_PLIST"); @@ -2863,11 +2897,12 @@ void cmLocalGenerator::GenerateAppleInfoPList(cmGeneratorTarget* target, cmLGInfoProp(mf, target, "MACOSX_BUNDLE_SHORT_VERSION_STRING"); cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_VERSION"); cmLGInfoProp(mf, target, "MACOSX_BUNDLE_COPYRIGHT"); - mf->ConfigureFile(inFile.c_str(), fname, false, false, false); + mf->ConfigureFile(inFile, fname, false, false, false); } void cmLocalGenerator::GenerateFrameworkInfoPList( - cmGeneratorTarget* target, const std::string& targetName, const char* fname) + cmGeneratorTarget* target, const std::string& targetName, + const std::string& fname) { // Find the Info.plist template. const char* in = target->GetProperty("MACOSX_FRAMEWORK_INFO_PLIST"); @@ -2897,5 +2932,5 @@ void cmLocalGenerator::GenerateFrameworkInfoPList( cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER"); cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING"); cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_VERSION"); - mf->ConfigureFile(inFile.c_str(), fname, false, false, false); + mf->ConfigureFile(inFile, fname, false, false, false); } diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index f9839f6..67e3c58 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -76,13 +76,13 @@ public: bool IsRootMakefile() const; - ///! Get the makefile for this generator + //! Get the makefile for this generator cmMakefile* GetMakefile() { return this->Makefile; } - ///! Get the makefile for this generator, const version + //! Get the makefile for this generator, const version const cmMakefile* GetMakefile() const { return this->Makefile; } - ///! Get the GlobalGenerator this is associated with + //! Get the GlobalGenerator this is associated with cmGlobalGenerator* GetGlobalGenerator() { return this->GlobalGenerator; } const cmGlobalGenerator* GetGlobalGenerator() const { @@ -118,7 +118,7 @@ public: void AddCompilerRequirementFlag(std::string& flags, cmGeneratorTarget const* target, const std::string& lang); - ///! Append flags to a string. + //! Append flags to a string. virtual void AppendFlags(std::string& flags, const std::string& newFlags) const; virtual void AppendFlags(std::string& flags, const char* newFlags) const; @@ -131,7 +131,7 @@ public: cmGeneratorTarget* target, const std::string& config, const std::string& lang); - ///! Get the include flags for the current makefile and language + //! Get the include flags for the current makefile and language std::string GetIncludeFlags(const std::vector<std::string>& includes, cmGeneratorTarget* target, const std::string& lang, @@ -340,14 +340,14 @@ public: */ void GenerateAppleInfoPList(cmGeneratorTarget* target, const std::string& targetName, - const char* fname); + const std::string& fname); /** * Generate a macOS framework Info.plist file. */ void GenerateFrameworkInfoPList(cmGeneratorTarget* target, const std::string& targetName, - const char* fname); + const std::string& fname); /** Construct a comment for a custom command. */ std::string ConstructComment(cmCustomCommandGenerator const& ccg, const char* default_comment = ""); @@ -403,7 +403,7 @@ public: const std::string& prop); protected: - ///! put all the libraries for a target on into the given stream + //! put all the libraries for a target on into the given stream void OutputLinkLibraries(cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer, std::string& linkLibraries, diff --git a/Source/cmLocalGhsMultiGenerator.cxx b/Source/cmLocalGhsMultiGenerator.cxx index 125e8b5..bf25f98 100644 --- a/Source/cmLocalGhsMultiGenerator.cxx +++ b/Source/cmLocalGhsMultiGenerator.cxx @@ -2,12 +2,15 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmLocalGhsMultiGenerator.h" -#include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGhsMultiTargetGenerator.h" -#include "cmGlobalGhsMultiGenerator.h" -#include "cmMakefile.h" +#include "cmGlobalGenerator.h" #include "cmSourceFile.h" +#include "cmStateTypes.h" +#include "cmSystemTools.h" + +#include <algorithm> +#include <utility> cmLocalGhsMultiGenerator::cmLocalGhsMultiGenerator(cmGlobalGenerator* gg, cmMakefile* mf) @@ -15,9 +18,7 @@ cmLocalGhsMultiGenerator::cmLocalGhsMultiGenerator(cmGlobalGenerator* gg, { } -cmLocalGhsMultiGenerator::~cmLocalGhsMultiGenerator() -{ -} +cmLocalGhsMultiGenerator::~cmLocalGhsMultiGenerator() = default; std::string cmLocalGhsMultiGenerator::GetTargetDirectory( cmGeneratorTarget const* target) const diff --git a/Source/cmLocalGhsMultiGenerator.h b/Source/cmLocalGhsMultiGenerator.h index d5bec42..b6ccd08 100644 --- a/Source/cmLocalGhsMultiGenerator.h +++ b/Source/cmLocalGhsMultiGenerator.h @@ -5,7 +5,14 @@ #include "cmLocalGenerator.h" -class cmGeneratedFileStream; +#include <map> +#include <string> +#include <vector> + +class cmGeneratorTarget; +class cmGlobalGenerator; +class cmMakefile; +class cmSourceFile; /** \class cmLocalGhsMultiGenerator * \brief Write Green Hills MULTI project files. @@ -18,12 +25,12 @@ class cmLocalGhsMultiGenerator : public cmLocalGenerator public: cmLocalGhsMultiGenerator(cmGlobalGenerator* gg, cmMakefile* mf); - virtual ~cmLocalGhsMultiGenerator(); + ~cmLocalGhsMultiGenerator() override; /** * Generate the makefile for this directory. */ - virtual void Generate(); + void Generate() override; std::string GetTargetDirectory( cmGeneratorTarget const* target) const override; diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index c0afc25..f4e3ed8 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -232,8 +232,8 @@ void cmLocalNinjaGenerator::WritePools(std::ostream& os) os << " depth = " << jobs << std::endl; os << std::endl; } else { - cmSystemTools::Error("Invalid pool defined by property 'JOB_POOLS': ", - pool.c_str()); + cmSystemTools::Error("Invalid pool defined by property 'JOB_POOLS': " + + pool); } } } @@ -466,10 +466,12 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement( cmNinjaDeps ninjaOutputs(outputs.size() + byproducts.size()), ninjaDeps; bool symbolic = false; - for (std::vector<std::string>::const_iterator o = outputs.begin(); - !symbolic && o != outputs.end(); ++o) { - if (cmSourceFile* sf = this->Makefile->GetSource(*o)) { + for (std::string const& output : outputs) { + if (cmSourceFile* sf = this->Makefile->GetSource(output)) { symbolic = sf->GetPropertyAsBool("SYMBOLIC"); + if (symbolic) { + break; + } } } diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 7eb4a03..bece12e 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -11,8 +11,9 @@ #include <utility> #include "cmAlgorithms.h" +#include "cmCustomCommand.h" // IWYU pragma: keep #include "cmCustomCommandGenerator.h" -#include "cmFileTimeComparison.h" +#include "cmFileTimeCache.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -22,6 +23,7 @@ #include "cmMakefile.h" #include "cmMakefileTargetGenerator.h" #include "cmOutputConverter.h" +#include "cmRange.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" #include "cmState.h" @@ -1054,7 +1056,7 @@ void cmLocalUnixMakefileGenerator3::AppendCleanCommand( std::string cleanfilePath = cmSystemTools::CollapseFullPath(cleanfile); cmsys::ofstream fout(cleanfilePath.c_str()); if (!fout) { - cmSystemTools::Error("Could not create ", cleanfilePath.c_str()); + cmSystemTools::Error("Could not create " + cleanfilePath); } if (!files.empty()) { fout << "file(REMOVE_RECURSE\n"; @@ -1263,21 +1265,20 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( // Check if any multiple output pairs have a missing file. this->CheckMultipleOutputs(verbose); - std::string dir = cmSystemTools::GetFilenamePath(tgtInfo); - std::string internalDependFile = dir + "/depend.internal"; - std::string dependFile = dir + "/depend.make"; + std::string const targetDir = cmSystemTools::GetFilenamePath(tgtInfo); + std::string const internalDependFile = targetDir + "/depend.internal"; + std::string const dependFile = targetDir + "/depend.make"; // If the target DependInfo.cmake file has changed since the last // time dependencies were scanned then force rescanning. This may // happen when a new source file is added and CMake regenerates the // project but no other sources were touched. bool needRescanDependInfo = false; - cmFileTimeComparison* ftc = - this->GlobalGenerator->GetCMakeInstance()->GetFileComparison(); + cmFileTimeCache* ftc = + this->GlobalGenerator->GetCMakeInstance()->GetFileTimeCache(); { int result; - if (!ftc->FileTimeCompare(internalDependFile, tgtInfo, &result) || - result < 0) { + if (!ftc->Compare(internalDependFile, tgtInfo, &result) || result < 0) { if (verbose) { std::ostringstream msg; msg << "Dependee \"" << tgtInfo << "\" is newer than depender \"" @@ -1291,12 +1292,12 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( // If the directory information is newer than depend.internal, include dirs // may have changed. In this case discard all old dependencies. bool needRescanDirInfo = false; - std::string dirInfoFile = this->GetCurrentBinaryDirectory(); - dirInfoFile += "/CMakeFiles"; - dirInfoFile += "/CMakeDirectoryInformation.cmake"; { + std::string dirInfoFile = this->GetCurrentBinaryDirectory(); + dirInfoFile += "/CMakeFiles"; + dirInfoFile += "/CMakeDirectoryInformation.cmake"; int result; - if (!ftc->FileTimeCompare(internalDependFile, dirInfoFile, &result) || + if (!ftc->Compare(internalDependFile, dirInfoFile, &result) || result < 0) { if (verbose) { std::ostringstream msg; @@ -1312,12 +1313,12 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( // The build.make file may have explicit dependencies for the object // files but these will not affect the scanning process so they need // not be considered. - std::map<std::string, cmDepends::DependencyVector> validDependencies; + cmDepends::DependencyMap validDependencies; bool needRescanDependencies = false; if (!needRescanDirInfo) { cmDependsC checker; checker.SetVerbose(verbose); - checker.SetFileComparison(ftc); + checker.SetFileTimeCache(ftc); // cmDependsC::Check() fills the vector validDependencies() with the // dependencies for those files where they are still valid, i.e. neither // the files themselves nor any files they depend on have changed. @@ -1334,7 +1335,7 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( if (needRescanDependInfo || needRescanDirInfo || needRescanDependencies) { // The dependencies must be regenerated. - std::string targetName = cmSystemTools::GetFilenameName(dir); + std::string targetName = cmSystemTools::GetFilenameName(targetDir); targetName = targetName.substr(0, targetName.length() - 4); std::string message = "Scanning dependencies of target "; message += targetName; @@ -1342,7 +1343,8 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( cmsysTerminal_Color_ForegroundBold, message.c_str(), true, color); - return this->ScanDependencies(dir, validDependencies); + return this->ScanDependencies(targetDir, dependFile, internalDependFile, + validDependencies); } // The dependencies are already up-to-date. @@ -1350,17 +1352,20 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( } bool cmLocalUnixMakefileGenerator3::ScanDependencies( - const std::string& targetDir, - std::map<std::string, cmDepends::DependencyVector>& validDeps) + std::string const& targetDir, std::string const& dependFile, + std::string const& internalDependFile, cmDepends::DependencyMap& validDeps) { // Read the directory information file. cmMakefile* mf = this->Makefile; bool haveDirectoryInfo = false; - std::string dirInfoFile = this->GetCurrentBinaryDirectory(); - dirInfoFile += "/CMakeFiles"; - dirInfoFile += "/CMakeDirectoryInformation.cmake"; - if (mf->ReadListFile(dirInfoFile) && !cmSystemTools::GetErrorOccuredFlag()) { - haveDirectoryInfo = true; + { + std::string dirInfoFile = this->GetCurrentBinaryDirectory(); + dirInfoFile += "/CMakeFiles"; + dirInfoFile += "/CMakeDirectoryInformation.cmake"; + if (mf->ReadListFile(dirInfoFile) && + !cmSystemTools::GetErrorOccuredFlag()) { + haveDirectoryInfo = true; + } } // Lookup useful directory information. @@ -1389,10 +1394,8 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies( // Open the make depends file. This should be copy-if-different // because the make tool may try to reload it needlessly otherwise. - std::string ruleFileNameFull = targetDir; - ruleFileNameFull += "/depend.make"; cmGeneratedFileStream ruleFileStream( - ruleFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding()); + dependFile, false, this->GlobalGenerator->GetMakefileEncoding()); ruleFileStream.SetCopyIfDifferent(true); if (!ruleFileStream) { return false; @@ -1401,11 +1404,8 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies( // Open the cmake dependency tracking file. This should not be // copy-if-different because dependencies are re-scanned when it is // older than the DependInfo.cmake. - std::string internalRuleFileNameFull = targetDir; - internalRuleFileNameFull += "/depend.internal"; cmGeneratedFileStream internalRuleFileStream( - internalRuleFileNameFull, false, - this->GlobalGenerator->GetMakefileEncoding()); + internalDependFile, false, this->GlobalGenerator->GetMakefileEncoding()); if (!internalRuleFileStream) { return false; } @@ -1414,39 +1414,35 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies( this->WriteDisclaimer(internalRuleFileStream); // for each language we need to scan, scan it - std::string const& langStr = - mf->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES"); std::vector<std::string> langs; - cmSystemTools::ExpandListArgument(langStr, langs); + cmSystemTools::ExpandListArgument( + mf->GetSafeDefinition("CMAKE_DEPENDS_LANGUAGES"), langs); for (std::string const& lang : langs) { // construct the checker // Create the scanner for this language - cmDepends* scanner = nullptr; + std::unique_ptr<cmDepends> scanner; if (lang == "C" || lang == "CXX" || lang == "RC" || lang == "ASM" || lang == "CUDA") { // TODO: Handle RC (resource files) dependencies correctly. - scanner = new cmDependsC(this, targetDir, lang, &validDeps); + scanner = cm::make_unique<cmDependsC>(this, targetDir, lang, &validDeps); } #ifdef CMAKE_BUILD_WITH_CMAKE else if (lang == "Fortran") { ruleFileStream << "# Note that incremental build could trigger " << "a call to cmake_copy_f90_mod on each re-build\n"; - scanner = new cmDependsFortran(this); + scanner = cm::make_unique<cmDependsFortran>(this); } else if (lang == "Java") { - scanner = new cmDependsJava(); + scanner = cm::make_unique<cmDependsJava>(); } #endif if (scanner) { scanner->SetLocalGenerator(this); - scanner->SetFileComparison( - this->GlobalGenerator->GetCMakeInstance()->GetFileComparison()); + scanner->SetFileTimeCache( + this->GlobalGenerator->GetCMakeInstance()->GetFileTimeCache()); scanner->SetLanguage(lang); scanner->SetTargetDirectory(targetDir); scanner->Write(ruleFileStream, internalRuleFileStream); - - // free the scanner for this language - delete scanner; } } diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h index ced2dbd..7a0ea98 100644 --- a/Source/cmLocalUnixMakefileGenerator3.h +++ b/Source/cmLocalUnixMakefileGenerator3.h @@ -143,8 +143,7 @@ public: // File pairs for implicit dependency scanning. The key of the map // is the depender and the value is the explicit dependee. - struct ImplicitDependFileMap - : public std::map<std::string, cmDepends::DependencyVector> + struct ImplicitDependFileMap : public cmDepends::DependencyMap { }; struct ImplicitDependLanguageMap @@ -230,9 +229,10 @@ protected: const char* filename = nullptr); // Helper methods for dependency updates. - bool ScanDependencies( - const std::string& targetDir, - std::map<std::string, cmDepends::DependencyVector>& validDeps); + bool ScanDependencies(std::string const& targetDir, + std::string const& dependFile, + std::string const& internalDependFile, + cmDepends::DependencyMap& validDeps); void CheckMultipleOutputs(bool verbose); private: diff --git a/Source/cmLocalVisualStudio10Generator.h b/Source/cmLocalVisualStudio10Generator.h index a4150b9..5c6400e 100644 --- a/Source/cmLocalVisualStudio10Generator.h +++ b/Source/cmLocalVisualStudio10Generator.h @@ -21,7 +21,7 @@ class cmMakefile; class cmLocalVisualStudio10Generator : public cmLocalVisualStudio7Generator { public: - ///! Set cache only and recurse to false by default. + //! Set cache only and recurse to false by default. cmLocalVisualStudio10Generator(cmGlobalGenerator* gg, cmMakefile* mf); virtual ~cmLocalVisualStudio10Generator(); diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 7019552..7ba3471 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmLocalVisualStudio7Generator.h" +#include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmGeneratorTarget.h" #include "cmGlobalVisualStudio7Generator.h" @@ -118,8 +119,8 @@ void cmLocalVisualStudio7Generator::WriteProjectFiles() // If not an in source build, then create the output directory if (this->GetCurrentBinaryDirectory() != this->GetSourceDirectory()) { if (!cmSystemTools::MakeDirectory(this->GetCurrentBinaryDirectory())) { - cmSystemTools::Error("Error creating directory ", - this->GetCurrentBinaryDirectory().c_str()); + cmSystemTools::Error("Error creating directory " + + this->GetCurrentBinaryDirectory()); } } @@ -283,7 +284,7 @@ cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule() file->GetFullPath(); return file; } else { - cmSystemTools::Error("Error adding rule for ", makefileIn.c_str()); + cmSystemTools::Error("Error adding rule for " + makefileIn); return nullptr; } } @@ -293,9 +294,8 @@ void cmLocalVisualStudio7Generator::WriteConfigurations( const std::string& libName, cmGeneratorTarget* target) { fout << "\t<Configurations>\n"; - for (std::vector<std::string>::const_iterator i = configs.begin(); - i != configs.end(); ++i) { - this->WriteConfiguration(fout, i->c_str(), libName, target); + for (std::string const& config : configs) { + this->WriteConfiguration(fout, config.c_str(), libName, target); } fout << "\t</Configurations>\n"; } @@ -569,9 +569,8 @@ public: void Finish() { this->Stream << (this->First ? "" : "\"") << "/>\n"; } void Write(std::vector<cmCustomCommand> const& ccs) { - for (std::vector<cmCustomCommand>::const_iterator ci = ccs.begin(); - ci != ccs.end(); ++ci) { - this->Write(*ci); + for (cmCustomCommand const& command : ccs) { + this->Write(command); } } void Write(cmCustomCommand const& cc) @@ -656,21 +655,14 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( : target->GetLinkerLanguage(configName)); if (linkLanguage.empty()) { cmSystemTools::Error( - "CMake can not determine linker language for target: ", - target->GetName().c_str()); + "CMake can not determine linker language for target: " + + target->GetName()); return; } langForClCompile = linkLanguage; if (langForClCompile == "C" || langForClCompile == "CXX" || langForClCompile == "Fortran") { - std::string baseFlagVar = "CMAKE_"; - baseFlagVar += langForClCompile; - baseFlagVar += "_FLAGS"; - flags = this->Makefile->GetRequiredDefinition(baseFlagVar); - std::string flagVar = - baseFlagVar + std::string("_") + cmSystemTools::UpperCase(configName); - flags += " "; - flags += this->Makefile->GetRequiredDefinition(flagVar); + this->AddLanguageFlags(flags, target, langForClCompile, configName); } // set the correct language if (linkLanguage == "C") { @@ -897,10 +889,8 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( target->GetManifests(manifest_srcs, configName); if (!manifest_srcs.empty()) { fout << "\n\t\t\t\tAdditionalManifestFiles=\""; - for (std::vector<cmSourceFile const*>::const_iterator mi = - manifest_srcs.begin(); - mi != manifest_srcs.end(); ++mi) { - std::string m = (*mi)->GetFullPath(); + for (cmSourceFile const* manifest : manifest_srcs) { + std::string m = manifest->GetFullPath(); fout << this->ConvertToXMLOutputPath(m.c_str()) << ";"; } fout << "\""; @@ -931,7 +921,7 @@ std::string cmLocalVisualStudio7Generator::GetBuildTypeLinkerFlags( std::string extraLinkOptionsBuildTypeDef = rootLinkerFlags + "_" + configTypeUpper; - std::string extraLinkOptionsBuildType = + const std::string& extraLinkOptionsBuildType = this->Makefile->GetRequiredDefinition(extraLinkOptionsBuildTypeDef); return extraLinkOptionsBuildType; @@ -947,21 +937,18 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( std::string extraLinkOptions; if (target->GetType() == cmStateEnums::EXECUTABLE) { extraLinkOptions = - this->Makefile->GetRequiredDefinition("CMAKE_EXE_LINKER_FLAGS") + - std::string(" ") + + this->Makefile->GetRequiredDefinition("CMAKE_EXE_LINKER_FLAGS") + " " + GetBuildTypeLinkerFlags("CMAKE_EXE_LINKER_FLAGS", configName); } if (target->GetType() == cmStateEnums::SHARED_LIBRARY) { extraLinkOptions = this->Makefile->GetRequiredDefinition("CMAKE_SHARED_LINKER_FLAGS") + - std::string(" ") + - GetBuildTypeLinkerFlags("CMAKE_SHARED_LINKER_FLAGS", configName); + " " + GetBuildTypeLinkerFlags("CMAKE_SHARED_LINKER_FLAGS", configName); } if (target->GetType() == cmStateEnums::MODULE_LIBRARY) { extraLinkOptions = this->Makefile->GetRequiredDefinition("CMAKE_MODULE_LINKER_FLAGS") + - std::string(" ") + - GetBuildTypeLinkerFlags("CMAKE_MODULE_LINKER_FLAGS", configName); + " " + GetBuildTypeLinkerFlags("CMAKE_MODULE_LINKER_FLAGS", configName); } const char* targetLinkFlags = target->GetProperty("LINK_FLAGS"); @@ -1050,13 +1037,8 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( } case cmStateEnums::SHARED_LIBRARY: case cmStateEnums::MODULE_LIBRARY: { - std::string targetName; - std::string targetNameSO; - std::string targetNameFull; - std::string targetNameImport; - std::string targetNamePDB; - target->GetLibraryNames(targetName, targetNameSO, targetNameFull, - targetNameImport, targetNamePDB, configName); + cmGeneratorTarget::Names targetNames = + target->GetLibraryNames(configName); // Compute the link library and directory information. cmComputeLinkInformation* pcli = target->GetLinkInformation(configName); @@ -1092,7 +1074,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( fout << "\"\n"; temp = target->GetDirectory(configName); temp += "/"; - temp += targetNameFull; + temp += targetNames.Output; fout << "\t\t\t\tOutputFile=\"" << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n"; this->WriteTargetVersionAttribute(fout, target); @@ -1102,7 +1084,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( fout << "\"\n"; temp = target->GetPDBDirectory(configName); temp += "/"; - temp += targetNamePDB; + temp += targetNames.PDB; fout << "\t\t\t\tProgramDatabaseFile=\"" << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n"; if (targetOptions.IsDebug()) { @@ -1125,7 +1107,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( temp = target->GetDirectory(configName, cmStateEnums::ImportLibraryArtifact); temp += "/"; - temp += targetNameImport; + temp += targetNames.ImportLibrary; fout << "\t\t\t\tImportLibrary=\"" << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\""; if (this->FortranProject) { @@ -1134,12 +1116,8 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( fout << "/>\n"; } break; case cmStateEnums::EXECUTABLE: { - std::string targetName; - std::string targetNameFull; - std::string targetNameImport; - std::string targetNamePDB; - target->GetExecutableNames(targetName, targetNameFull, targetNameImport, - targetNamePDB, configName); + cmGeneratorTarget::Names targetNames = + target->GetExecutableNames(configName); // Compute the link library and directory information. cmComputeLinkInformation* pcli = target->GetLinkInformation(configName); @@ -1177,7 +1155,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( fout << "\"\n"; temp = target->GetDirectory(configName); temp += "/"; - temp += targetNameFull; + temp += targetNames.Output; fout << "\t\t\t\tOutputFile=\"" << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"\n"; this->WriteTargetVersionAttribute(fout, target); @@ -1187,8 +1165,8 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( fout << "\"\n"; std::string path = this->ConvertToXMLOutputPathSingle( target->GetPDBDirectory(configName).c_str()); - fout << "\t\t\t\tProgramDatabaseFile=\"" << path << "/" << targetNamePDB - << "\"\n"; + fout << "\t\t\t\tProgramDatabaseFile=\"" << path << "/" + << targetNames.PDB << "\"\n"; if (targetOptions.IsDebug()) { fout << "\t\t\t\tGenerateDebugInformation=\"true\"\n"; } @@ -1223,7 +1201,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( temp = target->GetDirectory(configName, cmStateEnums::ImportLibraryArtifact); temp += "/"; - temp += targetNameImport; + temp += targetNames.ImportLibrary; fout << "\t\t\t\tImportLibrary=\"" << this->ConvertToXMLOutputPathSingle(temp.c_str()) << "\"/>\n"; break; @@ -1303,14 +1281,14 @@ void cmLocalVisualStudio7GeneratorInternals::OutputLibraries( { cmLocalVisualStudio7Generator* lg = this->LocalGenerator; std::string currentBinDir = lg->GetCurrentBinaryDirectory(); - for (ItemVector::const_iterator l = libs.begin(); l != libs.end(); ++l) { - if (l->IsPath) { + for (auto const& lib : libs) { + if (lib.IsPath) { std::string rel = - lg->MaybeConvertToRelativePath(currentBinDir, l->Value.c_str()); + lg->MaybeConvertToRelativePath(currentBinDir, lib.Value.c_str()); fout << lg->ConvertToXMLOutputPath(rel.c_str()) << " "; - } else if (!l->Target || - l->Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - fout << l->Value << " "; + } else if (!lib.Target || + lib.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { + fout << lib.Value << " "; } } } @@ -1328,10 +1306,9 @@ void cmLocalVisualStudio7GeneratorInternals::OutputObjects( gt->GetExternalObjects(objs, configName); const char* sep = isep ? isep : ""; - for (std::vector<cmSourceFile const*>::const_iterator i = objs.begin(); - i != objs.end(); ++i) { - if (!(*i)->GetObjectLibrary().empty()) { - std::string const& objFile = (*i)->GetFullPath(); + for (cmSourceFile const* obj : objs) { + if (!obj->GetObjectLibrary().empty()) { + std::string const& objFile = obj->GetFullPath(); std::string rel = lg->MaybeConvertToRelativePath(currentBinDir, objFile); fout << sep << lg->ConvertToXMLOutputPath(rel.c_str()); sep = " "; @@ -1344,10 +1321,8 @@ void cmLocalVisualStudio7Generator::OutputLibraryDirectories( { const char* comma = ""; std::string currentBinDir = this->GetCurrentBinaryDirectory(); - for (std::vector<std::string>::const_iterator d = dirs.begin(); - d != dirs.end(); ++d) { + for (std::string dir : dirs) { // Remove any trailing slash and skip empty paths. - std::string dir = *d; if (dir.back() == '/') { dir = dir.substr(0, dir.size() - 1); } @@ -1483,9 +1458,8 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo( // Compute per-source, per-config information. size_t ci = 0; - for (std::vector<std::string>::const_iterator i = configs.begin(); - i != configs.end(); ++i, ++ci) { - std::string configUpper = cmSystemTools::UpperCase(*i); + for (std::string const& config : configs) { + std::string configUpper = cmSystemTools::UpperCase(config); cmLVS7GFileConfig fc; std::string lang = @@ -1498,7 +1472,7 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo( lang = sourceLang; } - cmGeneratorExpressionInterpreter genexInterpreter(lg, *i, gt, lang); + cmGeneratorExpressionInterpreter genexInterpreter(lg, config, gt, lang); bool needfc = false; if (!objectName.empty()) { @@ -1564,7 +1538,7 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo( } } - const std::string& linkLanguage = gt->GetLinkerLanguage(i->c_str()); + const std::string& linkLanguage = gt->GetLinkerLanguage(config.c_str()); // If HEADER_FILE_ONLY is set, we must suppress this generation in // the project file fc.ExcludedFromBuild = sf.GetPropertyAsBool("HEADER_FILE_ONLY") || @@ -1589,8 +1563,9 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo( } if (needfc) { - this->FileConfigMap[*i] = fc; + this->FileConfigMap[config] = fc; } + ++ci; } } @@ -1654,16 +1629,14 @@ bool cmLocalVisualStudio7Generator::WriteGroup( } // Loop through each source in the source group. - for (std::vector<const cmSourceFile*>::const_iterator sf = - sourceFiles.begin(); - sf != sourceFiles.end(); ++sf) { - std::string source = (*sf)->GetFullPath(); + for (const cmSourceFile* sf : sourceFiles) { + std::string source = sf->GetFullPath(); if (source != libName || target->GetType() == cmStateEnums::UTILITY || target->GetType() == cmStateEnums::GLOBAL_TARGET) { // Look up the source kind and configs. std::map<cmSourceFile const*, size_t>::const_iterator map_it = - sources.Index.find(*sf); + sources.Index.find(sf); // The map entry must exist because we populated it earlier. assert(map_it != sources.Index.end()); cmGeneratorTarget::AllConfigSource const& acs = @@ -1676,7 +1649,7 @@ bool cmLocalVisualStudio7Generator::WriteGroup( // Tell MS-Dev what the source is. If the compiler knows how to // build it, then it will. fout << "\t\t\t\tRelativePath=\"" << d << "\">\n"; - if (cmCustomCommand const* command = (*sf)->GetCustomCommand()) { + if (cmCustomCommand const* command = sf->GetCustomCommand()) { this->WriteCustomRule(fout, configs, source.c_str(), *command, fcinfo); } else if (!fcinfo.FileConfigMap.empty()) { const char* aCompilerTool = "VCCLCompilerTool"; @@ -1684,8 +1657,8 @@ bool cmLocalVisualStudio7Generator::WriteGroup( if (this->FortranProject) { aCompilerTool = "VFFortranCompilerTool"; } - std::string const& lang = (*sf)->GetLanguage(); - std::string ext = (*sf)->GetExtension(); + std::string const& lang = sf->GetLanguage(); + std::string ext = sf->GetExtension(); ext = cmSystemTools::LowerCase(ext); if (ext == "idl") { aCompilerTool = "VCMIDLTool"; @@ -1713,12 +1686,10 @@ bool cmLocalVisualStudio7Generator::WriteGroup( if (acs.Kind == cmGeneratorTarget::SourceKindExternalObject) { aCompilerTool = "VCCustomBuildTool"; } - for (std::map<std::string, cmLVS7GFileConfig>::const_iterator fci = - fcinfo.FileConfigMap.begin(); - fci != fcinfo.FileConfigMap.end(); ++fci) { - cmLVS7GFileConfig const& fc = fci->second; + for (auto const& fci : fcinfo.FileConfigMap) { + cmLVS7GFileConfig const& fc = fci.second; fout << "\t\t\t\t<FileConfiguration\n" - << "\t\t\t\t\tName=\"" << fci->first << "|" + << "\t\t\t\t\tName=\"" << fci.first << "|" << gg->GetPlatformName() << "\""; if (fc.ExcludedFromBuild) { fout << " ExcludedFromBuild=\"true\""; @@ -1741,7 +1712,7 @@ bool cmLocalVisualStudio7Generator::WriteGroup( fileOptions.AddDefines(fc.CompileDefsConfig); // validate source level include directories std::vector<std::string> includes; - this->AppendIncludeDirectories(includes, fc.IncludeDirs, **sf); + this->AppendIncludeDirectories(includes, fc.IncludeDirs, *sf); fileOptions.AddIncludes(includes); fileOptions.OutputFlagMap(fout, 5); fileOptions.OutputAdditionalIncludeDirectories( @@ -1794,12 +1765,11 @@ void cmLocalVisualStudio7Generator::WriteCustomRule( if (this->FortranProject) { customTool = "VFCustomBuildTool"; } - for (std::vector<std::string>::const_iterator i = configs.begin(); - i != configs.end(); ++i) { - cmCustomCommandGenerator ccg(command, *i, this); - cmLVS7GFileConfig const& fc = fcinfo.FileConfigMap[*i]; + for (std::string const& config : configs) { + cmCustomCommandGenerator ccg(command, config, this); + cmLVS7GFileConfig const& fc = fcinfo.FileConfigMap[config]; fout << "\t\t\t\t<FileConfiguration\n"; - fout << "\t\t\t\t\tName=\"" << *i << "|" << gg->GetPlatformName() + fout << "\t\t\t\t\tName=\"" << config << "|" << gg->GetPlatformName() << "\">\n"; if (!fc.CompileFlags.empty()) { fout << "\t\t\t\t\t<Tool\n" @@ -1811,7 +1781,7 @@ void cmLocalVisualStudio7Generator::WriteCustomRule( std::string comment = this->ConstructComment(ccg); std::string script = this->ConstructScript(ccg); if (this->FortranProject) { - cmSystemTools::ReplaceString(script, "$(Configuration)", i->c_str()); + cmSystemTools::ReplaceString(script, "$(Configuration)", config.c_str()); } /* clang-format off */ fout << "\t\t\t\t\t<Tool\n" @@ -1832,12 +1802,10 @@ void cmLocalVisualStudio7Generator::WriteCustomRule( fout << this->ConvertToXMLOutputPath(source); } else { // Write out the dependencies for the rule. - for (std::vector<std::string>::const_iterator d = - ccg.GetDepends().begin(); - d != ccg.GetDepends().end(); ++d) { + for (std::string const& d : ccg.GetDepends()) { // Get the real name of the dependency in case it is a CMake target. std::string dep; - if (this->GetRealDependency(d->c_str(), i->c_str(), dep)) { + if (this->GetRealDependency(d.c_str(), config.c_str(), dep)) { fout << this->ConvertToXMLOutputPath(dep.c_str()) << ";"; } } @@ -1849,10 +1817,8 @@ void cmLocalVisualStudio7Generator::WriteCustomRule( } else { // Write a rule for the output generated by this command. const char* sep = ""; - for (std::vector<std::string>::const_iterator o = - ccg.GetOutputs().begin(); - o != ccg.GetOutputs().end(); ++o) { - fout << sep << this->ConvertToXMLOutputPathSingle(o->c_str()); + for (std::string const& output : ccg.GetOutputs()) { + fout << sep << this->ConvertToXMLOutputPathSingle(output.c_str()); sep = ";"; } } @@ -2063,16 +2029,14 @@ void cmLocalVisualStudio7Generator::WriteVCProjFooter( { fout << "\t<Globals>\n"; - std::vector<std::string> const& props = target->GetPropertyKeys(); - for (std::vector<std::string>::const_iterator i = props.begin(); - i != props.end(); ++i) { - if (i->find("VS_GLOBAL_") == 0) { - std::string name = i->substr(10); + for (std::string const& key : target->GetPropertyKeys()) { + if (key.find("VS_GLOBAL_") == 0) { + std::string name = key.substr(10); if (!name.empty()) { /* clang-format off */ fout << "\t\t<Global\n" << "\t\t\tName=\"" << name << "\"\n" - << "\t\t\tValue=\"" << target->GetProperty(*i) << "\"\n" + << "\t\t\tValue=\"" << target->GetProperty(key) << "\"\n" << "\t\t/>\n"; /* clang-format on */ } diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h index b093e6f..ce8eceb 100644 --- a/Source/cmLocalVisualStudio7Generator.h +++ b/Source/cmLocalVisualStudio7Generator.h @@ -43,7 +43,7 @@ public: class cmLocalVisualStudio7Generator : public cmLocalVisualStudioGenerator { public: - ///! Set cache only and recurse to false by default. + //! Set cache only and recurse to false by default. cmLocalVisualStudio7Generator(cmGlobalGenerator* gg, cmMakefile* mf); virtual ~cmLocalVisualStudio7Generator(); diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx index 660729c..f3f2042 100644 --- a/Source/cmLocalVisualStudioGenerator.cxx +++ b/Source/cmLocalVisualStudioGenerator.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmLocalVisualStudioGenerator.h" +#include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" diff --git a/Source/cmLocalXCodeGenerator.h b/Source/cmLocalXCodeGenerator.h index 5c22dcf..42de20b 100644 --- a/Source/cmLocalXCodeGenerator.h +++ b/Source/cmLocalXCodeGenerator.h @@ -24,7 +24,7 @@ class cmSourceFile; class cmLocalXCodeGenerator : public cmLocalGenerator { public: - ///! Set cache only and recurse to false by default. + //! Set cache only and recurse to false by default. cmLocalXCodeGenerator(cmGlobalGenerator* gg, cmMakefile* mf); ~cmLocalXCodeGenerator() override; diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index 7279d5f..6565f02 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -10,6 +10,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmState.h" #include "cmSystemTools.h" diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 7e33bda..3832427 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -31,6 +31,7 @@ #include "cmInstallSubdirectoryGenerator.h" #include "cmListFileCache.h" #include "cmMessageType.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmState.h" @@ -292,13 +293,14 @@ void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const std::string const& only_filename = cmSystemTools::GetFilenameName(full_path); bool trace = trace_only_this_files.empty(); if (!trace) { - for (std::vector<std::string>::const_iterator i = - trace_only_this_files.begin(); - !trace && i != trace_only_this_files.end(); ++i) { - std::string::size_type const pos = full_path.rfind(*i); + for (std::string const& file : trace_only_this_files) { + std::string::size_type const pos = full_path.rfind(file); trace = (pos != std::string::npos) && - ((pos + i->size()) == full_path.size()) && - (only_filename == cmSystemTools::GetFilenameName(*i)); + ((pos + file.size()) == full_path.size()) && + (only_filename == cmSystemTools::GetFilenameName(file)); + if (trace) { + break; + } } // Do nothing if current file wasn't requested for trace... if (!trace) { @@ -347,6 +349,9 @@ public: this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); } + cmMakefileCall(const cmMakefileCall&) = delete; + cmMakefileCall& operator=(const cmMakefileCall&) = delete; + private: cmMakefile* Makefile; }; @@ -438,6 +443,9 @@ public: ~IncludeScope(); void Quiet() { this->ReportError = false; } + IncludeScope(const IncludeScope&) = delete; + IncludeScope& operator=(const IncludeScope&) = delete; + private: cmMakefile* Makefile; bool NoPolicyScope; @@ -605,6 +613,9 @@ public: void Quiet() { this->ReportError = false; } + ListFileScope(const ListFileScope&) = delete; + ListFileScope& operator=(const ListFileScope&) = delete; + private: cmMakefile* Makefile; bool ReportError; @@ -949,9 +960,8 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput( if (file && file->GetCustomCommand() && !replace) { // The rule file already exists. if (commandLines != file->GetCustomCommand()->GetCommandLines()) { - cmSystemTools::Error("Attempt to add a custom rule to output \"", - outName.c_str(), - "\" which already has a custom rule."); + cmSystemTools::Error("Attempt to add a custom rule to output \"" + + outName + "\" which already has a custom rule."); } return file; } @@ -1091,8 +1101,8 @@ void cmMakefile::AddCustomCommandOldStyle( ti->second.AddSource(sf->GetFullPath()); } else { cmSystemTools::Error("Attempt to add a custom rule to a target " - "that does not exist yet for target ", - target.c_str()); + "that does not exist yet for target " + + target); return; } } @@ -1179,8 +1189,7 @@ cmTarget* cmMakefile::AddUtilityCommand( if (sf) { sf->SetProperty("SYMBOLIC", "1"); } else { - cmSystemTools::Error("Could not get source file entry for ", - force.c_str()); + cmSystemTools::Error("Could not get source file entry for " + force); } // Always create the byproduct sources and mark them generated. @@ -1496,6 +1505,9 @@ public: void Quiet() { this->ReportError = false; } + BuildsystemFileScope(const BuildsystemFileScope&) = delete; + BuildsystemFileScope& operator=(const BuildsystemFileScope&) = delete; + private: cmMakefile* Makefile; cmGlobalGenerator* GG; @@ -1848,10 +1860,8 @@ void cmMakefile::CheckForUnusedVariables() const if (!this->WarnUnused) { return; } - const std::vector<std::string>& unused = this->StateSnapshot.UnusedKeys(); - std::vector<std::string>::const_iterator it = unused.begin(); - for (; it != unused.end(); ++it) { - this->LogUnused("out of scope", *it); + for (const std::string& key : this->StateSnapshot.UnusedKeys()) { + this->LogUnused("out of scope", key); } } @@ -2013,8 +2023,7 @@ cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type, { cmTargets::iterator it = this->Targets - .insert(cmTargets::value_type( - name, cmTarget(name, type, cmTarget::VisibilityNormal, this))) + .emplace(name, cmTarget(name, type, cmTarget::VisibilityNormal, this)) .first; this->GetGlobalGenerator()->IndexTarget(&it->second); this->GetStateSnapshot().GetDirectory().AddNormalTargetName(name); @@ -2195,7 +2204,7 @@ cmSourceGroup* cmMakefile::FindSourceGroup( } // Shouldn't get here, but just in case, return the default group. - return &groups.front(); + return groups.data(); } #endif @@ -2427,16 +2436,19 @@ bool cmMakefile::CanIWriteThisFile(std::string const& fileName) const cmSystemTools::SameFile(fileName, this->GetHomeOutputDirectory()); } -std::string cmMakefile::GetRequiredDefinition(const std::string& name) const +const std::string& cmMakefile::GetRequiredDefinition( + const std::string& name) const { - const char* ret = this->GetDefinition(name); - if (!ret) { + static std::string const empty; + const std::string* def = GetDef(name); + if (!def) { cmSystemTools::Error("Error required internal CMake variable not " - "set, cmake may not be built correctly.\n", - "Missing variable is:\n", name.c_str()); - return std::string(); + "set, cmake may not be built correctly.\n" + "Missing variable is:\n" + + name); + return empty; } - return std::string(ret); + return *def; } bool cmMakefile::IsDefinitionSet(const std::string& name) const @@ -3057,10 +3069,8 @@ bool cmMakefile::IsFunctionBlocked(const cmListFileFunction& lff, // loop over all function blockers to see if any block this command // evaluate in reverse, this is critical for balanced IF statements etc - std::vector<cmFunctionBlocker*>::reverse_iterator pos; - for (pos = this->FunctionBlockers.rbegin(); - pos != this->FunctionBlockers.rend(); ++pos) { - if ((*pos)->IsFunctionBlocked(lff, *this, status)) { + for (cmFunctionBlocker* pos : cmReverseRange(this->FunctionBlockers)) { + if (pos->IsFunctionBlocked(lff, *this, status)) { return true; } } @@ -3548,7 +3558,7 @@ cmState* cmMakefile::GetState() const return this->GetCMakeInstance()->GetState(); } -void cmMakefile::DisplayStatus(const char* message, float s) const +void cmMakefile::DisplayStatus(const std::string& message, float s) const { cmake* cm = this->GetCMakeInstance(); if (cm->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { @@ -3718,22 +3728,23 @@ void cmMakefile::ConfigureString(const std::string& input, std::string& output, lineNumber, true, true); } -int cmMakefile::ConfigureFile(const char* infile, const char* outfile, - bool copyonly, bool atOnly, bool escapeQuotes, +int cmMakefile::ConfigureFile(const std::string& infile, + const std::string& outfile, bool copyonly, + bool atOnly, bool escapeQuotes, cmNewLineStyle newLine) { int res = 1; if (!this->CanIWriteThisFile(outfile)) { - cmSystemTools::Error("Attempt to write file: ", outfile, + cmSystemTools::Error("Attempt to write file: " + outfile + " into a source directory."); return 0; } if (!cmSystemTools::FileExists(infile)) { - cmSystemTools::Error("File ", infile, " does not exist."); + cmSystemTools::Error("File " + infile + " does not exist."); return 0; } std::string soutfile = outfile; - std::string sinfile = infile; + const std::string& sinfile = infile; this->AddCMakeDependFile(sinfile); cmSystemTools::ConvertToUnixSlashes(soutfile); @@ -3767,15 +3778,15 @@ int cmMakefile::ConfigureFile(const char* infile, const char* outfile, tempOutputFile += ".tmp"; cmsys::ofstream fout(tempOutputFile.c_str(), omode); if (!fout) { - cmSystemTools::Error("Could not open file for write in copy operation ", - tempOutputFile.c_str()); + cmSystemTools::Error("Could not open file for write in copy operation " + + tempOutputFile); cmSystemTools::ReportLastSystemError(""); return 0; } cmsys::ifstream fin(sinfile.c_str()); if (!fin) { - cmSystemTools::Error("Could not open file for read in copy operation ", - sinfile.c_str()); + cmSystemTools::Error("Could not open file for read in copy operation " + + sinfile); return 0; } @@ -4275,7 +4286,7 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, // Deprecate old policies, especially those that require a lot // of code to maintain the old behavior. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0065 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0066 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. @@ -4713,6 +4724,13 @@ bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, needCxx17, needCxx20); const char* existingCxxStandard = target->GetProperty("CXX_STANDARD"); + if (existingCxxStandard == nullptr) { + const char* defaultCxxStandard = + this->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT"); + if (defaultCxxStandard && *defaultCxxStandard) { + existingCxxStandard = defaultCxxStandard; + } + } const char* const* existingCxxLevel = nullptr; if (existingCxxStandard) { existingCxxLevel = @@ -4815,6 +4833,13 @@ bool cmMakefile::AddRequiredTargetCFeature(cmTarget* target, this->CheckNeededCLanguage(feature, needC90, needC99, needC11); const char* existingCStandard = target->GetProperty("C_STANDARD"); + if (existingCStandard == nullptr) { + const char* defaultCStandard = + this->GetDefinition("CMAKE_C_STANDARD_DEFAULT"); + if (defaultCStandard && *defaultCStandard) { + existingCStandard = defaultCStandard; + } + } if (existingCStandard) { if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(existingCStandard)) == cm::cend(C_STANDARDS)) { diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 70a5689..8cc14f3 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -257,7 +257,7 @@ public: * can be used in CMake to refer to lists, directories, etc. */ void AddDefinition(const std::string& name, const char* value); - ///! Add a definition to this makefile and the global cmake cache. + //! Add a definition to this makefile and the global cmake cache. void AddCacheDefinition(const std::string& name, const char* value, const char* doc, cmStateEnums::CacheEntryType type, bool force = false); @@ -272,7 +272,7 @@ public: * for cache entries, and will only affect the current makefile. */ void RemoveDefinition(const std::string& name); - ///! Remove a definition from the cache. + //! Remove a definition from the cache. void RemoveCacheDefinition(const std::string& name); /** @@ -313,6 +313,9 @@ public: PolicyPushPop(cmMakefile* m); ~PolicyPushPop(); + PolicyPushPop(const PolicyPushPop&) = delete; + PolicyPushPop& operator=(const PolicyPushPop&) = delete; + private: cmMakefile* Makefile; }; @@ -436,7 +439,7 @@ public: const char* GetDefinition(const std::string&) const; const std::string* GetDef(const std::string&) const; const std::string& GetSafeDefinition(const std::string&) const; - std::string GetRequiredDefinition(const std::string& name) const; + const std::string& GetRequiredDefinition(const std::string& name) const; bool IsDefinitionSet(const std::string&) const; /** * Get the list of all variables in the current space. If argument @@ -545,7 +548,7 @@ public: { return this->ListFiles; } - ///! When the file changes cmake will be re-run from the build system. + //! When the file changes cmake will be re-run from the build system. void AddCMakeDependFile(const std::string& file) { this->ListFiles.push_back(file); @@ -607,8 +610,8 @@ public: /** * Copy file but change lines according to ConfigureString */ - int ConfigureFile(const char* infile, const char* outfile, bool copyonly, - bool atOnly, bool escapeQuotes, + int ConfigureFile(const std::string& infile, const std::string& outfile, + bool copyonly, bool atOnly, bool escapeQuotes, cmNewLineStyle = cmNewLineStyle()); /** @@ -623,7 +626,7 @@ public: bool ExecuteCommand(const cmListFileFunction& lff, cmExecutionStatus& status); - ///! Enable support for named language, if nil then all languages are + //! Enable support for named language, if nil then all languages are /// enabled. void EnableLanguage(std::vector<std::string> const& languages, bool optional); @@ -638,8 +641,8 @@ public: cmVariableWatch* GetVariableWatch() const; #endif - ///! Display progress or status message. - void DisplayStatus(const char*, float) const; + //! Display progress or status message. + void DisplayStatus(const std::string&, float) const; /** * Expand the given list file arguments into the full set after @@ -674,7 +677,7 @@ public: */ cmSourceFile* GetSourceFileWithOutput(const std::string& outName) const; - ///! Add a new cmTest to the list of tests for this makefile. + //! Add a new cmTest to the list of tests for this makefile. cmTest* CreateTest(const std::string& testName); /** Get a cmTest pointer for a given test name, if the name is @@ -698,7 +701,7 @@ public: std::string GetModulesFile(const std::string& name, bool& system) const; - ///! Set/Get a property of this directory + //! Set/Get a property of this directory void SetProperty(const std::string& prop, const char* value); void AppendProperty(const std::string& prop, const char* value, bool asString = false); @@ -707,7 +710,7 @@ public: bool GetPropertyAsBool(const std::string& prop) const; std::vector<std::string> GetPropertyKeys() const; - ///! Initialize a makefile from its parent + //! Initialize a makefile from its parent void InitializeFromParent(cmMakefile* parent); void AddInstallGenerator(cmInstallGenerator* g) @@ -743,6 +746,9 @@ public: cmPolicies::PolicyMap const& pm); ~FunctionPushPop(); + FunctionPushPop(const FunctionPushPop&) = delete; + FunctionPushPop& operator=(const FunctionPushPop&) = delete; + void Quiet() { this->ReportError = false; } private: @@ -757,6 +763,9 @@ public: cmPolicies::PolicyMap const& pm); ~MacroPushPop(); + MacroPushPop(const MacroPushPop&) = delete; + MacroPushPop& operator=(const MacroPushPop&) = delete; + void Quiet() { this->ReportError = false; } private: diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index e8ae5ae..ebf5fc2 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -9,6 +9,7 @@ #include <utility> #include <vector> +#include "cmAlgorithms.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalUnixMakefileGenerator3.h" @@ -31,19 +32,16 @@ cmMakefileExecutableTargetGenerator::cmMakefileExecutableTargetGenerator( : cmMakefileTargetGenerator(target) { this->CustomCommandDriver = OnDepends; - this->GeneratorTarget->GetExecutableNames( - this->TargetNameOut, this->TargetNameReal, this->TargetNameImport, - this->TargetNamePDB, this->ConfigName); + this->TargetNames = + this->GeneratorTarget->GetExecutableNames(this->ConfigName); this->OSXBundleGenerator = - new cmOSXBundleGenerator(target, this->ConfigName); + cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName); this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders); } -cmMakefileExecutableTargetGenerator::~cmMakefileExecutableTargetGenerator() -{ - delete this->OSXBundleGenerator; -} +cmMakefileExecutableTargetGenerator::~cmMakefileExecutableTargetGenerator() = + default; void cmMakefileExecutableTargetGenerator::WriteRuleFiles() { @@ -305,18 +303,13 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) std::vector<std::string> commands; // Get the name of the executable to generate. - std::string targetName; - std::string targetNameReal; - std::string targetNameImport; - std::string targetNamePDB; - this->GeneratorTarget->GetExecutableNames(targetName, targetNameReal, - targetNameImport, targetNamePDB, - this->ConfigName); + cmGeneratorTarget::Names targetNames = + this->GeneratorTarget->GetExecutableNames(this->ConfigName); // Construct the full path version of the names. std::string outpath = this->GeneratorTarget->GetDirectory(this->ConfigName); if (this->GeneratorTarget->IsAppBundleOnApple()) { - this->OSXBundleGenerator->CreateAppBundle(targetName, outpath); + this->OSXBundleGenerator->CreateAppBundle(targetNames.Output, outpath); } outpath += "/"; std::string outpathImp; @@ -326,12 +319,12 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) outpath += "/CMakeRelink.dir"; cmSystemTools::MakeDirectory(outpath); outpath += "/"; - if (!targetNameImport.empty()) { + if (!targetNames.ImportLibrary.empty()) { outpathImp = outpath; } } else { cmSystemTools::MakeDirectory(outpath); - if (!targetNameImport.empty()) { + if (!targetNames.ImportLibrary.empty()) { outpathImp = this->GeneratorTarget->GetDirectory( this->ConfigName, cmStateEnums::ImportLibraryArtifact); cmSystemTools::MakeDirectory(outpathImp); @@ -348,10 +341,10 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) cmSystemTools::MakeDirectory(pdbOutputPath); pdbOutputPath += "/"; - std::string targetFullPath = outpath + targetName; - std::string targetFullPathReal = outpath + targetNameReal; - std::string targetFullPathPDB = pdbOutputPath + targetNamePDB; - std::string targetFullPathImport = outpathImp + targetNameImport; + std::string targetFullPath = outpath + targetNames.Output; + std::string targetFullPathReal = outpath + targetNames.Real; + std::string targetFullPathPDB = pdbOutputPath + targetNames.PDB; + std::string targetFullPathImport = outpathImp + targetNames.ImportLibrary; std::string targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat( targetFullPathPDB, cmOutputConverter::SHELL); // Convert to the output path to use in constructing commands. @@ -376,8 +369,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) // Make sure we have a link language. if (linkLanguage.empty()) { - cmSystemTools::Error("Cannot determine link language for target \"", - this->GeneratorTarget->GetName().c_str(), "\"."); + cmSystemTools::Error("Cannot determine link language for target \"" + + this->GeneratorTarget->GetName() + "\"."); return; } @@ -468,11 +461,11 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPath + ".manifest")); #endif - if (targetNameReal != targetName) { + if (this->TargetNames.Real != this->TargetNames.Output) { exeCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathReal)); } - if (!targetNameImport.empty()) { + if (!this->TargetNames.ImportLibrary.empty()) { exeCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathImport)); diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 5a1ef4e..0393e22 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -9,6 +9,7 @@ #include <utility> #include <vector> +#include "cmAlgorithms.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalUnixMakefileGenerator3.h" @@ -32,20 +33,17 @@ cmMakefileLibraryTargetGenerator::cmMakefileLibraryTargetGenerator( { this->CustomCommandDriver = OnDepends; if (this->GeneratorTarget->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - this->GeneratorTarget->GetLibraryNames( - this->TargetNameOut, this->TargetNameSO, this->TargetNameReal, - this->TargetNameImport, this->TargetNamePDB, this->ConfigName); + this->TargetNames = + this->GeneratorTarget->GetLibraryNames(this->ConfigName); } this->OSXBundleGenerator = - new cmOSXBundleGenerator(target, this->ConfigName); + cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName); this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders); } -cmMakefileLibraryTargetGenerator::~cmMakefileLibraryTargetGenerator() -{ - delete this->OSXBundleGenerator; -} +cmMakefileLibraryTargetGenerator::~cmMakefileLibraryTargetGenerator() = + default; void cmMakefileLibraryTargetGenerator::WriteRuleFiles() { @@ -463,8 +461,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( // Make sure we have a link language. if (linkLanguage.empty()) { - cmSystemTools::Error("Cannot determine link language for target \"", - this->GeneratorTarget->GetName().c_str(), "\"."); + cmSystemTools::Error("Cannot determine link language for target \"" + + this->GeneratorTarget->GetName() + "\"."); return; } @@ -489,25 +487,20 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( } // Construct the name of the library. - std::string targetName; - std::string targetNameSO; - std::string targetNameReal; - std::string targetNameImport; - std::string targetNamePDB; - this->GeneratorTarget->GetLibraryNames(targetName, targetNameSO, - targetNameReal, targetNameImport, - targetNamePDB, this->ConfigName); + this->GeneratorTarget->GetLibraryNames(this->ConfigName); // Construct the full path version of the names. std::string outpath; std::string outpathImp; if (this->GeneratorTarget->IsFrameworkOnApple()) { outpath = this->GeneratorTarget->GetDirectory(this->ConfigName); - this->OSXBundleGenerator->CreateFramework(targetName, outpath); + this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output, + outpath); outpath += "/"; } else if (this->GeneratorTarget->IsCFBundleOnApple()) { outpath = this->GeneratorTarget->GetDirectory(this->ConfigName); - this->OSXBundleGenerator->CreateCFBundle(targetName, outpath); + this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output, + outpath); outpath += "/"; } else if (relink) { outpath = this->Makefile->GetCurrentBinaryDirectory(); @@ -515,14 +508,14 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( outpath += "/CMakeRelink.dir"; cmSystemTools::MakeDirectory(outpath); outpath += "/"; - if (!targetNameImport.empty()) { + if (!this->TargetNames.ImportLibrary.empty()) { outpathImp = outpath; } } else { outpath = this->GeneratorTarget->GetDirectory(this->ConfigName); cmSystemTools::MakeDirectory(outpath); outpath += "/"; - if (!targetNameImport.empty()) { + if (!this->TargetNames.ImportLibrary.empty()) { outpathImp = this->GeneratorTarget->GetDirectory( this->ConfigName, cmStateEnums::ImportLibraryArtifact); cmSystemTools::MakeDirectory(outpathImp); @@ -539,11 +532,12 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( cmSystemTools::MakeDirectory(pdbOutputPath); pdbOutputPath += "/"; - std::string targetFullPath = outpath + targetName; - std::string targetFullPathPDB = pdbOutputPath + targetNamePDB; - std::string targetFullPathSO = outpath + targetNameSO; - std::string targetFullPathReal = outpath + targetNameReal; - std::string targetFullPathImport = outpathImp + targetNameImport; + std::string targetFullPath = outpath + this->TargetNames.Output; + std::string targetFullPathPDB = pdbOutputPath + this->TargetNames.PDB; + std::string targetFullPathSO = outpath + this->TargetNames.SharedObject; + std::string targetFullPathReal = outpath + this->TargetNames.Real; + std::string targetFullPathImport = + outpathImp + this->TargetNames.ImportLibrary; // Construct the output path version of the names for use in command // arguments. @@ -616,15 +610,16 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( commands1.clear(); } - if (targetName != targetNameReal) { + if (this->TargetNames.Output != this->TargetNames.Real) { libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPath)); } - if (targetNameSO != targetNameReal && targetNameSO != targetName) { + if (this->TargetNames.SharedObject != this->TargetNames.Real && + this->TargetNames.SharedObject != this->TargetNames.Output) { libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathSO)); } - if (!targetNameImport.empty()) { + if (!this->TargetNames.ImportLibrary.empty()) { libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathImport)); @@ -820,7 +815,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( vars.ObjectsQuoted = buildObjs.c_str(); if (this->GeneratorTarget->HasSOName(this->ConfigName)) { vars.SONameFlag = this->Makefile->GetSONameFlag(linkLanguage); - vars.TargetSOName = targetNameSO.c_str(); + vars.TargetSOName = this->TargetNames.SharedObject.c_str(); } vars.LinkFlags = linkFlags.c_str(); @@ -981,10 +976,11 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( // Compute the list of outputs. std::vector<std::string> outputs(1, targetFullPathReal); - if (targetNameSO != targetNameReal) { + if (this->TargetNames.SharedObject != this->TargetNames.Real) { outputs.push_back(targetFullPathSO); } - if (targetName != targetNameSO && targetName != targetNameReal) { + if (this->TargetNames.Output != this->TargetNames.SharedObject && + this->TargetNames.Output != this->TargetNames.Real) { outputs.push_back(targetFullPath); } diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index af34169..340e405 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -21,6 +21,7 @@ #include "cmMakefileLibraryTargetGenerator.h" #include "cmMakefileUtilityTargetGenerator.h" #include "cmOutputConverter.h" +#include "cmRange.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" #include "cmState.h" @@ -654,19 +655,20 @@ void cmMakefileTargetGenerator::WriteObjectBuildFile( std::string cmdVar; if (this->GeneratorTarget->GetPropertyAsBool( "CUDA_SEPARABLE_COMPILATION")) { - cmdVar = std::string("CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION"); + cmdVar = "CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION"; } else if (this->GeneratorTarget->GetPropertyAsBool( "CUDA_PTX_COMPILATION")) { - cmdVar = std::string("CMAKE_CUDA_COMPILE_PTX_COMPILATION"); + cmdVar = "CMAKE_CUDA_COMPILE_PTX_COMPILATION"; } else { - cmdVar = std::string("CMAKE_CUDA_COMPILE_WHOLE_COMPILATION"); + cmdVar = "CMAKE_CUDA_COMPILE_WHOLE_COMPILATION"; } - std::string compileRule = this->Makefile->GetRequiredDefinition(cmdVar); + const std::string& compileRule = + this->Makefile->GetRequiredDefinition(cmdVar); cmSystemTools::ExpandListArgument(compileRule, compileCommands); } else { - const std::string cmdVar = - std::string("CMAKE_") + lang + "_COMPILE_OBJECT"; - std::string compileRule = this->Makefile->GetRequiredDefinition(cmdVar); + const std::string cmdVar = "CMAKE_" + lang + "_COMPILE_OBJECT"; + const std::string& compileRule = + this->Makefile->GetRequiredDefinition(cmdVar); cmSystemTools::ExpandListArgument(compileRule, compileCommands); } @@ -974,18 +976,17 @@ bool cmMakefileTargetGenerator::WriteMakeRule( // For multiple outputs, make the extra ones depend on the first one. std::vector<std::string> const output_depends(1, outputs[0]); std::string binDir = this->LocalGenerator->GetBinaryDirectory(); - for (std::vector<std::string>::const_iterator o = outputs.begin() + 1; - o != outputs.end(); ++o) { + for (std::string const& output : cmMakeRange(outputs).advance(1)) { // Touch the extra output so "make" knows that it was updated, // but only if the output was actually created. std::string const out = this->LocalGenerator->ConvertToOutputFormat( - this->LocalGenerator->MaybeConvertToRelativePath(binDir, *o), + this->LocalGenerator->MaybeConvertToRelativePath(binDir, output), cmOutputConverter::SHELL); std::vector<std::string> output_commands; bool o_symbolic = false; if (need_symbolic) { - if (cmSourceFile* sf = this->Makefile->GetSource(*o)) { + if (cmSourceFile* sf = this->Makefile->GetSource(output)) { o_symbolic = sf->GetPropertyAsBool("SYMBOLIC"); } } @@ -994,13 +995,13 @@ bool cmMakefileTargetGenerator::WriteMakeRule( if (!o_symbolic) { output_commands.push_back("@$(CMAKE_COMMAND) -E touch_nocreate " + out); } - this->LocalGenerator->WriteMakeRule(os, nullptr, *o, output_depends, + this->LocalGenerator->WriteMakeRule(os, nullptr, output, output_depends, output_commands, o_symbolic, in_help); if (!o_symbolic) { // At build time, remove the first output if this one does not exist // so that "make" will rerun the real commands that create this one. - MultipleOutputPairsType::value_type p(*o, outputs[0]); + MultipleOutputPairsType::value_type p(output, outputs[0]); this->MultipleOutputPairs.insert(p); } } diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index 529b4db..ec58d17 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -12,12 +12,12 @@ #include <vector> #include "cmCommonTargetGenerator.h" +#include "cmGeneratorTarget.h" #include "cmLocalUnixMakefileGenerator3.h" #include "cmOSXBundleGenerator.h" class cmCustomCommandGenerator; class cmGeneratedFileStream; -class cmGeneratorTarget; class cmGlobalUnixMakefileGenerator3; class cmLinkLineComputer; class cmOutputConverter; @@ -231,15 +231,11 @@ protected: bool in_help = false); // Target name info. - std::string TargetNameOut; - std::string TargetNameSO; - std::string TargetNameReal; - std::string TargetNameImport; - std::string TargetNamePDB; + cmGeneratorTarget::Names TargetNames; // macOS content info. std::set<std::string> MacContentFolders; - cmOSXBundleGenerator* OSXBundleGenerator; + std::unique_ptr<cmOSXBundleGenerator> OSXBundleGenerator; MacOSXContentGeneratorType* MacOSXContentGenerator; }; diff --git a/Source/cmMakefileUtilityTargetGenerator.cxx b/Source/cmMakefileUtilityTargetGenerator.cxx index 8fbd5d2..4236995 100644 --- a/Source/cmMakefileUtilityTargetGenerator.cxx +++ b/Source/cmMakefileUtilityTargetGenerator.cxx @@ -7,6 +7,7 @@ #include <utility> #include <vector> +#include "cmAlgorithms.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalUnixMakefileGenerator3.h" @@ -21,14 +22,12 @@ cmMakefileUtilityTargetGenerator::cmMakefileUtilityTargetGenerator( { this->CustomCommandDriver = OnUtility; this->OSXBundleGenerator = - new cmOSXBundleGenerator(target, this->ConfigName); + cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName); this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders); } -cmMakefileUtilityTargetGenerator::~cmMakefileUtilityTargetGenerator() -{ - delete this->OSXBundleGenerator; -} +cmMakefileUtilityTargetGenerator::~cmMakefileUtilityTargetGenerator() = + default; void cmMakefileUtilityTargetGenerator::WriteRuleFiles() { diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx index 95f5fcb..5320ec5 100644 --- a/Source/cmMessageCommand.cxx +++ b/Source/cmMessageCommand.cxx @@ -6,7 +6,11 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmMessenger.h" +#include "cmRange.h" #include "cmSystemTools.h" +#include "cmake.h" + +#include <cassert> class cmExecutionStatus; @@ -18,49 +22,88 @@ bool cmMessageCommand::InitialPass(std::vector<std::string> const& args, this->SetError("called with incorrect number of arguments"); return false; } - std::vector<std::string>::const_iterator i = args.begin(); + auto i = args.cbegin(); - MessageType type = MessageType::MESSAGE; - bool status = false; - bool fatal = false; + auto type = MessageType::MESSAGE; + auto status = false; + auto fatal = false; + auto level = cmake::LogLevel::LOG_UNDEFINED; if (*i == "SEND_ERROR") { type = MessageType::FATAL_ERROR; + level = cmake::LogLevel::LOG_ERROR; ++i; } else if (*i == "FATAL_ERROR") { fatal = true; type = MessageType::FATAL_ERROR; + level = cmake::LogLevel::LOG_ERROR; ++i; } else if (*i == "WARNING") { type = MessageType::WARNING; + level = cmake::LogLevel::LOG_WARNING; ++i; } else if (*i == "AUTHOR_WARNING") { if (this->Makefile->IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") && !this->Makefile->IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) { fatal = true; type = MessageType::AUTHOR_ERROR; + level = cmake::LogLevel::LOG_ERROR; } else if (!this->Makefile->IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { type = MessageType::AUTHOR_WARNING; + level = cmake::LogLevel::LOG_WARNING; } else { return true; } ++i; } else if (*i == "STATUS") { status = true; + level = cmake::LogLevel::LOG_STATUS; + ++i; + } else if (*i == "VERBOSE") { + status = true; + level = cmake::LogLevel::LOG_VERBOSE; + ++i; + } else if (*i == "DEBUG") { + status = true; + level = cmake::LogLevel::LOG_DEBUG; + ++i; + } else if (*i == "TRACE") { + status = true; + level = cmake::LogLevel::LOG_TRACE; ++i; } else if (*i == "DEPRECATION") { if (this->Makefile->IsOn("CMAKE_ERROR_DEPRECATED")) { fatal = true; type = MessageType::DEPRECATION_ERROR; + level = cmake::LogLevel::LOG_ERROR; } else if ((!this->Makefile->IsSet("CMAKE_WARN_DEPRECATED") || this->Makefile->IsOn("CMAKE_WARN_DEPRECATED"))) { type = MessageType::DEPRECATION_WARNING; + level = cmake::LogLevel::LOG_WARNING; } else { return true; } ++i; + } else if (*i == "NOTICE") { + // `NOTICE` message type is going to be output to stderr + level = cmake::LogLevel::LOG_NOTICE; + ++i; + } else { + // Messages w/o any type are `NOTICE`s + level = cmake::LogLevel::LOG_NOTICE; + } + assert("Message log level expected to be set" && + level != cmake::LogLevel::LOG_UNDEFINED); + + auto desiredLevel = this->Makefile->GetCMakeInstance()->GetLogLevel(); + assert("Expected a valid log level here" && + desiredLevel != cmake::LogLevel::LOG_UNDEFINED); + + if (desiredLevel < level) { + // Suppress the message + return true; } - std::string message = cmJoin(cmMakeRange(i, args.end()), std::string()); + auto message = cmJoin(cmMakeRange(i, args.cend()), ""); if (type != MessageType::MESSAGE) { // we've overridden the message type, above, so display it directly @@ -68,7 +111,7 @@ bool cmMessageCommand::InitialPass(std::vector<std::string> const& args, m->DisplayMessage(type, message, this->Makefile->GetBacktrace()); } else { if (status) { - this->Makefile->DisplayStatus(message.c_str(), -1); + this->Makefile->DisplayStatus(message, -1); } else { cmSystemTools::Message(message); } diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index becc424..ebb7c15 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -12,6 +12,7 @@ #include <utility> #include "cmAlgorithms.h" +#include "cmCustomCommand.h" // IWYU pragma: keep #include "cmCustomCommandGenerator.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" @@ -32,8 +33,6 @@ #include "cmStateTypes.h" #include "cmSystemTools.h" -class cmCustomCommand; - cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator( cmGeneratorTarget* target) : cmNinjaTargetGenerator(target) @@ -41,13 +40,10 @@ cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator( { this->TargetLinkLanguage = target->GetLinkerLanguage(this->GetConfigName()); if (target->GetType() == cmStateEnums::EXECUTABLE) { - this->GetGeneratorTarget()->GetExecutableNames( - this->TargetNameOut, this->TargetNameReal, this->TargetNameImport, - this->TargetNamePDB, GetLocalGenerator()->GetConfigName()); + this->TargetNames = this->GetGeneratorTarget()->GetExecutableNames( + GetLocalGenerator()->GetConfigName()); } else { - this->GetGeneratorTarget()->GetLibraryNames( - this->TargetNameOut, this->TargetNameSO, this->TargetNameReal, - this->TargetNameImport, this->TargetNamePDB, + this->TargetNames = this->GetGeneratorTarget()->GetLibraryNames( GetLocalGenerator()->GetConfigName()); } @@ -58,21 +54,18 @@ cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator( } this->OSXBundleGenerator = - new cmOSXBundleGenerator(target, this->GetConfigName()); + cm::make_unique<cmOSXBundleGenerator>(target, this->GetConfigName()); this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders); } -cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() -{ - delete this->OSXBundleGenerator; -} +cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default; void cmNinjaNormalTargetGenerator::Generate() { if (this->TargetLinkLanguage.empty()) { cmSystemTools::Error("CMake can not determine linker language for " - "target: ", - this->GetGeneratorTarget()->GetName().c_str()); + "target: " + + this->GetGeneratorTarget()->GetName()); return; } @@ -286,6 +279,11 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile) cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()); vars.Language = this->TargetLinkLanguage.c_str(); + if (this->TargetLinkLanguage == "Swift") { + vars.SwiftPartialModules = "$SWIFT_PARTIAL_MODULES"; + vars.TargetSwiftModule = "$TARGET_SWIFT_MODULE"; + vars.TargetSwiftDoc = "$TARGET_SWIFT_DOC"; + } std::string responseFlag; if (!useResponseFile) { @@ -395,7 +393,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile) /*generator*/ false); } - if (this->TargetNameOut != this->TargetNameReal && + if (this->TargetNames.Output != this->TargetNames.Real && !this->GetGeneratorTarget()->IsFrameworkOnApple()) { std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat( @@ -690,7 +688,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() if (this->GetGeneratorTarget()->HasSOName(cfgName)) { vars["SONAME_FLAG"] = this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage); - vars["SONAME"] = this->TargetNameSO; + vars["SONAME"] = this->TargetNames.SharedObject; if (targetType == cmStateEnums::SHARED_LIBRARY) { std::string install_dir = this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(cfgName); @@ -701,7 +699,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() } } - if (!this->TargetNameImport.empty()) { + if (!this->TargetNames.ImportLibrary.empty()) { const std::string impLibPath = localGen.ConvertToOutputFormat( targetOutputImplib, cmOutputConverter::SHELL); vars["TARGET_IMPLIB"] = impLibPath; @@ -763,24 +761,25 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() if (gt.IsAppBundleOnApple()) { // Create the app bundle std::string outpath = gt.GetDirectory(cfgName); - this->OSXBundleGenerator->CreateAppBundle(this->TargetNameOut, outpath); + this->OSXBundleGenerator->CreateAppBundle(this->TargetNames.Output, + outpath); // Calculate the output path targetOutput = outpath; targetOutput += "/"; - targetOutput += this->TargetNameOut; + targetOutput += this->TargetNames.Output; targetOutput = this->ConvertToNinjaPath(targetOutput); targetOutputReal = outpath; targetOutputReal += "/"; - targetOutputReal += this->TargetNameReal; + targetOutputReal += this->TargetNames.Real; targetOutputReal = this->ConvertToNinjaPath(targetOutputReal); } else if (gt.IsFrameworkOnApple()) { // Create the library framework. - this->OSXBundleGenerator->CreateFramework(this->TargetNameOut, + this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output, gt.GetDirectory(cfgName)); } else if (gt.IsCFBundleOnApple()) { // Create the core foundation bundle. - this->OSXBundleGenerator->CreateCFBundle(this->TargetNameOut, + this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output, gt.GetDirectory(cfgName)); } @@ -803,6 +802,34 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() cmNinjaDeps outputs; outputs.push_back(targetOutputReal); + if (this->TargetLinkLanguage == "Swift") { + if (const char* name = gt.GetProperty("SWIFT_MODULE_NAME")) { + vars["TARGET_SWIFT_DOC"] = std::string(name) + ".swiftdoc"; + vars["TARGET_SWIFT_MODULE"] = std::string(name) + ".swiftmodule"; + } else { + vars["TARGET_SWIFT_DOC"] = gt.GetName() + ".swiftdoc"; + vars["TARGET_SWIFT_MODULE"] = gt.GetName() + ".swiftmodule"; + } + outputs.push_back(vars["TARGET_SWIFT_DOC"]); + outputs.push_back(vars["TARGET_SWIFT_MODULE"]); + + cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator(); + + std::string partials; + std::vector<cmSourceFile const*> sources; + gt.GetObjectSources(sources, this->GetConfigName()); + for (cmSourceFile const* source : sources) { + partials += " "; + if (const char* partial = source->GetProperty("SWIFT_PARTIAL_MODULE")) { + partials += partial; + } else { + partials += localGen.GetTargetDirectory(>) + "/" + + gt.GetObjectName(source) + ".swiftmodule"; + } + } + vars["SWIFT_PARTIAL_MODULES"] = partials; + } + // Compute specific libraries to link with. cmNinjaDeps explicitDeps = this->GetObjects(); cmNinjaDeps implicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage); @@ -815,10 +842,9 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() std::string frameworkPath; std::string linkPath; - cmGeneratorTarget& genTarget = *this->GetGeneratorTarget(); - std::string createRule = genTarget.GetCreateRuleVariable( - this->TargetLinkLanguage, this->GetConfigName()); + std::string createRule = + gt.GetCreateRuleVariable(this->TargetLinkLanguage, this->GetConfigName()); bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE"); cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator(); @@ -831,9 +857,9 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() this->GetLocalGenerator()->GetStateSnapshot().GetDirectory())); linkLineComputer->SetUseWatcomQuote(useWatcomQuote); - localGen.GetTargetFlags( - linkLineComputer.get(), this->GetConfigName(), vars["LINK_LIBRARIES"], - vars["FLAGS"], vars["LINK_FLAGS"], frameworkPath, linkPath, &genTarget); + localGen.GetTargetFlags(linkLineComputer.get(), this->GetConfigName(), + vars["LINK_LIBRARIES"], vars["FLAGS"], + vars["LINK_FLAGS"], frameworkPath, linkPath, >); // Add OS X version flags, if any. if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY || @@ -854,7 +880,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() vars["LINK_PATH"] = frameworkPath + linkPath; std::string lwyuFlags; - if (genTarget.GetPropertyAsBool("LINK_WHAT_YOU_USE")) { + if (gt.GetPropertyAsBool("LINK_WHAT_YOU_USE")) { lwyuFlags = " -Wl,--no-as-needed"; } @@ -863,22 +889,21 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() // code between the Makefile executable and library generators. if (targetType == cmStateEnums::EXECUTABLE) { std::string t = vars["FLAGS"]; - localGen.AddArchitectureFlags(t, &genTarget, TargetLinkLanguage, cfgName); + localGen.AddArchitectureFlags(t, >, TargetLinkLanguage, cfgName); t += lwyuFlags; vars["FLAGS"] = t; } else { std::string t = vars["ARCH_FLAGS"]; - localGen.AddArchitectureFlags(t, &genTarget, TargetLinkLanguage, cfgName); + localGen.AddArchitectureFlags(t, >, TargetLinkLanguage, cfgName); vars["ARCH_FLAGS"] = t; t.clear(); t += lwyuFlags; - localGen.AddLanguageFlagsForLinking(t, &genTarget, TargetLinkLanguage, - cfgName); + localGen.AddLanguageFlagsForLinking(t, >, TargetLinkLanguage, cfgName); vars["LANGUAGE_COMPILE_FLAGS"] = t; } if (this->GetGeneratorTarget()->HasSOName(cfgName)) { vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage); - vars["SONAME"] = this->TargetNameSO; + vars["SONAME"] = this->TargetNames.SharedObject; if (targetType == cmStateEnums::SHARED_LIBRARY) { std::string install_dir = this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(cfgName); @@ -891,12 +916,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() cmNinjaDeps byproducts; - if (!this->TargetNameImport.empty()) { + if (!this->TargetNames.ImportLibrary.empty()) { const std::string impLibPath = localGen.ConvertToOutputFormat( targetOutputImplib, cmOutputConverter::SHELL); vars["TARGET_IMPLIB"] = impLibPath; EnsureParentDirectoryExists(impLibPath); - if (genTarget.HasImportLibrary(cfgName)) { + if (gt.HasImportLibrary(cfgName)) { byproducts.push_back(targetOutputImplib); } } @@ -1051,8 +1076,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() emptyDeps, emptyDeps, symlinkVars); } else { cmNinjaDeps symlinks; - std::string const soName = - this->ConvertToNinjaPath(this->GetTargetFilePath(this->TargetNameSO)); + std::string const soName = this->ConvertToNinjaPath( + this->GetTargetFilePath(this->TargetNames.SharedObject)); // If one link has to be created. if (targetOutputReal == soName || targetOutput == soName) { symlinkVars["SONAME"] = soName; @@ -1070,7 +1095,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement() } // Add aliases for the file name and the target name. - globalGen.AddTargetAlias(this->TargetNameOut, >); + globalGen.AddTargetAlias(this->TargetNames.Output, >); globalGen.AddTargetAlias(this->GetTargetName(), >); } diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h index 01cc881..14991a2 100644 --- a/Source/cmNinjaNormalTargetGenerator.h +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -5,13 +5,12 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include "cmGeneratorTarget.h" #include "cmNinjaTargetGenerator.h" #include <string> #include <vector> -class cmGeneratorTarget; - class cmNinjaNormalTargetGenerator : public cmNinjaTargetGenerator { public: @@ -40,11 +39,7 @@ private: private: // Target name info. - std::string TargetNameOut; - std::string TargetNameSO; - std::string TargetNameReal; - std::string TargetNameImport; - std::string TargetNamePDB; + cmGeneratorTarget::Names TargetNames; std::string TargetLinkLanguage; std::string DeviceLinkObject; }; diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 82bc5f2..2324839 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -59,13 +59,10 @@ cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmGeneratorTarget* target) , LocalGenerator( static_cast<cmLocalNinjaGenerator*>(target->GetLocalGenerator())) { - MacOSXContentGenerator = new MacOSXContentGeneratorType(this); + MacOSXContentGenerator = cm::make_unique<MacOSXContentGeneratorType>(this); } -cmNinjaTargetGenerator::~cmNinjaTargetGenerator() -{ - delete this->MacOSXContentGenerator; -} +cmNinjaTargetGenerator::~cmNinjaTargetGenerator() = default; cmGeneratedFileStream& cmNinjaTargetGenerator::GetBuildFileStream() const { @@ -102,6 +99,12 @@ bool cmNinjaTargetGenerator::NeedExplicitPreprocessing( return lang == "Fortran"; } +bool cmNinjaTargetGenerator::UsePreprocessedSource( + std::string const& lang) const +{ + return lang == "Fortran"; +} + std::string cmNinjaTargetGenerator::LanguageDyndepRule( const std::string& lang) const { @@ -454,6 +457,9 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) if (lang == "Swift") { vars.SwiftAuxiliarySources = "$SWIFT_AUXILIARY_SOURCES"; vars.SwiftModuleName = "$SWIFT_MODULE_NAME"; + vars.SwiftLibraryName = "$SWIFT_LIBRARY_NAME"; + vars.SwiftPartialModule = "$SWIFT_PARTIAL_MODULE"; + vars.SwiftPartialDoc = "$SWIFT_PARTIAL_DOC"; } // For some cases we do an explicit preprocessor invocation. @@ -493,7 +499,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) if (explicitPP) { // Lookup the explicit preprocessing rule. std::string const ppVar = "CMAKE_" + lang + "_PREPROCESS_SOURCE"; - std::string const ppCmd = + std::string const& ppCmd = this->GetMakefile()->GetRequiredDefinition(ppVar); // Explicit preprocessing always uses a depfile. @@ -555,7 +561,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) cmake + " -E cmake_ninja_depends" " --tdi=" + - tdi + + tdi + " --lang=" + lang + " --pp=$out" " --dep=$DEP_FILE" + (needDyndep ? " --obj=$OBJ_FILE --ddi=$DYNDEP_INTERMEDIATE_FILE" : "")); @@ -585,13 +591,14 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) std::string ddRspContent = "$in"; std::string ddInput = "@" + ddRspFile; - // Run CMake dependency scanner on preprocessed output. + // Run CMake dependency scanner on the source file (using the preprocessed + // source if that was performed). std::string const cmake = this->GetLocalGenerator()->ConvertToOutputFormat( cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL); ddCmds.push_back(cmake + " -E cmake_ninja_dyndep" " --tdi=" + - tdi + + tdi + " --lang=" + lang + " --dd=$out" " " + ddInput); @@ -671,19 +678,18 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang) std::string cmdVar; if (this->GeneratorTarget->GetPropertyAsBool( "CUDA_SEPARABLE_COMPILATION")) { - cmdVar = std::string("CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION"); + cmdVar = "CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION"; } else if (this->GeneratorTarget->GetPropertyAsBool( "CUDA_PTX_COMPILATION")) { - cmdVar = std::string("CMAKE_CUDA_COMPILE_PTX_COMPILATION"); + cmdVar = "CMAKE_CUDA_COMPILE_PTX_COMPILATION"; } else { - cmdVar = std::string("CMAKE_CUDA_COMPILE_WHOLE_COMPILATION"); + cmdVar = "CMAKE_CUDA_COMPILE_WHOLE_COMPILATION"; } - std::string compileCmd = mf->GetRequiredDefinition(cmdVar); + const std::string& compileCmd = mf->GetRequiredDefinition(cmdVar); cmSystemTools::ExpandListArgument(compileCmd, compileCmds); } else { - const std::string cmdVar = - std::string("CMAKE_") + lang + "_COMPILE_OBJECT"; - std::string compileCmd = mf->GetRequiredDefinition(cmdVar); + const std::string cmdVar = "CMAKE_" + lang + "_COMPILE_OBJECT"; + const std::string& compileCmd = mf->GetRequiredDefinition(cmdVar); cmSystemTools::ExpandListArgument(compileCmd, compileCmds); } @@ -804,11 +810,11 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements() std::vector<cmSourceFile const*> headerSources; this->GeneratorTarget->GetHeaderSources(headerSources, config); this->OSXBundleGenerator->GenerateMacOSXContentStatements( - headerSources, this->MacOSXContentGenerator); + headerSources, this->MacOSXContentGenerator.get()); std::vector<cmSourceFile const*> extraSources; this->GeneratorTarget->GetExtraSources(extraSources, config); this->OSXBundleGenerator->GenerateMacOSXContentStatements( - extraSources, this->MacOSXContentGenerator); + extraSources, this->MacOSXContentGenerator.get()); std::vector<cmSourceFile const*> externalObjects; this->GeneratorTarget->GetExternalObjects(externalObjects, config); for (cmSourceFile const* sf : externalObjects) { @@ -866,24 +872,27 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements() this->WriteObjectBuildStatement(sf); } - if (!this->DDIFiles.empty()) { + for (auto const& langDDIFiles : this->DDIFiles) { + std::string const& language = langDDIFiles.first; + cmNinjaDeps const& ddiFiles = langDDIFiles.second; + std::string const ddComment; - std::string const ddRule = this->LanguageDyndepRule("Fortran"); + std::string const ddRule = this->LanguageDyndepRule(language); cmNinjaDeps ddOutputs; cmNinjaDeps ddImplicitOuts; - cmNinjaDeps const& ddExplicitDeps = this->DDIFiles; + cmNinjaDeps const& ddExplicitDeps = ddiFiles; cmNinjaDeps ddImplicitDeps; cmNinjaDeps ddOrderOnlyDeps; cmNinjaVars ddVars; - this->WriteTargetDependInfo("Fortran"); + this->WriteTargetDependInfo(language); - ddOutputs.push_back(this->GetDyndepFilePath("Fortran")); + ddOutputs.push_back(this->GetDyndepFilePath(language)); // Make sure dyndep files for all our dependencies have already - // been generated so that the 'FortranModules.json' files they + // been generated so that the '<LANG>Modules.json' files they // produced as side-effects are available for us to read. - // Ideally we should depend on the 'FortranModules.json' files + // Ideally we should depend on the '<LANG>Modules.json' files // from our dependencies directly, but we don't know which of // our dependencies produces them. Fixing this will require // refactoring the Ninja generator to generate targets in @@ -941,6 +950,22 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } else { vars["SWIFT_MODULE_NAME"] = this->GeneratorTarget->GetName(); } + + cmGeneratorTarget::Names targetNames = + this->GeneratorTarget->GetLibraryNames(this->GetConfigName()); + vars["SWIFT_LIBRARY_NAME"] = targetNames.Base; + + if (const char* partial = source->GetProperty("SWIFT_PARTIAL_MODULE")) { + vars["SWIFT_PARTIAL_MODULE"] = partial; + } else { + vars["SWIFT_PARTIAL_MODULE"] = objectFileName + ".swiftmodule"; + } + + if (const char* partial = source->GetProperty("SWIFT_PARTIAL_DOC")) { + vars["SWIFT_PARTIAL_DOC"] = partial; + } else { + vars["SWIFT_PARTIAL_DOC"] = objectFileName + ".swiftdoc"; + } } if (!this->NeedDepTypeMSVC(language)) { @@ -1014,6 +1039,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( // For some cases we do an explicit preprocessor invocation. bool const explicitPP = this->NeedExplicitPreprocessing(language); if (explicitPP) { + bool const compilePP = this->UsePreprocessedSource(language); std::string const ppComment; std::string const ppRule = this->LanguagePreprocessRule(language); cmNinjaDeps ppOutputs; @@ -1027,61 +1053,80 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( this->ConvertToNinjaPath(this->GetPreprocessedFilePath(source)); ppOutputs.push_back(ppFileName); - // Move compilation dependencies to the preprocessing build statement. - std::swap(ppExplicitDeps, explicitDeps); - std::swap(ppImplicitDeps, implicitDeps); - std::swap(ppOrderOnlyDeps, orderOnlyDeps); - std::swap(ppVars["IN_ABS"], vars["IN_ABS"]); + if (compilePP) { + // Move compilation dependencies to the preprocessing build statement. + std::swap(ppExplicitDeps, explicitDeps); + std::swap(ppImplicitDeps, implicitDeps); + std::swap(ppOrderOnlyDeps, orderOnlyDeps); + std::swap(ppVars["IN_ABS"], vars["IN_ABS"]); - // The actual compilation will now use the preprocessed source. - explicitDeps.push_back(ppFileName); + // The actual compilation will now use the preprocessed source. + explicitDeps.push_back(ppFileName); + } else { + // Copy compilation dependencies to the preprocessing build statement. + ppExplicitDeps = explicitDeps; + ppImplicitDeps = implicitDeps; + ppOrderOnlyDeps = orderOnlyDeps; + ppVars["IN_ABS"] = vars["IN_ABS"]; + } // Preprocessing and compilation generally use the same flags. ppVars["FLAGS"] = vars["FLAGS"]; - // In case compilation requires flags that are incompatible with - // preprocessing, include them here. - std::string const postFlag = - this->Makefile->GetSafeDefinition("CMAKE_Fortran_POSTPROCESS_FLAG"); - this->LocalGenerator->AppendFlags(vars["FLAGS"], postFlag); + if (compilePP) { + // In case compilation requires flags that are incompatible with + // preprocessing, include them here. + std::string const postFlag = this->Makefile->GetSafeDefinition( + "CMAKE_" + language + "_POSTPROCESS_FLAG"); + this->LocalGenerator->AppendFlags(vars["FLAGS"], postFlag); + } - // Move preprocessor definitions to the preprocessor build statement. - std::swap(ppVars["DEFINES"], vars["DEFINES"]); + if (compilePP) { + // Move preprocessor definitions to the preprocessor build statement. + std::swap(ppVars["DEFINES"], vars["DEFINES"]); + } else { + // Copy preprocessor definitions to the preprocessor build statement. + ppVars["DEFINES"] = vars["DEFINES"]; + } // Copy include directories to the preprocessor build statement. The // Fortran compilation build statement still needs them for the INCLUDE // directive. ppVars["INCLUDES"] = vars["INCLUDES"]; - // Prepend source file's original directory as an include directory - // so e.g. Fortran INCLUDE statements can look for files in it. - std::vector<std::string> sourceDirectory; - sourceDirectory.push_back( - cmSystemTools::GetParentDirectory(source->GetFullPath())); + if (compilePP) { + // Prepend source file's original directory as an include directory + // so e.g. Fortran INCLUDE statements can look for files in it. + std::vector<std::string> sourceDirectory; + sourceDirectory.push_back( + cmSystemTools::GetParentDirectory(source->GetFullPath())); - std::string sourceDirectoryFlag = this->LocalGenerator->GetIncludeFlags( - sourceDirectory, this->GeneratorTarget, language, false, false, - this->GetConfigName()); + std::string sourceDirectoryFlag = this->LocalGenerator->GetIncludeFlags( + sourceDirectory, this->GeneratorTarget, language, false, false, + this->GetConfigName()); - vars["INCLUDES"] = sourceDirectoryFlag + " " + vars["INCLUDES"]; + vars["INCLUDES"] = sourceDirectoryFlag + " " + vars["INCLUDES"]; + } // Explicit preprocessing always uses a depfile. ppVars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat( - ppFileName + ".d", cmOutputConverter::SHELL); - // The actual compilation does not need a depfile because it - // depends on the already-preprocessed source. - vars.erase("DEP_FILE"); + objectFileName + ".pp.d", cmOutputConverter::SHELL); + if (compilePP) { + // The actual compilation does not need a depfile because it + // depends on the already-preprocessed source. + vars.erase("DEP_FILE"); + } if (needDyndep) { // Tell dependency scanner the object file that will result from - // compiling the preprocessed source. + // compiling the source. ppVars["OBJ_FILE"] = objectFileName; // Tell dependency scanner where to store dyndep intermediate results. - std::string const ddiFile = ppFileName + ".ddi"; + std::string const ddiFile = objectFileName + ".ddi"; ppVars["DYNDEP_INTERMEDIATE_FILE"] = ddiFile; ppImplicitOuts.push_back(ddiFile); - this->DDIFiles.push_back(ddiFile); + this->DDIFiles[language].push_back(ddiFile); } this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(), @@ -1217,20 +1262,19 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand( std::string cmdVar; if (this->GeneratorTarget->GetPropertyAsBool( "CUDA_SEPARABLE_COMPILATION")) { - cmdVar = std::string("CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION"); + cmdVar = "CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION"; } else if (this->GeneratorTarget->GetPropertyAsBool( "CUDA_PTX_COMPILATION")) { - cmdVar = std::string("CMAKE_CUDA_COMPILE_PTX_COMPILATION"); + cmdVar = "CMAKE_CUDA_COMPILE_PTX_COMPILATION"; } else { - cmdVar = std::string("CMAKE_CUDA_COMPILE_WHOLE_COMPILATION"); + cmdVar = "CMAKE_CUDA_COMPILE_WHOLE_COMPILATION"; } - std::string compileCmd = + const std::string& compileCmd = this->GetMakefile()->GetRequiredDefinition(cmdVar); cmSystemTools::ExpandListArgument(compileCmd, compileCmds); } else { - const std::string cmdVar = - std::string("CMAKE_") + language + "_COMPILE_OBJECT"; - std::string compileCmd = + const std::string cmdVar = "CMAKE_" + language + "_COMPILE_OBJECT"; + const std::string& compileCmd = this->GetMakefile()->GetRequiredDefinition(cmdVar); cmSystemTools::ExpandListArgument(compileCmd, compileCmds); } diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 373c693..51c9ac7 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -10,6 +10,7 @@ #include "cmNinjaTypes.h" #include "cmOSXBundleGenerator.h" +#include <map> #include <set> #include <string> #include <vector> @@ -64,6 +65,7 @@ protected: bool NeedExplicitPreprocessing(std::string const& lang) const; std::string LanguageDyndepRule(std::string const& lang) const; bool NeedDyndep(std::string const& lang) const; + bool UsePreprocessedSource(std::string const& lang) const; std::string OrderDependsTargetForTarget(); @@ -151,9 +153,9 @@ protected: }; friend struct MacOSXContentGeneratorType; - MacOSXContentGeneratorType* MacOSXContentGenerator; + std::unique_ptr<MacOSXContentGeneratorType> MacOSXContentGenerator; // Properly initialized by sub-classes. - cmOSXBundleGenerator* OSXBundleGenerator; + std::unique_ptr<cmOSXBundleGenerator> OSXBundleGenerator; std::set<std::string> MacContentFolders; void addPoolNinjaVariable(const std::string& pool_property, @@ -165,7 +167,7 @@ private: cmLocalNinjaGenerator* LocalGenerator; /// List of object files for this target. cmNinjaDeps Objects; - cmNinjaDeps DDIFiles; // TODO: Make per-language. + std::map<std::string, cmNinjaDeps> DDIFiles; std::vector<cmCustomCommand const*> CustomCommands; cmNinjaDeps ExtraFiles; }; diff --git a/Source/cmOSXBundleGenerator.cxx b/Source/cmOSXBundleGenerator.cxx index 6857d5a..47a8df4 100644 --- a/Source/cmOSXBundleGenerator.cxx +++ b/Source/cmOSXBundleGenerator.cxx @@ -54,8 +54,7 @@ void cmOSXBundleGenerator::CreateAppBundle(const std::string& targetName, plist += this->GT->GetAppBundleDirectory(this->ConfigName, cmGeneratorTarget::ContentLevel); plist += "/Info.plist"; - this->LocalGenerator->GenerateAppleInfoPList(this->GT, targetName, - plist.c_str()); + this->LocalGenerator->GenerateAppleInfoPList(this->GT, targetName, plist); this->Makefile->AddCMakeOutputFile(plist); outpath = out; } @@ -90,8 +89,7 @@ void cmOSXBundleGenerator::CreateFramework(const std::string& targetName, } plist += "/Info.plist"; std::string name = cmSystemTools::GetFilenameName(targetName); - this->LocalGenerator->GenerateFrameworkInfoPList(this->GT, name, - plist.c_str()); + this->LocalGenerator->GenerateFrameworkInfoPList(this->GT, name, plist); // Generate Versions directory only for MacOSX frameworks if (this->Makefile->PlatformIsAppleEmbedded()) { @@ -184,7 +182,7 @@ void cmOSXBundleGenerator::CreateCFBundle(const std::string& targetName, cmGeneratorTarget::ContentLevel); plist += "/Info.plist"; std::string name = cmSystemTools::GetFilenameName(targetName); - this->LocalGenerator->GenerateAppleInfoPList(this->GT, name, plist.c_str()); + this->LocalGenerator->GenerateAppleInfoPList(this->GT, name, plist); this->Makefile->AddCMakeOutputFile(plist); } diff --git a/Source/cmOrderDirectories.h b/Source/cmOrderDirectories.h index 5916f7a..23e61d6 100644 --- a/Source/cmOrderDirectories.h +++ b/Source/cmOrderDirectories.h @@ -25,6 +25,8 @@ public: cmOrderDirectories(cmGlobalGenerator* gg, cmGeneratorTarget const* target, const char* purpose); ~cmOrderDirectories(); + cmOrderDirectories(const cmOrderDirectories&) = delete; + cmOrderDirectories& operator=(const cmOrderDirectories&) = delete; void AddRuntimeLibrary(std::string const& fullPath, const char* soname = nullptr); void AddLinkLibrary(std::string const& fullPath); diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h index 6438c7b..deca767 100644 --- a/Source/cmOutputConverter.h +++ b/Source/cmOutputConverter.h @@ -27,7 +27,7 @@ public: std::string ConvertDirectorySeparatorsForShell( const std::string& source) const; - ///! for existing files convert to output path and short path if spaces + //! for existing files convert to output path and short path if spaces std::string ConvertToOutputForExisting(const std::string& remote, OutputFormat format = SHELL) const; diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx index 46d04a6..cb9433f 100644 --- a/Source/cmOutputRequiredFilesCommand.cxx +++ b/Source/cmOutputRequiredFilesCommand.cxx @@ -92,6 +92,9 @@ public: */ ~cmLBDepend() { cmDeleteAll(this->DependInformationMap); } + cmLBDepend(const cmLBDepend&) = delete; + cmLBDepend& operator=(const cmLBDepend&) = delete; + /** * Set the makefile that is used as a source of classes. */ @@ -163,7 +166,7 @@ protected: { cmsys::ifstream fin(info->FullPath.c_str()); if (!fin) { - cmSystemTools::Error("error can not open ", info->FullPath.c_str()); + cmSystemTools::Error("error can not open " + info->FullPath); return; } @@ -178,7 +181,7 @@ protected: qstart = line.find('<', 8); // if a < is not found then move on if (qstart == std::string::npos) { - cmSystemTools::Error("unknown include directive ", line.c_str()); + cmSystemTools::Error("unknown include directive " + line); continue; } qend = line.find('>', qstart + 1); diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx index 796974c..5213432 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx @@ -8,13 +8,16 @@ #include <utility> #include "cmAlgorithms.h" +#include "cmArgumentParser.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" #include "cmSystemTools.h" +#include "cm_string_view.hxx" class cmExecutionStatus; -static std::string escape_arg(const std::string& arg) +static std::string EscapeArg(const std::string& arg) { // replace ";" with "\;" so output argument lists will split correctly std::string escapedArg; @@ -27,6 +30,82 @@ static std::string escape_arg(const std::string& arg) return escapedArg; } +static std::string JoinList(std::vector<std::string> const& arg, bool escape) +{ + return escape ? cmJoin(cmMakeRange(arg).transform(EscapeArg), ";") + : cmJoin(cmMakeRange(arg), ";"); +} + +namespace { + +typedef std::map<std::string, bool> options_map; +typedef std::map<std::string, std::string> single_map; +typedef std::map<std::string, std::vector<std::string>> multi_map; +typedef std::set<std::string> options_set; + +struct UserArgumentParser : public cmArgumentParser<void> +{ + template <typename T, typename H> + void Bind(std::vector<std::string> const& names, + std::map<std::string, T>& ref, H duplicateKey) + { + for (std::string const& key : names) { + auto const it = ref.emplace(key, T{}).first; + bool const inserted = this->cmArgumentParser<void>::Bind( + cm::string_view(it->first), it->second); + if (!inserted) { + duplicateKey(key); + } + } + } +}; + +} // namespace + +static void PassParsedArguments( + const std::string& prefix, cmMakefile& makefile, const options_map& options, + const single_map& singleValArgs, const multi_map& multiValArgs, + const std::vector<std::string>& unparsed, + const options_set& keywordsMissingValues, bool parseFromArgV) +{ + for (auto const& iter : options) { + makefile.AddDefinition(prefix + iter.first, + iter.second ? "TRUE" : "FALSE"); + } + + for (auto const& iter : singleValArgs) { + if (!iter.second.empty()) { + makefile.AddDefinition(prefix + iter.first, iter.second.c_str()); + } else { + makefile.RemoveDefinition(prefix + iter.first); + } + } + + for (auto const& iter : multiValArgs) { + if (!iter.second.empty()) { + makefile.AddDefinition(prefix + iter.first, + JoinList(iter.second, parseFromArgV).c_str()); + } else { + makefile.RemoveDefinition(prefix + iter.first); + } + } + + if (!unparsed.empty()) { + makefile.AddDefinition(prefix + "UNPARSED_ARGUMENTS", + JoinList(unparsed, parseFromArgV).c_str()); + } else { + makefile.RemoveDefinition(prefix + "UNPARSED_ARGUMENTS"); + } + + if (!keywordsMissingValues.empty()) { + makefile.AddDefinition( + prefix + "KEYWORDS_MISSING_VALUES", + cmJoin(cmMakeRange(keywordsMissingValues), ";").c_str()); + } else { + makefile.RemoveDefinition(prefix + "KEYWORDS_MISSING_VALUES"); + } +} + bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args, cmExecutionStatus&) { @@ -65,11 +144,10 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args, // the first argument is the prefix const std::string prefix = (*argIter++) + "_"; + UserArgumentParser parser; + // define the result maps holding key/value pairs for // options, single values and multi values - typedef std::map<std::string, bool> options_map; - typedef std::map<std::string, std::string> single_map; - typedef std::map<std::string, std::vector<std::string>> multi_map; options_map options; single_map singleValArgs; multi_map multiValArgs; @@ -77,50 +155,25 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args, // anything else is put into a vector of unparsed strings std::vector<std::string> unparsed; - // remember already defined keywords - std::set<std::string> used_keywords; - const std::string dup_warning = "keyword defined more than once: "; + auto const duplicateKey = [this](std::string const& key) { + this->GetMakefile()->IssueMessage( + MessageType::WARNING, "keyword defined more than once: " + key); + }; // the second argument is a (cmake) list of options without argument std::vector<std::string> list; cmSystemTools::ExpandListArgument(*argIter++, list); - for (std::string const& iter : list) { - if (!used_keywords.insert(iter).second) { - this->GetMakefile()->IssueMessage(MessageType::WARNING, - dup_warning + iter); - } - options[iter]; // default initialize - } + parser.Bind(list, options, duplicateKey); // the third argument is a (cmake) list of single argument options list.clear(); cmSystemTools::ExpandListArgument(*argIter++, list); - for (std::string const& iter : list) { - if (!used_keywords.insert(iter).second) { - this->GetMakefile()->IssueMessage(MessageType::WARNING, - dup_warning + iter); - } - singleValArgs[iter]; // default initialize - } + parser.Bind(list, singleValArgs, duplicateKey); // the fourth argument is a (cmake) list of multi argument options list.clear(); cmSystemTools::ExpandListArgument(*argIter++, list); - for (std::string const& iter : list) { - if (!used_keywords.insert(iter).second) { - this->GetMakefile()->IssueMessage(MessageType::WARNING, - dup_warning + iter); - } - multiValArgs[iter]; // default initialize - } - - enum insideValues - { - NONE, - SINGLE, - MULTI - } insideValues = NONE; - std::string currentArgName; + parser.Bind(list, multiValArgs, duplicateKey); list.clear(); if (!parseFromArgV) { @@ -155,81 +208,14 @@ bool cmParseArgumentsCommand::InitialPass(std::vector<std::string> const& args, } } - // iterate over the arguments list and fill in the values where applicable - for (std::string const& arg : list) { - const options_map::iterator optIter = options.find(arg); - if (optIter != options.end()) { - insideValues = NONE; - optIter->second = true; - continue; - } - - const single_map::iterator singleIter = singleValArgs.find(arg); - if (singleIter != singleValArgs.end()) { - insideValues = SINGLE; - currentArgName = arg; - continue; - } - - const multi_map::iterator multiIter = multiValArgs.find(arg); - if (multiIter != multiValArgs.end()) { - insideValues = MULTI; - currentArgName = arg; - continue; - } - - switch (insideValues) { - case SINGLE: - singleValArgs[currentArgName] = arg; - insideValues = NONE; - break; - case MULTI: - if (parseFromArgV) { - multiValArgs[currentArgName].push_back(escape_arg(arg)); - } else { - multiValArgs[currentArgName].push_back(arg); - } - break; - default: - if (parseFromArgV) { - unparsed.push_back(escape_arg(arg)); - } else { - unparsed.push_back(arg); - } - break; - } - } + std::vector<std::string> keywordsMissingValues; - // now iterate over the collected values and update their definition - // within the current scope. undefine if necessary. + parser.Parse(list, &unparsed, &keywordsMissingValues); - for (auto const& iter : options) { - this->Makefile->AddDefinition(prefix + iter.first, - iter.second ? "TRUE" : "FALSE"); - } - for (auto const& iter : singleValArgs) { - if (!iter.second.empty()) { - this->Makefile->AddDefinition(prefix + iter.first, iter.second.c_str()); - } else { - this->Makefile->RemoveDefinition(prefix + iter.first); - } - } - - for (auto const& iter : multiValArgs) { - if (!iter.second.empty()) { - this->Makefile->AddDefinition( - prefix + iter.first, cmJoin(cmMakeRange(iter.second), ";").c_str()); - } else { - this->Makefile->RemoveDefinition(prefix + iter.first); - } - } - - if (!unparsed.empty()) { - this->Makefile->AddDefinition(prefix + "UNPARSED_ARGUMENTS", - cmJoin(cmMakeRange(unparsed), ";").c_str()); - } else { - this->Makefile->RemoveDefinition(prefix + "UNPARSED_ARGUMENTS"); - } + PassParsedArguments( + prefix, *this->Makefile, options, singleValArgs, multiValArgs, unparsed, + options_set(keywordsMissingValues.begin(), keywordsMissingValues.end()), + parseFromArgV); return true; } diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx index 0a234c5..ec40136 100644 --- a/Source/cmPolicies.cxx +++ b/Source/cmPolicies.cxx @@ -145,7 +145,7 @@ static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy, } else { std::ostringstream e; e << defaultVar << " has value \"" << defaultValue - << "\" but must be \"OLD\", \"NEW\", or \"\" (empty)."; + << R"(" but must be "OLD", "NEW", or "" (empty).)"; mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); return false; } @@ -299,7 +299,7 @@ bool cmPolicies::GetPolicyID(const char* id, cmPolicies::PolicyID& pid) return stringToId(id, pid); } -///! return a warning string for a given policy +//! return a warning string for a given policy std::string cmPolicies::GetPolicyWarning(cmPolicies::PolicyID id) { std::ostringstream msg; @@ -333,7 +333,7 @@ std::string cmPolicies::GetPolicyDeprecatedWarning(cmPolicies::PolicyID id) return msg.str(); } -///! return an error string for when a required policy is unspecified +//! return an error string for when a required policy is unspecified std::string cmPolicies::GetRequiredPolicyError(cmPolicies::PolicyID id) { std::ostringstream error; @@ -359,7 +359,7 @@ std::string cmPolicies::GetRequiredPolicyError(cmPolicies::PolicyID id) return error.str(); } -///! Get the default status for a policy +//! Get the default status for a policy cmPolicies::PolicyStatus cmPolicies::GetPolicyStatus( cmPolicies::PolicyID /*unused*/) { diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 314f27d..0e42295 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -261,7 +261,19 @@ class cmMakefile; 3, 14, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0088, \ "FindBISON runs bison in CMAKE_CURRENT_BINARY_DIR when executing.", \ - 3, 14, 0, cmPolicies::WARN) + 3, 14, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0089, \ + "Compiler id for IBM Clang-based XL compilers is now XLClang.", 3, \ + 15, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0090, \ + "export(PACKAGE) does not populate package registry by default.", 3, \ + 15, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0091, \ + "MSVC runtime library flags are selected by an abstraction.", 3, 15, \ + 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0092, \ + "MSVC warning flags are not in CMAKE_<LANG>_FLAGS by default.", 3, \ + 15, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -326,27 +338,27 @@ public: CMPCOUNT }; - ///! convert a string policy ID into a number + //! convert a string policy ID into a number static bool GetPolicyID(const char* id, /* out */ cmPolicies::PolicyID& pid); - ///! Get the default status for a policy + //! Get the default status for a policy static cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id); - ///! Set a policy level for this listfile + //! Set a policy level for this listfile static bool ApplyPolicyVersion(cmMakefile* mf, std::string const& version_min, std::string const& version_max); static bool ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer, unsigned int minorVer, unsigned int patchVer); - ///! return a warning string for a given policy + //! return a warning string for a given policy static std::string GetPolicyWarning(cmPolicies::PolicyID id); static std::string GetPolicyDeprecatedWarning(cmPolicies::PolicyID id); - ///! return an error string for when a required policy is unspecified + //! return an error string for when a required policy is unspecified static std::string GetRequiredPolicyError(cmPolicies::PolicyID id); - ///! return an error string for when a required policy is unspecified + //! return an error string for when a required policy is unspecified static std::string GetRequiredAlwaysPolicyError(cmPolicies::PolicyID id); /** Represent a set of policy values. */ diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 2fe9fe8..8615ecc 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -25,6 +25,10 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, return false; } + if (!this->IncludeByVariable("CMAKE_PROJECT_INCLUDE_BEFORE")) { + return false; + } + std::string const& projectName = args[0]; this->Makefile->SetProjectName(projectName); @@ -214,7 +218,7 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, } cmsys::RegularExpression vx( - "^([0-9]+(\\.[0-9]+(\\.[0-9]+(\\.[0-9]+)?)?)?)?$"); + R"(^([0-9]+(\.[0-9]+(\.[0-9]+(\.[0-9]+)?)?)?)?$)"); if (!vx.find(version)) { std::string e = "VERSION \"" + version + "\" format invalid."; this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e); @@ -319,21 +323,41 @@ bool cmProjectCommand::InitialPass(std::vector<std::string> const& args, languages.emplace_back("CXX"); } this->Makefile->EnableLanguage(languages, false); - std::string extraInclude = "CMAKE_PROJECT_" + projectName + "_INCLUDE"; - const char* include = this->Makefile->GetDefinition(extraInclude); - if (include) { - bool readit = this->Makefile->ReadDependentFile(include); - if (!readit && !cmSystemTools::GetFatalErrorOccured()) { - std::string m = "could not find file:\n" - " "; - m += include; - this->SetError(m); - return false; - } + + if (!this->IncludeByVariable("CMAKE_PROJECT_INCLUDE")) { + return false; } + + if (!this->IncludeByVariable("CMAKE_PROJECT_" + projectName + "_INCLUDE")) { + return false; + } + return true; } +bool cmProjectCommand::IncludeByVariable(const std::string& variable) +{ + const char* include = this->Makefile->GetDefinition(variable); + if (!include) { + return true; + } + + const bool readit = this->Makefile->ReadDependentFile(include); + if (readit) { + return true; + } + + if (cmSystemTools::GetFatalErrorOccured()) { + return true; + } + + std::string m = "could not find file:\n" + " "; + m += include; + this->SetError(m); + return false; +} + void cmProjectCommand::TopLevelCMakeVarCondSet(const char* const name, const char* const value) { diff --git a/Source/cmProjectCommand.h b/Source/cmProjectCommand.h index 365d448..f1d03e7 100644 --- a/Source/cmProjectCommand.h +++ b/Source/cmProjectCommand.h @@ -36,6 +36,7 @@ public: cmExecutionStatus& status) override; private: + bool IncludeByVariable(const std::string& variable); void TopLevelCMakeVarCondSet(const char* name, const char* value); }; diff --git a/Source/cmQTWrapCPPCommand.cxx b/Source/cmQTWrapCPPCommand.cxx index 6acc7ef..9a764c6 100644 --- a/Source/cmQTWrapCPPCommand.cxx +++ b/Source/cmQTWrapCPPCommand.cxx @@ -4,6 +4,7 @@ #include "cmCustomCommandLines.h" #include "cmMakefile.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmSystemTools.h" @@ -29,13 +30,13 @@ bool cmQTWrapCPPCommand::InitialPass(std::vector<std::string> const& args, std::string sourceListValue = this->Makefile->GetSafeDefinition(sourceList); // Create a rule for all sources listed. - for (std::vector<std::string>::const_iterator j = (args.begin() + 2); - j != args.end(); ++j) { - cmSourceFile* curr = this->Makefile->GetSource(*j); + for (std::string const& arg : cmMakeRange(args).advance(2)) { + cmSourceFile* curr = this->Makefile->GetSource(arg); // if we should wrap the class if (!(curr && curr->GetPropertyAsBool("WRAP_EXCLUDE"))) { // Compute the name of the file to generate. - std::string srcName = cmSystemTools::GetFilenameWithoutLastExtension(*j); + std::string srcName = + cmSystemTools::GetFilenameWithoutLastExtension(arg); std::string newName = this->Makefile->GetCurrentBinaryDirectory(); newName += "/moc_"; newName += srcName; @@ -47,8 +48,8 @@ bool cmQTWrapCPPCommand::InitialPass(std::vector<std::string> const& args, // Compute the name of the header from which to generate the file. std::string hname; - if (cmSystemTools::FileIsFullPath(*j)) { - hname = *j; + if (cmSystemTools::FileIsFullPath(arg)) { + hname = arg; } else { if (curr && curr->GetIsGenerated()) { hname = this->Makefile->GetCurrentBinaryDirectory(); @@ -56,7 +57,7 @@ bool cmQTWrapCPPCommand::InitialPass(std::vector<std::string> const& args, hname = this->Makefile->GetCurrentSourceDirectory(); } hname += "/"; - hname += *j; + hname += arg; } // Append the generated source file to the list. diff --git a/Source/cmQTWrapUICommand.cxx b/Source/cmQTWrapUICommand.cxx index 43b1fb9..2223e2d 100644 --- a/Source/cmQTWrapUICommand.cxx +++ b/Source/cmQTWrapUICommand.cxx @@ -4,6 +4,7 @@ #include "cmCustomCommandLines.h" #include "cmMakefile.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmSystemTools.h" @@ -33,13 +34,13 @@ bool cmQTWrapUICommand::InitialPass(std::vector<std::string> const& args, std::string sourceListValue = this->Makefile->GetSafeDefinition(sourceList); // Create rules for all sources listed. - for (std::vector<std::string>::const_iterator j = (args.begin() + 3); - j != args.end(); ++j) { - cmSourceFile* curr = this->Makefile->GetSource(*j); + for (std::string const& arg : cmMakeRange(args).advance(3)) { + cmSourceFile* curr = this->Makefile->GetSource(arg); // if we should wrap the class if (!(curr && curr->GetPropertyAsBool("WRAP_EXCLUDE"))) { // Compute the name of the files to generate. - std::string srcName = cmSystemTools::GetFilenameWithoutLastExtension(*j); + std::string srcName = + cmSystemTools::GetFilenameWithoutLastExtension(arg); std::string hName = this->Makefile->GetCurrentBinaryDirectory(); hName += "/"; hName += srcName; @@ -55,8 +56,8 @@ bool cmQTWrapUICommand::InitialPass(std::vector<std::string> const& args, // Compute the name of the ui file from which to generate others. std::string uiName; - if (cmSystemTools::FileIsFullPath(*j)) { - uiName = *j; + if (cmSystemTools::FileIsFullPath(arg)) { + uiName = arg; } else { if (curr && curr->GetIsGenerated()) { uiName = this->Makefile->GetCurrentBinaryDirectory(); @@ -64,7 +65,7 @@ bool cmQTWrapUICommand::InitialPass(std::vector<std::string> const& args, uiName = this->Makefile->GetCurrentSourceDirectory(); } uiName += "/"; - uiName += *j; + uiName += arg; } // create the list of headers diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 653caf7..9918c35 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -1,23 +1,19 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGen.h" + #include "cmAlgorithms.h" +#include "cmDuration.h" +#include "cmProcessOutput.h" #include "cmSystemTools.h" - +#include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" #include <algorithm> -#include <iterator> +#include <array> #include <sstream> #include <utility> -// - Static variables - -std::string const genNameGen = "AutoGen"; -std::string const genNameMoc = "AutoMoc"; -std::string const genNameUic = "AutoUic"; -std::string const genNameRcc = "AutoRcc"; - // - Static functions /// @brief Merges newOpts into baseOpts @@ -77,27 +73,47 @@ void MergeOptions(std::vector<std::string>& baseOpts, // - Class definitions -std::string const cmQtAutoGen::ListSep = "<<<S>>>"; unsigned int const cmQtAutoGen::ParallelMax = 64; +std::string const cmQtAutoGen::ListSep = "<<<S>>>"; -std::string const& cmQtAutoGen::GeneratorName(GeneratorT type) +std::string const& cmQtAutoGen::GeneratorName(GenT genType) { - switch (type) { - case GeneratorT::GEN: - return genNameGen; - case GeneratorT::MOC: - return genNameMoc; - case GeneratorT::UIC: - return genNameUic; - case GeneratorT::RCC: - return genNameRcc; + static const std::string AutoGen("AutoGen"); + static const std::string AutoMoc("AutoMoc"); + static const std::string AutoUic("AutoUic"); + static const std::string AutoRcc("AutoRcc"); + + switch (genType) { + case GenT::GEN: + return AutoGen; + case GenT::MOC: + return AutoMoc; + case GenT::UIC: + return AutoUic; + case GenT::RCC: + return AutoRcc; } - return genNameGen; + return AutoGen; } -std::string cmQtAutoGen::GeneratorNameUpper(GeneratorT genType) +std::string const& cmQtAutoGen::GeneratorNameUpper(GenT genType) { - return cmSystemTools::UpperCase(cmQtAutoGen::GeneratorName(genType)); + static const std::string AUTOGEN("AUTOGEN"); + static const std::string AUTOMOC("AUTOMOC"); + static const std::string AUTOUIC("AUTOUIC"); + static const std::string AUTORCC("AUTORCC"); + + switch (genType) { + case GenT::GEN: + return AUTOGEN; + case GenT::MOC: + return AUTOMOC; + case GenT::UIC: + return AUTOUIC; + case GenT::RCC: + return AUTORCC; + } + return AUTOGEN; } std::string cmQtAutoGen::Tools(bool moc, bool uic, bool rcc) @@ -137,13 +153,21 @@ std::string cmQtAutoGen::Tools(bool moc, bool uic, bool rcc) std::string cmQtAutoGen::Quoted(std::string const& text) { - static const char* rep[18] = { "\\", "\\\\", "\"", "\\\"", "\a", "\\a", - "\b", "\\b", "\f", "\\f", "\n", "\\n", - "\r", "\\r", "\t", "\\t", "\v", "\\v" }; + const std::array<std::pair<const char*, const char*>, 9> replaces = { + { { "\\", "\\\\" }, + { "\"", "\\\"" }, + { "\a", "\\a" }, + { "\b", "\\b" }, + { "\f", "\\f" }, + { "\n", "\\n" }, + { "\r", "\\r" }, + { "\t", "\\t" }, + { "\v", "\\v" } } + }; std::string res = text; - for (const char* const* it = cm::cbegin(rep); it != cm::cend(rep); it += 2) { - cmSystemTools::ReplaceString(res, *it, *(it + 1)); + for (auto const& pair : replaces) { + cmSystemTools::ReplaceString(res, pair.first, pair.second); } res = '"' + res; res += '"'; @@ -216,8 +240,8 @@ void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts, MergeOptions(baseOpts, newOpts, valueOpts, isQt5); } -void cmQtAutoGen::RccListParseContent(std::string const& content, - std::vector<std::string>& files) +static void RccListParseContent(std::string const& content, + std::vector<std::string>& files) { cmsys::RegularExpression fileMatchRegex("(<file[^<]+)"); cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)"); @@ -234,10 +258,10 @@ void cmQtAutoGen::RccListParseContent(std::string const& content, } } -bool cmQtAutoGen::RccListParseOutput(std::string const& rccStdOut, - std::string const& rccStdErr, - std::vector<std::string>& files, - std::string& error) +static bool RccListParseOutput(std::string const& rccStdOut, + std::string const& rccStdErr, + std::vector<std::string>& files, + std::string& error) { // Lambda to strip CR characters auto StripCR = [](std::string& line) { @@ -284,11 +308,104 @@ bool cmQtAutoGen::RccListParseOutput(std::string const& rccStdOut, return true; } -void cmQtAutoGen::RccListConvertFullPath(std::string const& qrcFileDir, - std::vector<std::string>& files) +cmQtAutoGen::RccLister::RccLister() = default; + +cmQtAutoGen::RccLister::RccLister(std::string rccExecutable, + std::vector<std::string> listOptions) + : RccExcutable_(std::move(rccExecutable)) + , ListOptions_(std::move(listOptions)) { +} + +bool cmQtAutoGen::RccLister::list(std::string const& qrcFile, + std::vector<std::string>& files, + std::string& error, bool verbose) const +{ + error.clear(); + + if (!cmSystemTools::FileExists(qrcFile, true)) { + error = "The resource file "; + error += Quoted(qrcFile); + error += " does not exist."; + 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(qrcFile); + + if (!this->RccExcutable_.empty() && + cmSystemTools::FileExists(this->RccExcutable_, true) && + !this->ListOptions_.empty()) { + + bool result = false; + int retVal = 0; + std::string rccStdOut; + std::string rccStdErr; + { + std::vector<std::string> cmd; + cmd.emplace_back(this->RccExcutable_); + cmd.insert(cmd.end(), this->ListOptions_.begin(), + this->ListOptions_.end()); + cmd.emplace_back(cmSystemTools::GetFilenameName(qrcFile)); + + // Log command + if (verbose) { + std::string msg = "Running command:\n"; + msg += QuotedCommand(cmd); + msg += '\n'; + cmSystemTools::Stdout(msg); + } + + result = cmSystemTools::RunSingleCommand( + cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(), + cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto); + } + if (!result || retVal) { + error = "The rcc list process failed for "; + error += Quoted(qrcFile); + error += "\n"; + if (!rccStdOut.empty()) { + error += rccStdOut; + error += "\n"; + } + if (!rccStdErr.empty()) { + 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(qrcFile.c_str()); + if (ifs) { + std::ostringstream osst; + osst << ifs.rdbuf(); + qrcContents = osst.str(); + } else { + error = "The resource file "; + error += Quoted(qrcFile); + error += " is not readable\n"; + return false; + } + } + // Parse string content + RccListParseContent(qrcContents, files); + } + } + + // Convert relative paths to absolute paths for (std::string& entry : files) { - std::string tmp = cmSystemTools::CollapseCombinedPath(qrcFileDir, entry); - entry = std::move(tmp); + entry = cmSystemTools::CollapseFullPath(entry, fileDir); } + return true; } diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index 96d1946..3a346b5 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -14,20 +14,6 @@ class cmQtAutoGen { public: - /// @brief Nested lists separator - static std::string const ListSep; - /// @brief Maximum number of parallel threads/processes in a generator - static unsigned int const ParallelMax; - - /// @brief AutoGen generator type - enum class GeneratorT - { - GEN, // General - MOC, - UIC, - RCC - }; - /// @brief Integer version struct IntegerVersion { @@ -54,11 +40,25 @@ public: } }; + /// @brief AutoGen generator type + enum class GenT + { + GEN, // AUTOGEN + MOC, // AUTOMOC + UIC, // AUTOUIC + RCC // AUTORCC + }; + + /// @brief Nested lists separator + static std::string const ListSep; + /// @brief Maximum number of parallel threads/processes in a generator + static unsigned int const ParallelMax; + public: /// @brief Returns the generator name - static std::string const& GeneratorName(GeneratorT genType); + static std::string const& GeneratorName(GenT genType); /// @brief Returns the generator name in upper case - static std::string GeneratorNameUpper(GeneratorT genType); + static std::string const& GeneratorNameUpper(GenT genType); /// @brief Returns a string with the requested tool names static std::string Tools(bool moc, bool uic, bool rcc); @@ -85,21 +85,44 @@ public: std::vector<std::string> const& newOpts, bool isQt5); - /// @brief Parses the content of a qrc file - /// - /// Use when rcc does not support the "--list" option - static void RccListParseContent(std::string const& content, - std::vector<std::string>& files); - - /// @brief Parses the output of the "rcc --list ..." command - static bool RccListParseOutput(std::string const& rccStdOut, - std::string const& rccStdErr, - std::vector<std::string>& files, - std::string& error); - - /// @brief Converts relative qrc entry paths to full paths - static void RccListConvertFullPath(std::string const& qrcFileDir, - std::vector<std::string>& files); + /** @class RccLister + * @brief Lists files in qrc resource files + */ + class RccLister + { + public: + RccLister(); + RccLister(std::string rccExecutable, std::vector<std::string> listOptions); + + //! The rcc executable + std::string const& RccExcutable() const { return RccExcutable_; } + void SetRccExecutable(std::string const& rccExecutable) + { + RccExcutable_ = rccExecutable; + } + + //! The rcc executable list options + std::vector<std::string> const& ListOptions() const + { + return ListOptions_; + } + void SetListOptions(std::vector<std::string> const& listOptions) + { + ListOptions_ = listOptions; + } + + /** + * @brief Lists a files in the qrcFile + * @arg files The file names are appended to this list + * @arg error contains the error message when the function fails + */ + bool list(std::string const& qrcFile, std::vector<std::string>& files, + std::string& error, bool verbose = false) const; + + private: + std::string RccExcutable_; + std::vector<std::string> ListOptions_; + }; }; #endif diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx index 3ad91ee..59e17d7 100644 --- a/Source/cmQtAutoGenGlobalInitializer.cxx +++ b/Source/cmQtAutoGenGlobalInitializer.cxx @@ -20,6 +20,24 @@ #include <memory> #include <utility> +cmQtAutoGenGlobalInitializer::Keywords::Keywords() + : AUTOMOC("AUTOMOC") + , AUTOUIC("AUTOUIC") + , AUTORCC("AUTORCC") + , AUTOMOC_EXECUTABLE("AUTOMOC_EXECUTABLE") + , AUTOUIC_EXECUTABLE("AUTOUIC_EXECUTABLE") + , AUTORCC_EXECUTABLE("AUTORCC_EXECUTABLE") + , SKIP_AUTOGEN("SKIP_AUTOGEN") + , SKIP_AUTOMOC("SKIP_AUTOMOC") + , SKIP_AUTOUIC("SKIP_AUTOUIC") + , SKIP_AUTORCC("SKIP_AUTORCC") + , AUTOUIC_OPTIONS("AUTOUIC_OPTIONS") + , AUTORCC_OPTIONS("AUTORCC_OPTIONS") + , qrc("qrc") + , ui("ui") +{ +} + cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer( std::vector<cmLocalGenerator*> const& localGenerators) { @@ -74,16 +92,16 @@ cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer( continue; } - bool const moc = target->GetPropertyAsBool("AUTOMOC"); - bool const uic = target->GetPropertyAsBool("AUTOUIC"); - bool const rcc = target->GetPropertyAsBool("AUTORCC"); + bool const moc = target->GetPropertyAsBool(kw().AUTOMOC); + bool const uic = target->GetPropertyAsBool(kw().AUTOUIC); + bool const rcc = target->GetPropertyAsBool(kw().AUTORCC); if (moc || uic || rcc) { std::string const mocExec = - target->GetSafeProperty("AUTOMOC_EXECUTABLE"); + target->GetSafeProperty(kw().AUTOMOC_EXECUTABLE); std::string const uicExec = - target->GetSafeProperty("AUTOUIC_EXECUTABLE"); + target->GetSafeProperty(kw().AUTOUIC_EXECUTABLE); std::string const rccExec = - target->GetSafeProperty("AUTORCC_EXECUTABLE"); + target->GetSafeProperty(kw().AUTORCC_EXECUTABLE); // We support Qt4, Qt5 and Qt6 auto qtVersion = cmQtAutoGenInitializer::GetQtVersion(target); diff --git a/Source/cmQtAutoGenGlobalInitializer.h b/Source/cmQtAutoGenGlobalInitializer.h index 74184a0..77429b7 100644 --- a/Source/cmQtAutoGenGlobalInitializer.h +++ b/Source/cmQtAutoGenGlobalInitializer.h @@ -18,10 +18,39 @@ class cmQtAutoGenInitializer; class cmQtAutoGenGlobalInitializer { public: + /// @brief Collection of QtAutogen related keywords + class Keywords + { + public: + Keywords(); + + std::string AUTOMOC; + std::string AUTOUIC; + std::string AUTORCC; + + std::string AUTOMOC_EXECUTABLE; + std::string AUTOUIC_EXECUTABLE; + std::string AUTORCC_EXECUTABLE; + + std::string SKIP_AUTOGEN; + std::string SKIP_AUTOMOC; + std::string SKIP_AUTOUIC; + std::string SKIP_AUTORCC; + + std::string AUTOUIC_OPTIONS; + std::string AUTORCC_OPTIONS; + + std::string qrc; + std::string ui; + }; + +public: cmQtAutoGenGlobalInitializer( std::vector<cmLocalGenerator*> const& localGenerators); ~cmQtAutoGenGlobalInitializer(); + Keywords const& kw() const { return Keywords_; }; + bool generate(); private: @@ -48,6 +77,7 @@ private: std::map<cmLocalGenerator*, std::string> GlobalAutoGenTargets_; std::map<cmLocalGenerator*, std::string> GlobalAutoRccTargets_; std::unordered_map<std::string, std::string> ExecutableTestOutputs_; + Keywords const Keywords_; }; #endif diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 614a88b..be96f1a 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -7,7 +7,6 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" -#include "cmDuration.h" #include "cmFilePathChecksum.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" @@ -19,14 +18,14 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" -#include "cmProcessOutput.h" #include "cmSourceFile.h" +#include "cmSourceFileLocationKind.h" #include "cmSourceGroup.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" -#include "cmsys/FStream.hxx" +#include "cmake.h" #include "cmsys/SystemInformation.hxx" #include <algorithm> @@ -34,27 +33,10 @@ #include <deque> #include <map> #include <set> -#include <sstream> #include <string> -#include <type_traits> #include <utility> #include <vector> -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; @@ -69,58 +51,6 @@ static std::size_t GetParallelCPUCount() 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<std::string, 2> 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()); - 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(), @@ -343,6 +273,26 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } } + // Check status of policy CMP0071 + { + cmPolicies::PolicyStatus const CMP0071_status = + makefile->GetPolicyStatus(cmPolicies::CMP0071); + switch (CMP0071_status) { + case cmPolicies::WARN: + this->CMP0071Warn = 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 + this->CMP0071Accept = true; + break; + } + } + // Common directories { // Collapsed current binary directory @@ -392,23 +342,15 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } // Moc, Uic and _autogen target settings - if (this->Moc.Enabled || this->Uic.Enabled) { + if (this->MocOrUicEnabled()) { // 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; - } + if (this->Uic.Enabled && !InitUic()) { + return false; } // Autogen target name @@ -449,12 +391,6 @@ bool cmQtAutoGenInitializer::InitCustomTargets() 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()) { @@ -479,8 +415,7 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } // Add autogen include directory to the origin target INCLUDE_DIRECTORIES - if (this->Moc.Enabled || this->Uic.Enabled || - (this->Rcc.Enabled && this->MultiConfig)) { + if (this->MocOrUicEnabled() || (this->Rcc.Enabled && this->MultiConfig)) { this->Target->AddIncludeDirectory(this->Dir.Include, true); } @@ -490,7 +425,7 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } // Create autogen target - if ((this->Moc.Enabled || this->Uic.Enabled) && !this->InitAutogenTarget()) { + if (this->MocOrUicEnabled() && !this->InitAutogenTarget()) { return false; } @@ -575,7 +510,18 @@ bool cmQtAutoGenInitializer::InitMoc() } // Moc executable - return GetMocExecutable(); + { + if (!this->GetQtExecutable(this->Moc, "moc", false, nullptr)) { + return false; + } + // Let the _autogen target depend on the moc executable + if (this->Moc.ExecutableTarget != nullptr) { + this->AutogenTarget.DependTargets.insert( + this->Moc.ExecutableTarget->Target); + } + } + + return true; } bool cmQtAutoGenInitializer::InitUic() @@ -618,83 +564,118 @@ bool cmQtAutoGenInitializer::InitUic() } // Uic executable - return GetUicExecutable(); + { + if (!this->GetQtExecutable(this->Uic, "uic", true, nullptr)) { + return false; + } + // Let the _autogen target depend on the uic executable + if (this->Uic.ExecutableTarget != nullptr) { + this->AutogenTarget.DependTargets.insert( + this->Uic.ExecutableTarget->Target); + } + } + + return true; } bool cmQtAutoGenInitializer::InitRcc() { - return GetRccExecutable(); + // Rcc executable + { + std::string stdOut; + if (!this->GetQtExecutable(this->Rcc, "rcc", false, &stdOut)) { + return false; + } + // Evaluate test output + if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) { + if (stdOut.find("--list") != std::string::npos) { + this->Rcc.ListOptions.emplace_back("--list"); + } else if (stdOut.find("-list") != std::string::npos) { + this->Rcc.ListOptions.emplace_back("-list"); + } + } + } + + return true; } bool cmQtAutoGenInitializer::InitScanFiles() { cmMakefile* makefile = this->Target->Target->GetMakefile(); + auto const& kw = this->GlobalInitializer->kw(); + + auto makeMUFile = [this, &kw](cmSourceFile* sf, std::string const& fullPath, + bool muIt) -> MUFileHandle { + MUFileHandle muf = cm::make_unique<MUFile>(); + muf->RealPath = cmSystemTools::GetRealPath(fullPath); + muf->SF = sf; + muf->Generated = sf->GetIsGenerated(); + bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN); + muf->SkipMoc = this->Moc.Enabled && + (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOMOC)); + muf->SkipUic = this->Uic.Enabled && + (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC)); + if (muIt) { + muf->MocIt = this->Moc.Enabled && !muf->SkipMoc; + muf->UicIt = this->Uic.Enabled && !muf->SkipUic; + } + return muf; + }; - // String constants - std::string const SKIP_AUTOGEN_str = "SKIP_AUTOGEN"; - std::string const SKIP_AUTOMOC_str = "SKIP_AUTOMOC"; - std::string const SKIP_AUTOUIC_str = "SKIP_AUTOUIC"; + auto addMUFile = [&](MUFileHandle&& muf, bool isHeader) { + if ((muf->MocIt || muf->UicIt) && muf->Generated) { + this->AutogenTarget.FilesGenerated.emplace_back(muf.get()); + } + if (isHeader) { + this->AutogenTarget.Headers.emplace(muf->SF, std::move(muf)); + } else { + this->AutogenTarget.Sources.emplace(muf->SF, std::move(muf)); + } + }; // Scan through target files { - // String constants - std::string const qrc_str = "qrc"; - std::string const SKIP_AUTORCC_str = "SKIP_AUTORCC"; - std::string const AUTORCC_OPTIONS_str = "AUTORCC_OPTIONS"; - // Scan through target files std::vector<cmSourceFile*> srcFiles; this->Target->GetConfigCommonSourceFiles(srcFiles); for (cmSourceFile* sf : srcFiles) { - if (sf->GetPropertyAsBool(SKIP_AUTOGEN_str)) { + // 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 pathError; + std::string const& fullPath = sf->GetFullPath(&pathError); + if (!pathError.empty() || fullPath.empty()) { 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); - 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_str)) || - (this->Uic.Enabled && - !sf->GetPropertyAsBool(SKIP_AUTOUIC_str))) { - // Register source - const bool generated = sf->GetIsGenerated(); - 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 files that will be scanned by moc or uic + if (this->MocOrUicEnabled()) { + switch (cmSystemTools::GetFileFormat(ext)) { + case cmSystemTools::HEADER_FILE_FORMAT: + addMUFile(makeMUFile(sf, fullPath, true), true); + break; + case cmSystemTools::CXX_FILE_FORMAT: + addMUFile(makeMUFile(sf, fullPath, true), false); + break; + default: + break; } } + // Register rcc enabled files if (this->Rcc.Enabled) { - if ((ext == qrc_str) && !sf->GetPropertyAsBool(SKIP_AUTORCC_str)) { + if ((ext == kw.qrc) && !sf->GetPropertyAsBool(kw.SKIP_AUTOGEN) && + !sf->GetPropertyAsBool(kw.SKIP_AUTORCC)) { // Register qrc file Qrc qrc; - qrc.QrcFile = cmSystemTools::GetRealPath(fPath); + qrc.QrcFile = cmSystemTools::GetRealPath(fullPath); qrc.QrcName = cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile); qrc.Generated = sf->GetIsGenerated(); // RCC options { - std::string const opts = sf->GetSafeProperty(AUTORCC_OPTIONS_str); + std::string const opts = sf->GetSafeProperty(kw.AUTORCC_OPTIONS); if (!opts.empty()) { cmSystemTools::ExpandListArgument(opts, qrc.Options); } @@ -710,15 +691,72 @@ bool cmQtAutoGenInitializer::InitScanFiles() // mocs_compilation.cpp source acknowledged by this target. this->Target->ClearSourcesCache(); + // For source files find additional headers and private headers + if (this->MocOrUicEnabled()) { + std::vector<MUFileHandle> extraHeaders; + extraHeaders.reserve(this->AutogenTarget.Sources.size() * 2); + // Header search suffixes and extensions + std::array<std::string, 2> const suffixes{ { "", "_p" } }; + auto const& exts = makefile->GetCMakeInstance()->GetHeaderExtensions(); + // Scan through sources + for (auto const& pair : this->AutogenTarget.Sources) { + MUFile const& muf = *pair.second; + if (muf.MocIt || muf.UicIt) { + // Search for the default header file and a private header + std::string const& realPath = muf.RealPath; + std::string basePath = cmQtAutoGen::SubDirPrefix(realPath); + basePath += cmSystemTools::GetFilenameWithoutLastExtension(realPath); + for (auto const& suffix : suffixes) { + std::string const suffixedPath = basePath + suffix; + for (auto const& ext : exts) { + std::string fullPath = suffixedPath; + fullPath += '.'; + fullPath += ext; + + auto constexpr locationKind = cmSourceFileLocationKind::Known; + cmSourceFile* sf = makefile->GetSource(fullPath, locationKind); + if (sf != nullptr) { + // Check if we know about this header already + if (this->AutogenTarget.Headers.find(sf) != + this->AutogenTarget.Headers.end()) { + continue; + } + // We only accept not-GENERATED files that do exist. + if (!sf->GetIsGenerated() && + !cmSystemTools::FileExists(fullPath)) { + continue; + } + } else if (cmSystemTools::FileExists(fullPath)) { + // Create a new source file for the existing file + sf = makefile->CreateSource(fullPath, false, locationKind); + } + + if (sf != nullptr) { + auto eMuf = makeMUFile(sf, fullPath, true); + // Ony process moc/uic when the parent is processed as well + if (!muf.MocIt) { + eMuf->MocIt = false; + } + if (!muf.UicIt) { + eMuf->UicIt = false; + } + extraHeaders.emplace_back(std::move(eMuf)); + } + } + } + } + } + // Move generated files to main headers list + for (auto& eMuf : extraHeaders) { + addMUFile(std::move(eMuf), true); + } + } + // Scan through all source files in the makefile to extract moc and uic // parameters. Historically we support non target source file parameters. // The reason is that their file names might be discovered from source files // at generation time. - if (this->Moc.Enabled || this->Uic.Enabled) { - // String constants - std::string const ui_str = "ui"; - std::string const AUTOUIC_OPTIONS_str = "AUTOUIC_OPTIONS"; - + if (this->MocOrUicEnabled()) { 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 @@ -728,139 +766,94 @@ bool cmQtAutoGenInitializer::InitScanFiles() if (!pathError.empty() || fullPath.empty()) { continue; } + std::string const& ext = sf->GetExtension(); - // Check file type - auto const fileType = cmSystemTools::GetFileFormat(sf->GetExtension()); - bool const isSource = (fileType == cmSystemTools::CXX_FILE_FORMAT) || - (fileType == cmSystemTools::HEADER_FILE_FORMAT); - bool const isUi = (this->Moc.Enabled && sf->GetExtension() == ui_str); - - // Process only certain file types - if (isSource || isUi) { - std::string const absFile = cmSystemTools::GetRealPath(fullPath); - // Acquire file properties - bool const skipAUTOGEN = sf->GetPropertyAsBool(SKIP_AUTOGEN_str); - bool const skipMoc = (this->Moc.Enabled && isSource) && - (skipAUTOGEN || sf->GetPropertyAsBool(SKIP_AUTOMOC_str)); - bool const skipUic = this->Uic.Enabled && - (skipAUTOGEN || sf->GetPropertyAsBool(SKIP_AUTOUIC_str)); - - // Register moc and uic skipped file - if (skipMoc) { - this->Moc.Skip.insert(absFile); + auto const fileFormat = cmSystemTools::GetFileFormat(ext); + if (fileFormat == cmSystemTools::HEADER_FILE_FORMAT) { + if (this->AutogenTarget.Headers.find(sf) == + this->AutogenTarget.Headers.end()) { + auto muf = makeMUFile(sf, fullPath, false); + if (muf->SkipMoc || muf->SkipUic) { + this->AutogenTarget.Headers.emplace(sf, std::move(muf)); + } } - if (skipUic) { - this->Uic.Skip.insert(absFile); + } else if (fileFormat == cmSystemTools::CXX_FILE_FORMAT) { + if (this->AutogenTarget.Sources.find(sf) == + this->AutogenTarget.Sources.end()) { + auto muf = makeMUFile(sf, fullPath, false); + if (muf->SkipMoc || muf->SkipUic) { + this->AutogenTarget.Sources.emplace(sf, std::move(muf)); + } } - - // Check if the .ui file has uic options - if (isUi && !skipUic) { - std::string const uicOpts = sf->GetSafeProperty(AUTOUIC_OPTIONS_str); + } else if (this->Uic.Enabled && (ext == kw.ui)) { + // .ui file + std::string realPath = cmSystemTools::GetRealPath(fullPath); + bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN); + bool const skipUic = + (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC)); + if (!skipUic) { + // Check if the .ui file has uic options + std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS); if (!uicOpts.empty()) { - this->Uic.FileFiles.push_back(absFile); + this->Uic.FileFiles.push_back(std::move(realPath)); std::vector<std::string> optsVec; cmSystemTools::ExpandListArgument(uicOpts, optsVec); this->Uic.FileOptions.push_back(std::move(optsVec)); } + } else { + // Register skipped .ui file + this->Uic.SkipUi.insert(std::move(realPath)); } } } } // Process GENERATED sources and headers - if (this->Moc.Enabled || this->Uic.Enabled) { - 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 (this->MocOrUicEnabled() && !this->AutogenTarget.FilesGenerated.empty()) { + if (this->CMP0071Accept) { + // Let the autogen target depend on the GENERATED files + for (MUFile* muf : this->AutogenTarget.FilesGenerated) { + this->AutogenTarget.DependFiles.insert(muf->RealPath); } - - 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(MessageType::AUTHOR_WARNING, msg); - } + } else if (this->CMP0071Warn) { + std::string msg; + msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071); + msg += '\n'; + std::string property; + if (this->Moc.Enabled && this->Uic.Enabled) { + property = kw.SKIP_AUTOGEN; + } else if (this->Moc.Enabled) { + property = kw.SKIP_AUTOMOC; + } else if (this->Uic.Enabled) { + property = kw.SKIP_AUTOUIC; + } + msg += "For compatibility, CMake is excluding the GENERATED source " + "file(s):\n"; + for (MUFile* muf : this->AutogenTarget.FilesGenerated) { + msg += " "; + msg += Quoted(muf->RealPath); + msg += '\n'; } + msg += "from processing by "; + msg += cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false); + 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(MessageType::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<std::string> optionsTarget; cmSystemTools::ExpandListArgument( - this->Target->GetSafeProperty("AUTORCC_OPTIONS"), optionsTarget); + this->Target->GetSafeProperty(kw.AUTORCC_OPTIONS), optionsTarget); // Check if file name is unique for (Qrc& qrc : this->Rcc.Qrcs) { @@ -938,7 +931,8 @@ bool cmQtAutoGenInitializer::InitScanFiles() for (Qrc& qrc : this->Rcc.Qrcs) { if (!qrc.Generated) { std::string error; - if (!RccListInputs(qrc.QrcFile, qrc.Resources, error)) { + RccLister const lister(this->Rcc.Executable, this->Rcc.ListOptions); + if (!lister.list(qrc.QrcFile, qrc.Resources, error)) { cmSystemTools::Error(error); return false; } @@ -961,7 +955,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() // Files provided by the autogen target std::vector<std::string> autogenProvides; if (this->Moc.Enabled) { - this->AddGeneratedSource(this->Moc.MocsCompilation, GeneratorT::MOC, true); + this->AddGeneratedSource(this->Moc.MocsCompilation, this->Moc, true); autogenProvides.push_back(this->Moc.MocsCompilation); } @@ -1109,13 +1103,12 @@ 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); + this->AddGeneratedSource(qrc.RccFile, this->Rcc); std::vector<std::string> ccOutput; ccOutput.push_back(qrc.RccFile); @@ -1174,8 +1167,8 @@ bool cmQtAutoGenInitializer::InitRccTargets() if (!this->TargetsFolder.empty()) { autoRccTarget->SetProperty("FOLDER", this->TargetsFolder.c_str()); } - if (!rccTargetName.empty()) { - autoRccTarget->AddUtility(rccTargetName, makefile); + if (!this->Rcc.ExecutableTargetName.empty()) { + autoRccTarget->AddUtility(this->Rcc.ExecutableTargetName, makefile); } } // Add autogen target to the origin target dependencies @@ -1195,8 +1188,8 @@ bool cmQtAutoGenInitializer::InitRccTargets() // Add resource file to the custom command dependencies ccDepends.push_back(fileName); } - if (!rccTargetName.empty()) { - ccDepends.push_back(rccTargetName); + if (!this->Rcc.ExecutableTargetName.empty()) { + ccDepends.push_back(this->Rcc.ExecutableTargetName); } makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends, /*main_dependency*/ std::string(), @@ -1222,7 +1215,7 @@ bool cmQtAutoGenInitializer::SetupCustomTargets() } // Generate autogen target info file - if (this->Moc.Enabled || this->Uic.Enabled) { + if (this->MocOrUicEnabled()) { // Write autogen target info files if (!this->SetupWriteAutogenInfo()) { return false; @@ -1262,22 +1255,74 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() 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); + // Use sorted sets + std::set<std::string> headers; + std::set<std::string> sources; + std::set<std::string> moc_headers; + std::set<std::string> moc_sources; + std::set<std::string> moc_skip; + std::set<std::string> uic_headers; + std::set<std::string> uic_sources; + std::set<std::string> uic_skip; + // Filter headers + for (auto const& pair : this->AutogenTarget.Headers) { + MUFile const& muf = *pair.second; + if (muf.Generated && !this->CMP0071Accept) { + continue; + } + if (muf.SkipMoc) { + moc_skip.insert(muf.RealPath); + } + if (muf.SkipUic) { + uic_skip.insert(muf.RealPath); + } + if (muf.MocIt && muf.UicIt) { + headers.insert(muf.RealPath); + } else if (muf.MocIt) { + moc_headers.insert(muf.RealPath); + } else if (muf.UicIt) { + uic_headers.insert(muf.RealPath); + } + } + // Filter sources + for (auto const& pair : this->AutogenTarget.Sources) { + MUFile const& muf = *pair.second; + if (muf.Generated && !this->CMP0071Accept) { + continue; + } + if (muf.SkipMoc) { + moc_skip.insert(muf.RealPath); + } + if (muf.SkipUic) { + uic_skip.insert(muf.RealPath); + } + if (muf.MocIt && muf.UicIt) { + sources.insert(muf.RealPath); + } else if (muf.MocIt) { + moc_sources.insert(muf.RealPath); + } else if (muf.UicIt) { + uic_sources.insert(muf.RealPath); + } + } 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); + ofs.Write("# Files\n"); + ofs.Write("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile); + ofs.WriteConfig("AM_SETTINGS_FILE", + this->AutogenTarget.ConfigSettingsFile); + ofs.WriteStrings("AM_HEADERS", headers); + ofs.WriteStrings("AM_SOURCES", sources); + // Write moc settings if (this->Moc.Enabled) { ofs.Write("# MOC settings\n"); - ofs.WriteStrings("AM_MOC_SKIP", this->Moc.Skip); + ofs.WriteStrings("AM_MOC_HEADERS", moc_headers); + ofs.WriteStrings("AM_MOC_SOURCES", moc_sources); + ofs.WriteStrings("AM_MOC_SKIP", 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); @@ -1294,8 +1339,13 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() // Write uic settings if (this->Uic.Enabled) { + // Add skipped .ui files + uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end()); + ofs.Write("# UIC settings\n"); - ofs.WriteStrings("AM_UIC_SKIP", this->Uic.Skip); + ofs.WriteStrings("AM_UIC_HEADERS", uic_headers); + ofs.WriteStrings("AM_UIC_SOURCES", uic_sources); + ofs.WriteStrings("AM_UIC_SKIP", 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); @@ -1353,23 +1403,68 @@ bool cmQtAutoGenInitializer::SetupWriteRccInfo() return true; } -void cmQtAutoGenInitializer::AddGeneratedSource(std::string const& filename, - GeneratorT genType, - bool prepend) +void cmQtAutoGenInitializer::RegisterGeneratedSource( + std::string const& filename) { - // 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); + cmSourceFile* gFile = makefile->GetOrCreateSource(filename, true); + gFile->SetProperty("GENERATED", "1"); + gFile->SetProperty("SKIP_AUTOGEN", "1"); +} +bool cmQtAutoGenInitializer::AddGeneratedSource(std::string const& filename, + GenVarsT const& genVars, + bool prepend) +{ + // Register source at makefile + this->RegisterGeneratedSource(filename); // Add source file to target this->Target->AddSource(filename, prepend); + // Add source file to source group + return this->AddToSourceGroup(filename, genVars.GenNameUpper); +} + +bool cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName, + std::string const& genNameUpper) +{ + cmMakefile* makefile = this->Target->Target->GetMakefile(); + cmSourceGroup* sourceGroup = nullptr; + // Acquire source group + { + std::string property; + std::string groupName; + { + // Prefer generator specific source group name + std::array<std::string, 2> props{ { genNameUpper + "_SOURCE_GROUP", + "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::string err; + err += genNameUpper; + err += " error in "; + err += property; + err += ": Could not find or create the source group "; + err += cmQtAutoGen::Quoted(groupName); + cmSystemTools::Error(err); + return false; + } + } + } + if (sourceGroup != nullptr) { + sourceGroup->AddGroupFile(fileName); + } + return true; } static unsigned int CharPtrToUInt(const char* const input) @@ -1384,8 +1479,12 @@ static unsigned int CharPtrToUInt(const char* const input) static std::vector<cmQtAutoGen::IntegerVersion> GetKnownQtVersions( cmGeneratorTarget const* target) { - cmMakefile* makefile = target->Target->GetMakefile(); + // Qt version variable prefixes + static std::array<std::string, 3> const prefixes{ { "Qt6Core", "Qt5Core", + "QT" } }; + std::vector<cmQtAutoGen::IntegerVersion> result; + result.reserve(prefixes.size() * 2); // Adds a version to the result (nullptr safe) auto addVersion = [&result](const char* major, const char* minor) { cmQtAutoGen::IntegerVersion ver(CharPtrToUInt(major), @@ -1394,8 +1493,7 @@ static std::vector<cmQtAutoGen::IntegerVersion> GetKnownQtVersions( result.emplace_back(ver); } }; - // Qt version variable prefixes - std::array<std::string, 3> const prefixes{ { "Qt6Core", "Qt5Core", "QT" } }; + cmMakefile* makefile = target->Target->GetMakefile(); // Read versions from variables for (const std::string& prefix : prefixes) { @@ -1439,181 +1537,93 @@ cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target) return res; } -std::pair<bool, std::string> cmQtAutoGenInitializer::GetQtExecutable( - const std::string& executable, bool ignoreMissingTarget, std::string* output) +bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars, + const std::string& executable, + bool ignoreMissingTarget, + std::string* output) const { - const std::string upperExecutable = cmSystemTools::UpperCase(executable); - std::string result = this->Target->Target->GetSafeProperty( - "AUTO" + upperExecutable + "_EXECUTABLE"); - if (!result.empty()) { - cmListFileBacktrace lfbt = - this->Target->Target->GetMakefile()->GetBacktrace(); - cmGeneratorExpression ge(lfbt); - std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(result); - result = cge->Evaluate(this->Target->GetLocalGenerator(), ""); - - return std::make_pair(true, result); - } - - std::string err; - - // Find executable - { - const std::string targetName = - GetQtExecutableTargetName(this->QtVersion, executable); - if (targetName.empty()) { - err = "The AUTO" + upperExecutable + " feature "; - err += "supports only Qt 4, Qt 5 and Qt 6."; - } else { - cmLocalGenerator* localGen = this->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()) { - this->GlobalInitializer->GetExecutableTestOutput(executable, result, err, - output); - } - - // Print error - if (!err.empty()) { - std::string msg = "AutoGen ("; + auto print_err = [this, &genVars](std::string const& err) { + std::string msg = genVars.GenNameUpper; + msg += " for target "; msg += this->Target->GetName(); - msg += "): "; + msg += ": "; msg += err; cmSystemTools::Error(msg); - return std::make_pair(false, ""); - } - - return std::make_pair(true, result); -} - -bool cmQtAutoGenInitializer::GetMocExecutable() -{ - const auto result = this->GetQtExecutable("moc", false, nullptr); - this->Moc.Executable = result.second; - return result.first; -} - -bool cmQtAutoGenInitializer::GetUicExecutable() -{ - const auto result = this->GetQtExecutable("uic", true, nullptr); - this->Uic.Executable = result.second; - return result.first; -} + }; -bool cmQtAutoGenInitializer::GetRccExecutable() -{ - std::string stdOut; - const auto result = this->GetQtExecutable("rcc", false, &stdOut); - this->Rcc.Executable = result.second; - if (!result.first) { - return false; - } + // Custom executable + { + std::string const prop = genVars.GenNameUpper + "_EXECUTABLE"; + std::string const val = this->Target->Target->GetSafeProperty(prop); + if (!val.empty()) { + // Evaluate generator expression + { + cmListFileBacktrace lfbt = + this->Target->Target->GetMakefile()->GetBacktrace(); + cmGeneratorExpression ge(lfbt); + std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val); + genVars.Executable = + cge->Evaluate(this->Target->GetLocalGenerator(), ""); + } + if (genVars.Executable.empty() && !ignoreMissingTarget) { + print_err(prop + " evaluates to an empty value"); + return false; + } - if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) { - if (stdOut.find("--list") != std::string::npos) { - this->Rcc.ListOptions.emplace_back("--list"); - } else { - this->Rcc.ListOptions.emplace_back("-list"); + // Check if the provided executable already exists (it's possible for it + // not to exist when building Qt itself). + genVars.ExecutableExists = cmSystemTools::FileExists(genVars.Executable); + return true; } } - 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<std::string>& 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; + // Find executable target + { + // Find executable target name + std::string targetName; + if (this->QtVersion.Major == 4) { + targetName = "Qt4::"; + } else if (this->QtVersion.Major == 5) { + targetName = "Qt5::"; + } else if (this->QtVersion.Major == 6) { + targetName = "Qt6::"; } - - // 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<std::string> 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"; + targetName += executable; + + // Find target + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + cmGeneratorTarget* target = localGen->FindGeneratorTargetToUse(targetName); + if (target != nullptr) { + genVars.ExecutableTargetName = targetName; + genVars.ExecutableTarget = target; + if (target->IsImported()) { + genVars.Executable = target->ImportedGetLocation(""); + } else { + genVars.Executable = target->GetLocation(""); + } + } else { + if (ignoreMissingTarget) { + return true; + } + std::string err = "Could not find "; + err += executable; + err += " executable target "; + err += targetName; + print_err(err); return false; } - if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) { + } + + // Test executable + { + std::string err; + if (!this->GlobalInitializer->GetExecutableTestOutput( + executable, genVars.Executable, err, output)) { + print_err(err); 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); - } + genVars.ExecutableExists = true; } - // Convert relative paths to absolute paths - RccListConvertFullPath(cmSystemTools::GetFilenamePath(fileName), files); return true; } diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index 781dd15..7ff33c3 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -8,15 +8,18 @@ #include "cmQtAutoGen.h" #include <map> +#include <memory> // IWYU pragma: keep #include <ostream> #include <set> #include <string> +#include <unordered_map> #include <utility> #include <vector> class cmGeneratorTarget; class cmTarget; class cmQtAutoGenGlobalInitializer; +class cmSourceFile; /// @brief Initializes the QtAutoGen generators class cmQtAutoGenInitializer : public cmQtAutoGen @@ -40,6 +43,38 @@ public: std::vector<std::string> Resources; }; + /// @brief Moc/Uic file + struct MUFile + { + std::string RealPath; + cmSourceFile* SF = nullptr; + bool Generated = false; + bool SkipMoc = false; + bool SkipUic = false; + bool MocIt = false; + bool UicIt = false; + }; + typedef std::unique_ptr<MUFile> MUFileHandle; + + /// @brief Abstract moc/uic/rcc generator variables base class + struct GenVarsT + { + bool Enabled = false; + // Generator type/name + GenT Gen; + std::string const& GenNameUpper; + // Executable + std::string ExecutableTargetName; + cmGeneratorTarget* ExecutableTarget = nullptr; + std::string Executable; + bool ExecutableExists = false; + + /// @brief Constructor + GenVarsT(GenT gen) + : Gen(gen) + , GenNameUpper(cmQtAutoGen::GeneratorNameUpper(gen)){}; + }; + /// @brief Writes a CMake info file class InfoWriter { @@ -88,6 +123,12 @@ public: bool SetupCustomTargets(); private: + /// @brief If moc or uic is enabled, the autogen target will be generated + bool MocOrUicEnabled() const + { + return (this->Moc.Enabled || this->Uic.Enabled); + } + bool InitMoc(); bool InitUic(); bool InitRcc(); @@ -99,20 +140,14 @@ private: bool SetupWriteAutogenInfo(); bool SetupWriteRccInfo(); - void AddGeneratedSource(std::string const& filename, GeneratorT genType, + void RegisterGeneratedSource(std::string const& filename); + bool AddGeneratedSource(std::string const& filename, GenVarsT const& genVars, bool prepend = false); + bool AddToSourceGroup(std::string const& fileName, + std::string const& genNameUpper); - bool GetMocExecutable(); - bool GetUicExecutable(); - bool GetRccExecutable(); - - bool RccListInputs(std::string const& fileName, - std::vector<std::string>& files, - std::string& errorMessage); - - std::pair<bool, std::string> GetQtExecutable(const std::string& executable, - bool ignoreMissingTarget, - std::string* output); + bool GetQtExecutable(GenVarsT& genVars, const std::string& executable, + bool ignoreMissingTarget, std::string* output) const; private: cmQtAutoGenGlobalInitializer* GlobalInitializer; @@ -125,6 +160,8 @@ private: std::vector<std::string> ConfigsList; std::string Verbosity; std::string TargetsFolder; + bool CMP0071Accept = false; + bool CMP0071Warn = false; /// @brief Common directories struct @@ -152,47 +189,51 @@ private: std::set<std::string> DependFiles; std::set<cmTarget*> DependTargets; // Sources to process - std::vector<std::string> Headers; - std::vector<std::string> Sources; - std::vector<std::string> HeadersGenerated; - std::vector<std::string> SourcesGenerated; + std::unordered_map<cmSourceFile*, MUFileHandle> Headers; + std::unordered_map<cmSourceFile*, MUFileHandle> Sources; + std::vector<MUFile*> FilesGenerated; } AutogenTarget; /// @brief Moc only variables - struct + struct MocT : public GenVarsT { - bool Enabled = false; - std::string Executable; std::string PredefsCmd; - std::set<std::string> Skip; std::vector<std::string> Includes; std::map<std::string, std::vector<std::string>> ConfigIncludes; std::set<std::string> Defines; std::map<std::string, std::set<std::string>> ConfigDefines; std::string MocsCompilation; + + /// @brief Constructor + MocT() + : GenVarsT(GenT::MOC){}; } Moc; - ///@brief Uic only variables - struct + /// @brief Uic only variables + struct UicT : public GenVarsT { - bool Enabled = false; - std::string Executable; - std::set<std::string> Skip; + std::set<std::string> SkipUi; std::vector<std::string> SearchPaths; std::vector<std::string> Options; std::map<std::string, std::vector<std::string>> ConfigOptions; std::vector<std::string> FileFiles; std::vector<std::vector<std::string>> FileOptions; + + /// @brief Constructor + UicT() + : GenVarsT(GenT::UIC){}; } Uic; /// @brief Rcc only variables - struct + struct RccT : public GenVarsT { - bool Enabled = false; bool GlobalTarget = false; - std::string Executable; std::vector<std::string> ListOptions; std::vector<Qrc> Qrcs; + + /// @brief Constructor + RccT() + : GenVarsT(GenT::RCC){}; } Rcc; }; diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index e2d7deb..6fbea82 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -14,10 +14,33 @@ #include "cmSystemTools.h" #include "cmake.h" -#include <algorithm> -#include <utility> +cmQtAutoGenerator::Logger::Logger() +{ + // Initialize logger + { + std::string verbose; + if (cmSystemTools::GetEnv("VERBOSE", verbose) && !verbose.empty()) { + unsigned long iVerbose = 0; + if (cmSystemTools::StringToULong(verbose.c_str(), &iVerbose)) { + SetVerbosity(static_cast<unsigned int>(iVerbose)); + } else { + // Non numeric verbosity + SetVerbose(cmSystemTools::IsOn(verbose)); + } + } + } + { + std::string colorEnv; + cmSystemTools::GetEnv("COLOR", colorEnv); + if (!colorEnv.empty()) { + SetColorOutput(cmSystemTools::IsOn(colorEnv)); + } else { + SetColorOutput(true); + } + } +} -// -- Class methods +cmQtAutoGenerator::Logger::~Logger() = default; void cmQtAutoGenerator::Logger::RaiseVerbosity(std::string const& value) { @@ -43,8 +66,7 @@ std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title) return head; } -void cmQtAutoGenerator::Logger::Info(GeneratorT genType, - std::string const& message) +void cmQtAutoGenerator::Logger::Info(GenT genType, std::string const& message) { std::string msg = GeneratorName(genType); msg += ": "; @@ -58,7 +80,7 @@ void cmQtAutoGenerator::Logger::Info(GeneratorT genType, } } -void cmQtAutoGenerator::Logger::Warning(GeneratorT genType, +void cmQtAutoGenerator::Logger::Warning(GenT genType, std::string const& message) { std::string msg; @@ -82,7 +104,7 @@ void cmQtAutoGenerator::Logger::Warning(GeneratorT genType, } } -void cmQtAutoGenerator::Logger::WarningFile(GeneratorT genType, +void cmQtAutoGenerator::Logger::WarningFile(GenT genType, std::string const& filename, std::string const& message) { @@ -94,8 +116,7 @@ void cmQtAutoGenerator::Logger::WarningFile(GeneratorT genType, Warning(genType, msg); } -void cmQtAutoGenerator::Logger::Error(GeneratorT genType, - std::string const& message) +void cmQtAutoGenerator::Logger::Error(GenT genType, std::string const& message) { std::string msg; msg += HeadLine(GeneratorName(genType) + " error"); @@ -111,7 +132,7 @@ void cmQtAutoGenerator::Logger::Error(GeneratorT genType, } } -void cmQtAutoGenerator::Logger::ErrorFile(GeneratorT genType, +void cmQtAutoGenerator::Logger::ErrorFile(GenT genType, std::string const& filename, std::string const& message) { @@ -124,7 +145,7 @@ void cmQtAutoGenerator::Logger::ErrorFile(GeneratorT genType, } void cmQtAutoGenerator::Logger::ErrorCommand( - GeneratorT genType, std::string const& message, + GenT genType, std::string const& message, std::vector<std::string> const& command, std::string const& output) { std::string msg; @@ -153,6 +174,91 @@ void cmQtAutoGenerator::Logger::ErrorCommand( } } +bool cmQtAutoGenerator::MakeParentDirectory(std::string const& filename) +{ + bool success = true; + std::string const dirName = cmSystemTools::GetFilenamePath(filename); + if (!dirName.empty()) { + success = cmSystemTools::MakeDirectory(dirName); + } + return success; +} + +bool cmQtAutoGenerator::FileRead(std::string& content, + std::string const& filename, + std::string* error) +{ + content.clear(); + if (!cmSystemTools::FileExists(filename, true)) { + if (error != nullptr) { + error->append("Not a file."); + } + return false; + } + + unsigned long const length = cmSystemTools::FileLength(filename); + cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); + + // Use lambda to save destructor calls of ifs + return [&ifs, length, &content, error]() -> bool { + if (!ifs) { + if (error != nullptr) { + error->append("Opening the file for reading failed."); + } + return false; + } + content.reserve(length); + typedef std::istreambuf_iterator<char> IsIt; + content.assign(IsIt{ ifs }, IsIt{}); + if (!ifs) { + content.clear(); + if (error != nullptr) { + error->append("Reading from the file failed."); + } + return false; + } + return true; + }(); +} + +bool cmQtAutoGenerator::FileWrite(std::string const& filename, + std::string const& content, + std::string* error) +{ + // Make sure the parent directory exists + if (!cmQtAutoGenerator::MakeParentDirectory(filename)) { + if (error != nullptr) { + error->assign("Could not create parent directory."); + } + return false; + } + cmsys::ofstream ofs; + ofs.open(filename.c_str(), + (std::ios::out | std::ios::binary | std::ios::trunc)); + + // Use lambda to save destructor calls of ofs + return [&ofs, &content, error]() -> bool { + if (!ofs) { + if (error != nullptr) { + error->assign("Opening file for writing failed."); + } + return false; + } + ofs << content; + if (!ofs.good()) { + if (error != nullptr) { + error->assign("File writing failed."); + } + return false; + } + return true; + }(); +} + +cmQtAutoGenerator::FileSystem::FileSystem() = default; + +cmQtAutoGenerator::FileSystem::~FileSystem() = default; + std::string cmQtAutoGenerator::FileSystem::GetRealPath( std::string const& filename) { @@ -160,11 +266,11 @@ std::string cmQtAutoGenerator::FileSystem::GetRealPath( return cmSystemTools::GetRealPath(filename); } -std::string cmQtAutoGenerator::FileSystem::CollapseCombinedPath( - std::string const& dir, std::string const& file) +std::string cmQtAutoGenerator::FileSystem::CollapseFullPath( + std::string const& file, std::string const& dir) { std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::CollapseCombinedPath(dir, file); + return cmSystemTools::CollapseFullPath(file, dir); } void cmQtAutoGenerator::FileSystem::SplitPath( @@ -268,91 +374,16 @@ bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content, std::string const& filename, std::string* error) { - bool success = false; - if (FileExists(filename, true)) { - unsigned long const length = FileLength(filename); - { - std::lock_guard<std::mutex> lock(Mutex_); - cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); - if (ifs) { - content.reserve(length); - content.assign(std::istreambuf_iterator<char>{ ifs }, - std::istreambuf_iterator<char>{}); - if (ifs) { - success = true; - } else { - content.clear(); - if (error != nullptr) { - error->append("Reading from the file failed."); - } - } - } else if (error != nullptr) { - error->append("Opening the file for reading failed."); - } - } - } else if (error != nullptr) { - error->append( - "The file does not exist, is not readable or is a directory."); - } - return success; -} - -bool cmQtAutoGenerator::FileSystem::FileRead(GeneratorT genType, - std::string& content, - std::string const& filename) -{ - std::string error; - if (!FileRead(content, filename, &error)) { - Log()->ErrorFile(genType, filename, error); - return false; - } - return true; + std::lock_guard<std::mutex> lock(Mutex_); + return cmQtAutoGenerator::FileRead(content, filename, error); } bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename, std::string const& content, std::string* error) { - bool success = false; - // Make sure the parent directory exists - if (MakeParentDirectory(filename)) { - std::lock_guard<std::mutex> lock(Mutex_); - cmsys::ofstream outfile; - outfile.open(filename.c_str(), - (std::ios::out | std::ios::binary | std::ios::trunc)); - if (outfile) { - outfile << content; - // Check for write errors - if (outfile.good()) { - success = true; - } else { - if (error != nullptr) { - error->assign("File writing failed"); - } - } - } else { - if (error != nullptr) { - error->assign("Opening file for writing failed"); - } - } - } else { - if (error != nullptr) { - error->assign("Could not create parent directory"); - } - } - return success; -} - -bool cmQtAutoGenerator::FileSystem::FileWrite(GeneratorT genType, - std::string const& filename, - std::string const& content) -{ - std::string error; - if (!FileWrite(filename, content, &error)) { - Log()->ErrorFile(genType, filename, error); - return false; - } - return true; + std::lock_guard<std::mutex> lock(Mutex_); + return cmQtAutoGenerator::FileWrite(filename, content, error); } bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename, @@ -387,294 +418,16 @@ bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname) return cmSystemTools::MakeDirectory(dirname); } -bool cmQtAutoGenerator::FileSystem::MakeDirectory(GeneratorT genType, - std::string const& dirname) -{ - if (!MakeDirectory(dirname)) { - Log()->ErrorFile(genType, dirname, "Could not create directory"); - return false; - } - return true; -} - bool cmQtAutoGenerator::FileSystem::MakeParentDirectory( std::string const& filename) { - bool success = true; - std::string const dirName = cmSystemTools::GetFilenamePath(filename); - if (!dirName.empty()) { - success = MakeDirectory(dirName); - } - return success; -} - -bool cmQtAutoGenerator::FileSystem::MakeParentDirectory( - GeneratorT genType, std::string const& filename) -{ - if (!MakeParentDirectory(filename)) { - Log()->ErrorFile(genType, filename, "Could not create parent directory"); - return false; - } - return true; -} - -int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop, - ReadOnlyProcessT* process) -{ - Process_ = process; - Target_ = nullptr; - return UVPipe_.init(*uv_loop, 0, this); -} - -int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::startRead(std::string* target) -{ - Target_ = target; - return uv_read_start(uv_stream(), &PipeT::UVAlloc, &PipeT::UVData); -} - -void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::reset() -{ - Process_ = nullptr; - Target_ = nullptr; - UVPipe_.reset(); - Buffer_.clear(); - Buffer_.shrink_to_fit(); -} - -void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVAlloc(uv_handle_t* handle, - size_t suggestedSize, - uv_buf_t* buf) -{ - auto& pipe = *reinterpret_cast<PipeT*>(handle->data); - pipe.Buffer_.resize(suggestedSize); - buf->base = &pipe.Buffer_.front(); - buf->len = pipe.Buffer_.size(); -} - -void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVData(uv_stream_t* stream, - ssize_t nread, - const uv_buf_t* buf) -{ - auto& pipe = *reinterpret_cast<PipeT*>(stream->data); - if (nread > 0) { - // Append data to merged output - if ((buf->base != nullptr) && (pipe.Target_ != nullptr)) { - pipe.Target_->append(buf->base, nread); - } - } else if (nread < 0) { - // EOF or error - auto* proc = pipe.Process_; - // Check it this an unusual error - if (nread != UV_EOF) { - if (!proc->Result()->error()) { - proc->Result()->ErrorMessage = - "libuv reading from pipe failed with error code "; - proc->Result()->ErrorMessage += std::to_string(nread); - } - } - // Clear libuv pipe handle and try to finish - pipe.reset(); - proc->UVTryFinish(); - } -} - -void cmQtAutoGenerator::ProcessResultT::reset() -{ - ExitStatus = 0; - TermSignal = 0; - if (!StdOut.empty()) { - StdOut.clear(); - StdOut.shrink_to_fit(); - } - if (!StdErr.empty()) { - StdErr.clear(); - StdErr.shrink_to_fit(); - } - if (!ErrorMessage.empty()) { - ErrorMessage.clear(); - ErrorMessage.shrink_to_fit(); - } -} - -void cmQtAutoGenerator::ReadOnlyProcessT::setup( - ProcessResultT* result, bool mergedOutput, - std::vector<std::string> const& command, std::string const& workingDirectory) -{ - Setup_.WorkingDirectory = workingDirectory; - Setup_.Command = command; - Setup_.Result = result; - Setup_.MergedOutput = mergedOutput; -} - -bool cmQtAutoGenerator::ReadOnlyProcessT::start( - uv_loop_t* uv_loop, std::function<void()>&& finishedCallback) -{ - if (IsStarted() || (Result() == nullptr)) { - return false; - } - - // Reset result before the start - Result()->reset(); - - // Fill command string pointers - if (!Setup().Command.empty()) { - CommandPtr_.reserve(Setup().Command.size() + 1); - for (std::string const& arg : Setup().Command) { - CommandPtr_.push_back(arg.c_str()); - } - CommandPtr_.push_back(nullptr); - } else { - Result()->ErrorMessage = "Empty command"; - } - - if (!Result()->error()) { - if (UVPipeOut_.init(uv_loop, this) != 0) { - Result()->ErrorMessage = "libuv stdout pipe initialization failed"; - } - } - if (!Result()->error()) { - if (UVPipeErr_.init(uv_loop, this) != 0) { - Result()->ErrorMessage = "libuv stderr pipe initialization failed"; - } - } - if (!Result()->error()) { - // -- Setup process stdio options - // stdin - UVOptionsStdIO_[0].flags = UV_IGNORE; - UVOptionsStdIO_[0].data.stream = nullptr; - // stdout - UVOptionsStdIO_[1].flags = - static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); - UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream(); - // stderr - UVOptionsStdIO_[2].flags = - static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); - UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream(); - - // -- Setup process options - std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0); - UVOptions_.exit_cb = &ReadOnlyProcessT::UVExit; - UVOptions_.file = CommandPtr_[0]; - UVOptions_.args = const_cast<char**>(&CommandPtr_.front()); - UVOptions_.cwd = Setup_.WorkingDirectory.c_str(); - UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE; - UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size()); - UVOptions_.stdio = &UVOptionsStdIO_.front(); - - // -- Spawn process - if (UVProcess_.spawn(*uv_loop, UVOptions_, this) != 0) { - Result()->ErrorMessage = "libuv process spawn failed"; - } - } - // -- Start reading from stdio streams - if (!Result()->error()) { - if (UVPipeOut_.startRead(&Result()->StdOut) != 0) { - Result()->ErrorMessage = "libuv start reading from stdout pipe failed"; - } - } - if (!Result()->error()) { - if (UVPipeErr_.startRead(Setup_.MergedOutput ? &Result()->StdOut - : &Result()->StdErr) != 0) { - Result()->ErrorMessage = "libuv start reading from stderr pipe failed"; - } - } - - if (!Result()->error()) { - IsStarted_ = true; - FinishedCallback_ = std::move(finishedCallback); - } else { - // Clear libuv handles and finish - UVProcess_.reset(); - UVPipeOut_.reset(); - UVPipeErr_.reset(); - CommandPtr_.clear(); - } - - return IsStarted(); -} - -void cmQtAutoGenerator::ReadOnlyProcessT::UVExit(uv_process_t* handle, - int64_t exitStatus, - int termSignal) -{ - auto& proc = *reinterpret_cast<ReadOnlyProcessT*>(handle->data); - if (proc.IsStarted() && !proc.IsFinished()) { - // Set error message on demand - proc.Result()->ExitStatus = exitStatus; - proc.Result()->TermSignal = termSignal; - if (!proc.Result()->error()) { - if (termSignal != 0) { - proc.Result()->ErrorMessage = "Process was terminated by signal "; - proc.Result()->ErrorMessage += - std::to_string(proc.Result()->TermSignal); - } else if (exitStatus != 0) { - proc.Result()->ErrorMessage = "Process failed with return value "; - proc.Result()->ErrorMessage += - std::to_string(proc.Result()->ExitStatus); - } - } - - // Reset process handle and try to finish - proc.UVProcess_.reset(); - proc.UVTryFinish(); - } -} - -void cmQtAutoGenerator::ReadOnlyProcessT::UVTryFinish() -{ - // There still might be data in the pipes after the process has finished. - // Therefore check if the process is finished AND all pipes are closed - // before signaling the worker thread to continue. - if (UVProcess_.get() == nullptr) { - if (UVPipeOut_.uv_pipe() == nullptr) { - if (UVPipeErr_.uv_pipe() == nullptr) { - IsFinished_ = true; - FinishedCallback_(); - } - } - } + std::lock_guard<std::mutex> lock(Mutex_); + return cmQtAutoGenerator::MakeParentDirectory(filename); } -cmQtAutoGenerator::cmQtAutoGenerator() - : FileSys_(&Logger_) -{ - // Initialize logger - { - std::string verbose; - if (cmSystemTools::GetEnv("VERBOSE", verbose) && !verbose.empty()) { - unsigned long iVerbose = 0; - if (cmSystemTools::StringToULong(verbose.c_str(), &iVerbose)) { - Logger_.SetVerbosity(static_cast<unsigned int>(iVerbose)); - } else { - // Non numeric verbosity - Logger_.SetVerbose(cmSystemTools::IsOn(verbose)); - } - } - } - { - std::string colorEnv; - cmSystemTools::GetEnv("COLOR", colorEnv); - if (!colorEnv.empty()) { - Logger_.SetColorOutput(cmSystemTools::IsOn(colorEnv)); - } else { - Logger_.SetColorOutput(true); - } - } - - // Initialize libuv loop - uv_disable_stdio_inheritance(); -#ifdef CMAKE_UV_SIGNAL_HACK - UVHackRAII_ = cm::make_unique<cmUVSignalHackRAII>(); -#endif - UVLoop_ = cm::make_unique<uv_loop_t>(); - uv_loop_init(UVLoop()); -} +cmQtAutoGenerator::cmQtAutoGenerator() = default; -cmQtAutoGenerator::~cmQtAutoGenerator() -{ - // Close libuv loop - uv_loop_close(UVLoop()); -} +cmQtAutoGenerator::~cmQtAutoGenerator() = default; bool cmQtAutoGenerator::Run(std::string const& infoFile, std::string const& config) diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index 9956a99..437fa20 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -7,15 +7,8 @@ #include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" -#include "cmUVHandlePtr.h" -#include "cmUVSignalHackRAII.h" // IWYU pragma: keep -#include "cm_uv.h" -#include <array> -#include <functional> #include <mutex> -#include <stddef.h> -#include <stdint.h> #include <string> #include <vector> @@ -31,25 +24,29 @@ public: class Logger { public: + // -- Construction + Logger(); + ~Logger(); // -- Verbosity unsigned int Verbosity() const { return this->Verbosity_; } void SetVerbosity(unsigned int value) { this->Verbosity_ = value; } void RaiseVerbosity(std::string const& value); bool Verbose() const { return (this->Verbosity_ != 0); } void SetVerbose(bool value) { this->Verbosity_ = value ? 1 : 0; } + // -- Color output bool ColorOutput() const { return this->ColorOutput_; } void SetColorOutput(bool value); // -- Log info - void Info(GeneratorT genType, std::string const& message); + void Info(GenT genType, std::string const& message); // -- Log warning - void Warning(GeneratorT genType, std::string const& message); - void WarningFile(GeneratorT genType, std::string const& filename, + void Warning(GenT genType, std::string const& message); + void WarningFile(GenT genType, std::string const& filename, std::string const& message); // -- Log error - void Error(GeneratorT genType, std::string const& message); - void ErrorFile(GeneratorT genType, std::string const& filename, + void Error(GenT genType, std::string const& message); + void ErrorFile(GenT genType, std::string const& filename, std::string const& message); - void ErrorCommand(GeneratorT genType, std::string const& message, + void ErrorCommand(GenT genType, std::string const& message, std::vector<std::string> const& command, std::string const& output); @@ -62,24 +59,27 @@ public: bool ColorOutput_ = false; }; + // -- File system methods + static bool MakeParentDirectory(std::string const& filename); + static bool FileRead(std::string& content, std::string const& filename, + std::string* error = nullptr); + static bool FileWrite(std::string const& filename, + std::string const& content, + std::string* error = nullptr); + /// @brief Thread safe file system interface class FileSystem { public: - FileSystem(Logger* log) - : Log_(log) - { - } - - /// @brief Logger - Logger* Log() const { return Log_; } + FileSystem(); + ~FileSystem(); // -- Paths /// @brief Wrapper for cmSystemTools::GetRealPath std::string GetRealPath(std::string const& filename); - /// @brief Wrapper for cmSystemTools::CollapseCombinedPath - std::string CollapseCombinedPath(std::string const& dir, - std::string const& file); + /// @brief Wrapper for cmSystemTools::CollapseFullPath + std::string CollapseFullPath(std::string const& file, + std::string const& dir); /// @brief Wrapper for cmSystemTools::SplitPath void SplitPath(const std::string& p, std::vector<std::string>& components, bool expand_home_dir = true); @@ -113,15 +113,9 @@ public: bool FileRead(std::string& content, std::string const& filename, std::string* error = nullptr); - /// @brief Error logging version - bool FileRead(GeneratorT genType, std::string& content, - std::string const& filename); bool FileWrite(std::string const& filename, std::string const& content, std::string* error = nullptr); - /// @brief Error logging version - bool FileWrite(GeneratorT genType, std::string const& filename, - std::string const& content); bool FileDiffers(std::string const& filename, std::string const& content); @@ -130,113 +124,11 @@ public: // -- Directory access bool MakeDirectory(std::string const& dirname); - /// @brief Error logging version - bool MakeDirectory(GeneratorT genType, std::string const& dirname); - bool MakeParentDirectory(std::string const& filename); - /// @brief Error logging version - bool MakeParentDirectory(GeneratorT genType, std::string const& filename); private: std::mutex Mutex_; cmFilePathChecksum FilePathChecksum_; - Logger* Log_; - }; - - /// @brief Return value and output of an external process - struct ProcessResultT - { - void reset(); - bool error() const - { - return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty(); - } - - std::int64_t ExitStatus = 0; - int TermSignal = 0; - std::string StdOut; - std::string StdErr; - std::string ErrorMessage; - }; - - /// @brief External process management class - struct ReadOnlyProcessT - { - // -- Types - - /// @brief libuv pipe buffer class - class PipeT - { - public: - int init(uv_loop_t* uv_loop, ReadOnlyProcessT* process); - int startRead(std::string* target); - void reset(); - - // -- Libuv casts - uv_pipe_t* uv_pipe() { return UVPipe_.get(); } - uv_stream_t* uv_stream() - { - return reinterpret_cast<uv_stream_t*>(uv_pipe()); - } - uv_handle_t* uv_handle() - { - return reinterpret_cast<uv_handle_t*>(uv_pipe()); - } - - // -- Libuv callbacks - static void UVAlloc(uv_handle_t* handle, size_t suggestedSize, - uv_buf_t* buf); - static void UVData(uv_stream_t* stream, ssize_t nread, - const uv_buf_t* buf); - - private: - ReadOnlyProcessT* Process_ = nullptr; - std::string* Target_ = nullptr; - std::vector<char> Buffer_; - cm::uv_pipe_ptr UVPipe_; - }; - - /// @brief Process settings - struct SetupT - { - std::string WorkingDirectory; - std::vector<std::string> Command; - ProcessResultT* Result = nullptr; - bool MergedOutput = false; - }; - - // -- Const accessors - const SetupT& Setup() const { return Setup_; } - ProcessResultT* Result() const { return Setup_.Result; } - bool IsStarted() const { return IsStarted_; } - bool IsFinished() const { return IsFinished_; } - - // -- Runtime - void setup(ProcessResultT* result, bool mergedOutput, - std::vector<std::string> const& command, - std::string const& workingDirectory = std::string()); - bool start(uv_loop_t* uv_loop, std::function<void()>&& finishedCallback); - - private: - // -- Friends - friend class PipeT; - // -- Libuv callbacks - static void UVExit(uv_process_t* handle, int64_t exitStatus, - int termSignal); - void UVTryFinish(); - - // -- Setup - SetupT Setup_; - // -- Runtime - bool IsStarted_ = false; - bool IsFinished_ = false; - std::function<void()> FinishedCallback_; - std::vector<const char*> CommandPtr_; - std::array<uv_stdio_container_t, 3> UVOptionsStdIO_; - uv_process_options_t UVOptions_; - cm::uv_process_ptr UVProcess_; - PipeT UVPipeOut_; - PipeT UVPipeErr_; }; public: @@ -250,18 +142,10 @@ public: // -- Run bool Run(std::string const& infoFile, std::string const& config); - // -- Accessors - // Logging - Logger& Log() { return Logger_; } - // File System - FileSystem& FileSys() { return FileSys_; } // InfoFile std::string const& InfoFile() const { return InfoFile_; } std::string const& InfoDir() const { return InfoDir_; } std::string const& InfoConfig() const { return InfoConfig_; } - // libuv loop - uv_loop_t* UVLoop() { return UVLoop_.get(); } - cm::uv_async_ptr& UVRequest() { return UVRequest_; } // -- Utility static std::string SettingsFind(std::string const& content, const char* key); @@ -272,19 +156,10 @@ protected: virtual bool Process() = 0; private: - // -- Logging - Logger Logger_; - FileSystem FileSys_; // -- Info settings std::string InfoFile_; std::string InfoDir_; std::string InfoConfig_; -// -- libuv loop -#ifdef CMAKE_UV_SIGNAL_HACK - std::unique_ptr<cmUVSignalHackRAII> UVHackRAII_; -#endif - std::unique_ptr<uv_loop_t> UVLoop_; - cm::uv_async_ptr UVRequest_; }; #endif diff --git a/Source/cmQtAutoGeneratorRcc.cxx b/Source/cmQtAutoGeneratorRcc.cxx deleted file mode 100644 index 021a15f..0000000 --- a/Source/cmQtAutoGeneratorRcc.cxx +++ /dev/null @@ -1,672 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmQtAutoGeneratorRcc.h" -#include "cmQtAutoGen.h" - -#include "cmAlgorithms.h" -#include "cmCryptoHash.h" -#include "cmFileLockResult.h" -#include "cmMakefile.h" -#include "cmSystemTools.h" -#include "cmUVHandlePtr.h" - -// -- Class methods - -cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc() -{ - // Initialize libuv asynchronous iteration request - UVRequest().init(*UVLoop(), &cmQtAutoGeneratorRcc::UVPollStage, this); -} - -cmQtAutoGeneratorRcc::~cmQtAutoGeneratorRcc() = default; - -bool cmQtAutoGeneratorRcc::Init(cmMakefile* makefile) -{ - // -- Utility lambdas - auto InfoGet = [makefile](std::string const& key) { - return makefile->GetSafeDefinition(key); - }; - auto InfoGetList = - [makefile](std::string const& key) -> std::vector<std::string> { - std::vector<std::string> list; - cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); - return list; - }; - auto InfoGetConfig = [makefile, - this](std::string const& key) -> std::string { - const char* valueConf = nullptr; - { - std::string keyConf = key; - keyConf += '_'; - keyConf += InfoConfig(); - valueConf = makefile->GetDefinition(keyConf); - } - if (valueConf == nullptr) { - return makefile->GetSafeDefinition(key); - } - return std::string(valueConf); - }; - auto InfoGetConfigList = - [&InfoGetConfig](std::string const& key) -> std::vector<std::string> { - std::vector<std::string> list; - cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); - return list; - }; - - // -- Read info file - if (!makefile->ReadListFile(InfoFile())) { - Log().ErrorFile(GeneratorT::RCC, InfoFile(), "File processing failed"); - return false; - } - - // - Configurations - Log().RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); - MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG"); - - // - Directories - AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR"); - if (AutogenBuildDir_.empty()) { - Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Build directory empty"); - return false; - } - - IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR"); - if (IncludeDir_.empty()) { - Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Include directory empty"); - return false; - } - - // - Rcc executable - RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE"); - RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS"); - - // - Job - LockFile_ = InfoGet("ARCC_LOCK_FILE"); - QrcFile_ = InfoGet("ARCC_SOURCE"); - QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_); - QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_); - RccPathChecksum_ = InfoGet("ARCC_OUTPUT_CHECKSUM"); - RccFileName_ = InfoGet("ARCC_OUTPUT_NAME"); - Options_ = InfoGetConfigList("ARCC_OPTIONS"); - Inputs_ = InfoGetList("ARCC_INPUTS"); - - // - Settings file - SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE"); - - // - Validity checks - if (LockFile_.empty()) { - Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Lock file name missing"); - return false; - } - if (SettingsFile_.empty()) { - Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Settings file name missing"); - return false; - } - if (AutogenBuildDir_.empty()) { - Log().ErrorFile(GeneratorT::RCC, InfoFile(), - "Autogen build directory missing"); - return false; - } - if (RccExecutable_.empty()) { - Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc executable missing"); - return false; - } - if (QrcFile_.empty()) { - Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc input file missing"); - return false; - } - if (RccFileName_.empty()) { - Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc output file missing"); - return false; - } - - // Init derived information - // ------------------------ - - RccFilePublic_ = AutogenBuildDir_; - RccFilePublic_ += '/'; - RccFilePublic_ += RccPathChecksum_; - RccFilePublic_ += '/'; - RccFilePublic_ += RccFileName_; - - // Compute rcc output file name - if (IsMultiConfig()) { - RccFileOutput_ = IncludeDir_; - RccFileOutput_ += '/'; - RccFileOutput_ += MultiConfigOutput(); - } else { - RccFileOutput_ = RccFilePublic_; - } - - return true; -} - -bool cmQtAutoGeneratorRcc::Process() -{ - // Run libuv event loop - UVRequest().send(); - if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) { - if (Error_) { - return false; - } - } else { - return false; - } - return true; -} - -void cmQtAutoGeneratorRcc::UVPollStage(uv_async_t* handle) -{ - reinterpret_cast<cmQtAutoGeneratorRcc*>(handle->data)->PollStage(); -} - -void cmQtAutoGeneratorRcc::PollStage() -{ - switch (Stage_) { - // -- Initialize - case StageT::SETTINGS_READ: - if (SettingsFileRead()) { - SetStage(StageT::TEST_QRC_RCC_FILES); - } else { - SetStage(StageT::FINISH); - } - break; - - // -- Change detection - case StageT::TEST_QRC_RCC_FILES: - if (TestQrcRccFiles()) { - SetStage(StageT::GENERATE); - } else { - SetStage(StageT::TEST_RESOURCES_READ); - } - break; - case StageT::TEST_RESOURCES_READ: - if (TestResourcesRead()) { - SetStage(StageT::TEST_RESOURCES); - } - break; - case StageT::TEST_RESOURCES: - if (TestResources()) { - SetStage(StageT::GENERATE); - } else { - SetStage(StageT::TEST_INFO_FILE); - } - break; - case StageT::TEST_INFO_FILE: - TestInfoFile(); - SetStage(StageT::GENERATE_WRAPPER); - break; - - // -- Generation - case StageT::GENERATE: - GenerateParentDir(); - SetStage(StageT::GENERATE_RCC); - break; - case StageT::GENERATE_RCC: - if (GenerateRcc()) { - SetStage(StageT::GENERATE_WRAPPER); - } - break; - case StageT::GENERATE_WRAPPER: - GenerateWrapper(); - SetStage(StageT::SETTINGS_WRITE); - break; - - // -- Finalize - case StageT::SETTINGS_WRITE: - SettingsFileWrite(); - SetStage(StageT::FINISH); - break; - case StageT::FINISH: - // Clear all libuv handles - UVRequest().reset(); - // Set highest END stage manually - Stage_ = StageT::END; - break; - case StageT::END: - break; - } -} - -void cmQtAutoGeneratorRcc::SetStage(StageT stage) -{ - if (Error_) { - stage = StageT::FINISH; - } - // Only allow to increase the stage - if (Stage_ < stage) { - Stage_ = stage; - UVRequest().send(); - } -} - -std::string cmQtAutoGeneratorRcc::MultiConfigOutput() const -{ - static std::string const suffix = "_CMAKE_"; - std::string res; - res += RccPathChecksum_; - res += '/'; - res += AppendFilenameSuffix(RccFileName_, suffix); - return res; -} - -bool cmQtAutoGeneratorRcc::SettingsFileRead() -{ - // Compose current settings strings - { - cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); - std::string const sep(" ~~~ "); - { - std::string str; - str += RccExecutable_; - str += sep; - str += cmJoin(RccListOptions_, ";"); - str += sep; - str += QrcFile_; - str += sep; - str += RccPathChecksum_; - str += sep; - str += RccFileName_; - str += sep; - str += cmJoin(Options_, ";"); - str += sep; - str += cmJoin(Inputs_, ";"); - str += sep; - SettingsString_ = crypt.HashString(str); - } - } - - // Make sure the settings file exists - if (!FileSys().FileExists(SettingsFile_, true)) { - // Touch the settings file to make sure it exists - FileSys().Touch(SettingsFile_, true); - } - - // Lock the lock file - { - // Make sure the lock file exists - if (!FileSys().FileExists(LockFile_, true)) { - if (!FileSys().Touch(LockFile_, true)) { - Log().ErrorFile(GeneratorT::RCC, LockFile_, - "Lock file creation failed"); - Error_ = true; - return false; - } - } - // Lock the lock file - cmFileLockResult lockResult = - LockFileLock_.Lock(LockFile_, static_cast<unsigned long>(-1)); - if (!lockResult.IsOk()) { - Log().ErrorFile(GeneratorT::RCC, LockFile_, - "File lock failed: " + lockResult.GetOutputMessage()); - Error_ = true; - return false; - } - } - - // Read old settings - { - std::string content; - if (FileSys().FileRead(content, SettingsFile_)) { - SettingsChanged_ = (SettingsString_ != SettingsFind(content, "rcc")); - // In case any setting changed clear the old settings file. - // This triggers a full rebuild on the next run if the current - // build is aborted before writing the current settings in the end. - if (SettingsChanged_) { - FileSys().FileWrite(GeneratorT::RCC, SettingsFile_, ""); - } - } else { - SettingsChanged_ = true; - } - } - - return true; -} - -void cmQtAutoGeneratorRcc::SettingsFileWrite() -{ - // Only write if any setting changed - if (SettingsChanged_) { - if (Log().Verbose()) { - Log().Info(GeneratorT::RCC, - "Writing settings file " + Quoted(SettingsFile_)); - } - // Write settings file - std::string content = "rcc:"; - content += SettingsString_; - content += '\n'; - if (!FileSys().FileWrite(GeneratorT::RCC, SettingsFile_, content)) { - Log().ErrorFile(GeneratorT::RCC, SettingsFile_, - "Settings file writing failed"); - // Remove old settings file to trigger a full rebuild on the next run - FileSys().FileRemove(SettingsFile_); - Error_ = true; - } - } - - // Unlock the lock file - LockFileLock_.Release(); -} - -bool cmQtAutoGeneratorRcc::TestQrcRccFiles() -{ - // Do basic checks if rcc generation is required - - // Test if the rcc output file exists - if (!FileSys().FileExists(RccFileOutput_)) { - if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(RccFileOutput_); - reason += " from its source file "; - reason += Quoted(QrcFile_); - reason += " because it doesn't exist"; - Log().Info(GeneratorT::RCC, reason); - } - Generate_ = true; - return Generate_; - } - - // Test if the settings changed - if (SettingsChanged_) { - if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(RccFileOutput_); - reason += " from "; - reason += Quoted(QrcFile_); - reason += " because the RCC settings changed"; - Log().Info(GeneratorT::RCC, reason); - } - Generate_ = true; - return Generate_; - } - - // Test if the rcc output file is older than the .qrc file - { - bool isOlder = false; - { - std::string error; - isOlder = FileSys().FileIsOlderThan(RccFileOutput_, QrcFile_, &error); - if (!error.empty()) { - Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); - Error_ = true; - } - } - if (isOlder) { - if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(RccFileOutput_); - reason += " because it is older than "; - reason += Quoted(QrcFile_); - Log().Info(GeneratorT::RCC, reason); - } - Generate_ = true; - } - } - - return Generate_; -} - -bool cmQtAutoGeneratorRcc::TestResourcesRead() -{ - if (!Inputs_.empty()) { - // Inputs are known already - return true; - } - - if (!RccListOptions_.empty()) { - // Start a rcc list process and parse the output - if (Process_) { - // Process is running already - if (Process_->IsFinished()) { - // Process is finished - if (!ProcessResult_.error()) { - // Process success - std::string parseError; - if (!RccListParseOutput(ProcessResult_.StdOut, ProcessResult_.StdErr, - Inputs_, parseError)) { - Log().ErrorFile(GeneratorT::RCC, QrcFile_, parseError); - Error_ = true; - } - } else { - Log().ErrorFile(GeneratorT::RCC, QrcFile_, - ProcessResult_.ErrorMessage); - Error_ = true; - } - // Clean up - Process_.reset(); - ProcessResult_.reset(); - } else { - // Process is not finished, yet. - return false; - } - } else { - // Start a new process - // rcc prints relative entry paths when started in the directory of the - // qrc file with a pathless qrc file name argument. - // This is important because on Windows absolute paths returned by rcc - // might contain bad multibyte characters when the qrc file path - // contains non-ASCII pcharacters. - std::vector<std::string> cmd; - cmd.push_back(RccExecutable_); - cmd.insert(cmd.end(), RccListOptions_.begin(), RccListOptions_.end()); - cmd.push_back(QrcFileName_); - // We're done here if the process fails to start - return !StartProcess(QrcFileDir_, cmd, false); - } - } else { - // rcc does not support the --list command. - // Read the qrc file content and parse it. - std::string qrcContent; - if (FileSys().FileRead(GeneratorT::RCC, qrcContent, QrcFile_)) { - RccListParseContent(qrcContent, Inputs_); - } - } - - if (!Inputs_.empty()) { - // Convert relative paths to absolute paths - RccListConvertFullPath(QrcFileDir_, Inputs_); - } - - return true; -} - -bool cmQtAutoGeneratorRcc::TestResources() -{ - if (Inputs_.empty()) { - return true; - } - { - std::string error; - for (std::string const& resFile : Inputs_) { - // Check if the resource file exists - if (!FileSys().FileExists(resFile)) { - error = "Could not find the resource file\n "; - error += Quoted(resFile); - error += '\n'; - Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); - Error_ = true; - break; - } - // Check if the resource file is newer than the build file - if (FileSys().FileIsOlderThan(RccFileOutput_, resFile, &error)) { - if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(RccFileOutput_); - reason += " from "; - reason += Quoted(QrcFile_); - reason += " because it is older than "; - reason += Quoted(resFile); - Log().Info(GeneratorT::RCC, reason); - } - Generate_ = true; - break; - } - // Print error and break on demand - if (!error.empty()) { - Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); - Error_ = true; - break; - } - } - } - - return Generate_; -} - -void cmQtAutoGeneratorRcc::TestInfoFile() -{ - // Test if the rcc output file is older than the info file - { - bool isOlder = false; - { - std::string error; - isOlder = FileSys().FileIsOlderThan(RccFileOutput_, InfoFile(), &error); - if (!error.empty()) { - Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); - Error_ = true; - } - } - if (isOlder) { - if (Log().Verbose()) { - std::string reason = "Touching "; - reason += Quoted(RccFileOutput_); - reason += " because it is older than "; - reason += Quoted(InfoFile()); - Log().Info(GeneratorT::RCC, reason); - } - // Touch build file - FileSys().Touch(RccFileOutput_); - BuildFileChanged_ = true; - } - } -} - -void cmQtAutoGeneratorRcc::GenerateParentDir() -{ - // Make sure the parent directory exists - if (!FileSys().MakeParentDirectory(GeneratorT::RCC, RccFileOutput_)) { - Error_ = true; - } -} - -/** - * @return True when finished - */ -bool cmQtAutoGeneratorRcc::GenerateRcc() -{ - if (!Generate_) { - // Nothing to do - return true; - } - - if (Process_) { - // Process is running already - if (Process_->IsFinished()) { - // Process is finished - if (!ProcessResult_.error()) { - // Rcc process success - // Print rcc output - if (!ProcessResult_.StdOut.empty()) { - Log().Info(GeneratorT::RCC, ProcessResult_.StdOut); - } - BuildFileChanged_ = true; - } else { - // Rcc process failed - { - std::string emsg = "The rcc process failed to compile\n "; - emsg += Quoted(QrcFile_); - emsg += "\ninto\n "; - emsg += Quoted(RccFileOutput_); - if (ProcessResult_.error()) { - emsg += "\n"; - emsg += ProcessResult_.ErrorMessage; - } - Log().ErrorCommand(GeneratorT::RCC, emsg, Process_->Setup().Command, - ProcessResult_.StdOut); - } - FileSys().FileRemove(RccFileOutput_); - Error_ = true; - } - // Clean up - Process_.reset(); - ProcessResult_.reset(); - } else { - // Process is not finished, yet. - return false; - } - } else { - // Start a rcc process - std::vector<std::string> cmd; - cmd.push_back(RccExecutable_); - cmd.insert(cmd.end(), Options_.begin(), Options_.end()); - cmd.emplace_back("-o"); - cmd.push_back(RccFileOutput_); - cmd.push_back(QrcFile_); - // We're done here if the process fails to start - return !StartProcess(AutogenBuildDir_, cmd, true); - } - - return true; -} - -void cmQtAutoGeneratorRcc::GenerateWrapper() -{ - // Generate a wrapper source file on demand - if (IsMultiConfig()) { - // Wrapper file content - std::string content; - content += "// This is an autogenerated configuration wrapper file.\n"; - content += "// Changes will be overwritten.\n"; - content += "#include <"; - content += MultiConfigOutput(); - content += ">\n"; - - // Write content to file - if (FileSys().FileDiffers(RccFilePublic_, content)) { - // Write new wrapper file - if (Log().Verbose()) { - Log().Info(GeneratorT::RCC, - "Generating RCC wrapper file " + RccFilePublic_); - } - if (!FileSys().FileWrite(GeneratorT::RCC, RccFilePublic_, content)) { - Log().ErrorFile(GeneratorT::RCC, RccFilePublic_, - "RCC wrapper file writing failed"); - Error_ = true; - } - } else if (BuildFileChanged_) { - // Just touch the wrapper file - if (Log().Verbose()) { - Log().Info(GeneratorT::RCC, - "Touching RCC wrapper file " + RccFilePublic_); - } - FileSys().Touch(RccFilePublic_); - } - } -} - -bool cmQtAutoGeneratorRcc::StartProcess( - std::string const& workingDirectory, std::vector<std::string> const& command, - bool mergedOutput) -{ - // Log command - if (Log().Verbose()) { - std::string msg = "Running command:\n"; - msg += QuotedCommand(command); - msg += '\n'; - Log().Info(GeneratorT::RCC, msg); - } - - // Create process handler - Process_ = cm::make_unique<ReadOnlyProcessT>(); - Process_->setup(&ProcessResult_, mergedOutput, command, workingDirectory); - // Start process - if (!Process_->start(UVLoop(), [this] { UVRequest().send(); })) { - Log().ErrorFile(GeneratorT::RCC, QrcFile_, ProcessResult_.ErrorMessage); - Error_ = true; - // Clean up - Process_.reset(); - ProcessResult_.reset(); - return false; - } - return true; -} diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoMocUic.cxx index ddff4cf..005c27d 100644 --- a/Source/cmQtAutoGeneratorMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -1,11 +1,10 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmQtAutoGeneratorMocUic.h" +#include "cmQtAutoMocUic.h" #include <algorithm> #include <array> -#include <cstddef> -#include <functional> +#include <deque> #include <list> #include <memory> #include <set> @@ -25,10 +24,10 @@ // -- Class methods -std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath( +std::string cmQtAutoMocUic::BaseSettingsT::AbsoluteBuildPath( std::string const& relativePath) const { - return FileSys->CollapseCombinedPath(AutogenBuildDir, relativePath); + return FileSys->CollapseFullPath(relativePath, AutogenBuildDir); } /** @@ -36,7 +35,7 @@ std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath( * appending different header extensions * @return True on success */ -bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader( +bool cmQtAutoMocUic::BaseSettingsT::FindHeader( std::string& header, std::string const& testBasePath) const { for (std::string const& ext : HeaderExtensions) { @@ -51,8 +50,7 @@ bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader( return false; } -bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped( - std::string const& fileName) const +bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const { return (!Enabled || (SkipList.find(fileName) != SkipList.end())); } @@ -61,7 +59,7 @@ bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped( * @brief Returns the first relevant Qt macro name found in the given C++ code * @return The name of the Qt macro or an empty string */ -std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro( +std::string cmQtAutoMocUic::MocSettingsT::FindMacro( std::string const& content) const { for (KeyExpT const& filter : MacroFilters) { @@ -78,7 +76,7 @@ std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro( return std::string(); } -std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const +std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const { std::string res; const auto itB = MacroFilters.cbegin(); @@ -100,7 +98,7 @@ std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const return res; } -std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile( +std::string cmQtAutoMocUic::MocSettingsT::FindIncludedFile( std::string const& sourcePath, std::string const& includeString) const { // Search in vicinity of the source @@ -124,7 +122,7 @@ std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile( return std::string(); } -void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies( +void cmQtAutoMocUic::MocSettingsT::FindDependencies( std::string const& content, std::set<std::string>& depends) const { if (!DependFilters.empty() && !content.empty()) { @@ -148,17 +146,124 @@ void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies( } } -bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped( - std::string const& fileName) const +bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const { return (!Enabled || (SkipList.find(fileName) != SkipList.end())); } -void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk) +void cmQtAutoMocUic::JobT::LogError(GenT genType, + std::string const& message) const +{ + Gen()->AbortError(); + Gen()->Log().Error(genType, message); +} + +void cmQtAutoMocUic::JobT::LogFileError(GenT genType, + std::string const& filename, + std::string const& message) const +{ + Gen()->AbortError(); + Gen()->Log().ErrorFile(genType, filename, message); +} + +void cmQtAutoMocUic::JobT::LogCommandError( + GenT genType, std::string const& message, + std::vector<std::string> const& command, std::string const& output) const +{ + Gen()->AbortError(); + Gen()->Log().ErrorCommand(genType, message, command, output); +} + +bool cmQtAutoMocUic::JobT::RunProcess(GenT genType, + cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command) +{ + // Log command + if (Log().Verbose()) { + std::string msg = "Running command:\n"; + msg += QuotedCommand(command); + msg += '\n'; + Log().Info(genType, msg); + } + return cmWorkerPool::JobT::RunProcess(result, command, + Gen()->Base().AutogenBuildDir); +} + +void cmQtAutoMocUic::JobMocPredefsT::Process() +{ + // (Re)generate moc_predefs.h on demand + bool generate(false); + bool fileExists(FileSys().FileExists(Gen()->Moc().PredefsFileAbs)); + if (!fileExists) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(Gen()->Moc().PredefsFileRel); + reason += " because it doesn't exist"; + Log().Info(GenT::MOC, reason); + } + generate = true; + } else if (Gen()->Moc().SettingsChanged) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(Gen()->Moc().PredefsFileRel); + reason += " because the settings changed."; + Log().Info(GenT::MOC, reason); + } + generate = true; + } + if (generate) { + cmWorkerPool::ProcessResultT result; + { + // Compose command + std::vector<std::string> cmd = Gen()->Moc().PredefsCmd; + // Add includes + cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(), + Gen()->Moc().Includes.end()); + // Add definitions + for (std::string const& def : Gen()->Moc().Definitions) { + cmd.push_back("-D" + def); + } + // Execute command + if (!RunProcess(GenT::MOC, result, cmd)) { + std::string emsg = "The content generation command for "; + emsg += Quoted(Gen()->Moc().PredefsFileRel); + emsg += " failed.\n"; + emsg += result.ErrorMessage; + LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); + } + } + + // (Re)write predefs file only on demand + if (!result.error()) { + if (!fileExists || + FileSys().FileDiffers(Gen()->Moc().PredefsFileAbs, result.StdOut)) { + if (FileSys().FileWrite(Gen()->Moc().PredefsFileAbs, result.StdOut)) { + // Success + } else { + std::string emsg = "Writing "; + emsg += Quoted(Gen()->Moc().PredefsFileRel); + emsg += " failed."; + LogFileError(GenT::MOC, Gen()->Moc().PredefsFileAbs, emsg); + } + } else { + // Touch to update the time stamp + if (Log().Verbose()) { + std::string msg = "Touching "; + msg += Quoted(Gen()->Moc().PredefsFileRel); + msg += "."; + Log().Info(GenT::MOC, msg); + } + FileSys().Touch(Gen()->Moc().PredefsFileAbs); + } + } + } +} + +void cmQtAutoMocUic::JobParseT::Process() { if (AutoMoc && Header) { // Don't parse header for moc if the file is included by a source already - if (wrk.Gen().ParallelMocIncluded(FileName)) { + if (Gen()->ParallelMocIncluded(FileName)) { AutoMoc = false; } } @@ -166,36 +271,32 @@ void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk) if (AutoMoc || AutoUic) { std::string error; MetaT meta; - if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) { + if (FileSys().FileRead(meta.Content, FileName, &error)) { if (!meta.Content.empty()) { - meta.FileDir = wrk.FileSys().SubDirPrefix(FileName); - meta.FileBase = - wrk.FileSys().GetFilenameWithoutLastExtension(FileName); + meta.FileDir = FileSys().SubDirPrefix(FileName); + meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName); bool success = true; if (AutoMoc) { if (Header) { - success = ParseMocHeader(wrk, meta); + success = ParseMocHeader(meta); } else { - success = ParseMocSource(wrk, meta); + success = ParseMocSource(meta); } } if (AutoUic && success) { - ParseUic(wrk, meta); + ParseUic(meta); } } else { - wrk.LogFileWarning(GeneratorT::GEN, FileName, - "The source file is empty"); + Log().WarningFile(GenT::GEN, FileName, "The source file is empty"); } } else { - wrk.LogFileError(GeneratorT::GEN, FileName, - "Could not read the file: " + error); + LogFileError(GenT::GEN, FileName, "Could not read the file: " + error); } } } -bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, - MetaT const& meta) +bool cmQtAutoMocUic::JobParseT::ParseMocSource(MetaT const& meta) { struct JobPre { @@ -213,7 +314,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, }; // Check if this source file contains a relevant macro - std::string const ownMacro = wrk.Moc().FindMacro(meta.Content); + std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content); // Extract moc includes from file std::deque<MocInclude> mocIncsUsc; @@ -222,11 +323,11 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, if (meta.Content.find("moc") != std::string::npos) { const char* contentChars = meta.Content.c_str(); cmsys::RegularExpressionMatch match; - while (wrk.Moc().RegExpInclude.find(contentChars, match)) { + while (Gen()->Moc().RegExpInclude.find(contentChars, match)) { std::string incString = match.match(2); - std::string incDir(wrk.FileSys().SubDirPrefix(incString)); + std::string incDir(FileSys().SubDirPrefix(incString)); std::string incBase = - wrk.FileSys().GetFilenameWithoutLastExtension(incString); + FileSys().GetFilenameWithoutLastExtension(incString); if (cmHasLiteralPrefix(incBase, "moc_")) { // moc_<BASE>.cxx // Remove the moc_ part from the base name @@ -255,10 +356,10 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // Process moc_<BASE>.cxx includes for (const MocInclude& mocInc : mocIncsUsc) { std::string const header = - MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base); + MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base); if (!header.empty()) { // Check if header is skipped - if (wrk.Moc().skipped(header)) { + if (Gen()->Moc().skipped(header)) { continue; } // Register moc job @@ -273,9 +374,9 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, std::string emsg = "The file includes the moc file "; emsg += Quoted(mocInc.Inc); emsg += ", but the header "; - emsg += Quoted(MocStringHeaders(wrk, mocInc.Base)); + emsg += Quoted(MocStringHeaders(mocInc.Base)); emsg += " could not be found."; - wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + LogFileError(GenT::MOC, FileName, emsg); } return false; } @@ -284,7 +385,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // Process <BASE>.moc includes for (const MocInclude& mocInc : mocIncsDot) { const bool ownMoc = (mocInc.Base == meta.FileBase); - if (wrk.Moc().RelaxedMode) { + if (Gen()->Moc().RelaxedMode) { // Relaxed mode if (!ownMacro.empty() && ownMoc) { // Add self @@ -294,10 +395,10 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // In relaxed mode try to find a header instead but issue a warning. // This is for KDE4 compatibility std::string const header = - MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base); + MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base); if (!header.empty()) { // Check if header is skipped - if (wrk.Moc().skipped(header)) { + if (Gen()->Moc().skipped(header)) { continue; } // Register moc job @@ -307,14 +408,14 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, std::string emsg = "The file includes the moc file "; emsg += Quoted(mocInc.Inc); emsg += ", but does not contain a "; - emsg += wrk.Moc().MacrosString(); + emsg += Gen()->Moc().MacrosString(); emsg += " macro.\nRunning moc on\n "; emsg += Quoted(header); emsg += "!\nBetter include "; emsg += Quoted("moc_" + mocInc.Base + ".cpp"); emsg += " for a compatibility with strict mode.\n" "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + Log().WarningFile(GenT::MOC, FileName, emsg); } else { std::string emsg = "The file includes the moc file "; emsg += Quoted(mocInc.Inc); @@ -326,7 +427,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, emsg += Quoted("moc_" + mocInc.Base + ".cpp"); emsg += " for compatibility with strict mode.\n" "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + Log().WarningFile(GenT::MOC, FileName, emsg); } } } else { @@ -336,9 +437,9 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, emsg += ", which seems to be the moc file from a different " "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a " "matching header "; - emsg += Quoted(MocStringHeaders(wrk, mocInc.Base)); + emsg += Quoted(MocStringHeaders(mocInc.Base)); emsg += " could not be found."; - wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + LogFileError(GenT::MOC, FileName, emsg); } return false; } @@ -354,9 +455,9 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, std::string emsg = "The file includes the moc file "; emsg += Quoted(mocInc.Inc); emsg += ", but does not contain a "; - emsg += wrk.Moc().MacrosString(); + emsg += Gen()->Moc().MacrosString(); emsg += " macro."; - wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + Log().WarningFile(GenT::MOC, FileName, emsg); } } else { // Don't allow <BASE>.moc include other than self in strict mode @@ -367,7 +468,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, "source file.\nThis is not supported. Include "; emsg += Quoted(meta.FileBase + ".moc"); emsg += " to run moc on this source file."; - wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + LogFileError(GenT::MOC, FileName, emsg); } return false; } @@ -381,7 +482,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // foo.cpp instead of foo.h, because otherwise it won't build. // But warn, since this is not how it is supposed to be used. // This is for KDE4 compatibility. - if (wrk.Moc().RelaxedMode && ownMocUscIncluded) { + if (Gen()->Moc().RelaxedMode && ownMocUscIncluded) { JobPre uscJobPre; // Remove underscore job request { @@ -410,7 +511,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, emsg += Quoted(meta.FileBase + ".moc"); emsg += " for compatibility with strict mode.\n" "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + Log().WarningFile(GenT::MOC, FileName, emsg); } // Add own source job jobs.emplace_back( @@ -425,7 +526,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, emsg += "!\nConsider to\n - add #include \""; emsg += meta.FileBase; emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; - wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + LogFileError(GenT::MOC, FileName, emsg); } return false; } @@ -433,76 +534,80 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // Convert pre jobs to actual jobs for (JobPre& jobPre : jobs) { - JobHandleT jobHandle(new JobMocT(std::move(jobPre.SourceFile), FileName, - std::move(jobPre.IncludeString))); + cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>( + std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString)); if (jobPre.self) { // Read dependencies from this source - static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); + JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle); + Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends); + jobMoc.DependsValid = true; } - if (!wrk.Gen().ParallelJobPushMoc(jobHandle)) { + if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) { return false; } } return true; } -bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(WorkerT& wrk, - MetaT const& meta) +bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta) { bool success = true; - std::string const macroName = wrk.Moc().FindMacro(meta.Content); + std::string const macroName = Gen()->Moc().FindMacro(meta.Content); if (!macroName.empty()) { - JobHandleT jobHandle( - new JobMocT(std::string(FileName), std::string(), std::string())); + cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>( + std::string(FileName), std::string(), std::string()); // Read dependencies from this source - static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); - success = wrk.Gen().ParallelJobPushMoc(jobHandle); + { + JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle); + Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends); + jobMoc.DependsValid = true; + } + success = Gen()->ParallelJobPushMoc(std::move(jobHandle)); } return success; } -std::string cmQtAutoGeneratorMocUic::JobParseT::MocStringHeaders( - WorkerT& wrk, std::string const& fileBase) const +std::string cmQtAutoMocUic::JobParseT::MocStringHeaders( + std::string const& fileBase) const { std::string res = fileBase; res += ".{"; - res += cmJoin(wrk.Base().HeaderExtensions, ","); + res += cmJoin(Gen()->Base().HeaderExtensions, ","); res += "}"; return res; } -std::string cmQtAutoGeneratorMocUic::JobParseT::MocFindIncludedHeader( - WorkerT& wrk, std::string const& includerDir, std::string const& includeBase) +std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader( + std::string const& includerDir, std::string const& includeBase) { std::string header; // Search in vicinity of the source - if (!wrk.Base().FindHeader(header, includerDir + includeBase)) { + if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) { // Search in include directories - for (std::string const& path : wrk.Moc().IncludePaths) { + for (std::string const& path : Gen()->Moc().IncludePaths) { std::string fullPath = path; fullPath.push_back('/'); fullPath += includeBase; - if (wrk.Base().FindHeader(header, fullPath)) { + if (Gen()->Base().FindHeader(header, fullPath)) { break; } } } // Sanitize if (!header.empty()) { - header = wrk.FileSys().GetRealPath(header); + header = FileSys().GetRealPath(header); } return header; } -bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk, - MetaT const& meta) +bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta) { bool success = true; if (meta.Content.find("ui_") != std::string::npos) { const char* contentChars = meta.Content.c_str(); cmsys::RegularExpressionMatch match; - while (wrk.Uic().RegExpInclude.find(contentChars, match)) { - if (!ParseUicInclude(wrk, meta, match.match(2))) { + while (Gen()->Uic().RegExpInclude.find(contentChars, match)) { + if (!ParseUicInclude(meta, match.match(2))) { success = false; break; } @@ -512,16 +617,16 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk, return success; } -bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude( - WorkerT& wrk, MetaT const& meta, std::string&& includeString) +bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta, + std::string&& includeString) { bool success = false; - std::string uiInputFile = UicFindIncludedFile(wrk, meta, includeString); + std::string uiInputFile = UicFindIncludedFile(meta, includeString); if (!uiInputFile.empty()) { - if (!wrk.Uic().skipped(uiInputFile)) { - JobHandleT jobHandle(new JobUicT(std::move(uiInputFile), FileName, - std::move(includeString))); - success = wrk.Gen().ParallelJobPushUic(jobHandle); + if (!Gen()->Uic().skipped(uiInputFile)) { + cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobUicT>( + std::move(uiInputFile), FileName, std::move(includeString)); + success = Gen()->ParallelJobPushUic(std::move(jobHandle)); } else { // A skipped file is successful success = true; @@ -530,17 +635,17 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude( return success; } -std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( - WorkerT& wrk, MetaT const& meta, std::string const& includeString) +std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile( + MetaT const& meta, std::string const& includeString) { std::string res; std::string searchFile = - wrk.FileSys().GetFilenameWithoutLastExtension(includeString).substr(3); + FileSys().GetFilenameWithoutLastExtension(includeString).substr(3); searchFile += ".ui"; // Collect search paths list std::deque<std::string> testFiles; { - std::string const searchPath = wrk.FileSys().SubDirPrefix(includeString); + std::string const searchPath = FileSys().SubDirPrefix(includeString); std::string searchFileFull; if (!searchPath.empty()) { @@ -556,12 +661,12 @@ std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( } } // AUTOUIC search paths - if (!wrk.Uic().SearchPaths.empty()) { - for (std::string const& sPath : wrk.Uic().SearchPaths) { + if (!Gen()->Uic().SearchPaths.empty()) { + for (std::string const& sPath : Gen()->Uic().SearchPaths) { testFiles.push_back((sPath + "/").append(searchFile)); } if (!searchPath.empty()) { - for (std::string const& sPath : wrk.Uic().SearchPaths) { + for (std::string const& sPath : Gen()->Uic().SearchPaths) { testFiles.push_back((sPath + "/").append(searchFileFull)); } } @@ -570,8 +675,8 @@ std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( // Search for the .ui file! for (std::string const& testFile : testFiles) { - if (wrk.FileSys().FileExists(testFile)) { - res = wrk.FileSys().GetRealPath(testFile); + if (FileSys().FileExists(testFile)) { + res = FileSys().GetRealPath(testFile); break; } } @@ -586,160 +691,140 @@ std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( emsg += Quoted(testFile); emsg += "\n"; } - wrk.LogFileError(GeneratorT::UIC, FileName, emsg); + LogFileError(GenT::UIC, FileName, emsg); } return res; } -void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process(WorkerT& wrk) +void cmQtAutoMocUic::JobPostParseT::Process() { - // (Re)generate moc_predefs.h on demand - bool generate(false); - bool fileExists(wrk.FileSys().FileExists(wrk.Moc().PredefsFileAbs)); - if (!fileExists) { - if (wrk.Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(wrk.Moc().PredefsFileRel); - reason += " because it doesn't exist"; - wrk.LogInfo(GeneratorT::MOC, reason); - } - generate = true; - } else if (wrk.Moc().SettingsChanged) { - if (wrk.Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(wrk.Moc().PredefsFileRel); - reason += " because the settings changed."; - wrk.LogInfo(GeneratorT::MOC, reason); - } - generate = true; + if (Gen()->Moc().Enabled) { + // Add mocs compilations fence job + Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>(); } - if (generate) { - ProcessResultT result; - { - // Compose command - std::vector<std::string> cmd = wrk.Moc().PredefsCmd; - // Add includes - cmd.insert(cmd.end(), wrk.Moc().Includes.begin(), - wrk.Moc().Includes.end()); - // Add definitions - for (std::string const& def : wrk.Moc().Definitions) { - cmd.push_back("-D" + def); - } - // Execute command - if (!wrk.RunProcess(GeneratorT::MOC, result, cmd)) { - std::string emsg = "The content generation command for "; - emsg += Quoted(wrk.Moc().PredefsFileRel); - emsg += " failed.\n"; - emsg += result.ErrorMessage; - wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut); - } + // Add finish job + Gen()->WorkerPool().EmplaceJob<JobFinishT>(); +} + +void cmQtAutoMocUic::JobMocsCompilationT::Process() +{ + // Compose mocs compilation file content + std::string content = + "// This file is autogenerated. Changes will be overwritten.\n"; + if (Gen()->MocAutoFiles().empty()) { + // Placeholder content + content += "// No files found that require moc or the moc files are " + "included\n"; + content += "enum some_compilers { need_more_than_nothing };\n"; + } else { + // Valid content + char const sbeg = Gen()->Base().MultiConfig ? '<' : '"'; + char const send = Gen()->Base().MultiConfig ? '>' : '"'; + for (std::string const& mocfile : Gen()->MocAutoFiles()) { + content += "#include "; + content += sbeg; + content += mocfile; + content += send; + content += '\n'; } + } - // (Re)write predefs file only on demand - if (!result.error()) { - if (!fileExists || - wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) { - if (wrk.FileSys().FileWrite(GeneratorT::MOC, wrk.Moc().PredefsFileAbs, - result.StdOut)) { - // Success - } else { - std::string emsg = "Writing "; - emsg += Quoted(wrk.Moc().PredefsFileRel); - emsg += " failed."; - wrk.LogFileError(GeneratorT::MOC, wrk.Moc().PredefsFileAbs, emsg); - } - } else { - // Touch to update the time stamp - if (wrk.Log().Verbose()) { - std::string msg = "Touching "; - msg += Quoted(wrk.Moc().PredefsFileRel); - msg += "."; - wrk.LogInfo(GeneratorT::MOC, msg); - } - wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs); - } + std::string const& compAbs = Gen()->Moc().CompFileAbs; + if (FileSys().FileDiffers(compAbs, content)) { + // Actually write mocs compilation file + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs); + } + if (!FileSys().FileWrite(compAbs, content)) { + LogFileError(GenT::MOC, compAbs, + "mocs compilation file writing failed."); + } + } else if (Gen()->MocAutoFileUpdated()) { + // Only touch mocs compilation file + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs); } + FileSys().Touch(compAbs); } } -void cmQtAutoGeneratorMocUic::JobMocT::FindDependencies( - WorkerT& wrk, std::string const& content) +void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content) { - wrk.Moc().FindDependencies(content, Depends); + Gen()->Moc().FindDependencies(content, Depends); DependsValid = true; } -void cmQtAutoGeneratorMocUic::JobMocT::Process(WorkerT& wrk) +void cmQtAutoMocUic::JobMocT::Process() { // Compute build file name if (!IncludeString.empty()) { - BuildFile = wrk.Base().AutogenIncludeDir; + BuildFile = Gen()->Base().AutogenIncludeDir; BuildFile += '/'; BuildFile += IncludeString; } else { // Relative build path - std::string relPath = wrk.FileSys().GetFilePathChecksum(SourceFile); + std::string relPath = FileSys().GetFilePathChecksum(SourceFile); relPath += "/moc_"; - relPath += wrk.FileSys().GetFilenameWithoutLastExtension(SourceFile); + relPath += FileSys().GetFilenameWithoutLastExtension(SourceFile); // Register relative file path with duplication check - relPath = wrk.Gen().ParallelMocAutoRegister(relPath); + relPath = Gen()->ParallelMocAutoRegister(relPath); // Absolute build path - if (wrk.Base().MultiConfig) { - BuildFile = wrk.Base().AutogenIncludeDir; + if (Gen()->Base().MultiConfig) { + BuildFile = Gen()->Base().AutogenIncludeDir; BuildFile += '/'; BuildFile += relPath; } else { - BuildFile = wrk.Base().AbsoluteBuildPath(relPath); + BuildFile = Gen()->Base().AbsoluteBuildPath(relPath); } } - if (UpdateRequired(wrk)) { - GenerateMoc(wrk); + if (UpdateRequired()) { + GenerateMoc(); } } -bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) +bool cmQtAutoMocUic::JobMocT::UpdateRequired() { - bool const verbose = wrk.Gen().Log().Verbose(); + bool const verbose = Log().Verbose(); // Test if the build file exists - if (!wrk.FileSys().FileExists(BuildFile)) { + if (!FileSys().FileExists(BuildFile)) { if (verbose) { std::string reason = "Generating "; reason += Quoted(BuildFile); reason += " from its source file "; reason += Quoted(SourceFile); reason += " because it doesn't exist"; - wrk.LogInfo(GeneratorT::MOC, reason); + Log().Info(GenT::MOC, reason); } return true; } // Test if any setting changed - if (wrk.Moc().SettingsChanged) { + if (Gen()->Moc().SettingsChanged) { if (verbose) { std::string reason = "Generating "; reason += Quoted(BuildFile); reason += " from "; reason += Quoted(SourceFile); reason += " because the MOC settings changed"; - wrk.LogInfo(GeneratorT::MOC, reason); + Log().Info(GenT::MOC, reason); } return true; } // Test if the moc_predefs file is newer - if (!wrk.Moc().PredefsFileAbs.empty()) { + if (!Gen()->Moc().PredefsFileAbs.empty()) { bool isOlder = false; { std::string error; - isOlder = wrk.FileSys().FileIsOlderThan( - BuildFile, wrk.Moc().PredefsFileAbs, &error); + isOlder = FileSys().FileIsOlderThan(BuildFile, + Gen()->Moc().PredefsFileAbs, &error); if (!isOlder && !error.empty()) { - wrk.LogError(GeneratorT::MOC, error); + LogError(GenT::MOC, error); return false; } } @@ -748,8 +833,8 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) std::string reason = "Generating "; reason += Quoted(BuildFile); reason += " because it's older than: "; - reason += Quoted(wrk.Moc().PredefsFileAbs); - wrk.LogInfo(GeneratorT::MOC, reason); + reason += Quoted(Gen()->Moc().PredefsFileAbs); + Log().Info(GenT::MOC, reason); } return true; } @@ -760,9 +845,9 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) bool isOlder = false; { std::string error; - isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); if (!isOlder && !error.empty()) { - wrk.LogError(GeneratorT::MOC, error); + LogError(GenT::MOC, error); return false; } } @@ -772,7 +857,7 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) reason += Quoted(BuildFile); reason += " because it's older than its source file "; reason += Quoted(SourceFile); - wrk.LogInfo(GeneratorT::MOC, reason); + Log().Info(GenT::MOC, reason); } return true; } @@ -785,7 +870,7 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) std::string content; { std::string error; - if (!wrk.FileSys().FileRead(content, SourceFile, &error)) { + if (!FileSys().FileRead(content, SourceFile, &error)) { std::string emsg = "Could not read file\n "; emsg += Quoted(SourceFile); emsg += "\nrequired by moc include "; @@ -794,20 +879,20 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) emsg += Quoted(IncluderFile); emsg += ".\n"; emsg += error; - wrk.LogError(GeneratorT::MOC, emsg); + LogError(GenT::MOC, emsg); return false; } } - FindDependencies(wrk, content); + FindDependencies(content); } // Check dependency timestamps std::string error; - std::string sourceDir = wrk.FileSys().SubDirPrefix(SourceFile); + std::string sourceDir = FileSys().SubDirPrefix(SourceFile); for (std::string const& depFileRel : Depends) { std::string depFileAbs = - wrk.Moc().FindIncludedFile(sourceDir, depFileRel); + Gen()->Moc().FindIncludedFile(sourceDir, depFileRel); if (!depFileAbs.empty()) { - if (wrk.FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) { + if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) { if (verbose) { std::string reason = "Generating "; reason += Quoted(BuildFile); @@ -815,18 +900,18 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) reason += Quoted(SourceFile); reason += " because it is older than it's dependency file "; reason += Quoted(depFileAbs); - wrk.LogInfo(GeneratorT::MOC, reason); + Log().Info(GenT::MOC, reason); } return true; } if (!error.empty()) { - wrk.LogError(GeneratorT::MOC, error); + LogError(GenT::MOC, error); return false; } } else { std::string message = "Could not find dependency file "; message += Quoted(depFileRel); - wrk.LogFileWarning(GeneratorT::MOC, SourceFile, message); + Log().WarningFile(GenT::MOC, SourceFile, message); } } } @@ -834,36 +919,40 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) return false; } -void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk) +void cmQtAutoMocUic::JobMocT::GenerateMoc() { // Make sure the parent directory exists - if (wrk.FileSys().MakeParentDirectory(GeneratorT::MOC, BuildFile)) { + if (!FileSys().MakeParentDirectory(BuildFile)) { + LogFileError(GenT::MOC, BuildFile, "Could not create parent directory."); + return; + } + { // Compose moc command std::vector<std::string> cmd; - cmd.push_back(wrk.Moc().Executable); + cmd.push_back(Gen()->Moc().Executable); // Add options - cmd.insert(cmd.end(), wrk.Moc().AllOptions.begin(), - wrk.Moc().AllOptions.end()); + cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(), + Gen()->Moc().AllOptions.end()); // Add predefs include - if (!wrk.Moc().PredefsFileAbs.empty()) { + if (!Gen()->Moc().PredefsFileAbs.empty()) { cmd.emplace_back("--include"); - cmd.push_back(wrk.Moc().PredefsFileAbs); + cmd.push_back(Gen()->Moc().PredefsFileAbs); } cmd.emplace_back("-o"); cmd.push_back(BuildFile); cmd.push_back(SourceFile); // Execute moc command - ProcessResultT result; - if (wrk.RunProcess(GeneratorT::MOC, result, cmd)) { + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::MOC, result, cmd)) { // Moc command success // Print moc output if (!result.StdOut.empty()) { - wrk.LogInfo(GeneratorT::MOC, result.StdOut); + Log().Info(GenT::MOC, result.StdOut); } // Notify the generator that a not included file changed (on demand) if (IncludeString.empty()) { - wrk.Gen().ParallelMocAutoUpdated(); + Gen()->ParallelMocAutoUpdated(); } } else { // Moc command failed @@ -874,51 +963,51 @@ void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk) emsg += Quoted(BuildFile); emsg += ".\n"; emsg += result.ErrorMessage; - wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut); + LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); } - wrk.FileSys().FileRemove(BuildFile); + FileSys().FileRemove(BuildFile); } } } -void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk) +void cmQtAutoMocUic::JobUicT::Process() { // Compute build file name - BuildFile = wrk.Base().AutogenIncludeDir; + BuildFile = Gen()->Base().AutogenIncludeDir; BuildFile += '/'; BuildFile += IncludeString; - if (UpdateRequired(wrk)) { - GenerateUic(wrk); + if (UpdateRequired()) { + GenerateUic(); } } -bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) +bool cmQtAutoMocUic::JobUicT::UpdateRequired() { - bool const verbose = wrk.Gen().Log().Verbose(); + bool const verbose = Log().Verbose(); // Test if the build file exists - if (!wrk.FileSys().FileExists(BuildFile)) { + if (!FileSys().FileExists(BuildFile)) { if (verbose) { std::string reason = "Generating "; reason += Quoted(BuildFile); reason += " from its source file "; reason += Quoted(SourceFile); reason += " because it doesn't exist"; - wrk.LogInfo(GeneratorT::UIC, reason); + Log().Info(GenT::UIC, reason); } return true; } // Test if the uic settings changed - if (wrk.Uic().SettingsChanged) { + if (Gen()->Uic().SettingsChanged) { if (verbose) { std::string reason = "Generating "; reason += Quoted(BuildFile); reason += " from "; reason += Quoted(SourceFile); reason += " because the UIC settings changed"; - wrk.LogInfo(GeneratorT::UIC, reason); + Log().Info(GenT::UIC, reason); } return true; } @@ -928,9 +1017,9 @@ bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) bool isOlder = false; { std::string error; - isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); if (!isOlder && !error.empty()) { - wrk.LogError(GeneratorT::UIC, error); + LogError(GenT::UIC, error); return false; } } @@ -940,7 +1029,7 @@ bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) reason += Quoted(BuildFile); reason += " because it's older than its source file "; reason += Quoted(SourceFile); - wrk.LogInfo(GeneratorT::UIC, reason); + Log().Info(GenT::UIC, reason); } return true; } @@ -949,32 +1038,36 @@ bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) return false; } -void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk) +void cmQtAutoMocUic::JobUicT::GenerateUic() { // Make sure the parent directory exists - if (wrk.FileSys().MakeParentDirectory(GeneratorT::UIC, BuildFile)) { + if (!FileSys().MakeParentDirectory(BuildFile)) { + LogFileError(GenT::UIC, BuildFile, "Could not create parent directory."); + return; + } + { // Compose uic command std::vector<std::string> cmd; - cmd.push_back(wrk.Uic().Executable); + cmd.push_back(Gen()->Uic().Executable); { - std::vector<std::string> allOpts = wrk.Uic().TargetOptions; - auto optionIt = wrk.Uic().Options.find(SourceFile); - if (optionIt != wrk.Uic().Options.end()) { + std::vector<std::string> allOpts = Gen()->Uic().TargetOptions; + auto optionIt = Gen()->Uic().Options.find(SourceFile); + if (optionIt != Gen()->Uic().Options.end()) { UicMergeOptions(allOpts, optionIt->second, - (wrk.Base().QtVersionMajor == 5)); + (Gen()->Base().QtVersionMajor == 5)); } cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); } cmd.emplace_back("-o"); - cmd.push_back(BuildFile); - cmd.push_back(SourceFile); + cmd.emplace_back(BuildFile); + cmd.emplace_back(SourceFile); - ProcessResultT result; - if (wrk.RunProcess(GeneratorT::UIC, result, cmd)) { + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::UIC, result, cmd)) { // Uic command success // Print uic output if (!result.StdOut.empty()) { - wrk.LogInfo(GeneratorT::UIC, result.StdOut); + Log().Info(GenT::UIC, result.StdOut); } } else { // Uic command failed @@ -987,150 +1080,19 @@ void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk) emsg += Quoted(IncluderFile); emsg += ".\n"; emsg += result.ErrorMessage; - wrk.LogCommandError(GeneratorT::UIC, emsg, cmd, result.StdOut); + LogCommandError(GenT::UIC, emsg, cmd, result.StdOut); } - wrk.FileSys().FileRemove(BuildFile); - } - } -} - -void cmQtAutoGeneratorMocUic::JobDeleterT::operator()(JobT* job) -{ - delete job; -} - -cmQtAutoGeneratorMocUic::WorkerT::WorkerT(cmQtAutoGeneratorMocUic* gen, - uv_loop_t* uvLoop) - : Gen_(gen) -{ - // Initialize uv asynchronous callback for process starting - ProcessRequest_.init(*uvLoop, &WorkerT::UVProcessStart, this); - // Start thread - Thread_ = std::thread(&WorkerT::Loop, this); -} - -cmQtAutoGeneratorMocUic::WorkerT::~WorkerT() -{ - // Join thread - if (Thread_.joinable()) { - Thread_.join(); - } -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogInfo( - GeneratorT genType, std::string const& message) const -{ - return Log().Info(genType, message); -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogWarning( - GeneratorT genType, std::string const& message) const -{ - return Log().Warning(genType, message); -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning( - GeneratorT genType, std::string const& filename, - std::string const& message) const -{ - return Log().WarningFile(genType, filename, message); -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogError( - GeneratorT genType, std::string const& message) const -{ - Gen().ParallelRegisterJobError(); - Log().Error(genType, message); -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogFileError( - GeneratorT genType, std::string const& filename, - std::string const& message) const -{ - Gen().ParallelRegisterJobError(); - Log().ErrorFile(genType, filename, message); -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError( - GeneratorT genType, std::string const& message, - std::vector<std::string> const& command, std::string const& output) const -{ - Gen().ParallelRegisterJobError(); - Log().ErrorCommand(genType, message, command, output); -} - -bool cmQtAutoGeneratorMocUic::WorkerT::RunProcess( - GeneratorT genType, ProcessResultT& result, - std::vector<std::string> const& command) -{ - if (command.empty()) { - return false; - } - - // Create process instance - { - std::lock_guard<std::mutex> lock(ProcessMutex_); - Process_ = cm::make_unique<ReadOnlyProcessT>(); - Process_->setup(&result, true, command, Gen().Base().AutogenBuildDir); - } - - // Send asynchronous process start request to libuv loop - ProcessRequest_.send(); - - // Log command - if (this->Log().Verbose()) { - std::string msg = "Running command:\n"; - msg += QuotedCommand(command); - msg += '\n'; - this->LogInfo(genType, msg); - } - - // Wait until the process has been finished and destroyed - { - std::unique_lock<std::mutex> ulock(ProcessMutex_); - while (Process_) { - ProcessCondition_.wait(ulock); - } - } - return !result.error(); -} - -void cmQtAutoGeneratorMocUic::WorkerT::Loop() -{ - while (true) { - Gen().WorkerSwapJob(JobHandle_); - if (JobHandle_) { - JobHandle_->Process(*this); - } else { - break; - } - } -} - -void cmQtAutoGeneratorMocUic::WorkerT::UVProcessStart(uv_async_t* handle) -{ - auto& wrk = *reinterpret_cast<WorkerT*>(handle->data); - { - std::lock_guard<std::mutex> lock(wrk.ProcessMutex_); - if (wrk.Process_ && !wrk.Process_->IsStarted()) { - wrk.Process_->start(handle->loop, [&wrk] { wrk.UVProcessFinished(); }); + FileSys().FileRemove(BuildFile); } } } -void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished() +void cmQtAutoMocUic::JobFinishT::Process() { - { - std::lock_guard<std::mutex> lock(ProcessMutex_); - if (Process_ && Process_->IsFinished()) { - Process_.reset(); - } - } - // Notify idling thread - ProcessCondition_.notify_one(); + Gen()->AbortSuccess(); } -cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() +cmQtAutoMocUic::cmQtAutoMocUic() : Base_(&FileSys()) , Moc_(&FileSys()) { @@ -1140,14 +1102,11 @@ cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); - - // Initialize libuv asynchronous iteration request - UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this); } -cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic() = default; +cmQtAutoMocUic::~cmQtAutoMocUic() = default; -bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) +bool cmQtAutoMocUic::Init(cmMakefile* makefile) { // -- Meta Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); @@ -1213,7 +1172,7 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) // -- Read info file if (!makefile->ReadListFile(InfoFile())) { - Log().ErrorFile(GeneratorT::GEN, InfoFile(), "File processing failed"); + Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed"); return false; } @@ -1227,6 +1186,7 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) num = std::min<unsigned long>(num, ParallelMax); Base_.NumThreads = static_cast<unsigned int>(num); } + WorkerPool_.SetThreadCount(Base_.NumThreads); } // - Files and directories @@ -1238,14 +1198,13 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); if (Base_.AutogenBuildDir.empty()) { - Log().ErrorFile(GeneratorT::GEN, InfoFile(), - "Autogen build directory missing"); + Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing"); return false; } // include directory Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR"); if (Base_.AutogenIncludeDir.empty()) { - Log().ErrorFile(GeneratorT::GEN, InfoFile(), + Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen include directory missing"); return false; } @@ -1253,7 +1212,7 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) // - Files SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); if (SettingsFile_.empty()) { - Log().ErrorFile(GeneratorT::GEN, InfoFile(), "Settings file name missing"); + Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing"); return false; } @@ -1270,9 +1229,8 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); Moc_.Enabled = !Moc().Executable.empty(); if (Moc().Enabled) { - { - auto lst = InfoGetList("AM_MOC_SKIP"); - Moc_.SkipList.insert(lst.begin(), lst.end()); + for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) { + Moc_.SkipList.insert(std::move(sfl)); } Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); @@ -1334,20 +1292,20 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) } } else { Log().ErrorFile( - GeneratorT::MOC, InfoFile(), + GenT::MOC, InfoFile(), "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); return false; } } if (!error.empty()) { - Log().ErrorFile(GeneratorT::MOC, InfoFile(), error); + Log().ErrorFile(GenT::MOC, InfoFile(), error); return false; } } Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); // Install moc predefs job if (!Moc().PredefsCmd.empty()) { - JobQueues_.MocPredefs.emplace_back(new JobMocPredefsT()); + WorkerPool().EmplaceJob<JobMocPredefsT>(); } } @@ -1355,9 +1313,8 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); Uic_.Enabled = !Uic().Executable.empty(); if (Uic().Enabled) { - { - auto lst = InfoGetList("AM_UIC_SKIP"); - Uic_.SkipList.insert(lst.begin(), lst.end()); + for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) { + Uic_.SkipList.insert(std::move(sfl)); } Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); @@ -1369,7 +1326,7 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) std::ostringstream ost; ost << "files/options lists sizes mismatch (" << sources.size() << "/" << options.size() << ")"; - Log().ErrorFile(GeneratorT::UIC, InfoFile(), ost.str()); + Log().ErrorFile(GenT::UIC, InfoFile(), ost.str()); return false; } auto fitEnd = sources.cend(); @@ -1383,56 +1340,49 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) } } - // Initialize source file jobs + // - Headers and sources + // Add sources { - std::hash<std::string> stringHash; - std::set<std::size_t> uniqueHeaders; - - // Add header jobs + auto addSource = [this](std::string&& src, bool moc, bool uic) { + WorkerPool().EmplaceJob<JobParseT>(std::move(src), moc, uic, false); + }; + for (std::string& src : InfoGetList("AM_SOURCES")) { + addSource(std::move(src), true, true); + } + if (Moc().Enabled) { + for (std::string& src : InfoGetList("AM_MOC_SOURCES")) { + addSource(std::move(src), true, false); + } + } + if (Uic().Enabled) { + for (std::string& src : InfoGetList("AM_UIC_SOURCES")) { + addSource(std::move(src), false, true); + } + } + } + // Add Fence job + WorkerPool().EmplaceJob<JobFenceT>(); + // Add headers + { + auto addHeader = [this](std::string&& hdr, bool moc, bool uic) { + WorkerPool().EmplaceJob<JobParseT>(std::move(hdr), moc, uic, true); + }; for (std::string& hdr : InfoGetList("AM_HEADERS")) { - const bool moc = !Moc().skipped(hdr); - const bool uic = !Uic().skipped(hdr); - if ((moc || uic) && uniqueHeaders.emplace(stringHash(hdr)).second) { - JobQueues_.Headers.emplace_back( - new JobParseT(std::move(hdr), moc, uic, true)); + addHeader(std::move(hdr), true, true); + } + if (Moc().Enabled) { + for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) { + addHeader(std::move(hdr), true, false); } } - // Add source jobs - { - std::vector<std::string> sources = InfoGetList("AM_SOURCES"); - // Add header(s) for the source file - for (std::string& src : sources) { - const bool srcMoc = !Moc().skipped(src); - const bool srcUic = !Uic().skipped(src); - if (!srcMoc && !srcUic) { - continue; - } - // Search for the default header file and a private header - { - std::array<std::string, 2> bases; - bases[0] = FileSys().SubDirPrefix(src); - bases[0] += FileSys().GetFilenameWithoutLastExtension(src); - bases[1] = bases[0]; - bases[1] += "_p"; - for (std::string const& headerBase : bases) { - std::string header; - if (Base().FindHeader(header, headerBase)) { - const bool moc = srcMoc && !Moc().skipped(header); - const bool uic = srcUic && !Uic().skipped(header); - if ((moc || uic) && - uniqueHeaders.emplace(stringHash(header)).second) { - JobQueues_.Headers.emplace_back( - new JobParseT(std::move(header), moc, uic, true)); - } - } - } - } - // Add source job - JobQueues_.Sources.emplace_back( - new JobParseT(std::move(src), srcMoc, srcUic)); + if (Uic().Enabled) { + for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) { + addHeader(std::move(hdr), false, true); } } } + // Addpost parse fence job + WorkerPool().EmplaceJob<JobPostParseT>(); // Init derived information // ------------------------ @@ -1527,98 +1477,22 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) return true; } -bool cmQtAutoGeneratorMocUic::Process() +bool cmQtAutoMocUic::Process() { - // Run libuv event loop - UVRequest().send(); - if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) { - if (JobError_) { - return false; - } - } else { + SettingsFileRead(); + if (!CreateDirectories()) { return false; } - return true; -} - -void cmQtAutoGeneratorMocUic::UVPollStage(uv_async_t* handle) -{ - reinterpret_cast<cmQtAutoGeneratorMocUic*>(handle->data)->PollStage(); -} - -void cmQtAutoGeneratorMocUic::PollStage() -{ - switch (Stage_) { - case StageT::SETTINGS_READ: - SettingsFileRead(); - SetStage(StageT::CREATE_DIRECTORIES); - break; - case StageT::CREATE_DIRECTORIES: - CreateDirectories(); - SetStage(StageT::PARSE_SOURCES); - break; - case StageT::PARSE_SOURCES: - if (ThreadsStartJobs(JobQueues_.Sources)) { - SetStage(StageT::PARSE_HEADERS); - } - break; - case StageT::PARSE_HEADERS: - if (ThreadsStartJobs(JobQueues_.Headers)) { - SetStage(StageT::MOC_PREDEFS); - } - break; - case StageT::MOC_PREDEFS: - if (ThreadsStartJobs(JobQueues_.MocPredefs)) { - SetStage(StageT::MOC_PROCESS); - } - break; - case StageT::MOC_PROCESS: - if (ThreadsStartJobs(JobQueues_.Moc)) { - SetStage(StageT::MOCS_COMPILATION); - } - break; - case StageT::MOCS_COMPILATION: - if (ThreadsJobsDone()) { - MocGenerateCompilation(); - SetStage(StageT::UIC_PROCESS); - } - break; - case StageT::UIC_PROCESS: - if (ThreadsStartJobs(JobQueues_.Uic)) { - SetStage(StageT::SETTINGS_WRITE); - } - break; - case StageT::SETTINGS_WRITE: - SettingsFileWrite(); - SetStage(StageT::FINISH); - break; - case StageT::FINISH: - if (ThreadsJobsDone()) { - // Clear all libuv handles - ThreadsStop(); - UVRequest().reset(); - // Set highest END stage manually - Stage_ = StageT::END; - } - break; - case StageT::END: - break; + if (!WorkerPool_.Process(this)) { + return false; } -} - -void cmQtAutoGeneratorMocUic::SetStage(StageT stage) -{ if (JobError_) { - stage = StageT::FINISH; - } - // Only allow to increase the stage - if (Stage_ < stage) { - Stage_ = stage; - UVRequest().send(); + return false; } + return SettingsFileWrite(); } -void cmQtAutoGeneratorMocUic::SettingsFileRead() +void cmQtAutoMocUic::SettingsFileRead() { // Compose current settings strings { @@ -1684,14 +1558,12 @@ void cmQtAutoGeneratorMocUic::SettingsFileRead() } } -void cmQtAutoGeneratorMocUic::SettingsFileWrite() +bool cmQtAutoMocUic::SettingsFileWrite() { - std::lock_guard<std::mutex> jobsLock(JobsMutex_); // Only write if any setting changed - if (!JobError_ && (Moc().SettingsChanged || Uic().SettingsChanged)) { + if (Moc().SettingsChanged || Uic().SettingsChanged) { if (Log().Verbose()) { - Log().Info(GeneratorT::GEN, - "Writing settings file " + Quoted(SettingsFile_)); + Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_)); } // Compose settings file content std::string content; @@ -1709,258 +1581,148 @@ void cmQtAutoGeneratorMocUic::SettingsFileWrite() SettingAppend("uic", SettingsStringUic_); } // Write settings file - if (!FileSys().FileWrite(GeneratorT::GEN, SettingsFile_, content)) { - Log().ErrorFile(GeneratorT::GEN, SettingsFile_, - "Settings file writing failed"); + std::string error; + if (!FileSys().FileWrite(SettingsFile_, content, &error)) { + Log().ErrorFile(GenT::GEN, SettingsFile_, + "Settings file writing failed. " + error); // Remove old settings file to trigger a full rebuild on the next run FileSys().FileRemove(SettingsFile_); - RegisterJobError(); + return false; } } + return true; } -void cmQtAutoGeneratorMocUic::CreateDirectories() +bool cmQtAutoMocUic::CreateDirectories() { // Create AUTOGEN include directory - if (!FileSys().MakeDirectory(GeneratorT::GEN, Base().AutogenIncludeDir)) { - RegisterJobError(); + if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) { + Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir, + "Could not create directory."); + return false; } + return true; } -bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue) +// Private method that requires cmQtAutoMocUic::JobsMutex_ to be +// locked +void cmQtAutoMocUic::Abort(bool error) { - bool done = false; - std::size_t queueSize = queue.size(); - - // Change the active queue - { - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - // Check if there are still unfinished jobs from the previous queue - if (JobsRemain_ == 0) { - if (!JobThreadsAbort_) { - JobQueue_.swap(queue); - JobsRemain_ = queueSize; - } else { - // Abort requested - queue.clear(); - queueSize = 0; - } - done = true; - } + if (error) { + JobError_.store(true); } + WorkerPool_.Abort(); +} - if (done && (queueSize != 0)) { - // Start new threads on demand - if (Workers_.empty()) { - Workers_.resize(Base().NumThreads); - for (auto& item : Workers_) { - item = cm::make_unique<WorkerT>(this, UVLoop()); +bool cmQtAutoMocUic::ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle) +{ + JobMocT const& mocJob(static_cast<JobMocT&>(*jobHandle)); + // Do additional tests if this is an included moc job + if (!mocJob.IncludeString.empty()) { + std::lock_guard<std::mutex> guard(MocMetaMutex_); + // Register included moc file + MocIncludedFiles_.emplace(mocJob.SourceFile); + + // Check if the same moc file would be generated from a different + // source file. + auto const range = MocIncludes_.equal_range(mocJob.IncludeString); + for (auto it = range.first; it != range.second; ++it) { + if (it->second[0] == mocJob.SourceFile) { + // The output file already gets generated + return true; } - } else { - // Notify threads - if (queueSize == 1) { - JobsConditionRead_.notify_one(); - } else { - JobsConditionRead_.notify_all(); + { + // The output file already gets generated - from a different source + // file! + std::string error = "The two source files\n "; + error += Quoted(mocJob.IncluderFile); + error += " and\n "; + error += Quoted(it->second[1]); + error += "\ncontain the same moc include string "; + error += Quoted(mocJob.IncludeString); + error += "\nbut the moc file would be generated from different " + "source files\n "; + error += Quoted(mocJob.SourceFile); + error += " and\n "; + error += Quoted(it->second[0]); + error += ".\nConsider to\n" + "- not include the \"moc_<NAME>.cpp\" file\n" + "- add a directory prefix to a \"<NAME>.moc\" include " + "(e.g \"sub/<NAME>.moc\")\n" + "- rename the source file(s)\n"; + Log().Error(GenT::MOC, error); + AbortError(); + return false; } } - } - - return done; -} -void cmQtAutoGeneratorMocUic::ThreadsStop() -{ - if (!Workers_.empty()) { - // Clear all jobs - { - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - JobThreadsAbort_ = true; - JobsRemain_ -= JobQueue_.size(); - JobQueue_.clear(); - - JobQueues_.Sources.clear(); - JobQueues_.Headers.clear(); - JobQueues_.MocPredefs.clear(); - JobQueues_.Moc.clear(); - JobQueues_.Uic.clear(); - } - // Wake threads - JobsConditionRead_.notify_all(); - // Join and clear threads - Workers_.clear(); + // We're still here so register this job + MocIncludes_.emplace_hint(range.first, mocJob.IncludeString, + std::array<std::string, 2>{ + { mocJob.SourceFile, mocJob.IncluderFile } }); } + return WorkerPool_.PushJob(std::move(jobHandle)); } -bool cmQtAutoGeneratorMocUic::ThreadsJobsDone() -{ - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - return (JobsRemain_ == 0); -} - -void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle) +bool cmQtAutoMocUic::ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle) { - bool const jobProcessed(jobHandle); - if (jobProcessed) { - jobHandle.reset(nullptr); - } + const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle)); { - std::unique_lock<std::mutex> jobsLock(JobsMutex_); - // Reduce the remaining job count and notify the libuv loop - // when all jobs are done - if (jobProcessed) { - --JobsRemain_; - if (JobsRemain_ == 0) { - UVRequest().send(); + std::lock_guard<std::mutex> guard(UicMetaMutex_); + // Check if the same uic file would be generated from a different + // source file. + auto const range = UicIncludes_.equal_range(uicJob.IncludeString); + for (auto it = range.first; it != range.second; ++it) { + if (it->second[0] == uicJob.SourceFile) { + // The output file already gets generated + return true; } - } - // Wait for new jobs - while (!JobThreadsAbort_ && JobQueue_.empty()) { - JobsConditionRead_.wait(jobsLock); - } - // Try to pick up a new job handle - if (!JobThreadsAbort_ && !JobQueue_.empty()) { - jobHandle = std::move(JobQueue_.front()); - JobQueue_.pop_front(); - } - } -} - -void cmQtAutoGeneratorMocUic::ParallelRegisterJobError() -{ - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - RegisterJobError(); -} - -// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be -// locked -void cmQtAutoGeneratorMocUic::RegisterJobError() -{ - JobError_ = true; - if (!JobThreadsAbort_) { - JobThreadsAbort_ = true; - // Clear remaining jobs - if (JobsRemain_ != 0) { - JobsRemain_ -= JobQueue_.size(); - JobQueue_.clear(); - } - } -} - -bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle) -{ - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - if (!JobThreadsAbort_) { - bool pushJobHandle = true; - // Do additional tests if this is an included moc job - const JobMocT& mocJob(static_cast<JobMocT&>(*jobHandle)); - if (!mocJob.IncludeString.empty()) { - // Register included moc file and look for collisions - MocIncludedFiles_.emplace(mocJob.SourceFile); - if (!MocIncludedStrings_.emplace(mocJob.IncludeString).second) { - // Another source file includes the same moc file! - for (const JobHandleT& otherHandle : JobQueues_.Moc) { - const JobMocT& otherJob(static_cast<JobMocT&>(*otherHandle)); - if (otherJob.IncludeString == mocJob.IncludeString) { - // Check if the same moc file would be generated from different - // source files which is an error. - if (otherJob.SourceFile != mocJob.SourceFile) { - // Include string collision - std::string error = "The two source files\n "; - error += Quoted(mocJob.IncluderFile); - error += " and\n "; - error += Quoted(otherJob.IncluderFile); - error += "\ncontain the same moc include string "; - error += Quoted(mocJob.IncludeString); - error += "\nbut the moc file would be generated from different " - "source files\n "; - error += Quoted(mocJob.SourceFile); - error += " and\n "; - error += Quoted(otherJob.SourceFile); - error += ".\nConsider to\n" - "- not include the \"moc_<NAME>.cpp\" file\n" - "- add a directory prefix to a \"<NAME>.moc\" include " - "(e.g \"sub/<NAME>.moc\")\n" - "- rename the source file(s)\n"; - Log().Error(GeneratorT::MOC, error); - RegisterJobError(); - } - // Do not push this job in since the included moc file already - // gets generated by an other job. - pushJobHandle = false; - break; - } - } + { + // The output file already gets generated - from a different .ui + // file! + std::string error = "The two source files\n "; + error += Quoted(uicJob.IncluderFile); + error += " and\n "; + error += Quoted(it->second[1]); + error += "\ncontain the same uic include string "; + error += Quoted(uicJob.IncludeString); + error += "\nbut the uic file would be generated from different " + "source files\n "; + error += Quoted(uicJob.SourceFile); + error += " and\n "; + error += Quoted(it->second[0]); + error += + ".\nConsider to\n" + "- add a directory prefix to a \"ui_<NAME>.h\" include " + "(e.g \"sub/ui_<NAME>.h\")\n" + "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" " + "include(s)\n"; + Log().Error(GenT::UIC, error); + AbortError(); + return false; } } - // Push job on demand - if (pushJobHandle) { - JobQueues_.Moc.emplace_back(std::move(jobHandle)); - } - } - return !JobError_; -} -bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle) -{ - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - if (!JobThreadsAbort_) { - bool pushJobHandle = true; - // Look for include collisions. - const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle)); - for (const JobHandleT& otherHandle : JobQueues_.Uic) { - const JobUicT& otherJob(static_cast<JobUicT&>(*otherHandle)); - if (otherJob.IncludeString == uicJob.IncludeString) { - // Check if the same uic file would be generated from different - // source files which would be an error. - if (otherJob.SourceFile != uicJob.SourceFile) { - // Include string collision - std::string error = "The two source files\n "; - error += Quoted(uicJob.IncluderFile); - error += " and\n "; - error += Quoted(otherJob.IncluderFile); - error += "\ncontain the same uic include string "; - error += Quoted(uicJob.IncludeString); - error += "\nbut the uic file would be generated from different " - "source files\n "; - error += Quoted(uicJob.SourceFile); - error += " and\n "; - error += Quoted(otherJob.SourceFile); - error += - ".\nConsider to\n" - "- add a directory prefix to a \"ui_<NAME>.h\" include " - "(e.g \"sub/ui_<NAME>.h\")\n" - "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" " - "include(s)\n"; - Log().Error(GeneratorT::UIC, error); - RegisterJobError(); - } - // Do not push this job in since the uic file already - // gets generated by an other job. - pushJobHandle = false; - break; - } - } - if (pushJobHandle) { - JobQueues_.Uic.emplace_back(std::move(jobHandle)); - } + // We're still here so register this job + UicIncludes_.emplace_hint(range.first, uicJob.IncludeString, + std::array<std::string, 2>{ + { uicJob.SourceFile, uicJob.IncluderFile } }); } - return !JobError_; + return WorkerPool_.PushJob(std::move(jobHandle)); } -bool cmQtAutoGeneratorMocUic::ParallelMocIncluded( - std::string const& sourceFile) +bool cmQtAutoMocUic::ParallelMocIncluded(std::string const& sourceFile) { - std::lock_guard<std::mutex> mocLock(JobsMutex_); + std::lock_guard<std::mutex> guard(MocMetaMutex_); return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end()); } -std::string cmQtAutoGeneratorMocUic::ParallelMocAutoRegister( +std::string cmQtAutoMocUic::ParallelMocAutoRegister( std::string const& baseName) { std::string res; { - std::lock_guard<std::mutex> mocLock(JobsMutex_); + std::lock_guard<std::mutex> mocLock(MocMetaMutex_); res = baseName; res += ".cpp"; if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) { @@ -1981,62 +1743,3 @@ std::string cmQtAutoGeneratorMocUic::ParallelMocAutoRegister( } return res; } - -void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated() -{ - std::lock_guard<std::mutex> mocLock(JobsMutex_); - MocAutoFileUpdated_ = true; -} - -void cmQtAutoGeneratorMocUic::MocGenerateCompilation() -{ - std::lock_guard<std::mutex> mocLock(JobsMutex_); - if (!JobError_ && Moc().Enabled) { - // Write mocs compilation build file - { - // Compose mocs compilation file content - std::string content = - "// This file is autogenerated. Changes will be overwritten.\n"; - if (MocAutoFiles_.empty()) { - // Placeholder content - content += "// No files found that require moc or the moc files are " - "included\n"; - content += "enum some_compilers { need_more_than_nothing };\n"; - } else { - // Valid content - char const sbeg = Base().MultiConfig ? '<' : '"'; - char const send = Base().MultiConfig ? '>' : '"'; - for (std::string const& mocfile : MocAutoFiles_) { - content += "#include "; - content += sbeg; - content += mocfile; - content += send; - content += '\n'; - } - } - - std::string const& compAbs = Moc().CompFileAbs; - if (FileSys().FileDiffers(compAbs, content)) { - // Actually write mocs compilation file - if (Log().Verbose()) { - Log().Info(GeneratorT::MOC, "Generating MOC compilation " + compAbs); - } - if (!FileSys().FileWrite(GeneratorT::MOC, compAbs, content)) { - Log().ErrorFile(GeneratorT::MOC, compAbs, - "mocs compilation file writing failed"); - RegisterJobError(); - return; - } - } else if (MocAutoFileUpdated_) { - // Only touch mocs compilation file - if (Log().Verbose()) { - Log().Info(GeneratorT::MOC, "Touching mocs compilation " + compAbs); - } - FileSys().Touch(compAbs); - } - } - // Write mocs compilation wrapper file - if (Base().MultiConfig) { - } - } -} diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoMocUic.h index c22df29..3902abb 100644 --- a/Source/cmQtAutoGeneratorMocUic.h +++ b/Source/cmQtAutoMocUic.h @@ -1,43 +1,41 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGeneratorMocUic_h -#define cmQtAutoGeneratorMocUic_h +#ifndef cmQtAutoMocUic_h +#define cmQtAutoMocUic_h #include "cmConfigure.h" // IWYU pragma: keep #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" -#include "cmUVHandlePtr.h" -#include "cm_uv.h" +#include "cmWorkerPool.h" #include "cmsys/RegularExpression.hxx" -#include <condition_variable> -#include <cstddef> -#include <deque> +#include <array> +#include <atomic> #include <map> #include <memory> // IWYU pragma: keep #include <mutex> #include <set> #include <string> -#include <thread> +#include <unordered_set> #include <utility> #include <vector> class cmMakefile; // @brief AUTOMOC and AUTOUIC generator -class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator +class cmQtAutoMocUic : public cmQtAutoGenerator { public: - cmQtAutoGeneratorMocUic(); - ~cmQtAutoGeneratorMocUic() override; + cmQtAutoMocUic(); + ~cmQtAutoMocUic() override; - cmQtAutoGeneratorMocUic(cmQtAutoGeneratorMocUic const&) = delete; - cmQtAutoGeneratorMocUic& operator=(cmQtAutoGeneratorMocUic const&) = delete; + cmQtAutoMocUic(cmQtAutoMocUic const&) = delete; + cmQtAutoMocUic& operator=(cmQtAutoMocUic const&) = delete; public: // -- Types - class WorkerT; + typedef std::multimap<std::string, std::array<std::string, 2>> IncludesMap; /// @brief Search key plus regular expression pair /// @@ -133,7 +131,7 @@ public: std::string CompFileAbs; std::string PredefsFileRel; std::string PredefsFileAbs; - std::set<std::string> SkipList; + std::unordered_set<std::string> SkipList; std::vector<std::string> IncludePaths; std::vector<std::string> Includes; std::vector<std::string> Definitions; @@ -164,45 +162,78 @@ public: bool Enabled = false; bool SettingsChanged = false; std::string Executable; - std::set<std::string> SkipList; + std::unordered_set<std::string> SkipList; std::vector<std::string> TargetOptions; std::map<std::string, std::vector<std::string>> Options; std::vector<std::string> SearchPaths; cmsys::RegularExpression RegExpInclude; }; - /// @brief Abstract job class for threaded processing + /// @brief Abstract job class for concurrent job processing /// - class JobT + class JobT : public cmWorkerPool::JobT { - public: - JobT() = default; - virtual ~JobT() = default; + protected: + /** + * @brief Protected default constructor + */ + JobT(bool fence = false) + : cmWorkerPool::JobT(fence) + { + } + + //! Get the generator. Only valid during Process() call! + cmQtAutoMocUic* Gen() const + { + return static_cast<cmQtAutoMocUic*>(UserData()); + }; - JobT(JobT const&) = delete; - JobT& operator=(JobT const&) = delete; + //! Get the file system interface. Only valid during Process() call! + FileSystem& FileSys() { return Gen()->FileSys(); } + //! Get the logger. Only valid during Process() call! + Logger& Log() { return Gen()->Log(); } - // -- Abstract processing interface - virtual void Process(WorkerT& wrk) = 0; + // -- Error logging with automatic abort + void LogError(GenT genType, std::string const& message) const; + void LogFileError(GenT genType, std::string const& filename, + std::string const& message) const; + void LogCommandError(GenT genType, std::string const& message, + std::vector<std::string> const& command, + std::string const& output) const; + + /** + * @brief Run an external process. Use only during Process() call! + */ + bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command); }; - /// @brief Deleter for classes derived from Job + /// @brief Fence job utility class /// - struct JobDeleterT + class JobFenceT : public JobT { - void operator()(JobT* job); + public: + JobFenceT() + : JobT(true) + { + } + void Process() override{}; }; - // Job management types - typedef std::unique_ptr<JobT, JobDeleterT> JobHandleT; - typedef std::deque<JobHandleT> JobQueueT; + /// @brief Generate moc_predefs.h + /// + class JobMocPredefsT : public JobT + { + private: + void Process() override; + }; - /// @brief Parse source job + /// @brief Parses a source file /// class JobParseT : public JobT { public: - JobParseT(std::string&& fileName, bool moc, bool uic, bool header = false) + JobParseT(std::string fileName, bool moc, bool uic, bool header = false) : FileName(std::move(fileName)) , AutoMoc(moc) , AutoUic(uic) @@ -218,18 +249,15 @@ public: std::string FileBase; }; - void Process(WorkerT& wrk) override; - bool ParseMocSource(WorkerT& wrk, MetaT const& meta); - bool ParseMocHeader(WorkerT& wrk, MetaT const& meta); - std::string MocStringHeaders(WorkerT& wrk, - std::string const& fileBase) const; - std::string MocFindIncludedHeader(WorkerT& wrk, - std::string const& includerDir, + void Process() override; + bool ParseMocSource(MetaT const& meta); + bool ParseMocHeader(MetaT const& meta); + std::string MocStringHeaders(std::string const& fileBase) const; + std::string MocFindIncludedHeader(std::string const& includerDir, std::string const& includeBase); - bool ParseUic(WorkerT& wrk, MetaT const& meta); - bool ParseUicInclude(WorkerT& wrk, MetaT const& meta, - std::string&& includeString); - std::string UicFindIncludedFile(WorkerT& wrk, MetaT const& meta, + bool ParseUic(MetaT const& meta); + bool ParseUicInclude(MetaT const& meta, std::string&& includeString); + std::string UicFindIncludedFile(MetaT const& meta, std::string const& includeString); private: @@ -239,12 +267,20 @@ public: bool Header = false; }; - /// @brief Generate moc_predefs + /// @brief Generates additional jobs after all files have been parsed /// - class JobMocPredefsT : public JobT + class JobPostParseT : public JobFenceT + { + private: + void Process() override; + }; + + /// @brief Generate mocs_compilation.cpp + /// + class JobMocsCompilationT : public JobFenceT { private: - void Process(WorkerT& wrk) override; + void Process() override; }; /// @brief Moc a file job @@ -252,20 +288,20 @@ public: class JobMocT : public JobT { public: - JobMocT(std::string&& sourceFile, std::string includerFile, - std::string&& includeString) + JobMocT(std::string sourceFile, std::string includerFile, + std::string includeString) : SourceFile(std::move(sourceFile)) , IncluderFile(std::move(includerFile)) , IncludeString(std::move(includeString)) { } - void FindDependencies(WorkerT& wrk, std::string const& content); + void FindDependencies(std::string const& content); private: - void Process(WorkerT& wrk) override; - bool UpdateRequired(WorkerT& wrk); - void GenerateMoc(WorkerT& wrk); + void Process() override; + bool UpdateRequired(); + void GenerateMoc(); public: std::string SourceFile; @@ -281,8 +317,8 @@ public: class JobUicT : public JobT { public: - JobUicT(std::string&& sourceFile, std::string includerFile, - std::string&& includeString) + JobUicT(std::string sourceFile, std::string includerFile, + std::string includeString) : SourceFile(std::move(sourceFile)) , IncluderFile(std::move(includerFile)) , IncludeString(std::move(includeString)) @@ -290,9 +326,9 @@ public: } private: - void Process(WorkerT& wrk) override; - bool UpdateRequired(WorkerT& wrk); - void GenerateUic(WorkerT& wrk); + void Process() override; + bool UpdateRequired(); + void GenerateUic(); public: std::string SourceFile; @@ -301,80 +337,12 @@ public: std::string BuildFile; }; - /// @brief Worker Thread + /// @brief The last job /// - class WorkerT + class JobFinishT : public JobFenceT { - public: - WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop); - ~WorkerT(); - - WorkerT(WorkerT const&) = delete; - WorkerT& operator=(WorkerT const&) = delete; - - // -- Const accessors - cmQtAutoGeneratorMocUic& Gen() const { return *Gen_; } - Logger& Log() const { return Gen_->Log(); } - FileSystem& FileSys() const { return Gen_->FileSys(); } - const BaseSettingsT& Base() const { return Gen_->Base(); } - const MocSettingsT& Moc() const { return Gen_->Moc(); } - const UicSettingsT& Uic() const { return Gen_->Uic(); } - - // -- Log info - void LogInfo(GeneratorT genType, std::string const& message) const; - // -- Log warning - void LogWarning(GeneratorT genType, std::string const& message) const; - void LogFileWarning(GeneratorT genType, std::string const& filename, - std::string const& message) const; - // -- Log error - void LogError(GeneratorT genType, std::string const& message) const; - void LogFileError(GeneratorT genType, std::string const& filename, - std::string const& message) const; - void LogCommandError(GeneratorT genType, std::string const& message, - std::vector<std::string> const& command, - std::string const& output) const; - - // -- External processes - /// @brief Verbose logging version - bool RunProcess(GeneratorT genType, ProcessResultT& result, - std::vector<std::string> const& command); - - private: - /// @brief Thread main loop - void Loop(); - - // -- Libuv callbacks - static void UVProcessStart(uv_async_t* handle); - void UVProcessFinished(); - private: - // -- Generator - cmQtAutoGeneratorMocUic* Gen_; - // -- Job handle - JobHandleT JobHandle_; - // -- Process management - std::mutex ProcessMutex_; - cm::uv_async_ptr ProcessRequest_; - std::condition_variable ProcessCondition_; - std::unique_ptr<ReadOnlyProcessT> Process_; - // -- System thread - std::thread Thread_; - }; - - /// @brief Processing stage - enum class StageT - { - SETTINGS_READ, - CREATE_DIRECTORIES, - PARSE_SOURCES, - PARSE_HEADERS, - MOC_PREDEFS, - MOC_PROCESS, - MOCS_COMPILATION, - UIC_PROCESS, - SETTINGS_WRITE, - FINISH, - END + void Process() override; }; // -- Const settings interface @@ -382,69 +350,64 @@ public: const MocSettingsT& Moc() const { return this->Moc_; } const UicSettingsT& Uic() const { return this->Uic_; } - // -- Worker thread interface - void WorkerSwapJob(JobHandleT& jobHandle); // -- Parallel job processing interface - void ParallelRegisterJobError(); - bool ParallelJobPushMoc(JobHandleT& jobHandle); - bool ParallelJobPushUic(JobHandleT& jobHandle); - bool ParallelMocIncluded(std::string const& sourceFile); + cmWorkerPool& WorkerPool() { return WorkerPool_; } + void AbortError() { Abort(true); } + void AbortSuccess() { Abort(false); } + bool ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle); + bool ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle); + + // -- Mocs compilation include file updated flag + void ParallelMocAutoUpdated() { MocAutoFileUpdated_.store(true); } + bool MocAutoFileUpdated() const { return MocAutoFileUpdated_.load(); } + + // -- Mocs compilation file register std::string ParallelMocAutoRegister(std::string const& baseName); - void ParallelMocAutoUpdated(); + bool ParallelMocIncluded(std::string const& sourceFile); + std::set<std::string> const& MocAutoFiles() const + { + return this->MocAutoFiles_; + } private: + // -- Utility accessors + Logger& Log() { return Logger_; } + FileSystem& FileSys() { return FileSys_; } // -- Abstract processing interface bool Init(cmMakefile* makefile) override; bool Process() override; - // -- Process stage - static void UVPollStage(uv_async_t* handle); - void PollStage(); - void SetStage(StageT stage); // -- Settings file void SettingsFileRead(); - void SettingsFileWrite(); + bool SettingsFileWrite(); // -- Thread processing - bool ThreadsStartJobs(JobQueueT& queue); - bool ThreadsJobsDone(); - void ThreadsStop(); - void RegisterJobError(); + void Abort(bool error); // -- Generation - void CreateDirectories(); - void MocGenerateCompilation(); + bool CreateDirectories(); private: + // -- Utility + Logger Logger_; + FileSystem FileSys_; // -- Settings BaseSettingsT Base_; MocSettingsT Moc_; UicSettingsT Uic_; - // -- Progress - StageT Stage_ = StageT::SETTINGS_READ; - // -- Job queues - std::mutex JobsMutex_; - struct - { - JobQueueT Sources; - JobQueueT Headers; - JobQueueT MocPredefs; - JobQueueT Moc; - JobQueueT Uic; - } JobQueues_; - JobQueueT JobQueue_; - std::size_t volatile JobsRemain_ = 0; - bool volatile JobError_ = false; - bool volatile JobThreadsAbort_ = false; - std::condition_variable JobsConditionRead_; // -- Moc meta - std::set<std::string> MocIncludedStrings_; + std::mutex MocMetaMutex_; std::set<std::string> MocIncludedFiles_; + IncludesMap MocIncludes_; std::set<std::string> MocAutoFiles_; - bool volatile MocAutoFileUpdated_ = false; + std::atomic<bool> MocAutoFileUpdated_ = ATOMIC_VAR_INIT(false); + // -- Uic meta + std::mutex UicMetaMutex_; + IncludesMap UicIncludes_; // -- Settings file std::string SettingsFile_; std::string SettingsStringMoc_; std::string SettingsStringUic_; - // -- Threads and loops - std::vector<std::unique_ptr<WorkerT>> Workers_; + // -- Thread pool and job queue + std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false); + cmWorkerPool WorkerPool_; }; #endif diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx new file mode 100644 index 0000000..e58324d --- /dev/null +++ b/Source/cmQtAutoRcc.cxx @@ -0,0 +1,516 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmQtAutoRcc.h" +#include "cmQtAutoGen.h" + +#include "cmAlgorithms.h" +#include "cmCryptoHash.h" +#include "cmDuration.h" +#include "cmFileLockResult.h" +#include "cmMakefile.h" +#include "cmProcessOutput.h" +#include "cmSystemTools.h" + +// -- Class methods + +cmQtAutoRcc::cmQtAutoRcc() = default; + +cmQtAutoRcc::~cmQtAutoRcc() = default; + +bool cmQtAutoRcc::Init(cmMakefile* makefile) +{ + // -- Utility lambdas + auto InfoGet = [makefile](std::string const& key) { + return makefile->GetSafeDefinition(key); + }; + auto InfoGetList = + [makefile](std::string const& key) -> std::vector<std::string> { + std::vector<std::string> list; + cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); + return list; + }; + auto InfoGetConfig = [makefile, + this](std::string const& key) -> std::string { + const char* valueConf = nullptr; + { + std::string keyConf = key; + keyConf += '_'; + keyConf += InfoConfig(); + valueConf = makefile->GetDefinition(keyConf); + } + if (valueConf == nullptr) { + return makefile->GetSafeDefinition(key); + } + return std::string(valueConf); + }; + auto InfoGetConfigList = + [&InfoGetConfig](std::string const& key) -> std::vector<std::string> { + std::vector<std::string> list; + cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); + return list; + }; + + // -- Read info file + if (!makefile->ReadListFile(InfoFile())) { + Log().ErrorFile(GenT::RCC, InfoFile(), "File processing failed."); + return false; + } + + // - Configurations + Log().RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); + MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG"); + + // - Directories + AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR"); + if (AutogenBuildDir_.empty()) { + Log().ErrorFile(GenT::RCC, InfoFile(), "Build directory empty."); + return false; + } + + IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR"); + if (IncludeDir_.empty()) { + Log().ErrorFile(GenT::RCC, InfoFile(), "Include directory empty."); + return false; + } + + // - Rcc executable + RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE"); + RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS"); + + // - Job + LockFile_ = InfoGet("ARCC_LOCK_FILE"); + QrcFile_ = InfoGet("ARCC_SOURCE"); + QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_); + QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_); + RccPathChecksum_ = InfoGet("ARCC_OUTPUT_CHECKSUM"); + RccFileName_ = InfoGet("ARCC_OUTPUT_NAME"); + Options_ = InfoGetConfigList("ARCC_OPTIONS"); + Inputs_ = InfoGetList("ARCC_INPUTS"); + + // - Settings file + SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE"); + + // - Validity checks + if (LockFile_.empty()) { + Log().ErrorFile(GenT::RCC, InfoFile(), "Lock file name missing."); + return false; + } + if (SettingsFile_.empty()) { + Log().ErrorFile(GenT::RCC, InfoFile(), "Settings file name missing."); + return false; + } + if (AutogenBuildDir_.empty()) { + Log().ErrorFile(GenT::RCC, InfoFile(), "Autogen build directory missing."); + return false; + } + if (RccExecutable_.empty()) { + Log().ErrorFile(GenT::RCC, InfoFile(), "rcc executable missing."); + return false; + } + if (QrcFile_.empty()) { + Log().ErrorFile(GenT::RCC, InfoFile(), "rcc input file missing."); + return false; + } + if (RccFileName_.empty()) { + Log().ErrorFile(GenT::RCC, InfoFile(), "rcc output file missing."); + return false; + } + + // Init derived information + // ------------------------ + + RccFilePublic_ = AutogenBuildDir_; + RccFilePublic_ += '/'; + RccFilePublic_ += RccPathChecksum_; + RccFilePublic_ += '/'; + RccFilePublic_ += RccFileName_; + + // Compute rcc output file name + if (IsMultiConfig()) { + RccFileOutput_ = IncludeDir_; + RccFileOutput_ += '/'; + RccFileOutput_ += MultiConfigOutput(); + } else { + RccFileOutput_ = RccFilePublic_; + } + + return true; +} + +bool cmQtAutoRcc::Process() +{ + if (!SettingsFileRead()) { + return false; + } + + // Test if the rcc output needs to be regenerated + bool generate = false; + if (!TestQrcRccFiles(generate)) { + return false; + } + if (!generate && !TestResources(generate)) { + return false; + } + // Generate on demand + if (generate) { + if (!GenerateRcc()) { + return false; + } + } else { + // Test if the info file is newer than the output file + if (!TestInfoFile()) { + return false; + } + } + + if (!GenerateWrapper()) { + return false; + } + + return SettingsFileWrite(); +} + +std::string cmQtAutoRcc::MultiConfigOutput() const +{ + static std::string const suffix = "_CMAKE_"; + std::string res; + res += RccPathChecksum_; + res += '/'; + res += AppendFilenameSuffix(RccFileName_, suffix); + return res; +} + +bool cmQtAutoRcc::SettingsFileRead() +{ + // Compose current settings strings + { + cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); + std::string const sep(" ~~~ "); + { + std::string str; + str += RccExecutable_; + str += sep; + str += cmJoin(RccListOptions_, ";"); + str += sep; + str += QrcFile_; + str += sep; + str += RccPathChecksum_; + str += sep; + str += RccFileName_; + str += sep; + str += cmJoin(Options_, ";"); + str += sep; + str += cmJoin(Inputs_, ";"); + str += sep; + SettingsString_ = crypt.HashString(str); + } + } + + // Make sure the settings file exists + if (!cmSystemTools::FileExists(SettingsFile_, true)) { + // Touch the settings file to make sure it exists + if (!cmSystemTools::Touch(SettingsFile_, true)) { + Log().ErrorFile(GenT::RCC, SettingsFile_, + "Settings file creation failed."); + return false; + } + } + + // Lock the lock file + { + // Make sure the lock file exists + if (!cmSystemTools::FileExists(LockFile_, true)) { + if (!cmSystemTools::Touch(LockFile_, true)) { + Log().ErrorFile(GenT::RCC, LockFile_, "Lock file creation failed."); + return false; + } + } + // Lock the lock file + cmFileLockResult lockResult = + LockFileLock_.Lock(LockFile_, static_cast<unsigned long>(-1)); + if (!lockResult.IsOk()) { + Log().ErrorFile(GenT::RCC, LockFile_, + "File lock failed: " + lockResult.GetOutputMessage()); + return false; + } + } + + // Read old settings + { + std::string content; + if (FileRead(content, SettingsFile_)) { + SettingsChanged_ = (SettingsString_ != SettingsFind(content, "rcc")); + // In case any setting changed clear the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (SettingsChanged_) { + std::string error; + if (!FileWrite(SettingsFile_, "", &error)) { + Log().ErrorFile(GenT::RCC, SettingsFile_, + "Settings file clearing failed. " + error); + return false; + } + } + } else { + SettingsChanged_ = true; + } + } + + return true; +} + +bool cmQtAutoRcc::SettingsFileWrite() +{ + // Only write if any setting changed + if (SettingsChanged_) { + if (Log().Verbose()) { + Log().Info(GenT::RCC, "Writing settings file " + Quoted(SettingsFile_)); + } + // Write settings file + std::string content = "rcc:"; + content += SettingsString_; + content += '\n'; + std::string error; + if (!FileWrite(SettingsFile_, content, &error)) { + Log().ErrorFile(GenT::RCC, SettingsFile_, + "Settings file writing failed. " + error); + // Remove old settings file to trigger a full rebuild on the next run + cmSystemTools::RemoveFile(SettingsFile_); + return false; + } + } + + // Unlock the lock file + LockFileLock_.Release(); + return true; +} + +/// Do basic checks if rcc generation is required +bool cmQtAutoRcc::TestQrcRccFiles(bool& generate) +{ + // Test if the rcc input file exists + if (!QrcFileTime_.Load(QrcFile_)) { + std::string error; + error = "The resources file "; + error += Quoted(QrcFile_); + error += " does not exist"; + Log().ErrorFile(GenT::RCC, QrcFile_, error); + return false; + } + + // Test if the rcc output file exists + if (!RccFileTime_.Load(RccFileOutput_)) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileOutput_); + reason += " from its source file "; + reason += Quoted(QrcFile_); + reason += " because it doesn't exist"; + Log().Info(GenT::RCC, reason); + } + generate = true; + return true; + } + + // Test if the settings changed + if (SettingsChanged_) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileOutput_); + reason += " from "; + reason += Quoted(QrcFile_); + reason += " because the RCC settings changed"; + Log().Info(GenT::RCC, reason); + } + generate = true; + return true; + } + + // Test if the rcc output file is older than the .qrc file + if (RccFileTime_.Older(QrcFileTime_)) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileOutput_); + reason += " because it is older than "; + reason += Quoted(QrcFile_); + Log().Info(GenT::RCC, reason); + } + generate = true; + return true; + } + + return true; +} + +bool cmQtAutoRcc::TestResources(bool& generate) +{ + // Read resource files list + if (Inputs_.empty()) { + std::string error; + RccLister const lister(RccExecutable_, RccListOptions_); + if (!lister.list(QrcFile_, Inputs_, error, Log().Verbose())) { + Log().ErrorFile(GenT::RCC, QrcFile_, error); + return false; + } + } + + for (std::string const& resFile : Inputs_) { + // Check if the resource file exists + cmFileTime fileTime; + if (!fileTime.Load(resFile)) { + std::string error; + error = "Could not find the resource file\n "; + error += Quoted(resFile); + error += '\n'; + Log().ErrorFile(GenT::RCC, QrcFile_, error); + return false; + } + // Check if the resource file is newer than the build file + if (RccFileTime_.Older(fileTime)) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileOutput_); + reason += " from "; + reason += Quoted(QrcFile_); + reason += " because it is older than "; + reason += Quoted(resFile); + Log().Info(GenT::RCC, reason); + } + generate = true; + break; + } + } + return true; +} + +bool cmQtAutoRcc::TestInfoFile() +{ + // Test if the rcc output file is older than the info file + + cmFileTime infoFileTime; + if (!infoFileTime.Load(InfoFile())) { + std::string error; + error = "Could not find the info file "; + error += Quoted(InfoFile()); + error += '\n'; + Log().ErrorFile(GenT::RCC, QrcFile_, error); + return false; + } + if (RccFileTime_.Older(infoFileTime)) { + if (Log().Verbose()) { + std::string reason = "Touching "; + reason += Quoted(RccFileOutput_); + reason += " because it is older than "; + reason += Quoted(InfoFile()); + Log().Info(GenT::RCC, reason); + } + // Touch build file + if (!cmSystemTools::Touch(RccFileOutput_, false)) { + Log().ErrorFile(GenT::RCC, RccFileOutput_, "Build file touch failed"); + return false; + } + BuildFileChanged_ = true; + } + + return true; +} + +bool cmQtAutoRcc::GenerateRcc() +{ + // Make parent directory + if (!MakeParentDirectory(RccFileOutput_)) { + Log().ErrorFile(GenT::RCC, RccFileOutput_, + "Could not create parent directory"); + return false; + } + + // Start a rcc process + std::vector<std::string> cmd; + cmd.push_back(RccExecutable_); + cmd.insert(cmd.end(), Options_.begin(), Options_.end()); + cmd.emplace_back("-o"); + cmd.push_back(RccFileOutput_); + cmd.push_back(QrcFile_); + + // Log command + if (Log().Verbose()) { + std::string msg = "Running command:\n"; + msg += QuotedCommand(cmd); + msg += '\n'; + cmSystemTools::Stdout(msg); + } + + std::string rccStdOut; + std::string rccStdErr; + int retVal = 0; + bool result = cmSystemTools::RunSingleCommand( + cmd, &rccStdOut, &rccStdErr, &retVal, AutogenBuildDir_.c_str(), + cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto); + if (!result || (retVal != 0)) { + // rcc process failed + { + std::string err = "The rcc process failed to compile\n "; + err += Quoted(QrcFile_); + err += "\ninto\n "; + err += Quoted(RccFileOutput_); + Log().ErrorCommand(GenT::RCC, err, cmd, rccStdOut + rccStdErr); + } + cmSystemTools::RemoveFile(RccFileOutput_); + return false; + } + + // rcc process success + // Print rcc output + if (!rccStdOut.empty()) { + Log().Info(GenT::RCC, rccStdOut); + } + BuildFileChanged_ = true; + + return true; +} + +bool cmQtAutoRcc::GenerateWrapper() +{ + // Generate a wrapper source file on demand + if (IsMultiConfig()) { + // Wrapper file content + std::string content; + content += "// This is an autogenerated configuration wrapper file.\n"; + content += "// Changes will be overwritten.\n"; + content += "#include <"; + content += MultiConfigOutput(); + content += ">\n"; + + // Compare with existing file content + bool fileDiffers = true; + { + std::string oldContents; + if (FileRead(oldContents, RccFilePublic_)) { + fileDiffers = (oldContents != content); + } + } + if (fileDiffers) { + // Write new wrapper file + if (Log().Verbose()) { + Log().Info(GenT::RCC, "Generating RCC wrapper file " + RccFilePublic_); + } + std::string error; + if (!FileWrite(RccFilePublic_, content, &error)) { + Log().ErrorFile(GenT::RCC, RccFilePublic_, + "RCC wrapper file writing failed. " + error); + return false; + } + } else if (BuildFileChanged_) { + // Just touch the wrapper file + if (Log().Verbose()) { + Log().Info(GenT::RCC, "Touching RCC wrapper file " + RccFilePublic_); + } + if (!cmSystemTools::Touch(RccFilePublic_, false)) { + Log().ErrorFile(GenT::RCC, RccFilePublic_, + "RCC wrapper file touch failed."); + return false; + } + } + } + return true; +} diff --git a/Source/cmQtAutoGeneratorRcc.h b/Source/cmQtAutoRcc.h index 1ec1c4a..8dc9179 100644 --- a/Source/cmQtAutoGeneratorRcc.h +++ b/Source/cmQtAutoRcc.h @@ -1,13 +1,13 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGeneratorRcc_h -#define cmQtAutoGeneratorRcc_h +#ifndef cmQtAutoRcc_h +#define cmQtAutoRcc_h #include "cmConfigure.h" // IWYU pragma: keep #include "cmFileLock.h" +#include "cmFileTime.h" #include "cmQtAutoGenerator.h" -#include "cm_uv.h" #include <string> #include <vector> @@ -15,62 +15,38 @@ class cmMakefile; // @brief AUTORCC generator -class cmQtAutoGeneratorRcc : public cmQtAutoGenerator +class cmQtAutoRcc : public cmQtAutoGenerator { public: - cmQtAutoGeneratorRcc(); - ~cmQtAutoGeneratorRcc() override; + cmQtAutoRcc(); + ~cmQtAutoRcc() override; - cmQtAutoGeneratorRcc(cmQtAutoGeneratorRcc const&) = delete; - cmQtAutoGeneratorRcc& operator=(cmQtAutoGeneratorRcc const&) = delete; + cmQtAutoRcc(cmQtAutoRcc const&) = delete; + cmQtAutoRcc& operator=(cmQtAutoRcc const&) = delete; private: - // -- Types - - /// @brief Processing stage - enum class StageT : unsigned char - { - SETTINGS_READ, - TEST_QRC_RCC_FILES, - TEST_RESOURCES_READ, - TEST_RESOURCES, - TEST_INFO_FILE, - GENERATE, - GENERATE_RCC, - GENERATE_WRAPPER, - SETTINGS_WRITE, - FINISH, - END - }; + // -- Utility + Logger& Log() { return Logger_; } + bool IsMultiConfig() const { return MultiConfig_; } + std::string MultiConfigOutput() const; // -- Abstract processing interface bool Init(cmMakefile* makefile) override; bool Process() override; - // -- Process stage - static void UVPollStage(uv_async_t* handle); - void PollStage(); - void SetStage(StageT stage); // -- Settings file bool SettingsFileRead(); - void SettingsFileWrite(); + bool SettingsFileWrite(); // -- Tests - bool TestQrcRccFiles(); - bool TestResourcesRead(); - bool TestResources(); - void TestInfoFile(); + bool TestQrcRccFiles(bool& generate); + bool TestResources(bool& generate); + bool TestInfoFile(); // -- Generation - void GenerateParentDir(); bool GenerateRcc(); - void GenerateWrapper(); - - // -- Utility - bool IsMultiConfig() const { return MultiConfig_; } - std::string MultiConfigOutput() const; - bool StartProcess(std::string const& workingDirectory, - std::vector<std::string> const& command, - bool mergedOutput); + bool GenerateWrapper(); private: + // -- Logging + Logger Logger_; // -- Config settings bool MultiConfig_ = false; // -- Directories @@ -85,23 +61,18 @@ private: std::string QrcFile_; std::string QrcFileName_; std::string QrcFileDir_; + cmFileTime QrcFileTime_; std::string RccPathChecksum_; std::string RccFileName_; std::string RccFileOutput_; std::string RccFilePublic_; + cmFileTime RccFileTime_; std::vector<std::string> Options_; std::vector<std::string> Inputs_; - // -- Subprocess - ProcessResultT ProcessResult_; - std::unique_ptr<ReadOnlyProcessT> Process_; // -- Settings file std::string SettingsFile_; std::string SettingsString_; bool SettingsChanged_ = false; - // -- libuv loop - StageT Stage_ = StageT::SETTINGS_READ; - bool Error_ = false; - bool Generate_ = false; bool BuildFileChanged_ = false; }; diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx index 55204d7..2064275 100644 --- a/Source/cmRST.cxx +++ b/Source/cmRST.cxx @@ -3,6 +3,7 @@ #include "cmRST.h" #include "cmAlgorithms.h" +#include "cmRange.h" #include "cmSystemTools.h" #include "cmVersion.h" @@ -32,7 +33,7 @@ cmRST::cmRST(std::ostream& os, std::string docroot) , TocTreeDirective("^.. toctree::[ \t]*(.*)$") , ProductionListDirective("^.. productionlist::[ \t]*(.*)$") , NoteDirective("^.. note::[ \t]*(.*)$") - , ModuleRST("^#\\[(=*)\\[\\.rst:$") + , ModuleRST(R"(^#\[(=*)\[\.rst:$)") , CMakeRole("(:cmake)?:(" "command|cpack_gen|generator|variable|envvar|module|policy|" "prop_cache|prop_dir|prop_gbl|prop_inst|prop_sf|" @@ -456,6 +457,12 @@ void cmRST::UnindentLines(std::vector<std::string>& lines) size_t trailingEmpty = std::distance(rit, cmFindNot(cmReverseRange(lines), std::string())); + if ((leadingEmpty + trailingEmpty) >= lines.size()) { + // All lines are empty. The markup block is empty. Leave only one. + lines.resize(1); + return; + } + std::vector<std::string>::iterator contentEnd = cmRotate( lines.begin(), lines.begin() + leadingEmpty, lines.end() - trailingEmpty); lines.erase(contentEnd, lines.end()); diff --git a/Source/cmRange.h b/Source/cmRange.h new file mode 100644 index 0000000..3be5193 --- /dev/null +++ b/Source/cmRange.h @@ -0,0 +1,239 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmRange_h +#define cmRange_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <algorithm> +#include <functional> +#include <iterator> + +namespace RangeIterators { + +template <typename Iter, typename UnaryPredicate> +class FilterIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename std::iterator_traits<Iter>::value_type; + using difference_type = typename std::iterator_traits<Iter>::difference_type; + using pointer = typename std::iterator_traits<Iter>::pointer; + using reference = typename std::iterator_traits<Iter>::reference; + + FilterIterator(Iter b, Iter e, UnaryPredicate p) + : Cur(std::move(b)) + , End(std::move(e)) + , Pred(std::move(p)) + { + this->SatisfyPredicate(); + } + + FilterIterator& operator++() + { + ++this->Cur; + this->SatisfyPredicate(); + return *this; + } + + FilterIterator& operator--() + { + do { + --this->Cur; + } while (!this->Pred(*this->Cur)); + return *this; + } + + bool operator==(FilterIterator const& other) const + { + return this->Cur == other.Cur; + } + + bool operator!=(FilterIterator const& other) const + { + return !this->operator==(other); + } + + auto operator*() const -> decltype(*std::declval<Iter>()) + { + return *this->Cur; + } + +private: + void SatisfyPredicate() + { + while (this->Cur != this->End && !this->Pred(*this->Cur)) { + ++this->Cur; + } + } + + Iter Cur; + Iter End; + UnaryPredicate Pred; +}; + +template <typename Iter, typename UnaryFunction> +class TransformIterator +{ +public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = + typename std::remove_cv<typename std::remove_reference<decltype( + std::declval<UnaryFunction>()(*std::declval<Iter>()))>::type>::type; + using difference_type = typename std::iterator_traits<Iter>::difference_type; + using pointer = value_type const*; + using reference = value_type const&; + + TransformIterator(Iter i, UnaryFunction f) + : Base(std::move(i)) + , Func(std::move(f)) + { + } + + TransformIterator& operator++() + { + ++this->Base; + return *this; + } + + TransformIterator& operator--() + { + --this->Base; + return *this; + } + + bool operator==(TransformIterator const& other) const + { + return this->Base == other.Base; + } + + bool operator!=(TransformIterator const& other) const + { + return !this->operator==(other); + } + + auto operator*() const + -> decltype(std::declval<UnaryFunction>()(*std::declval<Iter>())) + { + return this->Func(*this->Base); + } + +private: + Iter Base; + UnaryFunction Func; +}; + +} // namespace RangeIterators + +template <typename Iter> +class cmRange +{ +public: + using const_iterator = Iter; + using value_type = typename std::iterator_traits<Iter>::value_type; + using difference_type = typename std::iterator_traits<Iter>::difference_type; + + cmRange(Iter b, Iter e) + : Begin(std::move(b)) + , End(std::move(e)) + { + } + + Iter begin() const { return this->Begin; } + Iter end() const { return this->End; } + bool empty() const { return this->Begin == this->End; } + + difference_type size() const + { + return std::distance(this->Begin, this->End); + } + + cmRange& advance(difference_type amount) & + { + std::advance(this->Begin, amount); + return *this; + } + + cmRange advance(difference_type amount) && + { + std::advance(this->Begin, amount); + return std::move(*this); + } + + cmRange& retreat(difference_type amount) & + { + std::advance(this->End, -amount); + return *this; + } + + cmRange retreat(difference_type amount) && + { + std::advance(this->End, -amount); + return std::move(*this); + } + + template <typename UnaryPredicate> + bool all_of(UnaryPredicate p) const + { + return std::all_of(this->Begin, this->End, std::ref(p)); + } + + template <typename UnaryPredicate> + bool any_of(UnaryPredicate p) const + { + return std::any_of(this->Begin, this->End, std::ref(p)); + } + + template <typename UnaryPredicate> + bool none_of(UnaryPredicate p) const + { + return std::none_of(this->Begin, this->End, std::ref(p)); + } + + template <typename UnaryPredicate> + auto filter(UnaryPredicate p) const + -> cmRange<RangeIterators::FilterIterator<Iter, UnaryPredicate>> + { + using It = RangeIterators::FilterIterator<Iter, UnaryPredicate>; + return { It(this->Begin, this->End, p), It(this->End, this->End, p) }; + } + + template <typename UnaryFunction> + auto transform(UnaryFunction f) const + -> cmRange<RangeIterators::TransformIterator<Iter, UnaryFunction>> + { + using It = RangeIterators::TransformIterator<Iter, UnaryFunction>; + return { It(this->Begin, f), It(this->End, f) }; + } + +private: + Iter Begin; + Iter End; +}; + +template <typename Iter1, typename Iter2> +bool operator==(cmRange<Iter1> const& left, cmRange<Iter2> const& right) +{ + return left.size() == right.size() && + std::equal(left.begin(), left.end(), right.begin()); +} + +template <typename Iter1, typename Iter2> +auto cmMakeRange(Iter1 begin, Iter2 end) -> cmRange<Iter1> +{ + return { begin, end }; +} + +template <typename Range> +auto cmMakeRange(Range const& range) -> cmRange<decltype(range.begin())> +{ + return { range.begin(), range.end() }; +} + +template <typename Range> +auto cmReverseRange(Range const& range) -> cmRange<decltype(range.rbegin())> +{ + return { range.rbegin(), range.rend() }; +} + +#endif diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx index e347a2c..309ee30 100644 --- a/Source/cmRulePlaceholderExpander.cxx +++ b/Source/cmRulePlaceholderExpander.cxx @@ -172,6 +172,36 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( return replaceValues.SwiftModuleName; } } + if (replaceValues.SwiftLibraryName) { + if (variable == "SWIFT_LIBRARY_NAME") { + return replaceValues.SwiftLibraryName; + } + } + if (replaceValues.SwiftPartialDoc) { + if (variable == "SWIFT_PARTIAL_DOC") { + return replaceValues.SwiftPartialDoc; + } + } + if (replaceValues.SwiftPartialModule) { + if (variable == "SWIFT_PARTIAL_MODULE") { + return replaceValues.SwiftPartialModule; + } + } + if (replaceValues.SwiftPartialModules) { + if (variable == "SWIFT_PARTIAL_MODULES") { + return replaceValues.SwiftPartialModules; + } + } + if (replaceValues.TargetSwiftDoc) { + if (variable == "TARGET_SWIFT_DOC") { + return replaceValues.TargetSwiftDoc; + } + } + if (replaceValues.TargetSwiftModule) { + if (variable == "TARGET_SWIFT_MODULE") { + return replaceValues.TargetSwiftModule; + } + } if (variable == "TARGET_SONAME" || variable == "SONAME_FLAG" || variable == "TARGET_INSTALLNAME_DIR") { // All these variables depend on TargetSOName diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h index 5c03637..ebd4d41 100644 --- a/Source/cmRulePlaceholderExpander.h +++ b/Source/cmRulePlaceholderExpander.h @@ -60,6 +60,12 @@ public: const char* FilterPrefix; const char* SwiftAuxiliarySources; const char* SwiftModuleName; + const char* SwiftLibraryName; + const char* SwiftPartialModule; + const char* SwiftPartialDoc; + const char* TargetSwiftModule; + const char* TargetSwiftDoc; + const char* SwiftPartialModules; }; // Expand rule variables in CMake of the type found in language rules diff --git a/Source/cmSearchPath.cxx b/Source/cmSearchPath.cxx index 0f51e0e..f98984e 100644 --- a/Source/cmSearchPath.cxx +++ b/Source/cmSearchPath.cxx @@ -144,7 +144,7 @@ void cmSearchPath::AddSuffixes(const std::vector<std::string>& suffixes) // this will get incorrectly considered a network // path on windows and cause huge delays. std::string p = inPath; - if (!p.empty() && *p.rbegin() != '/') { + if (!p.empty() && p.back() != '/') { p += "/"; } @@ -176,7 +176,7 @@ void cmSearchPath::AddPrefixPaths(const std::vector<std::string>& paths, for (std::string const& path : paths) { std::string dir = path; - if (!subdir.empty() && !dir.empty() && *dir.rbegin() != '/') { + if (!subdir.empty() && !dir.empty() && dir.back() != '/') { dir += "/"; } if (subdir == "include" || subdir == "lib") { diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index e740c05..1903fd9 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -97,13 +97,13 @@ void cmServer::ProcessRequest(cmConnection* connection, } cmSystemTools::SetMessageCallback( - [&request](const char* msg, const char* title) { + [&request](const std::string& msg, const char* title) { reportMessage(msg, title, request); }); if (this->Protocol) { this->Protocol->CMakeInstance()->SetProgressCallback( - [&request](const char* msg, float prog) { + [&request](const std::string& msg, float prog) { reportProgress(msg, prog, request); }); this->WriteResponse(connection, this->Protocol->Process(request), @@ -155,7 +155,7 @@ void cmServer::PrintHello(cmConnection* connection) const this->WriteJsonObject(connection, hello, nullptr); } -void cmServer::reportProgress(const char* msg, float progress, +void cmServer::reportProgress(const std::string& msg, float progress, const cmServerRequest& request) { if (progress < 0.0f || progress > 1.0f) { @@ -165,15 +165,14 @@ void cmServer::reportProgress(const char* msg, float progress, } } -void cmServer::reportMessage(const char* msg, const char* title, +void cmServer::reportMessage(const std::string& msg, const char* title, const cmServerRequest& request) { - assert(msg); std::string titleString; if (title) { titleString = title; } - request.ReportMessage(std::string(msg), titleString); + request.ReportMessage(msg, titleString); } cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) diff --git a/Source/cmServer.h b/Source/cmServer.h index e1ed27a..aba4924 100644 --- a/Source/cmServer.h +++ b/Source/cmServer.h @@ -119,9 +119,9 @@ public: void OnConnected(cmConnection* connection) override; private: - static void reportProgress(const char* msg, float progress, + static void reportProgress(const std::string& msg, float progress, const cmServerRequest& request); - static void reportMessage(const char* msg, const char* title, + static void reportMessage(const std::string& msg, const char* title, const cmServerRequest& request); // Handle requests: diff --git a/Source/cmServerConnection.cxx b/Source/cmServerConnection.cxx index 844a858..a878890 100644 --- a/Source/cmServerConnection.cxx +++ b/Source/cmServerConnection.cxx @@ -29,7 +29,7 @@ cm::uv_stream_ptr cmStdIoConnection::SetupStream(int file_id) tty.init(*this->Server->GetLoop(), file_id, file_id == 0, static_cast<cmEventBasedConnection*>(this)); uv_tty_set_mode(tty, UV_TTY_MODE_NORMAL); - return std::move(tty); + return { std::move(tty) }; } case UV_FILE: if (file_id == 0) { @@ -43,7 +43,7 @@ cm::uv_stream_ptr cmStdIoConnection::SetupStream(int file_id) pipe.init(*this->Server->GetLoop(), 0, static_cast<cmEventBasedConnection*>(this)); uv_pipe_open(pipe, file_id); - return std::move(pipe); + return { std::move(pipe) }; } default: assert(false && "Unable to determine stream type"); diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 203ee93..dad8821 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -8,6 +8,7 @@ #include "cmGlobalGenerator.h" #include "cmJsonObjectDictionary.h" #include "cmJsonObjects.h" +#include "cmMessageType.h" #include "cmServer.h" #include "cmServerDictionary.h" #include "cmState.h" @@ -600,6 +601,10 @@ cmServerResponse cmServerProtocol1::ProcessConfigure( } int ret = cm->Configure(); + cm->IssueMessage( + MessageType::DEPRECATION_WARNING, + "The 'cmake-server(7)' is deprecated. " + "Please port clients to use the 'cmake-file-api(7)' instead."); if (ret < 0) { return request.ReportError("Configuration failed."); } diff --git a/Source/cmSetCommand.cxx b/Source/cmSetCommand.cxx index 6bd071c..41555e8 100644 --- a/Source/cmSetCommand.cxx +++ b/Source/cmSetCommand.cxx @@ -5,6 +5,7 @@ #include "cmAlgorithms.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmSystemTools.h" diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx index 3c4111b..e9343c7 100644 --- a/Source/cmSetPropertyCommand.cxx +++ b/Source/cmSetPropertyCommand.cxx @@ -8,6 +8,7 @@ #include "cmInstalledFile.h" #include "cmMakefile.h" #include "cmProperty.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmSystemTools.h" @@ -33,25 +34,25 @@ bool cmSetPropertyCommand::InitialPass(std::vector<std::string> const& args, } // Get the scope on which to set the property. - std::vector<std::string>::const_iterator arg = args.begin(); + std::string const& scopeName = args.front(); cmProperty::ScopeType scope; - if (*arg == "GLOBAL") { + if (scopeName == "GLOBAL") { scope = cmProperty::GLOBAL; - } else if (*arg == "DIRECTORY") { + } else if (scopeName == "DIRECTORY") { scope = cmProperty::DIRECTORY; - } else if (*arg == "TARGET") { + } else if (scopeName == "TARGET") { scope = cmProperty::TARGET; - } else if (*arg == "SOURCE") { + } else if (scopeName == "SOURCE") { scope = cmProperty::SOURCE_FILE; - } else if (*arg == "TEST") { + } else if (scopeName == "TEST") { scope = cmProperty::TEST; - } else if (*arg == "CACHE") { + } else if (scopeName == "CACHE") { scope = cmProperty::CACHE; - } else if (*arg == "INSTALL") { + } else if (scopeName == "INSTALL") { scope = cmProperty::INSTALL; } else { std::ostringstream e; - e << "given invalid scope " << *arg << ". " + e << "given invalid scope " << scopeName << ". " << "Valid scopes are GLOBAL, DIRECTORY, " "TARGET, SOURCE, TEST, CACHE, INSTALL."; this->SetError(e.str()); @@ -68,32 +69,32 @@ bool cmSetPropertyCommand::InitialPass(std::vector<std::string> const& args, }; Doing doing = DoingNames; const char* sep = ""; - for (++arg; arg != args.end(); ++arg) { - if (*arg == "PROPERTY") { + for (std::string const& arg : cmMakeRange(args).advance(1)) { + if (arg == "PROPERTY") { doing = DoingProperty; - } else if (*arg == "APPEND") { + } else if (arg == "APPEND") { doing = DoingNone; this->AppendMode = true; this->Remove = false; this->AppendAsString = false; - } else if (*arg == "APPEND_STRING") { + } else if (arg == "APPEND_STRING") { doing = DoingNone; this->AppendMode = true; this->Remove = false; this->AppendAsString = true; } else if (doing == DoingNames) { - this->Names.insert(*arg); + this->Names.insert(arg); } else if (doing == DoingProperty) { - this->PropertyName = *arg; + this->PropertyName = arg; doing = DoingValues; } else if (doing == DoingValues) { this->PropertyValue += sep; sep = ";"; - this->PropertyValue += *arg; + this->PropertyValue += arg; this->Remove = false; } else { std::ostringstream e; - e << "given invalid argument \"" << *arg << "\"."; + e << "given invalid argument \"" << arg << "\"."; this->SetError(e.str()); return false; } @@ -334,7 +335,7 @@ bool cmSetPropertyCommand::HandleCacheMode() !cmSystemTools::IsOff(this->PropertyValue)) { std::ostringstream e; e << "given non-boolean value \"" << this->PropertyValue - << "\" for CACHE property \"ADVANCED\". "; + << R"(" for CACHE property "ADVANCED". )"; this->SetError(e.str()); return false; } diff --git a/Source/cmSiteNameCommand.cxx b/Source/cmSiteNameCommand.cxx index 01758ee..9f041bc 100644 --- a/Source/cmSiteNameCommand.cxx +++ b/Source/cmSiteNameCommand.cxx @@ -52,9 +52,8 @@ bool cmSiteNameCommand::InitialPass(std::vector<std::string> const& args, // try to find the hostname for this computer if (!cmSystemTools::IsOff(hostname_cmd)) { std::string host; - cmSystemTools::RunSingleCommand(hostname_cmd.c_str(), &host, nullptr, - nullptr, nullptr, - cmSystemTools::OUTPUT_NONE); + cmSystemTools::RunSingleCommand(hostname_cmd, &host, nullptr, nullptr, + nullptr, cmSystemTools::OUTPUT_NONE); // got the hostname if (!host.empty()) { diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h index a82a58a..edad4c7 100644 --- a/Source/cmSourceFile.h +++ b/Source/cmSourceFile.h @@ -34,6 +34,9 @@ public: ~cmSourceFile(); + cmSourceFile(const cmSourceFile&) = delete; + cmSourceFile& operator=(const cmSourceFile&) = delete; + /** * Get the list of the custom commands for this source file */ @@ -41,13 +44,13 @@ public: cmCustomCommand const* GetCustomCommand() const; void SetCustomCommand(cmCustomCommand* cc); - ///! Set/Get a property of this source file + //! Set/Get a property of this source file void SetProperty(const std::string& prop, const char* value); void AppendProperty(const std::string& prop, const char* value, bool asString = false); - ///! Might return a nullptr if the property is not set or invalid + //! Might return a nullptr if the property is not set or invalid const char* GetProperty(const std::string& prop) const; - ///! Always returns a valid pointer + //! Always returns a valid pointer const char* GetSafeProperty(const std::string& prop) const; bool GetPropertyAsBool(const std::string& prop) const; @@ -55,7 +58,7 @@ public: command like get_property or get_source_file_property. */ const char* GetPropertyForUser(const std::string& prop); - ///! Checks is the GENERATED property is set and true + //! Checks is the GENERATED property is set and true /// @return Equivalent to GetPropertyAsBool("GENERATED") bool GetIsGenerated() const { return this->IsGenerated; } diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx index 34ded38..5cdacaa 100644 --- a/Source/cmSourceGroupCommand.cxx +++ b/Source/cmSourceGroupCommand.cxx @@ -77,9 +77,13 @@ std::vector<std::string> prepareFilesPathsForTree( const std::string& currentSourceDir) { std::vector<std::string> prepared; + prepared.reserve(filesPaths.size()); for (auto const& filePath : filesPaths) { - prepared.push_back(prepareFilePathForTree(filePath, currentSourceDir)); + // If provided file path is actually not a file, silently ignore it. + if (cmSystemTools::FileExists(filePath, /*isFile=*/true)) { + prepared.push_back(prepareFilePathForTree(filePath, currentSourceDir)); + } } return prepared; diff --git a/Source/cmState.cxx b/Source/cmState.cxx index a08e9b8..fa7df0b 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -556,9 +556,28 @@ const char* cmState::GetGlobalProperty(const std::string& prop) if (prop == "CMAKE_C_KNOWN_FEATURES") { return &FOR_EACH_C_FEATURE(STRING_LIST_ELEMENT)[1]; } + if (prop == "CMAKE_C90_KNOWN_FEATURES") { + return &FOR_EACH_C90_FEATURE(STRING_LIST_ELEMENT)[1]; + } + if (prop == "CMAKE_C99_KNOWN_FEATURES") { + return &FOR_EACH_C99_FEATURE(STRING_LIST_ELEMENT)[1]; + } + if (prop == "CMAKE_C11_KNOWN_FEATURES") { + return &FOR_EACH_C11_FEATURE(STRING_LIST_ELEMENT)[1]; + } if (prop == "CMAKE_CXX_KNOWN_FEATURES") { return &FOR_EACH_CXX_FEATURE(STRING_LIST_ELEMENT)[1]; } + if (prop == "CMAKE_CXX98_KNOWN_FEATURES") { + return &FOR_EACH_CXX98_FEATURE(STRING_LIST_ELEMENT)[1]; + } + if (prop == "CMAKE_CXX11_KNOWN_FEATURES") { + return &FOR_EACH_CXX11_FEATURE(STRING_LIST_ELEMENT)[1]; + } + if (prop == "CMAKE_CXX14_KNOWN_FEATURES") { + return &FOR_EACH_CXX14_FEATURE(STRING_LIST_ELEMENT)[1]; + } + #undef STRING_LIST_ELEMENT return this->GlobalProperties.GetPropertyValue(prop); } diff --git a/Source/cmState.h b/Source/cmState.h index f755f83..6abe71c 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -35,6 +35,9 @@ public: cmState(); ~cmState(); + cmState(const cmState&) = delete; + cmState& operator=(const cmState&) = delete; + enum Mode { Unknown, @@ -104,7 +107,7 @@ public: void RemoveCacheEntryProperty(std::string const& key, std::string const& propertyName); - ///! Break up a line like VAR:type="value" into var, type and value + //! Break up a line like VAR:type="value" into var, type and value static bool ParseCacheEntry(const std::string& entry, std::string& var, std::string& value, cmStateEnums::CacheEntryType& type); diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx index 6752743..31273cc 100644 --- a/Source/cmStateDirectory.cxx +++ b/Source/cmStateDirectory.cxx @@ -8,8 +8,10 @@ #include <iterator> #include <utility> +#include "cmAlgorithms.h" #include "cmProperty.h" #include "cmPropertyMap.h" +#include "cmRange.h" #include "cmState.h" #include "cmStatePrivate.h" #include "cmStateTypes.h" @@ -40,9 +42,8 @@ void cmStateDirectory::ComputeRelativePathTopSource() std::string result = snapshots.front().GetDirectory().GetCurrentSource(); - for (std::vector<cmStateSnapshot>::const_iterator it = snapshots.begin() + 1; - it != snapshots.end(); ++it) { - std::string currentSource = it->GetDirectory().GetCurrentSource(); + for (cmStateSnapshot const& snp : cmMakeRange(snapshots).advance(1)) { + std::string currentSource = snp.GetDirectory().GetCurrentSource(); if (cmSystemTools::IsSubDirectory(result, currentSource)) { result = currentSource; } @@ -66,9 +67,8 @@ void cmStateDirectory::ComputeRelativePathTopBinary() std::string result = snapshots.front().GetDirectory().GetCurrentBinary(); - for (std::vector<cmStateSnapshot>::const_iterator it = snapshots.begin() + 1; - it != snapshots.end(); ++it) { - std::string currentBinary = it->GetDirectory().GetCurrentBinary(); + for (cmStateSnapshot const& snp : cmMakeRange(snapshots).advance(1)) { + std::string currentBinary = snp.GetDirectory().GetCurrentBinary(); if (cmSystemTools::IsSubDirectory(result, currentBinary)) { result = currentBinary; } diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index 91d6190..998f904 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx @@ -1,9 +1,13 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ +#define _SCL_SECURE_NO_WARNINGS + #include "cmStringCommand.h" #include "cmsys/RegularExpression.hxx" +#include <algorithm> #include <ctype.h> +#include <iterator> #include <memory> // IWYU pragma: keep #include <sstream> #include <stdio.h> @@ -13,6 +17,8 @@ #include "cmCryptoHash.h" #include "cmGeneratorExpression.h" #include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmRange.h" #include "cmStringReplaceHelper.h" #include "cmSystemTools.h" #include "cmTimestamp.h" @@ -78,6 +84,9 @@ bool cmStringCommand::InitialPass(std::vector<std::string> const& args, if (subCommand == "STRIP") { return this->HandleStripCommand(args); } + if (subCommand == "REPEAT") { + return this->HandleRepeatCommand(args); + } if (subCommand == "RANDOM") { return this->HandleRandomCommand(args); } @@ -708,6 +717,59 @@ bool cmStringCommand::HandleStripCommand(std::vector<std::string> const& args) return true; } +bool cmStringCommand::HandleRepeatCommand(std::vector<std::string> const& args) +{ + // `string(REPEAT "<str>" <times> OUTPUT_VARIABLE)` + enum ArgPos : std::size_t + { + SUB_COMMAND, + VALUE, + TIMES, + OUTPUT_VARIABLE, + TOTAL_ARGS + }; + + if (args.size() != ArgPos::TOTAL_ARGS) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + "sub-command REPEAT requires three arguments."); + return true; + } + + unsigned long times; + if (!cmSystemTools::StringToULong(args[ArgPos::TIMES].c_str(), ×)) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "repeat count is not a positive number."); + return true; + } + + const auto& stringValue = args[ArgPos::VALUE]; + const auto& variableName = args[ArgPos::OUTPUT_VARIABLE]; + const auto inStringLength = stringValue.size(); + + std::string result; + switch (inStringLength) { + case 0u: + // Nothing to do for zero length input strings + break; + case 1u: + // NOTE If the string to repeat consists of the only character, + // use the appropriate constructor. + result = std::string(times, stringValue[0]); + break; + default: + result = std::string(inStringLength * times, char{}); + for (auto i = 0u; i < times; ++i) { + std::copy(cm::cbegin(stringValue), cm::cend(stringValue), + &result[i * inStringLength]); + } + break; + } + + this->Makefile->AddDefinition(variableName, result.c_str()); + return true; +} + bool cmStringCommand::HandleRandomCommand(std::vector<std::string> const& args) { if (args.size() < 2 || args.size() == 3 || args.size() == 5) { @@ -771,7 +833,7 @@ bool cmStringCommand::HandleRandomCommand(std::vector<std::string> const& args) } result.push_back(0); - this->Makefile->AddDefinition(variableName, &*result.begin()); + this->Makefile->AddDefinition(variableName, result.data()); return true; } diff --git a/Source/cmStringCommand.h b/Source/cmStringCommand.h index cbff73e..acde605 100644 --- a/Source/cmStringCommand.h +++ b/Source/cmStringCommand.h @@ -51,6 +51,7 @@ protected: bool HandleConcatCommand(std::vector<std::string> const& args); bool HandleJoinCommand(std::vector<std::string> const& args); bool HandleStripCommand(std::vector<std::string> const& args); + bool HandleRepeatCommand(std::vector<std::string> const& args); bool HandleRandomCommand(std::vector<std::string> const& args); bool HandleFindCommand(std::vector<std::string> const& args); bool HandleTimestampCommand(std::vector<std::string> const& args); diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 1d20e2f..17ed3f6 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -5,6 +5,7 @@ #include "cmAlgorithms.h" #include "cmDuration.h" #include "cmProcessOutput.h" +#include "cmRange.h" #include "cm_sys_stat.h" #include "cm_uv.h" @@ -42,6 +43,7 @@ #include <assert.h> #include <ctype.h> #include <errno.h> +#include <fcntl.h> #include <iostream> #include <sstream> #include <stdio.h> @@ -55,8 +57,6 @@ # include <windows.h> // include wincrypt.h after windows.h # include <wincrypt.h> - -# include <fcntl.h> /* _O_TEXT */ #else # include <sys/time.h> # include <unistd.h> @@ -167,7 +167,6 @@ bool cmSystemTools::s_RunCommandHideConsole = false; bool cmSystemTools::s_DisableRunCommandOutput = false; bool cmSystemTools::s_ErrorOccured = false; bool cmSystemTools::s_FatalErrorOccured = false; -bool cmSystemTools::s_DisableMessages = false; bool cmSystemTools::s_ForceUnixPaths = false; // replace replace with with as many times as it shows up in source. @@ -323,16 +322,13 @@ void cmSystemTools::Stdout(const std::string& s) } } -void cmSystemTools::Message(const char* m1, const char* title) +void cmSystemTools::Message(const std::string& m, const char* title) { - if (s_DisableMessages) { - return; - } if (s_MessageCallback) { - s_MessageCallback(m1, title); - return; + s_MessageCallback(m, title); + } else { + std::cerr << m << std::endl; } - std::cerr << m1 << std::endl << std::flush; } void cmSystemTools::ReportLastSystemError(const char* msg) @@ -521,6 +517,8 @@ public: } free(this->ArgV); } + cmSystemToolsArgV(const cmSystemToolsArgV&) = delete; + cmSystemToolsArgV& operator=(const cmSystemToolsArgV&) = delete; void Store(std::vector<std::string>& args) const { for (char** arg = this->ArgV; arg && *arg; ++arg) { @@ -533,7 +531,7 @@ void cmSystemTools::ParseUnixCommandLine(const char* command, std::vector<std::string>& args) { // Invoke the underlying parser. - cmSystemToolsArgV argv = cmsysSystem_Parse_CommandForUnix(command, 0); + cmSystemToolsArgV argv(cmsysSystem_Parse_CommandForUnix(command, 0)); argv.Store(args); } @@ -542,8 +540,7 @@ std::vector<std::string> cmSystemTools::HandleResponseFile( std::vector<std::string>::const_iterator argEnd) { std::vector<std::string> arg_full; - for (std::vector<std::string>::const_iterator a = argBeg; a != argEnd; ++a) { - std::string const& arg = *a; + for (std::string const& arg : cmMakeRange(argBeg, argEnd)) { if (cmHasLiteralPrefix(arg, "@")) { cmsys::ifstream responseFile(arg.substr(1).c_str(), std::ios::in); if (!responseFile) { @@ -570,13 +567,14 @@ std::vector<std::string> cmSystemTools::HandleResponseFile( return arg_full; } -std::vector<std::string> cmSystemTools::ParseArguments(const char* command) +std::vector<std::string> cmSystemTools::ParseArguments(const std::string& cmd) { std::vector<std::string> args; std::string arg; bool win_path = false; + const char* command = cmd.c_str(); if (command[0] && command[1] && ((command[0] != '/' && command[1] == ':' && command[2] == '\\') || (command[0] == '\"' && command[1] != '/' && command[2] == ':' && @@ -740,7 +738,7 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, argv.push_back(nullptr); cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, &*argv.begin()); + cmsysProcess_SetCommand(cp, argv.data()); cmsysProcess_SetWorkingDirectory(cp, dir); if (cmSystemTools::GetRunCommandHideConsole()) { cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); @@ -867,7 +865,7 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command, return result; } -bool cmSystemTools::RunSingleCommand(const char* command, +bool cmSystemTools::RunSingleCommand(const std::string& command, std::string* captureStdOut, std::string* captureStdErr, int* retVal, const char* dir, OutputOption outputflag, @@ -897,7 +895,7 @@ std::string cmSystemTools::PrintSingleCommand( } bool cmSystemTools::DoesFileExistWithExtensions( - const char* name, const std::vector<std::string>& headerExts) + const std::string& name, const std::vector<std::string>& headerExts) { std::string hname; @@ -912,9 +910,9 @@ bool cmSystemTools::DoesFileExistWithExtensions( return false; } -std::string cmSystemTools::FileExistsInParentDirectories(const char* fname, - const char* directory, - const char* toplevel) +std::string cmSystemTools::FileExistsInParentDirectories( + const std::string& fname, const std::string& directory, + const std::string& toplevel) { std::string file = fname; cmSystemTools::ConvertToUnixSlashes(file); @@ -926,7 +924,7 @@ std::string cmSystemTools::FileExistsInParentDirectories(const char* fname, if (cmSystemTools::FileExists(path)) { return path; } - if (dir.size() < strlen(toplevel)) { + if (dir.size() < toplevel.size()) { break; } prevDir = dir; @@ -935,12 +933,6 @@ std::string cmSystemTools::FileExistsInParentDirectories(const char* fname, return ""; } -bool cmSystemTools::cmCopyFile(const std::string& source, - const std::string& destination) -{ - return Superclass::CopyFileAlways(source, destination); -} - #ifdef _WIN32 cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry() { @@ -1219,9 +1211,8 @@ void cmSystemTools::GlobDirs(const std::string& path, void cmSystemTools::ExpandList(std::vector<std::string> const& arguments, std::vector<std::string>& newargs) { - std::vector<std::string>::const_iterator i; - for (i = arguments.begin(); i != arguments.end(); ++i) { - cmSystemTools::ExpandListArgument(*i, newargs); + for (std::string const& arg : arguments) { + cmSystemTools::ExpandListArgument(arg, newargs); } } @@ -1389,14 +1380,6 @@ cmSystemTools::FileFormat cmSystemTools::GetFileFormat(std::string const& ext) return cmSystemTools::UNKNOWN_FILE_FORMAT; } -bool cmSystemTools::Split(const char* s, std::vector<std::string>& l) -{ - std::vector<std::string> temp; - bool res = Superclass::Split(s, temp); - l.insert(l.end(), temp.begin(), temp.end()); - return res; -} - std::string cmSystemTools::ConvertToOutputPath(std::string const& path) { #if defined(_WIN32) && !defined(__CYGWIN__) @@ -1424,7 +1407,7 @@ void cmSystemTools::ConvertToOutputSlashes(std::string& path) #endif } -std::string cmSystemTools::ConvertToRunCommandPath(const char* path) +std::string cmSystemTools::ConvertToRunCommandPath(const std::string& path) { #if defined(_WIN32) && !defined(__CYGWIN__) return cmSystemTools::ConvertToWindowsOutputPath(path); @@ -1438,12 +1421,12 @@ std::string cmSystemTools::RelativePath(std::string const& local, std::string const& remote) { if (!cmSystemTools::FileIsFullPath(local)) { - cmSystemTools::Error("RelativePath must be passed a full path to local: ", - local.c_str()); + cmSystemTools::Error("RelativePath must be passed a full path to local: " + + local); } if (!cmSystemTools::FileIsFullPath(remote)) { - cmSystemTools::Error("RelativePath must be passed a full path to remote: ", - remote.c_str()); + cmSystemTools::Error( + "RelativePath must be passed a full path to remote: " + remote); } return cmsys::SystemTools::RelativePath(local, remote); } @@ -1522,36 +1505,6 @@ std::string cmSystemTools::ForceToRelativePath(std::string const& local_path, return relative; } -std::string cmSystemTools::CollapseCombinedPath(std::string const& dir, - std::string const& file) -{ - if (dir.empty() || dir == ".") { - return file; - } - - std::vector<std::string> dirComponents; - std::vector<std::string> fileComponents; - cmSystemTools::SplitPath(dir, dirComponents); - cmSystemTools::SplitPath(file, fileComponents); - - if (fileComponents.empty()) { - return dir; - } - if (!fileComponents[0].empty()) { - // File is not a relative path. - return file; - } - - std::vector<std::string>::iterator i = fileComponents.begin() + 1; - while (i != fileComponents.end() && *i == ".." && dirComponents.size() > 1) { - ++i; // Remove ".." file component. - dirComponents.pop_back(); // Remove last dir component. - } - - dirComponents.insert(dirComponents.end(), i, fileComponents.end()); - return cmSystemTools::JoinPath(dirComponents); -} - #ifdef CMAKE_BUILD_WITH_CMAKE bool cmSystemTools::UnsetEnv(const char* value) { @@ -1627,7 +1580,7 @@ void cmSystemTools::EnableVSConsoleOutput() #endif } -bool cmSystemTools::IsPathToFramework(const char* path) +bool cmSystemTools::IsPathToFramework(const std::string& path) { return (cmSystemTools::FileIsFullPath(path) && cmHasLiteralSuffix(path, ".framework")); @@ -1670,20 +1623,18 @@ bool cmSystemTools::CreateTar(const char* outFileName, a.SetMTime(mtime); a.SetVerbose(verbose); + bool tarCreatedSuccessfully = true; for (auto path : files) { if (cmSystemTools::FileIsFullPath(path)) { // Get the relative path to the file. path = cmSystemTools::RelativePath(cwd, path); } if (!a.Add(path)) { - break; + cmSystemTools::Error(a.GetError()); + tarCreatedSuccessfully = false; } } - if (!a) { - cmSystemTools::Error(a.GetError()); - return false; - } - return true; + return tarCreatedSuccessfully; #else (void)outFileName; (void)files; @@ -1922,8 +1873,8 @@ bool extract_tar(const char* outFileName, bool verbose, bool extract) else { cmSystemTools::Error("Problem with archive_write_header(): ", archive_error_string(ext)); - cmSystemTools::Error("Current file: ", - cm_archive_entry_pathname(entry).c_str()); + cmSystemTools::Error("Current file: " + + cm_archive_entry_pathname(entry)); break; } } @@ -2055,6 +2006,71 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line, } } +#ifdef _WIN32 +static void EnsureStdPipe(DWORD fd) +{ + if (GetStdHandle(fd) != INVALID_HANDLE_VALUE) { + return; + } + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + HANDLE h = CreateFileW( + L"NUL", + fd == STD_INPUT_HANDLE ? FILE_GENERIC_READ + : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, NULL); + + if (h == INVALID_HANDLE_VALUE) { + LPSTR message = NULL; + DWORD size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&message, 0, NULL); + std::string msg = std::string(message, size); + LocalFree(message); + std::cerr << "failed to open NUL for missing stdio pipe: " << msg; + abort(); + } + + SetStdHandle(fd, h); +} + +void cmSystemTools::EnsureStdPipes() +{ + EnsureStdPipe(STD_INPUT_HANDLE); + EnsureStdPipe(STD_OUTPUT_HANDLE); + EnsureStdPipe(STD_ERROR_HANDLE); +} +#else +static void EnsureStdPipe(int fd) +{ + if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) { + return; + } + + int f = open("/dev/null", fd == STDIN_FILENO ? O_RDONLY : O_WRONLY); + if (f == -1) { + perror("failed to open /dev/null for missing stdio pipe"); + abort(); + } + if (f != fd) { + dup2(f, fd); + close(f); + } +} + +void cmSystemTools::EnsureStdPipes() +{ + EnsureStdPipe(STDIN_FILENO); + EnsureStdPipe(STDOUT_FILENO); + EnsureStdPipe(STDERR_FILENO); +} +#endif + void cmSystemTools::DoNotInheritStdPipes() { #ifdef _WIN32 @@ -2082,7 +2098,8 @@ void cmSystemTools::DoNotInheritStdPipes() #endif } -bool cmSystemTools::CopyFileTime(const char* fromFile, const char* toFile) +bool cmSystemTools::CopyFileTime(const std::string& fromFile, + const std::string& toFile) { #if defined(_WIN32) && !defined(__CYGWIN__) cmSystemToolsWindowsHandle hFrom = CreateFileW( @@ -2103,14 +2120,14 @@ bool cmSystemTools::CopyFileTime(const char* fromFile, const char* toFile) return SetFileTime(hTo, &timeCreation, &timeLastAccess, &timeLastWrite) != 0; #else struct stat fromStat; - if (stat(fromFile, &fromStat) < 0) { + if (stat(fromFile.c_str(), &fromStat) < 0) { return false; } struct utimbuf buf; buf.actime = fromStat.st_atime; buf.modtime = fromStat.st_mtime; - return utime(toFile, &buf) >= 0; + return utime(toFile.c_str(), &buf) >= 0; #endif } @@ -2124,7 +2141,8 @@ void cmSystemTools::FileTimeDelete(cmSystemToolsFileTime* t) delete t; } -bool cmSystemTools::FileTimeGet(const char* fname, cmSystemToolsFileTime* t) +bool cmSystemTools::FileTimeGet(const std::string& fname, + cmSystemToolsFileTime* t) { #if defined(_WIN32) && !defined(__CYGWIN__) cmSystemToolsWindowsHandle h = CreateFileW( @@ -2139,7 +2157,7 @@ bool cmSystemTools::FileTimeGet(const char* fname, cmSystemToolsFileTime* t) } #else struct stat st; - if (stat(fname, &st) < 0) { + if (stat(fname.c_str(), &st) < 0) { return false; } t->timeBuf.actime = st.st_atime; @@ -2148,7 +2166,8 @@ bool cmSystemTools::FileTimeGet(const char* fname, cmSystemToolsFileTime* t) return true; } -bool cmSystemTools::FileTimeSet(const char* fname, cmSystemToolsFileTime* t) +bool cmSystemTools::FileTimeSet(const std::string& fname, + const cmSystemToolsFileTime* t) { #if defined(_WIN32) && !defined(__CYGWIN__) cmSystemToolsWindowsHandle h = CreateFileW( @@ -2160,7 +2179,7 @@ bool cmSystemTools::FileTimeSet(const char* fname, cmSystemToolsFileTime* t) return SetFileTime(h, &t->timeCreation, &t->timeLastAccess, &t->timeLastWrite) != 0; #else - return utime(fname, &t->timeBuf) >= 0; + return utime(fname.c_str(), &t->timeBuf) >= 0; #endif } @@ -3015,7 +3034,7 @@ bool cmSystemTools::CheckRPath(std::string const& file, #endif } -bool cmSystemTools::RepeatedRemoveDirectory(const char* dir) +bool cmSystemTools::RepeatedRemoveDirectory(const std::string& dir) { // Windows sometimes locks files temporarily so try a few times. for (int i = 0; i < 10; ++i) { @@ -3064,6 +3083,12 @@ bool cmSystemTools::StringToULong(const char* str, unsigned long* value) { errno = 0; char* endp; + while (isspace(*str)) { + ++str; + } + if (*str == '-') { + return false; + } *value = strtoul(str, &endp, 10); return (*endp == '\0') && (endp != str) && (errno == 0); } diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 0f92fe2..8a87a37 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -45,7 +45,7 @@ public: static void ExpandRegistryValues(std::string& source, KeyWOW64 view = KeyWOW64_Default); - ///! Escape quotes in a string. + //! Escape quotes in a string. static std::string EscapeQuotes(const std::string& str); /** Map help document name to file name. */ @@ -56,7 +56,7 @@ public: */ static std::string TrimWhitespace(const std::string& s); - using MessageCallback = std::function<void(const char*, const char*)>; + using MessageCallback = std::function<void(const std::string&, const char*)>; /** * Set the function used by GUIs to display error messages * Function gets passed: message as a const char*, @@ -74,19 +74,15 @@ public: /** * Display a message. */ - static void Message(const char* m, const char* title = nullptr); - static void Message(const std::string& m, const char* title = nullptr) - { - Message(m.c_str(), title); - } + static void Message(const std::string& m, const char* title = nullptr); using OutputCallback = std::function<void(std::string const&)>; - ///! Send a string to stdout + //! Send a string to stdout static void Stdout(const std::string& s); static void SetStdoutCallback(OutputCallback f); - ///! Send a string to stderr + //! Send a string to stderr static void Stderr(const std::string& s); static void SetStderrCallback(OutputCallback f); @@ -94,25 +90,25 @@ public: static void SetInterruptCallback(InterruptCallback f); static bool GetInterruptFlag(); - ///! Return true if there was an error at any point. + //! Return true if there was an error at any point. static bool GetErrorOccuredFlag() { return cmSystemTools::s_ErrorOccured || cmSystemTools::s_FatalErrorOccured || GetInterruptFlag(); } - ///! If this is set to true, cmake stops processing commands. + //! If this is set to true, cmake stops processing commands. static void SetFatalErrorOccured() { cmSystemTools::s_FatalErrorOccured = true; } static void SetErrorOccured() { cmSystemTools::s_ErrorOccured = true; } - ///! Return true if there was an error at any point. + //! Return true if there was an error at any point. static bool GetFatalErrorOccured() { return cmSystemTools::s_FatalErrorOccured || GetInterruptFlag(); } - ///! Set the error occurred flag and fatal error back to false + //! Set the error occurred flag and fatal error back to false static void ResetErrorOccuredFlag() { cmSystemTools::s_FatalErrorOccured = false; @@ -142,22 +138,22 @@ public: static bool IsOff(const char* val); static bool IsOff(const std::string& val); - ///! Return true if value is NOTFOUND or ends in -NOTFOUND. + //! Return true if value is NOTFOUND or ends in -NOTFOUND. static bool IsNOTFOUND(const char* value); - ///! Return true if the path is a framework - static bool IsPathToFramework(const char* value); + //! Return true if the path is a framework + static bool IsPathToFramework(const std::string& value); static bool DoesFileExistWithExtensions( - const char* name, const std::vector<std::string>& sourceExts); + const std::string& name, const std::vector<std::string>& sourceExts); /** * Check if the given file exists in one of the parent directory of the * given file or directory and if it does, return the name of the file. * Toplevel specifies the top-most directory to where it will look. */ - static std::string FileExistsInParentDirectories(const char* fname, - const char* directory, - const char* toplevel); + static std::string FileExistsInParentDirectories( + const std::string& fname, const std::string& directory, + const std::string& toplevel); static void Glob(const std::string& directory, const std::string& regexp, std::vector<std::string>& files); @@ -176,23 +172,19 @@ public: static bool SimpleGlob(const std::string& glob, std::vector<std::string>& files, int type = 0); - ///! Copy a file. - static bool cmCopyFile(const std::string& source, - const std::string& destination); - /** Rename a file or directory within a single disk volume (atomic if possible). */ static bool RenameFile(const std::string& oldname, const std::string& newname); - ///! Compute the hash of a file + //! Compute the hash of a file static std::string ComputeFileHash(const std::string& source, cmCryptoHash::Algo algo); /** Compute the md5sum of a string. */ static std::string ComputeStringMD5(const std::string& input); - ///! Get the SHA thumbprint for a certificate file + //! Get the SHA thumbprint for a certificate file static std::string ComputeCertificateThumbprint(const std::string& source); /** @@ -224,7 +216,7 @@ public: OUTPUT_FORWARD, OUTPUT_PASSTHROUGH }; - static bool RunSingleCommand(const char* command, + static bool RunSingleCommand(const std::string& command, std::string* captureStdOut = nullptr, std::string* captureStdErr = nullptr, int* retVal = nullptr, @@ -250,7 +242,7 @@ public: /** * Parse arguments out of a single string command */ - static std::vector<std::string> ParseArguments(const char* command); + static std::vector<std::string> ParseArguments(const std::string& command); /** Parse arguments out of a windows command line string. */ static void ParseWindowsCommandLine(const char* command, @@ -274,8 +266,6 @@ public: static size_t CalculateCommandLineLengthLimit(); - static void EnableMessages() { s_DisableMessages = false; } - static void DisableMessages() { s_DisableMessages = true; } static void DisableRunCommandOutput() { s_DisableRunCommandOutput = true; } static void EnableRunCommandOutput() { s_DisableRunCommandOutput = false; } static bool GetRunCommandOutput() { return s_DisableRunCommandOutput; } @@ -351,9 +341,6 @@ public: cmDuration timeout, std::vector<char>& out, std::vector<char>& err); - /** Split a string on its newlines into multiple lines. Returns - false only if the last line stored had no newline. */ - static bool Split(const char* s, std::vector<std::string>& l); static void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; } static bool GetForceUnixPaths() { return s_ForceUnixPaths; } @@ -364,7 +351,7 @@ public: // ConvertToRunCommandPath does not use s_ForceUnixPaths and should // be used when RunCommand is called from cmake, because the // running cmake needs paths to be in its format - static std::string ConvertToRunCommandPath(const char* path); + static std::string ConvertToRunCommandPath(const std::string& path); /** compute the relative path from local to remote. local must be a directory. remote can be a file or a directory. @@ -385,12 +372,6 @@ public: static std::string ForceToRelativePath(std::string const& local_path, std::string const& remote_path); - /** Joins two paths while collapsing x/../ parts - * For example CollapseCombinedPath("a/b/c", "../../d") results in "a/d" - */ - static std::string CollapseCombinedPath(std::string const& dir, - std::string const& file); - #ifdef CMAKE_BUILD_WITH_CMAKE /** Remove an environment variable */ static bool UnsetEnv(const char* value); @@ -423,6 +404,14 @@ public: /** Setup the environment to enable VS 8 IDE output. */ static void EnableVSConsoleOutput(); + enum cmTarAction + { + TarActionCreate, + TarActionList, + TarActionExtract, + TarActionNone + }; + /** Create tar */ enum cmTarCompression { @@ -431,6 +420,7 @@ public: TarCompressXZ, TarCompressNone }; + static bool ListTar(const char* outFileName, bool verbose); static bool CreateTar(const char* outFileName, const std::vector<std::string>& files, @@ -445,15 +435,19 @@ public: // not get stuck waiting for all the output on the pipes. static void DoNotInheritStdPipes(); + static void EnsureStdPipes(); + /** Copy the file create/access/modify times from the file named by the first argument to that named by the second. */ - static bool CopyFileTime(const char* fromFile, const char* toFile); + static bool CopyFileTime(const std::string& fromFile, + const std::string& toFile); /** Save and restore file times. */ static cmSystemToolsFileTime* FileTimeNew(); static void FileTimeDelete(cmSystemToolsFileTime*); - static bool FileTimeGet(const char* fname, cmSystemToolsFileTime* t); - static bool FileTimeSet(const char* fname, cmSystemToolsFileTime* t); + static bool FileTimeGet(const std::string& fname, cmSystemToolsFileTime* t); + static bool FileTimeSet(const std::string& fname, + const cmSystemToolsFileTime* t); /** Random seed generation. */ static unsigned int RandomSeed(); @@ -497,7 +491,7 @@ public: static bool CheckRPath(std::string const& file, std::string const& newRPath); /** Remove a directory; repeat a few times in case of locked files. */ - static bool RepeatedRemoveDirectory(const char* dir); + static bool RepeatedRemoveDirectory(const std::string& dir); /** Tokenize a string */ static std::vector<std::string> tokenize(const std::string& str, @@ -546,7 +540,6 @@ private: static bool s_RunCommandHideConsole; static bool s_ErrorOccured; static bool s_FatalErrorOccured; - static bool s_DisableMessages; static bool s_DisableRunCommandOutput; }; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 93cdd46..9598a3f 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -5,6 +5,7 @@ #include "cmsys/RegularExpression.hxx" #include <algorithm> #include <assert.h> +#include <initializer_list> #include <iterator> #include <set> #include <sstream> @@ -12,6 +13,7 @@ #include <unordered_set> #include "cmAlgorithms.h" +#include "cmCustomCommand.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -20,6 +22,8 @@ #include "cmMessageType.h" #include "cmMessenger.h" #include "cmProperty.h" +#include "cmPropertyMap.h" +#include "cmRange.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" @@ -30,6 +34,13 @@ #include "cmTargetPropertyComputer.h" #include "cmake.h" +//! Append all elements from the second container to the first container +template <class C, class R> +static inline void CApp(C& container, R const& range) +{ + container.insert(container.end(), range.begin(), range.end()); +} + template <> const char* cmTargetPropertyComputer::ComputeLocationForBuild<cmTarget>( cmTarget const* tgt) @@ -155,6 +166,26 @@ const char* cmTargetPropertyComputer::GetSources<cmTarget>( class cmTargetInternals { public: + cmStateEnums::TargetType TargetType; + cmMakefile* Makefile; + cmPolicies::PolicyMap PolicyMap; + std::string Name; + std::string InstallPath; + std::string RuntimeInstallPath; + cmPropertyMap Properties; + bool IsGeneratorProvided; + bool HaveInstallRule; + bool DLLPlatform; + bool IsAndroid; + bool IsImportedTarget; + bool ImportedGloballyVisible; + bool BuildInterfaceIncludesAppended; + std::set<BT<std::string>> Utilities; + std::vector<cmCustomCommand> PreBuildCommands; + std::vector<cmCustomCommand> PreLinkCommands; + std::vector<cmCustomCommand> PostBuildCommands; + std::set<std::string> SystemIncludeDirectories; + cmTarget::LinkLibraryVectorType OriginalLinkLibraries; std::vector<std::string> IncludeDirectoriesEntries; std::vector<cmListFileBacktrace> IncludeDirectoriesBacktraces; std::vector<std::string> CompileOptionsEntries; @@ -171,166 +202,185 @@ public: std::vector<cmListFileBacktrace> LinkDirectoriesBacktraces; std::vector<std::string> LinkImplementationPropertyEntries; std::vector<cmListFileBacktrace> LinkImplementationPropertyBacktraces; + std::vector<std::pair<cmTarget::TLLSignature, cmListFileContext>> + TLLCommands; + cmListFileBacktrace Backtrace; + +public: + bool CheckImportedLibName(std::string const& prop, + std::string const& value) const; + + std::string ProcessSourceItemCMP0049(const std::string& s); }; cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, Visibility vis, cmMakefile* mf) + : impl(cm::make_unique<cmTargetInternals>()) { assert(mf); - this->IsGeneratorProvided = false; - this->Name = name; - this->TargetTypeValue = type; - this->Makefile = mf; - this->HaveInstallRule = false; - this->DLLPlatform = false; - this->IsAndroid = false; - this->IsImportedTarget = + impl->TargetType = type; + impl->Makefile = mf; + impl->Name = name; + impl->IsGeneratorProvided = false; + impl->HaveInstallRule = false; + impl->DLLPlatform = false; + impl->IsAndroid = false; + impl->IsImportedTarget = (vis == VisibilityImported || vis == VisibilityImportedGlobally); - this->ImportedGloballyVisible = vis == VisibilityImportedGlobally; - this->BuildInterfaceIncludesAppended = false; + impl->ImportedGloballyVisible = vis == VisibilityImportedGlobally; + impl->BuildInterfaceIncludesAppended = false; // Check whether this is a DLL platform. - this->DLLPlatform = - !this->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty(); + impl->DLLPlatform = + !impl->Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty(); // Check whether we are targeting an Android platform. - this->IsAndroid = - (this->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android"); + impl->IsAndroid = + (impl->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android"); + + std::string gKey; + gKey.reserve(128); + gKey += "CMAKE_"; + auto InitProperty = [this, mf, &gKey](const std::string& property, + const char* default_value) { + // Replace everything after "CMAKE_" + gKey.replace(gKey.begin() + 6, gKey.end(), property); + if (const char* value = mf->GetDefinition(gKey)) { + this->SetProperty(property, value); + } else if (default_value) { + this->SetProperty(property, default_value); + } + }; // Setup default property values. if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - this->SetPropertyDefault("ANDROID_API", nullptr); - this->SetPropertyDefault("ANDROID_API_MIN", nullptr); - this->SetPropertyDefault("ANDROID_ARCH", nullptr); - this->SetPropertyDefault("ANDROID_STL_TYPE", nullptr); - this->SetPropertyDefault("ANDROID_SKIP_ANT_STEP", nullptr); - this->SetPropertyDefault("ANDROID_PROCESS_MAX", nullptr); - this->SetPropertyDefault("ANDROID_PROGUARD", nullptr); - this->SetPropertyDefault("ANDROID_PROGUARD_CONFIG_PATH", nullptr); - this->SetPropertyDefault("ANDROID_SECURE_PROPS_PATH", nullptr); - this->SetPropertyDefault("ANDROID_NATIVE_LIB_DIRECTORIES", nullptr); - this->SetPropertyDefault("ANDROID_NATIVE_LIB_DEPENDENCIES", nullptr); - this->SetPropertyDefault("ANDROID_JAVA_SOURCE_DIR", nullptr); - this->SetPropertyDefault("ANDROID_JAR_DIRECTORIES", nullptr); - this->SetPropertyDefault("ANDROID_JAR_DEPENDENCIES", nullptr); - this->SetPropertyDefault("ANDROID_ASSETS_DIRECTORIES", nullptr); - this->SetPropertyDefault("ANDROID_ANT_ADDITIONAL_OPTIONS", nullptr); - this->SetPropertyDefault("BUILD_RPATH", nullptr); - this->SetPropertyDefault("BUILD_RPATH_USE_ORIGIN", nullptr); - this->SetPropertyDefault("INSTALL_NAME_DIR", nullptr); - this->SetPropertyDefault("INSTALL_RPATH", ""); - this->SetPropertyDefault("INSTALL_RPATH_USE_LINK_PATH", "OFF"); - this->SetPropertyDefault("INTERPROCEDURAL_OPTIMIZATION", nullptr); - this->SetPropertyDefault("SKIP_BUILD_RPATH", "OFF"); - this->SetPropertyDefault("BUILD_WITH_INSTALL_RPATH", "OFF"); - this->SetPropertyDefault("ARCHIVE_OUTPUT_DIRECTORY", nullptr); - this->SetPropertyDefault("LIBRARY_OUTPUT_DIRECTORY", nullptr); - this->SetPropertyDefault("RUNTIME_OUTPUT_DIRECTORY", nullptr); - this->SetPropertyDefault("PDB_OUTPUT_DIRECTORY", nullptr); - this->SetPropertyDefault("COMPILE_PDB_OUTPUT_DIRECTORY", nullptr); - this->SetPropertyDefault("Fortran_FORMAT", nullptr); - this->SetPropertyDefault("Fortran_MODULE_DIRECTORY", nullptr); - this->SetPropertyDefault("Fortran_COMPILER_LAUNCHER", nullptr); - this->SetPropertyDefault("GNUtoMS", nullptr); - this->SetPropertyDefault("OSX_ARCHITECTURES", nullptr); - this->SetPropertyDefault("IOS_INSTALL_COMBINED", nullptr); - this->SetPropertyDefault("AUTOMOC", nullptr); - this->SetPropertyDefault("AUTOUIC", nullptr); - this->SetPropertyDefault("AUTORCC", nullptr); - this->SetPropertyDefault("AUTOGEN_ORIGIN_DEPENDS", nullptr); - this->SetPropertyDefault("AUTOGEN_PARALLEL", nullptr); - this->SetPropertyDefault("AUTOMOC_COMPILER_PREDEFINES", nullptr); - this->SetPropertyDefault("AUTOMOC_DEPEND_FILTERS", nullptr); - this->SetPropertyDefault("AUTOMOC_MACRO_NAMES", nullptr); - this->SetPropertyDefault("AUTOMOC_MOC_OPTIONS", nullptr); - this->SetPropertyDefault("AUTOUIC_OPTIONS", nullptr); - this->SetPropertyDefault("AUTOUIC_SEARCH_PATHS", nullptr); - this->SetPropertyDefault("AUTORCC_OPTIONS", nullptr); - this->SetPropertyDefault("LINK_DEPENDS_NO_SHARED", nullptr); - this->SetPropertyDefault("LINK_INTERFACE_LIBRARIES", nullptr); - this->SetPropertyDefault("WIN32_EXECUTABLE", nullptr); - this->SetPropertyDefault("MACOSX_BUNDLE", nullptr); - this->SetPropertyDefault("MACOSX_RPATH", nullptr); - this->SetPropertyDefault("NO_SYSTEM_FROM_IMPORTED", nullptr); - this->SetPropertyDefault("BUILD_WITH_INSTALL_NAME_DIR", nullptr); - this->SetPropertyDefault("C_CLANG_TIDY", nullptr); - this->SetPropertyDefault("C_COMPILER_LAUNCHER", nullptr); - this->SetPropertyDefault("C_CPPLINT", nullptr); - this->SetPropertyDefault("C_CPPCHECK", nullptr); - this->SetPropertyDefault("C_INCLUDE_WHAT_YOU_USE", nullptr); - this->SetPropertyDefault("LINK_WHAT_YOU_USE", nullptr); - this->SetPropertyDefault("C_STANDARD", nullptr); - this->SetPropertyDefault("C_STANDARD_REQUIRED", nullptr); - this->SetPropertyDefault("C_EXTENSIONS", nullptr); - this->SetPropertyDefault("CXX_CLANG_TIDY", nullptr); - this->SetPropertyDefault("CXX_COMPILER_LAUNCHER", nullptr); - this->SetPropertyDefault("CXX_CPPLINT", nullptr); - this->SetPropertyDefault("CXX_CPPCHECK", nullptr); - this->SetPropertyDefault("CXX_INCLUDE_WHAT_YOU_USE", nullptr); - this->SetPropertyDefault("CXX_STANDARD", nullptr); - this->SetPropertyDefault("CXX_STANDARD_REQUIRED", nullptr); - this->SetPropertyDefault("CXX_EXTENSIONS", nullptr); - this->SetPropertyDefault("CUDA_STANDARD", nullptr); - this->SetPropertyDefault("CUDA_STANDARD_REQUIRED", nullptr); - this->SetPropertyDefault("CUDA_EXTENSIONS", nullptr); - this->SetPropertyDefault("CUDA_COMPILER_LAUNCHER", nullptr); - this->SetPropertyDefault("CUDA_SEPARABLE_COMPILATION", nullptr); - this->SetPropertyDefault("LINK_SEARCH_START_STATIC", nullptr); - this->SetPropertyDefault("LINK_SEARCH_END_STATIC", nullptr); - this->SetPropertyDefault("FOLDER", nullptr); + InitProperty("ANDROID_API", nullptr); + InitProperty("ANDROID_API_MIN", nullptr); + InitProperty("ANDROID_ARCH", nullptr); + InitProperty("ANDROID_STL_TYPE", nullptr); + InitProperty("ANDROID_SKIP_ANT_STEP", nullptr); + InitProperty("ANDROID_PROCESS_MAX", nullptr); + InitProperty("ANDROID_PROGUARD", nullptr); + InitProperty("ANDROID_PROGUARD_CONFIG_PATH", nullptr); + InitProperty("ANDROID_SECURE_PROPS_PATH", nullptr); + InitProperty("ANDROID_NATIVE_LIB_DIRECTORIES", nullptr); + InitProperty("ANDROID_NATIVE_LIB_DEPENDENCIES", nullptr); + InitProperty("ANDROID_JAVA_SOURCE_DIR", nullptr); + InitProperty("ANDROID_JAR_DIRECTORIES", nullptr); + InitProperty("ANDROID_JAR_DEPENDENCIES", nullptr); + InitProperty("ANDROID_ASSETS_DIRECTORIES", nullptr); + InitProperty("ANDROID_ANT_ADDITIONAL_OPTIONS", nullptr); + InitProperty("BUILD_RPATH", nullptr); + InitProperty("BUILD_RPATH_USE_ORIGIN", nullptr); + InitProperty("INSTALL_NAME_DIR", nullptr); + InitProperty("INSTALL_RPATH", ""); + InitProperty("INSTALL_RPATH_USE_LINK_PATH", "OFF"); + InitProperty("INTERPROCEDURAL_OPTIMIZATION", nullptr); + InitProperty("SKIP_BUILD_RPATH", "OFF"); + InitProperty("BUILD_WITH_INSTALL_RPATH", "OFF"); + InitProperty("ARCHIVE_OUTPUT_DIRECTORY", nullptr); + InitProperty("LIBRARY_OUTPUT_DIRECTORY", nullptr); + InitProperty("RUNTIME_OUTPUT_DIRECTORY", nullptr); + InitProperty("PDB_OUTPUT_DIRECTORY", nullptr); + InitProperty("COMPILE_PDB_OUTPUT_DIRECTORY", nullptr); + InitProperty("Fortran_FORMAT", nullptr); + InitProperty("Fortran_MODULE_DIRECTORY", nullptr); + InitProperty("Fortran_COMPILER_LAUNCHER", nullptr); + InitProperty("GNUtoMS", nullptr); + InitProperty("OSX_ARCHITECTURES", nullptr); + InitProperty("IOS_INSTALL_COMBINED", nullptr); + InitProperty("AUTOMOC", nullptr); + InitProperty("AUTOUIC", nullptr); + InitProperty("AUTORCC", nullptr); + InitProperty("AUTOGEN_ORIGIN_DEPENDS", nullptr); + InitProperty("AUTOGEN_PARALLEL", nullptr); + InitProperty("AUTOMOC_COMPILER_PREDEFINES", nullptr); + InitProperty("AUTOMOC_DEPEND_FILTERS", nullptr); + InitProperty("AUTOMOC_MACRO_NAMES", nullptr); + InitProperty("AUTOMOC_MOC_OPTIONS", nullptr); + InitProperty("AUTOUIC_OPTIONS", nullptr); + InitProperty("AUTOUIC_SEARCH_PATHS", nullptr); + InitProperty("AUTORCC_OPTIONS", nullptr); + InitProperty("LINK_DEPENDS_NO_SHARED", nullptr); + InitProperty("LINK_INTERFACE_LIBRARIES", nullptr); + InitProperty("MSVC_RUNTIME_LIBRARY", nullptr); + InitProperty("WIN32_EXECUTABLE", nullptr); + InitProperty("MACOSX_BUNDLE", nullptr); + InitProperty("MACOSX_RPATH", nullptr); + InitProperty("NO_SYSTEM_FROM_IMPORTED", nullptr); + InitProperty("BUILD_WITH_INSTALL_NAME_DIR", nullptr); + InitProperty("C_CLANG_TIDY", nullptr); + InitProperty("C_COMPILER_LAUNCHER", nullptr); + InitProperty("C_CPPLINT", nullptr); + InitProperty("C_CPPCHECK", nullptr); + InitProperty("C_INCLUDE_WHAT_YOU_USE", nullptr); + InitProperty("LINK_WHAT_YOU_USE", nullptr); + InitProperty("C_STANDARD", nullptr); + InitProperty("C_STANDARD_REQUIRED", nullptr); + InitProperty("C_EXTENSIONS", nullptr); + InitProperty("CXX_CLANG_TIDY", nullptr); + InitProperty("CXX_COMPILER_LAUNCHER", nullptr); + InitProperty("CXX_CPPLINT", nullptr); + InitProperty("CXX_CPPCHECK", nullptr); + InitProperty("CXX_INCLUDE_WHAT_YOU_USE", nullptr); + InitProperty("CXX_STANDARD", nullptr); + InitProperty("CXX_STANDARD_REQUIRED", nullptr); + InitProperty("CXX_EXTENSIONS", nullptr); + InitProperty("CUDA_STANDARD", nullptr); + InitProperty("CUDA_STANDARD_REQUIRED", nullptr); + InitProperty("CUDA_EXTENSIONS", nullptr); + InitProperty("CUDA_COMPILER_LAUNCHER", nullptr); + InitProperty("CUDA_SEPARABLE_COMPILATION", nullptr); + InitProperty("LINK_SEARCH_START_STATIC", nullptr); + InitProperty("LINK_SEARCH_END_STATIC", nullptr); + InitProperty("FOLDER", nullptr); #ifdef __APPLE__ if (this->GetGlobalGenerator()->IsXcode()) { - this->SetPropertyDefault("XCODE_SCHEME_ADDRESS_SANITIZER", nullptr); - this->SetPropertyDefault( - "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN", nullptr); - this->SetPropertyDefault("XCODE_SCHEME_THREAD_SANITIZER", nullptr); - this->SetPropertyDefault("XCODE_SCHEME_THREAD_SANITIZER_STOP", nullptr); - this->SetPropertyDefault("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER", - nullptr); - this->SetPropertyDefault( - "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP", nullptr); - this->SetPropertyDefault("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER", - nullptr); - this->SetPropertyDefault("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP", - nullptr); - this->SetPropertyDefault("XCODE_SCHEME_MALLOC_SCRIBBLE", nullptr); - this->SetPropertyDefault("XCODE_SCHEME_MALLOC_GUARD_EDGES", nullptr); - this->SetPropertyDefault("XCODE_SCHEME_GUARD_MALLOC", nullptr); - this->SetPropertyDefault("XCODE_SCHEME_ZOMBIE_OBJECTS", nullptr); - this->SetPropertyDefault("XCODE_SCHEME_MALLOC_STACK", nullptr); - this->SetPropertyDefault("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE", - nullptr); - this->SetPropertyDefault("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS", nullptr); + InitProperty("XCODE_GENERATE_SCHEME", nullptr); + InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER", nullptr); + InitProperty("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN", nullptr); + InitProperty("XCODE_SCHEME_THREAD_SANITIZER", nullptr); + InitProperty("XCODE_SCHEME_THREAD_SANITIZER_STOP", nullptr); + InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER", nullptr); + InitProperty("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP", nullptr); + InitProperty("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER", nullptr); + InitProperty("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP", nullptr); + InitProperty("XCODE_SCHEME_MALLOC_SCRIBBLE", nullptr); + InitProperty("XCODE_SCHEME_MALLOC_GUARD_EDGES", nullptr); + InitProperty("XCODE_SCHEME_GUARD_MALLOC", nullptr); + InitProperty("XCODE_SCHEME_ZOMBIE_OBJECTS", nullptr); + InitProperty("XCODE_SCHEME_MALLOC_STACK", nullptr); + InitProperty("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE", nullptr); + InitProperty("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS", nullptr); } #endif } - // Collect the set of configuration types. - std::vector<std::string> configNames; - mf->GetConfigurations(configNames); - // Setup per-configuration property default values. if (this->GetType() != cmStateEnums::UTILITY) { - const char* configProps[] = { + static const auto configProps = { /* clang-format needs this comment to break after the opening brace */ "ARCHIVE_OUTPUT_DIRECTORY_", "LIBRARY_OUTPUT_DIRECTORY_", "RUNTIME_OUTPUT_DIRECTORY_", "PDB_OUTPUT_DIRECTORY_", "COMPILE_PDB_OUTPUT_DIRECTORY_", "MAP_IMPORTED_CONFIG_", - "INTERPROCEDURAL_OPTIMIZATION_", nullptr + "INTERPROCEDURAL_OPTIMIZATION_" }; + // Collect the set of configuration types. + std::vector<std::string> configNames; + mf->GetConfigurations(configNames); for (std::string const& configName : configNames) { std::string configUpper = cmSystemTools::UpperCase(configName); - for (const char** p = configProps; *p; ++p) { + for (auto const& prop : configProps) { // Interface libraries have no output locations, so honor only // the configuration map. - if (this->TargetTypeValue == cmStateEnums::INTERFACE_LIBRARY && - strcmp(*p, "MAP_IMPORTED_CONFIG_") != 0) { + if (impl->TargetType == cmStateEnums::INTERFACE_LIBRARY && + strcmp(prop, "MAP_IMPORTED_CONFIG_") != 0) { continue; } - std::string property = *p; + std::string property = prop; property += configUpper; - this->SetPropertyDefault(property, nullptr); + InitProperty(property, nullptr); } // Initialize per-configuration name postfix property from the @@ -338,122 +388,92 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, // compatibility with previous CMake versions in which executables // did not support this variable. Projects may still specify the // property directly. - if (this->TargetTypeValue != cmStateEnums::EXECUTABLE && - this->TargetTypeValue != cmStateEnums::INTERFACE_LIBRARY) { + if (impl->TargetType != cmStateEnums::EXECUTABLE && + impl->TargetType != cmStateEnums::INTERFACE_LIBRARY) { std::string property = cmSystemTools::UpperCase(configName); property += "_POSTFIX"; - this->SetPropertyDefault(property, nullptr); + InitProperty(property, nullptr); } } } // Save the backtrace of target construction. - this->Backtrace = this->Makefile->GetBacktrace(); + impl->Backtrace = impl->Makefile->GetBacktrace(); if (!this->IsImported()) { // Initialize the INCLUDE_DIRECTORIES property based on the current value // of the same directory property: - const cmStringRange parentIncludes = - this->Makefile->GetIncludeDirectoriesEntries(); - const cmBacktraceRange parentIncludesBts = - this->Makefile->GetIncludeDirectoriesBacktraces(); - - this->Internal->IncludeDirectoriesEntries.insert( - this->Internal->IncludeDirectoriesEntries.end(), parentIncludes.begin(), - parentIncludes.end()); - this->Internal->IncludeDirectoriesBacktraces.insert( - this->Internal->IncludeDirectoriesBacktraces.end(), - parentIncludesBts.begin(), parentIncludesBts.end()); - - const std::set<std::string> parentSystemIncludes = - this->Makefile->GetSystemIncludeDirectories(); - - this->SystemIncludeDirectories.insert(parentSystemIncludes.begin(), - parentSystemIncludes.end()); - - const cmStringRange parentCompileOptions = - this->Makefile->GetCompileOptionsEntries(); - const cmBacktraceRange parentCompileOptionsBts = - this->Makefile->GetCompileOptionsBacktraces(); - - this->Internal->CompileOptionsEntries.insert( - this->Internal->CompileOptionsEntries.end(), - parentCompileOptions.begin(), parentCompileOptions.end()); - this->Internal->CompileOptionsBacktraces.insert( - this->Internal->CompileOptionsBacktraces.end(), - parentCompileOptionsBts.begin(), parentCompileOptionsBts.end()); - - const cmStringRange parentLinkOptions = - this->Makefile->GetLinkOptionsEntries(); - const cmBacktraceRange parentLinkOptionsBts = - this->Makefile->GetLinkOptionsBacktraces(); - - this->Internal->LinkOptionsEntries.insert( - this->Internal->LinkOptionsEntries.end(), parentLinkOptions.begin(), - parentLinkOptions.end()); - this->Internal->LinkOptionsBacktraces.insert( - this->Internal->LinkOptionsBacktraces.end(), - parentLinkOptionsBts.begin(), parentLinkOptionsBts.end()); - - const cmStringRange parentLinkDirectories = - this->Makefile->GetLinkDirectoriesEntries(); - const cmBacktraceRange parentLinkDirectoriesBts = - this->Makefile->GetLinkDirectoriesBacktraces(); - - this->Internal->LinkDirectoriesEntries.insert( - this->Internal->LinkDirectoriesEntries.end(), - parentLinkDirectories.begin(), parentLinkDirectories.end()); - this->Internal->LinkDirectoriesBacktraces.insert( - this->Internal->LinkDirectoriesBacktraces.end(), - parentLinkDirectoriesBts.begin(), parentLinkDirectoriesBts.end()); + CApp(impl->IncludeDirectoriesEntries, + impl->Makefile->GetIncludeDirectoriesEntries()); + CApp(impl->IncludeDirectoriesBacktraces, + impl->Makefile->GetIncludeDirectoriesBacktraces()); + + { + auto const& sysInc = impl->Makefile->GetSystemIncludeDirectories(); + impl->SystemIncludeDirectories.insert(sysInc.begin(), sysInc.end()); + } + + CApp(impl->CompileOptionsEntries, + impl->Makefile->GetCompileOptionsEntries()); + CApp(impl->CompileOptionsBacktraces, + impl->Makefile->GetCompileOptionsBacktraces()); + + CApp(impl->LinkOptionsEntries, impl->Makefile->GetLinkOptionsEntries()); + CApp(impl->LinkOptionsBacktraces, + impl->Makefile->GetLinkOptionsBacktraces()); + + CApp(impl->LinkDirectoriesEntries, + impl->Makefile->GetLinkDirectoriesEntries()); + CApp(impl->LinkDirectoriesBacktraces, + impl->Makefile->GetLinkDirectoriesBacktraces()); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - this->SetPropertyDefault("C_VISIBILITY_PRESET", nullptr); - this->SetPropertyDefault("CXX_VISIBILITY_PRESET", nullptr); - this->SetPropertyDefault("CUDA_VISIBILITY_PRESET", nullptr); - this->SetPropertyDefault("VISIBILITY_INLINES_HIDDEN", nullptr); + InitProperty("C_VISIBILITY_PRESET", nullptr); + InitProperty("CXX_VISIBILITY_PRESET", nullptr); + InitProperty("CUDA_VISIBILITY_PRESET", nullptr); + InitProperty("VISIBILITY_INLINES_HIDDEN", nullptr); } - if (this->TargetTypeValue == cmStateEnums::EXECUTABLE) { - this->SetPropertyDefault("ANDROID_GUI", nullptr); - this->SetPropertyDefault("CROSSCOMPILING_EMULATOR", nullptr); - this->SetPropertyDefault("ENABLE_EXPORTS", nullptr); + if (impl->TargetType == cmStateEnums::EXECUTABLE) { + InitProperty("ANDROID_GUI", nullptr); + InitProperty("CROSSCOMPILING_EMULATOR", nullptr); + InitProperty("ENABLE_EXPORTS", nullptr); } - if (this->TargetTypeValue == cmStateEnums::SHARED_LIBRARY || - this->TargetTypeValue == cmStateEnums::MODULE_LIBRARY) { + if (impl->TargetType == cmStateEnums::SHARED_LIBRARY || + impl->TargetType == cmStateEnums::MODULE_LIBRARY) { this->SetProperty("POSITION_INDEPENDENT_CODE", "True"); } - if (this->TargetTypeValue == cmStateEnums::SHARED_LIBRARY || - this->TargetTypeValue == cmStateEnums::EXECUTABLE) { - this->SetPropertyDefault("WINDOWS_EXPORT_ALL_SYMBOLS", nullptr); + if (impl->TargetType == cmStateEnums::SHARED_LIBRARY || + impl->TargetType == cmStateEnums::EXECUTABLE) { + InitProperty("WINDOWS_EXPORT_ALL_SYMBOLS", nullptr); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - this->SetPropertyDefault("POSITION_INDEPENDENT_CODE", nullptr); + InitProperty("POSITION_INDEPENDENT_CODE", nullptr); } // Record current policies for later use. - this->Makefile->RecordPolicies(this->PolicyMap); + impl->Makefile->RecordPolicies(impl->PolicyMap); - if (this->TargetTypeValue == cmStateEnums::INTERFACE_LIBRARY) { + if (impl->TargetType == cmStateEnums::INTERFACE_LIBRARY) { // This policy is checked in a few conditions. The properties relevant // to the policy are always ignored for cmStateEnums::INTERFACE_LIBRARY // targets, // so ensure that the conditions don't lead to nonsense. - this->PolicyMap.Set(cmPolicies::CMP0022, cmPolicies::NEW); + impl->PolicyMap.Set(cmPolicies::CMP0022, cmPolicies::NEW); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && this->GetType() != cmStateEnums::UTILITY) { - this->SetPropertyDefault("JOB_POOL_COMPILE", nullptr); - this->SetPropertyDefault("JOB_POOL_LINK", nullptr); + InitProperty("JOB_POOL_COMPILE", nullptr); + InitProperty("JOB_POOL_LINK", nullptr); } - if (this->TargetTypeValue <= cmStateEnums::UTILITY) { - this->SetPropertyDefault("DOTNET_TARGET_FRAMEWORK_VERSION", nullptr); + if (impl->TargetType <= cmStateEnums::UTILITY) { + InitProperty("DOTNET_TARGET_FRAMEWORK_VERSION", nullptr); } if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && @@ -474,7 +494,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (assignment != std::string::npos) { const std::string propName = vsGlobal + i.substr(0, assignment); const std::string propValue = i.substr(assignment + 1); - this->SetPropertyDefault(propName, propValue.c_str()); + InitProperty(propName, propValue.c_str()); } } } @@ -482,20 +502,56 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, } } +cmTarget::cmTarget(cmTarget&&) noexcept = default; +cmTarget::~cmTarget() = default; + +cmTarget& cmTarget::operator=(cmTarget&&) noexcept = default; + +cmStateEnums::TargetType cmTarget::GetType() const +{ + return impl->TargetType; +} + +cmMakefile* cmTarget::GetMakefile() const +{ + return impl->Makefile; +} + +cmPolicies::PolicyMap const& cmTarget::GetPolicyMap() const +{ + return impl->PolicyMap; +} + +const std::string& cmTarget::GetName() const +{ + return impl->Name; +} + +cmPolicies::PolicyStatus cmTarget::GetPolicyStatus( + cmPolicies::PolicyID policy) const +{ + return impl->PolicyMap.Get(policy); +} + cmGlobalGenerator* cmTarget::GetGlobalGenerator() const { - return this->GetMakefile()->GetGlobalGenerator(); + return impl->Makefile->GetGlobalGenerator(); +} + +void cmTarget::AddUtility(std::string const& name, cmMakefile* mf) +{ + impl->Utilities.insert( + BT<std::string>(name, mf ? mf->GetBacktrace() : cmListFileBacktrace())); } -void cmTarget::AddUtility(std::string const& u, cmMakefile* mf) +std::set<BT<std::string>> const& cmTarget::GetUtilities() const { - BT<std::string> util(u, mf ? mf->GetBacktrace() : cmListFileBacktrace()); - this->Utilities.insert(util); + return impl->Utilities; } cmListFileBacktrace const& cmTarget::GetBacktrace() const { - return this->Backtrace; + return impl->Backtrace; } bool cmTarget::IsExecutableWithExports() const @@ -504,34 +560,57 @@ bool cmTarget::IsExecutableWithExports() const this->GetPropertyAsBool("ENABLE_EXPORTS")); } -bool cmTarget::HasImportLibrary() const -{ - return (this->DLLPlatform && - (this->GetType() == cmStateEnums::SHARED_LIBRARY || - this->IsExecutableWithExports())); -} - bool cmTarget::IsFrameworkOnApple() const { return ((this->GetType() == cmStateEnums::SHARED_LIBRARY || this->GetType() == cmStateEnums::STATIC_LIBRARY) && - this->Makefile->IsOn("APPLE") && + impl->Makefile->IsOn("APPLE") && this->GetPropertyAsBool("FRAMEWORK")); } bool cmTarget::IsAppBundleOnApple() const { return (this->GetType() == cmStateEnums::EXECUTABLE && - this->Makefile->IsOn("APPLE") && + impl->Makefile->IsOn("APPLE") && this->GetPropertyAsBool("MACOSX_BUNDLE")); } +std::vector<cmCustomCommand> const& cmTarget::GetPreBuildCommands() const +{ + return impl->PreBuildCommands; +} + +void cmTarget::AddPreBuildCommand(cmCustomCommand const& cmd) +{ + impl->PreBuildCommands.push_back(cmd); +} + +std::vector<cmCustomCommand> const& cmTarget::GetPreLinkCommands() const +{ + return impl->PreLinkCommands; +} + +void cmTarget::AddPreLinkCommand(cmCustomCommand const& cmd) +{ + impl->PreLinkCommands.push_back(cmd); +} + +std::vector<cmCustomCommand> const& cmTarget::GetPostBuildCommands() const +{ + return impl->PostBuildCommands; +} + +void cmTarget::AddPostBuildCommand(cmCustomCommand const& cmd) +{ + impl->PostBuildCommands.push_back(cmd); +} + void cmTarget::AddTracedSources(std::vector<std::string> const& srcs) { if (!srcs.empty()) { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->SourceEntries.push_back(cmJoin(srcs, ";")); - this->Internal->SourceBacktraces.push_back(lfbt); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->SourceEntries.push_back(cmJoin(srcs, ";")); + impl->SourceBacktraces.push_back(lfbt); } } @@ -542,25 +621,25 @@ void cmTarget::AddSources(std::vector<std::string> const& srcs) for (auto filename : srcs) { if (!cmGeneratorExpression::StartsWithGeneratorExpression(filename)) { if (!filename.empty()) { - filename = this->ProcessSourceItemCMP0049(filename); + filename = impl->ProcessSourceItemCMP0049(filename); if (filename.empty()) { return; } } - this->Makefile->GetOrCreateSource(filename); + impl->Makefile->GetOrCreateSource(filename); } srcFiles += sep; srcFiles += filename; sep = ";"; } if (!srcFiles.empty()) { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->SourceEntries.push_back(std::move(srcFiles)); - this->Internal->SourceBacktraces.push_back(lfbt); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->SourceEntries.push_back(std::move(srcFiles)); + impl->SourceBacktraces.push_back(lfbt); } } -std::string cmTarget::ProcessSourceItemCMP0049(const std::string& s) +std::string cmTargetInternals::ProcessSourceItemCMP0049(const std::string& s) { std::string src = s; @@ -585,7 +664,7 @@ std::string cmTarget::ProcessSourceItemCMP0049(const std::string& s) } if (!noMessage) { e << "Legacy variable expansion in source file \"" << s - << "\" expanded to \"" << src << "\" in target \"" << this->GetName() + << "\" expanded to \"" << src << "\" in target \"" << this->Name << "\". This behavior will be removed in a " "future version of CMake."; this->Makefile->IssueMessage(messageType, e.str()); @@ -599,7 +678,7 @@ std::string cmTarget::ProcessSourceItemCMP0049(const std::string& s) cmSourceFile* cmTarget::AddSourceCMP0049(const std::string& s) { - std::string src = this->ProcessSourceItemCMP0049(s); + std::string src = impl->ProcessSourceItemCMP0049(s); if (!s.empty() && src.empty()) { return nullptr; } @@ -663,26 +742,22 @@ public: cmSourceFile* cmTarget::AddSource(const std::string& src, bool before) { - cmSourceFileLocation sfl(this->Makefile, src, + cmSourceFileLocation sfl(impl->Makefile, src, cmSourceFileLocationKind::Known); - if (std::find_if(this->Internal->SourceEntries.begin(), - this->Internal->SourceEntries.end(), + if (std::find_if(impl->SourceEntries.begin(), impl->SourceEntries.end(), TargetPropertyEntryFinder(sfl)) == - this->Internal->SourceEntries.end()) { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->SourceEntries.insert( - before ? this->Internal->SourceEntries.begin() - : this->Internal->SourceEntries.end(), - src); - this->Internal->SourceBacktraces.insert( - before ? this->Internal->SourceBacktraces.begin() - : this->Internal->SourceBacktraces.end(), - lfbt); + impl->SourceEntries.end()) { + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->SourceEntries.insert( + before ? impl->SourceEntries.begin() : impl->SourceEntries.end(), src); + impl->SourceBacktraces.insert(before ? impl->SourceBacktraces.begin() + : impl->SourceBacktraces.end(), + lfbt); } if (cmGeneratorExpression::Find(src) != std::string::npos) { return nullptr; } - return this->Makefile->GetOrCreateSource(src, false, + return impl->Makefile->GetOrCreateSource(src, false, cmSourceFileLocationKind::Known); } @@ -702,15 +777,13 @@ std::string cmTarget::GetDebugGeneratorExpressions( // Get the list of configurations considered to be DEBUG. std::vector<std::string> debugConfigs = - this->Makefile->GetCMakeInstance()->GetDebugConfigs(); + impl->Makefile->GetCMakeInstance()->GetDebugConfigs(); std::string configString = "$<CONFIG:" + debugConfigs[0] + ">"; if (debugConfigs.size() > 1) { - for (std::vector<std::string>::const_iterator li = - debugConfigs.begin() + 1; - li != debugConfigs.end(); ++li) { - configString += ",$<CONFIG:" + *li + ">"; + for (std::string const& conf : cmMakeRange(debugConfigs).advance(1)) { + configString += ",$<CONFIG:" + conf + ">"; } configString = "$<OR:" + configString + ">"; } @@ -730,13 +803,13 @@ bool cmTarget::PushTLLCommandTrace(TLLSignature signature, cmListFileContext const& lfc) { bool ret = true; - if (!this->TLLCommands.empty()) { - if (this->TLLCommands.back().first != signature) { + if (!impl->TLLCommands.empty()) { + if (impl->TLLCommands.back().first != signature) { ret = false; } } - if (this->TLLCommands.empty() || this->TLLCommands.back().second != lfc) { - this->TLLCommands.emplace_back(signature, lfc); + if (impl->TLLCommands.empty() || impl->TLLCommands.back().second != lfc) { + impl->TLLCommands.emplace_back(signature, lfc); } return ret; } @@ -746,18 +819,63 @@ void cmTarget::GetTllSignatureTraces(std::ostream& s, TLLSignature sig) const const char* sigString = (sig == cmTarget::KeywordTLLSignature ? "keyword" : "plain"); s << "The uses of the " << sigString << " signature are here:\n"; - cmStateDirectory cmDir = - this->GetMakefile()->GetStateSnapshot().GetDirectory(); - for (auto const& cmd : this->TLLCommands) { + cmStateDirectory cmDir = impl->Makefile->GetStateSnapshot().GetDirectory(); + for (auto const& cmd : impl->TLLCommands) { if (cmd.first == sig) { cmListFileContext lfc = cmd.second; lfc.FilePath = cmDir.ConvertToRelPathIfNotContained( - this->Makefile->GetState()->GetSourceDirectory(), lfc.FilePath); + impl->Makefile->GetState()->GetSourceDirectory(), lfc.FilePath); s << " * " << lfc << std::endl; } } } +std::string const& cmTarget::GetInstallPath() const +{ + return impl->InstallPath; +} + +void cmTarget::SetInstallPath(std::string const& name) +{ + impl->InstallPath = name; +} + +std::string const& cmTarget::GetRuntimeInstallPath() const +{ + return impl->RuntimeInstallPath; +} + +void cmTarget::SetRuntimeInstallPath(std::string const& name) +{ + impl->RuntimeInstallPath = name; +} + +bool cmTarget::GetHaveInstallRule() const +{ + return impl->HaveInstallRule; +} + +void cmTarget::SetHaveInstallRule(bool hir) +{ + impl->HaveInstallRule = hir; +} + +bool cmTarget::GetIsGeneratorProvided() const +{ + return impl->IsGeneratorProvided; +} + +void cmTarget::SetIsGeneratorProvided(bool igp) +{ + impl->IsGeneratorProvided = igp; +} + +cmTarget::LinkLibraryVectorType const& cmTarget::GetOriginalLinkLibraries() + const +{ + return impl->OriginalLinkLibraries; +} + void cmTarget::AddLinkLibrary(cmMakefile& mf, const std::string& lib, cmTargetLinkLibraryType llt) { @@ -785,11 +903,11 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib, (tgt && (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY || tgt->GetType() == cmStateEnums::OBJECT_LIBRARY)) || - (this->Name == lib)) { + (impl->Name == lib)) { return; } - this->OriginalLinkLibraries.emplace_back(lib, llt); + impl->OriginalLinkLibraries.emplace_back(lib, llt); // Add the explicit dependency information for libraries. This is // simply a set of libraries separated by ";". There should always @@ -799,11 +917,11 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib, // may be purposefully duplicated to handle recursive dependencies, // and we removing one instance will break the link line. Duplicates // will be appropriately eliminated at emit time. - if (this->TargetTypeValue >= cmStateEnums::STATIC_LIBRARY && - this->TargetTypeValue <= cmStateEnums::MODULE_LIBRARY && + if (impl->TargetType >= cmStateEnums::STATIC_LIBRARY && + impl->TargetType <= cmStateEnums::MODULE_LIBRARY && (this->GetPolicyStatusCMP0073() == cmPolicies::OLD || this->GetPolicyStatusCMP0073() == cmPolicies::WARN)) { - std::string targetEntry = this->Name; + std::string targetEntry = impl->Name; targetEntry += "_LIB_DEPENDS"; std::string dependencies; const char* old_val = mf.GetDefinition(targetEntry); @@ -831,94 +949,99 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib, void cmTarget::AddSystemIncludeDirectories(const std::set<std::string>& incs) { - this->SystemIncludeDirectories.insert(incs.begin(), incs.end()); + impl->SystemIncludeDirectories.insert(incs.begin(), incs.end()); +} + +std::set<std::string> const& cmTarget::GetSystemIncludeDirectories() const +{ + return impl->SystemIncludeDirectories; } cmStringRange cmTarget::GetIncludeDirectoriesEntries() const { - return cmMakeRange(this->Internal->IncludeDirectoriesEntries); + return cmMakeRange(impl->IncludeDirectoriesEntries); } cmBacktraceRange cmTarget::GetIncludeDirectoriesBacktraces() const { - return cmMakeRange(this->Internal->IncludeDirectoriesBacktraces); + return cmMakeRange(impl->IncludeDirectoriesBacktraces); } cmStringRange cmTarget::GetCompileOptionsEntries() const { - return cmMakeRange(this->Internal->CompileOptionsEntries); + return cmMakeRange(impl->CompileOptionsEntries); } cmBacktraceRange cmTarget::GetCompileOptionsBacktraces() const { - return cmMakeRange(this->Internal->CompileOptionsBacktraces); + return cmMakeRange(impl->CompileOptionsBacktraces); } cmStringRange cmTarget::GetCompileFeaturesEntries() const { - return cmMakeRange(this->Internal->CompileFeaturesEntries); + return cmMakeRange(impl->CompileFeaturesEntries); } cmBacktraceRange cmTarget::GetCompileFeaturesBacktraces() const { - return cmMakeRange(this->Internal->CompileFeaturesBacktraces); + return cmMakeRange(impl->CompileFeaturesBacktraces); } cmStringRange cmTarget::GetCompileDefinitionsEntries() const { - return cmMakeRange(this->Internal->CompileDefinitionsEntries); + return cmMakeRange(impl->CompileDefinitionsEntries); } cmBacktraceRange cmTarget::GetCompileDefinitionsBacktraces() const { - return cmMakeRange(this->Internal->CompileDefinitionsBacktraces); + return cmMakeRange(impl->CompileDefinitionsBacktraces); } cmStringRange cmTarget::GetSourceEntries() const { - return cmMakeRange(this->Internal->SourceEntries); + return cmMakeRange(impl->SourceEntries); } cmBacktraceRange cmTarget::GetSourceBacktraces() const { - return cmMakeRange(this->Internal->SourceBacktraces); + return cmMakeRange(impl->SourceBacktraces); } cmStringRange cmTarget::GetLinkOptionsEntries() const { - return cmMakeRange(this->Internal->LinkOptionsEntries); + return cmMakeRange(impl->LinkOptionsEntries); } cmBacktraceRange cmTarget::GetLinkOptionsBacktraces() const { - return cmMakeRange(this->Internal->LinkOptionsBacktraces); + return cmMakeRange(impl->LinkOptionsBacktraces); } cmStringRange cmTarget::GetLinkDirectoriesEntries() const { - return cmMakeRange(this->Internal->LinkDirectoriesEntries); + return cmMakeRange(impl->LinkDirectoriesEntries); } cmBacktraceRange cmTarget::GetLinkDirectoriesBacktraces() const { - return cmMakeRange(this->Internal->LinkDirectoriesBacktraces); + return cmMakeRange(impl->LinkDirectoriesBacktraces); } cmStringRange cmTarget::GetLinkImplementationEntries() const { - return cmMakeRange(this->Internal->LinkImplementationPropertyEntries); + return cmMakeRange(impl->LinkImplementationPropertyEntries); } cmBacktraceRange cmTarget::GetLinkImplementationBacktraces() const { - return cmMakeRange(this->Internal->LinkImplementationPropertyBacktraces); + return cmMakeRange(impl->LinkImplementationPropertyBacktraces); } void cmTarget::SetProperty(const std::string& prop, const char* value) { if (!cmTargetPropertyComputer::PassesWhitelist( - this->GetType(), prop, this->Makefile->GetMessenger(), - this->Makefile->GetBacktrace())) { + this->GetType(), prop, impl->Makefile->GetMessenger(), + impl->Makefile->GetBacktrace())) { return; } #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP @@ -940,133 +1063,133 @@ void cmTarget::SetProperty(const std::string& prop, const char* value) if (prop == propMANUALLY_ADDED_DEPENDENCIES) { std::ostringstream e; e << "MANUALLY_ADDED_DEPENDENCIES property is read-only\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == propNAME) { std::ostringstream e; e << "NAME property is read-only\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == propTYPE) { std::ostringstream e; e << "TYPE property is read-only\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == propEXPORT_NAME && this->IsImported()) { std::ostringstream e; e << "EXPORT_NAME property can't be set on imported targets (\"" - << this->Name << "\")\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + << impl->Name << "\")\n"; + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == propSOURCES && this->IsImported()) { std::ostringstream e; - e << "SOURCES property can't be set on imported targets (\"" << this->Name + e << "SOURCES property can't be set on imported targets (\"" << impl->Name << "\")\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == propIMPORTED_GLOBAL && !this->IsImported()) { std::ostringstream e; e << "IMPORTED_GLOBAL property can't be set on non-imported targets (\"" - << this->Name << "\")\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + << impl->Name << "\")\n"; + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == propINCLUDE_DIRECTORIES) { - this->Internal->IncludeDirectoriesEntries.clear(); - this->Internal->IncludeDirectoriesBacktraces.clear(); + impl->IncludeDirectoriesEntries.clear(); + impl->IncludeDirectoriesBacktraces.clear(); if (value) { - this->Internal->IncludeDirectoriesEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->IncludeDirectoriesBacktraces.push_back(lfbt); + impl->IncludeDirectoriesEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->IncludeDirectoriesBacktraces.push_back(lfbt); } } else if (prop == propCOMPILE_OPTIONS) { - this->Internal->CompileOptionsEntries.clear(); - this->Internal->CompileOptionsBacktraces.clear(); + impl->CompileOptionsEntries.clear(); + impl->CompileOptionsBacktraces.clear(); if (value) { - this->Internal->CompileOptionsEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->CompileOptionsBacktraces.push_back(lfbt); + impl->CompileOptionsEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->CompileOptionsBacktraces.push_back(lfbt); } } else if (prop == propCOMPILE_FEATURES) { - this->Internal->CompileFeaturesEntries.clear(); - this->Internal->CompileFeaturesBacktraces.clear(); + impl->CompileFeaturesEntries.clear(); + impl->CompileFeaturesBacktraces.clear(); if (value) { - this->Internal->CompileFeaturesEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->CompileFeaturesBacktraces.push_back(lfbt); + impl->CompileFeaturesEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->CompileFeaturesBacktraces.push_back(lfbt); } } else if (prop == propCOMPILE_DEFINITIONS) { - this->Internal->CompileDefinitionsEntries.clear(); - this->Internal->CompileDefinitionsBacktraces.clear(); + impl->CompileDefinitionsEntries.clear(); + impl->CompileDefinitionsBacktraces.clear(); if (value) { - this->Internal->CompileDefinitionsEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->CompileDefinitionsBacktraces.push_back(lfbt); + impl->CompileDefinitionsEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->CompileDefinitionsBacktraces.push_back(lfbt); } } else if (prop == propLINK_OPTIONS) { - this->Internal->LinkOptionsEntries.clear(); - this->Internal->LinkOptionsBacktraces.clear(); + impl->LinkOptionsEntries.clear(); + impl->LinkOptionsBacktraces.clear(); if (value) { - this->Internal->LinkOptionsEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->LinkOptionsBacktraces.push_back(lfbt); + impl->LinkOptionsEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->LinkOptionsBacktraces.push_back(lfbt); } } else if (prop == propLINK_DIRECTORIES) { - this->Internal->LinkDirectoriesEntries.clear(); - this->Internal->LinkDirectoriesBacktraces.clear(); + impl->LinkDirectoriesEntries.clear(); + impl->LinkDirectoriesBacktraces.clear(); if (value) { - this->Internal->LinkDirectoriesEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->LinkDirectoriesBacktraces.push_back(lfbt); + impl->LinkDirectoriesEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->LinkDirectoriesBacktraces.push_back(lfbt); } } else if (prop == propLINK_LIBRARIES) { - this->Internal->LinkImplementationPropertyEntries.clear(); - this->Internal->LinkImplementationPropertyBacktraces.clear(); + impl->LinkImplementationPropertyEntries.clear(); + impl->LinkImplementationPropertyBacktraces.clear(); if (value) { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->LinkImplementationPropertyEntries.emplace_back(value); - this->Internal->LinkImplementationPropertyBacktraces.push_back(lfbt); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->LinkImplementationPropertyEntries.emplace_back(value); + impl->LinkImplementationPropertyBacktraces.push_back(lfbt); } } else if (prop == propSOURCES) { - this->Internal->SourceEntries.clear(); - this->Internal->SourceBacktraces.clear(); + impl->SourceEntries.clear(); + impl->SourceBacktraces.clear(); if (value) { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->SourceEntries.emplace_back(value); - this->Internal->SourceBacktraces.push_back(lfbt); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->SourceEntries.emplace_back(value); + impl->SourceBacktraces.push_back(lfbt); } } else if (prop == propIMPORTED_GLOBAL) { if (!cmSystemTools::IsOn(value)) { std::ostringstream e; e << "IMPORTED_GLOBAL property can't be set to FALSE on targets (\"" - << this->Name << "\")\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + << impl->Name << "\")\n"; + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } /* no need to change anything if value does not change */ - if (!this->ImportedGloballyVisible) { - this->ImportedGloballyVisible = true; + if (!impl->ImportedGloballyVisible) { + impl->ImportedGloballyVisible = true; this->GetGlobalGenerator()->IndexTarget(this); } } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME") && - !this->CheckImportedLibName(prop, value ? value : "")) { + !impl->CheckImportedLibName(prop, value ? value : "")) { /* error was reported by check method */ } else if (prop == propCUDA_PTX_COMPILATION && this->GetType() != cmStateEnums::OBJECT_LIBRARY) { std::ostringstream e; e << "CUDA_PTX_COMPILATION property can only be applied to OBJECT " "targets (\"" - << this->Name << "\")\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + << impl->Name << "\")\n"; + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } else { - this->Properties.SetProperty(prop, value); + impl->Properties.SetProperty(prop, value); } } @@ -1074,89 +1197,89 @@ void cmTarget::AppendProperty(const std::string& prop, const char* value, bool asString) { if (!cmTargetPropertyComputer::PassesWhitelist( - this->GetType(), prop, this->Makefile->GetMessenger(), - this->Makefile->GetBacktrace())) { + this->GetType(), prop, impl->Makefile->GetMessenger(), + impl->Makefile->GetBacktrace())) { return; } if (prop == "NAME") { std::ostringstream e; e << "NAME property is read-only\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == "EXPORT_NAME" && this->IsImported()) { std::ostringstream e; e << "EXPORT_NAME property can't be set on imported targets (\"" - << this->Name << "\")\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + << impl->Name << "\")\n"; + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == "SOURCES" && this->IsImported()) { std::ostringstream e; - e << "SOURCES property can't be set on imported targets (\"" << this->Name + e << "SOURCES property can't be set on imported targets (\"" << impl->Name << "\")\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == "IMPORTED_GLOBAL") { std::ostringstream e; e << "IMPORTED_GLOBAL property can't be appended, only set on imported " "targets (\"" - << this->Name << "\")\n"; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + << impl->Name << "\")\n"; + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); return; } if (prop == "INCLUDE_DIRECTORIES") { if (value && *value) { - this->Internal->IncludeDirectoriesEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->IncludeDirectoriesBacktraces.push_back(lfbt); + impl->IncludeDirectoriesEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->IncludeDirectoriesBacktraces.push_back(lfbt); } } else if (prop == "COMPILE_OPTIONS") { if (value && *value) { - this->Internal->CompileOptionsEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->CompileOptionsBacktraces.push_back(lfbt); + impl->CompileOptionsEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->CompileOptionsBacktraces.push_back(lfbt); } } else if (prop == "COMPILE_FEATURES") { if (value && *value) { - this->Internal->CompileFeaturesEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->CompileFeaturesBacktraces.push_back(lfbt); + impl->CompileFeaturesEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->CompileFeaturesBacktraces.push_back(lfbt); } } else if (prop == "COMPILE_DEFINITIONS") { if (value && *value) { - this->Internal->CompileDefinitionsEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->CompileDefinitionsBacktraces.push_back(lfbt); + impl->CompileDefinitionsEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->CompileDefinitionsBacktraces.push_back(lfbt); } } else if (prop == "LINK_OPTIONS") { if (value && *value) { - this->Internal->LinkOptionsEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->LinkOptionsBacktraces.push_back(lfbt); + impl->LinkOptionsEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->LinkOptionsBacktraces.push_back(lfbt); } } else if (prop == "LINK_DIRECTORIES") { if (value && *value) { - this->Internal->LinkDirectoriesEntries.emplace_back(value); - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->LinkDirectoriesBacktraces.push_back(lfbt); + impl->LinkDirectoriesEntries.emplace_back(value); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->LinkDirectoriesBacktraces.push_back(lfbt); } } else if (prop == "LINK_LIBRARIES") { if (value && *value) { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->LinkImplementationPropertyEntries.emplace_back(value); - this->Internal->LinkImplementationPropertyBacktraces.push_back(lfbt); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->LinkImplementationPropertyEntries.emplace_back(value); + impl->LinkImplementationPropertyBacktraces.push_back(lfbt); } } else if (prop == "SOURCES") { - cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); - this->Internal->SourceEntries.emplace_back(value); - this->Internal->SourceBacktraces.push_back(lfbt); + cmListFileBacktrace lfbt = impl->Makefile->GetBacktrace(); + impl->SourceEntries.emplace_back(value); + impl->SourceBacktraces.push_back(lfbt); } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME")) { - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, prop + " property may not be APPENDed."); } else { - this->Properties.AppendProperty(prop, value, asString); + impl->Properties.AppendProperty(prop, value, asString); } } @@ -1169,17 +1292,17 @@ void cmTarget::AppendBuildInterfaceIncludes() !this->IsExecutableWithExports()) { return; } - if (this->BuildInterfaceIncludesAppended) { + if (impl->BuildInterfaceIncludesAppended) { return; } - this->BuildInterfaceIncludesAppended = true; + impl->BuildInterfaceIncludesAppended = true; - if (this->Makefile->IsOn("CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE")) { - std::string dirs = this->Makefile->GetCurrentBinaryDirectory(); + if (impl->Makefile->IsOn("CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE")) { + std::string dirs = impl->Makefile->GetCurrentBinaryDirectory(); if (!dirs.empty()) { dirs += ';'; } - dirs += this->Makefile->GetCurrentSourceDirectory(); + dirs += impl->Makefile->GetCurrentSourceDirectory(); if (!dirs.empty()) { this->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES", ("$<BUILD_INTERFACE:" + dirs + ">").c_str()); @@ -1191,67 +1314,66 @@ void cmTarget::InsertInclude(std::string const& entry, cmListFileBacktrace const& bt, bool before) { std::vector<std::string>::iterator position = before - ? this->Internal->IncludeDirectoriesEntries.begin() - : this->Internal->IncludeDirectoriesEntries.end(); + ? impl->IncludeDirectoriesEntries.begin() + : impl->IncludeDirectoriesEntries.end(); std::vector<cmListFileBacktrace>::iterator btPosition = before - ? this->Internal->IncludeDirectoriesBacktraces.begin() - : this->Internal->IncludeDirectoriesBacktraces.end(); + ? impl->IncludeDirectoriesBacktraces.begin() + : impl->IncludeDirectoriesBacktraces.end(); - this->Internal->IncludeDirectoriesEntries.insert(position, entry); - this->Internal->IncludeDirectoriesBacktraces.insert(btPosition, bt); + impl->IncludeDirectoriesEntries.insert(position, entry); + impl->IncludeDirectoriesBacktraces.insert(btPosition, bt); } void cmTarget::InsertCompileOption(std::string const& entry, cmListFileBacktrace const& bt, bool before) { std::vector<std::string>::iterator position = before - ? this->Internal->CompileOptionsEntries.begin() - : this->Internal->CompileOptionsEntries.end(); + ? impl->CompileOptionsEntries.begin() + : impl->CompileOptionsEntries.end(); std::vector<cmListFileBacktrace>::iterator btPosition = before - ? this->Internal->CompileOptionsBacktraces.begin() - : this->Internal->CompileOptionsBacktraces.end(); + ? impl->CompileOptionsBacktraces.begin() + : impl->CompileOptionsBacktraces.end(); - this->Internal->CompileOptionsEntries.insert(position, entry); - this->Internal->CompileOptionsBacktraces.insert(btPosition, bt); + impl->CompileOptionsEntries.insert(position, entry); + impl->CompileOptionsBacktraces.insert(btPosition, bt); } void cmTarget::InsertCompileDefinition(std::string const& entry, cmListFileBacktrace const& bt) { - this->Internal->CompileDefinitionsEntries.push_back(entry); - this->Internal->CompileDefinitionsBacktraces.push_back(bt); + impl->CompileDefinitionsEntries.push_back(entry); + impl->CompileDefinitionsBacktraces.push_back(bt); } void cmTarget::InsertLinkOption(std::string const& entry, cmListFileBacktrace const& bt, bool before) { - std::vector<std::string>::iterator position = before - ? this->Internal->LinkOptionsEntries.begin() - : this->Internal->LinkOptionsEntries.end(); + std::vector<std::string>::iterator position = + before ? impl->LinkOptionsEntries.begin() : impl->LinkOptionsEntries.end(); std::vector<cmListFileBacktrace>::iterator btPosition = before - ? this->Internal->LinkOptionsBacktraces.begin() - : this->Internal->LinkOptionsBacktraces.end(); + ? impl->LinkOptionsBacktraces.begin() + : impl->LinkOptionsBacktraces.end(); - this->Internal->LinkOptionsEntries.insert(position, entry); - this->Internal->LinkOptionsBacktraces.insert(btPosition, bt); + impl->LinkOptionsEntries.insert(position, entry); + impl->LinkOptionsBacktraces.insert(btPosition, bt); } void cmTarget::InsertLinkDirectory(std::string const& entry, cmListFileBacktrace const& bt, bool before) { std::vector<std::string>::iterator position = before - ? this->Internal->LinkDirectoriesEntries.begin() - : this->Internal->LinkDirectoriesEntries.end(); + ? impl->LinkDirectoriesEntries.begin() + : impl->LinkDirectoriesEntries.end(); std::vector<cmListFileBacktrace>::iterator btPosition = before - ? this->Internal->LinkDirectoriesBacktraces.begin() - : this->Internal->LinkDirectoriesBacktraces.end(); + ? impl->LinkDirectoriesBacktraces.begin() + : impl->LinkDirectoriesBacktraces.end(); - this->Internal->LinkDirectoriesEntries.insert(position, entry); - this->Internal->LinkDirectoriesBacktraces.insert(btPosition, bt); + impl->LinkDirectoriesEntries.insert(position, entry); + impl->LinkDirectoriesBacktraces.insert(btPosition, bt); } static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop, @@ -1402,12 +1524,12 @@ const char* cmTarget::GetProperty(const std::string& prop) const } if (specialProps.count(prop)) { if (prop == propLINK_LIBRARIES) { - if (this->Internal->LinkImplementationPropertyEntries.empty()) { + if (impl->LinkImplementationPropertyEntries.empty()) { return nullptr; } static std::string output; - output = cmJoin(this->Internal->LinkImplementationPropertyEntries, ";"); + output = cmJoin(impl->LinkImplementationPropertyEntries, ";"); return output.c_str(); } // the type property returns what type the target is @@ -1415,67 +1537,67 @@ const char* cmTarget::GetProperty(const std::string& prop) const return cmState::GetTargetTypeName(this->GetType()); } if (prop == propINCLUDE_DIRECTORIES) { - if (this->Internal->IncludeDirectoriesEntries.empty()) { + if (impl->IncludeDirectoriesEntries.empty()) { return nullptr; } static std::string output; - output = cmJoin(this->Internal->IncludeDirectoriesEntries, ";"); + output = cmJoin(impl->IncludeDirectoriesEntries, ";"); return output.c_str(); } if (prop == propCOMPILE_FEATURES) { - if (this->Internal->CompileFeaturesEntries.empty()) { + if (impl->CompileFeaturesEntries.empty()) { return nullptr; } static std::string output; - output = cmJoin(this->Internal->CompileFeaturesEntries, ";"); + output = cmJoin(impl->CompileFeaturesEntries, ";"); return output.c_str(); } if (prop == propCOMPILE_OPTIONS) { - if (this->Internal->CompileOptionsEntries.empty()) { + if (impl->CompileOptionsEntries.empty()) { return nullptr; } static std::string output; - output = cmJoin(this->Internal->CompileOptionsEntries, ";"); + output = cmJoin(impl->CompileOptionsEntries, ";"); return output.c_str(); } if (prop == propCOMPILE_DEFINITIONS) { - if (this->Internal->CompileDefinitionsEntries.empty()) { + if (impl->CompileDefinitionsEntries.empty()) { return nullptr; } static std::string output; - output = cmJoin(this->Internal->CompileDefinitionsEntries, ";"); + output = cmJoin(impl->CompileDefinitionsEntries, ";"); return output.c_str(); } if (prop == propLINK_OPTIONS) { - if (this->Internal->LinkOptionsEntries.empty()) { + if (impl->LinkOptionsEntries.empty()) { return nullptr; } static std::string output; - output = cmJoin(this->Internal->LinkOptionsEntries, ";"); + output = cmJoin(impl->LinkOptionsEntries, ";"); return output.c_str(); } if (prop == propLINK_DIRECTORIES) { - if (this->Internal->LinkDirectoriesEntries.empty()) { + if (impl->LinkDirectoriesEntries.empty()) { return nullptr; } static std::string output; - output = cmJoin(this->Internal->LinkDirectoriesEntries, ";"); + output = cmJoin(impl->LinkDirectoriesEntries, ";"); return output.c_str(); } if (prop == propMANUALLY_ADDED_DEPENDENCIES) { - if (this->Utilities.empty()) { + if (impl->Utilities.empty()) { return nullptr; } static std::string output; - output = cmJoin(this->Utilities, ";"); + output = cmJoin(impl->Utilities, ";"); return output.c_str(); } if (prop == propIMPORTED) { @@ -1488,27 +1610,25 @@ const char* cmTarget::GetProperty(const std::string& prop) const return this->GetName().c_str(); } if (prop == propBINARY_DIR) { - return this->GetMakefile() - ->GetStateSnapshot() + return impl->Makefile->GetStateSnapshot() .GetDirectory() .GetCurrentBinary() .c_str(); } if (prop == propSOURCE_DIR) { - return this->GetMakefile() - ->GetStateSnapshot() + return impl->Makefile->GetStateSnapshot() .GetDirectory() .GetCurrentSource() .c_str(); } } - const char* retVal = this->Properties.GetPropertyValue(prop); + const char* retVal = impl->Properties.GetPropertyValue(prop); if (!retVal) { - const bool chain = this->GetMakefile()->GetState()->IsPropertyChained( - prop, cmProperty::TARGET); + const bool chain = + impl->Makefile->GetState()->IsPropertyChained(prop, cmProperty::TARGET); if (chain) { - return this->Makefile->GetStateSnapshot().GetDirectory().GetProperty( + return impl->Makefile->GetStateSnapshot().GetDirectory().GetProperty( prop, chain); } } @@ -1529,6 +1649,21 @@ bool cmTarget::GetPropertyAsBool(const std::string& prop) const return cmSystemTools::IsOn(this->GetProperty(prop)); } +cmPropertyMap const& cmTarget::GetProperties() const +{ + return impl->Properties; +} + +bool cmTarget::IsImported() const +{ + return impl->IsImportedTarget; +} + +bool cmTarget::IsImportedGloballyVisible() const +{ + return impl->ImportedGloballyVisible; +} + const char* cmTarget::GetSuffixVariableInternal( cmStateEnums::ArtifactType artifact) const { @@ -1556,7 +1691,7 @@ const char* cmTarget::GetSuffixVariableInternal( case cmStateEnums::RuntimeBinaryArtifact: // Android GUI application packages store the native // binary as a shared library. - return (this->IsAndroid && this->GetPropertyAsBool("ANDROID_GUI") + return (impl->IsAndroid && this->GetPropertyAsBool("ANDROID_GUI") ? "CMAKE_SHARED_LIBRARY_SUFFIX" : "CMAKE_EXECUTABLE_SUFFIX"); case cmStateEnums::ImportLibraryArtifact: @@ -1596,7 +1731,7 @@ const char* cmTarget::GetPrefixVariableInternal( case cmStateEnums::RuntimeBinaryArtifact: // Android GUI application packages store the native // binary as a shared library. - return (this->IsAndroid && this->GetPropertyAsBool("ANDROID_GUI") + return (impl->IsAndroid && this->GetPropertyAsBool("ANDROID_GUI") ? "CMAKE_SHARED_LIBRARY_PREFIX" : ""); case cmStateEnums::ImportLibraryArtifact: @@ -1670,25 +1805,11 @@ std::string cmTarget::ImportedGetFullPath( return result; } -void cmTarget::SetPropertyDefault(const std::string& property, - const char* default_value) +bool cmTargetInternals::CheckImportedLibName(std::string const& prop, + std::string const& value) const { - // Compute the name of the variable holding the default value. - std::string var = "CMAKE_"; - var += property; - - if (const char* value = this->Makefile->GetDefinition(var)) { - this->SetProperty(property, value); - } else if (default_value) { - this->SetProperty(property, default_value); - } -} - -bool cmTarget::CheckImportedLibName(std::string const& prop, - std::string const& value) const -{ - if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY || - !this->IsImported()) { + if (this->TargetType != cmStateEnums::INTERFACE_LIBRARY || + !this->IsImportedTarget) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, prop + @@ -1748,7 +1869,9 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config, // If we needed to find one of the mapped configurations but did not // On a DLL platform there may be only IMPORTED_IMPLIB for a shared // library or an executable with exports. - bool allowImp = this->HasImportLibrary(); + bool allowImp = (impl->DLLPlatform && + (this->GetType() == cmStateEnums::SHARED_LIBRARY || + this->IsExecutableWithExports())); // If a mapping was found, check its configurations. for (std::vector<std::string>::const_iterator mci = mappedConfigs.begin(); @@ -1851,37 +1974,3 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config, return true; } - -cmTargetInternalPointer::cmTargetInternalPointer() -{ - this->Pointer = new cmTargetInternals; -} - -cmTargetInternalPointer::cmTargetInternalPointer( - cmTargetInternalPointer const& r) -{ - // Ideally cmTarget instances should never be copied. However until - // we can make a sweep to remove that, this copy constructor avoids - // allowing the resources (Internals) to be copied. - this->Pointer = new cmTargetInternals(*r.Pointer); -} - -cmTargetInternalPointer::~cmTargetInternalPointer() -{ - delete this->Pointer; -} - -cmTargetInternalPointer& cmTargetInternalPointer::operator=( - cmTargetInternalPointer const& r) -{ - if (this == &r) { - return *this; - } // avoid warning on HP about self check - // Ideally cmTarget instances should never be copied. However until - // we can make a sweep to remove that, this copy constructor avoids - // allowing the resources (Internals) to be copied. - cmTargetInternals* oldPointer = this->Pointer; - this->Pointer = new cmTargetInternals(*r.Pointer); - delete oldPointer; - return *this; -} diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 24b3742..0ac5ca7 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -6,6 +6,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <iosfwd> +#include <memory> // IWYU pragma: keep #include <set> #include <string> #include <unordered_map> @@ -13,38 +14,23 @@ #include <vector> #include "cmAlgorithms.h" -#include "cmCustomCommand.h" #include "cmListFileCache.h" #include "cmPolicies.h" -#include "cmPropertyMap.h" #include "cmStateTypes.h" #include "cmTargetLinkLibraryType.h" +class cmCustomCommand; class cmGlobalGenerator; class cmMakefile; class cmMessenger; +class cmPropertyMap; class cmSourceFile; class cmTargetInternals; -class cmTargetInternalPointer -{ -public: - cmTargetInternalPointer(); - cmTargetInternalPointer(cmTargetInternalPointer const& r); - ~cmTargetInternalPointer(); - cmTargetInternalPointer& operator=(cmTargetInternalPointer const& r); - cmTargetInternals* operator->() const { return this->Pointer; } - cmTargetInternals* Get() const { return this->Pointer; } - -private: - cmTargetInternals* Pointer; -}; - /** \class cmTarget * \brief Represent a library or executable target loaded from a makefile. * - * cmTarget represents a target loaded from - * a makefile. + * cmTarget represents a target loaded from a makefile. */ class cmTarget { @@ -56,9 +42,6 @@ public: VisibilityImportedGlobally }; - cmTarget(std::string const& name, cmStateEnums::TargetType type, - Visibility vis, cmMakefile* mf); - enum CustomCommandType { PRE_BUILD, @@ -66,77 +49,68 @@ public: POST_BUILD }; - /** - * Return the type of target. - */ - cmStateEnums::TargetType GetType() const { return this->TargetTypeValue; } + cmTarget(std::string const& name, cmStateEnums::TargetType type, + Visibility vis, cmMakefile* mf); + + cmTarget(cmTarget const&) = delete; + cmTarget(cmTarget&&) noexcept; + ~cmTarget(); + + cmTarget& operator=(cmTarget const&) = delete; + cmTarget& operator=(cmTarget&&) noexcept; + + //! Return the type of target. + cmStateEnums::TargetType GetType() const; + + //! Get the cmMakefile that owns this target. + cmMakefile* GetMakefile() const; + //! Return the global generator. cmGlobalGenerator* GetGlobalGenerator() const; - ///! Set/Get the name of the target - const std::string& GetName() const { return this->Name; } + //! Set/Get the name of the target + const std::string& GetName() const; - /** Get the cmMakefile that owns this target. */ - cmMakefile* GetMakefile() const { return this->Makefile; } + //! Get the policy map + cmPolicies::PolicyMap const& GetPolicyMap() const; + + //! Get policy status + cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID policy) const; #define DECLARE_TARGET_POLICY(POLICY) \ cmPolicies::PolicyStatus GetPolicyStatus##POLICY() const \ { \ - return this->PolicyMap.Get(cmPolicies::POLICY); \ + return this->GetPolicyStatus(cmPolicies::POLICY); \ } CM_FOR_EACH_TARGET_POLICY(DECLARE_TARGET_POLICY) #undef DECLARE_TARGET_POLICY - /** - * Get the list of the custom commands for this target - */ - std::vector<cmCustomCommand> const& GetPreBuildCommands() const - { - return this->PreBuildCommands; - } - std::vector<cmCustomCommand> const& GetPreLinkCommands() const - { - return this->PreLinkCommands; - } - std::vector<cmCustomCommand> const& GetPostBuildCommands() const - { - return this->PostBuildCommands; - } - void AddPreBuildCommand(cmCustomCommand const& cmd) - { - this->PreBuildCommands.push_back(cmd); - } - void AddPreLinkCommand(cmCustomCommand const& cmd) - { - this->PreLinkCommands.push_back(cmd); - } - void AddPostBuildCommand(cmCustomCommand const& cmd) - { - this->PostBuildCommands.push_back(cmd); - } + //! Get the list of the PRE_BUILD custom commands for this target + std::vector<cmCustomCommand> const& GetPreBuildCommands() const; + void AddPreBuildCommand(cmCustomCommand const& cmd); - /** - * Add sources to the target. - */ + //! Get the list of the PRE_LINK custom commands for this target + std::vector<cmCustomCommand> const& GetPreLinkCommands() const; + void AddPreLinkCommand(cmCustomCommand const& cmd); + + //! Get the list of the POST_BUILD custom commands for this target + std::vector<cmCustomCommand> const& GetPostBuildCommands() const; + void AddPostBuildCommand(cmCustomCommand const& cmd); + + //! Add sources to the target. void AddSources(std::vector<std::string> const& srcs); void AddTracedSources(std::vector<std::string> const& srcs); cmSourceFile* AddSourceCMP0049(const std::string& src); cmSourceFile* AddSource(const std::string& src, bool before = false); - //* how we identify a library, by name and type + //! how we identify a library, by name and type typedef std::pair<std::string, cmTargetLinkLibraryType> LibraryID; - typedef std::vector<LibraryID> LinkLibraryVectorType; - const LinkLibraryVectorType& GetOriginalLinkLibraries() const - { - return this->OriginalLinkLibraries; - } + LinkLibraryVectorType const& GetOriginalLinkLibraries() const; - /** - * Clear the dependency information recorded for this target, if any. - */ + //! Clear the dependency information recorded for this target, if any. void ClearDependencyInformation(cmMakefile& mf); void AddLinkLibrary(cmMakefile& mf, const std::string& lib, @@ -157,83 +131,69 @@ public: * Set the path where this target should be installed. This is relative to * INSTALL_PREFIX */ - std::string GetInstallPath() const { return this->InstallPath; } - void SetInstallPath(const char* name) { this->InstallPath = name; } + std::string const& GetInstallPath() const; + void SetInstallPath(std::string const& name); /** * Set the path where this target (if it has a runtime part) should be * installed. This is relative to INSTALL_PREFIX */ - std::string GetRuntimeInstallPath() const - { - return this->RuntimeInstallPath; - } - void SetRuntimeInstallPath(const char* name) - { - this->RuntimeInstallPath = name; - } + std::string const& GetRuntimeInstallPath() const; + void SetRuntimeInstallPath(std::string const& name); /** * Get/Set whether there is an install rule for this target. */ - bool GetHaveInstallRule() const { return this->HaveInstallRule; } - void SetHaveInstallRule(bool h) { this->HaveInstallRule = h; } + bool GetHaveInstallRule() const; + void SetHaveInstallRule(bool hir); /** * Get/Set whether this target was auto-created by a generator. */ - bool GetIsGeneratorProvided() const { return this->IsGeneratorProvided; } - void SetIsGeneratorProvided(bool igp) { this->IsGeneratorProvided = igp; } + bool GetIsGeneratorProvided() const; + void SetIsGeneratorProvided(bool igp); - /** Add a utility on which this project depends. A utility is an executable + /** + * Add a utility on which this project depends. A utility is an executable * name as would be specified to the ADD_EXECUTABLE or UTILITY_SOURCE * commands. It is not a full path nor does it have an extension. */ - void AddUtility(std::string const& u, cmMakefile* mf = nullptr); - ///! Get the utilities used by this target - std::set<BT<std::string>> const& GetUtilities() const - { - return this->Utilities; - } + void AddUtility(std::string const& name, cmMakefile* mf = nullptr); + //! Get the utilities used by this target + std::set<BT<std::string>> const& GetUtilities() const; - ///! Set/Get a property of this target file + //! Set/Get a property of this target file void SetProperty(const std::string& prop, const char* value); void AppendProperty(const std::string& prop, const char* value, bool asString = false); - ///! Might return a nullptr if the property is not set or invalid + //! Might return a nullptr if the property is not set or invalid const char* GetProperty(const std::string& prop) const; - ///! Always returns a valid pointer + //! Always returns a valid pointer const char* GetSafeProperty(const std::string& prop) const; bool GetPropertyAsBool(const std::string& prop) const; void CheckProperty(const std::string& prop, cmMakefile* context) const; const char* GetComputedProperty(const std::string& prop, cmMessenger* messenger, cmListFileBacktrace const& context) const; + //! Get all properties + cmPropertyMap const& GetProperties() const; - bool IsImported() const { return this->IsImportedTarget; } - bool IsImportedGloballyVisible() const - { - return this->ImportedGloballyVisible; - } - - // Get the properties - cmPropertyMap const& GetProperties() const { return this->Properties; } + bool IsImported() const; + bool IsImportedGloballyVisible() const; bool GetMappedConfig(std::string const& desired_config, const char** loc, const char** imp, std::string& suffix) const; - /** Return whether this target is an executable with symbol exports - enabled. */ + //! Return whether this target is an executable with symbol exports enabled. bool IsExecutableWithExports() const; - /** Return whether this target is a shared library Framework on - Apple. */ + //! Return whether this target is a shared library Framework on Apple. bool IsFrameworkOnApple() const; - /** Return whether this target is an executable Bundle on Apple. */ + //! Return whether this target is an executable Bundle on Apple. bool IsAppBundleOnApple() const; - /** Get a backtrace from the creation of the target. */ + //! Get a backtrace from the creation of the target. cmListFileBacktrace const& GetBacktrace() const; void InsertInclude(std::string const& entry, cmListFileBacktrace const& bt, @@ -252,11 +212,8 @@ public: std::string GetDebugGeneratorExpressions(const std::string& value, cmTargetLinkLibraryType llt) const; - void AddSystemIncludeDirectories(const std::set<std::string>& incs); - std::set<std::string> const& GetSystemIncludeDirectories() const - { - return this->SystemIncludeDirectories; - } + void AddSystemIncludeDirectories(std::set<std::string> const& incs); + std::set<std::string> const& GetSystemIncludeDirectories() const; cmStringRange GetIncludeDirectoriesEntries() const; cmBacktraceRange GetIncludeDirectoriesBacktraces() const; @@ -282,63 +239,25 @@ public: cmStringRange GetLinkImplementationEntries() const; cmBacktraceRange GetLinkImplementationBacktraces() const; + std::string ImportedGetFullPath(const std::string& config, + cmStateEnums::ArtifactType artifact) const; + struct StrictTargetComparison { bool operator()(cmTarget const* t1, cmTarget const* t2) const; }; - std::string ImportedGetFullPath(const std::string& config, - cmStateEnums::ArtifactType artifact) const; - private: + // Internal representation details. + friend class cmGeneratorTarget; + const char* GetSuffixVariableInternal( cmStateEnums::ArtifactType artifact) const; const char* GetPrefixVariableInternal( cmStateEnums::ArtifactType artifact) const; - // Use a makefile variable to set a default for the given property. - // If the variable is not defined use the given default instead. - void SetPropertyDefault(const std::string& property, - const char* default_value); - - bool CheckImportedLibName(std::string const& prop, - std::string const& value) const; - private: - bool IsGeneratorProvided; - cmPropertyMap Properties; - std::set<std::string> SystemIncludeDirectories; - std::set<BT<std::string>> Utilities; - cmPolicies::PolicyMap PolicyMap; - std::string Name; - std::string InstallPath; - std::string RuntimeInstallPath; - std::vector<cmCustomCommand> PreBuildCommands; - std::vector<cmCustomCommand> PreLinkCommands; - std::vector<cmCustomCommand> PostBuildCommands; - std::vector<std::pair<TLLSignature, cmListFileContext>> TLLCommands; - LinkLibraryVectorType OriginalLinkLibraries; - cmMakefile* Makefile; - cmTargetInternalPointer Internal; - cmStateEnums::TargetType TargetTypeValue; - bool HaveInstallRule; - bool DLLPlatform; - bool IsAndroid; - bool IsImportedTarget; - bool ImportedGloballyVisible; - bool BuildInterfaceIncludesAppended; - - std::string ProcessSourceItemCMP0049(const std::string& s); - - /** Return whether or not the target has a DLL import library. */ - bool HasImportLibrary() const; - - // Internal representation details. - friend class cmTargetInternals; - friend class cmGeneratorTarget; - friend class cmTargetTraceDependencies; - - cmListFileBacktrace Backtrace; + std::unique_ptr<cmTargetInternals> impl; }; typedef std::unordered_map<std::string, cmTarget> cmTargets; diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index 5c7b95c..3883b52 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -456,8 +456,8 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib, this->Target->GetType() == cmStateEnums::OBJECT_LIBRARY) { std::string configLib = this->Target->GetDebugGeneratorExpressions(libRef, llt); - if (cmGeneratorExpression::IsValidTargetName(libRef) || - cmGeneratorExpression::Find(libRef) != std::string::npos) { + if (cmGeneratorExpression::IsValidTargetName(lib) || + cmGeneratorExpression::Find(lib) != std::string::npos) { configLib = "$<LINK_ONLY:" + configLib + ">"; } this->Target->AppendProperty("INTERFACE_LINK_LIBRARIES", diff --git a/Source/cmTargetPropertyComputer.cxx b/Source/cmTargetPropertyComputer.cxx index d2c3496..994fcf7 100644 --- a/Source/cmTargetPropertyComputer.cxx +++ b/Source/cmTargetPropertyComputer.cxx @@ -67,6 +67,8 @@ bool cmTargetPropertyComputer::WhiteListedInterfaceProperty( builtIns.insert("IMPORTED_GLOBAL"); builtIns.insert("MANUALLY_ADDED_DEPENDENCIES"); builtIns.insert("NAME"); + builtIns.insert("PRIVATE_HEADER"); + builtIns.insert("PUBLIC_HEADER"); builtIns.insert("TYPE"); } diff --git a/Source/cmTargetPropertyComputer.h b/Source/cmTargetPropertyComputer.h index 97e4fba..efbf95f 100644 --- a/Source/cmTargetPropertyComputer.h +++ b/Source/cmTargetPropertyComputer.h @@ -81,7 +81,7 @@ private: context)) { return nullptr; } - const char* configName = prop.c_str() + 9; + std::string configName = prop.substr(9); return ComputeLocation(tgt, configName); } diff --git a/Source/cmTest.h b/Source/cmTest.h index d4839d1..88dc730 100644 --- a/Source/cmTest.h +++ b/Source/cmTest.h @@ -26,14 +26,14 @@ public: cmTest(cmMakefile* mf); ~cmTest(); - ///! Set the test name + //! Set the test name void SetName(const std::string& name); std::string GetName() const { return this->Name; } void SetCommand(std::vector<std::string> const& command); std::vector<std::string> const& GetCommand() const { return this->Command; } - ///! Set/Get a property of this source file + //! Set/Get a property of this source file void SetProperty(const std::string& prop, const char* value); void AppendProperty(const std::string& prop, const char* value, bool asString = false); diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx index 5102613..571cd09 100644 --- a/Source/cmTestGenerator.cxx +++ b/Source/cmTestGenerator.cxx @@ -12,6 +12,7 @@ #include "cmOutputConverter.h" #include "cmProperty.h" #include "cmPropertyMap.h" +#include "cmRange.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTest.h" @@ -94,10 +95,8 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, std::string emulatorExe(emulatorWithArgs[0]); cmSystemTools::ConvertToUnixSlashes(emulatorExe); os << cmOutputConverter::EscapeForCMake(emulatorExe) << " "; - for (std::vector<std::string>::const_iterator ei = - emulatorWithArgs.begin() + 1; - ei != emulatorWithArgs.end(); ++ei) { - os << cmOutputConverter::EscapeForCMake(*ei) << " "; + for (std::string const& arg : cmMakeRange(emulatorWithArgs).advance(1)) { + os << cmOutputConverter::EscapeForCMake(arg) << " "; } } } else { @@ -108,11 +107,10 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, // Generate the command line with full escapes. os << cmOutputConverter::EscapeForCMake(exe); - for (std::vector<std::string>::const_iterator ci = command.begin() + 1; - ci != command.end(); ++ci) { + for (std::string const& arg : cmMakeRange(command).advance(1)) { os << " " << cmOutputConverter::EscapeForCMake( - ge.Parse(*ci)->Evaluate(this->LG, config)); + ge.Parse(arg)->Evaluate(this->LG, config)); } // Finish the test command. @@ -157,12 +155,11 @@ void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent) fout << "add_test("; fout << this->Test->GetName() << " \"" << exe << "\""; - for (std::vector<std::string>::const_iterator argit = command.begin() + 1; - argit != command.end(); ++argit) { + for (std::string const& arg : cmMakeRange(command).advance(1)) { // Just double-quote all arguments so they are re-parsed // correctly by the test system. fout << " \""; - for (char c : *argit) { + for (char c : arg) { // Escape quotes within arguments. We should escape // backslashes too but we cannot because it makes the result // inconsistent with previous behavior of this command. diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index c57aabd..4b0707b 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -8,6 +8,7 @@ #include "cmDuration.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmSystemTools.h" @@ -172,25 +173,22 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs, std::vector<std::string> emulatorWithArgs; cmSystemTools::ExpandListArgument(emulator, emulatorWithArgs); finalCommand += - cmSystemTools::ConvertToRunCommandPath(emulatorWithArgs[0].c_str()); + cmSystemTools::ConvertToRunCommandPath(emulatorWithArgs[0]); finalCommand += " "; - for (std::vector<std::string>::const_iterator ei = - emulatorWithArgs.begin() + 1; - ei != emulatorWithArgs.end(); ++ei) { + for (std::string const& arg : cmMakeRange(emulatorWithArgs).advance(1)) { finalCommand += "\""; - finalCommand += *ei; + finalCommand += arg; finalCommand += "\""; finalCommand += " "; } } - finalCommand += - cmSystemTools::ConvertToRunCommandPath(this->OutputFile.c_str()); + finalCommand += cmSystemTools::ConvertToRunCommandPath(this->OutputFile); if (!runArgs.empty()) { finalCommand += runArgs; } bool worked = cmSystemTools::RunSingleCommand( - finalCommand.c_str(), out, out, &retVal, nullptr, - cmSystemTools::OUTPUT_NONE, cmDuration::zero()); + finalCommand, out, out, &retVal, nullptr, cmSystemTools::OUTPUT_NONE, + cmDuration::zero()); // set the run var char retChar[16]; const char* retStr; diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx index fd07d2d..27069ee 100644 --- a/Source/cmUVHandlePtr.cxx +++ b/Source/cmUVHandlePtr.cxx @@ -11,19 +11,59 @@ namespace cm { -static void close_delete(uv_handle_t* h) +struct uv_loop_deleter { - free(h); + void operator()(uv_loop_t* loop) const; +}; + +void uv_loop_deleter::operator()(uv_loop_t* loop) const +{ + uv_run(loop, UV_RUN_DEFAULT); + int result = uv_loop_close(loop); + (void)result; + assert(result >= 0); + free(loop); +} + +int uv_loop_ptr::init(void* data) +{ + this->reset(); + + this->loop.reset(static_cast<uv_loop_t*>(calloc(1, sizeof(uv_loop_t))), + uv_loop_deleter()); + this->loop->data = data; + + return uv_loop_init(this->loop.get()); +} + +void uv_loop_ptr::reset() +{ + this->loop.reset(); +} + +uv_loop_ptr::operator uv_loop_t*() +{ + return this->loop.get(); +} + +uv_loop_t* uv_loop_ptr::operator->() const noexcept +{ + return this->loop.get(); +} + +uv_loop_t* uv_loop_ptr::get() const +{ + return this->loop.get(); } template <typename T> -static void default_delete(T* type_handle) +static void handle_default_delete(T* type_handle) { auto handle = reinterpret_cast<uv_handle_t*>(type_handle); if (handle) { assert(!uv_is_closing(handle)); if (!uv_is_closing(handle)) { - uv_close(handle, &close_delete); + uv_close(handle, [](uv_handle_t* h) { free(h); }); } } } @@ -34,7 +74,7 @@ static void default_delete(T* type_handle) template <typename T> struct uv_handle_deleter { - void operator()(T* type_handle) const { default_delete(type_handle); } + void operator()(T* type_handle) const { handle_default_delete(type_handle); } }; template <typename T> @@ -107,7 +147,7 @@ struct uv_handle_deleter<uv_async_t> void operator()(uv_async_t* handle) { std::lock_guard<std::mutex> lock(*handleMutex); - default_delete(handle); + handle_default_delete(handle); } }; @@ -136,7 +176,7 @@ struct uv_handle_deleter<uv_signal_t> { if (handle) { uv_signal_stop(handle); - default_delete(handle); + handle_default_delete(handle); } } }; diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h index 992c429..c09e4bf 100644 --- a/Source/cmUVHandlePtr.h +++ b/Source/cmUVHandlePtr.h @@ -30,7 +30,45 @@ namespace cm { /*** - * RAII class to simplify and insure the safe usage of uv_*_t types. This + * RAII class to simplify and ensure the safe usage of uv_loop_t. This includes + * making sure resources are properly freed. + */ +class uv_loop_ptr +{ +protected: + std::shared_ptr<uv_loop_t> loop; + +public: + uv_loop_ptr(uv_loop_ptr const&) = delete; + uv_loop_ptr& operator=(uv_loop_ptr const&) = delete; + uv_loop_ptr(uv_loop_ptr&&) noexcept; + uv_loop_ptr& operator=(uv_loop_ptr&&) noexcept; + + // Dtor and ctor need to be inline defined like this for default ctors and + // dtors to work. Some compilers do not like '= default' here. + uv_loop_ptr() {} // NOLINT(modernize-use-equals-default) + uv_loop_ptr(std::nullptr_t) {} + ~uv_loop_ptr() { this->reset(); } + + int init(void* data = nullptr); + + /** + * Properly close the handle if needed and sets the inner handle to nullptr + */ + void reset(); + + /** + * Allow less verbose calling of uv_loop_* functions + * @return reinterpreted handle + */ + operator uv_loop_t*(); + + uv_loop_t* get() const; + uv_loop_t* operator->() const noexcept; +}; + +/*** + * RAII class to simplify and ensure the safe usage of uv_*_t types. This * includes making sure resources are properly freed and contains casting * operators which allow for passing into relevant uv_* functions. * diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h new file mode 100644 index 0000000..29e4fde --- /dev/null +++ b/Source/cmUVStreambuf.h @@ -0,0 +1,219 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmUVStreambuf_h +#define cmUVStreambuf_h + +#include "cmUVHandlePtr.h" + +#include "cm_uv.h" + +#include <algorithm> +#include <cstring> +#include <streambuf> +#include <vector> + +/* + * This file is based on example code from: + * + * http://www.voidcn.com/article/p-vjnlygmc-gy.html + * + * The example code was distributed under the following license: + * + * Copyright 2007 Edd Dawson. + * Distributed under the Boost Software License, Version 1.0. + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +template <typename CharT, typename Traits = std::char_traits<CharT>> +class cmBasicUVStreambuf : public std::basic_streambuf<CharT, Traits> +{ +public: + cmBasicUVStreambuf(std::size_t bufSize = 256, std::size_t putBack = 8); + ~cmBasicUVStreambuf() override; + + bool is_open() const; + + cmBasicUVStreambuf* open(uv_stream_t* stream); + + cmBasicUVStreambuf* close(); + +protected: + typename cmBasicUVStreambuf::int_type underflow() override; + std::streamsize showmanyc() override; + + // FIXME: Add write support + +private: + uv_stream_t* Stream = nullptr; + void* OldStreamData; + const std::size_t PutBack; + std::vector<CharT> InputBuffer; + bool EndOfFile; + + void StreamReadStartStop(); + + void StreamRead(ssize_t nread); + void HandleAlloc(uv_buf_t* buf); +}; + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>::cmBasicUVStreambuf(std::size_t bufSize, + std::size_t putBack) + : PutBack(std::max<std::size_t>(putBack, 1)) + , InputBuffer(std::max<std::size_t>(this->PutBack, bufSize) + this->PutBack) +{ + this->close(); +} + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>::~cmBasicUVStreambuf() +{ + this->close(); +} + +template <typename CharT, typename Traits> +bool cmBasicUVStreambuf<CharT, Traits>::is_open() const +{ + return this->Stream != nullptr; +} + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::open( + uv_stream_t* stream) +{ + this->close(); + this->Stream = stream; + this->EndOfFile = false; + if (this->Stream) { + this->OldStreamData = this->Stream->data; + this->Stream->data = this; + } + this->StreamReadStartStop(); + return this; +} + +template <typename CharT, typename Traits> +cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::close() +{ + if (this->Stream) { + uv_read_stop(this->Stream); + this->Stream->data = this->OldStreamData; + } + this->Stream = nullptr; + CharT* readEnd = this->InputBuffer.data() + this->InputBuffer.size(); + this->setg(readEnd, readEnd, readEnd); + return this; +} + +template <typename CharT, typename Traits> +typename cmBasicUVStreambuf<CharT, Traits>::int_type +cmBasicUVStreambuf<CharT, Traits>::underflow() +{ + if (!this->is_open()) { + return Traits::eof(); + } + + if (this->gptr() < this->egptr()) { + return Traits::to_int_type(*this->gptr()); + } + + this->StreamReadStartStop(); + while (this->in_avail() == 0) { + uv_run(this->Stream->loop, UV_RUN_ONCE); + } + if (this->in_avail() == -1) { + return Traits::eof(); + } + return Traits::to_int_type(*this->gptr()); +} + +template <typename CharT, typename Traits> +std::streamsize cmBasicUVStreambuf<CharT, Traits>::showmanyc() +{ + if (!this->is_open() || this->EndOfFile) { + return -1; + } + return 0; +} + +template <typename CharT, typename Traits> +void cmBasicUVStreambuf<CharT, Traits>::StreamReadStartStop() +{ + if (this->Stream) { + uv_read_stop(this->Stream); + if (this->gptr() >= this->egptr()) { + uv_read_start( + this->Stream, + [](uv_handle_t* handle, size_t /* unused */, uv_buf_t* buf) { + auto streambuf = + static_cast<cmBasicUVStreambuf<CharT, Traits>*>(handle->data); + streambuf->HandleAlloc(buf); + }, + [](uv_stream_t* stream2, ssize_t nread, const uv_buf_t* /* unused */) { + auto streambuf = + static_cast<cmBasicUVStreambuf<CharT, Traits>*>(stream2->data); + streambuf->StreamRead(nread); + }); + } + } +} + +template <typename CharT, typename Traits> +void cmBasicUVStreambuf<CharT, Traits>::HandleAlloc(uv_buf_t* buf) +{ + auto size = this->egptr() - this->gptr(); + std::memmove(this->InputBuffer.data(), this->gptr(), + this->egptr() - this->gptr()); + this->setg(this->InputBuffer.data(), this->InputBuffer.data(), + this->InputBuffer.data() + size); + buf->base = this->egptr(); +#ifdef _WIN32 +# define BUF_LEN_TYPE ULONG +#else +# define BUF_LEN_TYPE size_t +#endif + buf->len = BUF_LEN_TYPE( + (this->InputBuffer.data() + this->InputBuffer.size() - this->egptr()) * + sizeof(CharT)); +#undef BUF_LEN_TYPE +} + +template <typename CharT, typename Traits> +void cmBasicUVStreambuf<CharT, Traits>::StreamRead(ssize_t nread) +{ + if (nread > 0) { + this->setg(this->eback(), this->gptr(), + this->egptr() + nread / sizeof(CharT)); + uv_read_stop(this->Stream); + } else if (nread < 0 || nread == UV_EOF) { + this->EndOfFile = true; + uv_read_stop(this->Stream); + } +} + +using cmUVStreambuf = cmBasicUVStreambuf<char>; + +#endif diff --git a/Source/cmUseMangledMesaCommand.cxx b/Source/cmUseMangledMesaCommand.cxx index 9648b21..88e415a 100644 --- a/Source/cmUseMangledMesaCommand.cxx +++ b/Source/cmUseMangledMesaCommand.cxx @@ -19,7 +19,7 @@ bool cmUseMangledMesaCommand::InitialPass(std::vector<std::string> const& args, this->SetError("called with incorrect number of arguments"); return false; } - const char* inputDir = args[0].c_str(); + const std::string& inputDir = args[0]; std::string glh = inputDir; glh += "/"; glh += "gl.h"; @@ -34,7 +34,7 @@ bool cmUseMangledMesaCommand::InitialPass(std::vector<std::string> const& args, std::vector<std::string> files; cmSystemTools::Glob(inputDir, "\\.h$", files); if (files.empty()) { - cmSystemTools::Error("Could not open Mesa Directory ", inputDir); + cmSystemTools::Error("Could not open Mesa Directory " + inputDir); return false; } cmSystemTools::MakeDirectory(destDir); @@ -60,8 +60,8 @@ void cmUseMangledMesaCommand::CopyAndFullPathMesaHeader(const char* source, tempOutputFile += ".tmp"; cmsys::ofstream fout(tempOutputFile.c_str()); if (!fout) { - cmSystemTools::Error("Could not open file for write in copy operation: ", - tempOutputFile.c_str(), outdir); + cmSystemTools::Error("Could not open file for write in copy operation: " + + tempOutputFile + outdir); cmSystemTools::ReportLastSystemError(""); return; } @@ -78,7 +78,7 @@ void cmUseMangledMesaCommand::CopyAndFullPathMesaHeader(const char* source, cmsys::RegularExpression includeLine( "^[ \t]*#[ \t]*include[ \t]*[<\"]([^\">]+)[\">]"); // regular expression for gl/ or GL/ in a file (match(1) of above) - cmsys::RegularExpression glDirLine("(gl|GL)(/|\\\\)([^<\"]+)"); + cmsys::RegularExpression glDirLine(R"((gl|GL)(/|\\)([^<"]+))"); // regular expression for gl GL or xmesa in a file (match(1) of above) cmsys::RegularExpression glLine("(gl|GL|xmesa)"); while (cmSystemTools::GetLineFromStream(fin, inLine)) { diff --git a/Source/cmUtilitySourceCommand.cxx b/Source/cmUtilitySourceCommand.cxx index 231bca4..b59a587 100644 --- a/Source/cmUtilitySourceCommand.cxx +++ b/Source/cmUtilitySourceCommand.cxx @@ -79,7 +79,7 @@ bool cmUtilitySourceCommand::InitialPass(std::vector<std::string> const& args, } // The source exists. - std::string cmakeCFGout = + const std::string& cmakeCFGout = this->Makefile->GetRequiredDefinition("CMAKE_CFG_INTDIR"); std::string utilityDirectory = this->Makefile->GetCurrentBinaryDirectory(); std::string exePath; diff --git a/Source/cmUuid.cxx b/Source/cmUuid.cxx index 201e1cc..51ecbd1 100644 --- a/Source/cmUuid.cxx +++ b/Source/cmUuid.cxx @@ -4,16 +4,10 @@ #include "cmCryptoHash.h" +#include <array> #include <string.h> -cmUuid::cmUuid() -{ - Groups.push_back(4); - Groups.push_back(2); - Groups.push_back(2); - Groups.push_back(2); - Groups.push_back(6); -} +static const std::array<int, 5> kUuidGroups = { { 4, 2, 2, 2, 6 } }; std::string cmUuid::FromMd5(std::vector<unsigned char> const& uuidNamespace, std::string const& name) const @@ -83,11 +77,11 @@ bool cmUuid::StringToBinary(std::string const& input, return false; } size_t index = 0; - for (size_t i = 0; i < this->Groups.size(); ++i) { + for (size_t i = 0; i < kUuidGroups.size(); ++i) { if (i != 0 && input[index++] != '-') { return false; } - size_t digits = this->Groups[i] * 2; + size_t digits = kUuidGroups[i] * 2; if (!StringToBinaryImpl(input.substr(index, digits), output)) { return false; } @@ -103,12 +97,12 @@ std::string cmUuid::BinaryToString(const unsigned char* input) const std::string output; size_t inputIndex = 0; - for (size_t i = 0; i < this->Groups.size(); ++i) { + for (size_t i = 0; i < kUuidGroups.size(); ++i) { if (i != 0) { output += '-'; } - size_t bytes = this->Groups[i]; + size_t bytes = kUuidGroups[i]; for (size_t j = 0; j < bytes; ++j) { unsigned char byte = input[inputIndex++]; output += this->ByteToHex(byte); diff --git a/Source/cmUuid.h b/Source/cmUuid.h index 158ce6e..7de20dd 100644 --- a/Source/cmUuid.h +++ b/Source/cmUuid.h @@ -15,8 +15,6 @@ class cmUuid { public: - cmUuid(); - std::string FromMd5(std::vector<unsigned char> const& uuidNamespace, std::string const& name) const; @@ -42,8 +40,6 @@ private: std::string BinaryToString(const unsigned char* input) const; bool IntFromHexDigit(char input, char& output) const; - - std::vector<int> Groups; }; #endif diff --git a/Source/cmVariableWatch.h b/Source/cmVariableWatch.h index 5855fed..1230101 100644 --- a/Source/cmVariableWatch.h +++ b/Source/cmVariableWatch.h @@ -72,6 +72,9 @@ protected: this->DeleteDataCall(this->ClientData); } } + Pair() = default; + Pair(const Pair&) = delete; + Pair& operator=(const Pair&) = delete; }; typedef std::vector<std::shared_ptr<Pair>> VectorOfPairs; diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 7736e59..c60706d 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -4,6 +4,7 @@ #include "cmAlgorithms.h" #include "cmComputeLinkInformation.h" +#include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" @@ -623,8 +624,8 @@ void cmVisualStudio10TargetGenerator::Generate() propsLocal += this->DefaultArtifactDir; propsLocal += "\\nasm.props"; ConvertToWindowsSlash(propsLocal); - this->Makefile->ConfigureFile(propsTemplate.c_str(), - propsLocal.c_str(), false, true, true); + this->Makefile->ConfigureFile(propsTemplate, propsLocal, false, true, + true); Elem(e1, "Import").Attribute("Project", propsLocal); } } @@ -662,6 +663,7 @@ void cmVisualStudio10TargetGenerator::Generate() this->WriteCustomCommands(e0); this->WriteAllSources(e0); this->WriteDotNetReferences(e0); + this->WriteImports(e0); this->WriteEmbeddedResourceGroup(e0); this->WriteXamlFilesGroup(e0); this->WriteWinRTReferences(e0); @@ -810,6 +812,24 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReference( this->WriteDotNetReferenceCustomTags(e2, ref); } +void cmVisualStudio10TargetGenerator::WriteImports(Elem& e0) +{ + const char* imports = + this->GeneratorTarget->Target->GetProperty("VS_PROJECT_IMPORT"); + if (imports) { + std::vector<std::string> argsSplit; + cmSystemTools::ExpandListArgument(std::string(imports), argsSplit, false); + for (auto& path : argsSplit) { + if (!cmsys::SystemTools::FileIsFullPath(path)) { + path = this->Makefile->GetCurrentSourceDirectory() + "/" + path; + } + ConvertToWindowsSlash(path); + Elem e1(e0, "Import"); + e1.Attribute("Project", path); + } + } +} + void cmVisualStudio10TargetGenerator::WriteDotNetReferenceCustomTags( Elem& e2, std::string const& ref) { @@ -1322,8 +1342,7 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( std::string error = "Could not create file: ["; error += sourcePath; error += "] "; - cmSystemTools::Error(error.c_str(), - cmSystemTools::GetLastSystemError().c_str()); + cmSystemTools::Error(error + cmSystemTools::GetLastSystemError()); } } } @@ -1430,10 +1449,10 @@ std::string cmVisualStudio10TargetGenerator::ConvertPath( static void ConvertToWindowsSlash(std::string& s) { // first convert all of the slashes - std::string::size_type pos = 0; - while ((pos = s.find('/', pos)) != std::string::npos) { - s[pos] = '\\'; - pos++; + for (auto& ch : s) { + if (ch == '/') { + ch = '\\'; + } } } @@ -1449,7 +1468,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups() std::vector<cmGeneratorTarget::AllConfigSource> const& sources = this->GeneratorTarget->GetAllConfigSources(); - std::set<cmSourceGroup*> groupsUsed; + std::set<cmSourceGroup const*> groupsUsed; for (cmGeneratorTarget::AllConfigSource const& si : sources) { std::string const& source = si.Source->GetFullPath(); cmSourceGroup* sourceGroup = @@ -1534,13 +1553,13 @@ void cmVisualStudio10TargetGenerator::WriteGroups() { Elem e1(e0, "ItemGroup"); e1.SetHasElements(); - std::vector<cmSourceGroup*> groupsVec(groupsUsed.begin(), - groupsUsed.end()); + std::vector<cmSourceGroup const*> groupsVec(groupsUsed.begin(), + groupsUsed.end()); std::sort(groupsVec.begin(), groupsVec.end(), - [](cmSourceGroup* l, cmSourceGroup* r) { + [](cmSourceGroup const* l, cmSourceGroup const* r) { return l->GetFullName() < r->GetFullName(); }); - for (cmSourceGroup* sg : groupsVec) { + for (cmSourceGroup const* sg : groupsVec) { std::string const& name = sg->GetFullName(); if (!name.empty()) { std::string guidName = "SG_Filter_" + name; @@ -1572,7 +1591,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups() // Add to groupsUsed empty source groups that have non-empty children. void cmVisualStudio10TargetGenerator::AddMissingSourceGroups( - std::set<cmSourceGroup*>& groupsUsed, + std::set<cmSourceGroup const*>& groupsUsed, const std::vector<cmSourceGroup>& allGroups) { for (cmSourceGroup const& current : allGroups) { @@ -1583,17 +1602,15 @@ void cmVisualStudio10TargetGenerator::AddMissingSourceGroups( this->AddMissingSourceGroups(groupsUsed, children); - cmSourceGroup* current_ptr = const_cast<cmSourceGroup*>(¤t); - if (groupsUsed.find(current_ptr) != groupsUsed.end()) { + if (groupsUsed.count(¤t) > 0) { continue; // group has already been added to set } // check if it least one of the group's descendants is not empty // (at least one child must already have been added) - std::vector<cmSourceGroup>::const_iterator child_it = children.begin(); + auto child_it = children.begin(); while (child_it != children.end()) { - cmSourceGroup* child_ptr = const_cast<cmSourceGroup*>(&(*child_it)); - if (groupsUsed.find(child_ptr) != groupsUsed.end()) { + if (groupsUsed.count(&(*child_it)) > 0) { break; // found a child that was already added => add current group too } child_it++; @@ -1603,7 +1620,7 @@ void cmVisualStudio10TargetGenerator::AddMissingSourceGroups( continue; // no descendants have source files => ignore this group } - groupsUsed.insert(current_ptr); + groupsUsed.insert(¤t); } } @@ -2515,8 +2532,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( this->GeneratorTarget->GetLinkerLanguage(configName); if (linkLanguage.empty()) { cmSystemTools::Error( - "CMake can not determine linker language for target: ", - this->Name.c_str()); + "CMake can not determine linker language for target: " + this->Name); return false; } @@ -2531,24 +2547,17 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } else { std::set<std::string> languages; this->GeneratorTarget->GetLanguages(languages, configName); - for (const char* const* l = cm::cbegin(clLangs); l != cm::cend(clLangs); - ++l) { - if (languages.find(*l) != languages.end()) { - langForClCompile = *l; + for (const char* l : clLangs) { + if (languages.count(l)) { + langForClCompile = l; break; } } } this->LangForClCompile = langForClCompile; if (!langForClCompile.empty()) { - std::string baseFlagVar = "CMAKE_"; - baseFlagVar += langForClCompile; - baseFlagVar += "_FLAGS"; - flags = this->Makefile->GetRequiredDefinition(baseFlagVar); - std::string flagVar = - baseFlagVar + "_" + cmSystemTools::UpperCase(configName); - flags += " "; - flags += this->Makefile->GetRequiredDefinition(flagVar); + this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, + langForClCompile, configName); this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget, langForClCompile, configName); } @@ -3346,8 +3355,7 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( const std::string& linkLanguage = linkClosure->LinkerLanguage; if (linkLanguage.empty()) { cmSystemTools::Error( - "CMake can not determine linker language for target: ", - this->Name.c_str()); + "CMake can not determine linker language for target: " + this->Name); return false; } @@ -3392,8 +3400,8 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( this->GeneratorTarget->GetLinkInformation(config); if (!pcli) { cmSystemTools::Error( - "CMake can not compute cmComputeLinkInformation for target: ", - this->Name.c_str()); + "CMake can not compute cmComputeLinkInformation for target: " + + this->Name); return false; } cmComputeLinkInformation& cli = *pcli; @@ -3440,18 +3448,11 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( linkDirs.push_back("%(AdditionalLibraryDirectories)"); linkOptions.AddFlag("AdditionalLibraryDirectories", linkDirs); - std::string targetName; - std::string targetNameSO; - std::string targetNameFull; - std::string targetNameImport; - std::string targetNamePDB; + cmGeneratorTarget::Names targetNames; if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) { - this->GeneratorTarget->GetExecutableNames( - targetName, targetNameFull, targetNameImport, targetNamePDB, config); + targetNames = this->GeneratorTarget->GetExecutableNames(config); } else { - this->GeneratorTarget->GetLibraryNames(targetName, targetNameSO, - targetNameFull, targetNameImport, - targetNamePDB, config); + targetNames = this->GeneratorTarget->GetLibraryNames(config); } if (this->MSTools) { @@ -3492,11 +3493,11 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( std::string pdb = this->GeneratorTarget->GetPDBDirectory(config); pdb += "/"; - pdb += targetNamePDB; + pdb += targetNames.PDB; std::string imLib = this->GeneratorTarget->GetDirectory( config, cmStateEnums::ImportLibraryArtifact); imLib += "/"; - imLib += targetNameImport; + imLib += targetNames.ImportLibrary; linkOptions.AddFlag("ImportLibrary", imLib); linkOptions.AddFlag("ProgramDataBaseFile", pdb); @@ -3520,7 +3521,7 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( linkOptions.AppendFlag("IgnoreSpecificDefaultLibraries", "ole32.lib"); } } else if (this->NsightTegra) { - linkOptions.AddFlag("SoName", targetNameSO); + linkOptions.AddFlag("SoName", targetNames.SharedObject); } linkOptions.Parse(flags); @@ -3580,8 +3581,8 @@ bool cmVisualStudio10TargetGenerator::ComputeLibOptions( this->GeneratorTarget->GetLinkInformation(config); if (!pcli) { cmSystemTools::Error( - "CMake can not compute cmComputeLinkInformation for target: ", - this->Name.c_str()); + "CMake can not compute cmComputeLinkInformation for target: " + + this->Name); return false; } @@ -4073,10 +4074,7 @@ bool cmVisualStudio10TargetGenerator::IsResxHeader( { std::set<std::string> expectedResxHeaders; this->GeneratorTarget->GetExpectedResxHeaders(expectedResxHeaders, ""); - - std::set<std::string>::const_iterator it = - expectedResxHeaders.find(headerFile); - return it != expectedResxHeaders.end(); + return expectedResxHeaders.count(headerFile) > 0; } bool cmVisualStudio10TargetGenerator::IsXamlHeader( @@ -4084,10 +4082,7 @@ bool cmVisualStudio10TargetGenerator::IsXamlHeader( { std::set<std::string> expectedXamlHeaders; this->GeneratorTarget->GetExpectedXamlHeaders(expectedXamlHeaders, ""); - - std::set<std::string>::const_iterator it = - expectedXamlHeaders.find(headerFile); - return it != expectedXamlHeaders.end(); + return expectedXamlHeaders.count(headerFile) > 0; } bool cmVisualStudio10TargetGenerator::IsXamlSource( @@ -4095,10 +4090,7 @@ bool cmVisualStudio10TargetGenerator::IsXamlSource( { std::set<std::string> expectedXamlSources; this->GeneratorTarget->GetExpectedXamlSources(expectedXamlSources, ""); - - std::set<std::string>::const_iterator it = - expectedXamlSources.find(sourceFile); - return it != expectedXamlSources.end(); + return expectedXamlSources.count(sourceFile) > 0; } void cmVisualStudio10TargetGenerator::WriteApplicationTypeSettings(Elem& e1) @@ -4669,10 +4661,8 @@ void cmVisualStudio10TargetGenerator::GetCSharpSourceProperties( void cmVisualStudio10TargetGenerator::WriteCSharpSourceProperties( Elem& e2, const std::map<std::string, std::string>& tags) { - if (!tags.empty()) { - for (const auto& i : tags) { - e2.Element(i.first.c_str(), i.second); - } + for (const auto& i : tags) { + e2.Element(i.first.c_str(), i.second); } } diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 5901004..1dea8e9 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -76,6 +76,7 @@ private: void WriteDotNetReference(Elem& e1, std::string const& ref, std::string const& hint, std::string const& config); + void WriteImports(Elem& e0); void WriteDotNetReferenceCustomTags(Elem& e2, std::string const& ref); void WriteEmbeddedResourceGroup(Elem& e0); void WriteWinRTReferences(Elem& e0); @@ -165,7 +166,7 @@ private: void WriteGroupSources(Elem& e0, std::string const& name, ToolSources const& sources, std::vector<cmSourceGroup>&); - void AddMissingSourceGroups(std::set<cmSourceGroup*>& groupsUsed, + void AddMissingSourceGroups(std::set<cmSourceGroup const*>& groupsUsed, const std::vector<cmSourceGroup>& allGroups); bool IsResxHeader(const std::string& headerFile); bool IsXamlHeader(const std::string& headerFile); diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 5c3e533..e1b0c70 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -374,19 +374,19 @@ void cmVisualStudioGeneratorOptions::StoreUnknownFlag(std::string const& flag) { // Look for Intel Fortran flags that do not map well in the flag table. if (this->CurrentTool == FortranCompiler) { - if (flag == "/dbglibs") { + if (flag == "/dbglibs" || flag == "-dbglibs") { this->FortranRuntimeDebug = true; return; } - if (flag == "/threads") { + if (flag == "/threads" || flag == "-threads") { this->FortranRuntimeMT = true; return; } - if (flag == "/libs:dll") { + if (flag == "/libs:dll" || flag == "-libs:dll") { this->FortranRuntimeDLL = true; return; } - if (flag == "/libs:static") { + if (flag == "/libs:static" || flag == "-libs:static") { this->FortranRuntimeDLL = false; return; } @@ -438,14 +438,13 @@ void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions( const char* sep = ""; std::vector<std::string>::const_iterator de = cmRemoveDuplicates(this->Defines); - for (std::vector<std::string>::const_iterator di = this->Defines.begin(); - di != de; ++di) { + for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) { // Escape the definition for the compiler. std::string define; if (this->Version < cmGlobalVisualStudioGenerator::VS10) { - define = this->LocalGenerator->EscapeForShell(*di, true); + define = this->LocalGenerator->EscapeForShell(di, true); } else { - define = *di; + define = di; } // Escape this flag for the MSBuild. if (this->Version >= cmGlobalVisualStudioGenerator::VS10) { diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx new file mode 100644 index 0000000..cbf070e --- /dev/null +++ b/Source/cmWorkerPool.cxx @@ -0,0 +1,763 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmWorkerPool.h" + +#include "cmRange.h" +#include "cmUVHandlePtr.h" +#include "cmUVSignalHackRAII.h" // IWYU pragma: keep +#include "cm_uv.h" + +#include <algorithm> +#include <array> +#include <condition_variable> +#include <deque> +#include <functional> +#include <mutex> +#include <stddef.h> +#include <thread> + +/** + * @brief libuv pipe buffer class + */ +class cmUVPipeBuffer +{ +public: + typedef cmRange<char const*> DataRange; + typedef std::function<void(DataRange)> DataFunction; + /// On error the ssize_t argument is a non zero libuv error code + typedef std::function<void(ssize_t)> EndFunction; + +public: + /** + * Reset to construction state + */ + void reset(); + + /** + * Initializes uv_pipe(), uv_stream() and uv_handle() + * @return true on success + */ + bool init(uv_loop_t* uv_loop); + + /** + * Start reading + * @return true on success + */ + bool startRead(DataFunction dataFunction, EndFunction endFunction); + + //! libuv pipe + uv_pipe_t* uv_pipe() const { return UVPipe_.get(); } + //! uv_pipe() casted to libuv stream + uv_stream_t* uv_stream() const { return static_cast<uv_stream_t*>(UVPipe_); } + //! uv_pipe() casted to libuv handle + uv_handle_t* uv_handle() { return static_cast<uv_handle_t*>(UVPipe_); } + +private: + // -- Libuv callbacks + static void UVAlloc(uv_handle_t* handle, size_t suggestedSize, + uv_buf_t* buf); + static void UVData(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); + +private: + cm::uv_pipe_ptr UVPipe_; + std::vector<char> Buffer_; + DataFunction DataFunction_; + EndFunction EndFunction_; +}; + +void cmUVPipeBuffer::reset() +{ + if (UVPipe_.get() != nullptr) { + EndFunction_ = nullptr; + DataFunction_ = nullptr; + Buffer_.clear(); + Buffer_.shrink_to_fit(); + UVPipe_.reset(); + } +} + +bool cmUVPipeBuffer::init(uv_loop_t* uv_loop) +{ + reset(); + if (uv_loop == nullptr) { + return false; + } + int ret = UVPipe_.init(*uv_loop, 0, this); + return (ret == 0); +} + +bool cmUVPipeBuffer::startRead(DataFunction dataFunction, + EndFunction endFunction) +{ + if (UVPipe_.get() == nullptr) { + return false; + } + if (!dataFunction || !endFunction) { + return false; + } + DataFunction_ = std::move(dataFunction); + EndFunction_ = std::move(endFunction); + int ret = uv_read_start(uv_stream(), &cmUVPipeBuffer::UVAlloc, + &cmUVPipeBuffer::UVData); + return (ret == 0); +} + +void cmUVPipeBuffer::UVAlloc(uv_handle_t* handle, size_t suggestedSize, + uv_buf_t* buf) +{ + auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(handle->data); + pipe.Buffer_.resize(suggestedSize); + buf->base = pipe.Buffer_.data(); + buf->len = static_cast<unsigned long>(pipe.Buffer_.size()); +} + +void cmUVPipeBuffer::UVData(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) +{ + auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(stream->data); + if (nread > 0) { + if (buf->base != nullptr) { + // Call data function + pipe.DataFunction_(DataRange(buf->base, buf->base + nread)); + } + } else if (nread < 0) { + // Save the end function on the stack before resetting the pipe + EndFunction efunc; + efunc.swap(pipe.EndFunction_); + // Reset pipe before calling the end function + pipe.reset(); + // Call end function + efunc((nread == UV_EOF) ? 0 : nread); + } +} + +/** + * @brief External process management class + */ +class cmUVReadOnlyProcess +{ +public: + // -- Types + //! @brief Process settings + struct SetupT + { + std::string WorkingDirectory; + std::vector<std::string> Command; + cmWorkerPool::ProcessResultT* Result = nullptr; + bool MergedOutput = false; + }; + +public: + // -- Const accessors + SetupT const& Setup() const { return Setup_; } + cmWorkerPool::ProcessResultT* Result() const { return Setup_.Result; } + bool IsStarted() const { return IsStarted_; } + bool IsFinished() const { return IsFinished_; } + + // -- Runtime + void setup(cmWorkerPool::ProcessResultT* result, bool mergedOutput, + std::vector<std::string> const& command, + std::string const& workingDirectory = std::string()); + bool start(uv_loop_t* uv_loop, std::function<void()> finishedCallback); + +private: + // -- Libuv callbacks + static void UVExit(uv_process_t* handle, int64_t exitStatus, int termSignal); + void UVPipeOutData(cmUVPipeBuffer::DataRange data); + void UVPipeOutEnd(ssize_t error); + void UVPipeErrData(cmUVPipeBuffer::DataRange data); + void UVPipeErrEnd(ssize_t error); + void UVTryFinish(); + +private: + // -- Setup + SetupT Setup_; + // -- Runtime + bool IsStarted_ = false; + bool IsFinished_ = false; + std::function<void()> FinishedCallback_; + std::vector<const char*> CommandPtr_; + std::array<uv_stdio_container_t, 3> UVOptionsStdIO_; + uv_process_options_t UVOptions_; + cm::uv_process_ptr UVProcess_; + cmUVPipeBuffer UVPipeOut_; + cmUVPipeBuffer UVPipeErr_; +}; + +void cmUVReadOnlyProcess::setup(cmWorkerPool::ProcessResultT* result, + bool mergedOutput, + std::vector<std::string> const& command, + std::string const& workingDirectory) +{ + Setup_.WorkingDirectory = workingDirectory; + Setup_.Command = command; + Setup_.Result = result; + Setup_.MergedOutput = mergedOutput; +} + +bool cmUVReadOnlyProcess::start(uv_loop_t* uv_loop, + std::function<void()> finishedCallback) +{ + if (IsStarted() || (Result() == nullptr)) { + return false; + } + + // Reset result before the start + Result()->reset(); + + // Fill command string pointers + if (!Setup().Command.empty()) { + CommandPtr_.reserve(Setup().Command.size() + 1); + for (std::string const& arg : Setup().Command) { + CommandPtr_.push_back(arg.c_str()); + } + CommandPtr_.push_back(nullptr); + } else { + Result()->ErrorMessage = "Empty command"; + } + + if (!Result()->error()) { + if (!UVPipeOut_.init(uv_loop)) { + Result()->ErrorMessage = "libuv stdout pipe initialization failed"; + } + } + if (!Result()->error()) { + if (!UVPipeErr_.init(uv_loop)) { + Result()->ErrorMessage = "libuv stderr pipe initialization failed"; + } + } + if (!Result()->error()) { + // -- Setup process stdio options + // stdin + UVOptionsStdIO_[0].flags = UV_IGNORE; + UVOptionsStdIO_[0].data.stream = nullptr; + // stdout + UVOptionsStdIO_[1].flags = + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream(); + // stderr + UVOptionsStdIO_[2].flags = + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream(); + + // -- Setup process options + std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0); + UVOptions_.exit_cb = &cmUVReadOnlyProcess::UVExit; + UVOptions_.file = CommandPtr_[0]; + UVOptions_.args = const_cast<char**>(CommandPtr_.data()); + UVOptions_.cwd = Setup_.WorkingDirectory.c_str(); + UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE; + UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size()); + UVOptions_.stdio = UVOptionsStdIO_.data(); + + // -- Spawn process + int uvErrorCode = UVProcess_.spawn(*uv_loop, UVOptions_, this); + if (uvErrorCode != 0) { + Result()->ErrorMessage = "libuv process spawn failed"; + if (const char* uvErr = uv_strerror(uvErrorCode)) { + Result()->ErrorMessage += ": "; + Result()->ErrorMessage += uvErr; + } + } + } + // -- Start reading from stdio streams + if (!Result()->error()) { + if (!UVPipeOut_.startRead( + [this](cmUVPipeBuffer::DataRange range) { + this->UVPipeOutData(range); + }, + [this](ssize_t error) { this->UVPipeOutEnd(error); })) { + Result()->ErrorMessage = "libuv start reading from stdout pipe failed"; + } + } + if (!Result()->error()) { + if (!UVPipeErr_.startRead( + [this](cmUVPipeBuffer::DataRange range) { + this->UVPipeErrData(range); + }, + [this](ssize_t error) { this->UVPipeErrEnd(error); })) { + Result()->ErrorMessage = "libuv start reading from stderr pipe failed"; + } + } + + if (!Result()->error()) { + IsStarted_ = true; + FinishedCallback_ = std::move(finishedCallback); + } else { + // Clear libuv handles and finish + UVProcess_.reset(); + UVPipeOut_.reset(); + UVPipeErr_.reset(); + CommandPtr_.clear(); + } + + return IsStarted(); +} + +void cmUVReadOnlyProcess::UVExit(uv_process_t* handle, int64_t exitStatus, + int termSignal) +{ + auto& proc = *reinterpret_cast<cmUVReadOnlyProcess*>(handle->data); + if (proc.IsStarted() && !proc.IsFinished()) { + // Set error message on demand + proc.Result()->ExitStatus = exitStatus; + proc.Result()->TermSignal = termSignal; + if (!proc.Result()->error()) { + if (termSignal != 0) { + proc.Result()->ErrorMessage = "Process was terminated by signal "; + proc.Result()->ErrorMessage += + std::to_string(proc.Result()->TermSignal); + } else if (exitStatus != 0) { + proc.Result()->ErrorMessage = "Process failed with return value "; + proc.Result()->ErrorMessage += + std::to_string(proc.Result()->ExitStatus); + } + } + + // Reset process handle + proc.UVProcess_.reset(); + // Try finish + proc.UVTryFinish(); + } +} + +void cmUVReadOnlyProcess::UVPipeOutData(cmUVPipeBuffer::DataRange data) +{ + Result()->StdOut.append(data.begin(), data.end()); +} + +void cmUVReadOnlyProcess::UVPipeOutEnd(ssize_t error) +{ + // Process pipe error + if ((error != 0) && !Result()->error()) { + Result()->ErrorMessage = + "Reading from stdout pipe failed with libuv error code "; + Result()->ErrorMessage += std::to_string(error); + } + // Try finish + UVTryFinish(); +} + +void cmUVReadOnlyProcess::UVPipeErrData(cmUVPipeBuffer::DataRange data) +{ + std::string* str = + Setup_.MergedOutput ? &Result()->StdOut : &Result()->StdErr; + str->append(data.begin(), data.end()); +} + +void cmUVReadOnlyProcess::UVPipeErrEnd(ssize_t error) +{ + // Process pipe error + if ((error != 0) && !Result()->error()) { + Result()->ErrorMessage = + "Reading from stderr pipe failed with libuv error code "; + Result()->ErrorMessage += std::to_string(error); + } + // Try finish + UVTryFinish(); +} + +void cmUVReadOnlyProcess::UVTryFinish() +{ + // There still might be data in the pipes after the process has finished. + // Therefore check if the process is finished AND all pipes are closed + // before signaling the worker thread to continue. + if ((UVProcess_.get() != nullptr) || (UVPipeOut_.uv_pipe() != nullptr) || + (UVPipeErr_.uv_pipe() != nullptr)) { + return; + } + IsFinished_ = true; + FinishedCallback_(); +} + +/** + * @brief Worker pool worker thread + */ +class cmWorkerPoolWorker +{ +public: + cmWorkerPoolWorker(uv_loop_t& uvLoop); + ~cmWorkerPoolWorker(); + + cmWorkerPoolWorker(cmWorkerPoolWorker const&) = delete; + cmWorkerPoolWorker& operator=(cmWorkerPoolWorker const&) = delete; + + /** + * Set the internal thread + */ + void SetThread(std::thread&& aThread) { Thread_ = std::move(aThread); } + + /** + * Run an external process + */ + bool RunProcess(cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command, + std::string const& workingDirectory); + +private: + // -- Libuv callbacks + static void UVProcessStart(uv_async_t* handle); + void UVProcessFinished(); + +private: + // -- Process management + struct + { + std::mutex Mutex; + cm::uv_async_ptr Request; + std::condition_variable Condition; + std::unique_ptr<cmUVReadOnlyProcess> ROP; + } Proc_; + // -- System thread + std::thread Thread_; +}; + +cmWorkerPoolWorker::cmWorkerPoolWorker(uv_loop_t& uvLoop) +{ + Proc_.Request.init(uvLoop, &cmWorkerPoolWorker::UVProcessStart, this); +} + +cmWorkerPoolWorker::~cmWorkerPoolWorker() +{ + if (Thread_.joinable()) { + Thread_.join(); + } +} + +bool cmWorkerPoolWorker::RunProcess(cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command, + std::string const& workingDirectory) +{ + if (command.empty()) { + return false; + } + // Create process instance + { + std::lock_guard<std::mutex> lock(Proc_.Mutex); + Proc_.ROP = cm::make_unique<cmUVReadOnlyProcess>(); + Proc_.ROP->setup(&result, true, command, workingDirectory); + } + // Send asynchronous process start request to libuv loop + Proc_.Request.send(); + // Wait until the process has been finished and destroyed + { + std::unique_lock<std::mutex> ulock(Proc_.Mutex); + while (Proc_.ROP) { + Proc_.Condition.wait(ulock); + } + } + return !result.error(); +} + +void cmWorkerPoolWorker::UVProcessStart(uv_async_t* handle) +{ + auto* wrk = reinterpret_cast<cmWorkerPoolWorker*>(handle->data); + bool startFailed = false; + { + auto& Proc = wrk->Proc_; + std::lock_guard<std::mutex> lock(Proc.Mutex); + if (Proc.ROP && !Proc.ROP->IsStarted()) { + startFailed = + !Proc.ROP->start(handle->loop, [wrk] { wrk->UVProcessFinished(); }); + } + } + // Clean up if starting of the process failed + if (startFailed) { + wrk->UVProcessFinished(); + } +} + +void cmWorkerPoolWorker::UVProcessFinished() +{ + { + std::lock_guard<std::mutex> lock(Proc_.Mutex); + if (Proc_.ROP && (Proc_.ROP->IsFinished() || !Proc_.ROP->IsStarted())) { + Proc_.ROP.reset(); + } + } + // Notify idling thread + Proc_.Condition.notify_one(); +} + +/** + * @brief Private worker pool internals + */ +class cmWorkerPoolInternal +{ +public: + // -- Constructors + cmWorkerPoolInternal(cmWorkerPool* pool); + ~cmWorkerPoolInternal(); + + /** + * Runs the libuv loop. + */ + bool Process(); + + /** + * Clear queue and abort threads. + */ + void Abort(); + + /** + * Push a job to the queue and notify a worker. + */ + bool PushJob(cmWorkerPool::JobHandleT&& jobHandle); + + /** + * Worker thread main loop method. + */ + void Work(unsigned int workerIndex); + + // -- Request slots + static void UVSlotBegin(uv_async_t* handle); + static void UVSlotEnd(uv_async_t* handle); + +public: + // -- UV loop +#ifdef CMAKE_UV_SIGNAL_HACK + std::unique_ptr<cmUVSignalHackRAII> UVHackRAII; +#endif + std::unique_ptr<uv_loop_t> UVLoop; + cm::uv_async_ptr UVRequestBegin; + cm::uv_async_ptr UVRequestEnd; + + // -- Thread pool and job queue + std::mutex Mutex; + bool Processing = false; + bool Aborting = false; + bool FenceProcessing = false; + unsigned int WorkersRunning = 0; + unsigned int WorkersIdle = 0; + unsigned int JobsProcessing = 0; + std::deque<cmWorkerPool::JobHandleT> Queue; + std::condition_variable Condition; + std::vector<std::unique_ptr<cmWorkerPoolWorker>> Workers; + + // -- References + cmWorkerPool* Pool = nullptr; +}; + +void cmWorkerPool::ProcessResultT::reset() +{ + ExitStatus = 0; + TermSignal = 0; + if (!StdOut.empty()) { + StdOut.clear(); + StdOut.shrink_to_fit(); + } + if (!StdErr.empty()) { + StdErr.clear(); + StdErr.shrink_to_fit(); + } + if (!ErrorMessage.empty()) { + ErrorMessage.clear(); + ErrorMessage.shrink_to_fit(); + } +} + +cmWorkerPoolInternal::cmWorkerPoolInternal(cmWorkerPool* pool) + : Pool(pool) +{ + // Initialize libuv loop + uv_disable_stdio_inheritance(); +#ifdef CMAKE_UV_SIGNAL_HACK + UVHackRAII = cm::make_unique<cmUVSignalHackRAII>(); +#endif + UVLoop = cm::make_unique<uv_loop_t>(); + uv_loop_init(UVLoop.get()); +} + +cmWorkerPoolInternal::~cmWorkerPoolInternal() +{ + uv_loop_close(UVLoop.get()); +} + +bool cmWorkerPoolInternal::Process() +{ + // Reset state flags + Processing = true; + Aborting = false; + // Initialize libuv asynchronous request + UVRequestBegin.init(*UVLoop, &cmWorkerPoolInternal::UVSlotBegin, this); + UVRequestEnd.init(*UVLoop, &cmWorkerPoolInternal::UVSlotEnd, this); + // Send begin request + UVRequestBegin.send(); + // Run libuv loop + bool success = (uv_run(UVLoop.get(), UV_RUN_DEFAULT) == 0); + // Update state flags + Processing = false; + Aborting = false; + return success; +} + +void cmWorkerPoolInternal::Abort() +{ + bool notifyThreads = false; + // Clear all jobs and set abort flag + { + std::lock_guard<std::mutex> guard(Mutex); + if (Processing && !Aborting) { + // Register abort and clear queue + Aborting = true; + Queue.clear(); + notifyThreads = true; + } + } + if (notifyThreads) { + // Wake threads + Condition.notify_all(); + } +} + +inline bool cmWorkerPoolInternal::PushJob(cmWorkerPool::JobHandleT&& jobHandle) +{ + std::lock_guard<std::mutex> guard(Mutex); + if (Aborting) { + return false; + } + // Append the job to the queue + Queue.emplace_back(std::move(jobHandle)); + // Notify an idle worker if there's one + if (WorkersIdle != 0) { + Condition.notify_one(); + } + // Return success + return true; +} + +void cmWorkerPoolInternal::UVSlotBegin(uv_async_t* handle) +{ + auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data); + // Create worker threads + { + unsigned int const num = gint.Pool->ThreadCount(); + // Create workers + gint.Workers.reserve(num); + for (unsigned int ii = 0; ii != num; ++ii) { + gint.Workers.emplace_back( + cm::make_unique<cmWorkerPoolWorker>(*gint.UVLoop)); + } + // Start worker threads + for (unsigned int ii = 0; ii != num; ++ii) { + gint.Workers[ii]->SetThread( + std::thread(&cmWorkerPoolInternal::Work, &gint, ii)); + } + } + // Destroy begin request + gint.UVRequestBegin.reset(); +} + +void cmWorkerPoolInternal::UVSlotEnd(uv_async_t* handle) +{ + auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data); + // Join and destroy worker threads + gint.Workers.clear(); + // Destroy end request + gint.UVRequestEnd.reset(); +} + +void cmWorkerPoolInternal::Work(unsigned int workerIndex) +{ + cmWorkerPool::JobHandleT jobHandle; + std::unique_lock<std::mutex> uLock(Mutex); + // Increment running workers count + ++WorkersRunning; + // Enter worker main loop + while (true) { + // Abort on request + if (Aborting) { + break; + } + // Wait for new jobs + if (Queue.empty()) { + ++WorkersIdle; + Condition.wait(uLock); + --WorkersIdle; + continue; + } + + // Check for fence jobs + if (FenceProcessing || Queue.front()->IsFence()) { + if (JobsProcessing != 0) { + Condition.wait(uLock); + continue; + } + // No jobs get processed. Set the fence job processing flag. + FenceProcessing = true; + } + + // Pop next job from queue + jobHandle = std::move(Queue.front()); + Queue.pop_front(); + + // Unlocked scope for job processing + ++JobsProcessing; + { + uLock.unlock(); + jobHandle->Work(Pool, workerIndex); // Process job + jobHandle.reset(); // Destroy job + uLock.lock(); + } + --JobsProcessing; + + // Was this a fence job? + if (FenceProcessing) { + FenceProcessing = false; + Condition.notify_all(); + } + } + + // Decrement running workers count + if (--WorkersRunning == 0) { + // Last worker thread about to finish. Send libuv event. + UVRequestEnd.send(); + } +} + +cmWorkerPool::JobT::~JobT() = default; + +bool cmWorkerPool::JobT::RunProcess(ProcessResultT& result, + std::vector<std::string> const& command, + std::string const& workingDirectory) +{ + // Get worker by index + auto* wrk = Pool_->Int_->Workers.at(WorkerIndex_).get(); + return wrk->RunProcess(result, command, workingDirectory); +} + +cmWorkerPool::cmWorkerPool() + : Int_(cm::make_unique<cmWorkerPoolInternal>(this)) +{ +} + +cmWorkerPool::~cmWorkerPool() = default; + +void cmWorkerPool::SetThreadCount(unsigned int threadCount) +{ + if (!Int_->Processing) { + ThreadCount_ = (threadCount > 0) ? threadCount : 1u; + } +} + +bool cmWorkerPool::Process(void* userData) +{ + // Setup user data + UserData_ = userData; + // Run libuv loop + bool success = Int_->Process(); + // Clear user data + UserData_ = nullptr; + // Return + return success; +} + +bool cmWorkerPool::PushJob(JobHandleT&& jobHandle) +{ + return Int_->PushJob(std::move(jobHandle)); +} + +void cmWorkerPool::Abort() +{ + Int_->Abort(); +} diff --git a/Source/cmWorkerPool.h b/Source/cmWorkerPool.h new file mode 100644 index 0000000..f08bb4f --- /dev/null +++ b/Source/cmWorkerPool.h @@ -0,0 +1,226 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmWorkerPool_h +#define cmWorkerPool_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmAlgorithms.h" // IWYU pragma: keep + +#include <memory> // IWYU pragma: keep +#include <stdint.h> +#include <string> +#include <utility> +#include <vector> + +// -- Types +class cmWorkerPoolInternal; + +/** @class cmWorkerPool + * @brief Thread pool with job queue + */ +class cmWorkerPool +{ +public: + /** + * Return value and output of an external process. + */ + struct ProcessResultT + { + void reset(); + bool error() const + { + return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty(); + } + + std::int64_t ExitStatus = 0; + int TermSignal = 0; + std::string StdOut; + std::string StdErr; + std::string ErrorMessage; + }; + + /** + * Abstract job class for concurrent job processing. + */ + class JobT + { + public: + JobT(JobT const&) = delete; + JobT& operator=(JobT const&) = delete; + + /** + * Virtual destructor. + */ + virtual ~JobT(); + + /** + * Fence job flag + * + * Fence jobs require that: + * - all jobs before in the queue have been processed + * - no jobs later in the queue will be processed before this job was + * processed + */ + bool IsFence() const { return Fence_; } + + protected: + /** + * Protected default constructor + */ + JobT(bool fence = false) + : Fence_(fence) + { + } + + /** + * Abstract processing interface that must be implement in derived classes. + */ + virtual void Process() = 0; + + /** + * Get the worker pool. + * Only valid during the JobT::Process() call! + */ + cmWorkerPool* Pool() const { return Pool_; } + + /** + * Get the user data. + * Only valid during the JobT::Process() call! + */ + void* UserData() const { return Pool_->UserData(); }; + + /** + * Get the worker index. + * This is the index of the thread processing this job and is in the range + * [0..ThreadCount). + * Concurrently processing jobs will never have the same WorkerIndex(). + * Only valid during the JobT::Process() call! + */ + unsigned int WorkerIndex() const { return WorkerIndex_; } + + /** + * Run an external read only process. + * Use only during JobT::Process() call! + */ + bool RunProcess(ProcessResultT& result, + std::vector<std::string> const& command, + std::string const& workingDirectory); + + private: + //! Needs access to Work() + friend class cmWorkerPoolInternal; + //! Worker thread entry method. + void Work(cmWorkerPool* pool, unsigned int workerIndex) + { + Pool_ = pool; + WorkerIndex_ = workerIndex; + this->Process(); + } + + private: + cmWorkerPool* Pool_ = nullptr; + unsigned int WorkerIndex_ = 0; + bool Fence_ = false; + }; + + /** + * Job handle type + */ + typedef std::unique_ptr<JobT> JobHandleT; + + /** + * Fence job base class + */ + class JobFenceT : public JobT + { + public: + JobFenceT() + : JobT(true) + { + } + //! Does nothing + void Process() override{}; + }; + + /** + * Fence job that aborts the worker pool. + * + * Useful as the last job in the job queue. + */ + class JobEndT : JobFenceT + { + public: + //! Does nothing + void Process() override { Pool()->Abort(); } + }; + +public: + // -- Methods + cmWorkerPool(); + ~cmWorkerPool(); + + /** + * Number of worker threads. + */ + unsigned int ThreadCount() const { return ThreadCount_; } + + /** + * Set the number of worker threads. + * + * Calling this method during Process() has no effect. + */ + void SetThreadCount(unsigned int threadCount); + + /** + * Blocking function that starts threads to process all Jobs in the queue. + * + * This method blocks until a job calls the Abort() method. + * @arg threadCount Number of threads to process jobs. + * @arg userData Common user data pointer available in all Jobs. + */ + bool Process(void* userData = nullptr); + + /** + * User data reference passed to Process(). + * + * Only valid during Process(). + */ + void* UserData() const { return UserData_; } + + // -- Job processing interface + + /** + * Clears the job queue and aborts all worker threads. + * + * This method is thread safe and can be called from inside a job. + */ + void Abort(); + + /** + * Push job to the queue. + * + * This method is thread safe and can be called from inside a job or before + * Process(). + */ + bool PushJob(JobHandleT&& jobHandle); + + /** + * Push job to the queue + * + * This method is thread safe and can be called from inside a job or before + * Process(). + */ + template <class T, typename... Args> + bool EmplaceJob(Args&&... args) + { + return PushJob(cm::make_unique<T>(std::forward<Args>(args)...)); + } + +private: + void* UserData_ = nullptr; + unsigned int ThreadCount_ = 1; + std::unique_ptr<cmWorkerPoolInternal> Int_; +}; + +#endif diff --git a/Source/cmWorkingDirectory.h b/Source/cmWorkingDirectory.h index 1f18ce7..d4a164d 100644 --- a/Source/cmWorkingDirectory.h +++ b/Source/cmWorkingDirectory.h @@ -22,6 +22,9 @@ public: cmWorkingDirectory(std::string const& newdir); ~cmWorkingDirectory(); + cmWorkingDirectory(const cmWorkingDirectory&) = delete; + cmWorkingDirectory& operator=(const cmWorkingDirectory&) = delete; + bool SetDirectory(std::string const& newdir); void Pop(); bool Failed() const { return ResultCode != 0; } diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx index 1b16198..c33bb7e 100644 --- a/Source/cmXCodeScheme.cxx +++ b/Source/cmXCodeScheme.cxx @@ -177,6 +177,11 @@ void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout, WriteLaunchActionAttribute(xout, "stopOnEveryMainThreadCheckerIssue", "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP"); + if (this->Target->GetTarget()->GetPropertyAsBool( + "XCODE_SCHEME_DEBUG_AS_ROOT")) { + xout.Attribute("debugAsWhichUser", "root"); + } + // Diagnostics tab end if (IsExecutable(this->Target)) { diff --git a/Source/cmXMLWriter.cxx b/Source/cmXMLWriter.cxx index 9d2a3c4..f1ce608 100644 --- a/Source/cmXMLWriter.cxx +++ b/Source/cmXMLWriter.cxx @@ -23,7 +23,7 @@ cmXMLWriter::~cmXMLWriter() void cmXMLWriter::StartDocument(const char* encoding) { - this->Output << "<?xml version=\"1.0\" encoding=\"" << encoding << "\"?>"; + this->Output << R"(<?xml version="1.0" encoding=")" << encoding << "\"?>"; } void cmXMLWriter::EndDocument() diff --git a/Source/cmXMLWriter.h b/Source/cmXMLWriter.h index 1df8a09..512e103 100644 --- a/Source/cmXMLWriter.h +++ b/Source/cmXMLWriter.h @@ -146,6 +146,8 @@ public: xmlwr.StartDocument(); } ~cmXMLDocument() { xmlwr.EndDocument(); } + cmXMLDocument(const cmXMLDocument&) = delete; + cmXMLDocument& operator=(const cmXMLDocument&) = delete; private: friend class cmXMLElement; @@ -172,6 +174,9 @@ public: } ~cmXMLElement() { xmlwr.EndElement(); } + cmXMLElement(const cmXMLElement&) = delete; + cmXMLElement& operator=(const cmXMLElement&) = delete; + template <typename T> cmXMLElement& Attribute(const char* name, T const& value) { diff --git a/Source/cm_utf8.c b/Source/cm_utf8.c index 52af4a6..62e7e8c 100644 --- a/Source/cm_utf8.c +++ b/Source/cm_utf8.c @@ -2,6 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cm_utf8.h" +#include <string.h> + /* RFC 3629 07-bit: 0xxxxxxx @@ -71,7 +73,34 @@ const char* cm_utf8_decode_character(const char* first, const char* last, return 0; } + /* UTF-16 surrogate halves. */ + if (0xD800 <= uc && uc <= 0xDFFF) { + return 0; + } + + /* Invalid codepoints. */ + if (0x10FFFF < uc) { + return 0; + } + *pc = uc; return first; } } + +int cm_utf8_is_valid(const char* s) +{ + if (!s) { + return 0; + } + + const char* last = s + strlen(s); + const char* pos = s; + unsigned int pc; + + while (pos != last && (pos = cm_utf8_decode_character(pos, last, &pc))) { + /* Nothing to do. */ + } + + return pos == last; +} diff --git a/Source/cm_utf8.h b/Source/cm_utf8.h index fcb43e0..27dc559 100644 --- a/Source/cm_utf8.h +++ b/Source/cm_utf8.h @@ -13,6 +13,10 @@ extern "C" { const char* cm_utf8_decode_character(const char* first, const char* last, unsigned int* pc); +/** Returns whether a C string is a sequence of valid UTF-8 encoded Unicode + codepoints. Returns non-zero on success. */ +int cm_utf8_is_valid(const char* s); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 8023298..121d12d 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -9,7 +9,7 @@ #include "cmDocumentationFormatter.h" #include "cmDuration.h" #include "cmExternalMakefileProjectGenerator.h" -#include "cmFileTimeComparison.h" +#include "cmFileTimeCache.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmGlobalGeneratorFactory.h" @@ -48,7 +48,6 @@ #if defined(_WIN32) && !defined(__CYGWIN__) # if !defined(CMAKE_BOOT_MINGW) # include "cmGlobalBorlandMakefileGenerator.h" -# include "cmGlobalGhsMultiGenerator.h" # include "cmGlobalJOMMakefileGenerator.h" # include "cmGlobalNMakeMakefileGenerator.h" # include "cmGlobalVisualStudio10Generator.h" @@ -84,6 +83,10 @@ # include "cmExtraEclipseCDT4Generator.h" #endif +#if defined(__linux__) || defined(_WIN32) +# include "cmGlobalGhsMultiGenerator.h" +#endif + #if defined(__APPLE__) # if defined(CMAKE_BUILD_WITH_CMAKE) # include "cmGlobalXCodeGenerator.h" @@ -99,6 +102,7 @@ #include "cmsys/RegularExpression.hxx" #include <algorithm> #include <cstring> +#include <initializer_list> #include <iostream> #include <iterator> #include <memory> // IWYU pragma: keep @@ -115,10 +119,8 @@ typedef std::unordered_map<std::string, Json::Value> JsonValueMapType; } // namespace -static bool cmakeCheckStampFile(const std::string& stampName, - bool verbose = true); -static bool cmakeCheckStampList(const std::string& stampList, - bool verbose = true); +static bool cmakeCheckStampFile(const std::string& stampName); +static bool cmakeCheckStampList(const std::string& stampList); void cmWarnUnusedCliWarning(const std::string& variable, int /*unused*/, void* ctx, const char* /*unused*/, @@ -139,7 +141,7 @@ cmake::cmake(Role role, cmState::Mode mode) this->DebugOutput = false; this->DebugTryCompile = false; this->ClearBuildSystem = false; - this->FileComparison = new cmFileTimeComparison; + this->FileTimeCache = new cmFileTimeCache; this->State = new cmState; this->State->SetMode(mode); @@ -223,7 +225,7 @@ cmake::~cmake() #ifdef CMAKE_BUILD_WITH_CMAKE delete this->VariableWatch; #endif - delete this->FileComparison; + delete this->FileTimeCache; } #if defined(CMAKE_BUILD_WITH_CMAKE) @@ -708,6 +710,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) this->GraphVizFile = path; if (this->GraphVizFile.empty()) { cmSystemTools::Error("No file specified for --graphviz"); + return; } } else if (arg.find("--debug-trycompile", 0) == 0) { std::cout << "debug trycompile on\n"; @@ -715,6 +718,14 @@ void cmake::SetArgs(const std::vector<std::string>& args) } else if (arg.find("--debug-output", 0) == 0) { std::cout << "Running with debug output on.\n"; this->SetDebugOutputOn(true); + } else if (arg.find("--loglevel=", 0) == 0) { + const auto logLevel = + StringToLogLevel(arg.substr(sizeof("--loglevel=") - 1)); + if (logLevel == LogLevel::LOG_UNDEFINED) { + cmSystemTools::Error("Invalid level specified for --loglevel"); + return; + } + this->SetLogLevel(logLevel); } else if (arg.find("--trace-expand", 0) == 0) { std::cout << "Running with expanded trace output on.\n"; this->SetTrace(true); @@ -787,17 +798,17 @@ void cmake::SetArgs(const std::vector<std::string>& args) } cmGlobalGenerator* gen = this->CreateGlobalGenerator(value); if (!gen) { - const char* kdevError = nullptr; + std::string kdevError; if (value.find("KDevelop3", 0) != std::string::npos) { kdevError = "\nThe KDevelop3 generator is not supported anymore."; } - cmSystemTools::Error("Could not create named generator ", - value.c_str(), kdevError); + cmSystemTools::Error("Could not create named generator " + value + + kdevError); this->PrintGeneratorList(); - } else { - this->SetGlobalGenerator(gen); + return; } + this->SetGlobalGenerator(gen); } // no option assume it is the path to the source or an existing build else { @@ -825,6 +836,25 @@ void cmake::SetArgs(const std::vector<std::string>& args) } } +cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr) +{ + using LevelsPair = std::pair<std::string, LogLevel>; + static const std::vector<LevelsPair> levels = { + { "error", LogLevel::LOG_ERROR }, { "warning", LogLevel::LOG_WARNING }, + { "notice", LogLevel::LOG_NOTICE }, { "status", LogLevel::LOG_STATUS }, + { "verbose", LogLevel::LOG_VERBOSE }, { "debug", LogLevel::LOG_DEBUG }, + { "trace", LogLevel::LOG_TRACE } + }; + + const auto levelStrLowCase = cmSystemTools::LowerCase(levelStr); + + const auto it = std::find_if(levels.cbegin(), levels.cend(), + [&levelStrLowCase](const LevelsPair& p) { + return p.first == levelStrLowCase; + }); + return (it != levels.cend()) ? it->second : LogLevel::LOG_UNDEFINED; +} + void cmake::SetDirectoriesFromFile(const char* arg) { // Check if the argument refers to a CMakeCache.txt or @@ -937,8 +967,8 @@ int cmake::AddCMakePaths() cmSystemTools::Error( "Could not find CMAKE_ROOT !!!\n" "CMake has most likely not been installed correctly.\n" - "Modules directory not found in\n", - cmSystemTools::GetCMakeRoot().c_str()); + "Modules directory not found in\n" + + cmSystemTools::GetCMakeRoot()); return 0; } this->AddCacheEntry("CMAKE_ROOT", cmSystemTools::GetCMakeRoot().c_str(), @@ -1105,7 +1135,7 @@ std::string cmake::FindCacheFile(const std::string& binaryDir) if (cmSystemTools::FileExists(cmakeFiles)) { std::string cachePathFound = cmSystemTools::FileExistsInParentDirectories("CMakeCache.txt", - cachePath.c_str(), "/"); + cachePath, "/"); if (!cachePathFound.empty()) { cachePath = cmSystemTools::GetFilenamePath(cachePathFound); } @@ -1705,7 +1735,7 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure) ret = this->Generate(); std::string message = "Build files have been written to: "; message += this->GetHomeOutputDirectory(); - this->UpdateProgress(message.c_str(), -1); + this->UpdateProgress(message, -1); return ret; } @@ -1834,13 +1864,15 @@ void cmake::AddDefaultGenerators() this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalJOMMakefileGenerator::NewFactory()); - this->Generators.push_back(cmGlobalGhsMultiGenerator::NewFactory()); # endif this->Generators.push_back(cmGlobalMSYSMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalMinGWMakefileGenerator::NewFactory()); #endif this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory()); #if defined(CMAKE_BUILD_WITH_CMAKE) +# if defined(__linux__) || defined(_WIN32) + this->Generators.push_back(cmGlobalGhsMultiGenerator::NewFactory()); +# endif this->Generators.push_back(cmGlobalNinjaGenerator::NewFactory()); #endif #if defined(CMAKE_USE_WMAKE) @@ -1892,11 +1924,10 @@ bool cmake::LoadCache(const std::string& path, bool internal, std::set<std::string>& includes) { bool result = this->State->LoadCache(path, internal, excludes, includes); - static const char* entries[] = { "CMAKE_CACHE_MAJOR_VERSION", - "CMAKE_CACHE_MINOR_VERSION" }; - for (const char* const* nameIt = cm::cbegin(entries); - nameIt != cm::cend(entries); ++nameIt) { - this->UnwatchUnusedCli(*nameIt); + static const auto entries = { "CMAKE_CACHE_MAJOR_VERSION", + "CMAKE_CACHE_MINOR_VERSION" }; + for (auto const& entry : entries) { + this->UnwatchUnusedCli(entry); } return result; } @@ -1904,13 +1935,12 @@ bool cmake::LoadCache(const std::string& path, bool internal, bool cmake::SaveCache(const std::string& path) { bool result = this->State->SaveCache(path, this->GetMessenger()); - static const char* entries[] = { "CMAKE_CACHE_MAJOR_VERSION", - "CMAKE_CACHE_MINOR_VERSION", - "CMAKE_CACHE_PATCH_VERSION", - "CMAKE_CACHEFILE_DIR" }; - for (const char* const* nameIt = cm::cbegin(entries); - nameIt != cm::cend(entries); ++nameIt) { - this->UnwatchUnusedCli(*nameIt); + static const auto entries = { "CMAKE_CACHE_MAJOR_VERSION", + "CMAKE_CACHE_MINOR_VERSION", + "CMAKE_CACHE_PATCH_VERSION", + "CMAKE_CACHEFILE_DIR" }; + for (auto const& entry : entries) { + this->UnwatchUnusedCli(entry); } return result; } @@ -1925,7 +1955,7 @@ void cmake::SetProgressCallback(ProgressCallbackType f) this->ProgressCallback = std::move(f); } -void cmake::UpdateProgress(const char* msg, float prog) +void cmake::UpdateProgress(const std::string& msg, float prog) { if (this->ProgressCallback && !this->State->GetIsInTryCompile()) { this->ProgressCallback(msg, prog); @@ -2017,8 +2047,8 @@ void cmake::UpdateConversionPathTable() if (tablepath) { cmsys::ifstream table(tablepath->c_str()); if (!table) { - cmSystemTools::Error("CMAKE_PATH_TRANSLATION_FILE set to ", - tablepath->c_str(), ". CMake can not open file."); + cmSystemTools::Error("CMAKE_PATH_TRANSLATION_FILE set to " + *tablepath + + ". CMake can not open file."); cmSystemTools::ReportLastSystemError("CMake can not open file."); } else { std::string a, b; @@ -2142,7 +2172,7 @@ int cmake::CheckBuildSystem() std::string dep_newest = *dep++; for (; dep != depends.end(); ++dep) { int result = 0; - if (this->FileComparison->FileTimeCompare(dep_newest, *dep, &result)) { + if (this->FileTimeCache->Compare(dep_newest, *dep, &result)) { if (result < 0) { dep_newest = *dep; } @@ -2161,7 +2191,7 @@ int cmake::CheckBuildSystem() std::string out_oldest = *out++; for (; out != outputs.end(); ++out) { int result = 0; - if (this->FileComparison->FileTimeCompare(out_oldest, *out, &result)) { + if (this->FileTimeCache->Compare(out_oldest, *out, &result)) { if (result > 0) { out_oldest = *out; } @@ -2178,8 +2208,7 @@ int cmake::CheckBuildSystem() // If any output is older than any dependency then rerun. { int result = 0; - if (!this->FileComparison->FileTimeCompare(out_oldest, dep_newest, - &result) || + if (!this->FileTimeCache->Compare(out_oldest, dep_newest, &result) || result < 0) { if (verbose) { std::ostringstream msg; @@ -2326,8 +2355,7 @@ int cmake::GetSystemInformation(std::vector<std::string>& args) } cmGlobalGenerator* gen = this->CreateGlobalGenerator(value); if (!gen) { - cmSystemTools::Error("Could not create named generator ", - value.c_str()); + cmSystemTools::Error("Could not create named generator " + value); this->PrintGeneratorList(); } else { this->SetGlobalGenerator(gen); @@ -2354,7 +2382,7 @@ int cmake::GetSystemInformation(std::vector<std::string>& args) outFile += "/CMakeLists.txt"; // Copy file - if (!cmSystemTools::cmCopyFile(inFile, outFile)) { + if (!cmsys::SystemTools::CopyFileAlways(inFile, outFile)) { std::cerr << "Error copying file \"" << inFile << "\" to \"" << outFile << "\".\n"; return 1; @@ -2412,7 +2440,7 @@ int cmake::GetSystemInformation(std::vector<std::string>& args) return 0; } -static bool cmakeCheckStampFile(const std::string& stampName, bool verbose) +static bool cmakeCheckStampFile(const std::string& stampName) { // The stamp file does not exist. Use the stamp dependencies to // determine whether it is really out of date. This works in @@ -2435,20 +2463,22 @@ static bool cmakeCheckStampFile(const std::string& stampName, bool verbose) } // Compare the stamp dependencies against the dependency file itself. - cmFileTimeComparison ftc; - std::string dep; - while (cmSystemTools::GetLineFromStream(fin, dep)) { - int result; - if (!dep.empty() && dep[0] != '#' && - (!ftc.FileTimeCompare(stampDepends, dep, &result) || result < 0)) { - // The stamp depends file is older than this dependency. The - // build system is really out of date. - std::cout << "CMake is re-running because " << stampName - << " is out-of-date.\n"; - std::cout << " the file '" << dep << "'\n"; - std::cout << " is newer than '" << stampDepends << "'\n"; - std::cout << " result='" << result << "'\n"; - return false; + { + cmFileTimeCache ftc; + std::string dep; + while (cmSystemTools::GetLineFromStream(fin, dep)) { + int result; + if (!dep.empty() && dep[0] != '#' && + (!ftc.Compare(stampDepends, dep, &result) || result < 0)) { + // The stamp depends file is older than this dependency. The + // build system is really out of date. + std::cout << "CMake is re-running because " << stampName + << " is out-of-date.\n"; + std::cout << " the file '" << dep << "'\n"; + std::cout << " is newer than '" << stampDepends << "'\n"; + std::cout << " result='" << result << "'\n"; + return false; + } } } @@ -2464,21 +2494,15 @@ static bool cmakeCheckStampFile(const std::string& stampName, bool verbose) stamp << "# CMake generation timestamp file for this directory.\n"; } if (cmSystemTools::RenameFile(stampTemp, stampName)) { - if (verbose) { - // Notify the user why CMake is not re-running. It is safe to - // just print to stdout here because this code is only reachable - // through an undocumented flag used by the VS generator. - std::cout << "CMake does not need to re-run because " << stampName - << " is up-to-date.\n"; - } + // CMake does not need to re-run because the stamp file is up-to-date. return true; } cmSystemTools::RemoveFile(stampTemp); - cmSystemTools::Error("Cannot restore timestamp ", stampName.c_str()); + cmSystemTools::Error("Cannot restore timestamp " + stampName); return false; } -static bool cmakeCheckStampList(const std::string& stampList, bool verbose) +static bool cmakeCheckStampList(const std::string& stampList) { // If the stamp list does not exist CMake must rerun to generate it. if (!cmSystemTools::FileExists(stampList)) { @@ -2496,7 +2520,7 @@ static bool cmakeCheckStampList(const std::string& stampList, bool verbose) // Check each stamp. std::string stampName; while (cmSystemTools::GetLineFromStream(fin, stampName)) { - if (!cmakeCheckStampFile(stampName, verbose)) { + if (!cmakeCheckStampFile(stampName)) { return false; } } @@ -2531,7 +2555,8 @@ cmMessenger* cmake::GetMessenger() const return this->Messenger; } -int cmake::Build(int jobs, const std::string& dir, const std::string& target, +int cmake::Build(int jobs, const std::string& dir, + const std::vector<std::string>& targets, const std::string& config, const std::vector<std::string>& nativeOptions, bool clean, bool verbose) @@ -2617,7 +2642,7 @@ int cmake::Build(int jobs, const std::string& dir, const std::string& target, } } - if (!cmakeCheckStampList(stampList, false)) { + if (!cmakeCheckStampList(stampList)) { // Correctly initialize the home (=source) and home output (=binary) // directories, which is required for running the generation step. std::string homeOrig = this->GetHomeDirectory(); @@ -2640,7 +2665,7 @@ int cmake::Build(int jobs, const std::string& dir, const std::string& target, } std::string message = "Build files have been written to: "; message += this->GetHomeOutputDirectory(); - this->UpdateProgress(message.c_str(), -1); + this->UpdateProgress(message, -1); // Restore the previously set directories to their original value. this->SetHomeDirectory(homeOrig); @@ -2650,9 +2675,8 @@ int cmake::Build(int jobs, const std::string& dir, const std::string& target, #endif gen->PrintBuildCommandAdvice(std::cerr, jobs); - - return gen->Build(jobs, "", dir, projName, target, output, "", config, clean, - false, verbose, cmDuration::zero(), + return gen->Build(jobs, "", dir, projName, targets, output, "", config, + clean, false, verbose, cmDuration::zero(), cmSystemTools::OUTPUT_PASSTHROUGH, nativeOptions); } diff --git a/Source/cmake.h b/Source/cmake.h index 53d44f1..4a345cf 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -26,7 +26,7 @@ class cmExternalMakefileProjectGeneratorFactory; class cmFileAPI; -class cmFileTimeComparison; +class cmFileTimeCache; class cmGlobalGenerator; class cmGlobalGeneratorFactory; class cmMakefile; @@ -96,6 +96,19 @@ public: FIND_PACKAGE_MODE }; + /** \brief Define log level constants. */ + enum LogLevel + { + LOG_UNDEFINED, + LOG_ERROR, + LOG_WARNING, + LOG_NOTICE, + LOG_STATUS, + LOG_VERBOSE, + LOG_DEBUG, + LOG_TRACE + }; + struct GeneratorInfo { std::string name; @@ -162,7 +175,7 @@ public: int Configure(); int ActualConfigure(); - ///! Break up a line like VAR:type="value" into var, type and value + //! Break up a line like VAR:type="value" into var, type and value static bool ParseCacheEntry(const std::string& entry, std::string& var, std::string& value, cmStateEnums::CacheEntryType& type); @@ -176,40 +189,40 @@ public: bool DeleteCache(const std::string& path); void PreLoadCMakeFiles(); - ///! Create a GlobalGenerator + //! Create a GlobalGenerator cmGlobalGenerator* CreateGlobalGenerator(const std::string& name); - ///! Return the global generator assigned to this instance of cmake + //! Return the global generator assigned to this instance of cmake cmGlobalGenerator* GetGlobalGenerator() { return this->GlobalGenerator; } - ///! Return the global generator assigned to this instance of cmake, const + //! Return the global generator assigned to this instance of cmake, const const cmGlobalGenerator* GetGlobalGenerator() const { return this->GlobalGenerator; } - ///! Return the full path to where the CMakeCache.txt file should be. + //! Return the full path to where the CMakeCache.txt file should be. static std::string FindCacheFile(const std::string& binaryDir); - ///! Return the global generator assigned to this instance of cmake + //! Return the global generator assigned to this instance of cmake void SetGlobalGenerator(cmGlobalGenerator*); - ///! Get the names of the current registered generators + //! Get the names of the current registered generators void GetRegisteredGenerators(std::vector<GeneratorInfo>& generators, bool includeNamesWithPlatform = true) const; - ///! Set the name of the selected generator-specific instance. + //! Set the name of the selected generator-specific instance. void SetGeneratorInstance(std::string const& instance) { this->GeneratorInstance = instance; } - ///! Set the name of the selected generator-specific platform. + //! Set the name of the selected generator-specific platform. void SetGeneratorPlatform(std::string const& ts) { this->GeneratorPlatform = ts; } - ///! Set the name of the selected generator-specific toolset. + //! Set the name of the selected generator-specific toolset. void SetGeneratorToolset(std::string const& ts) { this->GeneratorToolset = ts; @@ -244,7 +257,7 @@ public: * Given a variable name, return its value (as a string). */ const char* GetCacheDefinition(const std::string&) const; - ///! Add an entry into the cache + //! Add an entry into the cache void AddCacheEntry(const std::string& key, const char* value, const char* helpString, int type); @@ -263,17 +276,17 @@ public: */ int GetSystemInformation(std::vector<std::string>&); - ///! Parse command line arguments + //! Parse command line arguments void SetArgs(const std::vector<std::string>& args); - ///! Is this cmake running as a result of a TRY_COMPILE command + //! Is this cmake running as a result of a TRY_COMPILE command bool GetIsInTryCompile() const; void SetIsInTryCompile(bool b); - ///! Parse command line arguments that might set cache values + //! Parse command line arguments that might set cache values bool SetCacheArgs(const std::vector<std::string>&); - using ProgressCallbackType = std::function<void(const char*, float)>; + using ProgressCallbackType = std::function<void(const std::string&, float)>; /** * Set the function used by GUIs to receive progress updates * Function gets passed: message as a const char*, a progress @@ -283,24 +296,24 @@ public: */ void SetProgressCallback(ProgressCallbackType f); - ///! this is called by generators to update the progress - void UpdateProgress(const char* msg, float prog); + //! this is called by generators to update the progress + void UpdateProgress(const std::string& msg, float prog); #if defined(CMAKE_BUILD_WITH_CMAKE) - ///! Get the variable watch object + //! Get the variable watch object cmVariableWatch* GetVariableWatch() { return this->VariableWatch; } #endif std::vector<cmDocumentationEntry> GetGeneratorsDocumentation(); - ///! Set/Get a property of this target file + //! Set/Get a property of this target file void SetProperty(const std::string& prop, const char* value); void AppendProperty(const std::string& prop, const char* value, bool asString = false); const char* GetProperty(const std::string& prop); bool GetPropertyAsBool(const std::string& prop); - ///! Get or create an cmInstalledFile instance and return a pointer to it + //! Get or create an cmInstalledFile instance and return a pointer to it cmInstalledFile* GetOrCreateInstalledFile(cmMakefile* mf, const std::string& name); @@ -311,13 +324,13 @@ public: return this->InstalledFiles; } - ///! Do all the checks before running configure + //! Do all the checks before running configure int DoPreConfigureChecks(); void SetWorkingMode(WorkingMode mode) { this->CurrentWorkingMode = mode; } WorkingMode GetWorkingMode() { return this->CurrentWorkingMode; } - ///! Debug the try compile stuff by not deleting the files + //! Debug the try compile stuff by not deleting the files bool GetDebugTryCompile() { return this->DebugTryCompile; } void DebugTryCompileOn() { this->DebugTryCompile = true; } @@ -329,7 +342,12 @@ public: /** * Get the file comparison class */ - cmFileTimeComparison* GetFileComparison() { return this->FileComparison; } + cmFileTimeCache* GetFileTimeCache() { return this->FileTimeCache; } + + // Get the selected log level for `message()` commands during the cmake run. + LogLevel GetLogLevel() const { return this->MessageLogLevel; } + void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; } + static LogLevel StringToLogLevel(const std::string& levelStr); // Do we want debug output during the cmake run. bool GetDebugOutput() { return this->DebugOutput; } @@ -423,13 +441,13 @@ public: MessageType t, std::string const& text, cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const; - ///! run the --build option - int Build(int jobs, const std::string& dir, const std::string& target, - const std::string& config, + //! run the --build option + int Build(int jobs, const std::string& dir, + const std::vector<std::string>& targets, const std::string& config, const std::vector<std::string>& nativeOptions, bool clean, bool verbose); - ///! run the --open option + //! run the --open option bool Open(const std::string& dir, bool dryRun); void UnwatchUnusedCli(const std::string& var); @@ -462,12 +480,12 @@ protected: std::string GeneratorPlatform; std::string GeneratorToolset; - ///! read in a cmake list file to initialize the cache + //! read in a cmake list file to initialize the cache void ReadListFile(const std::vector<std::string>& args, const std::string& path); bool FindPackage(const std::vector<std::string>& args); - ///! Check if CMAKE_CACHEFILE_DIR is set. If it is not, delete the log file. + //! Check if CMAKE_CACHEFILE_DIR is set. If it is not, delete the log file. /// If it is set, truncate it to 50kb void TruncateOutputLog(const char* fname); @@ -509,7 +527,7 @@ private: std::unordered_set<std::string> HeaderFileExtensionsSet; bool ClearBuildSystem; bool DebugTryCompile; - cmFileTimeComparison* FileComparison; + cmFileTimeCache* FileTimeCache; std::string GraphVizFile; InstalledFilesMap InstalledFiles; @@ -524,6 +542,8 @@ private: std::vector<std::string> TraceOnlyThisSources; + LogLevel MessageLogLevel = LogLevel::LOG_STATUS; + void UpdateConversionPathTable(); // Print a list of valid generators to stderr. @@ -562,40 +582,38 @@ private: "not errors." \ } +#define FOR_EACH_C90_FEATURE(F) F(c_function_prototypes) + +#define FOR_EACH_C99_FEATURE(F) \ + F(c_restrict) \ + F(c_variadic_macros) + +#define FOR_EACH_C11_FEATURE(F) F(c_static_assert) + #define FOR_EACH_C_FEATURE(F) \ F(c_std_90) \ F(c_std_99) \ F(c_std_11) \ - F(c_function_prototypes) \ - F(c_restrict) \ - F(c_static_assert) \ - F(c_variadic_macros) + FOR_EACH_C90_FEATURE(F) \ + FOR_EACH_C99_FEATURE(F) \ + FOR_EACH_C11_FEATURE(F) -#define FOR_EACH_CXX_FEATURE(F) \ - F(cxx_std_98) \ - F(cxx_std_11) \ - F(cxx_std_14) \ - F(cxx_std_17) \ - F(cxx_std_20) \ - F(cxx_aggregate_default_initializers) \ +#define FOR_EACH_CXX98_FEATURE(F) F(cxx_template_template_parameters) + +#define FOR_EACH_CXX11_FEATURE(F) \ F(cxx_alias_templates) \ F(cxx_alignas) \ F(cxx_alignof) \ F(cxx_attributes) \ - F(cxx_attribute_deprecated) \ F(cxx_auto_type) \ - F(cxx_binary_literals) \ F(cxx_constexpr) \ - F(cxx_contextual_conversions) \ F(cxx_decltype) \ - F(cxx_decltype_auto) \ F(cxx_decltype_incomplete_return_types) \ F(cxx_default_function_template_args) \ F(cxx_defaulted_functions) \ F(cxx_defaulted_move_initializers) \ F(cxx_delegating_constructors) \ F(cxx_deleted_functions) \ - F(cxx_digit_separators) \ F(cxx_enum_forward_declarations) \ F(cxx_explicit_conversions) \ F(cxx_extended_friend_declarations) \ @@ -603,11 +621,9 @@ private: F(cxx_final) \ F(cxx_func_identifier) \ F(cxx_generalized_initializers) \ - F(cxx_generic_lambdas) \ F(cxx_inheriting_constructors) \ F(cxx_inline_namespaces) \ F(cxx_lambdas) \ - F(cxx_lambda_init_captures) \ F(cxx_local_type_template_args) \ F(cxx_long_long_type) \ F(cxx_noexcept) \ @@ -617,22 +633,41 @@ private: F(cxx_range_for) \ F(cxx_raw_string_literals) \ F(cxx_reference_qualified_functions) \ - F(cxx_relaxed_constexpr) \ - F(cxx_return_type_deduction) \ F(cxx_right_angle_brackets) \ F(cxx_rvalue_references) \ F(cxx_sizeof_member) \ F(cxx_static_assert) \ F(cxx_strong_enums) \ - F(cxx_template_template_parameters) \ F(cxx_thread_local) \ F(cxx_trailing_return_types) \ F(cxx_unicode_literals) \ F(cxx_uniform_initialization) \ F(cxx_unrestricted_unions) \ F(cxx_user_literals) \ - F(cxx_variable_templates) \ F(cxx_variadic_macros) \ F(cxx_variadic_templates) +#define FOR_EACH_CXX14_FEATURE(F) \ + F(cxx_aggregate_default_initializers) \ + F(cxx_attribute_deprecated) \ + F(cxx_binary_literals) \ + F(cxx_contextual_conversions) \ + F(cxx_decltype_auto) \ + F(cxx_digit_separators) \ + F(cxx_generic_lambdas) \ + F(cxx_lambda_init_captures) \ + F(cxx_relaxed_constexpr) \ + F(cxx_return_type_deduction) \ + F(cxx_variable_templates) + +#define FOR_EACH_CXX_FEATURE(F) \ + F(cxx_std_98) \ + F(cxx_std_11) \ + F(cxx_std_14) \ + F(cxx_std_17) \ + F(cxx_std_20) \ + FOR_EACH_CXX98_FEATURE(F) \ + FOR_EACH_CXX11_FEATURE(F) \ + FOR_EACH_CXX14_FEATURE(F) + #endif diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index a83f7dc..e639c66 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -23,6 +23,8 @@ # include "cmsys/ConsoleBuf.hxx" #endif +#include <cassert> +#include <climits> #include <ctype.h> #include <iostream> #include <string.h> @@ -54,29 +56,38 @@ static const char* cmDocumentationUsageNote[][2] = { # define CMAKE_BUILD_OPTIONS \ " <dir> = Project binary directory to be built.\n" \ - " -j [<jobs>] --parallel [<jobs>] = Build in parallel using\n" \ - " the given number of jobs. If <jobs> is omitted\n" \ - " the native build tool's default number is used.\n" \ + " --parallel [<jobs>], -j [<jobs>]\n" \ + " = Build in parallel using the given number of jobs. \n" \ + " If <jobs> is omitted the native build tool's \n" \ + " default number is used.\n" \ " The CMAKE_BUILD_PARALLEL_LEVEL environment " \ "variable\n" \ " specifies a default parallel level when this " \ "option\n" \ " is not given.\n" \ - " --target <tgt> = Build <tgt> instead of default targets.\n" \ - " May only be specified once.\n" \ + " --target <tgt>..., -t <tgt>... \n" \ + " = Build <tgt> instead of default targets.\n" \ " --config <cfg> = For multi-configuration tools, choose <cfg>.\n" \ " --clean-first = Build target 'clean' first, then build.\n" \ " (To clean only, use --target 'clean'.)\n" \ - " --use-stderr = Ignored. Behavior is default in CMake >= 3.0.\n" \ - " -v --verbose = Enable verbose output - if supported - including\n" \ + " --verbose, -v = Enable verbose output - if supported - including\n" \ " the build commands to be executed. \n" \ " -- = Pass remaining options to the native tool.\n" +# define CMAKE_INSTALL_OPTIONS \ + " <dir> = Project binary directory to install.\n" \ + " --config <cfg> = For multi-configuration tools, choose <cfg>.\n" \ + " --component <comp> = Component-based install. Only install <comp>.\n" \ + " --prefix <prefix> = The installation prefix CMAKE_INSTALL_PREFIX.\n" \ + " --strip = Performing install/strip.\n" \ + " -v --verbose = Enable verbose output.\n" + static const char* cmDocumentationOptions[][2] = { CMAKE_STANDARD_OPTIONS_TABLE, { "-E", "CMake command mode." }, { "-L[A][H]", "List non-advanced cached variables." }, { "--build <dir>", "Build a CMake-generated project binary tree." }, + { "--install <dir>", "Install a CMake-generated project binary tree." }, { "--open <dir>", "Open generated project in the associated application." }, { "-N", "View mode only." }, { "-P <file>", "Process script mode." }, @@ -85,6 +96,8 @@ static const char* cmDocumentationOptions[][2] = { "Generate graphviz of dependencies, see " "CMakeGraphVizOptions.cmake for more." }, { "--system-information [file]", "Dump information about this system." }, + { "--loglevel=<error|warn|notice|status|verbose|debug|trace>", + "Set the verbosity of messages from CMake files." }, { "--debug-trycompile", "Do not delete the try_compile build tree. Only " "useful on one try_compile at a time." }, @@ -115,6 +128,7 @@ static int do_command(int ac, char const* const* av) int do_cmake(int ac, char const* const* av); static int do_build(int ac, char const* const* av); +static int do_install(int ac, char const* const* av); static int do_open(int ac, char const* const* av); static cmMakefile* cmakemainGetMakefile(cmake* cm) @@ -142,20 +156,21 @@ static std::string cmakemainGetStack(cmake* cm) return msg; } -static void cmakemainMessageCallback(const char* m, const char* /*unused*/, - cmake* cm) +static void cmakemainMessageCallback(const std::string& m, + const char* /*unused*/, cmake* cm) { std::cerr << m << cmakemainGetStack(cm) << std::endl << std::flush; } -static void cmakemainProgressCallback(const char* m, float prog, cmake* cm) +static void cmakemainProgressCallback(const std::string& m, float prog, + cmake* cm) { cmMakefile* mf = cmakemainGetMakefile(cm); std::string dir; - if ((mf) && (strstr(m, "Configuring") == m) && (prog < 0)) { + if (mf && cmHasLiteralPrefix(m, "Configuring") && (prog < 0)) { dir = " "; dir += mf->GetCurrentSourceDirectory(); - } else if ((mf) && (strstr(m, "Generating") == m)) { + } else if (mf && cmHasLiteralPrefix(m, "Generating")) { dir = " "; dir += mf->GetCurrentBinaryDirectory(); } @@ -169,6 +184,7 @@ static void cmakemainProgressCallback(const char* m, float prog, cmake* cm) int main(int ac, char const* const* av) { + cmSystemTools::EnsureStdPipes(); #if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) // Replace streambuf so we can output Unicode to console cmsys::ConsoleBuf::Manager consoleOut(std::cout); @@ -188,6 +204,9 @@ int main(int ac, char const* const* av) if (strcmp(av[1], "--build") == 0) { return do_build(ac, av); } + if (strcmp(av[1], "--install") == 0) { + return do_install(ac, av); + } if (strcmp(av[1], "--open") == 0) { return do_open(ac, av); } @@ -319,10 +338,11 @@ int do_cmake(int ac, char const* const* av) cmake cm(role, mode); cm.SetHomeDirectory(""); cm.SetHomeOutputDirectory(""); - cmSystemTools::SetMessageCallback([&cm](const char* msg, const char* title) { - cmakemainMessageCallback(msg, title, &cm); - }); - cm.SetProgressCallback([&cm](const char* msg, float prog) { + cmSystemTools::SetMessageCallback( + [&cm](const std::string& msg, const char* title) { + cmakemainMessageCallback(msg, title, &cm); + }); + cm.SetProgressCallback([&cm](const std::string& msg, float prog) { cmakemainProgressCallback(msg, prog, &cm); }); cm.SetWorkingMode(workingMode); @@ -377,7 +397,14 @@ int extract_job_number(int& index, char const* current, char const* next, if (jobString.empty()) { jobs = cmake::DEFAULT_BUILD_PARALLEL_LEVEL; } else if (cmSystemTools::StringToULong(jobString.c_str(), &numJobs)) { - jobs = int(numJobs); + if (numJobs == 0) { + std::cerr + << "The <jobs> value requires a positive integer argument.\n\n"; + } else if (numJobs > INT_MAX) { + std::cerr << "The <jobs> value is too large.\n\n"; + } else { + jobs = int(numJobs); + } } else { std::cerr << "'" << command.substr(0, len_of_flag) << "' invalid number '" << jobString << "' given.\n\n"; @@ -393,13 +420,14 @@ static int do_build(int ac, char const* const* av) return -1; #else int jobs = cmake::NO_BUILD_PARALLEL_LEVEL; - std::string target; + std::vector<std::string> targets; std::string config = "Debug"; std::string dir; std::vector<std::string> nativeOptions; - bool clean = false; + bool cleanFirst = false; + bool foundClean = false; + bool foundNonClean = false; bool verbose = cmSystemTools::HasEnv("VERBOSE"); - bool hasTarget = false; enum Doing { @@ -419,25 +447,21 @@ static int do_build(int ac, char const* const* av) if (jobs < 0) { dir.clear(); } + doing = DoingNone; } else if (cmHasLiteralPrefix(av[i], "--parallel")) { const char* nextArg = ((i + 1 < ac) ? av[i + 1] : nullptr); jobs = extract_job_number(i, av[i], nextArg, sizeof("--parallel") - 1); if (jobs < 0) { dir.clear(); } - } else if (strcmp(av[i], "--target") == 0) { - if (!hasTarget) { - doing = DoingTarget; - hasTarget = true; - } else { - std::cerr << "'--target' may not be specified more than once.\n\n"; - dir.clear(); - break; - } + doing = DoingNone; + } else if ((strcmp(av[i], "--target") == 0) || + (strcmp(av[i], "-t") == 0)) { + doing = DoingTarget; } else if (strcmp(av[i], "--config") == 0) { doing = DoingConfig; } else if (strcmp(av[i], "--clean-first") == 0) { - clean = true; + cleanFirst = true; doing = DoingNone; } else if ((strcmp(av[i], "--verbose") == 0) || (strcmp(av[i], "-v") == 0)) { @@ -454,8 +478,23 @@ static int do_build(int ac, char const* const* av) doing = DoingNone; break; case DoingTarget: - target = av[i]; - doing = DoingNone; + if (strlen(av[i]) == 0) { + std::cerr << "Warning: Argument number " << i + << " after --target option is empty." << std::endl; + } else { + targets.emplace_back(av[i]); + if (strcmp(av[i], "clean") == 0) { + foundClean = true; + } else { + foundNonClean = true; + } + } + if (foundClean && foundNonClean) { + std::cerr << "Error: Building 'clean' and other targets together " + "is not supported." + << std::endl; + dir.clear(); + } break; case DoingConfig: config = av[i]; @@ -477,7 +516,17 @@ static int do_build(int ac, char const* const* av) } else { unsigned long numJobs = 0; if (cmSystemTools::StringToULong(parallel.c_str(), &numJobs)) { - jobs = int(numJobs); + if (numJobs == 0) { + std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable " + "requires a positive integer argument.\n\n"; + dir.clear(); + } else if (numJobs > INT_MAX) { + std::cerr << "The CMAKE_BUILD_PARALLEL_LEVEL environment variable " + "is too large.\n\n"; + dir.clear(); + } else { + jobs = int(numJobs); + } } else { std::cerr << "'CMAKE_BUILD_PARALLEL_LEVEL' environment variable\n" << "invalid number '" << parallel << "' given.\n\n"; @@ -499,13 +548,126 @@ static int do_build(int ac, char const* const* av) } cmake cm(cmake::RoleInternal, cmState::Project); - cmSystemTools::SetMessageCallback([&cm](const char* msg, const char* title) { - cmakemainMessageCallback(msg, title, &cm); + cmSystemTools::SetMessageCallback( + [&cm](const std::string& msg, const char* title) { + cmakemainMessageCallback(msg, title, &cm); + }); + cm.SetProgressCallback([&cm](const std::string& msg, float prog) { + cmakemainProgressCallback(msg, prog, &cm); }); - cm.SetProgressCallback([&cm](const char* msg, float prog) { + return cm.Build(jobs, dir, targets, config, nativeOptions, cleanFirst, + verbose); +#endif +} + +static int do_install(int ac, char const* const* av) +{ +#ifndef CMAKE_BUILD_WITH_CMAKE + std::cerr << "This cmake does not support --install\n"; + return -1; +#else + assert(1 < ac); + + std::string config; + std::string component; + std::string prefix; + std::string dir; + bool strip = false; + bool verbose = cmSystemTools::HasEnv("VERBOSE"); + + enum Doing + { + DoingNone, + DoingDir, + DoingConfig, + DoingComponent, + DoingPrefix, + }; + + Doing doing = DoingDir; + + for (int i = 2; i < ac; ++i) { + if (strcmp(av[i], "--config") == 0) { + doing = DoingConfig; + } else if (strcmp(av[i], "--component") == 0) { + doing = DoingComponent; + } else if (strcmp(av[i], "--prefix") == 0) { + doing = DoingPrefix; + } else if (strcmp(av[i], "--strip") == 0) { + strip = true; + doing = DoingNone; + } else if ((strcmp(av[i], "--verbose") == 0) || + (strcmp(av[i], "-v") == 0)) { + verbose = true; + doing = DoingNone; + } else { + switch (doing) { + case DoingDir: + dir = cmSystemTools::CollapseFullPath(av[i]); + doing = DoingNone; + break; + case DoingConfig: + config = av[i]; + doing = DoingNone; + break; + case DoingComponent: + component = av[i]; + doing = DoingNone; + break; + case DoingPrefix: + prefix = av[i]; + doing = DoingNone; + break; + default: + std::cerr << "Unknown argument " << av[i] << std::endl; + dir.clear(); + break; + } + } + } + + if (dir.empty()) { + std::cerr << "Usage: cmake --install <dir> " + "[options]\nOptions:\n" CMAKE_INSTALL_OPTIONS; + return 1; + } + + cmake cm(cmake::RoleScript, cmState::Script); + + cmSystemTools::SetMessageCallback( + [&cm](const std::string& msg, const char* title) { + cmakemainMessageCallback(msg, title, &cm); + }); + cm.SetProgressCallback([&cm](const std::string& msg, float prog) { cmakemainProgressCallback(msg, prog, &cm); }); - return cm.Build(jobs, dir, target, config, nativeOptions, clean, verbose); + cm.SetHomeDirectory(""); + cm.SetHomeOutputDirectory(""); + cm.SetDebugOutputOn(verbose); + cm.SetWorkingMode(cmake::SCRIPT_MODE); + + std::vector<std::string> args{ av[0] }; + + if (!prefix.empty()) { + args.emplace_back("-DCMAKE_INSTALL_PREFIX=" + prefix); + } + + if (!component.empty()) { + args.emplace_back("-DCMAKE_INSTALL_COMPONENT=" + component); + } + + if (strip) { + args.emplace_back("-DCMAKE_INSTALL_DO_STRIP=1"); + } + + if (!config.empty()) { + args.emplace_back("-DCMAKE_INSTALL_CONFIG_NAME=" + config); + } + + args.emplace_back("-P"); + args.emplace_back(dir + "/cmake_install.cmake"); + + return cm.Run(args) ? 1 : 0; #endif } @@ -541,10 +703,11 @@ static int do_open(int ac, char const* const* av) } cmake cm(cmake::RoleInternal, cmState::Unknown); - cmSystemTools::SetMessageCallback([&cm](const char* msg, const char* title) { - cmakemainMessageCallback(msg, title, &cm); - }); - cm.SetProgressCallback([&cm](const char* msg, float prog) { + cmSystemTools::SetMessageCallback( + [&cm](const std::string& msg, const char* title) { + cmakemainMessageCallback(msg, title, &cm); + }); + cm.SetProgressCallback([&cm](const std::string& msg, float prog) { cmakemainProgressCallback(msg, prog, &cm); }); return cm.Open(dir, false) ? 0 : 1; diff --git a/Source/cmakexbuild.cxx b/Source/cmakexbuild.cxx deleted file mode 100644 index 2951945..0000000 --- a/Source/cmakexbuild.cxx +++ /dev/null @@ -1,80 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ - -#include "cmConfigure.h" // IWYU pragma: keep - -#include "cmsys/Process.h" -#include <iostream> -#include <string> -#include <vector> - -#include "cmDuration.h" -#include "cmSystemTools.h" - -// This is a wrapper program for xcodebuild -// it calls xcodebuild, and does two things -// it removes much of the output, all the setenv -// stuff. Also, it checks for the text file busy -// error, and re-runs xcodebuild until that error does -// not show up. - -int RunXCode(std::vector<const char*>& argv, bool& hitbug) -{ - hitbug = false; - cmsysProcess* cp = cmsysProcess_New(); - cmsysProcess_SetCommand(cp, &*argv.begin()); - cmsysProcess_SetTimeout(cp, 0); - cmsysProcess_Execute(cp); - std::vector<char> out; - std::vector<char> err; - std::string line; - int pipe = - cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err); - while (pipe != cmsysProcess_Pipe_None) { - if (line.find("/bin/sh: bad interpreter: Text file busy") != - std::string::npos) { - hitbug = true; - std::cerr << "Hit xcodebuild bug : " << line << "\n"; - } - // if the bug is hit, no more output should be generated - // because it may contain bogus errors - // also remove all output with setenv in it to tone down - // the verbosity of xcodebuild - if (!hitbug && (line.find("setenv") == std::string::npos)) { - if (pipe == cmsysProcess_Pipe_STDERR) { - std::cerr << line << "\n"; - } else if (pipe == cmsysProcess_Pipe_STDOUT) { - std::cout << line << "\n"; - } - } - pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, - err); - } - cmsysProcess_WaitForExit(cp, nullptr); - if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) { - return cmsysProcess_GetExitValue(cp); - } - if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) { - return -1; - } - return -1; -} - -int main(int ac, char* av[]) -{ - std::vector<const char*> argv; - argv.push_back("xcodebuild"); - for (int i = 1; i < ac; i++) { - argv.push_back(av[i]); - } - argv.push_back(nullptr); - bool hitbug = true; - int ret = 0; - while (hitbug) { - ret = RunXCode(argv, hitbug); - } - if (ret < 0) { - return 255; - } - return ret; -} diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx index 1a10666..19d0d38 100644 --- a/Source/cmcldeps.cxx +++ b/Source/cmcldeps.cxx @@ -276,7 +276,7 @@ int main() std::string clrest = rest; // rc: /fo x.dir\x.rc.res -> cl: /out:x.dir\x.rc.res.dep.obj - clrest = replace(clrest, "/fo", "/out:"); + clrest = replace(clrest, "/fo ", "/out:"); clrest = replace(clrest, objfile, objfile + ".dep.obj "); cl = "\"" + cl + "\" /P /DRC_INVOKED /TC "; diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index d20c5d2..3c75957 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -7,8 +7,9 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmQtAutoGeneratorMocUic.h" -#include "cmQtAutoGeneratorRcc.h" +#include "cmQtAutoMocUic.h" +#include "cmQtAutoRcc.h" +#include "cmRange.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" @@ -37,6 +38,7 @@ #include "cmsys/Process.h" #include "cmsys/Terminal.h" #include <algorithm> +#include <array> #include <iostream> #include <iterator> #include <memory> // IWYU pragma: keep @@ -109,8 +111,8 @@ void CMakeCommandUsage(const char* program) << " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n" << " - create or extract a tar or zip archive\n" << " time command [args...] - run command and display elapsed time\n" - << " touch file - touch a file.\n" - << " touch_nocreate file - touch a file but do not create it.\n" + << " touch <file>... - touch a <file>.\n" + << " touch_nocreate <file>... - touch a <file> but do not create it.\n" << " create_symlink old new - create a symbolic link new -> old\n" #if defined(_WIN32) && !defined(__CYGWIN__) << "Available on Windows only:\n" @@ -350,12 +352,13 @@ struct CoCompiler bool NoOriginalCommand; }; -static CoCompiler CoCompilers[] = { // Table of options and handlers. - { "--cppcheck=", HandleCppCheck, false }, - { "--cpplint=", HandleCppLint, false }, - { "--iwyu=", HandleIWYU, false }, - { "--lwyu=", HandleLWYU, true }, - { "--tidy=", HandleTidy, false } +static const std::array<CoCompiler, 5> CoCompilers = { + { // Table of options and handlers. + { "--cppcheck=", HandleCppCheck, false }, + { "--cpplint=", HandleCppLint, false }, + { "--iwyu=", HandleIWYU, false }, + { "--lwyu=", HandleLWYU, true }, + { "--tidy=", HandleTidy, false } } }; struct CoCompileJob @@ -365,7 +368,7 @@ struct CoCompileJob }; // called when args[0] == "__run_co_compile" -int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args) +int cmcmd::HandleCoCompileCommands(std::vector<std::string> const& args) { std::vector<CoCompileJob> jobs; std::string sourceFile; // store --source= @@ -378,24 +381,22 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args) std::vector<std::string> orig_cmd; bool doing_options = true; - for (std::string::size_type i = 2; i < args.size(); ++i) { - std::string const& arg = args[i]; + for (std::string const& arg : cmMakeRange(args).advance(2)) { // if the arg is -- then the rest of the args after // go into orig_cmd if (arg == "--") { doing_options = false; } else if (doing_options) { bool optionFound = false; - for (CoCompiler const* cc = cm::cbegin(CoCompilers); - cc != cm::cend(CoCompilers); ++cc) { - size_t optionLen = strlen(cc->Option); - if (arg.compare(0, optionLen, cc->Option) == 0) { + for (CoCompiler const& cc : CoCompilers) { + size_t optionLen = strlen(cc.Option); + if (arg.compare(0, optionLen, cc.Option) == 0) { optionFound = true; CoCompileJob job; job.Command = arg.substr(optionLen); - job.Handler = cc->Handler; + job.Handler = cc.Handler; jobs.push_back(std::move(job)); - if (cc->NoOriginalCommand) { + if (cc.NoOriginalCommand) { runOriginalCmd = false; } } @@ -419,9 +420,8 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args) if (jobs.empty()) { std::cerr << "__run_co_compile missing command to run. " "Looking for one or more of the following:\n"; - for (CoCompiler const* cc = cm::cbegin(CoCompilers); - cc != cm::cend(CoCompilers); ++cc) { - std::cerr << cc->Option << "\n"; + for (CoCompiler const& cc : CoCompilers) { + std::cerr << cc.Option << "\n"; } return 1; } @@ -465,7 +465,7 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string>& args) return ret; } -int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) +int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args) { // IF YOU ADD A NEW COMMAND, DOCUMENT IT ABOVE and in cmakemain.cxx if (args.size() > 1) { @@ -481,9 +481,9 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) } // If error occurs we want to continue copying next files. bool return_value = false; - for (std::string::size_type cc = 2; cc < args.size() - 1; cc++) { - if (!cmSystemTools::cmCopyFile(args[cc], args.back())) { - std::cerr << "Error copying file \"" << args[cc] << "\" to \"" + for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) { + if (!cmsys::SystemTools::CopyFileAlways(arg, args.back())) { + std::cerr << "Error copying file \"" << arg << "\" to \"" << args.back() << "\".\n"; return_value = true; } @@ -503,9 +503,9 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) } // If error occurs we want to continue copying next files. bool return_value = false; - for (std::string::size_type cc = 2; cc < args.size() - 1; cc++) { - if (!cmSystemTools::CopyFileIfDifferent(args[cc], args.back())) { - std::cerr << "Error copying file (if different) from \"" << args[cc] + for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) { + if (!cmSystemTools::CopyFileIfDifferent(arg, args.back())) { + std::cerr << "Error copying file (if different) from \"" << arg << "\" to \"" << args.back() << "\".\n"; return_value = true; } @@ -517,10 +517,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) if (args[1] == "copy_directory" && args.size() > 3) { // If error occurs we want to continue copying next files. bool return_value = false; - for (std::string::size_type cc = 2; cc < args.size() - 1; cc++) { - if (!cmSystemTools::CopyADirectory(args[cc], args.back())) { - std::cerr << "Error copying directory from \"" << args[cc] - << "\" to \"" << args.back() << "\".\n"; + for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) { + if (!cmSystemTools::CopyADirectory(arg, args.back())) { + std::cerr << "Error copying directory from \"" << arg << "\" to \"" + << args.back() << "\".\n"; return_value = true; } } @@ -613,13 +613,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) } if (args[1] == "env") { - std::vector<std::string>::const_iterator ai = args.begin() + 2; - std::vector<std::string>::const_iterator ae = args.end(); + auto ai = args.cbegin() + 2; + auto ae = args.cend(); for (; ai != ae; ++ai) { std::string const& a = *ai; if (cmHasLiteralPrefix(a, "--unset=")) { // Unset environment variable. - cmSystemTools::UnPutEnv(a.c_str() + 8); + cmSystemTools::UnPutEnv(a.substr(8)); } else if (!a.empty() && a[0] == '-') { // Environment variable and command names cannot start in '-', // so this must be an unknown option. @@ -653,10 +653,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) #if defined(CMAKE_BUILD_WITH_CMAKE) if (args[1] == "environment") { - std::vector<std::string> env = cmSystemTools::GetEnvironmentVariables(); - std::vector<std::string>::iterator it; - for (it = env.begin(); it != env.end(); ++it) { - std::cout << *it << std::endl; + for (auto const& env : cmSystemTools::GetEnvironmentVariables()) { + std::cout << env << std::endl; } return 0; } @@ -665,9 +663,9 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) if (args[1] == "make_directory" && args.size() > 2) { // If error occurs we want to continue copying next files. bool return_value = false; - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - if (!cmSystemTools::MakeDirectory(args[cc])) { - std::cerr << "Error creating directory \"" << args[cc] << "\".\n"; + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (!cmSystemTools::MakeDirectory(arg)) { + std::cerr << "Error creating directory \"" << arg << "\".\n"; return_value = true; } } @@ -686,14 +684,14 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Remove file if (args[1] == "remove" && args.size() > 2) { bool force = false; - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - if (args[cc] == "\\-f" || args[cc] == "-f") { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (arg == "\\-f" || arg == "-f") { force = true; } else { // Complain if the file could not be removed, still exists, // and the -f option was not given. - if (!cmSystemTools::RemoveFile(args[cc]) && !force && - cmSystemTools::FileExists(args[cc])) { + if (!cmSystemTools::RemoveFile(arg) && !force && + cmSystemTools::FileExists(arg)) { return 1; } } @@ -703,10 +701,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Touch file if (args[1] == "touch" && args.size() > 2) { - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - if (!cmSystemTools::Touch(args[cc], true)) { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (!cmSystemTools::Touch(arg, true)) { std::cerr << "cmake -E touch: failed to update \""; - std::cerr << args[cc] << "\".\n"; + std::cerr << arg << "\".\n"; return 1; } } @@ -715,10 +713,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Touch file if (args[1] == "touch_nocreate" && args.size() > 2) { - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - if (!cmSystemTools::Touch(args[cc], false)) { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (!cmSystemTools::Touch(arg, false)) { std::cerr << "cmake -E touch_nocreate: failed to update \""; - std::cerr << args[cc] << "\".\n"; + std::cerr << arg << "\".\n"; return 1; } } @@ -743,15 +741,15 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) // Sleep command if (args[1] == "sleep" && args.size() > 2) { double total = 0; - for (size_t i = 2; i < args.size(); ++i) { + for (auto const& arg : cmMakeRange(args).advance(2)) { double num = 0.0; char unit; char extra; - int n = sscanf(args[i].c_str(), "%lg%c%c", &num, &unit, &extra); + int n = sscanf(arg.c_str(), "%lg%c%c", &num, &unit, &extra); if ((n == 1 || (n == 2 && unit == 's')) && num >= 0) { total += num; } else { - std::cerr << "Unknown sleep time format \"" << args[i] << "\".\n"; + std::cerr << "Unknown sleep time format \"" << arg << "\".\n"; return 1; } } @@ -817,8 +815,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) if (args[1] == "chdir" && args.size() >= 4) { std::string const& directory = args[2]; if (!cmSystemTools::FileExists(directory)) { - cmSystemTools::Error("Directory does not exist for chdir command: ", - args[2].c_str()); + cmSystemTools::Error("Directory does not exist for chdir command: " + + args[2]); return 1; } @@ -826,7 +824,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) cmWrap('"', cmMakeRange(args).advance(3), '"', " "); int retval = 0; if (cmSystemTools::RunSingleCommand( - command.c_str(), nullptr, nullptr, &retval, directory.c_str(), + command, nullptr, nullptr, &retval, directory.c_str(), cmSystemTools::OUTPUT_PASSTHROUGH, cmDuration::zero())) { return retval; } @@ -1020,13 +1018,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) #ifdef CMAKE_BUILD_WITH_CMAKE if ((args[1] == "cmake_autogen") && (args.size() >= 4)) { - cmQtAutoGeneratorMocUic autoGen; + cmQtAutoMocUic autoGen; std::string const& infoDir = args[2]; std::string const& config = args[3]; return autoGen.Run(infoDir, config) ? 0 : 1; } if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) { - cmQtAutoGeneratorRcc autoGen; + cmQtAutoRcc autoGen; std::string const& infoFile = args[2]; std::string config; if (args.size() > 3) { @@ -1046,8 +1044,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) std::string mtime; std::string format; bool doing_options = true; - for (std::string::size_type cc = 4; cc < args.size(); cc++) { - std::string const& arg = args[cc]; + for (auto const& arg : cmMakeRange(args).advance(4)) { if (doing_options && cmHasLiteralPrefix(arg, "--")) { if (arg == "--") { doing_options = false; @@ -1065,37 +1062,63 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) format) != cm::cend(knownFormats); if (!isKnown) { - cmSystemTools::Error("Unknown -E tar --format= argument: ", - format.c_str()); + cmSystemTools::Error("Unknown -E tar --format= argument: " + + format); return 1; } } else { - cmSystemTools::Error("Unknown option to -E tar: ", arg.c_str()); + cmSystemTools::Error("Unknown option to -E tar: " + arg); return 1; } } else { files.push_back(arg); } } + cmSystemTools::cmTarAction action = cmSystemTools::TarActionNone; cmSystemTools::cmTarCompression compress = cmSystemTools::TarCompressNone; bool verbose = false; int nCompress = 0; - if (flags.find_first_of('j') != std::string::npos) { - compress = cmSystemTools::TarCompressBZip2; - ++nCompress; - } - if (flags.find_first_of('J') != std::string::npos) { - compress = cmSystemTools::TarCompressXZ; - ++nCompress; - } - if (flags.find_first_of('z') != std::string::npos) { - compress = cmSystemTools::TarCompressGZip; - ++nCompress; + + for (auto flag : flags) { + switch (flag) { + case '-': + case 'f': { + // Keep for backward compatibility. Ignored + } break; + case 'j': { + compress = cmSystemTools::TarCompressBZip2; + ++nCompress; + } break; + case 'J': { + compress = cmSystemTools::TarCompressXZ; + ++nCompress; + } break; + case 'z': { + compress = cmSystemTools::TarCompressGZip; + ++nCompress; + } break; + case 'v': { + verbose = true; + } break; + case 't': { + action = cmSystemTools::TarActionList; + } break; + case 'c': { + action = cmSystemTools::TarActionCreate; + } break; + case 'x': { + action = cmSystemTools::TarActionExtract; + } break; + default: { + cmSystemTools::Message( + std::string("tar: Unknown argument: ") + flag, "Warning"); + } + } } if ((format == "7zip" || format == "zip") && nCompress > 0) { - cmSystemTools::Error("Can not use compression flags with format: ", - format.c_str()); + cmSystemTools::Error("Can not use compression flags with format: " + + format); return 1; } if (nCompress > 1) { @@ -1103,24 +1126,24 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) "at most one flag of z, j, or J may be used"); return 1; } - if (flags.find_first_of('v') != std::string::npos) { - verbose = true; - } - - if (flags.find_first_of('t') != std::string::npos) { + if (action == cmSystemTools::TarActionList) { if (!cmSystemTools::ListTar(outFile.c_str(), verbose)) { - cmSystemTools::Error("Problem listing tar: ", outFile.c_str()); + cmSystemTools::Error("Problem listing tar: " + outFile); return 1; } - } else if (flags.find_first_of('c') != std::string::npos) { + } else if (action == cmSystemTools::TarActionCreate) { + if (files.empty()) { + cmSystemTools::Message("tar: No files or directories specified", + "Warning"); + } if (!cmSystemTools::CreateTar(outFile.c_str(), files, compress, verbose, mtime, format)) { - cmSystemTools::Error("Problem creating tar: ", outFile.c_str()); + cmSystemTools::Error("Problem creating tar: " + outFile); return 1; } - } else if (flags.find_first_of('x') != std::string::npos) { + } else if (action == cmSystemTools::TarActionExtract) { if (!cmSystemTools::ExtractTar(outFile.c_str(), verbose)) { - cmSystemTools::Error("Problem extracting tar: ", outFile.c_str()); + cmSystemTools::Error("Problem extracting tar: " + outFile); return 1; } #ifdef WIN32 @@ -1139,6 +1162,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) cmSystemTools::Delay(delay); } #endif + } else { + cmSystemTools::Error("tar: No action specified. Please choose: 't' " + "(list), 'c' (create) or 'x' (extract)"); + return 1; } return 0; } @@ -1149,17 +1176,15 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) bool isDebug = false; std::string pipe; - for (size_t i = 2; i < args.size(); ++i) { - const std::string& a = args[i]; - - if (a == "--experimental") { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (arg == "--experimental") { supportExperimental = true; - } else if (a == "--debug") { + } else if (arg == "--debug") { pipe.clear(); isDebug = true; - } else if (a.substr(0, pipePrefix.size()) == pipePrefix) { + } else if (arg.substr(0, pipePrefix.size()) == pipePrefix) { isDebug = false; - pipe = a.substr(pipePrefix.size()); + pipe = arg.substr(pipePrefix.size()); if (pipe.empty()) { cmSystemTools::Error("No pipe given after --pipe="); return 2; @@ -1231,15 +1256,15 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args) return 1; } -int cmcmd::HashSumFile(std::vector<std::string>& args, cmCryptoHash::Algo algo) +int cmcmd::HashSumFile(std::vector<std::string> const& args, + cmCryptoHash::Algo algo) { if (args.size() < 3) { return -1; } int retval = 0; - for (std::string::size_type cc = 2; cc < args.size(); cc++) { - const char* filename = args[cc].c_str(); + for (auto const& filename : cmMakeRange(args).advance(2)) { // Cannot compute sum of a directory if (cmSystemTools::FileIsDirectory(filename)) { std::cerr << "Error: " << filename << " is a directory" << std::endl; @@ -1258,7 +1283,7 @@ int cmcmd::HashSumFile(std::vector<std::string>& args, cmCryptoHash::Algo algo) return retval; } -int cmcmd::SymlinkLibrary(std::vector<std::string>& args) +int cmcmd::SymlinkLibrary(std::vector<std::string> const& args) { int result = 0; std::string realName = args[2]; @@ -1282,7 +1307,7 @@ int cmcmd::SymlinkLibrary(std::vector<std::string>& args) return result; } -int cmcmd::SymlinkExecutable(std::vector<std::string>& args) +int cmcmd::SymlinkExecutable(std::vector<std::string> const& args) { int result = 0; std::string const& realName = args[2]; @@ -1356,7 +1381,7 @@ static void cmcmdProgressReport(std::string const& dir, std::string const& num) } } -int cmcmd::ExecuteEchoColor(std::vector<std::string>& args) +int cmcmd::ExecuteEchoColor(std::vector<std::string> const& args) { // The arguments are // args[0] == <cmake-executable> @@ -1366,55 +1391,54 @@ int cmcmd::ExecuteEchoColor(std::vector<std::string>& args) int color = cmsysTerminal_Color_Normal; bool newline = true; std::string progressDir; - for (unsigned int i = 2; i < args.size(); ++i) { - if (args[i].find("--switch=") == 0) { + for (auto const& arg : cmMakeRange(args).advance(2)) { + if (arg.find("--switch=") == 0) { // Enable or disable color based on the switch value. - std::string value = args[i].substr(9); + std::string value = arg.substr(9); if (!value.empty()) { enabled = cmSystemTools::IsOn(value); } - } else if (cmHasLiteralPrefix(args[i], "--progress-dir=")) { - progressDir = args[i].substr(15); - } else if (cmHasLiteralPrefix(args[i], "--progress-num=")) { + } else if (cmHasLiteralPrefix(arg, "--progress-dir=")) { + progressDir = arg.substr(15); + } else if (cmHasLiteralPrefix(arg, "--progress-num=")) { if (!progressDir.empty()) { - std::string const& progressNum = args[i].substr(15); + std::string const& progressNum = arg.substr(15); cmcmdProgressReport(progressDir, progressNum); } - } else if (args[i] == "--normal") { + } else if (arg == "--normal") { color = cmsysTerminal_Color_Normal; - } else if (args[i] == "--black") { + } else if (arg == "--black") { color = cmsysTerminal_Color_ForegroundBlack; - } else if (args[i] == "--red") { + } else if (arg == "--red") { color = cmsysTerminal_Color_ForegroundRed; - } else if (args[i] == "--green") { + } else if (arg == "--green") { color = cmsysTerminal_Color_ForegroundGreen; - } else if (args[i] == "--yellow") { + } else if (arg == "--yellow") { color = cmsysTerminal_Color_ForegroundYellow; - } else if (args[i] == "--blue") { + } else if (arg == "--blue") { color = cmsysTerminal_Color_ForegroundBlue; - } else if (args[i] == "--magenta") { + } else if (arg == "--magenta") { color = cmsysTerminal_Color_ForegroundMagenta; - } else if (args[i] == "--cyan") { + } else if (arg == "--cyan") { color = cmsysTerminal_Color_ForegroundCyan; - } else if (args[i] == "--white") { + } else if (arg == "--white") { color = cmsysTerminal_Color_ForegroundWhite; - } else if (args[i] == "--bold") { + } else if (arg == "--bold") { color |= cmsysTerminal_Color_ForegroundBold; - } else if (args[i] == "--no-newline") { + } else if (arg == "--no-newline") { newline = false; - } else if (args[i] == "--newline") { + } else if (arg == "--newline") { newline = true; } else { // Color is enabled. Print with the current color. - cmSystemTools::MakefileColorEcho(color, args[i].c_str(), newline, - enabled); + cmSystemTools::MakefileColorEcho(color, arg.c_str(), newline, enabled); } } return 0; } -int cmcmd::ExecuteLinkScript(std::vector<std::string>& args) +int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args) { // The arguments are // args[0] == <cmake-executable> @@ -1627,9 +1651,9 @@ std::ostream& operator<<(std::ostream& stream, return stream; } -static bool RunCommand(const char* comment, std::vector<std::string>& command, - bool verbose, NumberFormat exitFormat, - int* retCodeOut = nullptr, +static bool RunCommand(const char* comment, + std::vector<std::string> const& command, bool verbose, + NumberFormat exitFormat, int* retCodeOut = nullptr, bool (*retCodeOkay)(int) = nullptr) { if (verbose) { @@ -1830,7 +1854,8 @@ int cmVSLink::LinkIncremental() // Compile the resource file. std::vector<std::string> rcCommand; rcCommand.push_back(this->RcPath.empty() ? "rc" : this->RcPath); - rcCommand.push_back("/fo" + this->ManifestFileRes); + rcCommand.emplace_back("/fo"); + rcCommand.push_back(this->ManifestFileRes); rcCommand.push_back(this->ManifestFileRC); if (!RunCommand("RC Pass 1", rcCommand, this->Verbose, FORMAT_DECIMAL)) { return -1; diff --git a/Source/cmcmd.h b/Source/cmcmd.h index d1e03d0..69a7ecb 100644 --- a/Source/cmcmd.h +++ b/Source/cmcmd.h @@ -16,18 +16,18 @@ public: * Execute commands during the build process. Supports options such * as echo, remove file etc. */ - static int ExecuteCMakeCommand(std::vector<std::string>&); + static int ExecuteCMakeCommand(std::vector<std::string> const&); protected: - static int HandleCoCompileCommands(std::vector<std::string>& args); - static int HashSumFile(std::vector<std::string>& args, + static int HandleCoCompileCommands(std::vector<std::string> const& args); + static int HashSumFile(std::vector<std::string> const& args, cmCryptoHash::Algo algo); - static int SymlinkLibrary(std::vector<std::string>& args); - static int SymlinkExecutable(std::vector<std::string>& args); + static int SymlinkLibrary(std::vector<std::string> const& args); + static int SymlinkExecutable(std::vector<std::string> const& args); static bool SymlinkInternal(std::string const& file, std::string const& link); - static int ExecuteEchoColor(std::vector<std::string>& args); - static int ExecuteLinkScript(std::vector<std::string>& args); + static int ExecuteEchoColor(std::vector<std::string> const& args); + static int ExecuteLinkScript(std::vector<std::string> const& args); static int WindowsCEEnvironment(const char* version, const std::string& name); static int VisualStudioLink(std::vector<std::string> const& args, int type); diff --git a/Source/ctest.cxx b/Source/ctest.cxx index 4a2531a..3b3630f 100644 --- a/Source/ctest.cxx +++ b/Source/ctest.cxx @@ -143,6 +143,7 @@ static const char* cmDocumentationOptions[][2] = { // this is a test driver program for cmCTest. int main(int argc, char const* const* argv) { + cmSystemTools::EnsureStdPipes(); #if defined(_WIN32) && defined(CMAKE_BUILD_WITH_CMAKE) // Replace streambuf so we can output Unicode to console cmsys::ConsoleBuf::Manager consoleOut(std::cout); @@ -191,8 +192,7 @@ int main(int argc, char const* const* argv) doc.addCTestStandardDocSections(); if (doc.CheckOptions(argc, argv)) { // Construct and print requested documentation. - cmCTestScriptHandler* ch = - static_cast<cmCTestScriptHandler*>(inst.GetHandler("script")); + cmCTestScriptHandler* ch = inst.GetScriptHandler(); ch->CreateCMake(); doc.SetShowGenerators(false); diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt index e7da994..1302c64 100644 --- a/Source/kwsys/CMakeLists.txt +++ b/Source/kwsys/CMakeLists.txt @@ -172,6 +172,9 @@ ENDIF() IF(KWSYS_USE_Directory) SET(KWSYS_USE_Encoding 1) ENDIF() +IF(KWSYS_USE_DynamicLoader) + SET(KWSYS_USE_Encoding 1) +ENDIF() IF(KWSYS_USE_FStream) SET(KWSYS_USE_Encoding 1) ENDIF() @@ -179,13 +182,6 @@ IF(KWSYS_USE_ConsoleBuf) SET(KWSYS_USE_Encoding 1) ENDIF() -# Setup the large file support default. -IF(KWSYS_LFS_DISABLE) - SET(KWSYS_LFS_REQUESTED 0) -ELSE() - SET(KWSYS_LFS_REQUESTED 1) -ENDIF() - # Specify default 8 bit encoding for Windows IF(NOT KWSYS_ENCODING_DEFAULT_CODEPAGE) SET(KWSYS_ENCODING_DEFAULT_CODEPAGE CP_ACP) @@ -353,30 +349,6 @@ IF(KWSYS_STANDALONE) ENDIF() #----------------------------------------------------------------------------- -# Configure Large File Support. -KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_CSTDIO - "Checking whether header cstdio is available" DIRECT) -SET(KWSYS_LFS_AVAILABLE 0) -IF(KWSYS_LFS_REQUESTED) - # Large File Support is requested. - SET(KWSYS_LFS_REQUESTED 1) - - # Check for large file support. - SET(KWSYS_PLATFORM_CXX_TEST_DEFINES - -DKWSYS_CXX_HAS_CSTDIO=${KWSYS_CXX_HAS_CSTDIO}) - KWSYS_PLATFORM_CXX_TEST_RUN(KWSYS_LFS_WORKS - "Checking for Large File Support" DIRECT) - SET(KWSYS_PLATFORM_CXX_TEST_DEFINES) - - IF(KWSYS_LFS_WORKS) - SET(KWSYS_LFS_AVAILABLE 1) - ENDIF() -ELSE() - # Large File Support is not requested. - SET(KWSYS_LFS_REQUESTED 0) -ENDIF() - -#----------------------------------------------------------------------------- # Configure the standard library header wrappers based on compiler's # capabilities and parent project's request. Enforce 0/1 as only # possible values for configuration into Configure.hxx. @@ -493,6 +465,14 @@ IF(KWSYS_USE_DynamicLoader) ENDIF() IF(KWSYS_USE_SystemTools) + if (NOT DEFINED KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP) + set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1) + endif () + if (KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP) + set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1) + else () + set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 0) + endif () KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_SETENV "Checking whether CXX compiler has setenv" DIRECT) KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_UNSETENV @@ -575,9 +555,6 @@ IF(KWSYS_USE_SystemInformation) COMPILE_DEFINITIONS KWSYS_SYS_HAS_MACHINE_CPU_H=1) ENDIF() ENDIF() - IF(KWSYS_LFS_AVAILABLE AND NOT KWSYS_LFS_DISABLE) - SET(KWSYS_PLATFORM_CXX_TEST_DEFINES -DKWSYS_HAS_LFS=1) - ENDIF() KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_RLIMIT64 "Checking whether CXX compiler has rlimit64" DIRECT) SET(KWSYS_PLATFORM_CXX_TEST_DEFINES) @@ -1130,6 +1107,20 @@ IF(KWSYS_STANDALONE OR CMake_SOURCE_DIR) ADD_LIBRARY(${KWSYS_NAMESPACE}TestDynload MODULE testDynload.c) SET_PROPERTY(TARGET ${KWSYS_NAMESPACE}TestDynload PROPERTY LABELS ${KWSYS_LABELS_LIB}) ADD_DEPENDENCIES(${KWSYS_NAMESPACE}TestDynload ${KWSYS_TARGET_INTERFACE}) + + if (WIN32) + # Windows tests supported flags. + add_library(${KWSYS_NAMESPACE}TestDynloadImpl SHARED testDynloadImpl.c) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadImpl PROPERTY LABELS ${KWSYS_LABELS_LIB}) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadImpl PROPERTY DEFINE_SYMBOL BUILDING_TestDynloadImpl) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadImpl PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dynloaddir") + add_dependencies(${KWSYS_NAMESPACE}TestDynloadImpl ${KWSYS_TARGET_INTERFACE}) + add_library(${KWSYS_NAMESPACE}TestDynloadUse MODULE testDynloadUse.c) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadUse PROPERTY LABELS ${KWSYS_LABELS_LIB}) + set_property(TARGET ${KWSYS_NAMESPACE}TestDynloadUse PROPERTY LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dynloaddir") + add_dependencies(${KWSYS_NAMESPACE}TestDynloadUse ${KWSYS_TARGET_INTERFACE}) + target_link_libraries(${KWSYS_NAMESPACE}TestDynloadUse PRIVATE ${KWSYS_NAMESPACE}TestDynloadImpl) + endif () ENDIF() CREATE_TEST_SOURCELIST( KWSYS_CXX_TEST_SRCS ${KWSYS_NAMESPACE}TestsCxx.cxx diff --git a/Source/kwsys/CommandLineArguments.hxx.in b/Source/kwsys/CommandLineArguments.hxx.in index 31115e5..7db9015 100644 --- a/Source/kwsys/CommandLineArguments.hxx.in +++ b/Source/kwsys/CommandLineArguments.hxx.in @@ -62,6 +62,9 @@ public: CommandLineArguments(); ~CommandLineArguments(); + CommandLineArguments(const CommandLineArguments&) = delete; + CommandLineArguments& operator=(const CommandLineArguments&) = delete; + /** * Various argument types. */ diff --git a/Source/kwsys/Configure.h.in b/Source/kwsys/Configure.h.in index bec1abc..5323c57 100644 --- a/Source/kwsys/Configure.h.in +++ b/Source/kwsys/Configure.h.in @@ -28,43 +28,6 @@ /* Whether kwsys namespace is "kwsys". */ #define @KWSYS_NAMESPACE@_NAME_IS_KWSYS @KWSYS_NAME_IS_KWSYS@ -/* Whether Large File Support is requested. */ -#define @KWSYS_NAMESPACE@_LFS_REQUESTED @KWSYS_LFS_REQUESTED@ - -/* Whether Large File Support is available. */ -#if @KWSYS_NAMESPACE@_LFS_REQUESTED -# define @KWSYS_NAMESPACE@_LFS_AVAILABLE @KWSYS_LFS_AVAILABLE@ -#endif - -/* Setup Large File Support if requested. */ -#if @KWSYS_NAMESPACE@_LFS_REQUESTED -/* Since LFS is requested this header must be included before system - headers whether or not LFS is available. */ -# if 0 && (defined(_SYS_TYPES_H) || defined(_SYS_TYPES_INCLUDED)) -# error "@KWSYS_NAMESPACE@/Configure.h must be included before sys/types.h" -# endif -/* Enable the large file API if it is available. */ -# if @KWSYS_NAMESPACE@_LFS_AVAILABLE && \ - !defined(@KWSYS_NAMESPACE@_LFS_NO_DEFINES) -# if !defined(_LARGEFILE_SOURCE) && \ - !defined(@KWSYS_NAMESPACE@_LFS_NO_DEFINE_LARGEFILE_SOURCE) -# define _LARGEFILE_SOURCE -# endif -# if !defined(_LARGEFILE64_SOURCE) && \ - !defined(@KWSYS_NAMESPACE@_LFS_NO_DEFINE_LARGEFILE64_SOURCE) -# define _LARGEFILE64_SOURCE -# endif -# if !defined(_LARGE_FILES) && \ - !defined(@KWSYS_NAMESPACE@_LFS_NO_DEFINE_LARGE_FILES) -# define _LARGE_FILES -# endif -# if !defined(_FILE_OFFSET_BITS) && \ - !defined(@KWSYS_NAMESPACE@_LFS_NO_DEFINE_FILE_OFFSET_BITS) -# define _FILE_OFFSET_BITS 64 -# endif -# endif -#endif - /* Setup the export macro. */ #if @KWSYS_BUILD_SHARED@ # if defined(_WIN32) || defined(__CYGWIN__) diff --git a/Source/kwsys/Configure.hxx.in b/Source/kwsys/Configure.hxx.in index d1e7464..92ffea3 100644 --- a/Source/kwsys/Configure.hxx.in +++ b/Source/kwsys/Configure.hxx.in @@ -11,6 +11,9 @@ /* Whether <ext/stdio_filebuf.h> is available. */ #define @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H \ @KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H@ +/* Whether the translation map is available or not. */ +#define @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP \ + @KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP@ #if defined(__SUNPRO_CC) && __SUNPRO_CC > 0x5130 && defined(__has_attribute) # define @KWSYS_NAMESPACE@__has_cpp_attribute(x) __has_attribute(x) @@ -56,6 +59,8 @@ @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H # define KWSYS_FALLTHROUGH @KWSYS_NAMESPACE@_FALLTHROUGH # define KWSYS_NULLPTR @KWSYS_NAMESPACE@_NULLPTR +# define KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP \ + @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP #endif #endif diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index 31b1c15..59530a4 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx @@ -102,7 +102,7 @@ bool Directory::Load(const std::string& name) # endif char* buf; size_t n = name.size(); - if (*name.rbegin() == '/' || *name.rbegin() == '\\') { + if (name.back() == '/' || name.back() == '\\') { buf = new char[n + 1 + 1]; sprintf(buf, "%s*", name.c_str()); } else { @@ -144,7 +144,7 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name) # endif char* buf; size_t n = name.size(); - if (*name.rbegin() == '/') { + if (name.back() == '/') { buf = new char[n + 1 + 1]; sprintf(buf, "%s*", name.c_str()); } else { diff --git a/Source/kwsys/DynamicLoader.cxx b/Source/kwsys/DynamicLoader.cxx index a85690f..b93a215 100644 --- a/Source/kwsys/DynamicLoader.cxx +++ b/Source/kwsys/DynamicLoader.cxx @@ -1,9 +1,14 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#if defined(_WIN32) +# define NOMINMAX // hide min,max to not conflict with <limits> +#endif + #include "kwsysPrivate.h" #include KWSYS_HEADER(DynamicLoader.hxx) #include KWSYS_HEADER(Configure.hxx) +#include KWSYS_HEADER(Encoding.hxx) // Work-around CMake dependency scanning limitation. This must // duplicate the above list of headers. @@ -25,6 +30,28 @@ // Each part of the ifdef contains a complete implementation for // the static methods of DynamicLoader. +#define CHECK_OPEN_FLAGS(var, supported, ret) \ + do { \ + /* Check for unknown flags. */ \ + if ((var & AllOpenFlags) != var) { \ + return ret; \ + } \ + \ + /* Check for unsupported flags. */ \ + if ((var & (supported)) != var) { \ + return ret; \ + } \ + } while (0) + +namespace KWSYS_NAMESPACE { + +DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( + const std::string& libname) +{ + return DynamicLoader::OpenLibrary(libname, 0); +} +} + #if !KWSYS_SUPPORTS_SHARED_LIBS // Implementation for environments without dynamic libs # include <string.h> // for strerror() @@ -32,7 +59,7 @@ namespace KWSYS_NAMESPACE { DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( - const std::string& libname) + const std::string& libname, int flags) { return 0; } @@ -67,8 +94,10 @@ const char* DynamicLoader::LastError() namespace KWSYS_NAMESPACE { DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( - const std::string& libname) + const std::string& libname, int flags) { + CHECK_OPEN_FLAGS(flags, 0, 0); + return shl_load(libname.c_str(), BIND_DEFERRED | DYNAMIC_PATH, 0L); } @@ -130,8 +159,10 @@ const char* DynamicLoader::LastError() namespace KWSYS_NAMESPACE { DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( - const std::string& libname) + const std::string& libname, int flags) { + CHECK_OPEN_FLAGS(flags, 0, 0); + NSObjectFileImageReturnCode rc; NSObjectFileImage image = 0; @@ -185,19 +216,22 @@ const char* DynamicLoader::LastError() // Implementation for Windows win32 code but not cygwin # include <windows.h> +# include <stdio.h> + namespace KWSYS_NAMESPACE { DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( - const std::string& libname) + const std::string& libname, int flags) { - DynamicLoader::LibraryHandle lh; - int length = MultiByteToWideChar(CP_UTF8, 0, libname.c_str(), -1, NULL, 0); - wchar_t* wchars = new wchar_t[length + 1]; - wchars[0] = '\0'; - MultiByteToWideChar(CP_UTF8, 0, libname.c_str(), -1, wchars, length); - lh = LoadLibraryW(wchars); - delete[] wchars; - return lh; + CHECK_OPEN_FLAGS(flags, SearchBesideLibrary, NULL); + + DWORD llFlags = 0; + if (flags & SearchBesideLibrary) { + llFlags |= LOAD_WITH_ALTERED_SEARCH_PATH; + } + + return LoadLibraryExW(Encoding::ToWindowsExtendedPath(libname).c_str(), NULL, + llFlags); } int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib) @@ -247,24 +281,38 @@ DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress( # endif } +# define DYNLOAD_ERROR_BUFFER_SIZE 1024 + const char* DynamicLoader::LastError() { - LPVOID lpMsgBuf = NULL; - - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPTSTR)&lpMsgBuf, 0, NULL); + wchar_t lpMsgBuf[DYNLOAD_ERROR_BUFFER_SIZE + 1]; + + DWORD error = GetLastError(); + DWORD length = FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + lpMsgBuf, DYNLOAD_ERROR_BUFFER_SIZE, NULL); + + static char str[DYNLOAD_ERROR_BUFFER_SIZE + 1]; + + if (length < 1) { + /* FormatMessage failed. Use a default message. */ + _snprintf(str, DYNLOAD_ERROR_BUFFER_SIZE, + "DynamicLoader encountered error 0x%X. " + "FormatMessage failed with error 0x%X", + error, GetLastError()); + return str; + } - if (!lpMsgBuf) { - return NULL; + if (!WideCharToMultiByte(CP_UTF8, 0, lpMsgBuf, -1, str, + DYNLOAD_ERROR_BUFFER_SIZE, NULL, NULL)) { + /* WideCharToMultiByte failed. Use a default message. */ + _snprintf(str, DYNLOAD_ERROR_BUFFER_SIZE, + "DynamicLoader encountered error 0x%X. " + "WideCharToMultiByte failed with error 0x%X", + error, GetLastError()); } - static char* str = 0; - delete[] str; - str = strcpy(new char[strlen((char*)lpMsgBuf) + 1], (char*)lpMsgBuf); - // Free the buffer. - LocalFree(lpMsgBuf); return str; } @@ -282,8 +330,10 @@ namespace KWSYS_NAMESPACE { static image_id last_dynamic_err = B_OK; DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( - const std::string& libname) + const std::string& libname, int flags) { + CHECK_OPEN_FLAGS(flags, 0, 0); + // image_id's are integers, errors are negative. Add one just in case we // get a valid image_id of zero (is that even possible?). image_id rc = load_add_on(libname.c_str()); @@ -360,8 +410,10 @@ const char* DynamicLoader::LastError() namespace KWSYS_NAMESPACE { DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( - const std::string& libname) + const std::string& libname, int flags) { + CHECK_OPEN_FLAGS(flags, 0, NULL); + char* name = (char*)calloc(1, libname.size() + 1); dld_init(program_invocation_name); strncpy(name, libname.c_str(), libname.size()); @@ -404,8 +456,10 @@ const char* DynamicLoader::LastError() namespace KWSYS_NAMESPACE { DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary( - const std::string& libname) + const std::string& libname, int flags) { + CHECK_OPEN_FLAGS(flags, 0, NULL); + return dlopen(libname.c_str(), RTLD_LAZY); } diff --git a/Source/kwsys/DynamicLoader.hxx.in b/Source/kwsys/DynamicLoader.hxx.in index 08f2790..539c742 100644 --- a/Source/kwsys/DynamicLoader.hxx.in +++ b/Source/kwsys/DynamicLoader.hxx.in @@ -66,10 +66,23 @@ public: // Return type from DynamicLoader::GetSymbolAddress. typedef void (*SymbolPointer)(); + enum OpenFlags + { + // Search for dependent libraries beside the library being loaded. + // + // This is currently only supported on Windows. + SearchBesideLibrary = 0x00000001, + + AllOpenFlags = SearchBesideLibrary + }; + /** Load a dynamic library into the current process. * The returned LibraryHandle can be used to access the symbols in the - * library. */ + * library. The optional second argument is a set of flags to use when + * opening the library. If unrecognized or unsupported flags are specified, + * the library is not opened. */ static LibraryHandle OpenLibrary(const std::string&); + static LibraryHandle OpenLibrary(const std::string&, int); /** Attempt to detach a dynamic library from the * process. A value of true is returned if it is successful. */ diff --git a/Source/kwsys/Glob.cxx b/Source/kwsys/Glob.cxx index 6952d24..829c138 100644 --- a/Source/kwsys/Glob.cxx +++ b/Source/kwsys/Glob.cxx @@ -263,7 +263,7 @@ bool Glob::RecurseDirectory(std::string::size_type start, } } else { if (!this->Internals->Expressions.empty() && - this->Internals->Expressions.rbegin()->find(fname)) { + this->Internals->Expressions.back().find(fname)) { this->AddFile(this->Internals->Files, realname); } } diff --git a/Source/kwsys/Glob.hxx.in b/Source/kwsys/Glob.hxx.in index bd4a176..4c3bde1 100644 --- a/Source/kwsys/Glob.hxx.in +++ b/Source/kwsys/Glob.hxx.in @@ -41,17 +41,9 @@ public: , content(c) { } - Message(const Message& msg) - : type(msg.type) - , content(msg.content) - { - } - Message& operator=(Message const& msg) - { - this->type = msg.type; - this->content = msg.content; - return *this; - } + ~Message() = default; + Message(const Message& msg) = default; + Message& operator=(Message const& msg) = default; }; typedef std::vector<Message> GlobMessages; diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx index f323efc..4354753 100644 --- a/Source/kwsys/SystemInformation.cxx +++ b/Source/kwsys/SystemInformation.cxx @@ -4620,7 +4620,7 @@ std::string SystemInformationImplementation::RunProcess( // Run the application kwsysProcess* gp = kwsysProcess_New(); - kwsysProcess_SetCommand(gp, &*args.begin()); + kwsysProcess_SetCommand(gp, args.data()); kwsysProcess_SetOption(gp, kwsysProcess_Option_HideWindow, 1); kwsysProcess_Execute(gp); diff --git a/Source/kwsys/SystemInformation.hxx.in b/Source/kwsys/SystemInformation.hxx.in index 9e1ce6c..5e93878 100644 --- a/Source/kwsys/SystemInformation.hxx.in +++ b/Source/kwsys/SystemInformation.hxx.in @@ -56,6 +56,9 @@ public: SystemInformation(); ~SystemInformation(); + SystemInformation(const SystemInformation&) = delete; + SystemInformation& operator=(const SystemInformation&) = delete; + const char* GetVendorString(); const char* GetVendorID(); std::string GetTypeID(); diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index d8904fe..e756cdc 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -28,6 +28,7 @@ #include <iostream> #include <set> #include <sstream> +#include <utility> #include <vector> // Work-around CMake dependency scanning limitation. This must @@ -363,10 +364,6 @@ double SystemTools::GetTime(void) #endif } -class SystemToolsTranslationMap : public std::map<std::string, std::string> -{ -}; - /* Type of character storing the environment. */ #if defined(_WIN32) typedef wchar_t envchar; @@ -416,6 +413,9 @@ public: { } ~Free() { free(const_cast<envchar*>(this->Env)); } + + Free(const Free&) = delete; + Free& operator=(const Free&) = delete; }; const envchar* Release(const envchar* env) @@ -444,15 +444,141 @@ struct SystemToolsPathCaseCmp # endif } }; +#endif -class SystemToolsPathCaseMap - : public std::map<std::string, std::string, SystemToolsPathCaseCmp> +/** + * SystemTools static variables singleton class. + */ +class SystemToolsStatic { +public: + typedef std::map<std::string, std::string> StringMap; +#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP + /** + * Path translation table from dir to refdir + * Each time 'dir' will be found it will be replace by 'refdir' + */ + StringMap TranslationMap; +#endif +#ifdef _WIN32 + static std::string GetCasePathName(std::string const& pathIn); + static std::string GetActualCaseForPathCached(std::string const& path); + static const char* GetEnvBuffered(const char* key); + std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap; + std::map<std::string, std::string> EnvMap; +#endif +#ifdef __CYGWIN__ + StringMap Cyg2Win32Map; +#endif + + /** + * Actual implementation of ReplaceString. + */ + static void ReplaceString(std::string& source, const char* replace, + size_t replaceSize, const std::string& with); + + /** + * Actual implementation of FileIsFullPath. + */ + static bool FileIsFullPath(const char*, size_t); + + /** + * Find a filename (file or directory) in the system PATH, with + * optional extra paths. + */ + static std::string FindName( + const std::string& name, + const std::vector<std::string>& path = std::vector<std::string>(), + bool no_system_path = false); }; -class SystemToolsEnvMap : public std::map<std::string, std::string> +#ifdef _WIN32 +std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) { -}; + std::string casePath; + + // First check if the file is relative. We don't fix relative paths since the + // real case depends on the root directory and the given path fragment may + // have meaning elsewhere in the project. + if (!SystemTools::FileIsFullPath(pathIn)) { + // This looks unnecessary, but it allows for the return value optimization + // since all return paths return the same local variable. + casePath = pathIn; + return casePath; + } + + std::vector<std::string> path_components; + SystemTools::SplitPath(pathIn, path_components); + + // Start with root component. + std::vector<std::string>::size_type idx = 0; + casePath = path_components[idx++]; + // make sure drive letter is always upper case + if (casePath.size() > 1 && casePath[1] == ':') { + casePath[0] = toupper(casePath[0]); + } + const char* sep = ""; + + // If network path, fill casePath with server/share so FindFirstFile + // will work after that. Maybe someday call other APIs to get + // actual case of servers and shares. + if (path_components.size() > 2 && path_components[0] == "//") { + casePath += path_components[idx++]; + casePath += "/"; + casePath += path_components[idx++]; + sep = "/"; + } + + // Convert case of all components that exist. + bool converting = true; + for (; idx < path_components.size(); idx++) { + casePath += sep; + sep = "/"; + + if (converting) { + // If path component contains wildcards, we skip matching + // because these filenames are not allowed on windows, + // and we do not want to match a different file. + if (path_components[idx].find('*') != std::string::npos || + path_components[idx].find('?') != std::string::npos) { + converting = false; + } else { + std::string test_str = casePath; + test_str += path_components[idx]; + WIN32_FIND_DATAW findData; + HANDLE hFind = + ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); + if (INVALID_HANDLE_VALUE != hFind) { + path_components[idx] = Encoding::ToNarrow(findData.cFileName); + ::FindClose(hFind); + } else { + converting = false; + } + } + } + + casePath += path_components[idx]; + } + return casePath; +} + +std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p) +{ + // Check to see if actual case has already been called + // for this path, and the result is stored in the PathCaseMap + auto& pcm = SystemTools::Statics->PathCaseMap; + { + auto itr = pcm.find(p); + if (itr != pcm.end()) { + return itr->second; + } + } + std::string casePath = SystemToolsStatic::GetCasePathName(p); + if (casePath.size() <= MAX_PATH) { + pcm[p] = casePath; + } + return casePath; +} #endif // adds the elements of the env variable path to the arg passed in @@ -473,7 +599,7 @@ void SystemTools::GetPath(std::vector<std::string>& path, const char* env) } // A hack to make the below algorithm work. - if (!pathEnv.empty() && *pathEnv.rbegin() != pathSep) { + if (!pathEnv.empty() && pathEnv.back() != pathSep) { pathEnv += pathSep; } std::string::size_type start = 0; @@ -493,30 +619,37 @@ void SystemTools::GetPath(std::vector<std::string>& path, const char* env) } } -const char* SystemTools::GetEnvImpl(const char* key) -{ - const char* v = KWSYS_NULLPTR; #if defined(_WIN32) +const char* SystemToolsStatic::GetEnvBuffered(const char* key) +{ std::string env; if (SystemTools::GetEnv(key, env)) { - std::string& menv = (*SystemTools::EnvMap)[key]; - menv = env; - v = menv.c_str(); + std::string& menv = SystemTools::Statics->EnvMap[key]; + if (menv != env) { + menv = std::move(env); + } + return menv.c_str(); } -#else - v = getenv(key); -#endif - return v; + return KWSYS_NULLPTR; } +#endif const char* SystemTools::GetEnv(const char* key) { - return SystemTools::GetEnvImpl(key); +#if defined(_WIN32) + return SystemToolsStatic::GetEnvBuffered(key); +#else + return getenv(key); +#endif } const char* SystemTools::GetEnv(const std::string& key) { - return SystemTools::GetEnvImpl(key.c_str()); +#if defined(_WIN32) + return SystemToolsStatic::GetEnvBuffered(key.c_str()); +#else + return getenv(key.c_str()); +#endif } bool SystemTools::GetEnv(const char* key, std::string& result) @@ -819,7 +952,8 @@ void SystemTools::ReplaceString(std::string& source, return; } - SystemTools::ReplaceString(source, replace.c_str(), replace.size(), with); + SystemToolsStatic::ReplaceString(source, replace.c_str(), replace.size(), + with); } void SystemTools::ReplaceString(std::string& source, const char* replace, @@ -830,12 +964,13 @@ void SystemTools::ReplaceString(std::string& source, const char* replace, return; } - SystemTools::ReplaceString(source, replace, strlen(replace), - with ? with : ""); + SystemToolsStatic::ReplaceString(source, replace, strlen(replace), + with ? with : ""); } -void SystemTools::ReplaceString(std::string& source, const char* replace, - size_t replaceSize, const std::string& with) +void SystemToolsStatic::ReplaceString(std::string& source, const char* replace, + size_t replaceSize, + const std::string& with) { const char* src = source.c_str(); char* searchPos = const_cast<char*>(strstr(src, replace)); @@ -1313,18 +1448,16 @@ int SystemTools::Stat(const std::string& path, SystemTools::Stat_t* buf) #ifdef __CYGWIN__ bool SystemTools::PathCygwinToWin32(const char* path, char* win32_path) { - SystemToolsTranslationMap::iterator i = - SystemTools::Cyg2Win32Map->find(path); - - if (i != SystemTools::Cyg2Win32Map->end()) { - strncpy(win32_path, i->second.c_str(), MAX_PATH); + auto itr = SystemTools::Statics->Cyg2Win32Map.find(path); + if (itr != SystemTools::Statics->Cyg2Win32Map.end()) { + strncpy(win32_path, itr->second.c_str(), MAX_PATH); } else { if (cygwin_conv_path(CCP_POSIX_TO_WIN_A, path, win32_path, MAX_PATH) != 0) { win32_path[0] = 0; } - SystemToolsTranslationMap::value_type entry(path, win32_path); - SystemTools::Cyg2Win32Map->insert(entry); + SystemTools::Statics->Cyg2Win32Map.insert( + SystemToolsStatic::StringMap::value_type(path, win32_path)); } return win32_path[0] != 0; } @@ -1943,7 +2076,7 @@ void SystemTools::ConvertToUnixSlashes(std::string& path) // a single / pathCString = path.c_str(); size_t size = path.size(); - if (size > 1 && *path.rbegin() == '/') { + if (size > 1 && path.back() == '/') { // if it is c:/ then do not remove the trailing slash if (!((size == 3 && pathCString[1] == ':'))) { path.resize(size - 1); @@ -2670,9 +2803,9 @@ size_t SystemTools::GetMaximumFilePathLength() * the system search path. Returns the full path to the file if it is * found. Otherwise, the empty string is returned. */ -std::string SystemTools::FindName(const std::string& name, - const std::vector<std::string>& userPaths, - bool no_system_path) +std::string SystemToolsStatic::FindName( + const std::string& name, const std::vector<std::string>& userPaths, + bool no_system_path) { // Add the system search path to our path first std::vector<std::string> path; @@ -2681,27 +2814,15 @@ std::string SystemTools::FindName(const std::string& name, SystemTools::GetPath(path); } // now add the additional paths - { - for (std::vector<std::string>::const_iterator i = userPaths.begin(); - i != userPaths.end(); ++i) { - path.push_back(*i); - } - } - // Add a trailing slash to all paths to aid the search process. - { - for (std::vector<std::string>::iterator i = path.begin(); i != path.end(); - ++i) { - std::string& p = *i; - if (p.empty() || *p.rbegin() != '/') { - p += "/"; - } - } - } + path.reserve(path.size() + userPaths.size()); + path.insert(path.end(), userPaths.begin(), userPaths.end()); // now look for the file std::string tryPath; - for (std::vector<std::string>::const_iterator p = path.begin(); - p != path.end(); ++p) { - tryPath = *p; + for (std::string const& p : path) { + tryPath = p; + if (tryPath.empty() || tryPath.back() != '/') { + tryPath += '/'; + } tryPath += name; if (SystemTools::FileExists(tryPath)) { return tryPath; @@ -2720,7 +2841,8 @@ std::string SystemTools::FindFile(const std::string& name, const std::vector<std::string>& userPaths, bool no_system_path) { - std::string tryPath = SystemTools::FindName(name, userPaths, no_system_path); + std::string tryPath = + SystemToolsStatic::FindName(name, userPaths, no_system_path); if (!tryPath.empty() && !SystemTools::FileIsDirectory(tryPath)) { return SystemTools::CollapseFullPath(tryPath); } @@ -2737,7 +2859,8 @@ std::string SystemTools::FindDirectory( const std::string& name, const std::vector<std::string>& userPaths, bool no_system_path) { - std::string tryPath = SystemTools::FindName(name, userPaths, no_system_path); + std::string tryPath = + SystemToolsStatic::FindName(name, userPaths, no_system_path); if (!tryPath.empty() && SystemTools::FileIsDirectory(tryPath)) { return SystemTools::CollapseFullPath(tryPath); } @@ -2773,14 +2896,13 @@ std::string SystemTools::FindProgram(const std::string& name, // the end of it // on windows try .com then .exe if (name.size() <= 3 || name[name.size() - 4] != '.') { - extensions.push_back(".com"); - extensions.push_back(".exe"); + extensions.emplace_back(".com"); + extensions.emplace_back(".exe"); // first try with extensions if the os supports them - for (std::vector<std::string>::iterator i = extensions.begin(); - i != extensions.end(); ++i) { + for (std::string const& ext : extensions) { tryPath = name; - tryPath += *i; + tryPath += ext; if (SystemTools::FileExists(tryPath, true)) { return SystemTools::CollapseFullPath(tryPath); } @@ -2799,43 +2921,33 @@ std::string SystemTools::FindProgram(const std::string& name, SystemTools::GetPath(path); } // now add the additional paths - { - for (std::vector<std::string>::const_iterator i = userPaths.begin(); - i != userPaths.end(); ++i) { - path.push_back(*i); - } - } + path.reserve(path.size() + userPaths.size()); + path.insert(path.end(), userPaths.begin(), userPaths.end()); // Add a trailing slash to all paths to aid the search process. - { - for (std::vector<std::string>::iterator i = path.begin(); i != path.end(); - ++i) { - std::string& p = *i; - if (p.empty() || *p.rbegin() != '/') { - p += "/"; - } + for (std::string& p : path) { + if (p.empty() || p.back() != '/') { + p += '/'; } } // Try each path - for (std::vector<std::string>::iterator p = path.begin(); p != path.end(); - ++p) { + for (std::string& p : path) { #ifdef _WIN32 // Remove double quotes from the path on windows - SystemTools::ReplaceString(*p, "\"", ""); + SystemTools::ReplaceString(p, "\"", ""); #endif #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) // first try with extensions - for (std::vector<std::string>::iterator ext = extensions.begin(); - ext != extensions.end(); ++ext) { - tryPath = *p; + for (std::string const& ext : extensions) { + tryPath = p; tryPath += name; - tryPath += *ext; + tryPath += ext; if (SystemTools::FileExists(tryPath, true)) { return SystemTools::CollapseFullPath(tryPath); } } #endif // now try it without them - tryPath = *p; + tryPath = p; tryPath += name; if (SystemTools::FileExists(tryPath, true)) { return SystemTools::CollapseFullPath(tryPath); @@ -2849,10 +2961,9 @@ std::string SystemTools::FindProgram(const std::vector<std::string>& names, const std::vector<std::string>& path, bool noSystemPath) { - for (std::vector<std::string>::const_iterator it = names.begin(); - it != names.end(); ++it) { + for (std::string const& name : names) { // Try to find the program. - std::string result = SystemTools::FindProgram(*it, path, noSystemPath); + std::string result = SystemTools::FindProgram(name, path, noSystemPath); if (!result.empty()) { return result; } @@ -2877,27 +2988,18 @@ std::string SystemTools::FindLibrary(const std::string& name, std::vector<std::string> path; SystemTools::GetPath(path); // now add the additional paths - { - for (std::vector<std::string>::const_iterator i = userPaths.begin(); - i != userPaths.end(); ++i) { - path.push_back(*i); - } - } + path.reserve(path.size() + userPaths.size()); + path.insert(path.end(), userPaths.begin(), userPaths.end()); // Add a trailing slash to all paths to aid the search process. - { - for (std::vector<std::string>::iterator i = path.begin(); i != path.end(); - ++i) { - std::string& p = *i; - if (p.empty() || *p.rbegin() != '/') { - p += "/"; - } + for (std::string& p : path) { + if (p.empty() || p.back() != '/') { + p += '/'; } } std::string tryPath; - for (std::vector<std::string>::const_iterator p = path.begin(); - p != path.end(); ++p) { + for (std::string const& p : path) { #if defined(__APPLE__) - tryPath = *p; + tryPath = p; tryPath += name; tryPath += ".framework"; if (SystemTools::FileIsDirectory(tryPath)) { @@ -2905,42 +3007,42 @@ std::string SystemTools::FindLibrary(const std::string& name, } #endif #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) - tryPath = *p; + tryPath = p; tryPath += name; tryPath += ".lib"; if (SystemTools::FileExists(tryPath, true)) { return SystemTools::CollapseFullPath(tryPath); } #else - tryPath = *p; + tryPath = p; tryPath += "lib"; tryPath += name; tryPath += ".so"; if (SystemTools::FileExists(tryPath, true)) { return SystemTools::CollapseFullPath(tryPath); } - tryPath = *p; + tryPath = p; tryPath += "lib"; tryPath += name; tryPath += ".a"; if (SystemTools::FileExists(tryPath, true)) { return SystemTools::CollapseFullPath(tryPath); } - tryPath = *p; + tryPath = p; tryPath += "lib"; tryPath += name; tryPath += ".sl"; if (SystemTools::FileExists(tryPath, true)) { return SystemTools::CollapseFullPath(tryPath); } - tryPath = *p; + tryPath = p; tryPath += "lib"; tryPath += name; tryPath += ".dylib"; if (SystemTools::FileExists(tryPath, true)) { return SystemTools::CollapseFullPath(tryPath); } - tryPath = *p; + tryPath = p; tryPath += "lib"; tryPath += name; tryPath += ".dll"; @@ -3202,9 +3304,8 @@ bool SystemTools::FindProgramPath(const char* argv0, std::string& pathOut, msg << " argv[0] = \"" << argv0 << "\"\n"; } msg << " Attempted paths:\n"; - std::vector<std::string>::iterator i; - for (i = failures.begin(); i != failures.end(); ++i) { - msg << " \"" << *i << "\"\n"; + for (std::string const& ff : failures) { + msg << " \"" << ff << "\"\n"; } errorMsg = msg.str(); return false; @@ -3218,6 +3319,7 @@ std::string SystemTools::CollapseFullPath(const std::string& in_relative) return SystemTools::CollapseFullPath(in_relative, KWSYS_NULLPTR); } +#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP void SystemTools::AddTranslationPath(const std::string& a, const std::string& b) { @@ -3234,15 +3336,16 @@ void SystemTools::AddTranslationPath(const std::string& a, if (SystemTools::FileIsFullPath(path_b) && path_b.find("..") == std::string::npos) { // Before inserting make sure path ends with '/' - if (!path_a.empty() && *path_a.rbegin() != '/') { + if (!path_a.empty() && path_a.back() != '/') { path_a += '/'; } - if (!path_b.empty() && *path_b.rbegin() != '/') { + if (!path_b.empty() && path_b.back() != '/') { path_b += '/'; } if (!(path_a == path_b)) { - SystemTools::TranslationMap->insert( - SystemToolsTranslationMap::value_type(path_a, path_b)); + SystemTools::Statics->TranslationMap.insert( + SystemToolsStatic::StringMap::value_type(std::move(path_a), + std::move(path_b))); } } } @@ -3266,22 +3369,21 @@ void SystemTools::CheckTranslationPath(std::string& path) // Always add a trailing slash before translation. It does not // matter if this adds an extra slash, but we do not want to // translate part of a directory (like the foo part of foo-dir). - path += "/"; + path += '/'; // In case a file was specified we still have to go through this: // Now convert any path found in the table back to the one desired: - std::map<std::string, std::string>::const_iterator it; - for (it = SystemTools::TranslationMap->begin(); - it != SystemTools::TranslationMap->end(); ++it) { + for (auto const& pair : SystemTools::Statics->TranslationMap) { // We need to check of the path is a substring of the other path - if (path.find(it->first) == 0) { - path = path.replace(0, it->first.size(), it->second); + if (path.find(pair.first) == 0) { + path = path.replace(0, pair.first.size(), pair.second); } } // Remove the trailing slash we added before. - path.erase(path.end() - 1, path.end()); + path.pop_back(); } +#endif static void SystemToolsAppendComponents( std::vector<std::string>& out_components, @@ -3352,6 +3454,7 @@ std::string SystemTools::CollapseFullPath(const std::string& in_path, // Transform the path back to a string. std::string newPath = SystemTools::JoinPath(out_components); +#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP // Update the translation table with this potentially new path. I am not // sure why this line is here, it seems really questionable, but yet I // would put good money that if I remove it something will break, basically @@ -3367,8 +3470,9 @@ std::string SystemTools::CollapseFullPath(const std::string& in_path, // SystemTools::AddTranslationPath(newPath, in_path); SystemTools::CheckTranslationPath(newPath); +#endif #ifdef _WIN32 - newPath = SystemTools::GetActualCaseForPathCached(newPath); + newPath = SystemTools::Statics->GetActualCaseForPathCached(newPath); SystemTools::ConvertToUnixSlashes(newPath); #endif // Return the reconstructed path. @@ -3428,130 +3532,39 @@ std::string SystemTools::RelativePath(const std::string& local, // for each entry that is not common in the local path // add a ../ to the finalpath array, this gets us out of the local // path into the remote dir - for (unsigned int i = 0; i < localSplit.size(); ++i) { - if (!localSplit[i].empty()) { - finalPath.push_back("../"); + for (std::string const& lp : localSplit) { + if (!lp.empty()) { + finalPath.emplace_back("../"); } } // for each entry that is not common in the remote path add it // to the final path. - for (std::vector<std::string>::iterator vit = remoteSplit.begin(); - vit != remoteSplit.end(); ++vit) { - if (!vit->empty()) { - finalPath.push_back(*vit); + for (std::string const& rp : remoteSplit) { + if (!rp.empty()) { + finalPath.push_back(rp); } } std::string relativePath; // result string // now turn the array of directories into a unix path by puttint / // between each entry that does not already have one - for (std::vector<std::string>::iterator vit1 = finalPath.begin(); - vit1 != finalPath.end(); ++vit1) { - if (!relativePath.empty() && *relativePath.rbegin() != '/') { - relativePath += "/"; + for (std::string const& fp : finalPath) { + if (!relativePath.empty() && relativePath.back() != '/') { + relativePath += '/'; } - relativePath += *vit1; + relativePath += fp; } return relativePath; } -#ifdef _WIN32 -static std::string GetCasePathName(std::string const& pathIn) -{ - std::string casePath; - - // First check if the file is relative. We don't fix relative paths since the - // real case depends on the root directory and the given path fragment may - // have meaning elsewhere in the project. - if (!SystemTools::FileIsFullPath(pathIn)) { - // This looks unnecessary, but it allows for the return value optimization - // since all return paths return the same local variable. - casePath = pathIn; - return casePath; - } - - std::vector<std::string> path_components; - SystemTools::SplitPath(pathIn, path_components); - - // Start with root component. - std::vector<std::string>::size_type idx = 0; - casePath = path_components[idx++]; - // make sure drive letter is always upper case - if (casePath.size() > 1 && casePath[1] == ':') { - casePath[0] = toupper(casePath[0]); - } - const char* sep = ""; - - // If network path, fill casePath with server/share so FindFirstFile - // will work after that. Maybe someday call other APIs to get - // actual case of servers and shares. - if (path_components.size() > 2 && path_components[0] == "//") { - casePath += path_components[idx++]; - casePath += "/"; - casePath += path_components[idx++]; - sep = "/"; - } - - // Convert case of all components that exist. - bool converting = true; - for (; idx < path_components.size(); idx++) { - casePath += sep; - sep = "/"; - - if (converting) { - // If path component contains wildcards, we skip matching - // because these filenames are not allowed on windows, - // and we do not want to match a different file. - if (path_components[idx].find('*') != std::string::npos || - path_components[idx].find('?') != std::string::npos) { - converting = false; - } else { - std::string test_str = casePath; - test_str += path_components[idx]; - WIN32_FIND_DATAW findData; - HANDLE hFind = - ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); - if (INVALID_HANDLE_VALUE != hFind) { - path_components[idx] = Encoding::ToNarrow(findData.cFileName); - ::FindClose(hFind); - } else { - converting = false; - } - } - } - - casePath += path_components[idx]; - } - return casePath; -} -#endif - std::string SystemTools::GetActualCaseForPath(const std::string& p) { -#ifndef _WIN32 - return p; +#ifdef _WIN32 + return SystemToolsStatic::GetCasePathName(p); #else - return GetCasePathName(p); + return p; #endif } -#ifdef _WIN32 -std::string SystemTools::GetActualCaseForPathCached(std::string const& p) -{ - // Check to see if actual case has already been called - // for this path, and the result is stored in the PathCaseMap - SystemToolsPathCaseMap::iterator i = SystemTools::PathCaseMap->find(p); - if (i != SystemTools::PathCaseMap->end()) { - return i->second; - } - std::string casePath = GetCasePathName(p); - if (casePath.size() > MAX_PATH) { - return casePath; - } - (*SystemTools::PathCaseMap)[p] = casePath; - return casePath; -} -#endif - const char* SystemTools::SplitPathRootComponent(const std::string& p, std::string* root) { @@ -3648,7 +3661,7 @@ void SystemTools::SplitPath(const std::string& p, } #endif if (!homedir.empty() && - (*homedir.rbegin() == '/' || *homedir.rbegin() == '\\')) { + (homedir.back() == '/' || homedir.back() == '\\')) { homedir.resize(homedir.size() - 1); } SystemTools::SplitPath(homedir, components); @@ -3686,8 +3699,7 @@ std::string SystemTools::JoinPath( // Construct result in a single string. std::string result; size_t len = 0; - std::vector<std::string>::const_iterator i; - for (i = first; i != last; ++i) { + for (std::vector<std::string>::const_iterator i = first; i != last; ++i) { len += 1 + i->size(); } result.reserve(len); @@ -4016,7 +4028,7 @@ bool SystemTools::LocateFileInDir(const char* filename, const char* dir, filename_dir = SystemTools::GetFilenamePath(filename_dir); filename_dir_base = SystemTools::GetFilenameName(filename_dir); #if defined(_WIN32) - if (filename_dir_base.empty() || *filename_dir_base.rbegin() == ':') + if (filename_dir_base.empty() || filename_dir_base.back() == ':') #else if (filename_dir_base.empty()) #endif @@ -4044,16 +4056,16 @@ bool SystemTools::LocateFileInDir(const char* filename, const char* dir, bool SystemTools::FileIsFullPath(const std::string& in_name) { - return SystemTools::FileIsFullPath(in_name.c_str(), in_name.size()); + return SystemToolsStatic::FileIsFullPath(in_name.c_str(), in_name.size()); } bool SystemTools::FileIsFullPath(const char* in_name) { - return SystemTools::FileIsFullPath(in_name, - in_name[0] ? (in_name[1] ? 2 : 1) : 0); + return SystemToolsStatic::FileIsFullPath( + in_name, in_name[0] ? (in_name[1] ? 2 : 1) : 0); } -bool SystemTools::FileIsFullPath(const char* in_name, size_t len) +bool SystemToolsStatic::FileIsFullPath(const char* in_name, size_t len) { #if defined(_WIN32) || defined(__CYGWIN__) // On Windows, the name must be at least two characters long. @@ -4092,7 +4104,7 @@ bool SystemTools::GetShortPath(const std::string& path, std::string& shortPath) std::string tempPath = path; // create a buffer // if the path passed in has quotes around it, first remove the quotes - if (!path.empty() && path[0] == '"' && *path.rbegin() == '"') { + if (!path.empty() && path[0] == '"' && path.back() == '"') { tempPath = path.substr(1, path.length() - 2); } @@ -4169,7 +4181,7 @@ bool SystemTools::GetLineFromStream(std::istream& is, std::string& line, bool haveData = !line.empty() || !is.eof(); if (!line.empty()) { // Avoid storing a carriage return character. - if (*line.rbegin() == '\r') { + if (line.back() == '\r') { line.resize(line.size() - 1); } @@ -4307,7 +4319,7 @@ bool SystemTools::IsSubDirectory(const std::string& cSubdir, if (subdir.size() <= dir.size() || dir.empty()) { return false; } - bool isRootPath = *dir.rbegin() == '/'; // like "/" or "C:/" + bool isRootPath = dir.back() == '/'; // like "/" or "C:/" size_t expectedSlashPosition = isRootPath ? dir.size() - 1u : dir.size(); if (subdir[expectedSlashPosition] != '/') { return false; @@ -4651,14 +4663,7 @@ bool SystemTools::ParseURL(const std::string& URL, std::string& protocol, // These must NOT be initialized. Default initialization to zero is // necessary. static unsigned int SystemToolsManagerCount; -SystemToolsTranslationMap* SystemTools::TranslationMap; -#ifdef _WIN32 -SystemToolsPathCaseMap* SystemTools::PathCaseMap; -SystemToolsEnvMap* SystemTools::EnvMap; -#endif -#ifdef __CYGWIN__ -SystemToolsTranslationMap* SystemTools::Cyg2Win32Map; -#endif +SystemToolsStatic* SystemTools::Statics; // SystemToolsManager manages the SystemTools singleton. // SystemToolsManager should be included in any translation unit @@ -4699,20 +4704,15 @@ void SystemTools::ClassInitialize() #ifdef __VMS SetVMSFeature("DECC$FILENAME_UNIX_ONLY", 1); #endif - // Allocate the translation map first. - SystemTools::TranslationMap = new SystemToolsTranslationMap; -#ifdef _WIN32 - SystemTools::PathCaseMap = new SystemToolsPathCaseMap; - SystemTools::EnvMap = new SystemToolsEnvMap; -#endif -#ifdef __CYGWIN__ - SystemTools::Cyg2Win32Map = new SystemToolsTranslationMap; -#endif + // Create statics singleton instance + SystemTools::Statics = new SystemToolsStatic; + +#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP // Add some special translation paths for unix. These are not added // for windows because drive letters need to be maintained. Also, // there are not sym-links and mount points on windows anyway. -#if !defined(_WIN32) || defined(__CYGWIN__) +# if !defined(_WIN32) || defined(__CYGWIN__) // The tmp path is frequently a logical path so always keep it: SystemTools::AddKeepPath("/tmp/"); @@ -4750,19 +4750,13 @@ void SystemTools::ClassInitialize() } } } +# endif #endif } void SystemTools::ClassFinalize() { - delete SystemTools::TranslationMap; -#ifdef _WIN32 - delete SystemTools::PathCaseMap; - delete SystemTools::EnvMap; -#endif -#ifdef __CYGWIN__ - delete SystemTools::Cyg2Win32Map; -#endif + delete SystemTools::Statics; } } // namespace KWSYS_NAMESPACE diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index 1967860..dd1266b 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in @@ -41,9 +41,7 @@ typedef @KWSYS_NAMESPACE@_VA_LIST::hack_va_list va_list; namespace @KWSYS_NAMESPACE@ { -class SystemToolsTranslationMap; -class SystemToolsPathCaseMap; -class SystemToolsEnvMap; +class SystemToolsStatic; /** \class SystemToolsManager * \brief Use to make sure SystemTools is initialized before it is used @@ -54,6 +52,9 @@ class @KWSYS_NAMESPACE@_EXPORT SystemToolsManager public: SystemToolsManager(); ~SystemToolsManager(); + + SystemToolsManager(const SystemToolsManager&) = delete; + SystemToolsManager& operator=(const SystemToolsManager&) = delete; }; // This instance will show up in any translation unit that uses @@ -890,6 +891,7 @@ public: */ static int GetTerminalWidth(); +#if @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP /** * Add an entry in the path translation table. */ @@ -906,6 +908,7 @@ public: * Update path by going through the Path Translation table; */ static void CheckTranslationPath(std::string& path); +#endif /** * Delay the execution for a specified amount of time specified @@ -964,41 +967,8 @@ private: return &SystemToolsManagerInstance; } - /** - * Actual implementation of ReplaceString. - */ - static void ReplaceString(std::string& source, const char* replace, - size_t replaceSize, const std::string& with); - - /** - * Actual implementation of FileIsFullPath. - */ - static bool FileIsFullPath(const char*, size_t); - - /** - * Find a filename (file or directory) in the system PATH, with - * optional extra paths. - */ - static std::string FindName( - const std::string& name, - const std::vector<std::string>& path = std::vector<std::string>(), - bool no_system_path = false); - - static const char* GetEnvImpl(const char* key); - - /** - * Path translation table from dir to refdir - * Each time 'dir' will be found it will be replace by 'refdir' - */ - static SystemToolsTranslationMap* TranslationMap; -#ifdef _WIN32 - static std::string GetActualCaseForPathCached(std::string const& path); - static SystemToolsPathCaseMap* PathCaseMap; - static SystemToolsEnvMap* EnvMap; -#endif -#ifdef __CYGWIN__ - static SystemToolsTranslationMap* Cyg2Win32Map; -#endif + static SystemToolsStatic* Statics; + friend class SystemToolsStatic; friend class SystemToolsManager; }; diff --git a/Source/kwsys/hashtable.hxx.in b/Source/kwsys/hashtable.hxx.in index fc0d60e..0981c66 100644 --- a/Source/kwsys/hashtable.hxx.in +++ b/Source/kwsys/hashtable.hxx.in @@ -73,7 +73,7 @@ struct _Hashtable_node void public_method_to_quiet_warning_about_all_methods_private(); private: - void operator=(_Hashtable_node<_Val> const&); // poison node assignment + void operator=(_Hashtable_node<_Val> const&) = delete; }; template <class _Val, class _Key, class _HashFcn, class _ExtractKey, diff --git a/Source/kwsys/kwsysPlatformTestsCXX.cxx b/Source/kwsys/kwsysPlatformTestsCXX.cxx index b77d729..cfd5666 100644 --- a/Source/kwsys/kwsysPlatformTestsCXX.cxx +++ b/Source/kwsys/kwsysPlatformTestsCXX.cxx @@ -136,42 +136,6 @@ int main() } #endif -#ifdef TEST_KWSYS_LFS_WORKS -/* Return 0 when LFS is available and 1 otherwise. */ -# define _LARGEFILE_SOURCE -# define _LARGEFILE64_SOURCE -# define _LARGE_FILES -# define _FILE_OFFSET_BITS 64 -# include <sys/types.h> - -# include <assert.h> -# include <sys/stat.h> -# if KWSYS_CXX_HAS_CSTDIO -# include <cstdio> -# endif -# include <stdio.h> - -int main(int, char** argv) -{ -/* check that off_t can hold 2^63 - 1 and perform basic operations... */ -# define OFF_T_64 (((off_t)1 << 62) - 1 + ((off_t)1 << 62)) - if (OFF_T_64 % 2147483647 != 1) - return 1; - - // stat breaks on SCO OpenServer - struct stat buf; - stat(argv[0], &buf); - if (!S_ISREG(buf.st_mode)) - return 2; - - FILE* file = fopen(argv[0], "r"); - off_t offset = ftello(file); - fseek(file, offset, SEEK_CUR); - fclose(file); - return 0; -} -#endif - #ifdef TEST_KWSYS_CXX_HAS_SETENV # include <stdlib.h> int main() @@ -212,12 +176,6 @@ int main() #endif #ifdef TEST_KWSYS_CXX_HAS_RLIMIT64 -# if defined(KWSYS_HAS_LFS) -# define _LARGEFILE_SOURCE -# define _LARGEFILE64_SOURCE -# define _LARGE_FILES -# define _FILE_OFFSET_BITS 64 -# endif # include <sys/resource.h> int main() { diff --git a/Source/kwsys/testDynamicLoader.cxx b/Source/kwsys/testDynamicLoader.cxx index ce87117..eff2ed7 100644 --- a/Source/kwsys/testDynamicLoader.cxx +++ b/Source/kwsys/testDynamicLoader.cxx @@ -21,11 +21,15 @@ // left on disk. #include <testSystemTools.h> -static std::string GetLibName(const char* lname) +static std::string GetLibName(const char* lname, const char* subdir = NULL) { // Construct proper name of lib std::string slname; slname = EXECUTABLE_OUTPUT_PATH; + if (subdir) { + slname += "/"; + slname += subdir; + } #ifdef CMAKE_INTDIR slname += "/"; slname += CMAKE_INTDIR; @@ -45,26 +49,29 @@ static std::string GetLibName(const char* lname) * r3: should CloseLibrary succeed ? */ static int TestDynamicLoader(const char* libname, const char* symbol, int r1, - int r2, int r3) + int r2, int r3, int flags = 0) { std::cerr << "Testing: " << libname << std::endl; kwsys::DynamicLoader::LibraryHandle l = - kwsys::DynamicLoader::OpenLibrary(libname); + kwsys::DynamicLoader::OpenLibrary(libname, flags); // If result is incompatible with expectation just fails (xor): if ((r1 && !l) || (!r1 && l)) { - std::cerr << kwsys::DynamicLoader::LastError() << std::endl; + std::cerr << "OpenLibrary: " << kwsys::DynamicLoader::LastError() + << std::endl; return 1; } kwsys::DynamicLoader::SymbolPointer f = kwsys::DynamicLoader::GetSymbolAddress(l, symbol); if ((r2 && !f) || (!r2 && f)) { - std::cerr << kwsys::DynamicLoader::LastError() << std::endl; + std::cerr << "GetSymbolAddress: " << kwsys::DynamicLoader::LastError() + << std::endl; return 1; } #ifndef __APPLE__ int s = kwsys::DynamicLoader::CloseLibrary(l); if ((r3 && !s) || (!r3 && s)) { - std::cerr << kwsys::DynamicLoader::LastError() << std::endl; + std::cerr << "CloseLibrary: " << kwsys::DynamicLoader::LastError() + << std::endl; return 1; } #else @@ -113,5 +120,14 @@ int testDynamicLoader(int argc, char* argv[]) res += TestDynamicLoader(libname.c_str(), "TestDynamicLoaderData", 1, 1, 1); res += TestDynamicLoader(libname.c_str(), "_TestDynamicLoaderData", 1, 0, 1); +#ifdef _WIN32 + libname = GetLibName(KWSYS_NAMESPACE_STRING "TestDynloadUse", "dynloaddir"); + res += TestDynamicLoader(libname.c_str(), "dummy", 0, 0, 0); + res += TestDynamicLoader(libname.c_str(), "TestLoad", 1, 1, 1, + kwsys::DynamicLoader::SearchBesideLibrary); + res += TestDynamicLoader(libname.c_str(), "_TestLoad", 1, 0, 1, + kwsys::DynamicLoader::SearchBesideLibrary); +#endif + return res; } diff --git a/Source/kwsys/testDynloadImpl.c b/Source/kwsys/testDynloadImpl.c new file mode 100644 index 0000000..2b9069b --- /dev/null +++ b/Source/kwsys/testDynloadImpl.c @@ -0,0 +1,10 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ + +#include "testDynloadImpl.h" + +int TestDynamicLoaderImplData = 0; + +void TestDynamicLoaderImplSymbolPointer() +{ +} diff --git a/Source/kwsys/testDynloadImpl.h b/Source/kwsys/testDynloadImpl.h new file mode 100644 index 0000000..d0c9dfb --- /dev/null +++ b/Source/kwsys/testDynloadImpl.h @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#ifdef _WIN32 +# ifdef BUILDING_TestDynloadImpl +# define DLIMPL_EXPORT __declspec(dllexport) +# else +# define DLIMPL_EXPORT __declspec(dllimport) +# endif +#else +# define DLIMPL_EXPORT +#endif + +DLIMPL_EXPORT int TestDynamicLoaderImplData; + +DLIMPL_EXPORT void TestDynamicLoaderImplSymbolPointer(); diff --git a/Source/kwsys/testDynloadUse.c b/Source/kwsys/testDynloadUse.c new file mode 100644 index 0000000..5402add --- /dev/null +++ b/Source/kwsys/testDynloadUse.c @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ +#include "testDynloadImpl.h" + +#ifdef _WIN32 +# define DL_EXPORT __declspec(dllexport) +#else +# define DL_EXPORT +#endif + +DL_EXPORT int TestLoad() +{ + TestDynamicLoaderImplSymbolPointer(); + return TestDynamicLoaderImplData; +} |