diff options
Diffstat (limited to 'Source')
686 files changed, 15991 insertions, 7803 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 2026ab1457..ca56d3af99 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -2,9 +2,12 @@ # file Copyright.txt or https://cmake.org/licensing for details. # To ensure maximum portability across various compilers and platforms -# deactivate any compiler extensions -set(CMAKE_C_EXTENSIONS FALSE) -set(CMAKE_CXX_EXTENSIONS FALSE) +# deactivate any compiler extensions. Skip this for QNX, where additional +# work is needed to build without compiler extensions. +if (NOT CMAKE_SYSTEM_NAME STREQUAL "QNX") + set(CMAKE_C_EXTENSIONS FALSE) + set(CMAKE_CXX_EXTENSIONS FALSE) +endif() include(CheckIncludeFile) # Check if we can build support for ELF parsing. @@ -182,6 +185,10 @@ set(SRCS cmCheckCustomOutputs.cxx cmCLocaleEnvironmentScope.h cmCLocaleEnvironmentScope.cxx + cmCMakePath.h + cmCMakePath.cxx + cmCMakePresetsFile.cxx + cmCMakePresetsFile.h cmCommandArgumentParserHelper.cxx cmCommonTargetGenerator.cxx cmCommonTargetGenerator.h @@ -336,6 +343,7 @@ set(SRCS cmInstallTargetGenerator.cxx cmInstallDirectoryGenerator.h cmInstallDirectoryGenerator.cxx + cmJSONHelpers.h cmLDConfigLDConfigTool.cxx cmLDConfigLDConfigTool.h cmLDConfigTool.cxx @@ -414,6 +422,8 @@ set(SRCS cmSourceFileLocationKind.h cmSourceGroup.cxx cmSourceGroup.h + cmStandardLevelResolver.cxx + cmStandardLevelResolver.h cmState.cxx cmState.h cmStateDirectory.cxx @@ -941,6 +951,7 @@ set(CTEST_SRCS cmCTest.cxx CTest/cmCTestResourceAllocator.cxx CTest/cmCTestResourceSpec.cxx CTest/cmCTestLaunch.cxx + CTest/cmCTestLaunchReporter.cxx CTest/cmCTestMemCheckCommand.cxx CTest/cmCTestMemCheckHandler.cxx CTest/cmCTestMultiProcessHandler.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index b77e56e545..0f9f8e8636 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) -set(CMake_VERSION_MINOR 18) -set(CMake_VERSION_PATCH 5) +set(CMake_VERSION_MINOR 19) +set(CMake_VERSION_PATCH 3) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/CPack/IFW/cmCPackIFWCommon.h b/Source/CPack/IFW/cmCPackIFWCommon.h index 354d849662..95ed21370e 100644 --- a/Source/CPack/IFW/cmCPackIFWCommon.h +++ b/Source/CPack/IFW/cmCPackIFWCommon.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackIFWCommon_h -#define cmCPackIFWCommon_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -77,5 +76,3 @@ protected: cmCPackLog_msg.str().c_str()); \ } \ } while (false) - -#endif // cmCPackIFWCommon_h diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.h b/Source/CPack/IFW/cmCPackIFWGenerator.h index 86a73c83ff..024d25d62c 100644 --- a/Source/CPack/IFW/cmCPackIFWGenerator.h +++ b/Source/CPack/IFW/cmCPackIFWGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackIFWGenerator_h -#define cmCPackIFWGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -152,5 +151,3 @@ private: std::vector<std::string> PkgsDirsVector; std::vector<std::string> RepoDirsVector; }; - -#endif diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.h b/Source/CPack/IFW/cmCPackIFWInstaller.h index 8b3f96af8e..6f398e308b 100644 --- a/Source/CPack/IFW/cmCPackIFWInstaller.h +++ b/Source/CPack/IFW/cmCPackIFWInstaller.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackIFWInstaller_h -#define cmCPackIFWInstaller_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -132,5 +131,3 @@ protected: void printSkippedOptionWarning(const std::string& optionName, const std::string& optionValue); }; - -#endif // cmCPackIFWInstaller_h diff --git a/Source/CPack/IFW/cmCPackIFWPackage.h b/Source/CPack/IFW/cmCPackIFWPackage.h index 6a4a170a5c..dbd554054a 100644 --- a/Source/CPack/IFW/cmCPackIFWPackage.h +++ b/Source/CPack/IFW/cmCPackIFWPackage.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackIFWPackage_h -#define cmCPackIFWPackage_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -149,5 +148,3 @@ public: // Patch to package directory std::string Directory; }; - -#endif // cmCPackIFWPackage_h diff --git a/Source/CPack/IFW/cmCPackIFWRepository.h b/Source/CPack/IFW/cmCPackIFWRepository.h index c29398124e..21afd8bdb1 100644 --- a/Source/CPack/IFW/cmCPackIFWRepository.h +++ b/Source/CPack/IFW/cmCPackIFWRepository.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackIFWRepository_h -#define cmCPackIFWRepository_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -84,5 +83,3 @@ public: RepositoriesVector RepositoryUpdate; std::string Directory; }; - -#endif // cmCPackIFWRepository_h diff --git a/Source/CPack/WiX/cmCMakeToWixPath.h b/Source/CPack/WiX/cmCMakeToWixPath.h index 8bb9e04088..074cc8e951 100644 --- a/Source/CPack/WiX/cmCMakeToWixPath.h +++ b/Source/CPack/WiX/cmCMakeToWixPath.h @@ -1,12 +1,9 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCMakeToWixPath_h -#define cmCMakeToWixPath_h +#pragma once #include "cmConfigure.h" //IWYU pragma: keep #include <string> std::string CMakeToWixPath(const std::string& cygpath); - -#endif // cmCMakeToWixPath_h diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx index 72af10b3cf..8b3644f910 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx @@ -221,6 +221,7 @@ bool cmCPackWIXGenerator::InitializeWiXConfiguration() this->LightExtensions.insert("WixUIExtension"); CollectExtensions("CPACK_WIX_EXTENSIONS", this->LightExtensions); CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions); + CollectXmlNamespaces("CPACK_WIX_CUSTOM_XMLNS", this->CustomXmlNamespaces); const char* patchFilePath = GetOption("CPACK_WIX_PATCH_FILE"); if (patchFilePath) { @@ -322,6 +323,7 @@ void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile() cmWIXSourceWriter includeFile(this->Logger, includeFilename, this->ComponentGuidType, cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); + InjectXmlNamespaces(includeFile); CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_GUID"); CopyDefinition(includeFile, "CPACK_WIX_UPGRADE_GUID"); @@ -345,6 +347,7 @@ void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile() cmWIXSourceWriter includeFile(this->Logger, includeFilename, this->ComponentGuidType, cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); + InjectXmlNamespaces(includeFile); std::string prefix = "CPACK_WIX_PROPERTY_"; std::vector<std::string> options = GetOptions(); @@ -393,6 +396,7 @@ void cmCPackWIXGenerator::CreateWiXProductFragmentIncludeFile() cmWIXSourceWriter includeFile(this->Logger, includeFilename, this->ComponentGuidType, cmWIXSourceWriter::INCLUDE_ELEMENT_ROOT); + InjectXmlNamespaces(includeFile); this->Patch->ApplyFragment("#PRODUCT", includeFile); } @@ -432,6 +436,7 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() cmWIXDirectoriesSourceWriter directoryDefinitions( this->Logger, directoryDefinitionsFilename, this->ComponentGuidType); + InjectXmlNamespaces(directoryDefinitions); directoryDefinitions.BeginElement("Fragment"); std::string installRoot; @@ -453,6 +458,7 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() cmWIXFilesSourceWriter fileDefinitions(this->Logger, fileDefinitionsFilename, this->ComponentGuidType); + InjectXmlNamespaces(fileDefinitions); fileDefinitions.BeginElement("Fragment"); @@ -463,6 +469,7 @@ bool cmCPackWIXGenerator::CreateWiXSourceFiles() cmWIXFeaturesSourceWriter featureDefinitions( this->Logger, featureDefinitionsFilename, this->ComponentGuidType); + InjectXmlNamespaces(featureDefinitions); featureDefinitions.BeginElement("Fragment"); @@ -1147,6 +1154,35 @@ void cmCPackWIXGenerator::CollectExtensions(std::string const& variableName, extensions.insert(list.begin(), list.end()); } +void cmCPackWIXGenerator::CollectXmlNamespaces(std::string const& variableName, + xmlns_map_t& namespaces) +{ + const char* variableContent = GetOption(variableName.c_str()); + if (!variableContent) { + return; + } + + std::vector<std::string> list = cmExpandedList(variableContent); + for (std::string const& str : list) { + auto pos = str.find('='); + if (pos != std::string::npos) { + auto name = str.substr(0, pos); + auto value = str.substr(pos + 1); + namespaces.emplace(std::make_pair(name, value)); + } else { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Invalid element in CPACK_WIX_CUSTOM_XMLNS ignored: " + << "\"" << str << "\"" << std::endl); + } + } + std::ostringstream oss; + for (auto& ns : namespaces) { + oss << " xmlns:" << ns.first << "=\"" + << cmWIXSourceWriter::EscapeAttributeValue(ns.second) << '"'; + } + SetOption("CPACK_WIX_CUSTOM_XMLNS_EXPANDED", oss.str().c_str()); +} + void cmCPackWIXGenerator::AddCustomFlags(std::string const& variableName, std::ostream& stream) { @@ -1172,3 +1208,10 @@ std::string cmCPackWIXGenerator::RelativePathWithoutComponentPrefix( return path.substr(pos + 1); } + +void cmCPackWIXGenerator::InjectXmlNamespaces(cmWIXSourceWriter& sourceWriter) +{ + for (auto& ns : this->CustomXmlNamespaces) { + sourceWriter.AddAttributeUnlessEmpty("xmlns:" + ns.first, ns.second); + } +} diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h index d5a16ec053..8609cf3607 100644 --- a/Source/CPack/WiX/cmCPackWIXGenerator.h +++ b/Source/CPack/WiX/cmCPackWIXGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackWIXGenerator_h -#define cmCPackWIXGenerator_h +#pragma once #include <map> #include <memory> @@ -49,6 +48,7 @@ private: using id_map_t = std::map<std::string, std::string>; using ambiguity_map_t = std::map<std::string, size_t>; using extension_set_t = std::set<std::string>; + using xmlns_map_t = std::map<std::string, std::string>; enum class DefinitionType { @@ -147,16 +147,22 @@ private: void CollectExtensions(std::string const& variableName, extension_set_t& extensions); + void CollectXmlNamespaces(std::string const& variableName, + xmlns_map_t& namespaces); + void AddCustomFlags(std::string const& variableName, std::ostream& stream); std::string RelativePathWithoutComponentPrefix(std::string const& path); + void InjectXmlNamespaces(cmWIXSourceWriter& sourceWriter); + std::vector<std::string> WixSources; id_map_t PathToIdMap; ambiguity_map_t IdAmbiguityCounter; extension_set_t CandleExtensions; extension_set_t LightExtensions; + xmlns_map_t CustomXmlNamespaces; std::string CPackTopLevel; @@ -164,5 +170,3 @@ private: cmWIXSourceWriter::GuidType ComponentGuidType; }; - -#endif diff --git a/Source/CPack/WiX/cmWIXAccessControlList.h b/Source/CPack/WiX/cmWIXAccessControlList.h index 64f9a13828..ee5efa5296 100644 --- a/Source/CPack/WiX/cmWIXAccessControlList.h +++ b/Source/CPack/WiX/cmWIXAccessControlList.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWIXAccessControlList_h -#define cmWIXAccessControlList_h +#pragma once #include "cmCPackLog.h" #include "cmInstalledFile.h" @@ -29,5 +28,3 @@ private: cmInstalledFile const& InstalledFile; cmWIXSourceWriter& SourceWriter; }; - -#endif diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h index a907d6d181..0af3094c70 100644 --- a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h +++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWIXDirectoriesSourceWriter_h -#define cmWIXDirectoriesSourceWriter_h +#pragma once #include <string> @@ -29,5 +28,3 @@ public: void EndInstallationPrefixDirectory(size_t size); }; - -#endif diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h index e03e87bada..0facf97a0e 100644 --- a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h +++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWIXFeaturesSourceWriter_h -#define cmWIXFeaturesSourceWriter_h +#pragma once #include "cmCPackGenerator.h" #include "cmWIXPatch.h" @@ -27,5 +26,3 @@ public: void EmitComponentRef(std::string const& id); }; - -#endif diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.h b/Source/CPack/WiX/cmWIXFilesSourceWriter.h index 8cc98f52b3..60dddd4cf8 100644 --- a/Source/CPack/WiX/cmWIXFilesSourceWriter.h +++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWIXFilesSourceWriter_h -#define cmWIXFilesSourceWriter_h +#pragma once #include "cmCPackGenerator.h" #include "cmWIXPatch.h" @@ -37,5 +36,3 @@ public: std::string const& filePath, cmWIXPatch& patch, cmInstalledFile const* installedFile); }; - -#endif diff --git a/Source/CPack/WiX/cmWIXPatch.h b/Source/CPack/WiX/cmWIXPatch.h index 31a60f4e82..c78722dac0 100644 --- a/Source/CPack/WiX/cmWIXPatch.h +++ b/Source/CPack/WiX/cmWIXPatch.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWIXPatch_h -#define cmWIXPatch_h +#pragma once #include <string> @@ -33,5 +32,3 @@ private: cmWIXPatchParser::fragment_map_t Fragments; }; - -#endif diff --git a/Source/CPack/WiX/cmWIXPatchParser.h b/Source/CPack/WiX/cmWIXPatchParser.h index 8d5d2adc44..70a21bc14b 100644 --- a/Source/CPack/WiX/cmWIXPatchParser.h +++ b/Source/CPack/WiX/cmWIXPatchParser.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackWIXPatchParser_h -#define cmCPackWIXPatchParser_h +#pragma once #include <map> #include <memory> @@ -91,5 +90,3 @@ private: std::vector<cmWIXPatchElement*> ElementStack; }; - -#endif diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.h b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h index a879f3da93..99471f142f 100644 --- a/Source/CPack/WiX/cmWIXRichTextFormatWriter.h +++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWIXRichTextFormatWriter_h -#define cmWIXRichTextFormatWriter_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -42,5 +41,3 @@ private: cmsys::ofstream File; }; - -#endif diff --git a/Source/CPack/WiX/cmWIXShortcut.h b/Source/CPack/WiX/cmWIXShortcut.h index c67baf3697..315b5ea15c 100644 --- a/Source/CPack/WiX/cmWIXShortcut.h +++ b/Source/CPack/WiX/cmWIXShortcut.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWIXShortcut_h -#define cmWIXShortcut_h +#pragma once #include <map> #include <set> @@ -56,5 +55,3 @@ private: shortcut_type_map_t Shortcuts; shortcut_id_map_t EmptyIdMap; }; - -#endif diff --git a/Source/CPack/WiX/cmWIXSourceWriter.h b/Source/CPack/WiX/cmWIXSourceWriter.h index 8cc2070a83..f643acdf94 100644 --- a/Source/CPack/WiX/cmWIXSourceWriter.h +++ b/Source/CPack/WiX/cmWIXSourceWriter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWIXSourceWriter_h -#define cmWIXSourceWriter_h +#pragma once #include <string> #include <vector> @@ -50,6 +49,8 @@ public: std::string CreateGuidFromComponentId(std::string const& componentId); + static std::string EscapeAttributeValue(std::string const& value); + protected: cmCPackLog* Logger; @@ -64,8 +65,6 @@ private: void Indent(size_t count); - static std::string EscapeAttributeValue(std::string const& value); - cmsys::ofstream File; State State; @@ -76,5 +75,3 @@ private: GuidType ComponentGuidType; }; - -#endif diff --git a/Source/CPack/cmCPackArchiveGenerator.h b/Source/CPack/cmCPackArchiveGenerator.h index 7eb5665970..5b40013b4a 100644 --- a/Source/CPack/cmCPackArchiveGenerator.h +++ b/Source/CPack/cmCPackArchiveGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackArchiveGenerator_h -#define cmCPackArchiveGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -93,5 +92,3 @@ private: std::string ArchiveFormat; std::string OutputExtension; }; - -#endif diff --git a/Source/CPack/cmCPackBundleGenerator.h b/Source/CPack/cmCPackBundleGenerator.h index 27bac3a5ac..072d14f857 100644 --- a/Source/CPack/cmCPackBundleGenerator.h +++ b/Source/CPack/cmCPackBundleGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackBundleGenerator_h -#define cmCPackBundleGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -33,5 +32,3 @@ protected: std::string InstallPrefix; }; - -#endif diff --git a/Source/CPack/cmCPackComponentGroup.h b/Source/CPack/cmCPackComponentGroup.h index bb980d77fa..58377d4127 100644 --- a/Source/CPack/cmCPackComponentGroup.h +++ b/Source/CPack/cmCPackComponentGroup.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackComponentGroup_h -#define cmCPackComponentGroup_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -167,5 +166,3 @@ public: /// The list of components. std::vector<cmCPackComponent*> Components; }; - -#endif diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.h b/Source/CPack/cmCPackCygwinBinaryGenerator.h index 47bd41e139..f5f77005d2 100644 --- a/Source/CPack/cmCPackCygwinBinaryGenerator.h +++ b/Source/CPack/cmCPackCygwinBinaryGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackCygwinBinaryGenerator_h -#define cmCPackCygwinBinaryGenerator_h +#pragma once #include "cmCPackArchiveGenerator.h" @@ -25,5 +24,3 @@ protected: virtual const char* GetOutputExtension(); std::string OutputExtension; }; - -#endif diff --git a/Source/CPack/cmCPackCygwinSourceGenerator.h b/Source/CPack/cmCPackCygwinSourceGenerator.h index 98d8f0ab71..964a4d4f75 100644 --- a/Source/CPack/cmCPackCygwinSourceGenerator.h +++ b/Source/CPack/cmCPackCygwinSourceGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackCygwinSourceGenerator_h -#define cmCPackCygwinSourceGenerator_h +#pragma once #include "cmCPackArchiveGenerator.h" @@ -27,5 +26,3 @@ protected: std::string InstallPrefix; std::string OutputExtension; }; - -#endif diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx index 3d5fe6b9de..560e5c1dc5 100644 --- a/Source/CPack/cmCPackDebGenerator.cxx +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -696,57 +696,57 @@ int cmCPackDebGenerator::createDeb() const char* debian_pkg_source = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE"); - if (debian_pkg_source && *debian_pkg_source) { + if (cmNonempty(debian_pkg_source)) { controlValues["Source"] = debian_pkg_source; } const char* debian_pkg_dep = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DEPENDS"); - if (debian_pkg_dep && *debian_pkg_dep) { + if (cmNonempty(debian_pkg_dep)) { controlValues["Depends"] = debian_pkg_dep; } const char* debian_pkg_rec = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS"); - if (debian_pkg_rec && *debian_pkg_rec) { + if (cmNonempty(debian_pkg_rec)) { controlValues["Recommends"] = debian_pkg_rec; } const char* debian_pkg_sug = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS"); - if (debian_pkg_sug && *debian_pkg_sug) { + if (cmNonempty(debian_pkg_sug)) { controlValues["Suggests"] = debian_pkg_sug; } const char* debian_pkg_url = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_HOMEPAGE"); - if (debian_pkg_url && *debian_pkg_url) { + if (cmNonempty(debian_pkg_url)) { controlValues["Homepage"] = debian_pkg_url; } const char* debian_pkg_predep = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PREDEPENDS"); - if (debian_pkg_predep && *debian_pkg_predep) { + if (cmNonempty(debian_pkg_predep)) { controlValues["Pre-Depends"] = debian_pkg_predep; } const char* debian_pkg_enhances = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ENHANCES"); - if (debian_pkg_enhances && *debian_pkg_enhances) { + if (cmNonempty(debian_pkg_enhances)) { controlValues["Enhances"] = debian_pkg_enhances; } const char* debian_pkg_breaks = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_BREAKS"); - if (debian_pkg_breaks && *debian_pkg_breaks) { + if (cmNonempty(debian_pkg_breaks)) { controlValues["Breaks"] = debian_pkg_breaks; } const char* debian_pkg_conflicts = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONFLICTS"); - if (debian_pkg_conflicts && *debian_pkg_conflicts) { + if (cmNonempty(debian_pkg_conflicts)) { controlValues["Conflicts"] = debian_pkg_conflicts; } const char* debian_pkg_provides = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PROVIDES"); - if (debian_pkg_provides && *debian_pkg_provides) { + if (cmNonempty(debian_pkg_provides)) { controlValues["Provides"] = debian_pkg_provides; } const char* debian_pkg_replaces = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_REPLACES"); - if (debian_pkg_replaces && *debian_pkg_replaces) { + if (cmNonempty(debian_pkg_replaces)) { controlValues["Replaces"] = debian_pkg_replaces; } @@ -756,7 +756,7 @@ int cmCPackDebGenerator::createDeb() const char* debian_pkg_shlibs = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SHLIBS"); const bool gen_shibs = this->IsOn("CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS") && - debian_pkg_shlibs && *debian_pkg_shlibs; + cmNonempty(debian_pkg_shlibs); if (gen_shibs) { cmGeneratedFileStream out; out.Open(shlibsfilename, false, true); @@ -832,11 +832,11 @@ int cmCPackDebGenerator::createDbgsymDDeb() const char* debian_pkg_source = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE"); - if (debian_pkg_source && *debian_pkg_source) { + if (cmNonempty(debian_pkg_source)) { controlValues["Source"] = debian_pkg_source; } const char* debian_build_ids = this->GetOption("GEN_BUILD_IDS"); - if (debian_build_ids && *debian_build_ids) { + if (cmNonempty(debian_build_ids)) { controlValues["Build-Ids"] = debian_build_ids; } diff --git a/Source/CPack/cmCPackDebGenerator.h b/Source/CPack/cmCPackDebGenerator.h index ce77e08044..ee8f39af6e 100644 --- a/Source/CPack/cmCPackDebGenerator.h +++ b/Source/CPack/cmCPackDebGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackDebGenerator_h -#define cmCPackDebGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -69,5 +68,3 @@ private: std::vector<std::string> packageFiles; }; - -#endif diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx index fe7abf4223..cefb906bb6 100644 --- a/Source/CPack/cmCPackDragNDropGenerator.cxx +++ b/Source/CPack/cmCPackDragNDropGenerator.cxx @@ -8,7 +8,9 @@ #include <map> #include <CoreFoundation/CoreFoundation.h> +#include <cm3p/kwiml/abi.h> +#include "cmsys/Base64.h" #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" @@ -18,6 +20,7 @@ #include "cmGeneratedFileStream.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmXMLWriter.h" #ifdef HAVE_CoreServices // For the old LocaleStringToLangAndRegionCodes() function, to convert @@ -26,36 +29,28 @@ # include <CoreServices/CoreServices.h> #endif -static const char* SLAHeader = - "data 'LPic' (5000) {\n" - " $\"0002 0011 0003 0001 0000 0000 0002 0000\"\n" - " $\"0008 0003 0000 0001 0004 0000 0004 0005\"\n" - " $\"0000 000E 0006 0001 0005 0007 0000 0007\"\n" - " $\"0008 0000 0047 0009 0000 0034 000A 0001\"\n" - " $\"0035 000B 0001 0020 000C 0000 0011 000D\"\n" - " $\"0000 005B 0004 0000 0033 000F 0001 000C\"\n" - " $\"0010 0000 000B 000E 0000\"\n" - "};\n" - "\n"; - -static const char* SLASTREnglish = - "resource 'STR#' (5002, \"English\") {\n" - " {\n" - " \"English\",\n" - " \"Agree\",\n" - " \"Disagree\",\n" - " \"Print\",\n" - " \"Save...\",\n" - " \"You agree to the License Agreement terms when you click \"\n" - " \"the \\\"Agree\\\" button.\",\n" - " \"Software License Agreement\",\n" - " \"This text cannot be saved. This disk may be full or locked, " - "or the \"\n" - " \"file may be locked.\",\n" - " \"Unable to print. Make sure you have selected a printer.\"\n" - " }\n" - "};\n" - "\n"; +static const uint16_t DefaultLpic[] = { + /* clang-format off */ + 0x0002, 0x0011, 0x0003, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0008, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0005, + 0x0000, 0x000E, 0x0006, 0x0001, 0x0005, 0x0007, 0x0000, 0x0007, + 0x0008, 0x0000, 0x0047, 0x0009, 0x0000, 0x0034, 0x000A, 0x0001, + 0x0035, 0x000B, 0x0001, 0x0020, 0x000C, 0x0000, 0x0011, 0x000D, + 0x0000, 0x005B, 0x0004, 0x0000, 0x0033, 0x000F, 0x0001, 0x000C, + 0x0010, 0x0000, 0x000B, 0x000E, 0x0000 + /* clang-format on */ +}; + +static const std::vector<std::string> DefaultMenu = { + { "English", "Agree", "Disagree", "Print", "Save...", + // NOLINTNEXTLINE(bugprone-suspicious-missing-comma) + "You agree to the License Agreement terms when " + "you click the \"Agree\" button.", + "Software License Agreement", + "This text cannot be saved. " + "This disk may be full or locked, or the file may be locked.", + "Unable to print. Make sure you have selected a printer." } +}; cmCPackDragNDropGenerator::cmCPackDragNDropGenerator() : singleLicense(false) @@ -523,22 +518,43 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, } } + // Create the final compressed read-only disk image ... + std::ostringstream final_image_command; + final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + final_image_command << " convert \"" << temp_image << "\""; + final_image_command << " -format "; + final_image_command << cpack_dmg_format; + final_image_command << " -imagekey"; + final_image_command << " zlib-level=9"; + final_image_command << " -o \"" << output_file << "\""; + + std::string convert_error; + + if (!this->RunCommand(final_image_command, &convert_error)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error compressing disk image." << std::endl + << convert_error + << std::endl); + + return 0; + } + if (!cpack_license_file.empty() || !slaDirectory.empty()) { // Use old hardcoded style if sla_dir is not set bool oldStyle = slaDirectory.empty(); - std::string sla_r = - cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/sla.r"); + std::string sla_xml = + cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/sla.xml"); std::vector<std::string> languages; if (!oldStyle) { cmExpandList(cpack_dmg_languages, languages); } - cmGeneratedFileStream ofs(sla_r); - ofs << "#include <CoreServices/CoreServices.r>\n\n"; + std::vector<uint16_t> header_data; if (oldStyle) { - ofs << SLAHeader; - ofs << "\n"; + header_data = std::vector<uint16_t>( + DefaultLpic, + DefaultLpic + (sizeof(DefaultLpic) / sizeof(*DefaultLpic))); } else { /* * LPic Layout @@ -558,8 +574,6 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, * } */ - // Create vector first for readability, then iterate to write to ofs - std::vector<uint16_t> header_data; header_data.push_back(0); header_data.push_back(languages.size()); for (size_t i = 0; i < languages.size(); ++i) { @@ -596,52 +610,50 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, header_data.push_back(0); #endif } - ofs << "data 'LPic' (5000) {\n"; - ofs << std::hex << std::uppercase << std::setfill('0'); - - for (size_t i = 0; i < header_data.size(); ++i) { - if (i % 8 == 0) { - ofs << " $\""; - } - - ofs << std::setw(4) << header_data[i]; + } - if (i % 8 == 7 || i == header_data.size() - 1) { - ofs << "\"\n"; - } else { - ofs << " "; - } + RezDoc rez; + + { + RezDict lpic = { {}, 5000, {} }; + lpic.Data.reserve(header_data.size() * sizeof(header_data[0])); + for (uint16_t x : header_data) { + // LPic header is big-endian. + char* d = reinterpret_cast<char*>(&x); +#if KWIML_ABI_ENDIAN_ID == KWIML_ABI_ENDIAN_ID_LITTLE + lpic.Data.push_back(d[1]); + lpic.Data.push_back(d[0]); +#else + lpic.Data.push_back(d[0]); + lpic.Data.push_back(d[1]); +#endif } - ofs << "};\n\n"; - // Reset ofs options - ofs << std::dec << std::nouppercase << std::setfill(' '); + rez.LPic.Entries.emplace_back(std::move(lpic)); } bool have_write_license_error = false; std::string error; if (oldStyle) { - if (!this->WriteLicense(ofs, 0, "", cpack_license_file, &error)) { + if (!this->WriteLicense(rez, 0, "", cpack_license_file, &error)) { have_write_license_error = true; } } else { for (size_t i = 0; i < languages.size() && !have_write_license_error; ++i) { if (singleLicense) { - if (!this->WriteLicense(ofs, i + 5000, languages[i], + if (!this->WriteLicense(rez, i + 5000, languages[i], cpack_license_file, &error)) { have_write_license_error = true; } } else { - if (!this->WriteLicense(ofs, i + 5000, languages[i], "", &error)) { + if (!this->WriteLicense(rez, i + 5000, languages[i], "", &error)) { have_write_license_error = true; } } } } - ofs.Close(); - if (have_write_license_error) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error writing license file to SLA." << std::endl @@ -650,96 +662,27 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, return 0; } - if (temp_image_format != "UDZO") { - temp_image_format = "UDZO"; - // convert to UDZO to enable unflatten/flatten - std::string temp_udzo = cmStrCat( - this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/temp-udzo.dmg"); + this->WriteRezXML(sla_xml, rez); - std::ostringstream udco_image_command; - udco_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); - udco_image_command << " convert \"" << temp_image << "\""; - udco_image_command << " -format UDZO"; - udco_image_command << " -ov -o \"" << temp_udzo << "\""; - - if (!this->RunCommand(udco_image_command, &error)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error converting to UDCO dmg for adding SLA." - << std::endl - << error << std::endl); - return 0; - } - temp_image = temp_udzo; - } - - // unflatten dmg - std::ostringstream unflatten_command; - unflatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); - unflatten_command << " unflatten "; - unflatten_command << "\"" << temp_image << "\""; - - if (!this->RunCommand(unflatten_command, &error)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error unflattening dmg for adding SLA." << std::endl - << error - << std::endl); - return 0; - } - - // Rez the SLA + // Create the final compressed read-only disk image ... std::ostringstream embed_sla_command; - embed_sla_command << this->GetOption("CPACK_COMMAND_REZ"); - const char* sysroot = this->GetOption("CPACK_OSX_SYSROOT"); - if (sysroot && sysroot[0] != '\0') { - embed_sla_command << " -isysroot \"" << sysroot << "\""; - } - embed_sla_command << " \"" << sla_r << "\""; - embed_sla_command << " -a -o "; - embed_sla_command << "\"" << temp_image << "\""; - - if (!this->RunCommand(embed_sla_command, &error)) { + embed_sla_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + embed_sla_command << " udifrez"; + embed_sla_command << " -xml"; + embed_sla_command << " \"" << sla_xml << "\""; + embed_sla_command << " FIXME_WHY_IS_THIS_ARGUMENT_NEEDED"; + embed_sla_command << " \"" << output_file << "\""; + std::string embed_error; + if (!this->RunCommand(embed_sla_command, &embed_error)) { cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error adding SLA." << std::endl - << error << std::endl); - return 0; - } - - // flatten dmg - std::ostringstream flatten_command; - flatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); - flatten_command << " flatten "; - flatten_command << "\"" << temp_image << "\""; + "Error compressing disk image." << std::endl + << embed_error + << std::endl); - if (!this->RunCommand(flatten_command, &error)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error flattening dmg for adding SLA." << std::endl - << error - << std::endl); return 0; } } - // Create the final compressed read-only disk image ... - std::ostringstream final_image_command; - final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); - final_image_command << " convert \"" << temp_image << "\""; - final_image_command << " -format "; - final_image_command << cpack_dmg_format; - final_image_command << " -imagekey"; - final_image_command << " zlib-level=9"; - final_image_command << " -o \"" << output_file << "\""; - - std::string convert_error; - - if (!this->RunCommand(final_image_command, &convert_error)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Error compressing disk image." << std::endl - << convert_error - << std::endl); - - return 0; - } - return 1; } @@ -788,10 +731,67 @@ std::string cmCPackDragNDropGenerator::GetComponentInstallDirNameSuffix( return GetComponentPackageFileName(package_file_name, componentName, false); } -bool cmCPackDragNDropGenerator::WriteLicense( - cmGeneratedFileStream& outputStream, int licenseNumber, - std::string licenseLanguage, const std::string& licenseFile, - std::string* error) +void cmCPackDragNDropGenerator::WriteRezXML(std::string const& file, + RezDoc const& rez) +{ + cmGeneratedFileStream fxml(file); + cmXMLWriter xml(fxml); + xml.StartDocument(); + xml.StartElement("plist"); + xml.Attribute("version", "1.0"); + xml.StartElement("dict"); + this->WriteRezArray(xml, rez.LPic); + this->WriteRezArray(xml, rez.Menu); + this->WriteRezArray(xml, rez.Text); + this->WriteRezArray(xml, rez.RTF); + xml.EndElement(); // dict + xml.EndElement(); // plist + xml.EndDocument(); + fxml.Close(); +} + +void cmCPackDragNDropGenerator::WriteRezArray(cmXMLWriter& xml, + RezArray const& array) +{ + if (array.Entries.empty()) { + return; + } + xml.StartElement("key"); + xml.Content(array.Key); + xml.EndElement(); // key + xml.StartElement("array"); + for (RezDict const& dict : array.Entries) { + this->WriteRezDict(xml, dict); + } + xml.EndElement(); // array +} + +void cmCPackDragNDropGenerator::WriteRezDict(cmXMLWriter& xml, + RezDict const& dict) +{ + std::vector<char> base64buf(dict.Data.size() * 3 / 2 + 5); + size_t base64len = + cmsysBase64_Encode(dict.Data.data(), dict.Data.size(), + reinterpret_cast<unsigned char*>(base64buf.data()), 0); + std::string base64data(base64buf.data(), base64len); + /* clang-format off */ + xml.StartElement("dict"); + xml.StartElement("key"); xml.Content("Attributes"); xml.EndElement(); + xml.StartElement("string"); xml.Content("0x0000"); xml.EndElement(); + xml.StartElement("key"); xml.Content("Data"); xml.EndElement(); + xml.StartElement("data"); xml.Content(base64data); xml.EndElement(); + xml.StartElement("key"); xml.Content("ID"); xml.EndElement(); + xml.StartElement("string"); xml.Content(dict.ID); xml.EndElement(); + xml.StartElement("key"); xml.Content("Name"); xml.EndElement(); + xml.StartElement("string"); xml.Content(dict.Name); xml.EndElement(); + xml.EndElement(); // dict + /* clang-format on */ +} + +bool cmCPackDragNDropGenerator::WriteLicense(RezDoc& rez, size_t licenseNumber, + std::string licenseLanguage, + const std::string& licenseFile, + std::string* error) { if (!licenseFile.empty() && !singleLicense) { licenseNumber = 5002; @@ -799,11 +799,11 @@ bool cmCPackDragNDropGenerator::WriteLicense( } // License file - std::string license_format = "TEXT"; + RezArray* licenseArray = &rez.Text; std::string actual_license; if (!licenseFile.empty()) { if (cmHasLiteralSuffix(licenseFile, ".rtf")) { - license_format = "RTF "; + licenseArray = &rez.RTF; } actual_license = licenseFile; } else { @@ -812,85 +812,86 @@ bool cmCPackDragNDropGenerator::WriteLicense( if (cmSystemTools::FileExists(license_wo_ext + ".txt")) { actual_license = license_wo_ext + ".txt"; } else { - license_format = "RTF "; + licenseArray = &rez.RTF; actual_license = license_wo_ext + ".rtf"; } } - // License header - outputStream << "data '" << license_format << "' (" << licenseNumber - << ", \"" << licenseLanguage << "\") {\n"; // License body - cmsys::ifstream license_ifs; - license_ifs.open(actual_license.c_str()); - if (license_ifs.is_open()) { - while (license_ifs.good()) { - std::string line; - std::getline(license_ifs, line); - if (!line.empty()) { - EscapeQuotesAndBackslashes(line); - std::vector<std::string> lines; - if (!this->BreakLongLine(line, lines, error)) { - return false; - } - for (auto const& l : lines) { - outputStream << " \"" << l << "\"\n"; - } - } - outputStream << " \"\\n\"\n"; + { + RezDict license = { licenseLanguage, licenseNumber, {} }; + std::vector<std::string> lines; + if (!this->ReadFile(actual_license, lines, error)) { + return false; } - license_ifs.close(); + this->EncodeLicense(license, lines); + licenseArray->Entries.emplace_back(std::move(license)); } - // End of License - outputStream << "};\n\n"; - if (!licenseFile.empty() && !singleLicense) { - outputStream << SLASTREnglish; - } else { - // Menu header - outputStream << "resource 'STR#' (" << licenseNumber << ", \"" - << licenseLanguage << "\") {\n"; - outputStream << " {\n"; - - // Menu body - cmsys::ifstream menu_ifs; - menu_ifs.open( - (slaDirectory + "/" + licenseLanguage + ".menu.txt").c_str()); - if (menu_ifs.is_open()) { - size_t lines_written = 0; - while (menu_ifs.good()) { - // Lines written from original file, not from broken up lines - std::string line; - std::getline(menu_ifs, line); - if (!line.empty()) { - EscapeQuotesAndBackslashes(line); - std::vector<std::string> lines; - if (!this->BreakLongLine(line, lines, error)) { - return false; - } - for (size_t i = 0; i < lines.size(); ++i) { - std::string comma; - // We need a comma after every complete string, - // but not on the very last line - if (lines_written != 8 && i == lines.size() - 1) { - comma = ","; - } else { - comma = ""; - } - outputStream << " \"" << lines[i] << "\"" << comma << "\n"; - } - ++lines_written; - } + // Menu body + { + RezDict menu = { licenseLanguage, licenseNumber, {} }; + if (!licenseFile.empty() && !singleLicense) { + this->EncodeMenu(menu, DefaultMenu); + } else { + std::vector<std::string> lines; + std::string actual_menu = + slaDirectory + "/" + licenseLanguage + ".menu.txt"; + if (!this->ReadFile(actual_menu, lines, error)) { + return false; } - menu_ifs.close(); + this->EncodeMenu(menu, lines); } + rez.Menu.Entries.emplace_back(std::move(menu)); + } + + return true; +} + +void cmCPackDragNDropGenerator::EncodeLicense( + RezDict& dict, std::vector<std::string> const& lines) +{ + // License text uses CR newlines. + for (std::string const& l : lines) { + dict.Data.insert(dict.Data.end(), l.begin(), l.end()); + dict.Data.push_back('\r'); + } + dict.Data.push_back('\r'); +} - // End of menu - outputStream << " }\n"; - outputStream << "};\n"; - outputStream << "\n"; +void cmCPackDragNDropGenerator::EncodeMenu( + RezDict& dict, std::vector<std::string> const& lines) +{ + // Menu resources start with a big-endian uint16_t for number of lines: + { + uint16_t numLines = static_cast<uint16_t>(lines.size()); + char* d = reinterpret_cast<char*>(&numLines); +#if KWIML_ABI_ENDIAN_ID == KWIML_ABI_ENDIAN_ID_LITTLE + dict.Data.push_back(d[1]); + dict.Data.push_back(d[0]); +#else + dict.Data.push_back(d[0]); + dict.Data.push_back(d[1]); +#endif } + // Each line starts with a uint8_t length, plus the bytes themselves: + for (std::string const& l : lines) { + dict.Data.push_back(static_cast<unsigned char>(l.length())); + dict.Data.insert(dict.Data.end(), l.begin(), l.end()); + } +} +bool cmCPackDragNDropGenerator::ReadFile(std::string const& file, + std::vector<std::string>& lines, + std::string* error) +{ + cmsys::ifstream ifs(file); + std::string line; + while (std::getline(ifs, line)) { + if (!this->BreakLongLine(line, lines, error)) { + return false; + } + } return true; } @@ -898,7 +899,7 @@ bool cmCPackDragNDropGenerator::BreakLongLine(const std::string& line, std::vector<std::string>& lines, std::string* error) { - const size_t max_line_length = 512; + const size_t max_line_length = 255; size_t line_length = max_line_length; for (size_t i = 0; i < line.size(); i += line_length) { line_length = max_line_length; @@ -913,25 +914,10 @@ bool cmCPackDragNDropGenerator::BreakLongLine(const std::string& line, if (line_length == 0) { *error = "Please make sure there are no words " "(or character sequences not broken up by spaces or newlines) " - "in your license file which are more than 512 characters long."; + "in your license file which are more than 255 characters long."; return false; } lines.push_back(line.substr(i, line_length)); } return true; } - -void cmCPackDragNDropGenerator::EscapeQuotesAndBackslashes(std::string& line) -{ - std::string::size_type backslash_pos = line.find('\\'); - while (backslash_pos != std::string::npos) { - line.replace(backslash_pos, 1, "\\\\"); - backslash_pos = line.find('\\', backslash_pos + 2); - } - - std::string::size_type quote_pos = line.find('\"'); - while (quote_pos != std::string::npos) { - line.replace(quote_pos, 1, "\\\""); - quote_pos = line.find('\"', quote_pos + 2); - } -} diff --git a/Source/CPack/cmCPackDragNDropGenerator.h b/Source/CPack/cmCPackDragNDropGenerator.h index f8c86c06d7..310b0ab9ac 100644 --- a/Source/CPack/cmCPackDragNDropGenerator.h +++ b/Source/CPack/cmCPackDragNDropGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackDragNDropGenerator_h -#define cmCPackDragNDropGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -14,6 +13,7 @@ #include "cmCPackGenerator.h" class cmGeneratedFileStream; +class cmXMLWriter; /** \class cmCPackDragNDropGenerator * \brief A generator for OSX drag-n-drop installs @@ -45,12 +45,38 @@ private: std::string slaDirectory; bool singleLicense; - bool WriteLicense(cmGeneratedFileStream& outputStream, int licenseNumber, + struct RezDict + { + std::string Name; + size_t ID; + std::vector<unsigned char> Data; + }; + + struct RezArray + { + std::string Key; + std::vector<RezDict> Entries; + }; + + struct RezDoc + { + RezArray LPic = { "LPic", {} }; + RezArray Menu = { "STR#", {} }; + RezArray Text = { "TEXT", {} }; + RezArray RTF = { "RTF ", {} }; + }; + + void WriteRezXML(std::string const& file, RezDoc const& rez); + void WriteRezArray(cmXMLWriter& xml, RezArray const& array); + void WriteRezDict(cmXMLWriter& xml, RezDict const& dict); + + bool WriteLicense(RezDoc& rez, size_t licenseNumber, std::string licenseLanguage, const std::string& licenseFile, std::string* error); + void EncodeLicense(RezDict& dict, std::vector<std::string> const& lines); + void EncodeMenu(RezDict& dict, std::vector<std::string> const& lines); + bool ReadFile(std::string const& file, std::vector<std::string>& lines, + std::string* error); bool BreakLongLine(const std::string& line, std::vector<std::string>& lines, std::string* error); - void EscapeQuotesAndBackslashes(std::string& line); }; - -#endif diff --git a/Source/CPack/cmCPackExternalGenerator.cxx b/Source/CPack/cmCPackExternalGenerator.cxx index 11e1aeccef..0bc8456710 100644 --- a/Source/CPack/cmCPackExternalGenerator.cxx +++ b/Source/CPack/cmCPackExternalGenerator.cxx @@ -61,7 +61,7 @@ int cmCPackExternalGenerator::PackageFiles() } const char* packageScript = this->GetOption("CPACK_EXTERNAL_PACKAGE_SCRIPT"); - if (packageScript && *packageScript) { + if (cmNonempty(packageScript)) { if (!cmSystemTools::FileIsFullPath(packageScript)) { cmCPackLogger( cmCPackLog::LOG_ERROR, @@ -75,6 +75,12 @@ int cmCPackExternalGenerator::PackageFiles() if (cmSystemTools::GetErrorOccuredFlag() || !res) { return 0; } + + const char* builtPackagesStr = + this->GetOption("CPACK_EXTERNAL_BUILT_PACKAGES"); + if (builtPackagesStr) { + cmExpandList(builtPackagesStr, this->packageFileNames, false); + } } return 1; @@ -205,7 +211,7 @@ int cmCPackExternalGenerator::cmCPackExternalVersionGenerator::WriteToJSON( const char* defaultDirectoryPermissions = this->Parent->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS"); - if (defaultDirectoryPermissions && *defaultDirectoryPermissions) { + if (cmNonempty(defaultDirectoryPermissions)) { root["defaultDirectoryPermissions"] = defaultDirectoryPermissions; } if (cmIsInternallyOn(this->Parent->GetOption("CPACK_SET_DESTDIR"))) { diff --git a/Source/CPack/cmCPackExternalGenerator.h b/Source/CPack/cmCPackExternalGenerator.h index 80011fddd9..dfd13e8ff3 100644 --- a/Source/CPack/cmCPackExternalGenerator.h +++ b/Source/CPack/cmCPackExternalGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackExternalGenerator_h -#define cmCPackExternalGenerator_h +#pragma once #include <memory> #include <string> @@ -86,5 +85,3 @@ private: std::unique_ptr<cmCPackExternalVersionGenerator> Generator; }; - -#endif diff --git a/Source/CPack/cmCPackFreeBSDGenerator.h b/Source/CPack/cmCPackFreeBSDGenerator.h index a18b72f62d..eed8053ca9 100644 --- a/Source/CPack/cmCPackFreeBSDGenerator.h +++ b/Source/CPack/cmCPackFreeBSDGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackFreeBSDGenerator_h -#define cmCPackFreeBSDGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -33,5 +32,3 @@ protected: std::string var_lookup(const char* var_name); void write_manifest_fields(cmGeneratedFileStream&); }; - -#endif diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index 288dc58a77..a9fe91cd16 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx @@ -20,6 +20,7 @@ #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" @@ -216,7 +217,7 @@ int cmCPackGenerator::InstallProject() mode_t* default_dir_mode = nullptr; const char* default_dir_install_permissions = this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS"); - if (default_dir_install_permissions && *default_dir_install_permissions) { + if (cmNonempty(default_dir_install_permissions)) { std::vector<std::string> items = cmExpandedList(default_dir_install_permissions); for (const auto& arg : items) { @@ -264,6 +265,23 @@ int cmCPackGenerator::InstallProject() return 0; } + // Run pre-build actions + const char* preBuildScripts = this->GetOption("CPACK_PRE_BUILD_SCRIPTS"); + if (preBuildScripts) { + const auto scripts = cmExpandedList(preBuildScripts, false); + for (const auto& script : scripts) { + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "Executing pre-build script: " << script << std::endl); + + if (!this->MakefileMap->ReadListFile(script)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "The pre-build script not found: " << script + << std::endl); + return 0; + } + } + } + if (setDestDir) { cmSystemTools::PutEnv("DESTDIR="); } @@ -276,7 +294,7 @@ int cmCPackGenerator::InstallProjectViaInstallCommands( { (void)setDestDir; const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS"); - if (installCommands && *installCommands) { + if (cmNonempty(installCommands)) { std::string tempInstallDirectoryEnv = cmStrCat("CMAKE_INSTALL_PREFIX=", tempInstallDirectory); cmSystemTools::PutEnv(tempInstallDirectoryEnv); @@ -327,13 +345,14 @@ int cmCPackGenerator::InstallProjectViaInstalledDirectories( } const char* installDirectories = this->GetOption("CPACK_INSTALLED_DIRECTORIES"); - if (installDirectories && *installDirectories) { + if (cmNonempty(installDirectories)) { std::vector<std::string> installDirectoriesVector = cmExpandedList(installDirectories); if (installDirectoriesVector.size() % 2 != 0) { cmCPackLogger( cmCPackLog::LOG_ERROR, - "CPACK_INSTALLED_DIRECTORIES should contain pairs of <directory> and " + "CPACK_INSTALLED_DIRECTORIES should contain pairs of <directory> " + "and " "<subdirectory>. The <subdirectory> can be '.' to be installed in " "the toplevel directory of installation." << std::endl); @@ -475,10 +494,10 @@ int cmCPackGenerator::InstallProjectViaInstallScript( "- Install script: " << installScript << std::endl); if (setDestDir) { - // For DESTDIR based packaging, use the *project* CMAKE_INSTALL_PREFIX - // underneath the tempInstallDirectory. The value of the project's - // CMAKE_INSTALL_PREFIX is sent in here as the value of the - // CPACK_INSTALL_PREFIX variable. + // For DESTDIR based packaging, use the *project* + // CMAKE_INSTALL_PREFIX underneath the tempInstallDirectory. The + // value of the project's CMAKE_INSTALL_PREFIX is sent in here as the + // value of the CPACK_INSTALL_PREFIX variable. std::string dir; if (this->GetOption("CPACK_INSTALL_PREFIX")) { @@ -523,7 +542,7 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( const char* cmakeProjects = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS"); const char* cmakeGenerator = this->GetOption("CPACK_CMAKE_GENERATOR"); std::string absoluteDestFiles; - if (cmakeProjects && *cmakeProjects) { + if (cmNonempty(cmakeProjects)) { if (!cmakeGenerator) { cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_INSTALL_CMAKE_PROJECTS is specified, but " @@ -576,7 +595,7 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( std::string installTypesVar = "CPACK_" + cmSystemTools::UpperCase(project.Component) + "_INSTALL_TYPES"; const char* installTypes = this->GetOption(installTypesVar); - if (installTypes && *installTypes) { + if (cmNonempty(installTypes)) { std::vector<std::string> installTypesVector = cmExpandedList(installTypes); for (std::string const& installType : installTypesVector) { @@ -589,7 +608,7 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( std::string componentsVar = "CPACK_COMPONENTS_" + cmSystemTools::UpperCase(project.Component); const char* components = this->GetOption(componentsVar); - if (components && *components) { + if (cmNonempty(components)) { cmExpandList(components, componentsVector); for (std::string const& comp : componentsVector) { project.Components.push_back( @@ -753,7 +772,7 @@ int cmCPackGenerator::InstallCMakeProject( const char* default_dir_inst_permissions = this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS"); - if (default_dir_inst_permissions && *default_dir_inst_permissions) { + if (cmNonempty(default_dir_inst_permissions)) { mf.AddDefinition("CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS", default_dir_inst_permissions); } @@ -811,7 +830,7 @@ int cmCPackGenerator::InstallCMakeProject( * - Because it was already used for component install * in order to put things in subdirs... */ - cmSystemTools::PutEnv(std::string("DESTDIR=") + tempInstallDirectory); + cmSystemTools::PutEnv("DESTDIR=" + tempInstallDirectory); cmCPackLogger(cmCPackLog::LOG_DEBUG, "- Creating directory: '" << dir << "'" << std::endl); @@ -887,8 +906,8 @@ int cmCPackGenerator::InstallCMakeProject( // forward definition of CMAKE_ABSOLUTE_DESTINATION_FILES // to CPack (may be used by generators like CPack RPM or DEB) // in order to transparently handle ABSOLUTE PATH - if (const char* def = mf.GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")) { - mf.AddDefinition("CPACK_ABSOLUTE_DESTINATION_FILES", def); + if (cmProp def = mf.GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")) { + mf.AddDefinition("CPACK_ABSOLUTE_DESTINATION_FILES", *def); } // Now rebuild the list of files after installation @@ -921,11 +940,11 @@ int cmCPackGenerator::InstallCMakeProject( } } - if (auto d = mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")) { + if (cmProp d = mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")) { if (!absoluteDestFiles.empty()) { absoluteDestFiles += ";"; } - absoluteDestFiles += d; + absoluteDestFiles += *d; cmCPackLogger(cmCPackLog::LOG_DEBUG, "Got some ABSOLUTE DESTINATION FILES: " << absoluteDestFiles << std::endl); @@ -936,12 +955,13 @@ int cmCPackGenerator::InstallCMakeProject( GetComponentInstallDirNameSuffix(component); if (nullptr != this->GetOption(absoluteDestFileComponent)) { std::string absoluteDestFilesListComponent = - cmStrCat(this->GetOption(absoluteDestFileComponent), ';', d); + cmStrCat(this->GetOption(absoluteDestFileComponent), ';', *d); this->SetOption(absoluteDestFileComponent, absoluteDestFilesListComponent.c_str()); } else { - this->SetOption(absoluteDestFileComponent, - mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")); + this->SetOption( + absoluteDestFileComponent, + cmToCStr(mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES"))); } } } @@ -964,8 +984,8 @@ bool cmCPackGenerator::ReadListFile(const char* moduleName) void cmCPackGenerator::SetOptionIfNotSet(const std::string& op, const char* value) { - const char* def = this->MakefileMap->GetDefinition(op); - if (def && *def) { + cmProp def = this->MakefileMap->GetDefinition(op); + if (cmNonempty(def)) { return; } this->SetOption(op, value); @@ -1076,6 +1096,25 @@ int cmCPackGenerator::DoPackage() return 0; } } + // Run post-build actions + const char* postBuildScripts = this->GetOption("CPACK_POST_BUILD_SCRIPTS"); + if (postBuildScripts) { + this->MakefileMap->AddDefinition("CPACK_PACKAGE_FILES", + cmJoin(this->packageFileNames, ";")); + + const auto scripts = cmExpandedList(postBuildScripts, false); + for (const auto& script : scripts) { + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "Executing post-build script: " << script << std::endl); + + if (!this->MakefileMap->ReadListFile(script)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "The post-build script not found: " << script + << std::endl); + return 0; + } + } + } /* Prepare checksum algorithm*/ const char* algo = this->GetOption("CPACK_PACKAGE_CHECKSUM"); @@ -1177,30 +1216,31 @@ bool cmCPackGenerator::IsOn(const std::string& name) const bool cmCPackGenerator::IsSetToOff(const std::string& op) const { - const char* ret = this->MakefileMap->GetDefinition(op); - if (ret && *ret) { - return cmIsOff(ret); + cmProp ret = this->MakefileMap->GetDefinition(op); + if (cmNonempty(ret)) { + return cmIsOff(*ret); } return false; } bool cmCPackGenerator::IsSetToEmpty(const std::string& op) const { - const char* ret = this->MakefileMap->GetDefinition(op); + cmProp ret = this->MakefileMap->GetDefinition(op); if (ret) { - return !*ret; + return ret->empty(); } return false; } const char* cmCPackGenerator::GetOption(const std::string& op) const { - const char* ret = this->MakefileMap->GetDefinition(op); + cmProp ret = this->MakefileMap->GetDefinition(op); if (!ret) { cmCPackLogger(cmCPackLog::LOG_DEBUG, "Warning, GetOption return NULL for: " << op << std::endl); + return nullptr; } - return ret; + return ret->c_str(); } std::vector<std::string> cmCPackGenerator::GetOptions() const @@ -1289,7 +1329,7 @@ bool cmCPackGenerator::ConfigureFile(const std::string& inName, bool copyOnly /* = false */) { return this->MakefileMap->ConfigureFile(inName, outName, copyOnly, true, - false) == 1; + false, true) == 1; } int cmCPackGenerator::CleanTemporaryDirectory() @@ -1378,7 +1418,8 @@ int cmCPackGenerator::PrepareGroupingKind() << std::endl); } - // if user specified packaging method, override the default packaging method + // if user specified packaging method, override the default packaging + // method if (method != UNKNOWN_COMPONENT_PACKAGE_METHOD) { componentPackageMethod = method; } @@ -1471,7 +1512,7 @@ cmCPackInstallationType* cmCPackGenerator::GetInstallationType( installType->Name = name; const char* displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME"); - if (displayName && *displayName) { + if (cmNonempty(displayName)) { installType->DisplayName = displayName; } else { installType->DisplayName = installType->Name; @@ -1493,7 +1534,7 @@ cmCPackComponent* cmCPackGenerator::GetComponent( "CPACK_COMPONENT_" + cmsys::SystemTools::UpperCase(name); component->Name = name; const char* displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME"); - if (displayName && *displayName) { + if (cmNonempty(displayName)) { component->DisplayName = displayName; } else { component->DisplayName = component->Name; @@ -1505,17 +1546,17 @@ cmCPackComponent* cmCPackGenerator::GetComponent( cmIsOn(this->GetOption("CPACK_DOWNLOAD_ALL")); const char* archiveFile = this->GetOption(macroPrefix + "_ARCHIVE_FILE"); - if (archiveFile && *archiveFile) { + if (cmNonempty(archiveFile)) { component->ArchiveFile = archiveFile; } const char* plist = this->GetOption(macroPrefix + "_PLIST"); - if (plist && *plist) { + if (cmNonempty(plist)) { component->Plist = plist; } const char* groupName = this->GetOption(macroPrefix + "_GROUP"); - if (groupName && *groupName) { + if (cmNonempty(groupName)) { component->Group = GetComponentGroup(projectName, groupName); component->Group->Components.push_back(component); } else { @@ -1523,13 +1564,13 @@ cmCPackComponent* cmCPackGenerator::GetComponent( } const char* description = this->GetOption(macroPrefix + "_DESCRIPTION"); - if (description && *description) { + if (cmNonempty(description)) { component->Description = description; } // Determine the installation types. const char* installTypes = this->GetOption(macroPrefix + "_INSTALL_TYPES"); - if (installTypes && *installTypes) { + if (cmNonempty(installTypes)) { std::vector<std::string> installTypesVector = cmExpandedList(installTypes); for (std::string const& installType : installTypesVector) { @@ -1540,7 +1581,7 @@ cmCPackComponent* cmCPackGenerator::GetComponent( // Determine the component dependencies. const char* depends = this->GetOption(macroPrefix + "_DEPENDS"); - if (depends && *depends) { + if (cmNonempty(depends)) { std::vector<std::string> dependsVector = cmExpandedList(depends); for (std::string const& depend : dependsVector) { cmCPackComponent* child = GetComponent(projectName, depend); @@ -1564,21 +1605,21 @@ cmCPackComponentGroup* cmCPackGenerator::GetComponentGroup( // Define the group group->Name = name; const char* displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME"); - if (displayName && *displayName) { + if (cmNonempty(displayName)) { group->DisplayName = displayName; } else { group->DisplayName = group->Name; } const char* description = this->GetOption(macroPrefix + "_DESCRIPTION"); - if (description && *description) { + if (cmNonempty(description)) { group->Description = description; } group->IsBold = this->IsOn(macroPrefix + "_BOLD_TITLE"); group->IsExpandedByDefault = this->IsOn(macroPrefix + "_EXPANDED"); const char* parentGroupName = this->GetOption(macroPrefix + "_PARENT_GROUP"); - if (parentGroupName && *parentGroupName) { + if (cmNonempty(parentGroupName)) { group->ParentGroup = GetComponentGroup(projectName, parentGroupName); group->ParentGroup->Subgroups.push_back(group); } else { diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h index 33026c1d9d..2512d4208a 100644 --- a/Source/CPack/cmCPackGenerator.h +++ b/Source/CPack/cmCPackGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackGenerator_h -#define cmCPackGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -339,5 +338,3 @@ protected: this->Logger->Log(logType, __FILE__, __LINE__, \ cmCPackLog_msg.str().c_str()); \ } while (false) - -#endif diff --git a/Source/CPack/cmCPackGeneratorFactory.h b/Source/CPack/cmCPackGeneratorFactory.h index 62b7484235..0846573bb0 100644 --- a/Source/CPack/cmCPackGeneratorFactory.h +++ b/Source/CPack/cmCPackGeneratorFactory.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackGeneratorFactory_h -#define cmCPackGeneratorFactory_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -44,5 +43,3 @@ private: DescriptionsMap GeneratorDescriptions; cmCPackLog* Logger; }; - -#endif diff --git a/Source/CPack/cmCPackLog.h b/Source/CPack/cmCPackLog.h index 68ffccee9b..6cec39cd81 100644 --- a/Source/CPack/cmCPackLog.h +++ b/Source/CPack/cmCPackLog.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackLog_h -#define cmCPackLog_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -139,5 +138,3 @@ inline std::ostream& operator<<(std::ostream& os, const cmCPackLogWrite& c) os.flush(); return os; } - -#endif diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index 058b090e0c..2109b4e349 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -696,7 +696,7 @@ std::string cmCPackNSISGenerator::CreateComponentDescription( const char* userUploadDirectory = this->GetOption("CPACK_UPLOAD_DIRECTORY"); std::string uploadDirectory; - if (userUploadDirectory && *userUploadDirectory) { + if (cmNonempty(userUploadDirectory)) { uploadDirectory = userUploadDirectory; } else { uploadDirectory = diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h index 88cba45e82..ded02debcb 100644 --- a/Source/CPack/cmCPackNSISGenerator.h +++ b/Source/CPack/cmCPackNSISGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackNSISGenerator_h -#define cmCPackNSISGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -85,5 +84,3 @@ protected: bool Nsis64; }; - -#endif diff --git a/Source/CPack/cmCPackNuGetGenerator.h b/Source/CPack/cmCPackNuGetGenerator.h index a59db2d04e..609ec79507 100644 --- a/Source/CPack/cmCPackNuGetGenerator.h +++ b/Source/CPack/cmCPackNuGetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackNuGetGenerator_h -#define cmCPackNuGetGenerator_h +#pragma once #include "cmCPackGenerator.h" @@ -33,5 +32,3 @@ protected: */ void AddGeneratedPackageNames(); }; - -#endif diff --git a/Source/CPack/cmCPackOSXX11Generator.h b/Source/CPack/cmCPackOSXX11Generator.h index a6461c8542..8fae136eb6 100644 --- a/Source/CPack/cmCPackOSXX11Generator.h +++ b/Source/CPack/cmCPackOSXX11Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackOSXX11Generator_h -#define cmCPackOSXX11Generator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -38,5 +37,3 @@ protected: bool copyOnly = false); std::string InstallPrefix; }; - -#endif diff --git a/Source/CPack/cmCPackPKGGenerator.h b/Source/CPack/cmCPackPKGGenerator.h index be730abddf..17cdcdf59f 100644 --- a/Source/CPack/cmCPackPKGGenerator.h +++ b/Source/CPack/cmCPackPKGGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackPKGGenerator_h -#define cmCPackPKGGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -94,5 +93,3 @@ protected: // The PostFlight component when creating a metapackage cmCPackComponent PostFlightComponent; }; - -#endif diff --git a/Source/CPack/cmCPackPackageMakerGenerator.h b/Source/CPack/cmCPackPackageMakerGenerator.h index 0575587d5d..cda9277233 100644 --- a/Source/CPack/cmCPackPackageMakerGenerator.h +++ b/Source/CPack/cmCPackPackageMakerGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackPackageMakerGenerator_h -#define cmCPackPackageMakerGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -49,5 +48,3 @@ protected: double PackageMakerVersion; unsigned int PackageCompatibilityVersion; }; - -#endif diff --git a/Source/CPack/cmCPackProductBuildGenerator.h b/Source/CPack/cmCPackProductBuildGenerator.h index 015fe4ace9..462e2fcc9c 100644 --- a/Source/CPack/cmCPackProductBuildGenerator.h +++ b/Source/CPack/cmCPackProductBuildGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackProductBuildGenerator_h -#define cmCPackProductBuildGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -49,5 +48,3 @@ protected: const char* GetComponentScript(const char* script, const char* script_component); }; - -#endif diff --git a/Source/CPack/cmCPackRPMGenerator.h b/Source/CPack/cmCPackRPMGenerator.h index 075ce8458e..0288f2fbd0 100644 --- a/Source/CPack/cmCPackRPMGenerator.h +++ b/Source/CPack/cmCPackRPMGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackRPMGenerator_h -#define cmCPackRPMGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -68,5 +67,3 @@ protected: void AddGeneratedPackageNames(); }; - -#endif diff --git a/Source/CPack/cmCPackSTGZGenerator.h b/Source/CPack/cmCPackSTGZGenerator.h index 79d7035e1d..d2df1f228d 100644 --- a/Source/CPack/cmCPackSTGZGenerator.h +++ b/Source/CPack/cmCPackSTGZGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackSTGZGenerator_h -#define cmCPackSTGZGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -30,5 +29,3 @@ protected: int InitializeInternal() override; int GenerateHeader(std::ostream* os) override; }; - -#endif diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index cc1ddf509d..85c13ada76 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx @@ -22,6 +22,7 @@ #include "cmDocumentationFormatter.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" @@ -319,21 +320,22 @@ int main(int argc, char const* const* argv) } // Force CPACK_PACKAGE_DIRECTORY as absolute path - cpackProjectDirectory = globalMF.GetDefinition("CPACK_PACKAGE_DIRECTORY"); + cpackProjectDirectory = + globalMF.GetSafeDefinition("CPACK_PACKAGE_DIRECTORY"); cpackProjectDirectory = cmSystemTools::CollapseFullPath(cpackProjectDirectory); globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory); - const char* cpackModulesPath = globalMF.GetDefinition("CPACK_MODULE_PATH"); + cmProp cpackModulesPath = globalMF.GetDefinition("CPACK_MODULE_PATH"); if (cpackModulesPath) { - globalMF.AddDefinition("CMAKE_MODULE_PATH", cpackModulesPath); + globalMF.AddDefinition("CMAKE_MODULE_PATH", *cpackModulesPath); } - const char* genList = globalMF.GetDefinition("CPACK_GENERATOR"); + cmProp genList = globalMF.GetDefinition("CPACK_GENERATOR"); if (!genList) { cmCPack_Log(&log, cmCPackLog::LOG_ERROR, "CPack generator not specified" << std::endl); } else { - std::vector<std::string> generatorsVector = cmExpandedList(genList); + std::vector<std::string> generatorsVector = cmExpandedList(*genList); for (std::string const& gen : generatorsVector) { cmMakefile::ScopePushPop raii(&globalMF); cmMakefile* mf = &globalMF; @@ -406,32 +408,31 @@ int main(int argc, char const* const* argv) parsed = 0; } if (parsed) { - const char* projName = mf->GetDefinition("CPACK_PACKAGE_NAME"); + cmProp projName = mf->GetDefinition("CPACK_PACKAGE_NAME"); cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, "Use generator: " << cpackGenerator->GetNameOfClass() << std::endl); cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, - "For project: " << projName << std::endl); + "For project: " << *projName << std::endl); - const char* projVersion = - mf->GetDefinition("CPACK_PACKAGE_VERSION"); + cmProp projVersion = mf->GetDefinition("CPACK_PACKAGE_VERSION"); if (!projVersion) { - const char* projVersionMajor = + cmProp projVersionMajor = mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR"); - const char* projVersionMinor = + cmProp projVersionMinor = mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR"); - const char* projVersionPatch = + cmProp projVersionPatch = mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH"); std::ostringstream ostr; - ostr << projVersionMajor << "." << projVersionMinor << "." - << projVersionPatch; + ostr << *projVersionMajor << "." << *projVersionMinor << "." + << *projVersionPatch; mf->AddDefinition("CPACK_PACKAGE_VERSION", ostr.str()); } int res = cpackGenerator->DoPackage(); if (!res) { cmCPack_Log(&log, cmCPackLog::LOG_ERROR, - "Error when generating package: " << projName + "Error when generating package: " << *projName << std::endl); return 1; } diff --git a/Source/CTest/cmCTestBZR.h b/Source/CTest/cmCTestBZR.h index d7c6321281..eb0dbbef81 100644 --- a/Source/CTest/cmCTestBZR.h +++ b/Source/CTest/cmCTestBZR.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestBZR_h -#define cmCTestBZR_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -50,5 +49,3 @@ private: friend class UpdateParser; friend class StatusParser; }; - -#endif diff --git a/Source/CTest/cmCTestBinPacker.h b/Source/CTest/cmCTestBinPacker.h index ff02b8565c..e56a4379ad 100644 --- a/Source/CTest/cmCTestBinPacker.h +++ b/Source/CTest/cmCTestBinPacker.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestBinPacker_h -#define cmCTestBinPacker_h +#pragma once #include <cstddef> #include <map> @@ -27,5 +26,3 @@ bool cmAllocateCTestResourcesRoundRobin( bool cmAllocateCTestResourcesBlock( const std::map<std::string, cmCTestResourceAllocator::Resource>& resources, std::vector<cmCTestBinPackerAllocation>& allocations); - -#endif diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index db426b2ffd..a18cbb49bc 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -182,10 +182,9 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) std::vector<std::string> extraPaths; std::vector<std::string> failed; fullPath = cmCTestTestHandler::FindExecutable( - this->CTest, this->ConfigSample.c_str(), resultingConfig, extraPaths, - failed); + this->CTest, this->ConfigSample, resultingConfig, extraPaths, failed); if (!fullPath.empty() && !resultingConfig.empty()) { - this->CTest->SetConfigType(resultingConfig.c_str()); + this->CTest->SetConfigType(resultingConfig); } out << "Using config sample with results: " << fullPath << " and " << resultingConfig << std::endl; @@ -296,9 +295,8 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring) extraPaths.push_back(tempPath); } std::vector<std::string> failed; - fullPath = - cmCTestTestHandler::FindExecutable(this->CTest, this->TestCommand.c_str(), - resultingConfig, extraPaths, failed); + fullPath = cmCTestTestHandler::FindExecutable( + this->CTest, this->TestCommand, resultingConfig, extraPaths, failed); if (!cmSystemTools::FileExists(fullPath)) { out << "Could not find path to executable, perhaps it was not built: " diff --git a/Source/CTest/cmCTestBuildAndTestHandler.h b/Source/CTest/cmCTestBuildAndTestHandler.h index 0c8a040793..b9cc35ccbd 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.h +++ b/Source/CTest/cmCTestBuildAndTestHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestBuildAndTestHandler_h -#define cmCTestBuildAndTestHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -71,5 +70,3 @@ protected: bool BuildNoCMake; cmDuration Timeout; }; - -#endif diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index 44fdc29452..1cc267e95a 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestBuildCommand.h" -#include <cstring> #include <sstream> #include <cmext/string_view> @@ -12,6 +11,7 @@ #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmake.h" @@ -38,13 +38,13 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() this->Handler = handler; - const char* ctestBuildCommand = + cmProp ctestBuildCommand = this->Makefile->GetDefinition("CTEST_BUILD_COMMAND"); - if (ctestBuildCommand && *ctestBuildCommand) { - this->CTest->SetCTestConfiguration("MakeCommand", ctestBuildCommand, + if (cmNonempty(ctestBuildCommand)) { + this->CTest->SetCTestConfiguration("MakeCommand", *ctestBuildCommand, this->Quiet); } else { - const char* cmakeGeneratorName = + cmProp cmakeGeneratorName = this->Makefile->GetDefinition("CTEST_CMAKE_GENERATOR"); // Build configuration is determined by: CONFIGURATION argument, @@ -52,49 +52,51 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() // CTEST_CONFIGURATION_TYPE script variable, or ctest -C command // line argument... in that order. // - const char* ctestBuildConfiguration = + cmProp ctestBuildConfiguration = this->Makefile->GetDefinition("CTEST_BUILD_CONFIGURATION"); - const char* cmakeBuildConfiguration = !this->Configuration.empty() - ? this->Configuration.c_str() - : ((ctestBuildConfiguration && *ctestBuildConfiguration) - ? ctestBuildConfiguration - : this->CTest->GetConfigType().c_str()); - - const char* cmakeBuildAdditionalFlags = !this->Flags.empty() - ? this->Flags.c_str() + const std::string* cmakeBuildConfiguration = !this->Configuration.empty() + ? &this->Configuration + : (cmNonempty(ctestBuildConfiguration) ? ctestBuildConfiguration + : &this->CTest->GetConfigType()); + + const std::string* cmakeBuildAdditionalFlags = !this->Flags.empty() + ? &this->Flags : this->Makefile->GetDefinition("CTEST_BUILD_FLAGS"); - const char* cmakeBuildTarget = !this->Target.empty() - ? this->Target.c_str() + const std::string* cmakeBuildTarget = !this->Target.empty() + ? &this->Target : this->Makefile->GetDefinition("CTEST_BUILD_TARGET"); - if (cmakeGeneratorName && *cmakeGeneratorName) { + if (cmNonempty(cmakeGeneratorName)) { if (!cmakeBuildConfiguration) { - cmakeBuildConfiguration = "Release"; + static const std::string sRelease = "Release"; + cmakeBuildConfiguration = &sRelease; } if (this->GlobalGenerator) { - if (this->GlobalGenerator->GetName() != cmakeGeneratorName) { + if (this->GlobalGenerator->GetName() != *cmakeGeneratorName) { this->GlobalGenerator.reset(); } } if (!this->GlobalGenerator) { this->GlobalGenerator = this->Makefile->GetCMakeInstance()->CreateGlobalGenerator( - cmakeGeneratorName); + *cmakeGeneratorName); if (!this->GlobalGenerator) { std::string e = cmStrCat("could not create generator named \"", - cmakeGeneratorName, '"'); + *cmakeGeneratorName, '"'); this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e); cmSystemTools::SetFatalErrorOccured(); return nullptr; } } - if (strlen(cmakeBuildConfiguration) == 0) { - const char* config = nullptr; + if (cmakeBuildConfiguration->empty()) { + const std::string* config = nullptr; #ifdef CMAKE_INTDIR - config = CMAKE_INTDIR; + static const std::string sIntDir = CMAKE_INTDIR; + config = &sIntDir; #endif if (!config) { - config = "Debug"; + static const std::string sDebug = "Debug"; + config = &sDebug; } cmakeBuildConfiguration = config; } @@ -102,13 +104,13 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() std::string dir = this->CTest->GetCTestConfiguration("BuildDirectory"); std::string buildCommand = this->GlobalGenerator->GenerateCMakeBuildCommand( - cmakeBuildTarget ? cmakeBuildTarget : "", cmakeBuildConfiguration, - cmakeBuildAdditionalFlags ? cmakeBuildAdditionalFlags : "", + cmakeBuildTarget ? *cmakeBuildTarget : "", *cmakeBuildConfiguration, + cmakeBuildAdditionalFlags ? *cmakeBuildAdditionalFlags : "", this->Makefile->IgnoreErrorsCMP0061()); cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "SetMakeCommand:" << buildCommand << "\n", this->Quiet); - this->CTest->SetCTestConfiguration("MakeCommand", buildCommand.c_str(), + this->CTest->SetCTestConfiguration("MakeCommand", buildCommand, this->Quiet); } else { std::ostringstream ostr; @@ -123,16 +125,16 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler() } } - if (const char* useLaunchers = + if (cmProp useLaunchers = this->Makefile->GetDefinition("CTEST_USE_LAUNCHERS")) { - this->CTest->SetCTestConfiguration("UseLaunchers", useLaunchers, + this->CTest->SetCTestConfiguration("UseLaunchers", *useLaunchers, this->Quiet); } - if (const char* labelsForSubprojects = + if (cmProp labelsForSubprojects = this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) { this->CTest->SetCTestConfiguration("LabelsForSubprojects", - labelsForSubprojects, this->Quiet); + *labelsForSubprojects, this->Quiet); } handler->SetQuiet(this->Quiet); diff --git a/Source/CTest/cmCTestBuildCommand.h b/Source/CTest/cmCTestBuildCommand.h index 0f82817589..00dbcc451b 100644 --- a/Source/CTest/cmCTestBuildCommand.h +++ b/Source/CTest/cmCTestBuildCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestBuildCommand_h -#define cmCTestBuildCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -62,5 +61,3 @@ protected: std::string Flags; std::string ProjectName; }; - -#endif diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index 35c2b11188..103dc1ed85 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -14,11 +14,13 @@ #include "cmsys/Process.h" #include "cmCTest.h" +#include "cmCTestLaunchReporter.h" #include "cmDuration.h" #include "cmFileTimeCache.h" #include "cmGeneratedFileStream.h" #include "cmMakefile.h" #include "cmProcessOutput.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmStringReplaceHelper.h" #include "cmSystemTools.h" @@ -248,13 +250,14 @@ void cmCTestBuildHandler::PopulateCustomVectors(cmMakefile* mf) } // Record the user-specified custom warning rules. - if (const char* customWarningMatchers = + if (cmProp customWarningMatchers = mf->GetDefinition("CTEST_CUSTOM_WARNING_MATCH")) { - cmExpandList(customWarningMatchers, this->ReallyCustomWarningMatches); + cmExpandList(*customWarningMatchers, this->ReallyCustomWarningMatches); } - if (const char* customWarningExceptions = + if (cmProp customWarningExceptions = mf->GetDefinition("CTEST_CUSTOM_WARNING_EXCEPTION")) { - cmExpandList(customWarningExceptions, this->ReallyCustomWarningExceptions); + cmExpandList(*customWarningExceptions, + this->ReallyCustomWarningExceptions); } } @@ -885,15 +888,29 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, if (*retVal) { // If there was an error running command, report that on the // dashboard. - cmCTestBuildErrorWarning errorwarning; - errorwarning.LogLine = 1; - errorwarning.Text = cmStrCat( - "*** WARNING non-zero return value in ctest from: ", argv[0]); - errorwarning.PreContext.clear(); - errorwarning.PostContext.clear(); - errorwarning.Error = false; - this->ErrorsAndWarnings.push_back(std::move(errorwarning)); - this->TotalWarnings++; + if (this->UseCTestLaunch) { + cmCTestLaunchReporter reporter; + reporter.RealArgs = args; + reporter.ComputeFileNames(); + reporter.ExitCode = *retVal; + reporter.Process = cp; + // Use temporary BuildLog file to populate this error for CDash. + ofs.flush(); + reporter.LogOut = this->LogFileNames["Build"]; + reporter.LogOut += ".tmp"; + reporter.WriteXML(); + } else { + cmCTestBuildErrorWarning errorwarning; + errorwarning.LineNumber = 0; + errorwarning.LogLine = 1; + errorwarning.Text = cmStrCat( + "*** WARNING non-zero return value in ctest from: ", argv[0]); + errorwarning.PreContext.clear(); + errorwarning.PostContext.clear(); + errorwarning.Error = false; + this->ErrorsAndWarnings.push_back(std::move(errorwarning)); + this->TotalWarnings++; + } } } } else if (result == cmsysProcess_State_Exception) { @@ -909,6 +926,7 @@ int cmCTestBuildHandler::RunMakeCommand(const std::string& command, } else if (result == cmsysProcess_State_Error) { // If there was an error running command, report that on the dashboard. cmCTestBuildErrorWarning errorwarning; + errorwarning.LineNumber = 0; errorwarning.LogLine = 1; errorwarning.Text = cmStrCat("*** ERROR executing: ", cmsysProcess_GetErrorString(cp)); diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h index a5193f6e55..58e8d9cbb1 100644 --- a/Source/CTest/cmCTestBuildHandler.h +++ b/Source/CTest/cmCTestBuildHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestBuildHandler_h -#define cmCTestBuildHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -156,5 +155,3 @@ private: friend class LaunchHelper; class FragmentCompare; }; - -#endif diff --git a/Source/CTest/cmCTestCVS.h b/Source/CTest/cmCTestCVS.h index 7d33d8f2de..d20239b037 100644 --- a/Source/CTest/cmCTestCVS.h +++ b/Source/CTest/cmCTestCVS.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestCVS_h -#define cmCTestCVS_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -51,5 +50,3 @@ private: friend class LogParser; friend class UpdateParser; }; - -#endif diff --git a/Source/CTest/cmCTestCommand.h b/Source/CTest/cmCTestCommand.h index 8efb4194c9..007378dc2e 100644 --- a/Source/CTest/cmCTestCommand.h +++ b/Source/CTest/cmCTestCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestCommand_h -#define cmCTestCommand_h +#pragma once #include "cmCommand.h" @@ -27,5 +26,3 @@ public: cmCTest* CTest; cmCTestScriptHandler* CTestScriptHandler; }; - -#endif diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx index f42c3f18ae..db9923e95f 100644 --- a/Source/CTest/cmCTestConfigureCommand.cxx +++ b/Source/CTest/cmCTestConfigureCommand.cxx @@ -12,6 +12,7 @@ #include "cmCTestConfigureHandler.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmake.h" @@ -38,16 +39,16 @@ cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler() return nullptr; } - const char* ctestConfigureCommand = + cmProp ctestConfigureCommand = this->Makefile->GetDefinition("CTEST_CONFIGURE_COMMAND"); - if (ctestConfigureCommand && *ctestConfigureCommand) { + if (cmNonempty(ctestConfigureCommand)) { this->CTest->SetCTestConfiguration("ConfigureCommand", - ctestConfigureCommand, this->Quiet); + *ctestConfigureCommand, this->Quiet); } else { - const char* cmakeGeneratorName = + cmProp cmakeGeneratorName = this->Makefile->GetDefinition("CTEST_CMAKE_GENERATOR"); - if (cmakeGeneratorName && *cmakeGeneratorName) { + if (cmNonempty(cmakeGeneratorName)) { const std::string& source_dir = this->CTest->GetCTestConfiguration("SourceDirectory"); if (source_dir.empty()) { @@ -70,7 +71,7 @@ cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler() bool cmakeBuildTypeInOptions = false; auto gg = this->Makefile->GetCMakeInstance()->CreateGlobalGenerator( - cmakeGeneratorName); + *cmakeGeneratorName); if (gg) { multiConfig = gg->IsMultiConfig(); gg.reset(); @@ -102,22 +103,22 @@ cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler() } cmakeConfigureCommand += " \"-G"; - cmakeConfigureCommand += cmakeGeneratorName; + cmakeConfigureCommand += *cmakeGeneratorName; cmakeConfigureCommand += "\""; - const char* cmakeGeneratorPlatform = + cmProp cmakeGeneratorPlatform = this->Makefile->GetDefinition("CTEST_CMAKE_GENERATOR_PLATFORM"); - if (cmakeGeneratorPlatform && *cmakeGeneratorPlatform) { + if (cmNonempty(cmakeGeneratorPlatform)) { cmakeConfigureCommand += " \"-A"; - cmakeConfigureCommand += cmakeGeneratorPlatform; + cmakeConfigureCommand += *cmakeGeneratorPlatform; cmakeConfigureCommand += "\""; } - const char* cmakeGeneratorToolset = + cmProp cmakeGeneratorToolset = this->Makefile->GetDefinition("CTEST_CMAKE_GENERATOR_TOOLSET"); - if (cmakeGeneratorToolset && *cmakeGeneratorToolset) { + if (cmNonempty(cmakeGeneratorToolset)) { cmakeConfigureCommand += " \"-T"; - cmakeConfigureCommand += cmakeGeneratorToolset; + cmakeConfigureCommand += *cmakeGeneratorToolset; cmakeConfigureCommand += "\""; } @@ -125,8 +126,8 @@ cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler() cmakeConfigureCommand += source_dir; cmakeConfigureCommand += "\""; - this->CTest->SetCTestConfiguration( - "ConfigureCommand", cmakeConfigureCommand.c_str(), this->Quiet); + this->CTest->SetCTestConfiguration("ConfigureCommand", + cmakeConfigureCommand, this->Quiet); } else { this->SetError( "Configure command is not specified. If this is a " @@ -136,10 +137,10 @@ cmCTestGenericHandler* cmCTestConfigureCommand::InitializeHandler() } } - if (const char* labelsForSubprojects = + if (cmProp labelsForSubprojects = this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) { this->CTest->SetCTestConfiguration("LabelsForSubprojects", - labelsForSubprojects, this->Quiet); + *labelsForSubprojects, this->Quiet); } cmCTestConfigureHandler* handler = this->CTest->GetConfigureHandler(); diff --git a/Source/CTest/cmCTestConfigureCommand.h b/Source/CTest/cmCTestConfigureCommand.h index 3f5944af60..f338637eef 100644 --- a/Source/CTest/cmCTestConfigureCommand.h +++ b/Source/CTest/cmCTestConfigureCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestConfigureCommand_h -#define cmCTestConfigureCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -45,5 +44,3 @@ protected: std::string Options; }; - -#endif diff --git a/Source/CTest/cmCTestConfigureHandler.h b/Source/CTest/cmCTestConfigureHandler.h index 01fe8011e6..2aad98c83d 100644 --- a/Source/CTest/cmCTestConfigureHandler.h +++ b/Source/CTest/cmCTestConfigureHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestConfigureHandler_h -#define cmCTestConfigureHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -25,5 +24,3 @@ public: void Initialize() override; }; - -#endif diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h index 76aaf46f81..93448529ac 100644 --- a/Source/CTest/cmCTestCoverageCommand.h +++ b/Source/CTest/cmCTestCoverageCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestCoverageCommand_h -#define cmCTestCoverageCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -48,5 +47,3 @@ protected: bool LabelsMentioned; std::vector<std::string> Labels; }; - -#endif diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx index b839c10ad4..093b2d16dd 100644 --- a/Source/CTest/cmCTestCoverageHandler.cxx +++ b/Source/CTest/cmCTestCoverageHandler.cxx @@ -469,8 +469,8 @@ int cmCTestCoverageHandler::ProcessHandler() } const std::string fileName = cmSystemTools::GetFilenameName(fullFileName); - std::string shortFileName = - this->CTest->GetShortPathToFile(fullFileName.c_str()); + const std::string shortFileName = + this->CTest->GetShortPathToFile(fullFileName); const cmCTestCoverageHandlerContainer::SingleFileCoverageVector& fcov = file.second; covLogXML.StartElement("File"); @@ -538,7 +538,7 @@ int cmCTestCoverageHandler::ProcessHandler() covSumXML.StartElement("File"); covSumXML.Attribute("Name", fileName); covSumXML.Attribute("FullPath", - this->CTest->GetShortPathToFile(fullFileName.c_str())); + this->CTest->GetShortPathToFile(fullFileName)); covSumXML.Attribute("Covered", tested + untested > 0 ? "true" : "false"); covSumXML.Element("LOCTested", tested); covSumXML.Element("LOCUnTested", untested); @@ -1887,8 +1887,8 @@ int cmCTestCoverageHandler::RunBullseyeCoverageBranch( // start the file output covLogXML.StartElement("File"); covLogXML.Attribute("Name", i->first); - covLogXML.Attribute( - "FullPath", this->CTest->GetShortPathToFile(i->second.c_str())); + covLogXML.Attribute("FullPath", + this->CTest->GetShortPathToFile(i->second)); covLogXML.StartElement("Report"); // write the bullseye header line = 0; @@ -2064,8 +2064,7 @@ int cmCTestCoverageHandler::RunBullseyeSourceSummary( total_untested += (totalFunctions - functionsCalled); std::string fileName = cmSystemTools::GetFilenameName(file); - std::string shortFileName = - this->CTest->GetShortPathToFile(file.c_str()); + std::string shortFileName = this->CTest->GetShortPathToFile(file); float cper = static_cast<float>(percentBranch + percentFunction); if (totalBranches > 0) { @@ -2266,7 +2265,7 @@ void cmCTestCoverageHandler::LoadLabels(const char* dir) // is the end of the target-wide labels. inTarget = false; - source = this->CTest->GetShortPathToFile(line.c_str()); + source = this->CTest->GetShortPathToFile(line); // Label the source with the target labels. LabelSet& labelSet = this->SourceLabels[source]; @@ -2320,7 +2319,7 @@ bool cmCTestCoverageHandler::IsFilteredOut(std::string const& source) // The source is filtered out if it does not have any labels in // common with the filter set. - std::string shortSrc = this->CTest->GetShortPathToFile(source.c_str()); + std::string shortSrc = this->CTest->GetShortPathToFile(source); auto li = this->SourceLabels.find(shortSrc); if (li != this->SourceLabels.end()) { return !this->IntersectsFilter(li->second); @@ -2342,14 +2341,14 @@ std::set<std::string> cmCTestCoverageHandler::FindUncoveredFiles( std::vector<std::string> files = gl.GetFiles(); for (std::string const& f : files) { if (this->ShouldIDoCoverage(f, cont->SourceDir, cont->BinaryDir)) { - extraMatches.insert(this->CTest->GetShortPathToFile(f.c_str())); + extraMatches.insert(this->CTest->GetShortPathToFile(f)); } } } if (!extraMatches.empty()) { for (auto const& i : cont->TotalCoverage) { - std::string shortPath = this->CTest->GetShortPathToFile(i.first.c_str()); + std::string shortPath = this->CTest->GetShortPathToFile(i.first); extraMatches.erase(shortPath); } } diff --git a/Source/CTest/cmCTestCoverageHandler.h b/Source/CTest/cmCTestCoverageHandler.h index 991b89d7ba..8732723bc4 100644 --- a/Source/CTest/cmCTestCoverageHandler.h +++ b/Source/CTest/cmCTestCoverageHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestCoverageHandler_h -#define cmCTestCoverageHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -149,5 +148,3 @@ private: bool IntersectsFilter(LabelSet const& labels); bool IsFilteredOut(std::string const& source); }; - -#endif diff --git a/Source/CTest/cmCTestCurl.h b/Source/CTest/cmCTestCurl.h index b0d7f0757e..d9aa916807 100644 --- a/Source/CTest/cmCTestCurl.h +++ b/Source/CTest/cmCTestCurl.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestCurl_h -#define cmCTestCurl_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -52,5 +51,3 @@ private: bool Quiet; int TimeOutSeconds; }; - -#endif diff --git a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h index ac96a4e30e..ba2b0ebbd3 100644 --- a/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h +++ b/Source/CTest/cmCTestEmptyBinaryDirectoryCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestEmptyBinaryDirectoryCommand_h -#define cmCTestEmptyBinaryDirectoryCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -45,5 +44,3 @@ public: bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) override; }; - -#endif diff --git a/Source/CTest/cmCTestGIT.h b/Source/CTest/cmCTestGIT.h index 3103d8492b..a15aef53e3 100644 --- a/Source/CTest/cmCTestGIT.h +++ b/Source/CTest/cmCTestGIT.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestGIT_h -#define cmCTestGIT_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -53,5 +52,3 @@ public: friend class DiffParser; friend class OneLineParser; }; - -#endif diff --git a/Source/CTest/cmCTestGenericHandler.cxx b/Source/CTest/cmCTestGenericHandler.cxx index cc0b4ed0cf..91818bb2e8 100644 --- a/Source/CTest/cmCTestGenericHandler.cxx +++ b/Source/CTest/cmCTestGenericHandler.cxx @@ -6,6 +6,7 @@ #include <utility> #include "cmCTest.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" cmCTestGenericHandler::cmCTestGenericHandler() @@ -100,7 +101,7 @@ bool cmCTestGenericHandler::StartResultingXML(cmCTest::Part part, << std::endl); return false; } - this->CTest->AddSubmitFile(part, ostr.str().c_str()); + this->CTest->AddSubmitFile(part, ostr.str()); return true; } @@ -122,6 +123,8 @@ bool cmCTestGenericHandler::StartLogFile(const char* name, ostr << "_" << this->CTest->GetCurrentTag(); } ostr << ".log"; + this->LogFileNames[name] = + cmStrCat(this->CTest->GetBinaryDir(), "/Testing/Temporary/", ostr.str()); if (!this->CTest->OpenOutputFile("Temporary", ostr.str(), xofs)) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot create log file: " << ostr.str() << std::endl); diff --git a/Source/CTest/cmCTestGenericHandler.h b/Source/CTest/cmCTestGenericHandler.h index 94e5418650..89d75968e6 100644 --- a/Source/CTest/cmCTestGenericHandler.h +++ b/Source/CTest/cmCTestGenericHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestGenericHandler_h -#define cmCTestGenericHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -101,9 +100,8 @@ protected: cmCTest* CTest; t_StringToString Options; t_StringToString PersistentOptions; + t_StringToString LogFileNames; cmCTestCommand* Command; int SubmitIndex; }; - -#endif diff --git a/Source/CTest/cmCTestGlobalVC.h b/Source/CTest/cmCTestGlobalVC.h index ff86591eae..679b0e19bf 100644 --- a/Source/CTest/cmCTestGlobalVC.h +++ b/Source/CTest/cmCTestGlobalVC.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestGlobalVC_h -#define cmCTestGlobalVC_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -73,5 +72,3 @@ protected: void WriteXMLDirectory(cmXMLWriter& xml, std::string const& path, Directory const& dir); }; - -#endif diff --git a/Source/CTest/cmCTestHG.h b/Source/CTest/cmCTestHG.h index 2900139b4a..b81f0424fb 100644 --- a/Source/CTest/cmCTestHG.h +++ b/Source/CTest/cmCTestHG.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestHG_h -#define cmCTestHG_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -42,5 +41,3 @@ private: friend class LogParser; friend class StatusParser; }; - -#endif diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index a755632c5c..731932e38c 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -14,6 +14,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmWorkingDirectory.h" @@ -125,23 +126,22 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, // CTEST_CONFIGURATION_TYPE script variable if it is defined. // The current script value trumps the -C argument on the command // line. - const char* ctestConfigType = + cmProp ctestConfigType = this->Makefile->GetDefinition("CTEST_CONFIGURATION_TYPE"); if (ctestConfigType) { - this->CTest->SetConfigType(ctestConfigType); + this->CTest->SetConfigType(*ctestConfigType); } if (!this->Build.empty()) { this->CTest->SetCTestConfiguration( - "BuildDirectory", cmSystemTools::CollapseFullPath(this->Build).c_str(), + "BuildDirectory", cmSystemTools::CollapseFullPath(this->Build), this->Quiet); } else { std::string const& bdir = this->Makefile->GetSafeDefinition("CTEST_BINARY_DIRECTORY"); if (!bdir.empty()) { this->CTest->SetCTestConfiguration( - "BuildDirectory", cmSystemTools::CollapseFullPath(bdir).c_str(), - this->Quiet); + "BuildDirectory", cmSystemTools::CollapseFullPath(bdir), this->Quiet); } else { cmCTestLog(this->CTest, ERROR_MESSAGE, "CTEST_BINARY_DIRECTORY not set" << std::endl;); @@ -151,20 +151,18 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, cmCTestLog(this->CTest, DEBUG, "Set source directory to: " << this->Source << std::endl); this->CTest->SetCTestConfiguration( - "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source).c_str(), + "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source), this->Quiet); } else { this->CTest->SetCTestConfiguration( "SourceDirectory", cmSystemTools::CollapseFullPath( - this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY")) - .c_str(), + this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY")), this->Quiet); } - if (const char* changeId = - this->Makefile->GetDefinition("CTEST_CHANGE_ID")) { - this->CTest->SetCTestConfiguration("ChangeId", changeId, this->Quiet); + if (cmProp changeId = this->Makefile->GetDefinition("CTEST_CHANGE_ID")) { + this->CTest->SetCTestConfiguration("ChangeId", *changeId, this->Quiet); } cmCTestLog(this->CTest, DEBUG, "Initialize handler" << std::endl;); diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h index a20d607e2f..756952d8ec 100644 --- a/Source/CTest/cmCTestHandlerCommand.h +++ b/Source/CTest/cmCTestHandlerCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestHandlerCommand_h -#define cmCTestHandlerCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -58,5 +57,3 @@ protected: "The APPEND option marks results for append to those previously " \ "submitted to a dashboard server since the last ctest_start. " \ "Append semantics are defined by the dashboard server in use." - -#endif diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx index 647f5fff9f..b9ed033559 100644 --- a/Source/CTest/cmCTestLaunch.cxx +++ b/Source/CTest/cmCTestLaunch.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestLaunch.h" -#include <cstdlib> #include <cstring> #include <iostream> @@ -10,8 +9,7 @@ #include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" -#include "cmCryptoHash.h" -#include "cmGeneratedFileStream.h" +#include "cmCTestLaunchReporter.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmProcessOutput.h" @@ -19,7 +17,6 @@ #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmXMLWriter.h" #include "cmake.h" #ifdef _WIN32 @@ -30,16 +27,14 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) { - this->Passthru = true; this->Process = nullptr; - this->ExitCode = 1; - this->CWD = cmSystemTools::GetCurrentWorkingDirectory(); if (!this->ParseArguments(argc, argv)) { return; } - this->ComputeFileNames(); + this->Reporter.RealArgs = this->RealArgs; + this->Reporter.ComputeFileNames(); this->ScrapeRulesLoaded = false; this->HaveOut = false; @@ -50,10 +45,6 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv) cmCTestLaunch::~cmCTestLaunch() { cmsysProcess_Delete(this->Process); - if (!this->Passthru) { - cmSystemTools::RemoveFile(this->LogOut); - cmSystemTools::RemoveFile(this->LogErr); - } } bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) @@ -93,28 +84,28 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv) } else if (strcmp(arg, "--filter-prefix") == 0) { doing = DoingFilterPrefix; } else if (doing == DoingOutput) { - this->OptionOutput = arg; + this->Reporter.OptionOutput = arg; doing = DoingNone; } else if (doing == DoingSource) { - this->OptionSource = arg; + this->Reporter.OptionSource = arg; doing = DoingNone; } else if (doing == DoingLanguage) { - this->OptionLanguage = arg; - if (this->OptionLanguage == "CXX") { - this->OptionLanguage = "C++"; + this->Reporter.OptionLanguage = arg; + if (this->Reporter.OptionLanguage == "CXX") { + this->Reporter.OptionLanguage = "C++"; } doing = DoingNone; } else if (doing == DoingTargetName) { - this->OptionTargetName = arg; + this->Reporter.OptionTargetName = arg; doing = DoingNone; } else if (doing == DoingTargetType) { - this->OptionTargetType = arg; + this->Reporter.OptionTargetType = arg; doing = DoingNone; } else if (doing == DoingBuildDir) { - this->OptionBuildDir = arg; + this->Reporter.OptionBuildDir = arg; doing = DoingNone; } else if (doing == DoingFilterPrefix) { - this->OptionFilterPrefix = arg; + this->Reporter.OptionFilterPrefix = arg; doing = DoingNone; } } @@ -150,42 +141,11 @@ void cmCTestLaunch::HandleRealArg(const char* arg) this->RealArgs.emplace_back(arg); } -void cmCTestLaunch::ComputeFileNames() -{ - // We just passthru the behavior of the real command unless the - // CTEST_LAUNCH_LOGS environment variable is set. - const char* d = getenv("CTEST_LAUNCH_LOGS"); - if (!(d && *d)) { - return; - } - this->Passthru = false; - - // The environment variable specifies the directory into which we - // generate build logs. - this->LogDir = d; - cmSystemTools::ConvertToUnixSlashes(this->LogDir); - this->LogDir += "/"; - - // We hash the input command working dir and command line to obtain - // a repeatable and (probably) unique name for log files. - cmCryptoHash md5(cmCryptoHash::AlgoMD5); - md5.Initialize(); - md5.Append(this->CWD); - for (std::string const& realArg : this->RealArgs) { - md5.Append(realArg); - } - this->LogHash = md5.FinalizeHex(); - - // We store stdout and stderr in temporary log files. - this->LogOut = cmStrCat(this->LogDir, "launch-", this->LogHash, "-out.txt"); - this->LogErr = cmStrCat(this->LogDir, "launch-", this->LogHash, "-err.txt"); -} - void cmCTestLaunch::RunChild() { // Ignore noopt make rules if (this->RealArgs.empty() || this->RealArgs[0] == ":") { - this->ExitCode = 0; + this->Reporter.ExitCode = 0; return; } @@ -195,14 +155,14 @@ void cmCTestLaunch::RunChild() cmsys::ofstream fout; cmsys::ofstream ferr; - if (this->Passthru) { + if (this->Reporter.Passthru) { // In passthru mode we just share the output pipes. cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1); cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1); } else { // In full mode we record the child output pipes to log files. - fout.open(this->LogOut.c_str(), std::ios::out | std::ios::binary); - ferr.open(this->LogErr.c_str(), std::ios::out | std::ios::binary); + fout.open(this->Reporter.LogOut.c_str(), std::ios::out | std::ios::binary); + ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary); } #ifdef _WIN32 @@ -216,7 +176,7 @@ void cmCTestLaunch::RunChild() cmsysProcess_Execute(cp); // Record child stdout and stderr if necessary. - if (!this->Passthru) { + if (!this->Reporter.Passthru) { char* data = nullptr; int length = 0; cmProcessOutput processOutput; @@ -248,7 +208,7 @@ void cmCTestLaunch::RunChild() // Wait for the real command to finish. cmsysProcess_WaitForExit(cp, nullptr); - this->ExitCode = cmsysProcess_GetExitValue(cp); + this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp); } int cmCTestLaunch::Run() @@ -261,255 +221,31 @@ int cmCTestLaunch::Run() this->RunChild(); if (this->CheckResults()) { - return this->ExitCode; + return this->Reporter.ExitCode; } this->LoadConfig(); - this->WriteXML(); - - return this->ExitCode; -} - -void cmCTestLaunch::LoadLabels() -{ - if (this->OptionBuildDir.empty() || this->OptionTargetName.empty()) { - return; - } - - // Labels are listed in per-target files. - std::string fname = cmStrCat(this->OptionBuildDir, "/CMakeFiles/", - this->OptionTargetName, ".dir/Labels.txt"); - - // We are interested in per-target labels for this source file. - std::string source = this->OptionSource; - cmSystemTools::ConvertToUnixSlashes(source); - - // Load the labels file. - cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); - if (!fin) { - return; - } - bool inTarget = true; - bool inSource = false; - std::string line; - while (cmSystemTools::GetLineFromStream(fin, line)) { - if (line.empty() || line[0] == '#') { - // Ignore blank and comment lines. - continue; - } - if (line[0] == ' ') { - // Label lines appear indented by one space. - if (inTarget || inSource) { - this->Labels.insert(line.substr(1)); - } - } else if (!this->OptionSource.empty() && !inSource) { - // Non-indented lines specify a source file name. The first one - // is the end of the target-wide labels. Use labels following a - // matching source. - inTarget = false; - inSource = this->SourceMatches(line, source); - } else { - return; - } - } -} - -bool cmCTestLaunch::SourceMatches(std::string const& lhs, - std::string const& rhs) -{ - // TODO: Case sensitivity, UseRelativePaths, etc. Note that both - // paths in the comparison get generated by CMake. This is done for - // every source in the target, so it should be efficient (cannot use - // cmSystemTools::IsSameFile). - return lhs == rhs; -} - -bool cmCTestLaunch::IsError() const -{ - return this->ExitCode != 0; -} - -void cmCTestLaunch::WriteXML() -{ - // Name the xml file. - std::string logXML = - cmStrCat(this->LogDir, this->IsError() ? "error-" : "warning-", - this->LogHash, ".xml"); - - // Use cmGeneratedFileStream to atomically create the report file. - cmGeneratedFileStream fxml(logXML); - cmXMLWriter xml(fxml, 2); - cmXMLElement e2(xml, "Failure"); - e2.Attribute("type", this->IsError() ? "Error" : "Warning"); - this->WriteXMLAction(e2); - this->WriteXMLCommand(e2); - this->WriteXMLResult(e2); - this->WriteXMLLabels(e2); -} - -void cmCTestLaunch::WriteXMLAction(cmXMLElement& e2) -{ - e2.Comment("Meta-information about the build action"); - cmXMLElement e3(e2, "Action"); - - // TargetName - if (!this->OptionTargetName.empty()) { - e3.Element("TargetName", this->OptionTargetName); - } - - // Language - if (!this->OptionLanguage.empty()) { - e3.Element("Language", this->OptionLanguage); - } - - // SourceFile - if (!this->OptionSource.empty()) { - std::string source = this->OptionSource; - cmSystemTools::ConvertToUnixSlashes(source); - - // If file is in source tree use its relative location. - if (cmSystemTools::FileIsFullPath(this->SourceDir) && - cmSystemTools::FileIsFullPath(source) && - cmSystemTools::IsSubDirectory(source, this->SourceDir)) { - source = cmSystemTools::RelativePath(this->SourceDir, source); - } - - e3.Element("SourceFile", source); - } - - // OutputFile - if (!this->OptionOutput.empty()) { - e3.Element("OutputFile", this->OptionOutput); - } - - // OutputType - const char* outputType = nullptr; - if (!this->OptionTargetType.empty()) { - if (this->OptionTargetType == "EXECUTABLE") { - outputType = "executable"; - } else if (this->OptionTargetType == "SHARED_LIBRARY") { - outputType = "shared library"; - } else if (this->OptionTargetType == "MODULE_LIBRARY") { - outputType = "module library"; - } else if (this->OptionTargetType == "STATIC_LIBRARY") { - outputType = "static library"; - } - } else if (!this->OptionSource.empty()) { - outputType = "object file"; - } - if (outputType) { - e3.Element("OutputType", outputType); - } -} - -void cmCTestLaunch::WriteXMLCommand(cmXMLElement& e2) -{ - e2.Comment("Details of command"); - cmXMLElement e3(e2, "Command"); - if (!this->CWD.empty()) { - e3.Element("WorkingDirectory", this->CWD); - } - for (std::string const& realArg : this->RealArgs) { - e3.Element("Argument", realArg); - } -} - -void cmCTestLaunch::WriteXMLResult(cmXMLElement& e2) -{ - e2.Comment("Result of command"); - cmXMLElement e3(e2, "Result"); - - // StdOut - this->DumpFileToXML(e3, "StdOut", this->LogOut); - - // StdErr - this->DumpFileToXML(e3, "StdErr", this->LogErr); - - // ExitCondition - cmXMLElement e4(e3, "ExitCondition"); - cmsysProcess* cp = this->Process; - switch (cmsysProcess_GetState(cp)) { - case cmsysProcess_State_Starting: - e4.Content("No process has been executed"); - break; - case cmsysProcess_State_Executing: - e4.Content("The process is still executing"); - break; - case cmsysProcess_State_Disowned: - e4.Content("Disowned"); - break; - case cmsysProcess_State_Killed: - e4.Content("Killed by parent"); - break; - - case cmsysProcess_State_Expired: - e4.Content("Killed when timeout expired"); - break; - case cmsysProcess_State_Exited: - e4.Content(this->ExitCode); - break; - case cmsysProcess_State_Exception: - e4.Content("Terminated abnormally: "); - e4.Content(cmsysProcess_GetExceptionString(cp)); - break; - case cmsysProcess_State_Error: - e4.Content("Error administrating child process: "); - e4.Content(cmsysProcess_GetErrorString(cp)); - break; - } -} + this->Reporter.Process = this->Process; + this->Reporter.WriteXML(); -void cmCTestLaunch::WriteXMLLabels(cmXMLElement& e2) -{ - this->LoadLabels(); - if (!this->Labels.empty()) { - e2.Comment("Interested parties"); - cmXMLElement e3(e2, "Labels"); - for (std::string const& label : this->Labels) { - e3.Element("Label", label); - } - } -} - -void cmCTestLaunch::DumpFileToXML(cmXMLElement& e3, const char* tag, - std::string const& fname) -{ - cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); - - std::string line; - const char* sep = ""; - - cmXMLElement e4(e3, tag); - while (cmSystemTools::GetLineFromStream(fin, line)) { - if (MatchesFilterPrefix(line)) { - continue; - } - if (this->Match(line, this->RegexWarningSuppress)) { - line = cmStrCat("[CTest: warning suppressed] ", line); - } else if (this->Match(line, this->RegexWarning)) { - line = cmStrCat("[CTest: warning matched] ", line); - } - e4.Content(sep); - e4.Content(line); - sep = "\n"; - } + return this->Reporter.ExitCode; } bool cmCTestLaunch::CheckResults() { // Skip XML in passthru mode. - if (this->Passthru) { + if (this->Reporter.Passthru) { return true; } // We always report failure for error conditions. - if (this->IsError()) { + if (this->Reporter.IsError()) { return false; } // Scrape the output logs to look for warnings. - if ((this->HaveErr && this->ScrapeLog(this->LogErr)) || - (this->HaveOut && this->ScrapeLog(this->LogOut))) { + if ((this->HaveErr && this->ScrapeLog(this->Reporter.LogErr)) || + (this->HaveOut && this->ScrapeLog(this->Reporter.LogOut))) { return false; } return true; @@ -522,22 +258,17 @@ void cmCTestLaunch::LoadScrapeRules() } this->ScrapeRulesLoaded = true; - // Common compiler warning formats. These are much simpler than the - // full log-scraping expressions because we do not need to extract - // file and line information. - this->RegexWarning.emplace_back("(^|[ :])[Ww][Aa][Rr][Nn][Ii][Nn][Gg]"); - this->RegexWarning.emplace_back("(^|[ :])[Rr][Ee][Mm][Aa][Rr][Kk]"); - this->RegexWarning.emplace_back("(^|[ :])[Nn][Oo][Tt][Ee]"); - // Load custom match rules given to us by CTest. - this->LoadScrapeRules("Warning", this->RegexWarning); - this->LoadScrapeRules("WarningSuppress", this->RegexWarningSuppress); + this->LoadScrapeRules("Warning", this->Reporter.RegexWarning); + this->LoadScrapeRules("WarningSuppress", + this->Reporter.RegexWarningSuppress); } void cmCTestLaunch::LoadScrapeRules( const char* purpose, std::vector<cmsys::RegularExpression>& regexps) { - std::string fname = cmStrCat(this->LogDir, "Custom", purpose, ".txt"); + std::string fname = + cmStrCat(this->Reporter.LogDir, "Custom", purpose, ".txt"); cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); std::string line; cmsys::RegularExpression rex; @@ -557,35 +288,18 @@ bool cmCTestLaunch::ScrapeLog(std::string const& fname) cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); std::string line; while (cmSystemTools::GetLineFromStream(fin, line)) { - if (MatchesFilterPrefix(line)) { + if (this->Reporter.MatchesFilterPrefix(line)) { continue; } - if (this->Match(line, this->RegexWarning) && - !this->Match(line, this->RegexWarningSuppress)) { + if (this->Reporter.Match(line, this->Reporter.RegexWarning) && + !this->Reporter.Match(line, this->Reporter.RegexWarningSuppress)) { return true; } } return false; } -bool cmCTestLaunch::Match(std::string const& line, - std::vector<cmsys::RegularExpression>& regexps) -{ - for (cmsys::RegularExpression& r : regexps) { - if (r.find(line)) { - return true; - } - } - return false; -} - -bool cmCTestLaunch::MatchesFilterPrefix(std::string const& line) const -{ - return !this->OptionFilterPrefix.empty() && - cmHasPrefix(line, this->OptionFilterPrefix); -} - int cmCTestLaunch::Main(int argc, const char* const argv[]) { if (argc == 2) { @@ -605,9 +319,10 @@ void cmCTestLaunch::LoadConfig() cm.GetCurrentSnapshot().SetDefaultDefinitions(); cmGlobalGenerator gg(&cm); cmMakefile mf(&gg, cm.GetCurrentSnapshot()); - std::string fname = cmStrCat(this->LogDir, "CTestLaunchConfig.cmake"); + std::string fname = + cmStrCat(this->Reporter.LogDir, "CTestLaunchConfig.cmake"); if (cmSystemTools::FileExists(fname) && mf.ReadListFile(fname)) { - this->SourceDir = mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY"); - cmSystemTools::ConvertToUnixSlashes(this->SourceDir); + this->Reporter.SourceDir = mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY"); + cmSystemTools::ConvertToUnixSlashes(this->Reporter.SourceDir); } } diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h index 79a7712aa5..d18f66d6a9 100644 --- a/Source/CTest/cmCTestLaunch.h +++ b/Source/CTest/cmCTestLaunch.h @@ -1,17 +1,17 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestLaunch_h -#define cmCTestLaunch_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep -#include <set> #include <string> #include <vector> -#include "cmsys/RegularExpression.hxx" +#include "cmCTestLaunchReporter.h" -class cmXMLElement; +namespace cmsys { +class RegularExpression; +} /** \class cmCTestLaunch * \brief Launcher for make rules to report results for ctest @@ -36,72 +36,36 @@ private: int Run(); void RunChild(); - // Methods to check the result of the real command. - bool IsError() const; + // Method to check the result of the real command. bool CheckResults(); - // Launcher options specified before the real command. - std::string OptionOutput; - std::string OptionSource; - std::string OptionLanguage; - std::string OptionTargetName; - std::string OptionTargetType; - std::string OptionBuildDir; - std::string OptionFilterPrefix; + // Parse out launcher-specific options specified before the real command. bool ParseArguments(int argc, const char* const* argv); // The real command line appearing after launcher arguments. int RealArgC; const char* const* RealArgV; - std::string CWD; // The real command line after response file expansion. std::vector<std::string> RealArgs; void HandleRealArg(const char* arg); - // A hash of the real command line is unique and unlikely to collide. - std::string LogHash; - void ComputeFileNames(); - - bool Passthru; struct cmsysProcess_s* Process; - int ExitCode; - // Temporary log files for stdout and stderr of real command. - std::string LogDir; - std::string LogOut; - std::string LogErr; + // Whether or not any data have been written to stdout or stderr. bool HaveOut; bool HaveErr; - // Labels associated with the build rule. - std::set<std::string> Labels; - void LoadLabels(); - bool SourceMatches(std::string const& lhs, std::string const& rhs); - - // Regular expressions to match warnings and their exceptions. + // Load custom rules to match warnings and their exceptions. bool ScrapeRulesLoaded; - std::vector<cmsys::RegularExpression> RegexWarning; - std::vector<cmsys::RegularExpression> RegexWarningSuppress; void LoadScrapeRules(); void LoadScrapeRules(const char* purpose, std::vector<cmsys::RegularExpression>& regexps); bool ScrapeLog(std::string const& fname); - bool Match(std::string const& line, - std::vector<cmsys::RegularExpression>& regexps); - bool MatchesFilterPrefix(std::string const& line) const; - - // Methods to generate the xml fragment. - void WriteXML(); - void WriteXMLAction(cmXMLElement&); - void WriteXMLCommand(cmXMLElement&); - void WriteXMLResult(cmXMLElement&); - void WriteXMLLabels(cmXMLElement&); - void DumpFileToXML(cmXMLElement&, const char* tag, std::string const& fname); + + // Helper class to generate the xml fragment. + cmCTestLaunchReporter Reporter; // Configuration void LoadConfig(); - std::string SourceDir; }; - -#endif diff --git a/Source/CTest/cmCTestLaunchReporter.cxx b/Source/CTest/cmCTestLaunchReporter.cxx new file mode 100644 index 0000000000..6ec7d0e395 --- /dev/null +++ b/Source/CTest/cmCTestLaunchReporter.cxx @@ -0,0 +1,316 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCTestLaunchReporter.h" + +#include "cmsys/FStream.hxx" +#include "cmsys/Process.h" +#include "cmsys/RegularExpression.hxx" + +#include "cmCryptoHash.h" +#include "cmGeneratedFileStream.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmXMLWriter.h" + +#ifdef _WIN32 +# include <fcntl.h> // for _O_BINARY +# include <io.h> // for _setmode +# include <stdio.h> // for std{out,err} and fileno +#endif + +cmCTestLaunchReporter::cmCTestLaunchReporter() +{ + this->Passthru = true; + this->ExitCode = 1; + this->CWD = cmSystemTools::GetCurrentWorkingDirectory(); + + this->ComputeFileNames(); + + // Common compiler warning formats. These are much simpler than the + // full log-scraping expressions because we do not need to extract + // file and line information. + this->RegexWarning.emplace_back("(^|[ :])[Ww][Aa][Rr][Nn][Ii][Nn][Gg]"); + this->RegexWarning.emplace_back("(^|[ :])[Rr][Ee][Mm][Aa][Rr][Kk]"); + this->RegexWarning.emplace_back("(^|[ :])[Nn][Oo][Tt][Ee]"); +} + +cmCTestLaunchReporter::~cmCTestLaunchReporter() +{ + if (!this->Passthru) { + cmSystemTools::RemoveFile(this->LogOut); + cmSystemTools::RemoveFile(this->LogErr); + } +} + +void cmCTestLaunchReporter::ComputeFileNames() +{ + // We just passthru the behavior of the real command unless the + // CTEST_LAUNCH_LOGS environment variable is set. + std::string d; + if (!cmSystemTools::GetEnv("CTEST_LAUNCH_LOGS", d) || d.empty()) { + return; + } + this->Passthru = false; + + // The environment variable specifies the directory into which we + // generate build logs. + this->LogDir = d; + cmSystemTools::ConvertToUnixSlashes(this->LogDir); + this->LogDir += "/"; + + // We hash the input command working dir and command line to obtain + // a repeatable and (probably) unique name for log files. + cmCryptoHash md5(cmCryptoHash::AlgoMD5); + md5.Initialize(); + md5.Append(this->CWD); + for (std::string const& realArg : this->RealArgs) { + md5.Append(realArg); + } + this->LogHash = md5.FinalizeHex(); + + // We store stdout and stderr in temporary log files. + this->LogOut = cmStrCat(this->LogDir, "launch-", this->LogHash, "-out.txt"); + this->LogErr = cmStrCat(this->LogDir, "launch-", this->LogHash, "-err.txt"); +} + +void cmCTestLaunchReporter::LoadLabels() +{ + if (this->OptionBuildDir.empty() || this->OptionTargetName.empty()) { + return; + } + + // Labels are listed in per-target files. + std::string fname = cmStrCat(this->OptionBuildDir, "/CMakeFiles/", + this->OptionTargetName, ".dir/Labels.txt"); + + // We are interested in per-target labels for this source file. + std::string source = this->OptionSource; + cmSystemTools::ConvertToUnixSlashes(source); + + // Load the labels file. + cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); + if (!fin) { + return; + } + bool inTarget = true; + bool inSource = false; + std::string line; + while (cmSystemTools::GetLineFromStream(fin, line)) { + if (line.empty() || line[0] == '#') { + // Ignore blank and comment lines. + continue; + } + if (line[0] == ' ') { + // Label lines appear indented by one space. + if (inTarget || inSource) { + this->Labels.insert(line.substr(1)); + } + } else if (!this->OptionSource.empty() && !inSource) { + // Non-indented lines specify a source file name. The first one + // is the end of the target-wide labels. Use labels following a + // matching source. + inTarget = false; + inSource = this->SourceMatches(line, source); + } else { + return; + } + } +} + +bool cmCTestLaunchReporter::SourceMatches(std::string const& lhs, + std::string const& rhs) +{ + // TODO: Case sensitivity, UseRelativePaths, etc. Note that both + // paths in the comparison get generated by CMake. This is done for + // every source in the target, so it should be efficient (cannot use + // cmSystemTools::IsSameFile). + return lhs == rhs; +} + +bool cmCTestLaunchReporter::IsError() const +{ + return this->ExitCode != 0; +} + +void cmCTestLaunchReporter::WriteXML() +{ + // Name the xml file. + std::string logXML = + cmStrCat(this->LogDir, this->IsError() ? "error-" : "warning-", + this->LogHash, ".xml"); + + // Use cmGeneratedFileStream to atomically create the report file. + cmGeneratedFileStream fxml(logXML); + cmXMLWriter xml(fxml, 2); + cmXMLElement e2(xml, "Failure"); + e2.Attribute("type", this->IsError() ? "Error" : "Warning"); + this->WriteXMLAction(e2); + this->WriteXMLCommand(e2); + this->WriteXMLResult(e2); + this->WriteXMLLabels(e2); +} + +void cmCTestLaunchReporter::WriteXMLAction(cmXMLElement& e2) +{ + e2.Comment("Meta-information about the build action"); + cmXMLElement e3(e2, "Action"); + + // TargetName + if (!this->OptionTargetName.empty()) { + e3.Element("TargetName", this->OptionTargetName); + } + + // Language + if (!this->OptionLanguage.empty()) { + e3.Element("Language", this->OptionLanguage); + } + + // SourceFile + if (!this->OptionSource.empty()) { + std::string source = this->OptionSource; + cmSystemTools::ConvertToUnixSlashes(source); + + // If file is in source tree use its relative location. + if (cmSystemTools::FileIsFullPath(this->SourceDir) && + cmSystemTools::FileIsFullPath(source) && + cmSystemTools::IsSubDirectory(source, this->SourceDir)) { + source = cmSystemTools::RelativePath(this->SourceDir, source); + } + + e3.Element("SourceFile", source); + } + + // OutputFile + if (!this->OptionOutput.empty()) { + e3.Element("OutputFile", this->OptionOutput); + } + + // OutputType + const char* outputType = nullptr; + if (!this->OptionTargetType.empty()) { + if (this->OptionTargetType == "EXECUTABLE") { + outputType = "executable"; + } else if (this->OptionTargetType == "SHARED_LIBRARY") { + outputType = "shared library"; + } else if (this->OptionTargetType == "MODULE_LIBRARY") { + outputType = "module library"; + } else if (this->OptionTargetType == "STATIC_LIBRARY") { + outputType = "static library"; + } + } else if (!this->OptionSource.empty()) { + outputType = "object file"; + } + if (outputType) { + e3.Element("OutputType", outputType); + } +} + +void cmCTestLaunchReporter::WriteXMLCommand(cmXMLElement& e2) +{ + e2.Comment("Details of command"); + cmXMLElement e3(e2, "Command"); + if (!this->CWD.empty()) { + e3.Element("WorkingDirectory", this->CWD); + } + for (std::string const& realArg : this->RealArgs) { + e3.Element("Argument", realArg); + } +} + +void cmCTestLaunchReporter::WriteXMLResult(cmXMLElement& e2) +{ + e2.Comment("Result of command"); + cmXMLElement e3(e2, "Result"); + + // StdOut + this->DumpFileToXML(e3, "StdOut", this->LogOut); + + // StdErr + this->DumpFileToXML(e3, "StdErr", this->LogErr); + + // ExitCondition + cmXMLElement e4(e3, "ExitCondition"); + cmsysProcess* cp = this->Process; + switch (cmsysProcess_GetState(cp)) { + case cmsysProcess_State_Starting: + e4.Content("No process has been executed"); + break; + case cmsysProcess_State_Executing: + e4.Content("The process is still executing"); + break; + case cmsysProcess_State_Disowned: + e4.Content("Disowned"); + break; + case cmsysProcess_State_Killed: + e4.Content("Killed by parent"); + break; + + case cmsysProcess_State_Expired: + e4.Content("Killed when timeout expired"); + break; + case cmsysProcess_State_Exited: + e4.Content(this->ExitCode); + break; + case cmsysProcess_State_Exception: + e4.Content("Terminated abnormally: "); + e4.Content(cmsysProcess_GetExceptionString(cp)); + break; + case cmsysProcess_State_Error: + e4.Content("Error administrating child process: "); + e4.Content(cmsysProcess_GetErrorString(cp)); + break; + } +} + +void cmCTestLaunchReporter::WriteXMLLabels(cmXMLElement& e2) +{ + this->LoadLabels(); + if (!this->Labels.empty()) { + e2.Comment("Interested parties"); + cmXMLElement e3(e2, "Labels"); + for (std::string const& label : this->Labels) { + e3.Element("Label", label); + } + } +} + +void cmCTestLaunchReporter::DumpFileToXML(cmXMLElement& e3, const char* tag, + std::string const& fname) +{ + cmsys::ifstream fin(fname.c_str(), std::ios::in | std::ios::binary); + + std::string line; + const char* sep = ""; + + cmXMLElement e4(e3, tag); + while (cmSystemTools::GetLineFromStream(fin, line)) { + if (MatchesFilterPrefix(line)) { + continue; + } + if (this->Match(line, this->RegexWarningSuppress)) { + line = cmStrCat("[CTest: warning suppressed] ", line); + } else if (this->Match(line, this->RegexWarning)) { + line = cmStrCat("[CTest: warning matched] ", line); + } + e4.Content(sep); + e4.Content(line); + sep = "\n"; + } +} + +bool cmCTestLaunchReporter::Match( + std::string const& line, std::vector<cmsys::RegularExpression>& regexps) +{ + for (cmsys::RegularExpression& r : regexps) { + if (r.find(line)) { + return true; + } + } + return false; +} + +bool cmCTestLaunchReporter::MatchesFilterPrefix(std::string const& line) const +{ + return !this->OptionFilterPrefix.empty() && + cmHasPrefix(line, this->OptionFilterPrefix); +} diff --git a/Source/CTest/cmCTestLaunchReporter.h b/Source/CTest/cmCTestLaunchReporter.h new file mode 100644 index 0000000000..675a8787e5 --- /dev/null +++ b/Source/CTest/cmCTestLaunchReporter.h @@ -0,0 +1,81 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <set> +#include <string> +#include <vector> + +#include "cmsys/RegularExpression.hxx" + +class cmXMLElement; + +/** \class cmCTestLaunchReporter + * \brief Generate CTest XML output for the 'ctest --launch' tool. + */ +class cmCTestLaunchReporter +{ +public: + // Initialize the launcher from its command line. + cmCTestLaunchReporter(); + ~cmCTestLaunchReporter(); + + cmCTestLaunchReporter(const cmCTestLaunchReporter&) = delete; + cmCTestLaunchReporter& operator=(const cmCTestLaunchReporter&) = delete; + + // Methods to check the result of the real command. + bool IsError() const; + + // Launcher options specified before the real command. + std::string OptionOutput; + std::string OptionSource; + std::string OptionLanguage; + std::string OptionTargetName; + std::string OptionTargetType; + std::string OptionBuildDir; + std::string OptionFilterPrefix; + + // The real command line appearing after launcher arguments. + std::string CWD; + + // The real command line after response file expansion. + std::vector<std::string> RealArgs; + + // A hash of the real command line is unique and unlikely to collide. + std::string LogHash; + void ComputeFileNames(); + + bool Passthru; + struct cmsysProcess_s* Process; + int ExitCode; + + // Temporary log files for stdout and stderr of real command. + std::string LogDir; + std::string LogOut; + std::string LogErr; + + // Labels associated with the build rule. + std::set<std::string> Labels; + void LoadLabels(); + bool SourceMatches(std::string const& lhs, std::string const& rhs); + + // Regular expressions to match warnings and their exceptions. + std::vector<cmsys::RegularExpression> RegexWarning; + std::vector<cmsys::RegularExpression> RegexWarningSuppress; + bool Match(std::string const& line, + std::vector<cmsys::RegularExpression>& regexps); + bool MatchesFilterPrefix(std::string const& line) const; + + // Methods to generate the xml fragment. + void WriteXML(); + void WriteXMLAction(cmXMLElement&); + void WriteXMLCommand(cmXMLElement&); + void WriteXMLResult(cmXMLElement&); + void WriteXMLLabels(cmXMLElement&); + void DumpFileToXML(cmXMLElement&, const char* tag, std::string const& fname); + + // Configuration + std::string SourceDir; +}; diff --git a/Source/CTest/cmCTestMemCheckCommand.h b/Source/CTest/cmCTestMemCheckCommand.h index 8f4ffb89a4..6544f16cfd 100644 --- a/Source/CTest/cmCTestMemCheckCommand.h +++ b/Source/CTest/cmCTestMemCheckCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestMemCheckCommand_h -#define cmCTestMemCheckCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -43,5 +42,3 @@ protected: std::string DefectCount; }; - -#endif diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx index 85b8ab15f3..8a30dc0148 100644 --- a/Source/CTest/cmCTestMemCheckHandler.cxx +++ b/Source/CTest/cmCTestMemCheckHandler.cxx @@ -326,6 +326,9 @@ void cmCTestMemCheckHandler::GenerateDartOutput(cmXMLWriter& xml) case cmCTestMemCheckHandler::BOUNDS_CHECKER: xml.Attribute("Checker", "BoundsChecker"); break; + case cmCTestMemCheckHandler::CUDA_SANITIZER: + xml.Attribute("Checker", "CudaSanitizer"); + break; case cmCTestMemCheckHandler::ADDRESS_SANITIZER: xml.Attribute("Checker", "AddressSanitizer"); break; @@ -351,7 +354,7 @@ void cmCTestMemCheckHandler::GenerateDartOutput(cmXMLWriter& xml) cmCTestMemCheckHandler::TestResultsVector::size_type cc; for (cmCTestTestResult const& result : this->TestResults) { std::string testPath = result.Path + "/" + result.Name; - xml.Element("Test", this->CTest->GetShortPathToFile(testPath.c_str())); + xml.Element("Test", this->CTest->GetShortPathToFile(testPath)); } xml.EndElement(); // TestList cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, @@ -465,6 +468,9 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() this->MemoryTesterStyle = cmCTestMemCheckHandler::PURIFY; } else if (testerName.find("BC") != std::string::npos) { this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER; + } else if (testerName.find("cuda-memcheck") != std::string::npos || + testerName.find("compute-sanitizer") != std::string::npos) { + this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER; } else { this->MemoryTesterStyle = cmCTestMemCheckHandler::UNKNOWN; } @@ -485,6 +491,11 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() this->MemoryTester = this->CTest->GetCTestConfiguration("BoundsCheckerCommand"); this->MemoryTesterStyle = cmCTestMemCheckHandler::BOUNDS_CHECKER; + } else if (cmSystemTools::FileExists( + this->CTest->GetCTestConfiguration("CudaSanitizerCommand"))) { + this->MemoryTester = + this->CTest->GetCTestConfiguration("CudaSanitizerCommand"); + this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER; } if (this->CTest->GetCTestConfiguration("MemoryCheckType") == "AddressSanitizer") { @@ -528,6 +539,8 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() this->MemoryTesterStyle = cmCTestMemCheckHandler::VALGRIND; } else if (checkType == "DrMemory") { this->MemoryTesterStyle = cmCTestMemCheckHandler::DRMEMORY; + } else if (checkType == "CudaSanitizer") { + this->MemoryTesterStyle = cmCTestMemCheckHandler::CUDA_SANITIZER; } } if (this->MemoryTester.empty()) { @@ -553,6 +566,10 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() .empty()) { memoryTesterOptions = this->CTest->GetCTestConfiguration("DrMemoryCommandOptions"); + } else if (!this->CTest->GetCTestConfiguration("CudaSanitizerCommandOptions") + .empty()) { + memoryTesterOptions = + this->CTest->GetCTestConfiguration("CudaSanitizerCommandOptions"); } this->MemoryTesterOptions = cmSystemTools::ParseArguments(memoryTesterOptions); @@ -686,6 +703,18 @@ bool cmCTestMemCheckHandler::InitializeMemoryChecking() this->MemoryTesterOptions.emplace_back("/M"); break; } + case cmCTestMemCheckHandler::CUDA_SANITIZER: { + // cuda sanitizer separates flags from arguments by spaces + if (this->MemoryTesterOptions.empty()) { + this->MemoryTesterOptions.emplace_back("--tool"); + this->MemoryTesterOptions.emplace_back("memcheck"); + this->MemoryTesterOptions.emplace_back("--leak-check"); + this->MemoryTesterOptions.emplace_back("full"); + } + this->MemoryTesterDynamicOptions.emplace_back("--log-file"); + this->MemoryTesterDynamicOptions.push_back(this->MemoryTesterOutputFile); + break; + } // these are almost the same but the env var used is different case cmCTestMemCheckHandler::ADDRESS_SANITIZER: case cmCTestMemCheckHandler::LEAK_SANITIZER: @@ -771,6 +800,8 @@ bool cmCTestMemCheckHandler::ProcessMemCheckOutput(const std::string& str, return this->ProcessMemCheckSanitizerOutput(str, log, results); case cmCTestMemCheckHandler::BOUNDS_CHECKER: return this->ProcessMemCheckBoundsCheckerOutput(str, log, results); + case cmCTestMemCheckHandler::CUDA_SANITIZER: + return this->ProcessMemCheckCudaOutput(str, log, results); default: log.append("\nMemory checking style used was: "); log.append("None that I know"); @@ -1103,6 +1134,119 @@ bool cmCTestMemCheckHandler::ProcessMemCheckBoundsCheckerOutput( return defects == 0; } +bool cmCTestMemCheckHandler::ProcessMemCheckCudaOutput( + const std::string& str, std::string& log, std::vector<int>& results) +{ + std::vector<std::string> lines; + cmsys::SystemTools::Split(str, lines); + bool unlimitedOutput = false; + if (str.find("CTEST_FULL_OUTPUT") != std::string::npos || + this->CustomMaximumFailedTestOutputSize == 0) { + unlimitedOutput = true; + } + + std::string::size_type cc; + + std::ostringstream ostr; + log.clear(); + + int defects = 0; + + cmsys::RegularExpression memcheckLine("^========"); + + cmsys::RegularExpression leakExpr("== Leaked [0-9,]+ bytes at"); + + // list of matchers for output messages that contain variable content + // (addresses, sizes, ...) or can be shortened in general. the first match is + // used as a error name. + std::vector<cmsys::RegularExpression> matchers{ + // API errors + "== Malloc/Free error encountered: (.*)", + "== Program hit error ([^ ]*).* on CUDA API call to", + "== Program hit ([^ ]*).* on CUDA API call to", + // memcheck + "== (Invalid .*) of size [0-9,]+", "== (Fatal UVM [CG]PU fault)", + // racecheck + "== .* (Potential .* hazard detected)", "== .* (Race reported)", + // synccheck + "== (Barrier error)", + // initcheck + "== (Uninitialized .* memory read)", "== (Unused memory)", + "== (Host API memory access error)", + // generic error: ignore ERROR SUMMARY, CUDA-MEMCHECK and others + "== ([A-Z][a-z].*)" + }; + + std::vector<std::string::size_type> nonMemcheckOutput; + auto sttime = std::chrono::steady_clock::now(); + cmCTestOptionalLog(this->CTest, DEBUG, + "Start test: " << lines.size() << std::endl, this->Quiet); + std::string::size_type totalOutputSize = 0; + for (cc = 0; cc < lines.size(); cc++) { + cmCTestOptionalLog(this->CTest, DEBUG, + "test line " << lines[cc] << std::endl, this->Quiet); + + if (memcheckLine.find(lines[cc])) { + cmCTestOptionalLog(this->CTest, DEBUG, + "cuda sanitizer line " << lines[cc] << std::endl, + this->Quiet); + int failure = -1; + auto& line = lines[cc]; + if (leakExpr.find(line)) { + failure = static_cast<int>(this->FindOrAddWarning("Memory leak")); + } else { + for (auto& matcher : matchers) { + if (matcher.find(line)) { + failure = + static_cast<int>(this->FindOrAddWarning(matcher.match(1))); + break; + } + } + } + + if (failure >= 0) { + ostr << "<b>" << this->ResultStrings[failure] << "</b> "; + if (results.empty() || unsigned(failure) > results.size() - 1) { + results.push_back(1); + } else { + results[failure]++; + } + defects++; + } + totalOutputSize += lines[cc].size(); + ostr << lines[cc] << std::endl; + } else { + nonMemcheckOutput.push_back(cc); + } + } + // Now put all all the non cuda sanitizer output into the test output + // This should be last in case it gets truncated by the output + // limiting code + for (std::string::size_type i : nonMemcheckOutput) { + totalOutputSize += lines[i].size(); + ostr << lines[i] << std::endl; + if (!unlimitedOutput && + totalOutputSize > + static_cast<size_t>(this->CustomMaximumFailedTestOutputSize)) { + ostr << "....\n"; + ostr << "Test Output for this test has been truncated see testing" + " machine logs for full output,\n"; + ostr << "or put CTEST_FULL_OUTPUT in the output of " + "this test program.\n"; + break; // stop the copy of output if we are full + } + } + cmCTestOptionalLog(this->CTest, DEBUG, + "End test (elapsed: " + << cmDurationTo<unsigned int>( + std::chrono::steady_clock::now() - sttime) + << "s)" << std::endl, + this->Quiet); + log = ostr.str(); + this->DefectCount += defects; + return defects == 0; +} + // PostProcessTest memcheck results void cmCTestMemCheckHandler::PostProcessTest(cmCTestTestResult& res, int test) { diff --git a/Source/CTest/cmCTestMemCheckHandler.h b/Source/CTest/cmCTestMemCheckHandler.h index 52667f8cfe..7ab00dbd7a 100644 --- a/Source/CTest/cmCTestMemCheckHandler.h +++ b/Source/CTest/cmCTestMemCheckHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestMemCheckHandler_h -#define cmCTestMemCheckHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -46,6 +45,7 @@ private: DRMEMORY, BOUNDS_CHECKER, // checkers after here do not use the standard error list + CUDA_SANITIZER, ADDRESS_SANITIZER, LEAK_SANITIZER, THREAD_SANITIZER, @@ -137,6 +137,8 @@ private: std::vector<int>& results); bool ProcessMemCheckPurifyOutput(const std::string& str, std::string& log, std::vector<int>& results); + bool ProcessMemCheckCudaOutput(const std::string& str, std::string& log, + std::vector<int>& results); bool ProcessMemCheckSanitizerOutput(const std::string& str, std::string& log, std::vector<int>& results); bool ProcessMemCheckBoundsCheckerOutput(const std::string& str, @@ -154,5 +156,3 @@ private: //! generate the output filename for the given test index void TestOutputFileNames(int test, std::vector<std::string>& files); }; - -#endif diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index e21b912c7c..5de42f9e32 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestMultiProcessHandler_h -#define cmCTestMultiProcessHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -200,5 +199,3 @@ protected: bool Quiet; bool SerialTestRunning; }; - -#endif diff --git a/Source/CTest/cmCTestP4.h b/Source/CTest/cmCTestP4.h index e19472ee89..d03f9cbae3 100644 --- a/Source/CTest/cmCTestP4.h +++ b/Source/CTest/cmCTestP4.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestP4_h -#define cmCTestP4_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -72,5 +71,3 @@ private: friend class DescribeParser; friend class DiffParser; }; - -#endif diff --git a/Source/CTest/cmCTestReadCustomFilesCommand.cxx b/Source/CTest/cmCTestReadCustomFilesCommand.cxx index ed14d06887..a25cca49c8 100644 --- a/Source/CTest/cmCTestReadCustomFilesCommand.cxx +++ b/Source/CTest/cmCTestReadCustomFilesCommand.cxx @@ -15,7 +15,7 @@ bool cmCTestReadCustomFilesCommand::InitialPass( } for (std::string const& arg : args) { - this->CTest->ReadCustomConfigurationFileTree(arg.c_str(), this->Makefile); + this->CTest->ReadCustomConfigurationFileTree(arg, this->Makefile); } return true; diff --git a/Source/CTest/cmCTestReadCustomFilesCommand.h b/Source/CTest/cmCTestReadCustomFilesCommand.h index cbb939030b..03714f6fb6 100644 --- a/Source/CTest/cmCTestReadCustomFilesCommand.h +++ b/Source/CTest/cmCTestReadCustomFilesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestReadCustomFilesCommand_h -#define cmCTestReadCustomFilesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -44,5 +43,3 @@ public: bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) override; }; - -#endif diff --git a/Source/CTest/cmCTestResourceAllocator.h b/Source/CTest/cmCTestResourceAllocator.h index 9f0b9c95a3..129e64e5e1 100644 --- a/Source/CTest/cmCTestResourceAllocator.h +++ b/Source/CTest/cmCTestResourceAllocator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestResourceAllocator_h -#define cmCTestResourceAllocator_h +#pragma once #include <map> #include <string> @@ -35,5 +34,3 @@ public: private: std::map<std::string, std::map<std::string, Resource>> Resources; }; - -#endif diff --git a/Source/CTest/cmCTestResourceGroupsLexerHelper.h b/Source/CTest/cmCTestResourceGroupsLexerHelper.h index 2cb6cb1c58..ae4fa994d2 100644 --- a/Source/CTest/cmCTestResourceGroupsLexerHelper.h +++ b/Source/CTest/cmCTestResourceGroupsLexerHelper.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestResourceGroupsLexerHelper_h -#define cmCTestResourceGroupsLexerHelper_h +#pragma once #include <string> #include <vector> @@ -40,5 +39,3 @@ private: }; #define YY_EXTRA_TYPE cmCTestResourceGroupsLexerHelper* - -#endif diff --git a/Source/CTest/cmCTestResourceSpec.cxx b/Source/CTest/cmCTestResourceSpec.cxx index 21c97de34b..101dc2c8b5 100644 --- a/Source/CTest/cmCTestResourceSpec.cxx +++ b/Source/CTest/cmCTestResourceSpec.cxx @@ -2,19 +2,140 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCTestResourceSpec.h" +#include <functional> #include <map> #include <string> #include <utility> #include <vector> +#include <cmext/string_view> + #include <cm3p/json/reader.h> #include <cm3p/json/value.h> #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" -static const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" }; -static const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" }; +#include "cmJSONHelpers.h" + +namespace { +const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" }; +const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" }; + +struct Version +{ + int Major = 1; + int Minor = 0; +}; + +struct TopVersion +{ + struct Version Version; +}; + +auto const VersionFieldHelper = + cmJSONIntHelper<cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_VERSION); + +auto const VersionHelper = + cmJSONRequiredHelper<Version, cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::NO_VERSION, + cmJSONObjectHelper<Version, cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_VERSION) + .Bind("major"_s, &Version::Major, VersionFieldHelper) + .Bind("minor"_s, &Version::Minor, VersionFieldHelper)); + +auto const RootVersionHelper = + cmJSONObjectHelper<TopVersion, cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_ROOT) + .Bind("version"_s, &TopVersion::Version, VersionHelper, false); + +cmCTestResourceSpec::ReadFileResult ResourceIdHelper(std::string& out, + const Json::Value* value) +{ + auto result = cmJSONStringHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE)(out, value); + if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) { + return result; + } + cmsys::RegularExpressionMatch match; + if (!IdRegex.find(out.c_str(), match)) { + return cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE; + } + return cmCTestResourceSpec::ReadFileResult::READ_OK; +} + +auto const ResourceHelper = + cmJSONObjectHelper<cmCTestResourceSpec::Resource, + cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE) + .Bind("id"_s, &cmCTestResourceSpec::Resource::Id, ResourceIdHelper) + .Bind("slots"_s, &cmCTestResourceSpec::Resource::Capacity, + cmJSONUIntHelper( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, 1), + false); + +auto const ResourceListHelper = + cmJSONVectorHelper<cmCTestResourceSpec::Resource, + cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE_TYPE, + ResourceHelper); + +auto const ResourceMapHelper = + cmJSONMapFilterHelper<std::vector<cmCTestResourceSpec::Resource>, + cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, + ResourceListHelper, [](const std::string& key) -> bool { + cmsys::RegularExpressionMatch match; + return IdentifierRegex.find(key.c_str(), match); + }); + +auto const SocketSetHelper = cmJSONVectorHelper< + std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, ResourceMapHelper); + +cmCTestResourceSpec::ReadFileResult SocketHelper( + cmCTestResourceSpec::Socket& out, const Json::Value* value) +{ + std::vector< + std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>> + sockets; + cmCTestResourceSpec::ReadFileResult result = SocketSetHelper(sockets, value); + if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) { + return result; + } + if (sockets.size() > 1) { + return cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC; + } + if (sockets.empty()) { + out.Resources.clear(); + } else { + out.Resources = std::move(sockets[0]); + } + return cmCTestResourceSpec::ReadFileResult::READ_OK; +} + +auto const LocalRequiredHelper = + cmJSONRequiredHelper<cmCTestResourceSpec::Socket, + cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, SocketHelper); + +auto const RootHelper = + cmJSONObjectHelper<cmCTestResourceSpec, cmCTestResourceSpec::ReadFileResult>( + cmCTestResourceSpec::ReadFileResult::READ_OK, + cmCTestResourceSpec::ReadFileResult::INVALID_ROOT) + .Bind("local", &cmCTestResourceSpec::LocalSocket, LocalRequiredHelper, + false); +} cmCTestResourceSpec::ReadFileResult cmCTestResourceSpec::ReadFromJSONFile( const std::string& filename) @@ -30,99 +151,17 @@ cmCTestResourceSpec::ReadFileResult cmCTestResourceSpec::ReadFromJSONFile( return ReadFileResult::JSON_PARSE_ERROR; } - if (!root.isObject()) { - return ReadFileResult::INVALID_ROOT; - } - - int majorVersion = 1; - int minorVersion = 0; - if (root.isMember("version")) { - auto const& version = root["version"]; - if (version.isObject()) { - if (!version.isMember("major") || !version.isMember("minor")) { - return ReadFileResult::INVALID_VERSION; - } - auto const& major = version["major"]; - auto const& minor = version["minor"]; - if (!major.isInt() || !minor.isInt()) { - return ReadFileResult::INVALID_VERSION; - } - majorVersion = major.asInt(); - minorVersion = minor.asInt(); - } else { - return ReadFileResult::INVALID_VERSION; - } - } else { - return ReadFileResult::NO_VERSION; + TopVersion version; + ReadFileResult result; + if ((result = RootVersionHelper(version, &root)) != + ReadFileResult::READ_OK) { + return result; } - - if (majorVersion != 1 || minorVersion != 0) { + if (version.Version.Major != 1 || version.Version.Minor != 0) { return ReadFileResult::UNSUPPORTED_VERSION; } - auto const& local = root["local"]; - if (!local.isArray()) { - return ReadFileResult::INVALID_SOCKET_SPEC; - } - if (local.size() > 1) { - return ReadFileResult::INVALID_SOCKET_SPEC; - } - - if (local.empty()) { - this->LocalSocket.Resources.clear(); - return ReadFileResult::READ_OK; - } - - auto const& localSocket = local[0]; - if (!localSocket.isObject()) { - return ReadFileResult::INVALID_SOCKET_SPEC; - } - std::map<std::string, std::vector<cmCTestResourceSpec::Resource>> resources; - cmsys::RegularExpressionMatch match; - for (auto const& key : localSocket.getMemberNames()) { - if (IdentifierRegex.find(key.c_str(), match)) { - auto const& value = localSocket[key]; - auto& r = resources[key]; - if (value.isArray()) { - for (auto const& item : value) { - if (item.isObject()) { - cmCTestResourceSpec::Resource resource; - - if (!item.isMember("id")) { - return ReadFileResult::INVALID_RESOURCE; - } - auto const& id = item["id"]; - if (!id.isString()) { - return ReadFileResult::INVALID_RESOURCE; - } - resource.Id = id.asString(); - if (!IdRegex.find(resource.Id.c_str(), match)) { - return ReadFileResult::INVALID_RESOURCE; - } - - if (item.isMember("slots")) { - auto const& capacity = item["slots"]; - if (!capacity.isConvertibleTo(Json::uintValue)) { - return ReadFileResult::INVALID_RESOURCE; - } - resource.Capacity = capacity.asUInt(); - } else { - resource.Capacity = 1; - } - - r.push_back(resource); - } else { - return ReadFileResult::INVALID_RESOURCE; - } - } - } else { - return ReadFileResult::INVALID_RESOURCE_TYPE; - } - } - } - - this->LocalSocket.Resources = std::move(resources); - return ReadFileResult::READ_OK; + return RootHelper(*this, &root); } const char* cmCTestResourceSpec::ResultToString(ReadFileResult result) diff --git a/Source/CTest/cmCTestResourceSpec.h b/Source/CTest/cmCTestResourceSpec.h index cb242c0507..72628a346a 100644 --- a/Source/CTest/cmCTestResourceSpec.h +++ b/Source/CTest/cmCTestResourceSpec.h @@ -1,7 +1,8 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestResourceSpec_h -#define cmCTestResourceSpec_h +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep #include <map> #include <string> @@ -51,5 +52,3 @@ public: bool operator==(const cmCTestResourceSpec& other) const; bool operator!=(const cmCTestResourceSpec& other) const; }; - -#endif diff --git a/Source/CTest/cmCTestRunScriptCommand.h b/Source/CTest/cmCTestRunScriptCommand.h index 2d8bde19df..510b748631 100644 --- a/Source/CTest/cmCTestRunScriptCommand.h +++ b/Source/CTest/cmCTestRunScriptCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestRunScriptCommand_h -#define cmCTestRunScriptCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -45,5 +44,3 @@ public: bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) override; }; - -#endif diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 2c8e3855ff..4d65c9bda4 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -619,11 +619,11 @@ void cmCTestRunTest::ComputeArguments() cmCTestMemCheckHandler* handler = static_cast<cmCTestMemCheckHandler*>(this->TestHandler); this->ActualCommand = handler->MemoryTester; - this->TestProperties->Args[1] = this->TestHandler->FindTheExecutable( - this->TestProperties->Args[1].c_str()); + this->TestProperties->Args[1] = + this->TestHandler->FindTheExecutable(this->TestProperties->Args[1]); } else { - this->ActualCommand = this->TestHandler->FindTheExecutable( - this->TestProperties->Args[1].c_str()); + this->ActualCommand = + this->TestHandler->FindTheExecutable(this->TestProperties->Args[1]); ++j; // skip the executable (it will be actualCommand) } std::string testCommand = diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h index d831247e1f..863ac1bfdf 100644 --- a/Source/CTest/cmCTestRunTest.h +++ b/Source/CTest/cmCTestRunTest.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestRunTest_h -#define cmCTestRunTest_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -159,5 +158,3 @@ inline int getNumWidth(size_t n) } return w; } - -#endif diff --git a/Source/CTest/cmCTestSVN.h b/Source/CTest/cmCTestSVN.h index b74dc12b0d..370d17682e 100644 --- a/Source/CTest/cmCTestSVN.h +++ b/Source/CTest/cmCTestSVN.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestSVN_h -#define cmCTestSVN_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -103,5 +102,3 @@ private: friend class UpdateParser; friend class ExternalParser; }; - -#endif diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx index 4fa4dc0daf..4808c36aa3 100644 --- a/Source/CTest/cmCTestScriptHandler.cxx +++ b/Source/CTest/cmCTestScriptHandler.cxx @@ -35,6 +35,7 @@ #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" @@ -372,8 +373,8 @@ int cmCTestScriptHandler::ReadInScript(const std::string& total_script_arg) int cmCTestScriptHandler::ExtractVariables() { // Temporary variables - const char* minInterval; - const char* contDuration; + cmProp minInterval; + cmProp contDuration; this->SourceDir = this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY"); @@ -412,7 +413,7 @@ int cmCTestScriptHandler::ExtractVariables() int i; for (i = 1; i < 10; ++i) { sprintf(updateVar, "CTEST_EXTRA_UPDATES_%i", i); - const char* updateVal = this->Makefile->GetDefinition(updateVar); + cmProp updateVal = this->Makefile->GetDefinition(updateVar); if (updateVal) { if (this->UpdateCmd.empty()) { cmSystemTools::Error( @@ -420,7 +421,7 @@ int cmCTestScriptHandler::ExtractVariables() " specified without specifying CTEST_CVS_COMMAND."); return 12; } - this->ExtraUpdates.emplace_back(updateVal); + this->ExtraUpdates.emplace_back(*updateVal); } } @@ -455,10 +456,10 @@ int cmCTestScriptHandler::ExtractVariables() // the script may override the minimum continuous interval if (minInterval) { - this->MinimumInterval = 60 * atof(minInterval); + this->MinimumInterval = 60 * atof(minInterval->c_str()); } if (contDuration) { - this->ContinuousDuration = 60.0 * atof(contDuration); + this->ContinuousDuration = 60.0 * atof(contDuration->c_str()); } this->UpdateElapsedTime(); @@ -932,13 +933,13 @@ cmDuration cmCTestScriptHandler::GetRemainingTimeAllowed() return cmCTest::MaxDuration(); } - const char* timelimitS = this->Makefile->GetDefinition("CTEST_TIME_LIMIT"); + cmProp timelimitS = this->Makefile->GetDefinition("CTEST_TIME_LIMIT"); if (!timelimitS) { return cmCTest::MaxDuration(); } - auto timelimit = cmDuration(atof(timelimitS)); + auto timelimit = cmDuration(atof(timelimitS->c_str())); auto duration = std::chrono::duration_cast<cmDuration>( std::chrono::steady_clock::now() - this->ScriptStartTime); diff --git a/Source/CTest/cmCTestScriptHandler.h b/Source/CTest/cmCTestScriptHandler.h index ebb79055ff..8eb96582bb 100644 --- a/Source/CTest/cmCTestScriptHandler.h +++ b/Source/CTest/cmCTestScriptHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestScriptHandler_h -#define cmCTestScriptHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -176,5 +175,3 @@ private: std::unique_ptr<cmGlobalGenerator> GlobalGenerator; std::unique_ptr<cmake> CMake; }; - -#endif diff --git a/Source/CTest/cmCTestSleepCommand.h b/Source/CTest/cmCTestSleepCommand.h index 1c3b8a1e1d..94255761d0 100644 --- a/Source/CTest/cmCTestSleepCommand.h +++ b/Source/CTest/cmCTestSleepCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestSleepCommand_h -#define cmCTestSleepCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -45,5 +44,3 @@ public: bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) override; }; - -#endif diff --git a/Source/CTest/cmCTestStartCommand.cxx b/Source/CTest/cmCTestStartCommand.cxx index fe684062d4..53e1b2f96f 100644 --- a/Source/CTest/cmCTestStartCommand.cxx +++ b/Source/CTest/cmCTestStartCommand.cxx @@ -9,6 +9,7 @@ #include "cmCTestVC.h" #include "cmGeneratedFileStream.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmSystemTools.h" class cmExecutionStatus; @@ -29,8 +30,8 @@ bool cmCTestStartCommand::InitialPass(std::vector<std::string> const& args, size_t cnt = 0; const char* smodel = nullptr; - const char* src_dir = nullptr; - const char* bld_dir = nullptr; + const std::string* src_dir = nullptr; + const std::string* bld_dir = nullptr; while (cnt < args.size()) { if (args[cnt] == "GROUP" || args[cnt] == "TRACK") { @@ -54,10 +55,10 @@ bool cmCTestStartCommand::InitialPass(std::vector<std::string> const& args, smodel = args[cnt].c_str(); cnt++; } else if (!src_dir) { - src_dir = args[cnt].c_str(); + src_dir = &args[cnt]; cnt++; } else if (!bld_dir) { - bld_dir = args[cnt].c_str(); + bld_dir = &args[cnt]; cnt++; } else { this->SetError("Too many arguments"); @@ -87,32 +88,31 @@ bool cmCTestStartCommand::InitialPass(std::vector<std::string> const& args, return false; } - cmSystemTools::AddKeepPath(src_dir); - cmSystemTools::AddKeepPath(bld_dir); + cmSystemTools::AddKeepPath(*src_dir); + cmSystemTools::AddKeepPath(*bld_dir); this->CTest->EmptyCTestConfiguration(); - std::string sourceDir = cmSystemTools::CollapseFullPath(src_dir); - std::string binaryDir = cmSystemTools::CollapseFullPath(bld_dir); - this->CTest->SetCTestConfiguration("SourceDirectory", sourceDir.c_str(), - this->Quiet); - this->CTest->SetCTestConfiguration("BuildDirectory", binaryDir.c_str(), + std::string sourceDir = cmSystemTools::CollapseFullPath(*src_dir); + std::string binaryDir = cmSystemTools::CollapseFullPath(*bld_dir); + this->CTest->SetCTestConfiguration("SourceDirectory", sourceDir, this->Quiet); + this->CTest->SetCTestConfiguration("BuildDirectory", binaryDir, this->Quiet); if (smodel) { cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Run dashboard with model " << smodel << std::endl - << " Source directory: " << src_dir << std::endl - << " Build directory: " << bld_dir << std::endl, + << " Source directory: " << *src_dir << std::endl + << " Build directory: " << *bld_dir << std::endl, this->Quiet); } else { cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Run dashboard with " "to-be-determined model" << std::endl - << " Source directory: " << src_dir << std::endl - << " Build directory: " << bld_dir << std::endl, + << " Source directory: " << *src_dir << std::endl + << " Build directory: " << *bld_dir << std::endl, this->Quiet); } const char* group = this->CTest->GetSpecificGroup(); @@ -162,7 +162,7 @@ bool cmCTestStartCommand::InitialCheckout(std::ostream& ofs, std::string const& sourceDir) { // Use the user-provided command to create the source tree. - const char* initialCheckoutCommand = + cmProp initialCheckoutCommand = this->Makefile->GetDefinition("CTEST_CHECKOUT_COMMAND"); if (!initialCheckoutCommand) { initialCheckoutCommand = @@ -172,7 +172,7 @@ bool cmCTestStartCommand::InitialCheckout(std::ostream& ofs, // Use a generic VC object to run and log the command. cmCTestVC vc(this->CTest, ofs); vc.SetSourceDirectory(sourceDir); - if (!vc.InitialCheckout(initialCheckoutCommand)) { + if (!vc.InitialCheckout(*initialCheckoutCommand)) { return false; } } diff --git a/Source/CTest/cmCTestStartCommand.h b/Source/CTest/cmCTestStartCommand.h index b30b1bb445..b3d06a729a 100644 --- a/Source/CTest/cmCTestStartCommand.h +++ b/Source/CTest/cmCTestStartCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestStartCommand_h -#define cmCTestStartCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -62,5 +61,3 @@ private: bool CreateNewTag; bool Quiet; }; - -#endif diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx index 279216eeb8..bdba0e5d29 100644 --- a/Source/CTest/cmCTestSubmitCommand.cxx +++ b/Source/CTest/cmCTestSubmitCommand.cxx @@ -16,6 +16,7 @@ #include "cmCommand.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -35,12 +36,12 @@ std::unique_ptr<cmCommand> cmCTestSubmitCommand::Clone() cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() { - const char* submitURL = !this->SubmitURL.empty() - ? this->SubmitURL.c_str() + const std::string* submitURL = !this->SubmitURL.empty() + ? &this->SubmitURL : this->Makefile->GetDefinition("CTEST_SUBMIT_URL"); if (submitURL) { - this->CTest->SetCTestConfiguration("SubmitURL", submitURL, this->Quiet); + this->CTest->SetCTestConfiguration("SubmitURL", *submitURL, this->Quiet); } else { this->CTest->SetCTestConfigurationFromCMakeVariable( this->Makefile, "DropMethod", "CTEST_DROP_METHOD", this->Quiet); @@ -58,17 +59,17 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() this->CTest->SetCTestConfigurationFromCMakeVariable( this->Makefile, "CurlOptions", "CTEST_CURL_OPTIONS", this->Quiet); - const char* notesFilesVariable = + cmProp notesFilesVariable = this->Makefile->GetDefinition("CTEST_NOTES_FILES"); if (notesFilesVariable) { - std::vector<std::string> notesFiles = cmExpandedList(notesFilesVariable); + std::vector<std::string> notesFiles = cmExpandedList(*notesFilesVariable); this->CTest->GenerateNotesFile(notesFiles); } - const char* extraFilesVariable = + cmProp extraFilesVariable = this->Makefile->GetDefinition("CTEST_EXTRA_SUBMIT_FILES"); if (extraFilesVariable) { - std::vector<std::string> extraFiles = cmExpandedList(extraFilesVariable); + std::vector<std::string> extraFiles = cmExpandedList(*extraFilesVariable); if (!this->CTest->SubmitExtraFiles(extraFiles)) { this->SetError("problem submitting extra files."); return nullptr; @@ -108,7 +109,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() if (this->PartsMentioned) { auto parts = cmMakeRange(this->Parts).transform([this](std::string const& arg) { - return this->CTest->GetPartFromName(arg.c_str()); + return this->CTest->GetPartFromName(arg); }); handler->SelectParts(std::set<cmCTest::Part>(parts.begin(), parts.end())); } @@ -177,7 +178,7 @@ void cmCTestSubmitCommand::CheckArguments( !this->Files.empty() || cm::contains(keywords, "FILES"); cm::erase_if(this->Parts, [this](std::string const& arg) -> bool { - cmCTest::Part p = this->CTest->GetPartFromName(arg.c_str()); + cmCTest::Part p = this->CTest->GetPartFromName(arg); if (p == cmCTest::PartCount) { std::ostringstream e; e << "Part name \"" << arg << "\" is invalid."; diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h index 90607713b1..c5d11dfd8b 100644 --- a/Source/CTest/cmCTestSubmitCommand.h +++ b/Source/CTest/cmCTestSubmitCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestSubmitCommand_h -#define cmCTestSubmitCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -55,5 +54,3 @@ protected: std::vector<std::string> HttpHeaders; std::vector<std::string> Parts; }; - -#endif diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index ea36df54a2..5b54573f34 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -797,7 +797,7 @@ int cmCTestSubmitHandler::ProcessHandler() gfile = gfile.substr(glen); cmCTestOptionalLog(this->CTest, DEBUG, "Glob file: " << gfile << std::endl, this->Quiet); - this->CTest->AddSubmitFile(cmCTest::PartCoverage, gfile.c_str()); + this->CTest->AddSubmitFile(cmCTest::PartCoverage, gfile); } } else { cmCTestLog(this->CTest, ERROR_MESSAGE, "Problem globbing" << std::endl); diff --git a/Source/CTest/cmCTestSubmitHandler.h b/Source/CTest/cmCTestSubmitHandler.h index 304daaa980..809c6157bd 100644 --- a/Source/CTest/cmCTestSubmitHandler.h +++ b/Source/CTest/cmCTestSubmitHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestSubmitHandler_h -#define cmCTestSubmitHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -77,5 +76,3 @@ private: std::set<std::string> Files; std::vector<std::string> HttpHeaders; }; - -#endif diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx index c71b409ff9..4403733731 100644 --- a/Source/CTest/cmCTestTestCommand.cxx +++ b/Source/CTest/cmCTestTestCommand.cxx @@ -13,6 +13,7 @@ #include "cmCTestTestHandler.h" #include "cmDuration.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" void cmCTestTestCommand::BindArguments() @@ -39,12 +40,11 @@ void cmCTestTestCommand::BindArguments() cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler() { - const char* ctestTimeout = - this->Makefile->GetDefinition("CTEST_TEST_TIMEOUT"); + cmProp ctestTimeout = this->Makefile->GetDefinition("CTEST_TEST_TIMEOUT"); cmDuration timeout; if (ctestTimeout) { - timeout = cmDuration(atof(ctestTimeout)); + timeout = cmDuration(atof(ctestTimeout->c_str())); } else { timeout = this->CTest->GetTimeOut(); if (timeout <= cmDuration::zero()) { @@ -54,10 +54,10 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler() } this->CTest->SetTimeOut(timeout); - const char* resourceSpecFile = + cmProp resourceSpecFile = this->Makefile->GetDefinition("CTEST_RESOURCE_SPEC_FILE"); if (this->ResourceSpecFile.empty() && resourceSpecFile) { - this->ResourceSpecFile = resourceSpecFile; + this->ResourceSpecFile = *resourceSpecFile; } cmCTestGenericHandler* handler = this->InitializeActualHandler(); @@ -114,19 +114,19 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler() // or CTEST_TEST_LOAD script variable, or ctest --test-load // command line argument... in that order. unsigned long testLoad; - const char* ctestTestLoad = this->Makefile->GetDefinition("CTEST_TEST_LOAD"); + cmProp ctestTestLoad = this->Makefile->GetDefinition("CTEST_TEST_LOAD"); if (!this->TestLoad.empty()) { - if (!cmStrToULong(this->TestLoad.c_str(), &testLoad)) { + if (!cmStrToULong(this->TestLoad, &testLoad)) { testLoad = 0; cmCTestLog(this->CTest, WARNING, "Invalid value for 'TEST_LOAD' : " << this->TestLoad << std::endl); } - } else if (ctestTestLoad && *ctestTestLoad) { - if (!cmStrToULong(ctestTestLoad, &testLoad)) { + } else if (cmNonempty(ctestTestLoad)) { + if (!cmStrToULong(*ctestTestLoad, &testLoad)) { testLoad = 0; cmCTestLog(this->CTest, WARNING, - "Invalid value for 'CTEST_TEST_LOAD' : " << ctestTestLoad + "Invalid value for 'CTEST_TEST_LOAD' : " << *ctestTestLoad << std::endl); } } else { @@ -134,10 +134,10 @@ cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler() } handler->SetTestLoad(testLoad); - if (const char* labelsForSubprojects = + if (cmProp labelsForSubprojects = this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) { this->CTest->SetCTestConfiguration("LabelsForSubprojects", - labelsForSubprojects, this->Quiet); + *labelsForSubprojects, this->Quiet); } handler->SetQuiet(this->Quiet); diff --git a/Source/CTest/cmCTestTestCommand.h b/Source/CTest/cmCTestTestCommand.h index 7925586792..624cd91fef 100644 --- a/Source/CTest/cmCTestTestCommand.h +++ b/Source/CTest/cmCTestTestCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestTestCommand_h -#define cmCTestTestCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -62,5 +61,3 @@ protected: std::string ResourceSpecFile; bool StopOnFailure = false; }; - -#endif diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index d0dbaae81c..4d1a589dc2 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -37,6 +37,7 @@ #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" @@ -819,14 +820,16 @@ void cmCTestTestHandler::CheckLabelFilter(cmCTestTestProperties& it) this->CheckLabelFilterExclude(it); } -void cmCTestTestHandler::ComputeTestList() +bool cmCTestTestHandler::ComputeTestList() { this->TestList.clear(); // clear list of test - this->GetListOfTests(); + if (!this->GetListOfTests()) { + return false; + } if (this->RerunFailed) { this->ComputeTestListForRerunFailed(); - return; + return true; } cmCTestTestHandler::ListOfTests::size_type tmsize = this->TestList.size(); @@ -882,6 +885,7 @@ void cmCTestTestHandler::ComputeTestList() this->TestList = finalList; this->UpdateMaxTestNameWidth(); + return true; } void cmCTestTestHandler::ComputeTestListForRerunFailed() @@ -1260,7 +1264,10 @@ bool cmCTestTestHandler::GetValue(const char* tag, std::string& value, bool cmCTestTestHandler::ProcessDirectory(std::vector<std::string>& passed, std::vector<std::string>& failed) { - this->ComputeTestList(); + if (!this->ComputeTestList()) { + return false; + } + this->StartTest = this->CTest->CurrentTime(); this->StartTestTime = std::chrono::system_clock::now(); auto elapsed_time_start = std::chrono::steady_clock::now(); @@ -1374,7 +1381,7 @@ void cmCTestTestHandler::GenerateDartOutput(cmXMLWriter& xml) xml.StartElement("TestList"); for (cmCTestTestResult const& result : this->TestResults) { std::string testPath = result.Path + "/" + result.Name; - xml.Element("Test", this->CTest->GetShortPathToFile(testPath.c_str())); + xml.Element("Test", this->CTest->GetShortPathToFile(testPath)); } xml.EndElement(); // TestList for (cmCTestTestResult& result : this->TestResults) { @@ -1483,8 +1490,8 @@ void cmCTestTestHandler::WriteTestResultHeader(cmXMLWriter& xml, } std::string testPath = result.Path + "/" + result.Name; xml.Element("Name", result.Name); - xml.Element("Path", this->CTest->GetShortPathToFile(result.Path.c_str())); - xml.Element("FullName", this->CTest->GetShortPathToFile(testPath.c_str())); + xml.Element("Path", this->CTest->GetShortPathToFile(result.Path)); + xml.Element("FullName", this->CTest->GetShortPathToFile(testPath)); xml.Element("FullCommandLine", result.FullCommandLine); } @@ -1546,12 +1553,12 @@ int cmCTestTestHandler::ExecuteCommands(std::vector<std::string>& vec) } // Find the appropriate executable to run for a test -std::string cmCTestTestHandler::FindTheExecutable(const char* exe) +std::string cmCTestTestHandler::FindTheExecutable(const std::string& exe) { std::string resConfig; std::vector<std::string> extraPaths; std::vector<std::string> failedPaths; - if (strcmp(exe, "NOT_AVAILABLE") == 0) { + if (exe == "NOT_AVAILABLE") { return exe; } return cmCTestTestHandler::FindExecutable(this->CTest, exe, resConfig, @@ -1607,7 +1614,7 @@ void cmCTestTestHandler::AddConfigurations( // Find the appropriate executable to run for a test std::string cmCTestTestHandler::FindExecutable( - cmCTest* ctest, const char* testCommand, std::string& resultingConfig, + cmCTest* ctest, const std::string& testCommand, std::string& resultingConfig, std::vector<std::string>& extraPaths, std::vector<std::string>& failed) { // now run the compiled test if we can find it @@ -1695,7 +1702,7 @@ bool cmCTestTestHandler::ParseResourceGroupsProperty( return lexer.ParseString(val); } -void cmCTestTestHandler::GetListOfTests() +bool cmCTestTestHandler::GetListOfTests() { if (!this->IncludeLabelRegExp.empty()) { this->IncludeLabelRegularExpression.compile( @@ -1748,22 +1755,24 @@ void cmCTestTestHandler::GetListOfTests() // does the DartTestfile.txt exist ? testFilename = "DartTestfile.txt"; } else { - return; + return true; } if (!mf.ReadListFile(testFilename)) { - return; + return false; } if (cmSystemTools::GetErrorOccuredFlag()) { - return; + // SEND_ERROR or FATAL_ERROR in CTestTestfile or TEST_INCLUDE_FILES + return false; } - const char* specFile = mf.GetDefinition("CTEST_RESOURCE_SPEC_FILE"); + cmProp specFile = mf.GetDefinition("CTEST_RESOURCE_SPEC_FILE"); if (this->ResourceSpecFile.empty() && specFile) { - this->ResourceSpecFile = specFile; + this->ResourceSpecFile = *specFile; } cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Done constructing a list of tests" << std::endl, this->Quiet); + return true; } void cmCTestTestHandler::UseIncludeRegExp() diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h index 0d88c306fb..aa29eeb019 100644 --- a/Source/CTest/cmCTestTestHandler.h +++ b/Source/CTest/cmCTestTestHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestTestHandler_h -#define cmCTestTestHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -198,7 +197,8 @@ public: std::string filepath, std::string& filename); // full signature static method to find an executable - static std::string FindExecutable(cmCTest* ctest, const char* testCommand, + static std::string FindExecutable(cmCTest* ctest, + const std::string& testCommand, std::string& resultingConfig, std::vector<std::string>& extraPaths, std::vector<std::string>& failed); @@ -285,10 +285,10 @@ private: /** * Get the list of tests in directory and subdirectories. */ - void GetListOfTests(); + bool GetListOfTests(); // compute the lists of tests that will actually run // based on union regex and -I stuff - void ComputeTestList(); + bool ComputeTestList(); // compute the lists of tests that will actually run // based on LastTestFailed.log @@ -309,7 +309,7 @@ private: /** * Find the executable for a test */ - std::string FindTheExecutable(const char* exe); + std::string FindTheExecutable(const std::string& exe); std::string GetTestStatus(cmCTestTestResult const&); void ExpandTestsToRunInformation(size_t numPossibleTests); @@ -359,5 +359,3 @@ private: int RepeatCount = 1; bool RerunFailed; }; - -#endif diff --git a/Source/CTest/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx index 673eb9ac11..6fef90a563 100644 --- a/Source/CTest/cmCTestUpdateCommand.cxx +++ b/Source/CTest/cmCTestUpdateCommand.cxx @@ -5,20 +5,20 @@ #include "cmCTest.h" #include "cmCTestUpdateHandler.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmSystemTools.h" cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler() { if (!this->Source.empty()) { this->CTest->SetCTestConfiguration( - "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source).c_str(), + "SourceDirectory", cmSystemTools::CollapseFullPath(this->Source), this->Quiet); } else { this->CTest->SetCTestConfiguration( "SourceDirectory", cmSystemTools::CollapseFullPath( - this->Makefile->GetDefinition("CTEST_SOURCE_DIRECTORY")) - .c_str(), + cmToCStrSafe(this->Makefile->GetDefinition("CTEST_SOURCE_DIRECTORY"))), this->Quiet); } std::string source_dir = diff --git a/Source/CTest/cmCTestUpdateCommand.h b/Source/CTest/cmCTestUpdateCommand.h index 5555c16377..e4c34536f1 100644 --- a/Source/CTest/cmCTestUpdateCommand.h +++ b/Source/CTest/cmCTestUpdateCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestUpdateCommand_h -#define cmCTestUpdateCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -42,5 +41,3 @@ public: protected: cmCTestGenericHandler* InitializeHandler() override; }; - -#endif diff --git a/Source/CTest/cmCTestUpdateHandler.h b/Source/CTest/cmCTestUpdateHandler.h index afc0e3d808..25bbb2f5cf 100644 --- a/Source/CTest/cmCTestUpdateHandler.h +++ b/Source/CTest/cmCTestUpdateHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestUpdateHandler_h -#define cmCTestUpdateHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -63,5 +62,3 @@ private: int DetectVCS(const char* dir); bool SelectVCS(); }; - -#endif diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h index 8334a9e08d..fe155f670f 100644 --- a/Source/CTest/cmCTestUploadCommand.h +++ b/Source/CTest/cmCTestUploadCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestUploadCommand_h -#define cmCTestUploadCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -48,5 +47,3 @@ protected: std::vector<std::string> Files; }; - -#endif diff --git a/Source/CTest/cmCTestUploadHandler.h b/Source/CTest/cmCTestUploadHandler.h index dde14df166..55d21c1f08 100644 --- a/Source/CTest/cmCTestUploadHandler.h +++ b/Source/CTest/cmCTestUploadHandler.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestUploadHandler_h -#define cmCTestUploadHandler_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -36,5 +35,3 @@ public: private: std::set<std::string> Files; }; - -#endif diff --git a/Source/CTest/cmCTestVC.h b/Source/CTest/cmCTestVC.h index 3037e01c94..9bd7229b3d 100644 --- a/Source/CTest/cmCTestVC.h +++ b/Source/CTest/cmCTestVC.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTestVC_h -#define cmCTestVC_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -150,5 +149,3 @@ protected: // Count paths reported with each PathStatus value. int PathCount[3]; }; - -#endif diff --git a/Source/CTest/cmParseBlanketJSCoverage.h b/Source/CTest/cmParseBlanketJSCoverage.h index cd1b225e5d..e107454d16 100644 --- a/Source/CTest/cmParseBlanketJSCoverage.h +++ b/Source/CTest/cmParseBlanketJSCoverage.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmParseBlanketJSCoverage_h -#define cmParseBlanketJSCoverage_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -39,4 +38,3 @@ protected: cmCTestCoverageHandlerContainer& Coverage; cmCTest* CTest; }; -#endif diff --git a/Source/CTest/cmParseCacheCoverage.h b/Source/CTest/cmParseCacheCoverage.h index a8200b7873..523f83b554 100644 --- a/Source/CTest/cmParseCacheCoverage.h +++ b/Source/CTest/cmParseCacheCoverage.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmParseCacheCoverage_h -#define cmParseCacheCoverage_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -31,5 +30,3 @@ protected: // Read a single mcov file bool ReadCMCovFile(const char* f); }; - -#endif diff --git a/Source/CTest/cmParseCoberturaCoverage.h b/Source/CTest/cmParseCoberturaCoverage.h index cb6d0976ba..0340433014 100644 --- a/Source/CTest/cmParseCoberturaCoverage.h +++ b/Source/CTest/cmParseCoberturaCoverage.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmParseCoberturaCoverage_h -#define cmParseCoberturaCoverage_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -41,5 +40,3 @@ private: cmCTest* CTest; std::string CurFileName; }; - -#endif diff --git a/Source/CTest/cmParseDelphiCoverage.h b/Source/CTest/cmParseDelphiCoverage.h index 1b374050a5..2a014a198c 100644 --- a/Source/CTest/cmParseDelphiCoverage.h +++ b/Source/CTest/cmParseDelphiCoverage.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmParseDelphiCoverage_h -#define cmParseDelphiCoverage_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -35,4 +34,3 @@ protected: cmCTestCoverageHandlerContainer& Coverage; cmCTest* CTest; }; -#endif diff --git a/Source/CTest/cmParseGTMCoverage.h b/Source/CTest/cmParseGTMCoverage.h index 41cc7f5754..c35bf6e982 100644 --- a/Source/CTest/cmParseGTMCoverage.h +++ b/Source/CTest/cmParseGTMCoverage.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmParseGTMCoverage_h -#define cmParseGTMCoverage_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -37,5 +36,3 @@ protected: bool ParseMCOVLine(std::string const& line, std::string& routine, std::string& function, int& linenumber, int& count); }; - -#endif diff --git a/Source/CTest/cmParseJacocoCoverage.h b/Source/CTest/cmParseJacocoCoverage.h index f2aec6db92..3442dd03a9 100644 --- a/Source/CTest/cmParseJacocoCoverage.h +++ b/Source/CTest/cmParseJacocoCoverage.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmParseJacocoCoverage_h -#define cmParseJacocoCoverage_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -49,5 +48,3 @@ private: cmCTestCoverageHandlerContainer& Coverage; cmCTest* CTest; }; - -#endif diff --git a/Source/CTest/cmParseMumpsCoverage.h b/Source/CTest/cmParseMumpsCoverage.h index 8c0870254c..00a8431edb 100644 --- a/Source/CTest/cmParseMumpsCoverage.h +++ b/Source/CTest/cmParseMumpsCoverage.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmParseMumpsCoverage_h -#define cmParseMumpsCoverage_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -43,5 +42,3 @@ protected: cmCTestCoverageHandlerContainer& Coverage; cmCTest* CTest; }; - -#endif diff --git a/Source/CTest/cmParsePHPCoverage.h b/Source/CTest/cmParsePHPCoverage.h index ff0e6366da..763a6bba15 100644 --- a/Source/CTest/cmParsePHPCoverage.h +++ b/Source/CTest/cmParsePHPCoverage.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmParsePHPCoverage_h -#define cmParsePHPCoverage_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -35,5 +34,3 @@ private: cmCTestCoverageHandlerContainer& Coverage; cmCTest* CTest; }; - -#endif diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx index a549117ca2..9ee1c170c2 100644 --- a/Source/CTest/cmProcess.cxx +++ b/Source/CTest/cmProcess.cxx @@ -545,17 +545,17 @@ std::string cmProcess::GetExitExceptionString() # endif # ifdef SIGABRT case SIGABRT: - exception_str = "Child aborted"; + exception_str = "Subprocess aborted"; break; # endif # ifdef SIGKILL case SIGKILL: - exception_str = "Child killed"; + exception_str = "Subprocess killed"; break; # endif # ifdef SIGTERM case SIGTERM: - exception_str = "Child terminated"; + exception_str = "Subprocess terminated"; break; # endif # ifdef SIGHUP diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h index 1e6578cf93..9eec9526f5 100644 --- a/Source/CTest/cmProcess.h +++ b/Source/CTest/cmProcess.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmProcess_h -#define cmProcess_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -132,5 +131,3 @@ private: int Id; int64_t ExitValue; }; - -#endif diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake index 50ccc7c869..7917d41faf 100644 --- a/Source/Checks/cm_cxx_features.cmake +++ b/Source/Checks/cm_cxx_features.cmake @@ -1,6 +1,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/cm_message_checks_compat.cmake) function(cm_check_cxx_feature name) + set(TRY_RUN_FEATURE "${ARGN}") string(TOUPPER ${name} FEATURE) if(NOT DEFINED CMake_HAVE_CXX_${FEATURE}) cm_message_checks_compat( @@ -12,15 +13,31 @@ function(cm_check_cxx_feature name) else() set(maybe_cxx_standard "") endif() - try_compile(CMake_HAVE_CXX_${FEATURE} - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_LIST_DIR}/cm_cxx_${name}.cxx - CMAKE_FLAGS ${maybe_cxx_standard} - OUTPUT_VARIABLE OUTPUT - ) + if (TRY_RUN_FEATURE) + try_run(CMake_RUN_CXX_${FEATURE} CMake_COMPILE_CXX_${FEATURE} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_LIST_DIR}/cm_cxx_${name}.cxx + CMAKE_FLAGS ${maybe_cxx_standard} + OUTPUT_VARIABLE OUTPUT + ) + if (CMake_RUN_CXX_${FEATURE} EQUAL "0" AND CMake_COMPILE_CXX_${FEATURE}) + set(CMake_HAVE_CXX_${FEATURE} ON CACHE INTERNAL "TRY_RUN" FORCE) + else() + set(CMake_HAVE_CXX_${FEATURE} OFF CACHE INTERNAL "TRY_RUN" FORCE) + endif() + else() + try_compile(CMake_HAVE_CXX_${FEATURE} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_LIST_DIR}/cm_cxx_${name}.cxx + CMAKE_FLAGS ${maybe_cxx_standard} + OUTPUT_VARIABLE OUTPUT + ) + endif() set(check_output "${OUTPUT}") # Filter out MSBuild output that looks like a warning. string(REGEX REPLACE " +0 Warning\\(s\\)" "" check_output "${check_output}") + # Filter out MSBuild output that looks like a warning. + string(REGEX REPLACE "[^\n]*warning MSB[0-9][0-9][0-9][0-9][^\n]*" "" check_output "${check_output}") # Filter out warnings caused by user flags. string(REGEX REPLACE "[^\n]*warning:[^\n]*-Winvalid-command-line-argument[^\n]*" "" check_output "${check_output}") # Filter out warnings caused by local configuration. @@ -63,3 +80,14 @@ if(CMake_HAVE_CXX_MAKE_UNIQUE) set(CMake_HAVE_CXX_UNIQUE_PTR 1) endif() cm_check_cxx_feature(unique_ptr) +if (NOT CMAKE_CXX_STANDARD LESS "17") + if (NOT CMAKE_CROSSCOMPILING OR CMAKE_CROSSCOMPILING_EMULATOR) + cm_check_cxx_feature(filesystem TRY_RUN) + else() + # In cross-compiling mode, it is not possible to check implementation bugs + # so rely only on conformance done by compilation + cm_check_cxx_feature(filesystem) + endif() +else() + set(CMake_HAVE_CXX_FILESYSTEM FALSE) +endif() diff --git a/Source/Checks/cm_cxx_filesystem.cxx b/Source/Checks/cm_cxx_filesystem.cxx new file mode 100644 index 0000000000..ae8acc56a6 --- /dev/null +++ b/Source/Checks/cm_cxx_filesystem.cxx @@ -0,0 +1,27 @@ + +#include <filesystem> + +int main() +{ + std::filesystem::path p0(L"/a/b/c"); + + std::filesystem::path p1("/a/b/c"); + std::filesystem::path p2("/a/b/c"); + if (p1 != p2) { + return 1; + } + +#if defined(_WIN32) + std::filesystem::path p3("//host/a/b/../c"); + if (p3.lexically_normal().generic_string() != "//host/a/c") { + return 1; + } + + std::filesystem::path p4("c://a/.///b/../"); + if (p4.lexically_normal().generic_string() != "c:/a/") { + return 1; + } +#endif + + return 0; +} diff --git a/Source/CursesDialog/ccmake.cxx b/Source/CursesDialog/ccmake.cxx index 9a26db5992..85e256b8bc 100644 --- a/Source/CursesDialog/ccmake.cxx +++ b/Source/CursesDialog/ccmake.cxx @@ -2,11 +2,15 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include <csignal> +#include <cstdio> +#include <cstdlib> #include <cstring> #include <iostream> #include <string> #include <vector> +#include <unistd.h> + #include "cmsys/Encoding.hxx" #include "cmCursesColor.h" @@ -54,7 +58,12 @@ void onsig(int /*unused*/) { if (cmCursesForm::CurrentForm) { endwin(); - initscr(); /* Initialization */ + if (initscr() == nullptr) { + static const char errmsg[] = "Error: ncurses initialization failed\n"; + auto r = write(STDERR_FILENO, errmsg, sizeof(errmsg) - 1); + static_cast<void>(r); + exit(1); + } noecho(); /* Echo off */ cbreak(); /* nl- or cr not needed */ keypad(stdscr, true); /* Use key symbols as KEY_DOWN */ @@ -124,7 +133,10 @@ int main(int argc, char const* const* argv) cmCursesForm::DebugStart(); } - initscr(); /* Initialization */ + if (initscr() == nullptr) { + fprintf(stderr, "Error: ncurses initialization failed\n"); + exit(1); + } noecho(); /* Echo off */ cbreak(); /* nl- or cr not needed */ keypad(stdscr, true); /* Use key symbols as KEY_DOWN */ diff --git a/Source/CursesDialog/cmCursesBoolWidget.h b/Source/CursesDialog/cmCursesBoolWidget.h index 8c96256356..746825bb55 100644 --- a/Source/CursesDialog/cmCursesBoolWidget.h +++ b/Source/CursesDialog/cmCursesBoolWidget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesBoolWidget_h -#define cmCursesBoolWidget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -29,5 +28,3 @@ public: void SetValueAsBool(bool value); bool GetValueAsBool(); }; - -#endif // cmCursesBoolWidget_h diff --git a/Source/CursesDialog/cmCursesCacheEntryComposite.h b/Source/CursesDialog/cmCursesCacheEntryComposite.h index a71136331b..d4149180e7 100644 --- a/Source/CursesDialog/cmCursesCacheEntryComposite.h +++ b/Source/CursesDialog/cmCursesCacheEntryComposite.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesCacheEntryComposite_h -#define cmCursesCacheEntryComposite_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -41,5 +40,3 @@ protected: int LabelWidth; int EntryWidth; }; - -#endif // cmCursesCacheEntryComposite_h diff --git a/Source/CursesDialog/cmCursesColor.h b/Source/CursesDialog/cmCursesColor.h index f83265f05c..4e8a1e4bd9 100644 --- a/Source/CursesDialog/cmCursesColor.h +++ b/Source/CursesDialog/cmCursesColor.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesColor_h -#define cmCursesColor_h +#pragma once class cmCursesColor { @@ -23,5 +22,3 @@ public: protected: static short GetColor(char id, short fallback); }; - -#endif // cmCursesColor_h diff --git a/Source/CursesDialog/cmCursesDummyWidget.h b/Source/CursesDialog/cmCursesDummyWidget.h index 07b7288abe..4347746421 100644 --- a/Source/CursesDialog/cmCursesDummyWidget.h +++ b/Source/CursesDialog/cmCursesDummyWidget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesDummyWidget_h -#define cmCursesDummyWidget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -24,5 +23,3 @@ public: // handled. bool HandleInput(int& key, cmCursesMainForm* fm, WINDOW* w) override; }; - -#endif // cmCursesDummyWidget_h diff --git a/Source/CursesDialog/cmCursesFilePathWidget.h b/Source/CursesDialog/cmCursesFilePathWidget.h index 3f71259d5d..2ae5d1492d 100644 --- a/Source/CursesDialog/cmCursesFilePathWidget.h +++ b/Source/CursesDialog/cmCursesFilePathWidget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesFilePathWidget_h -#define cmCursesFilePathWidget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -15,5 +14,3 @@ public: cmCursesFilePathWidget(cmCursesFilePathWidget const&) = delete; cmCursesFilePathWidget& operator=(cmCursesFilePathWidget const&) = delete; }; - -#endif // cmCursesFilePathWidget_h diff --git a/Source/CursesDialog/cmCursesForm.h b/Source/CursesDialog/cmCursesForm.h index e3626e66a1..93459b9bb5 100644 --- a/Source/CursesDialog/cmCursesForm.h +++ b/Source/CursesDialog/cmCursesForm.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesForm_h -#define cmCursesForm_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -62,5 +61,3 @@ protected: FORM* Form; }; - -#endif // cmCursesForm_h diff --git a/Source/CursesDialog/cmCursesLabelWidget.h b/Source/CursesDialog/cmCursesLabelWidget.h index 9e7568187b..c10aa37c55 100644 --- a/Source/CursesDialog/cmCursesLabelWidget.h +++ b/Source/CursesDialog/cmCursesLabelWidget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesLabelWidget_h -#define cmCursesLabelWidget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -28,5 +27,3 @@ public: // handled bool HandleInput(int& key, cmCursesMainForm* fm, WINDOW* w) override; }; - -#endif // cmCursesLabelWidget_h diff --git a/Source/CursesDialog/cmCursesLongMessageForm.h b/Source/CursesDialog/cmCursesLongMessageForm.h index da9fea2b15..4f69cb192e 100644 --- a/Source/CursesDialog/cmCursesLongMessageForm.h +++ b/Source/CursesDialog/cmCursesLongMessageForm.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesLongMessageForm_h -#define cmCursesLongMessageForm_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -59,5 +58,3 @@ protected: FIELD* Fields[2]; }; - -#endif // cmCursesLongMessageForm_h diff --git a/Source/CursesDialog/cmCursesMainForm.h b/Source/CursesDialog/cmCursesMainForm.h index 2e06b90a1c..c6db66f679 100644 --- a/Source/CursesDialog/cmCursesMainForm.h +++ b/Source/CursesDialog/cmCursesMainForm.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesMainForm_h -#define cmCursesMainForm_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -170,5 +169,3 @@ protected: std::string OldSearchString; bool SearchMode; }; - -#endif // cmCursesMainForm_h diff --git a/Source/CursesDialog/cmCursesOptionsWidget.h b/Source/CursesDialog/cmCursesOptionsWidget.h index 0de8e64073..cb06e4d5ad 100644 --- a/Source/CursesDialog/cmCursesOptionsWidget.h +++ b/Source/CursesDialog/cmCursesOptionsWidget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesOptionsWidget_h -#define cmCursesOptionsWidget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -35,5 +34,3 @@ protected: std::vector<std::string> Options; std::vector<std::string>::size_type CurrentOption; }; - -#endif // cmCursesOptionsWidget_h diff --git a/Source/CursesDialog/cmCursesPathWidget.h b/Source/CursesDialog/cmCursesPathWidget.h index fb365e9c5a..79e342ec4c 100644 --- a/Source/CursesDialog/cmCursesPathWidget.h +++ b/Source/CursesDialog/cmCursesPathWidget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesPathWidget_h -#define cmCursesPathWidget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -34,5 +33,3 @@ protected: bool Cycle; std::string::size_type CurrentIndex; }; - -#endif // cmCursesPathWidget_h diff --git a/Source/CursesDialog/cmCursesStandardIncludes.h b/Source/CursesDialog/cmCursesStandardIncludes.h index 5b0ad5824f..9745b97de5 100644 --- a/Source/CursesDialog/cmCursesStandardIncludes.h +++ b/Source/CursesDialog/cmCursesStandardIncludes.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesStandardIncludes_h -#define cmCursesStandardIncludes_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -41,5 +40,3 @@ inline void curses_clear() # undef __attribute__ #endif #undef cm_no__attribute__ - -#endif // cmCursesStandardIncludes_h diff --git a/Source/CursesDialog/cmCursesStringWidget.h b/Source/CursesDialog/cmCursesStringWidget.h index ce06c6da74..faa2adead3 100644 --- a/Source/CursesDialog/cmCursesStringWidget.h +++ b/Source/CursesDialog/cmCursesStringWidget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesStringWidget_h -#define cmCursesStringWidget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -65,5 +64,3 @@ protected: std::string OriginalString; bool Done; }; - -#endif // cmCursesStringWidget_h diff --git a/Source/CursesDialog/cmCursesWidget.h b/Source/CursesDialog/cmCursesWidget.h index 9d03c6e422..29ec28b305 100644 --- a/Source/CursesDialog/cmCursesWidget.h +++ b/Source/CursesDialog/cmCursesWidget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCursesWidget_h -#define cmCursesWidget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -68,5 +67,3 @@ protected: // The page in the main form this widget is in int Page; }; - -#endif // cmCursesWidget_h diff --git a/Source/CursesDialog/form/CMakeLists.txt b/Source/CursesDialog/form/CMakeLists.txt index b468f5b318..21c499e83e 100644 --- a/Source/CursesDialog/form/CMakeLists.txt +++ b/Source/CursesDialog/form/CMakeLists.txt @@ -3,6 +3,14 @@ project(CMAKE_FORM) +# Disable warnings to avoid changing 3rd party code. +if(CMAKE_C_COMPILER_ID MATCHES + "^(GNU|Clang|AppleClang|XLClang|XL|VisualAge|SunPro|HP|Intel)$") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w") +elseif(CMAKE_C_COMPILER_ID STREQUAL "PathScale") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall") +endif() + configure_file(cmFormConfigure.h.in "${CMAKE_CURRENT_BINARY_DIR}/cmFormConfigure.h") add_library(cmForm diff --git a/Source/CursesDialog/form/frm_driver.c b/Source/CursesDialog/form/frm_driver.c index e4e72aa9ce..112ab0898c 100644 --- a/Source/CursesDialog/form/frm_driver.c +++ b/Source/CursesDialog/form/frm_driver.c @@ -2983,7 +2983,7 @@ INLINE static FIELD *Right_Neighbour_Field(FIELD * field) | Function : static FIELD *Upper_Neighbour_Field(FIELD * field) | | Description : Because of the row-major nature of sorting the fields, -| its more difficult to define whats the upper neighbour +| its more difficult to define what's the upper neighbour | field really means. We define that it must be on a | 'previous' line (cyclic order!) and is the rightmost | field laying on the left side of the given field. If @@ -3030,7 +3030,7 @@ static FIELD *Upper_Neighbour_Field(FIELD * field) | Function : static FIELD *Down_Neighbour_Field(FIELD * field) | | Description : Because of the row-major nature of sorting the fields, -| its more difficult to define whats the down neighbour +| its more difficult to define what's the down neighbour | field really means. We define that it must be on a | 'next' line (cyclic order!) and is the leftmost | field laying on the right side of the given field. If diff --git a/Source/LexerParser/cmCommandArgumentLexer.cxx b/Source/LexerParser/cmCommandArgumentLexer.cxx index 58799123f9..46220ffc7c 100644 --- a/Source/LexerParser/cmCommandArgumentLexer.cxx +++ b/Source/LexerParser/cmCommandArgumentLexer.cxx @@ -653,7 +653,7 @@ This file must be translated to C++ and modified to build everywhere. Run flex >= 2.6 like this: - flex --nounistd -DFLEXINT_H --noline --header-file=cmCommandArgumentLexer.h -ocmCommandArgumentLexer.cxx cmCommandArgumentLexer.in.l + flex --nounistd --never-interactive --batch -DFLEXINT_H --noline --header-file=cmCommandArgumentLexer.h -ocmCommandArgumentLexer.cxx cmCommandArgumentLexer.in.l Modify cmCommandArgumentLexer.cxx: - remove trailing whitespace: sed -i 's/\s*$//' cmCommandArgumentLexer.h cmCommandArgumentLexer.cxx @@ -668,10 +668,7 @@ Modify cmCommandArgumentLexer.cxx: #include "cmCommandArgumentParserHelper.h" -/* Replace the lexer input function. */ -#undef YY_INPUT -#define YY_INPUT(buf, result, max_size) \ - do { result = yyextra->LexInput(buf, max_size); } while (0) +#define YY_USER_ACTION yyextra->UpdateInputPosition(yyleng); /* Include the set of tokens from the parser. */ #include "cmCommandArgumentParserTokens.h" @@ -967,16 +964,12 @@ yy_match: yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } - while ( yy_base[yy_current_state] != 41 ); + while ( yy_current_state != 29 ); + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; yy_find_action: yy_act = yy_accept[yy_current_state]; - if ( yy_act == 0 ) - { /* have to back up */ - yy_cp = yyg->yy_last_accepting_cpos; - yy_current_state = yyg->yy_last_accepting_state; - yy_act = yy_accept[yy_current_state]; - } YY_DO_BEFORE_ACTION; @@ -1173,7 +1166,8 @@ case YY_STATE_EOF(NOESCAPES): else { - yy_cp = yyg->yy_c_buf_p; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; } } @@ -1661,7 +1655,7 @@ static void yy_load_buffer_state (yyscan_t yyscanner) b->yy_bs_column = 0; } - b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + b->yy_is_interactive = 0; errno = oerrno; } diff --git a/Source/LexerParser/cmCommandArgumentLexer.in.l b/Source/LexerParser/cmCommandArgumentLexer.in.l index 010d54bf57..8ad23351ac 100644 --- a/Source/LexerParser/cmCommandArgumentLexer.in.l +++ b/Source/LexerParser/cmCommandArgumentLexer.in.l @@ -7,7 +7,7 @@ This file must be translated to C++ and modified to build everywhere. Run flex >= 2.6 like this: - flex --nounistd -DFLEXINT_H --noline --header-file=cmCommandArgumentLexer.h -ocmCommandArgumentLexer.cxx cmCommandArgumentLexer.in.l + flex --nounistd --never-interactive --batch -DFLEXINT_H --noline --header-file=cmCommandArgumentLexer.h -ocmCommandArgumentLexer.cxx cmCommandArgumentLexer.in.l Modify cmCommandArgumentLexer.cxx: - remove trailing whitespace: sed -i 's/\s*$//' cmCommandArgumentLexer.h cmCommandArgumentLexer.cxx @@ -22,10 +22,7 @@ Modify cmCommandArgumentLexer.cxx: #include "cmCommandArgumentParserHelper.h" -/* Replace the lexer input function. */ -#undef YY_INPUT -#define YY_INPUT(buf, result, max_size) \ - do { result = yyextra->LexInput(buf, max_size); } while (0) +#define YY_USER_ACTION yyextra->UpdateInputPosition(yyleng); /* Include the set of tokens from the parser. */ #include "cmCommandArgumentParserTokens.h" diff --git a/Source/QtDialog/AddCacheEntry.cxx b/Source/QtDialog/AddCacheEntry.cxx index f5e0777bfc..1075895b92 100644 --- a/Source/QtDialog/AddCacheEntry.cxx +++ b/Source/QtDialog/AddCacheEntry.cxx @@ -40,8 +40,10 @@ AddCacheEntry::AddCacheEntry(QWidget* p, const QStringList& varNames, AddCacheEntry::setTabOrder(string, this->Description); QCompleter* completer = new QCompleter(this->VarNames, this); this->Name->setCompleter(completer); - connect(completer, SIGNAL(activated(const QString&)), this, - SLOT(onCompletionActivated(const QString&))); + connect( + completer, + static_cast<void (QCompleter::*)(const QString&)>(&QCompleter::activated), + this, &AddCacheEntry::onCompletionActivated); } QString AddCacheEntry::name() const diff --git a/Source/QtDialog/AddCacheEntry.h b/Source/QtDialog/AddCacheEntry.h index e7a60ddb37..35522c5e82 100644 --- a/Source/QtDialog/AddCacheEntry.h +++ b/Source/QtDialog/AddCacheEntry.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef AddCacheEntry_h -#define AddCacheEntry_h +#pragma once #include "QCMake.h" #include <QCheckBox> @@ -32,5 +31,3 @@ private: const QStringList& VarNames; const QStringList& VarTypes; }; - -#endif diff --git a/Source/QtDialog/CMakeGUIExec.cxx b/Source/QtDialog/CMakeGUIExec.cxx new file mode 100644 index 0000000000..157211220c --- /dev/null +++ b/Source/QtDialog/CMakeGUIExec.cxx @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <QApplication> + +class CMakeSetupDialog; + +void SetupDefaultQSettings() +{ +} + +int CMakeGUIExec(CMakeSetupDialog* /*window*/) +{ + return QApplication::exec(); +} diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 98dd0e212b..0f0c39a1f1 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -3,125 +3,104 @@ project(QtDialog) CMake_OPTIONAL_COMPONENT(cmake-gui) -find_package(Qt5Widgets QUIET) -if (Qt5Widgets_FOUND) - include_directories(${Qt5Widgets_INCLUDE_DIRS}) - add_definitions(${Qt5Widgets_DEFINITONS}) - macro(qt4_wrap_ui) - qt5_wrap_ui(${ARGN}) - endmacro() - macro(qt4_wrap_cpp) - qt5_wrap_cpp(${ARGN}) - endmacro() - macro(qt4_add_resources) - qt5_add_resources(${ARGN}) - endmacro() +find_package(Qt5Widgets REQUIRED) - set(CMake_QT_LIBRARIES ${Qt5Widgets_LIBRARIES}) - set(QT_QTMAIN_LIBRARY ${Qt5Core_QTMAIN_LIBRARIES}) +set(CMake_QT_EXTRA_LIBRARIES) - # Try to find the package WinExtras for the task bar progress - if(WIN32) - find_package(Qt5WinExtras QUIET) - if (Qt5WinExtras_FOUND) - include_directories(${Qt5WinExtras_INCLUDE_DIRS}) - add_definitions(-DQT_WINEXTRAS) - list(APPEND CMake_QT_LIBRARIES ${Qt5WinExtras_LIBRARIES}) - endif() +# Try to find the package WinExtras for the task bar progress +if(WIN32) + find_package(Qt5WinExtras QUIET) + if (Qt5WinExtras_FOUND) + add_definitions(-DQT_WINEXTRAS) + list(APPEND CMake_QT_EXTRA_LIBRARIES Qt5::WinExtras) endif() +endif() - # Remove this when the minimum version of Qt is 4.6. - add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0) - - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") - if(CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES) - list(APPEND CMake_QT_LIBRARIES ${CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES}) - set_property(SOURCE CMakeSetup.cxx - PROPERTY COMPILE_DEFINITIONS USE_QXcbIntegrationPlugin) - endif() +if(CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES) + list(APPEND CMake_QT_EXTRA_LIBRARIES ${CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES}) + set_property(SOURCE CMakeSetup.cxx + PROPERTY COMPILE_DEFINITIONS USE_QXcbIntegrationPlugin) +endif() - if(CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES) - list(APPEND CMake_QT_LIBRARIES ${CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES}) - set_property(SOURCE CMakeSetup.cxx - PROPERTY COMPILE_DEFINITIONS USE_QWindowsIntegrationPlugin) - endif() +if(CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES) + list(APPEND CMake_QT_EXTRA_LIBRARIES ${CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES}) + set_property(SOURCE CMakeSetup.cxx + PROPERTY COMPILE_DEFINITIONS USE_QWindowsIntegrationPlugin) +endif() - # We need to install platform plugin and add qt.conf for Qt5 on Mac and Windows. - # FIXME: This should be part of Qt5 CMake scripts, but unfortunately - # Qt5 support is missing there. - if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) - macro(install_qt5_plugin _qt_plugin_name _qt_plugins_var) - get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION) - if(EXISTS "${_qt_plugin_path}") - get_filename_component(_qt_plugin_file "${_qt_plugin_path}" NAME) - get_filename_component(_qt_plugin_type "${_qt_plugin_path}" PATH) - get_filename_component(_qt_plugin_type "${_qt_plugin_type}" NAME) - if(APPLE) - set(_qt_plugin_dir "PlugIns") - elseif(WIN32) - set(_qt_plugin_dir "plugins") - endif() - set(_qt_plugin_dest "${_qt_plugin_dir}/${_qt_plugin_type}") - install(FILES "${_qt_plugin_path}" - DESTINATION "${_qt_plugin_dest}" - ${COMPONENT}) - set(${_qt_plugins_var} - "${${_qt_plugins_var}};\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${_qt_plugin_dest}/${_qt_plugin_file}") - else() - message(FATAL_ERROR "QT plugin ${_qt_plugin_name} not found") +# We need to install platform plugin and add qt.conf for Qt5 on Mac and Windows. +# FIXME: This should be part of Qt5 CMake scripts, but unfortunately +# Qt5 support is missing there. +if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) + macro(install_qt5_plugin _qt_plugin_name _qt_plugins_var) + get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION) + if(EXISTS "${_qt_plugin_path}") + get_filename_component(_qt_plugin_file "${_qt_plugin_path}" NAME) + get_filename_component(_qt_plugin_type "${_qt_plugin_path}" PATH) + get_filename_component(_qt_plugin_type "${_qt_plugin_type}" NAME) + if(APPLE) + set(_qt_plugin_dir "PlugIns") + elseif(WIN32) + set(_qt_plugin_dir "plugins") endif() - endmacro() - if(APPLE) - install_qt5_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS) - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" - "[Paths]\nPlugins = ${_qt_plugin_dir}\n") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" - DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources" - ${COMPONENT}) - elseif(WIN32 AND NOT CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES) - install_qt5_plugin("Qt5::QWindowsIntegrationPlugin" QT_PLUGINS) - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" - "[Paths]\nPlugins = ../${_qt_plugin_dir}\n") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" - DESTINATION bin + set(_qt_plugin_dest "${_qt_plugin_dir}/${_qt_plugin_type}") + install(FILES "${_qt_plugin_path}" + DESTINATION "${_qt_plugin_dest}" ${COMPONENT}) + set(${_qt_plugins_var} + "${${_qt_plugins_var}};\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${_qt_plugin_dest}/${_qt_plugin_file}") + else() + message(FATAL_ERROR "QT plugin ${_qt_plugin_name} not found") endif() - endif() - - if(TARGET Qt5::Core) - get_property(_Qt5_Core_LOCATION TARGET Qt5::Core PROPERTY LOCATION) - get_filename_component(Qt_BIN_DIR "${_Qt5_Core_LOCATION}" PATH) - if(APPLE) - get_filename_component(Qt_BIN_DIR "${Qt_BIN_DIR}" PATH) + endmacro() + if(APPLE) + install_qt5_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS) + if(TARGET Qt5::QMacStylePlugin) + install_qt5_plugin("Qt5::QMacStylePlugin" QT_PLUGINS) endif() + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + "[Paths]\nPlugins = ${_qt_plugin_dir}\n") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources" + ${COMPONENT}) + elseif(WIN32 AND NOT CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES) + install_qt5_plugin("Qt5::QWindowsIntegrationPlugin" QT_PLUGINS) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + "[Paths]\nPlugins = ../${_qt_plugin_dir}\n") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + DESTINATION bin + ${COMPONENT}) endif() -else() - set(QT_MIN_VERSION "4.4.0") - find_package(Qt4 REQUIRED) - if(NOT QT4_FOUND) - message(SEND_ERROR "Failed to find Qt 4.4 or greater.") - return() - endif() - - include(${QT_USE_FILE}) - - set(CMake_QT_LIBRARIES ${QT_LIBRARIES}) +endif() +get_property(_Qt5_Core_LOCATION TARGET Qt5::Core PROPERTY LOCATION) +get_filename_component(Qt_BIN_DIR "${_Qt5_Core_LOCATION}" PATH) +if(APPLE) + get_filename_component(Qt_BIN_DIR "${Qt_BIN_DIR}" PATH) endif() set(SRCS AddCacheEntry.cxx AddCacheEntry.h - CMakeSetup.cxx CMakeSetupDialog.cxx CMakeSetupDialog.h + Compilers.h + EnvironmentDialog.cxx + EnvironmentDialog.h FirstConfigure.cxx FirstConfigure.h QCMake.cxx QCMake.h QCMakeCacheView.cxx QCMakeCacheView.h + QCMakePreset.cxx + QCMakePreset.h + QCMakePresetComboBox.cxx + QCMakePresetComboBox.h + QCMakePresetItemModel.cxx + QCMakePresetItemModel.h QCMakeWidgets.cxx QCMakeWidgets.h RegexExplorer.cxx @@ -129,37 +108,41 @@ set(SRCS WarningMessagesDialog.cxx WarningMessagesDialog.h ) -QT4_WRAP_UI(UI_SRCS +qt5_wrap_ui(UI_SRCS CMakeSetupDialog.ui Compilers.ui CrossCompiler.ui AddCacheEntry.ui + EnvironmentDialog.ui RegexExplorer.ui WarningMessagesDialog.ui ) -QT4_WRAP_CPP(MOC_SRCS +qt5_wrap_cpp(MOC_SRCS AddCacheEntry.h Compilers.h CMakeSetupDialog.h + EnvironmentDialog.h FirstConfigure.h QCMake.h QCMakeCacheView.h + QCMakePresetComboBox.h + QCMakePresetItemModel.h QCMakeWidgets.h RegexExplorer.h WarningMessagesDialog.h ) -QT4_ADD_RESOURCES(RC_SRCS CMakeSetup.qrc) +qt5_add_resources(RC_SRCS CMakeSetup.qrc) +add_library(CMakeGUIQRCLib OBJECT ${RC_SRCS}) -set(SRCS ${SRCS} ${UI_SRCS} ${MOC_SRCS} ${RC_SRCS}) -if(WIN32) - set(SRCS ${SRCS} CMakeSetup.rc) -endif() -if(APPLE) - set(SRCS ${SRCS} CMakeSetup.icns) - set(MACOSX_BUNDLE_ICON_FILE CMakeSetup.icns) - set_source_files_properties(CMakeSetup.icns PROPERTIES - MACOSX_PACKAGE_LOCATION Resources) -endif() +if (FALSE) # CMake's bootstrap binary does not support automoc + set(CMAKE_AUTOMOC 1) + set(CMAKE_AUTORCC 1) + set(CMAKE_AUTOUIC 1) +else () + list(APPEND SRCS + ${UI_SRCS} + ${MOC_SRCS}) +endif () if(USE_LGPL) install(FILES ${CMake_SOURCE_DIR}/Licenses/LGPLv${USE_LGPL}.txt @@ -171,11 +154,25 @@ endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) -add_executable(cmake-gui WIN32 MACOSX_BUNDLE ${SRCS} ${MANIFEST_FILE}) -target_link_libraries(cmake-gui CMakeLib ${QT_QTMAIN_LIBRARY} ${CMake_QT_LIBRARIES}) +add_library(CMakeGUILib STATIC ${SRCS}) +# CMake_QT_EXTRA_LIBRARIES have to come before the main libraries on the link line +target_link_libraries(CMakeGUILib PUBLIC CMakeLib ${CMake_QT_EXTRA_LIBRARIES} Qt5::Core Qt5::Widgets) + +add_library(CMakeGUIMainLib STATIC CMakeSetup.cxx) +target_link_libraries(CMakeGUIMainLib PUBLIC CMakeGUILib) +add_executable(cmake-gui WIN32 MACOSX_BUNDLE CMakeGUIExec.cxx ${MANIFEST_FILE}) +target_link_libraries(cmake-gui CMakeGUIMainLib Qt5::Core) + +target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeGUIQRCLib>) if(WIN32) - target_sources(cmake-gui PRIVATE $<TARGET_OBJECTS:CMakeVersion>) + target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeVersion> CMakeSetup.rc) +endif() +if(APPLE) + target_sources(CMakeGUIMainLib INTERFACE CMakeSetup.icns) + set(MACOSX_BUNDLE_ICON_FILE CMakeSetup.icns) + set_source_files_properties(CMakeSetup.icns PROPERTIES + MACOSX_PACKAGE_LOCATION Resources) endif() if(CMake_JOB_POOL_LINK_BIN) @@ -184,7 +181,7 @@ endif() # cmake-gui has not been updated for `include-what-you-use`. # Block the tool until this is done. -set_target_properties(cmake-gui PROPERTIES +set_target_properties(CMakeGUILib CMakeGUIMainLib cmake-gui PROPERTIES CXX_INCLUDE_WHAT_YOU_USE "" ) diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx index 9d928b2ff9..c1555a26e8 100644 --- a/Source/QtDialog/CMakeSetup.cxx +++ b/Source/QtDialog/CMakeSetup.cxx @@ -21,7 +21,6 @@ #include "cmDocumentationEntry.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" // IWYU pragma: keep -#include "cmVersion.h" #include "cmake.h" static const char* cmDocumentationName[][2] = { { nullptr, @@ -33,11 +32,17 @@ static const char* cmDocumentationUsage[][2] = { " cmake-gui [options]\n" " cmake-gui [options] <path-to-source>\n" " cmake-gui [options] <path-to-existing-build>\n" - " cmake-gui [options] -S <path-to-source> -B <path-to-build>\n" }, + " cmake-gui [options] -S <path-to-source> -B <path-to-build>\n" + " cmake-gui [options] --browse-manual\n" }, { nullptr, nullptr } }; -static const char* cmDocumentationOptions[][2] = { { nullptr, nullptr } }; +static const char* cmDocumentationOptions[][2] = { + { "-S <path-to-source>", "Explicitly specify a source directory." }, + { "-B <path-to-build>", "Explicitly specify a build directory." }, + { "--preset=<preset>", "Specify a configure preset." }, + { nullptr, nullptr } +}; #if defined(Q_OS_MAC) static int cmOSXInstall(std::string dir); @@ -55,6 +60,10 @@ Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin); # endif #endif +int CMakeGUIExec(CMakeSetupDialog* window); +void SetupDefaultQSettings(); +void OpenReferenceManual(); + int main(int argc, char** argv) { cmSystemTools::EnsureStdPipes(); @@ -108,6 +117,7 @@ int main(int argc, char** argv) QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif + SetupDefaultQSettings(); QApplication app(argc, argv); setlocale(LC_NUMERIC, "C"); @@ -115,14 +125,6 @@ int main(int argc, char** argv) QTextCodec* utf8_codec = QTextCodec::codecForName("UTF-8"); QTextCodec::setCodecForLocale(utf8_codec); -#if QT_VERSION < 0x050000 - // clean out standard Qt paths for plugins, which we don't use anyway - // when creating Mac bundles, it potentially causes problems - foreach (QString p, QApplication::libraryPaths()) { - QApplication::removeLibraryPath(p); - } -#endif - // tell the cmake library where cmake is QDir cmExecDir(QApplication::applicationDirPath()); #if defined(Q_OS_MAC) @@ -152,6 +154,7 @@ int main(int argc, char** argv) QStringList args = QApplication::arguments(); std::string binaryDirectory; std::string sourceDirectory; + std::string presetName; for (int i = 1; i < args.size(); ++i) { const QString& arg = args[i]; if (arg.startsWith("-S")) { @@ -190,11 +193,31 @@ int main(int argc, char** argv) binaryDirectory = cmSystemTools::CollapseFullPath(path.toLocal8Bit().data()); cmSystemTools::ConvertToUnixSlashes(binaryDirectory); + } else if (arg.startsWith("--preset=")) { + QString preset = arg.mid(cmStrLen("--preset=")); + if (preset.isEmpty()) { + std::cerr << "No preset specified for --preset" << std::endl; + return 1; + } + presetName = preset.toLocal8Bit().data(); + } else if (arg == "--browse-manual") { + OpenReferenceManual(); + return 0; } } - if (!sourceDirectory.empty() && !binaryDirectory.empty()) { + if (!sourceDirectory.empty() && + (!binaryDirectory.empty() || !presetName.empty())) { dialog.setSourceDirectory(QString::fromLocal8Bit(sourceDirectory.c_str())); - dialog.setBinaryDirectory(QString::fromLocal8Bit(binaryDirectory.c_str())); + if (!binaryDirectory.empty()) { + dialog.setBinaryDirectory( + QString::fromLocal8Bit(binaryDirectory.c_str())); + if (!presetName.empty()) { + dialog.setStartupBinaryDirectory(true); + } + } + if (!presetName.empty()) { + dialog.setDeferredPreset(QString::fromLocal8Bit(presetName.c_str())); + } } else { if (args.count() == 2) { std::string filePath = @@ -223,7 +246,7 @@ int main(int argc, char** argv) } } - return QApplication::exec(); + return CMakeGUIExec(&dialog); } #if defined(Q_OS_MAC) diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx index 6dbfe1125a..05518a99c6 100644 --- a/Source/QtDialog/CMakeSetupDialog.cxx +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -16,12 +16,15 @@ #include <QMenuBar> #include <QMessageBox> #include <QMimeData> +#include <QProcessEnvironment> #include <QProgressBar> #include <QSettings> #include <QShortcut> #include <QStatusBar> +#include <QString> #include <QToolButton> #include <QUrl> +#include <QVector> #ifdef QT_WINEXTRAS # include <QWinTaskbarButton> @@ -35,10 +38,32 @@ #include "cmVersion.h" #include "AddCacheEntry.h" +#include "EnvironmentDialog.h" #include "FirstConfigure.h" #include "RegexExplorer.h" #include "WarningMessagesDialog.h" +void OpenReferenceManual() +{ + QString urlFormat("https://cmake.org/cmake/help/v%1.%2/"); + QUrl url(urlFormat.arg(QString::number(cmVersion::GetMajorVersion()), + QString::number(cmVersion::GetMinorVersion()))); + + if (!cmSystemTools::GetHTMLDoc().empty()) { + url = QUrl::fromLocalFile( + QDir(QString::fromLocal8Bit(cmSystemTools::GetHTMLDoc().data())) + .filePath("index.html")); + } + + QDesktopServices::openUrl(url); +} + +namespace { +const QString PRESETS_DISABLED_TOOLTIP = + "This option is disabled because there are no available presets in " + "CMakePresets.json or CMakeUserPresets.json."; +} + QCMakeThread::QCMakeThread(QObject* p) : QThread(p) { @@ -88,6 +113,7 @@ CMakeSetupDialog::CMakeSetupDialog() this->ProgressBar->reset(); this->RemoveEntry->setEnabled(false); this->AddEntry->setEnabled(false); + this->Preset->setStatusTip(PRESETS_DISABLED_TOOLTIP); QByteArray p = settings.value("SplitterSizes").toByteArray(); this->Splitter->restoreState(p); @@ -103,79 +129,89 @@ CMakeSetupDialog::CMakeSetupDialog() QMenu* FileMenu = this->menuBar()->addMenu(tr("&File")); this->ReloadCacheAction = FileMenu->addAction(tr("&Reload Cache")); - QObject::connect(this->ReloadCacheAction, SIGNAL(triggered(bool)), this, - SLOT(doReloadCache())); + QObject::connect(this->ReloadCacheAction, &QAction::triggered, this, + &CMakeSetupDialog::doReloadCache); this->DeleteCacheAction = FileMenu->addAction(tr("&Delete Cache")); - QObject::connect(this->DeleteCacheAction, SIGNAL(triggered(bool)), this, - SLOT(doDeleteCache())); + QObject::connect(this->DeleteCacheAction, &QAction::triggered, this, + &CMakeSetupDialog::doDeleteCache); this->ExitAction = FileMenu->addAction(tr("E&xit")); - this->ExitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); - QObject::connect(this->ExitAction, SIGNAL(triggered(bool)), this, - SLOT(close())); + QObject::connect(this->ExitAction, &QAction::triggered, this, + &CMakeSetupDialog::close); + this->ExitAction->setShortcut(QKeySequence::Quit); QMenu* ToolsMenu = this->menuBar()->addMenu(tr("&Tools")); this->ConfigureAction = ToolsMenu->addAction(tr("&Configure")); + QObject::connect(this->ConfigureAction, &QAction::triggered, this, + &CMakeSetupDialog::doConfigure); // prevent merging with Preferences menu item on macOS this->ConfigureAction->setMenuRole(QAction::NoRole); - QObject::connect(this->ConfigureAction, SIGNAL(triggered(bool)), this, - SLOT(doConfigure())); this->GenerateAction = ToolsMenu->addAction(tr("&Generate")); - QObject::connect(this->GenerateAction, SIGNAL(triggered(bool)), this, - SLOT(doGenerate())); - QAction* showChangesAction = ToolsMenu->addAction(tr("&Show My Changes")); - QObject::connect(showChangesAction, SIGNAL(triggered(bool)), this, - SLOT(showUserChanges())); + QObject::connect(this->GenerateAction, &QAction::triggered, this, + &CMakeSetupDialog::doGenerate); + auto* a = ToolsMenu->addAction(tr("&Show My Changes")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::showUserChanges); #if defined(Q_WS_MAC) || defined(Q_OS_MAC) this->InstallForCommandLineAction = ToolsMenu->addAction(tr("&How to Install For Command Line Use")); - QObject::connect(this->InstallForCommandLineAction, SIGNAL(triggered(bool)), - this, SLOT(doInstallForCommandLine())); + QObject::connect(this->InstallForCommandLineAction, &QAction::triggered, + this, &CMakeSetupDialog::doInstallForCommandLine); #endif ToolsMenu->addSeparator(); - ToolsMenu->addAction(tr("Regular Expression Explorer..."), this, - SLOT(doRegexExplorerDialog())); + a = ToolsMenu->addAction(tr("Regular Expression Explorer...")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doRegexExplorerDialog); ToolsMenu->addSeparator(); - ToolsMenu->addAction(tr("&Find in Output..."), this, - SLOT(doOutputFindDialog()), QKeySequence::Find); - ToolsMenu->addAction(tr("Find Next"), this, SLOT(doOutputFindNext()), - QKeySequence::FindNext); - ToolsMenu->addAction(tr("Find Previous"), this, SLOT(doOutputFindPrev()), - QKeySequence::FindPrevious); - ToolsMenu->addAction(tr("Goto Next Error"), this, SLOT(doOutputErrorNext()), - QKeySequence(Qt::Key_F8)); // in Visual Studio - new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Period), this, - SLOT(doOutputErrorNext())); // in Eclipse + a = ToolsMenu->addAction(tr("&Find in Output...")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doOutputFindDialog); + a->setShortcut(QKeySequence::Find); + a = ToolsMenu->addAction(tr("Find Next")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doOutputFindNext); + a->setShortcut(QKeySequence::FindNext); + a = ToolsMenu->addAction(tr("Find Previous")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doOutputFindPrev); + a->setShortcut(QKeySequence::FindPrevious); + a = ToolsMenu->addAction(tr("Goto Next Error")); // in Visual Studio + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doOutputErrorNext); + a->setShortcut(QKeySequence(Qt::Key_F8)); + auto* s = new QShortcut(this); + s->setKey(QKeySequence(Qt::CTRL + Qt::Key_Period)); + QObject::connect(s, &QShortcut::activated, this, + &CMakeSetupDialog::doOutputErrorNext); // in Eclipse QMenu* OptionsMenu = this->menuBar()->addMenu(tr("&Options")); - OptionsMenu->addAction(tr("Warning Messages..."), this, - SLOT(doWarningMessagesDialog())); + a = OptionsMenu->addAction(tr("Warning Messages...")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doWarningMessagesDialog); this->WarnUninitializedAction = OptionsMenu->addAction(tr("&Warn Uninitialized (--warn-uninitialized)")); this->WarnUninitializedAction->setCheckable(true); - this->WarnUnusedAction = - OptionsMenu->addAction(tr("&Warn Unused (--warn-unused-vars)")); - this->WarnUnusedAction->setCheckable(true); QAction* debugAction = OptionsMenu->addAction(tr("&Debug Output")); debugAction->setCheckable(true); - QObject::connect(debugAction, SIGNAL(toggled(bool)), this, - SLOT(setDebugOutput(bool))); + QObject::connect(debugAction, &QAction::toggled, this, + &CMakeSetupDialog::setDebugOutput); OptionsMenu->addSeparator(); - QAction* expandAction = - OptionsMenu->addAction(tr("&Expand Grouped Entries")); - QObject::connect(expandAction, SIGNAL(triggered(bool)), this->CacheValues, - SLOT(expandAll())); - QAction* collapseAction = - OptionsMenu->addAction(tr("&Collapse Grouped Entries")); - QObject::connect(collapseAction, SIGNAL(triggered(bool)), this->CacheValues, - SLOT(collapseAll())); + a = OptionsMenu->addAction(tr("&Expand Grouped Entries")); + QObject::connect(a, &QAction::triggered, this->CacheValues, + &QCMakeCacheView::expandAll); + a = OptionsMenu->addAction(tr("&Collapse Grouped Entries")); + QObject::connect(a, &QAction::triggered, this->CacheValues, + &QCMakeCacheView::collapseAll); QMenu* HelpMenu = this->menuBar()->addMenu(tr("&Help")); - QAction* a = HelpMenu->addAction(tr("About")); - QObject::connect(a, SIGNAL(triggered(bool)), this, SLOT(doAbout())); a = HelpMenu->addAction(tr("Help")); - QObject::connect(a, SIGNAL(triggered(bool)), this, SLOT(doHelp())); + QObject::connect(a, &QAction::triggered, this, &CMakeSetupDialog::doHelp); + a->setShortcut(QKeySequence::HelpContents); + a = HelpMenu->addAction(tr("CMake Reference Manual")); + QObject::connect(a, &QAction::triggered, this, OpenReferenceManual); + a = HelpMenu->addAction(tr("About")); + QObject::connect(a, &QAction::triggered, this, &CMakeSetupDialog::doAbout); this->setAcceptDrops(true); @@ -192,16 +228,16 @@ CMakeSetupDialog::CMakeSetupDialog() this->ErrorFormat.setForeground(QBrush(Qt::red)); this->Output->setContextMenuPolicy(Qt::CustomContextMenu); - connect(this->Output, SIGNAL(customContextMenuRequested(const QPoint&)), - this, SLOT(doOutputContextMenu(const QPoint&))); + connect(this->Output, &QTextEdit::customContextMenuRequested, this, + &CMakeSetupDialog::doOutputContextMenu); // disable open project button this->OpenProjectButton->setDisabled(true); // start the cmake worker thread this->CMakeThread = new QCMakeThread(this); - QObject::connect(this->CMakeThread, SIGNAL(cmakeInitialized()), this, - SLOT(initialize()), Qt::QueuedConnection); + QObject::connect(this->CMakeThread, &QCMakeThread::cmakeInitialized, this, + &CMakeSetupDialog::initialize, Qt::QueuedConnection); this->CMakeThread->start(); this->enterState(ReadyConfigure); @@ -214,88 +250,100 @@ void CMakeSetupDialog::initialize() { // now the cmake worker thread is running, lets make our connections to it QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(propertiesChanged(const QCMakePropertyList&)), - this->CacheValues->cacheModel(), - SLOT(setProperties(const QCMakePropertyList&))); - - QObject::connect(this->ConfigureButton, SIGNAL(clicked(bool)), this, - SLOT(doConfigure())); + &QCMake::propertiesChanged, this->CacheValues->cacheModel(), + &QCMakeCacheModel::setProperties); + + QObject::connect(this->ConfigureButton, &QPushButton::clicked, this, + &CMakeSetupDialog::doConfigure); + + QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::configureDone, + this, &CMakeSetupDialog::exitLoop); + QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::generateDone, + this, &CMakeSetupDialog::exitLoop); + + QObject::connect(this->GenerateButton, &QPushButton::clicked, this, + &CMakeSetupDialog::doGenerate); + QObject::connect(this->OpenProjectButton, &QPushButton::clicked, this, + &CMakeSetupDialog::doOpenProject); + + QObject::connect(this->BrowseSourceDirectoryButton, &QPushButton::clicked, + this, &CMakeSetupDialog::doSourceBrowse); + QObject::connect(this->BrowseBinaryDirectoryButton, &QPushButton::clicked, + this, &CMakeSetupDialog::doBinaryBrowse); + + QObject::connect(this->BinaryDirectory, &QComboBox::editTextChanged, this, + &CMakeSetupDialog::onBinaryDirectoryChanged); + QObject::connect(this->SourceDirectory, &QLineEdit::textChanged, this, + &CMakeSetupDialog::onSourceDirectoryChanged); + QObject::connect(this->Preset, &QCMakePresetComboBox::presetChanged, this, + &CMakeSetupDialog::onBuildPresetChanged); QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(configureDone(int)), this, SLOT(exitLoop(int))); + &QCMake::sourceDirChanged, this, + &CMakeSetupDialog::updateSourceDirectory); QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(generateDone(int)), this, SLOT(exitLoop(int))); - - QObject::connect(this->GenerateButton, SIGNAL(clicked(bool)), this, - SLOT(doGenerate())); - QObject::connect(this->OpenProjectButton, SIGNAL(clicked(bool)), this, - SLOT(doOpenProject())); - - QObject::connect(this->BrowseSourceDirectoryButton, SIGNAL(clicked(bool)), - this, SLOT(doSourceBrowse())); - QObject::connect(this->BrowseBinaryDirectoryButton, SIGNAL(clicked(bool)), - this, SLOT(doBinaryBrowse())); - - QObject::connect(this->BinaryDirectory, SIGNAL(editTextChanged(QString)), - this, SLOT(onBinaryDirectoryChanged(QString))); - QObject::connect(this->SourceDirectory, SIGNAL(textChanged(QString)), this, - SLOT(onSourceDirectoryChanged(QString))); - - QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(sourceDirChanged(QString)), this, - SLOT(updateSourceDirectory(QString))); + &QCMake::binaryDirChanged, this, + &CMakeSetupDialog::updateBinaryDirectory); + QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::presetsChanged, + this, &CMakeSetupDialog::updatePresets); + QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::presetChanged, + this, &CMakeSetupDialog::updatePreset); QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(binaryDirChanged(QString)), this, - SLOT(updateBinaryDirectory(QString))); + &QCMake::presetLoadError, this, + &CMakeSetupDialog::showPresetLoadError); QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(progressChanged(QString, float)), this, - SLOT(showProgress(QString, float))); + &QCMake::progressChanged, this, + &CMakeSetupDialog::showProgress); - QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(errorMessage(QString)), this, SLOT(error(QString))); + QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::errorMessage, + this, &CMakeSetupDialog::error); - QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(outputMessage(QString)), this, - SLOT(message(QString))); + QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::outputMessage, + this, &CMakeSetupDialog::message); - QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(openPossible(bool)), this->OpenProjectButton, - SLOT(setEnabled(bool))); + QObject::connect(this->CMakeThread->cmakeInstance(), &QCMake::openPossible, + this->OpenProjectButton, &CMakeSetupDialog::setEnabled); - QObject::connect(this->groupedCheck, SIGNAL(toggled(bool)), this, - SLOT(setGroupedView(bool))); - QObject::connect(this->advancedCheck, SIGNAL(toggled(bool)), this, - SLOT(setAdvancedView(bool))); - QObject::connect(this->Search, SIGNAL(textChanged(QString)), this, - SLOT(setSearchFilter(QString))); + QObject::connect(this->groupedCheck, &QCheckBox::toggled, this, + &CMakeSetupDialog::setGroupedView); + QObject::connect(this->advancedCheck, &QCheckBox::toggled, this, + &CMakeSetupDialog::setAdvancedView); + QObject::connect(this->Search, &QLineEdit::textChanged, this, + &CMakeSetupDialog::setSearchFilter); QObject::connect(this->CMakeThread->cmakeInstance(), - SIGNAL(generatorChanged(QString)), this, - SLOT(updateGeneratorLabel(QString))); + &QCMake::generatorChanged, this, + &CMakeSetupDialog::updateGeneratorLabel); this->updateGeneratorLabel(QString()); QObject::connect(this->CacheValues->cacheModel(), - SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, - SLOT(setCacheModified())); + &QCMakeCacheModel::dataChanged, this, + &CMakeSetupDialog::setCacheModified); QObject::connect(this->CacheValues->selectionModel(), - SIGNAL(selectionChanged(QItemSelection, QItemSelection)), - this, SLOT(selectionChanged())); - QObject::connect(this->RemoveEntry, SIGNAL(clicked(bool)), this, - SLOT(removeSelectedCacheEntries())); - QObject::connect(this->AddEntry, SIGNAL(clicked(bool)), this, - SLOT(addCacheEntry())); - - QObject::connect(this->WarnUninitializedAction, SIGNAL(triggered(bool)), - this->CMakeThread->cmakeInstance(), - SLOT(setWarnUninitializedMode(bool))); - QObject::connect(this->WarnUnusedAction, SIGNAL(triggered(bool)), + &QItemSelectionModel::selectionChanged, this, + &CMakeSetupDialog::selectionChanged); + QObject::connect(this->RemoveEntry, &QToolButton::clicked, this, + &CMakeSetupDialog::removeSelectedCacheEntries); + QObject::connect(this->AddEntry, &QToolButton::clicked, this, + &CMakeSetupDialog::addCacheEntry); + + QObject::connect(this->Environment, &QToolButton::clicked, this, + &CMakeSetupDialog::editEnvironment); + + QObject::connect(this->WarnUninitializedAction, &QAction::triggered, this->CMakeThread->cmakeInstance(), - SLOT(setWarnUnusedMode(bool))); + &QCMake::setWarnUninitializedMode); + QObject::connect(this->CMakeThread->cmakeInstance(), + &QCMake::warnUninitializedModeChanged, + this->WarnUninitializedAction, &QAction::setChecked); - if (!this->SourceDirectory->text().isEmpty() || - !this->BinaryDirectory->lineEdit()->text().isEmpty()) { + if (!this->SourceDirectory->text().isEmpty() && + !this->DeferredPreset.isNull()) { + this->onSourceDirectoryChanged(this->SourceDirectory->text()); + } else if (!this->SourceDirectory->text().isEmpty() || + !this->BinaryDirectory->lineEdit()->text().isEmpty()) { this->onSourceDirectoryChanged(this->SourceDirectory->text()); this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text()); } else { @@ -451,7 +499,8 @@ void CMakeSetupDialog::doInstallForCommandLine() lab->setTextInteractionFlags(Qt::TextSelectableByMouse); QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, &dialog); - QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept())); + QObject::connect(btns, &QDialogButtonBox::accepted, &dialog, + &QDialog::accept); l->addWidget(btns); dialog.exec(); } @@ -608,7 +657,8 @@ void CMakeSetupDialog::doHelp() lab->setWordWrap(true); QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, &dialog); - QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept())); + QObject::connect(btns, &QDialogButtonBox::accepted, &dialog, + &QDialog::accept); l->addWidget(lab); l->addWidget(btns); dialog.exec(); @@ -648,6 +698,41 @@ void CMakeSetupDialog::updateBinaryDirectory(const QString& dir) } } +void CMakeSetupDialog::updatePresets(const QVector<QCMakePreset>& presets) +{ + if (this->Preset->presets() != presets) { + this->Preset->blockSignals(true); + this->Preset->setPresets(presets); + this->Preset->blockSignals(false); + } + + this->Preset->setDisabled(presets.isEmpty()); + this->Preset->setToolTip(presets.isEmpty() ? PRESETS_DISABLED_TOOLTIP : ""); + + if (!this->DeferredPreset.isNull()) { + this->Preset->setPresetName(this->DeferredPreset); + this->DeferredPreset = QString{}; + } +} + +void CMakeSetupDialog::updatePreset(const QString& name) +{ + if (this->Preset->presetName() != name) { + this->Preset->blockSignals(true); + this->Preset->setPresetName(name); + this->Preset->blockSignals(false); + } +} + +void CMakeSetupDialog::showPresetLoadError( + const QString& dir, cmCMakePresetsFile::ReadFileResult result) +{ + QMessageBox::warning( + this, "Error Reading CMake Presets", + QString::fromLocal8Bit("Could not read presets from %1: %2") + .arg(dir, cmCMakePresetsFile::ResultToString(result))); +} + void CMakeSetupDialog::doBinaryBrowse() { QString dir = QFileDialog::getExistingDirectory( @@ -663,6 +748,11 @@ void CMakeSetupDialog::setBinaryDirectory(const QString& dir) this->BinaryDirectory->setEditText(dir); } +void CMakeSetupDialog::setStartupBinaryDirectory(bool startup) +{ + this->StartupBinaryDirectory = startup; +} + void CMakeSetupDialog::onSourceDirectoryChanged(const QString& dir) { this->Output->clear(); @@ -688,11 +778,24 @@ void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir) Q_ARG(QString, dir)); } +void CMakeSetupDialog::onBuildPresetChanged(const QString& name) +{ + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "setPreset", + Qt::QueuedConnection, Q_ARG(QString, name), + Q_ARG(bool, !this->StartupBinaryDirectory)); + this->StartupBinaryDirectory = false; +} + void CMakeSetupDialog::setSourceDirectory(const QString& dir) { this->SourceDirectory->setText(dir); } +void CMakeSetupDialog::setDeferredPreset(const QString& preset) +{ + this->DeferredPreset = preset; +} + void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent) { percent = (percent * ProgressFactor) + ProgressOffset; @@ -730,6 +833,7 @@ void CMakeSetupDialog::setEnabledState(bool enabled) this->CacheValues->cacheModel()->setEditEnabled(enabled); this->SourceDirectory->setEnabled(enabled); this->BrowseSourceDirectoryButton->setEnabled(enabled); + this->Preset->setEnabled(enabled && !this->Preset->presets().isEmpty()); this->BinaryDirectory->setEnabled(enabled); this->BrowseBinaryDirectoryButton->setEnabled(enabled); this->ReloadCacheAction->setEnabled(enabled); @@ -738,6 +842,7 @@ void CMakeSetupDialog::setEnabledState(bool enabled) this->ConfigureAction->setEnabled(enabled); this->AddEntry->setEnabled(enabled); this->RemoveEntry->setEnabled(false); // let selection re-enable it + this->Environment->setEnabled(enabled); } bool CMakeSetupDialog::setupFirstConfigure() @@ -753,6 +858,19 @@ bool CMakeSetupDialog::setupFirstConfigure() // restore from settings dialog.loadFromSettings(); + auto presetData = this->Preset->currentData(); + if (presetData.isValid()) { + auto preset = presetData.value<QCMakePreset>(); + dialog.setCurrentGenerator(preset.generator); + if (preset.setArchitecture) { + dialog.setPlatform(preset.architecture); + } + if (preset.setToolset) { + dialog.setToolset(preset.toolset); + } + dialog.setCompilerOption(CompilerOption::DefaultNative); + } + if (dialog.exec() == QDialog::Accepted) { dialog.saveToSettings(); this->CMakeThread->cmakeInstance()->setGenerator(dialog.getGenerator()); @@ -897,7 +1015,8 @@ void CMakeSetupDialog::doAbout() lab->setWordWrap(true); QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, &dialog); - QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept())); + QObject::connect(btns, &QDialogButtonBox::accepted, &dialog, + &QDialog::accept); l->addWidget(btns); dialog.exec(); } @@ -1070,6 +1189,17 @@ void CMakeSetupDialog::enterState(CMakeSetupDialog::State s) } } +void CMakeSetupDialog::editEnvironment() +{ + EnvironmentDialog dialog(this->CMakeThread->cmakeInstance()->environment(), + this); + if (dialog.exec() == QDialog::Accepted) { + QMetaObject::invokeMethod( + this->CMakeThread->cmakeInstance(), "setEnvironment", + Q_ARG(QProcessEnvironment, dialog.environment())); + } +} + void CMakeSetupDialog::addCacheEntry() { QDialog dialog(this); @@ -1080,8 +1210,10 @@ void CMakeSetupDialog::addCacheEntry() new AddCacheEntry(&dialog, this->AddVariableNames, this->AddVariableTypes); QDialogButtonBox* btns = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); - QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept())); - QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(reject())); + QObject::connect(btns, &QDialogButtonBox::accepted, &dialog, + &QDialog::accept); + QObject::connect(btns, &QDialogButtonBox::rejected, &dialog, + &QDialog::reject); l->addWidget(w); l->addStretch(); l->addWidget(btns); @@ -1159,7 +1291,8 @@ void CMakeSetupDialog::showUserChanges() l->addWidget(textedit); QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); - QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(accept())); + QObject::connect(btns, &QDialogButtonBox::rejected, &dialog, + &QDialog::accept); l->addWidget(btns); QString command; @@ -1213,15 +1346,23 @@ void CMakeSetupDialog::doOutputContextMenu(QPoint pt) std::unique_ptr<QMenu> menu(this->Output->createStandardContextMenu()); menu->addSeparator(); - menu->addAction(tr("Find..."), this, SLOT(doOutputFindDialog()), - QKeySequence::Find); - menu->addAction(tr("Find Next"), this, SLOT(doOutputFindNext()), - QKeySequence::FindNext); - menu->addAction(tr("Find Previous"), this, SLOT(doOutputFindPrev()), - QKeySequence::FindPrevious); + auto* a = menu->addAction(tr("Find...")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doOutputFindDialog); + a->setShortcut(QKeySequence::Find); + a = menu->addAction(tr("Find Next")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doOutputFindNext); + a->setShortcut(QKeySequence::FindNext); + a = menu->addAction(tr("Find Previous")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doOutputFindPrev); + a->setShortcut(QKeySequence::FindPrevious); menu->addSeparator(); - menu->addAction(tr("Goto Next Error"), this, SLOT(doOutputErrorNext()), - QKeySequence(Qt::Key_F8)); + a = menu->addAction(tr("Goto Next Error")); + QObject::connect(a, &QAction::triggered, this, + &CMakeSetupDialog::doOutputErrorNext); + a->setShortcut(QKeySequence(Qt::Key_F8)); menu->exec(this->Output->mapToGlobal(pt)); } diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h index d1e20354b0..f0cc9294d8 100644 --- a/Source/QtDialog/CMakeSetupDialog.h +++ b/Source/QtDialog/CMakeSetupDialog.h @@ -1,17 +1,19 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef CMakeSetupDialog_h -#define CMakeSetupDialog_h +#pragma once #include <memory> #include "QCMake.h" +#include "QCMakePreset.h" #include <QEventLoop> #include <QMainWindow> #include <QThread> +#include <QVector> #include "ui_CMakeSetupDialog.h" +class QCMakePresetItemModel; class QCMakeThread; class CMakeCacheModel; class QProgressBar; @@ -34,6 +36,8 @@ public: public slots: void setBinaryDirectory(const QString& dir); void setSourceDirectory(const QString& dir); + void setDeferredPreset(const QString& preset); + void setStartupBinaryDirectory(bool startup); protected slots: void initialize(); @@ -53,6 +57,10 @@ protected slots: void doDeleteCache(); void updateSourceDirectory(const QString& dir); void updateBinaryDirectory(const QString& dir); + void updatePresets(const QVector<QCMakePreset>& presets); + void updatePreset(const QString& name); + void showPresetLoadError(const QString& dir, + cmCMakePresetsFile::ReadFileResult result); void showProgress(const QString& msg, float percent); void setEnabledState(bool); bool setupFirstConfigure(); @@ -63,9 +71,11 @@ protected slots: void saveBuildPaths(const QStringList&); void onBinaryDirectoryChanged(const QString& dir); void onSourceDirectoryChanged(const QString& dir); + void onBuildPresetChanged(const QString& name); void setCacheModified(); void removeSelectedCacheEntries(); void selectionChanged(); + void editEnvironment(); void addCacheEntry(); void startSearch(); void setDebugOutput(bool); @@ -111,9 +121,10 @@ protected: QAction* ConfigureAction; QAction* GenerateAction; QAction* WarnUninitializedAction; - QAction* WarnUnusedAction; QAction* InstallForCommandLineAction; State CurrentState; + QString DeferredPreset; + bool StartupBinaryDirectory = false; QTextCharFormat ErrorFormat; QTextCharFormat MessageFormat; @@ -147,5 +158,3 @@ protected: virtual void run(); std::unique_ptr<QCMake> CMakeInstance; }; - -#endif // CMakeSetupDialog_h diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui index dc22a29891..a5c35b1c30 100644 --- a/Source/QtDialog/CMakeSetupDialog.ui +++ b/Source/QtDialog/CMakeSetupDialog.ui @@ -11,7 +11,16 @@ </rect> </property> <layout class="QGridLayout"> - <property name="margin"> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> <number>9</number> </property> <property name="spacing"> @@ -19,14 +28,23 @@ </property> <item row="0" column="0"> <layout class="QGridLayout"> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <property name="spacing"> <number>6</number> </property> <item row="0" column="0"> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="SourceLabel"> <property name="text"> <string>Where is the source code:</string> </property> @@ -43,13 +61,27 @@ </widget> </item> <item row="1" column="0"> - <widget class="QLabel" name="label_2"> + <widget class="QLabel" name="PresetLabel"> <property name="text"> - <string>Where to build the binaries:</string> + <string>Preset:</string> </property> </widget> </item> <item row="1" column="1"> + <widget class="QCMakePresetComboBox" name="Preset"> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="BinaryLabel"> + <property name="text"> + <string>Where to build the binaries:</string> + </property> + </widget> + </item> + <item row="2" column="1"> <widget class="QComboBox" name="BinaryDirectory"> <property name="sizePolicy"> <sizepolicy hsizetype="Ignored" vsizetype="Fixed"> @@ -65,7 +97,7 @@ </property> </widget> </item> - <item row="1" column="2"> + <item row="2" column="2"> <widget class="QPushButton" name="BrowseBinaryDirectoryButton"> <property name="text"> <string>Browse &Build...</string> @@ -90,7 +122,16 @@ <property name="spacing"> <number>6</number> </property> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -98,7 +139,16 @@ <property name="spacing"> <number>6</number> </property> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -191,6 +241,13 @@ </property> </widget> </item> + <item> + <widget class="QPushButton" name="Environment"> + <property name="text"> + <string>E&nvironment...</string> + </property> + </widget> + </item> </layout> </item> <item> @@ -224,7 +281,16 @@ <property name="spacing"> <number>6</number> </property> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -241,13 +307,13 @@ </property> </widget> </item> - <item> - <widget class="QPushButton" name="OpenProjectButton"> - <property name="text"> - <string>Open &Project</string> - </property> - </widget> - </item> + <item> + <widget class="QPushButton" name="OpenProjectButton"> + <property name="text"> + <string>Open &Project</string> + </property> + </widget> + </item> <item> <widget class="QLabel" name="Generator"> <property name="text"> @@ -315,6 +381,11 @@ <extends>QTreeView</extends> <header>QCMakeCacheView.h</header> </customwidget> + <customwidget> + <class>QCMakePresetComboBox</class> + <extends>QComboBox</extends> + <header>QCMakePresetComboBox.h</header> + </customwidget> </customwidgets> <resources> <include location="CMakeSetup.qrc"/> diff --git a/Source/QtDialog/Compilers.h b/Source/QtDialog/Compilers.h index 931c93566f..5da0781c86 100644 --- a/Source/QtDialog/Compilers.h +++ b/Source/QtDialog/Compilers.h @@ -1,7 +1,6 @@ -#ifndef COMPILERS_HPP -#define COMPILERS_HPP +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -21,5 +20,3 @@ public: this->setupUi(this); } }; - -#endif diff --git a/Source/QtDialog/EnvironmentDialog.cxx b/Source/QtDialog/EnvironmentDialog.cxx new file mode 100644 index 0000000000..846456c8e3 --- /dev/null +++ b/Source/QtDialog/EnvironmentDialog.cxx @@ -0,0 +1,194 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "EnvironmentDialog.h" + +#include <QDialogButtonBox> +#include <QGridLayout> +#include <QItemSelectionModel> +#include <QLabel> +#include <QLineEdit> +#include <QMessageBox> +#include <QStandardItem> + +EnvironmentItemModel::EnvironmentItemModel( + const QProcessEnvironment& environment, QObject* parent) + : QStandardItemModel(parent) +{ + this->clear(); + for (auto const& key : environment.keys()) { + auto value = environment.value(key); + this->appendVariable(key, value); + } +} + +QProcessEnvironment EnvironmentItemModel::environment() const +{ + QProcessEnvironment env; + for (int i = 0; i < this->rowCount(); ++i) { + auto name = this->data(this->index(i, 0), Qt::DisplayRole).toString(); + auto value = this->data(this->index(i, 1), Qt::DisplayRole).toString(); + env.insert(name, value); + } + return env; +} + +void EnvironmentItemModel::clear() +{ + this->QStandardItemModel::clear(); + + QStringList labels; + labels << tr("Name") << tr("Value"); + this->setHorizontalHeaderLabels(labels); +} + +QModelIndex EnvironmentItemModel::buddy(const QModelIndex& index) const +{ + if (index.column() == 0) { + return this->index(index.row(), index.column() + 1, index.parent()); + } + return index; +} + +void EnvironmentItemModel::appendVariable(const QString& key, + const QString& value) +{ + this->insertVariable(this->rowCount(), key, value); +} + +void EnvironmentItemModel::insertVariable(int row, const QString& key, + const QString& value) +{ + for (int i = 0; i < this->rowCount(); ++i) { + if (this->data(this->index(i, 0), Qt::DisplayRole) == key) { + this->setData(this->index(i, 1), value, Qt::DisplayRole); + return; + } + } + + auto* keyItem = new QStandardItem(key); + auto* valueItem = new QStandardItem(value); + this->insertRow(row, { keyItem, valueItem }); +} + +EnvironmentSearchFilter::EnvironmentSearchFilter(QObject* parent) + : QSortFilterProxyModel(parent) +{ +} + +bool EnvironmentSearchFilter::filterAcceptsRow(int row, + const QModelIndex& parent) const +{ + auto* model = this->sourceModel(); + auto key = + model->data(model->index(row, 0, parent), Qt::DisplayRole).toString(); + return key.contains(this->filterRegExp()); +} + +EnvironmentDialog::EnvironmentDialog(const QProcessEnvironment& environment, + QWidget* parent) + : QDialog(parent) +{ + this->setupUi(this); + + this->RemoveEntry->setEnabled(false); + + this->m_model = new EnvironmentItemModel(environment, this); + this->m_filter = new EnvironmentSearchFilter(this); + this->m_filter->setSourceModel(this->m_model); + this->Environment->setModel(this->m_filter); + + this->Environment->setUniformRowHeights(true); + this->Environment->setRootIsDecorated(false); + this->Environment->setSelectionMode(QAbstractItemView::ExtendedSelection); + this->Environment->setSelectionBehavior(QAbstractItemView::SelectRows); + + QObject::connect(this->AddEntry, &QToolButton::clicked, this, + &EnvironmentDialog::addEntry); + QObject::connect(this->RemoveEntry, &QToolButton::clicked, this, + &EnvironmentDialog::removeSelectedEntries); + QObject::connect(this->Search, &QLineEdit::textChanged, this->m_filter, + &EnvironmentSearchFilter::setFilterFixedString); + QObject::connect(this->Environment->selectionModel(), + &QItemSelectionModel::selectionChanged, this, + &EnvironmentDialog::selectionChanged); +} + +QProcessEnvironment EnvironmentDialog::environment() const +{ + return this->m_model->environment(); +} + +void EnvironmentDialog::addEntry() +{ + // Build the dialog manually because it's simple enough + QDialog dialog(this); + dialog.setWindowTitle("Add Environment Variable"); + + auto* layout = new QGridLayout; + dialog.setLayout(layout); + + auto* nameLabel = new QLabel; + nameLabel->setText("Name:"); + layout->addWidget(nameLabel, 0, 0); + + auto* nameEdit = new QLineEdit; + nameEdit->setObjectName("name"); + layout->addWidget(nameEdit, 0, 1); + + auto* valueLabel = new QLabel; + valueLabel->setText("Value:"); + layout->addWidget(valueLabel, 1, 0); + + auto* valueEdit = new QLineEdit; + valueEdit->setObjectName("value"); + layout->addWidget(valueEdit, 1, 1); + + auto* buttons = new QDialogButtonBox; + buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + QObject::connect( + buttons, &QDialogButtonBox::accepted, &dialog, + [this, &dialog, nameEdit]() { + auto text = nameEdit->text(); + if (text.isEmpty()) { + QMessageBox::critical(&dialog, "Error", "Name must be non-empty."); + return; + } + + auto* model = this->Environment->model(); + for (int i = 0; i < model->rowCount(); ++i) { + if (model->data(model->index(i, 0), Qt::DisplayRole) == text) { + QMessageBox::critical( + &dialog, "Error", + tr("Environment variable \"%1\" already exists.").arg(text)); + return; + } + } + + dialog.accept(); + }); + QObject::connect(buttons, &QDialogButtonBox::rejected, &dialog, + &QDialog::reject); + layout->addWidget(buttons, 2, 0, 1, 2); + + if (dialog.exec() == QDialog::Accepted) { + this->m_model->insertVariable(0, nameEdit->text(), valueEdit->text()); + } +} + +void EnvironmentDialog::removeSelectedEntries() +{ + QModelIndexList idxs = this->Environment->selectionModel()->selectedRows(); + QList<QPersistentModelIndex> pidxs; + foreach (QModelIndex const& i, idxs) { + pidxs.append(i); + } + foreach (QPersistentModelIndex const& pi, pidxs) { + this->Environment->model()->removeRow(pi.row(), pi.parent()); + } +} + +void EnvironmentDialog::selectionChanged() +{ + auto selected = this->Environment->selectionModel()->selectedRows(); + this->RemoveEntry->setEnabled(!selected.isEmpty()); +} diff --git a/Source/QtDialog/EnvironmentDialog.h b/Source/QtDialog/EnvironmentDialog.h new file mode 100644 index 0000000000..6aae798065 --- /dev/null +++ b/Source/QtDialog/EnvironmentDialog.h @@ -0,0 +1,59 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <QDialog> +#include <QObject> +#include <QProcessEnvironment> +#include <QSortFilterProxyModel> +#include <QStandardItemModel> + +#include "ui_EnvironmentDialog.h" + +class EnvironmentItemModel : public QStandardItemModel +{ + Q_OBJECT +public: + EnvironmentItemModel(const QProcessEnvironment& environment, + QObject* parent = nullptr); + + QProcessEnvironment environment() const; + void clear(); + + QModelIndex buddy(const QModelIndex& index) const override; + +public slots: + void appendVariable(const QString& key, const QString& value); + void insertVariable(int row, const QString& key, const QString& value); +}; + +class EnvironmentSearchFilter : public QSortFilterProxyModel +{ + Q_OBJECT +public: + EnvironmentSearchFilter(QObject* parent = nullptr); + +protected: + bool filterAcceptsRow(int row, const QModelIndex& parent) const override; +}; + +class EnvironmentDialog + : public QDialog + , public Ui::EnvironmentDialog +{ + Q_OBJECT +public: + EnvironmentDialog(const QProcessEnvironment& environment, + QWidget* parent = nullptr); + + QProcessEnvironment environment() const; + +protected slots: + void addEntry(); + void removeSelectedEntries(); + void selectionChanged(); + +private: + EnvironmentItemModel* m_model; + EnvironmentSearchFilter* m_filter; +}; diff --git a/Source/QtDialog/EnvironmentDialog.ui b/Source/QtDialog/EnvironmentDialog.ui new file mode 100644 index 0000000000..dea762446e --- /dev/null +++ b/Source/QtDialog/EnvironmentDialog.ui @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>EnvironmentDialog</class> + <widget class="QDialog" name="EnvironmentDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Environment Editor</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>S&earch:</string> + </property> + <property name="buddy"> + <cstring>Search</cstring> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="Search"/> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>12</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QToolButton" name="AddEntry"> + <property name="text"> + <string>&Add Entry</string> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/Plus16.png</normaloff>:/Icons/Plus16.png</iconset> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="RemoveEntry"> + <property name="text"> + <string>&Remove Entry</string> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/Delete16.png</normaloff>:/Icons/Delete16.png</iconset> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QTreeView" name="Environment"/> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="CMakeSetup.qrc"/> + </resources> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>EnvironmentDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>EnvironmentDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx index 3c24b9b05a..10360bb701 100644 --- a/Source/QtDialog/FirstConfigure.cxx +++ b/Source/QtDialog/FirstConfigure.cxx @@ -47,17 +47,18 @@ StartCompilerSetup::StartCompilerSetup(QString defaultGeneratorPlatform, this->CompilerSetupOptions[0]->setChecked(true); - QObject::connect(this->CompilerSetupOptions[0], SIGNAL(toggled(bool)), this, - SLOT(onSelectionChanged(bool))); - QObject::connect(this->CompilerSetupOptions[1], SIGNAL(toggled(bool)), this, - SLOT(onSelectionChanged(bool))); - QObject::connect(this->CompilerSetupOptions[2], SIGNAL(toggled(bool)), this, - SLOT(onSelectionChanged(bool))); - QObject::connect(this->CompilerSetupOptions[3], SIGNAL(toggled(bool)), this, - SLOT(onSelectionChanged(bool))); - QObject::connect(this->GeneratorOptions, - SIGNAL(currentIndexChanged(QString const&)), this, - SLOT(onGeneratorChanged(QString const&))); + QObject::connect(this->CompilerSetupOptions[0], &QRadioButton::toggled, this, + &StartCompilerSetup::onSelectionChanged); + QObject::connect(this->CompilerSetupOptions[1], &QRadioButton::toggled, this, + &StartCompilerSetup::onSelectionChanged); + QObject::connect(this->CompilerSetupOptions[2], &QRadioButton::toggled, this, + &StartCompilerSetup::onSelectionChanged); + QObject::connect(this->CompilerSetupOptions[3], &QRadioButton::toggled, this, + &StartCompilerSetup::onSelectionChanged); + QObject::connect( + this->GeneratorOptions, + static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + this, &StartCompilerSetup::onGeneratorChanged); } QFrame* StartCompilerSetup::CreateToolsetWidgets() @@ -144,6 +145,36 @@ void StartCompilerSetup::setCurrentGenerator(const QString& gen) } } +void StartCompilerSetup::setPlatform(const QString& platform) +{ + this->PlatformOptions->setCurrentText(platform); +} + +void StartCompilerSetup::setToolset(const QString& toolset) +{ + this->Toolset->setText(toolset); +} + +void StartCompilerSetup::setCompilerOption(CompilerOption option) +{ + std::size_t index = 0; + switch (option) { + case CompilerOption::DefaultNative: + index = 0; + break; + case CompilerOption::SpecifyNative: + index = 1; + break; + case CompilerOption::ToolchainFile: + index = 2; + break; + case CompilerOption::Options: + index = 3; + break; + } + this->CompilerSetupOptions[index]->setChecked(true); +} + QString StartCompilerSetup::getGenerator() const { return this->GeneratorOptions->currentText(); @@ -186,8 +217,10 @@ void StartCompilerSetup::onSelectionChanged(bool on) } } -void StartCompilerSetup::onGeneratorChanged(QString const& name) +void StartCompilerSetup::onGeneratorChanged(int index) { + QString name = this->GeneratorOptions->itemText(index); + // Display the generator platform for the generators supporting it if (GeneratorsSupportingPlatform.contains(name)) { @@ -458,9 +491,9 @@ FirstConfigure::FirstConfigure() this->mStartCompilerSetupPage = new StartCompilerSetup( env_generator_platform, env_generator_toolset, this); this->setPage(Start, this->mStartCompilerSetupPage); - QObject::connect(this->mStartCompilerSetupPage, SIGNAL(selectionChanged()), - this, SLOT(restart())); - + QObject::connect(this->mStartCompilerSetupPage, + &StartCompilerSetup::selectionChanged, this, + &FirstConfigure::restart); this->mNativeCompilerSetupPage = new NativeCompilerSetup(this); this->setPage(NativeSetup, this->mNativeCompilerSetupPage); @@ -479,6 +512,26 @@ void FirstConfigure::setGenerators( this->mStartCompilerSetupPage->setGenerators(gens); } +void FirstConfigure::setCurrentGenerator(const QString& gen) +{ + this->mStartCompilerSetupPage->setCurrentGenerator(gen); +} + +void FirstConfigure::setPlatform(const QString& platform) +{ + this->mStartCompilerSetupPage->setPlatform(platform); +} + +void FirstConfigure::setToolset(const QString& toolset) +{ + this->mStartCompilerSetupPage->setToolset(toolset); +} + +void FirstConfigure::setCompilerOption(CompilerOption option) +{ + this->mStartCompilerSetupPage->setCompilerOption(option); +} + QString FirstConfigure::getGenerator() const { return this->mStartCompilerSetupPage->getGenerator(); @@ -500,7 +553,7 @@ void FirstConfigure::loadFromSettings() // restore generator settings.beginGroup("Settings/StartPath"); QString lastGen = settings.value("LastGenerator").toString(); - this->mStartCompilerSetupPage->setCurrentGenerator(lastGen); + this->setCurrentGenerator(lastGen); settings.endGroup(); // restore compiler setup @@ -547,7 +600,7 @@ void FirstConfigure::loadFromSettings() // this prevents them from being taken from environment, while the // generator is taken from application settings if (!mDefaultGenerator.isEmpty()) { - this->mStartCompilerSetupPage->setCurrentGenerator(mDefaultGenerator); + this->setCurrentGenerator(mDefaultGenerator); } } diff --git a/Source/QtDialog/FirstConfigure.h b/Source/QtDialog/FirstConfigure.h index c26f48984d..5844f3ab04 100644 --- a/Source/QtDialog/FirstConfigure.h +++ b/Source/QtDialog/FirstConfigure.h @@ -1,6 +1,5 @@ -#ifndef FirstConfigure_h -#define FirstConfigure_h +#pragma once #include <QWizard> #include <QWizardPage> @@ -23,6 +22,14 @@ enum FirstConfigurePages Done }; +enum class CompilerOption +{ + DefaultNative, + SpecifyNative, + ToolchainFile, + Options, +}; + //! the first page that gives basic options for what compilers setup to choose //! from class StartCompilerSetup : public QWizardPage @@ -34,6 +41,9 @@ public: ~StartCompilerSetup(); void setGenerators(std::vector<cmake::GeneratorInfo> const& gens); void setCurrentGenerator(const QString& gen); + void setToolset(const QString& toolset); + void setPlatform(const QString& platform); + void setCompilerOption(CompilerOption option); QString getGenerator() const; QString getToolset() const; QString getPlatform() const; @@ -50,7 +60,7 @@ signals: protected slots: void onSelectionChanged(bool); - void onGeneratorChanged(QString const& name); + void onGeneratorChanged(int index); protected: QComboBox* GeneratorOptions; @@ -168,6 +178,10 @@ public: ~FirstConfigure(); void setGenerators(std::vector<cmake::GeneratorInfo> const& gens); + void setCurrentGenerator(const QString& gen); + void setToolset(const QString& toolset); + void setPlatform(const QString& platform); + void setCompilerOption(CompilerOption option); QString getGenerator() const; QString getPlatform() const; QString getToolset() const; @@ -201,5 +215,3 @@ protected: ToolchainCompilerSetup* mToolchainCompilerSetupPage; QString mDefaultGenerator; }; - -#endif // FirstConfigure_h diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index 776af81ff6..2f41f70bf2 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -2,10 +2,14 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "QCMake.h" +#include <algorithm> + #include <cm/memory> #include <QCoreApplication> #include <QDir> +#include <QString> +#include <QVector> #include "cmExternalMakefileProjectGenerator.h" #include "cmGlobalGenerator.h" @@ -19,11 +23,15 @@ QCMake::QCMake(QObject* p) : QObject(p) + , StartEnvironment(QProcessEnvironment::systemEnvironment()) + , Environment(QProcessEnvironment::systemEnvironment()) { this->WarnUninitializedMode = false; - this->WarnUnusedMode = false; qRegisterMetaType<QCMakeProperty>(); qRegisterMetaType<QCMakePropertyList>(); + qRegisterMetaType<QProcessEnvironment>(); + qRegisterMetaType<QVector<QCMakePreset>>(); + qRegisterMetaType<cmCMakePresetsFile::ReadFileResult>(); cmSystemTools::DisableRunCommandOutput(); cmSystemTools::SetRunCommandHideConsole(true); @@ -56,6 +64,17 @@ QCMake::QCMake(QObject* p) for (cmake::GeneratorInfo const& gen : generators) { this->AvailableGenerators.push_back(gen); } + + connect(&this->LoadPresetsTimer, &QTimer::timeout, this, [this]() { + this->loadPresets(); + if (!this->PresetName.isEmpty() && + this->CMakePresetsFile.Presets.find( + std::string(this->PresetName.toLocal8Bit())) == + this->CMakePresetsFile.Presets.end()) { + this->setPreset(QString{}); + } + }); + this->LoadPresetsTimer.start(1000); } QCMake::~QCMake() = default; @@ -72,6 +91,8 @@ void QCMake::setSourceDirectory(const QString& _dir) if (this->SourceDirectory != dir) { this->SourceDirectory = QDir::fromNativeSeparators(dir); emit this->sourceDirChanged(this->SourceDirectory); + this->loadPresets(); + this->setPreset(QString{}); } } @@ -128,6 +149,56 @@ void QCMake::setBinaryDirectory(const QString& _dir) } } +void QCMake::setPreset(const QString& name, bool setBinary) +{ + if (this->PresetName != name) { + this->PresetName = name; + emit this->presetChanged(this->PresetName); + + if (!name.isNull()) { + std::string presetName(name.toLocal8Bit()); + auto const& expandedPreset = + this->CMakePresetsFile.Presets[presetName].Expanded; + if (expandedPreset) { + if (setBinary) { + QString binaryDir = + QString::fromLocal8Bit(expandedPreset->BinaryDir.data()); + this->setBinaryDirectory(binaryDir); + } + if (expandedPreset->WarnDev) { + this->CMakeInstance->SetSuppressDevWarnings( + !*expandedPreset->WarnDev); + } + if (expandedPreset->ErrorDev) { + this->CMakeInstance->SetDevWarningsAsErrors( + *expandedPreset->ErrorDev); + } + if (expandedPreset->WarnDeprecated) { + this->CMakeInstance->SetSuppressDeprecatedWarnings( + !*expandedPreset->WarnDeprecated); + } + if (expandedPreset->ErrorDeprecated) { + this->CMakeInstance->SetDeprecatedWarningsAsErrors( + *expandedPreset->ErrorDeprecated); + } + if (expandedPreset->WarnUninitialized) { + this->WarnUninitializedMode = *expandedPreset->WarnUninitialized; + emit this->warnUninitializedModeChanged( + *expandedPreset->WarnUninitialized); + } + this->Environment = this->StartEnvironment; + for (auto const& v : expandedPreset->Environment) { + if (v.second) { + this->Environment.insert(QString::fromLocal8Bit(v.first.data()), + QString::fromLocal8Bit(v.second->data())); + } + } + } + } + emit this->propertiesChanged(this->properties()); + } +} + void QCMake::setGenerator(const QString& gen) { if (this->Generator != gen) { @@ -152,35 +223,46 @@ void QCMake::setToolset(const QString& toolset) } } +void QCMake::setEnvironment(const QProcessEnvironment& environment) +{ + this->Environment = environment; +} + void QCMake::configure() { + int err; + { + cmSystemTools::SaveRestoreEnvironment restoreEnv; + this->setUpEnvironment(); + #ifdef Q_OS_WIN - UINT lastErrorMode = SetErrorMode(0); + UINT lastErrorMode = SetErrorMode(0); #endif - this->CMakeInstance->SetHomeDirectory( - this->SourceDirectory.toLocal8Bit().data()); - this->CMakeInstance->SetHomeOutputDirectory( - this->BinaryDirectory.toLocal8Bit().data()); - this->CMakeInstance->SetGlobalGenerator( - this->CMakeInstance->CreateGlobalGenerator( - this->Generator.toLocal8Bit().data())); - this->CMakeInstance->SetGeneratorPlatform( - this->Platform.toLocal8Bit().data()); - this->CMakeInstance->SetGeneratorToolset(this->Toolset.toLocal8Bit().data()); - this->CMakeInstance->LoadCache(); - this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode); - this->CMakeInstance->SetWarnUnused(this->WarnUnusedMode); - this->CMakeInstance->PreLoadCMakeFiles(); - - InterruptFlag = 0; - cmSystemTools::ResetErrorOccuredFlag(); - - int err = this->CMakeInstance->Configure(); + this->CMakeInstance->SetHomeDirectory( + this->SourceDirectory.toLocal8Bit().data()); + this->CMakeInstance->SetHomeOutputDirectory( + this->BinaryDirectory.toLocal8Bit().data()); + this->CMakeInstance->SetGlobalGenerator( + this->CMakeInstance->CreateGlobalGenerator( + this->Generator.toLocal8Bit().data())); + this->CMakeInstance->SetGeneratorPlatform( + this->Platform.toLocal8Bit().data()); + this->CMakeInstance->SetGeneratorToolset( + this->Toolset.toLocal8Bit().data()); + this->CMakeInstance->LoadCache(); + this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode); + this->CMakeInstance->PreLoadCMakeFiles(); + + InterruptFlag = 0; + cmSystemTools::ResetErrorOccuredFlag(); + + err = this->CMakeInstance->Configure(); #ifdef Q_OS_WIN - SetErrorMode(lastErrorMode); + SetErrorMode(lastErrorMode); #endif + } emit this->propertiesChanged(this->properties()); emit this->configureDone(err); @@ -188,18 +270,24 @@ void QCMake::configure() void QCMake::generate() { + int err; + { + cmSystemTools::SaveRestoreEnvironment restoreEnv; + this->setUpEnvironment(); + #ifdef Q_OS_WIN - UINT lastErrorMode = SetErrorMode(0); + UINT lastErrorMode = SetErrorMode(0); #endif - InterruptFlag = 0; - cmSystemTools::ResetErrorOccuredFlag(); + InterruptFlag = 0; + cmSystemTools::ResetErrorOccuredFlag(); - int err = this->CMakeInstance->Generate(); + err = this->CMakeInstance->Generate(); #ifdef Q_OS_WIN - SetErrorMode(lastErrorMode); + SetErrorMode(lastErrorMode); #endif + } emit this->generateDone(err); checkOpenPossible(); @@ -330,6 +418,55 @@ QCMakePropertyList QCMake::properties() const ret.append(prop); } + if (!this->PresetName.isNull()) { + std::string presetName(this->PresetName.toLocal8Bit()); + auto const& p = this->CMakePresetsFile.Presets.at(presetName).Expanded; + if (p) { + for (auto const& v : p->CacheVariables) { + if (!v.second) { + continue; + } + QCMakeProperty prop; + prop.Key = QString::fromLocal8Bit(v.first.data()); + prop.Value = QString::fromLocal8Bit(v.second->Value.data()); + prop.Type = QCMakeProperty::STRING; + if (!v.second->Type.empty()) { + auto type = cmState::StringToCacheEntryType(v.second->Type); + switch (type) { + case cmStateEnums::BOOL: + prop.Type = QCMakeProperty::BOOL; + prop.Value = cmIsOn(v.second->Value); + break; + case cmStateEnums::PATH: + prop.Type = QCMakeProperty::PATH; + break; + case cmStateEnums::FILEPATH: + prop.Type = QCMakeProperty::FILEPATH; + break; + default: + prop.Type = QCMakeProperty::STRING; + break; + } + } + + // QCMakeCacheModel prefers variables earlier in the list rather than + // later, so overwrite them if they already exist rather than simply + // appending + bool found = false; + for (auto& orig : ret) { + if (orig.Key == prop.Key) { + orig = prop; + found = true; + break; + } + } + if (!found) { + ret.append(prop); + } + } + } + } + return ret; } @@ -340,10 +477,10 @@ void QCMake::interrupt() bool QCMake::interruptCallback() { -#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - return this->InterruptFlag; -#else +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) return this->InterruptFlag.load(); +#else + return this->InterruptFlag.loadRelaxed(); #endif } @@ -375,6 +512,61 @@ void QCMake::stderrCallback(std::string const& msg) QCoreApplication::processEvents(); } +void QCMake::setUpEnvironment() const +{ + auto env = QProcessEnvironment::systemEnvironment(); + for (auto const& key : env.keys()) { + cmSystemTools::UnsetEnv(key.toLocal8Bit().data()); + } + + for (auto const& var : this->Environment.toStringList()) { + cmSystemTools::PutEnv(var.toLocal8Bit().data()); + } +} + +void QCMake::loadPresets() +{ + auto result = this->CMakePresetsFile.ReadProjectPresets( + this->SourceDirectory.toLocal8Bit().data(), true); + if (result != this->LastLoadPresetsResult && + result != cmCMakePresetsFile::ReadFileResult::READ_OK) { + emit this->presetLoadError(this->SourceDirectory, result); + } + this->LastLoadPresetsResult = result; + + QVector<QCMakePreset> presets; + for (auto const& name : this->CMakePresetsFile.PresetOrder) { + auto const& it = this->CMakePresetsFile.Presets[name]; + auto const& p = it.Unexpanded; + if (p.Hidden) { + continue; + } + + QCMakePreset preset; + preset.name = std::move(QString::fromLocal8Bit(p.Name.data())); + preset.displayName = + std::move(QString::fromLocal8Bit(p.DisplayName.data())); + preset.description = + std::move(QString::fromLocal8Bit(p.Description.data())); + preset.generator = std::move(QString::fromLocal8Bit(p.Generator.data())); + preset.architecture = + std::move(QString::fromLocal8Bit(p.Architecture.data())); + preset.setArchitecture = !p.ArchitectureStrategy || + p.ArchitectureStrategy == cmCMakePresetsFile::ArchToolsetStrategy::Set; + preset.toolset = std::move(QString::fromLocal8Bit(p.Toolset.data())); + preset.setToolset = !p.ToolsetStrategy || + p.ToolsetStrategy == cmCMakePresetsFile::ArchToolsetStrategy::Set; + preset.enabled = it.Expanded && + std::find_if(this->AvailableGenerators.begin(), + this->AvailableGenerators.end(), + [&p](const cmake::GeneratorInfo& g) { + return g.name == p.Generator; + }) != this->AvailableGenerators.end(); + presets.push_back(preset); + } + emit this->presetsChanged(presets); +} + QString QCMake::binaryDirectory() const { return this->BinaryDirectory; @@ -390,6 +582,11 @@ QString QCMake::generator() const return this->Generator; } +QProcessEnvironment QCMake::environment() const +{ + return this->Environment; +} + std::vector<cmake::GeneratorInfo> const& QCMake::availableGenerators() const { return AvailableGenerators; @@ -478,11 +675,6 @@ void QCMake::setWarnUninitializedMode(bool value) this->WarnUninitializedMode = value; } -void QCMake::setWarnUnusedMode(bool value) -{ - this->WarnUnusedMode = value; -} - void QCMake::checkOpenPossible() { std::string data = this->BinaryDirectory.toLocal8Bit().data(); diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h index 110a971d0a..a6751b0cae 100644 --- a/Source/QtDialog/QCMake.h +++ b/Source/QtDialog/QCMake.h @@ -1,10 +1,10 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef QCMake_h -#define QCMake_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep +#include "cmCMakePresetsFile.h" #include "cmake.h" #ifdef _MSC_VER @@ -15,12 +15,15 @@ #include <memory> #include <vector> +#include "QCMakePreset.h" #include <QAtomicInt> #include <QList> #include <QMetaType> #include <QObject> +#include <QProcessEnvironment> #include <QString> #include <QStringList> +#include <QTimer> #include <QVariant> /// struct to represent cmake properties in Qt @@ -56,6 +59,8 @@ using QCMakePropertyList = QList<QCMakeProperty>; // allow QVariant to be a property or list of properties Q_DECLARE_METATYPE(QCMakeProperty) Q_DECLARE_METATYPE(QCMakePropertyList) +Q_DECLARE_METATYPE(QProcessEnvironment) +Q_DECLARE_METATYPE(cmCMakePresetsFile::ReadFileResult) /// Qt API for CMake library. /// Wrapper like class allows for easier integration with @@ -73,12 +78,16 @@ public slots: void setSourceDirectory(const QString& dir); /// set the binary directory to build in void setBinaryDirectory(const QString& dir); + /// set the preset name to use + void setPreset(const QString& name, bool setBinary = true); /// set the desired generator to use void setGenerator(const QString& generator); /// set the desired generator to use void setPlatform(const QString& platform); /// set the desired generator to use void setToolset(const QString& toolset); + /// set the configure and generate environment + void setEnvironment(const QProcessEnvironment& environment); /// do the configure step void configure(); /// generate the files @@ -114,8 +123,6 @@ public slots: void setDeprecatedWarningsAsErrors(bool value); /// set whether to run cmake with warnings about uninitialized variables void setWarnUninitializedMode(bool value); - /// set whether to run cmake with warnings about unused variables - void setWarnUnusedMode(bool value); /// check if project IDE open is possible and emit openPossible signal void checkOpenPossible(); @@ -128,6 +135,8 @@ public: QString sourceDirectory() const; /// get the current generator QString generator() const; + /// get the configure and generate environment + QProcessEnvironment environment() const; /// get the available generators std::vector<cmake::GeneratorInfo> const& availableGenerators() const; /// get whether to do debug output @@ -144,6 +153,15 @@ signals: void sourceDirChanged(const QString& dir); /// signal when the binary directory changes void binaryDirChanged(const QString& dir); + /// signal when the preset list changes + void presetsChanged(const QVector<QCMakePreset>& presets); + /// signal when the selected preset changes + void presetChanged(const QString& name); + /// signal when there's an error reading the presets files + void presetLoadError(const QString& dir, + cmCMakePresetsFile::ReadFileResult error); + /// signal when uninitialized warning changes + void warnUninitializedModeChanged(bool value); /// signal for progress events void progressChanged(const QString& msg, float percent); /// signal when configure is done @@ -173,18 +191,24 @@ protected: void messageCallback(std::string const& msg, const char* title); void stdoutCallback(std::string const& msg); void stderrCallback(std::string const& msg); + void setUpEnvironment() const; + + void loadPresets(); bool WarnUninitializedMode; - bool WarnUnusedMode; - bool WarnUnusedAllMode; QString SourceDirectory; QString BinaryDirectory; QString Generator; QString Platform; QString Toolset; std::vector<cmake::GeneratorInfo> AvailableGenerators; + cmCMakePresetsFile CMakePresetsFile; + cmCMakePresetsFile::ReadFileResult LastLoadPresetsResult = + cmCMakePresetsFile::ReadFileResult::READ_OK; + QString PresetName; QString CMakeExecutable; QAtomicInt InterruptFlag; + QProcessEnvironment StartEnvironment; + QProcessEnvironment Environment; + QTimer LoadPresetsTimer; }; - -#endif // QCMake_h diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx index 9b24fbdc93..22f5be19f7 100644 --- a/Source/QtDialog/QCMakeCacheView.cxx +++ b/Source/QtDialog/QCMakeCacheView.cxx @@ -155,11 +155,7 @@ QModelIndex QCMakeCacheView::moveCursor(CursorAction act, void QCMakeCacheView::setShowAdvanced(bool s) { -#if QT_VERSION >= 040300 - // new 4.3 API that needs to be called. what about an older Qt? this->SearchFilter->invalidate(); -#endif - this->AdvancedFilter->setShowAdvanced(s); } @@ -209,9 +205,7 @@ void QCMakeCacheModel::clear() void QCMakeCacheModel::setProperties(const QCMakePropertyList& props) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) this->beginResetModel(); -#endif QSet<QCMakeProperty> newProps; QSet<QCMakeProperty> newProps2; @@ -335,11 +329,7 @@ void QCMakeCacheModel::setProperties(const QCMakePropertyList& props) } this->blockSignals(b); -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) this->endResetModel(); -#else - this->reset(); -#endif } QCMakeCacheModel::ViewType QCMakeCacheModel::viewType() const @@ -349,9 +339,7 @@ QCMakeCacheModel::ViewType QCMakeCacheModel::viewType() const void QCMakeCacheModel::setViewType(QCMakeCacheModel::ViewType t) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) this->beginResetModel(); -#endif this->View = t; @@ -368,11 +356,7 @@ void QCMakeCacheModel::setViewType(QCMakeCacheModel::ViewType t) this->setProperties(oldProps); this->setProperties(props); this->blockSignals(b); -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) this->endResetModel(); -#else - this->reset(); -#endif } void QCMakeCacheModel::setPropertyData(const QModelIndex& idx1, @@ -498,8 +482,7 @@ QCMakePropertyList QCMakeCacheModel::properties() const // go to the next in the tree while (!idxs.isEmpty() && ( -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && \ - QT_VERSION < QT_VERSION_CHECK(5, 1, 0) +#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0) (idxs.last().row() + 1) >= rowCount(idxs.last().parent()) || #endif !idxs.last().sibling(idxs.last().row() + 1, 0).isValid())) { @@ -593,15 +576,15 @@ QWidget* QCMakeCacheModelDelegate::createEditor( if (type == QCMakeProperty::PATH) { QCMakePathEditor* editor = new QCMakePathEditor(p, var.data(Qt::DisplayRole).toString()); - QObject::connect(editor, SIGNAL(fileDialogExists(bool)), this, - SLOT(setFileDialogFlag(bool))); + QObject::connect(editor, &QCMakePathEditor::fileDialogExists, this, + &QCMakeCacheModelDelegate::setFileDialogFlag); return editor; } if (type == QCMakeProperty::FILEPATH) { QCMakeFilePathEditor* editor = new QCMakeFilePathEditor(p, var.data(Qt::DisplayRole).toString()); - QObject::connect(editor, SIGNAL(fileDialogExists(bool)), this, - SLOT(setFileDialogFlag(bool))); + QObject::connect(editor, &QCMakePathEditor::fileDialogExists, this, + &QCMakeCacheModelDelegate::setFileDialogFlag); return editor; } if (type == QCMakeProperty::STRING && @@ -659,14 +642,13 @@ bool QCMakeCacheModelDelegate::editorEvent(QEvent* e, return success; } -// Issue 205903 fixed in Qt 4.5.0. -// Can remove this function and FileDialogFlag when minimum Qt version is 4.5 bool QCMakeCacheModelDelegate::eventFilter(QObject* object, QEvent* evt) { - // workaround for what looks like a bug in Qt on macOS - // where it doesn't create a QWidget wrapper for the native file dialog - // so the Qt library ends up assuming the focus was lost to something else - + // FIXME: This filter avoids a crash when opening a file dialog + // with the '...' button on a cache entry line in the GUI. + // Previously this filter was commented as a workaround for Qt issue 205903, + // but that was fixed in Qt 4.5.0 and the crash still occurs as of Qt 5.14 + // without this filter. This needs further investigation. if (evt->type() == QEvent::FocusOut && this->FileDialogFlag) { return false; } diff --git a/Source/QtDialog/QCMakeCacheView.h b/Source/QtDialog/QCMakeCacheView.h index bea1965868..c5e6dd4438 100644 --- a/Source/QtDialog/QCMakeCacheView.h +++ b/Source/QtDialog/QCMakeCacheView.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef QCMakeCacheView_h -#define QCMakeCacheView_h +#pragma once #include "QCMake.h" #include <QItemDelegate> @@ -49,7 +48,7 @@ class QCMakeCacheModel : public QStandardItemModel { Q_OBJECT public: - QCMakeCacheModel(QObject* parent); + QCMakeCacheModel(QObject* parent = nullptr); ~QCMakeCacheModel(); // roles used to retrieve extra data such has help strings, types of @@ -165,5 +164,3 @@ protected: // properties changed by user via this delegate QSet<QCMakeProperty> mChanges; }; - -#endif diff --git a/Source/QtDialog/QCMakePreset.cxx b/Source/QtDialog/QCMakePreset.cxx new file mode 100644 index 0000000000..176f532ad1 --- /dev/null +++ b/Source/QtDialog/QCMakePreset.cxx @@ -0,0 +1,53 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "QCMakePreset.h" + +bool operator==(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return lhs.name == rhs.name && lhs.displayName == rhs.displayName && + lhs.description == rhs.description && lhs.generator == rhs.generator && + lhs.architecture == rhs.architecture && + lhs.setArchitecture == rhs.setArchitecture && lhs.toolset == rhs.toolset && + lhs.setToolset == rhs.setToolset && lhs.enabled == rhs.enabled; +} + +bool operator!=(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return !(lhs == rhs); +} + +bool operator<(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return lhs.name < rhs.name || + (lhs.name == rhs.name && + (lhs.displayName < rhs.displayName || + (lhs.displayName == rhs.displayName && + (lhs.description < rhs.description || + (lhs.description == rhs.description && + (lhs.generator < rhs.generator || + (lhs.generator == rhs.generator && + (lhs.architecture < rhs.architecture || + (lhs.architecture == rhs.architecture && + (lhs.setArchitecture < rhs.setArchitecture || + (lhs.setArchitecture == rhs.setArchitecture && + (lhs.toolset < rhs.toolset || + (lhs.toolset == rhs.toolset && + (lhs.setToolset < rhs.setToolset || + (lhs.setToolset == rhs.setToolset && + (lhs.enabled < rhs.enabled)))))))))))))))); +} + +bool operator<=(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return rhs >= lhs; +} + +bool operator>(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return rhs < lhs; +} + +bool operator>=(const QCMakePreset& lhs, const QCMakePreset& rhs) +{ + return !(lhs < rhs); +} diff --git a/Source/QtDialog/QCMakePreset.h b/Source/QtDialog/QCMakePreset.h new file mode 100644 index 0000000000..1609fcb357 --- /dev/null +++ b/Source/QtDialog/QCMakePreset.h @@ -0,0 +1,31 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <QString> +#include <QVariant> + +#include "cmCMakePresetsFile.h" + +class QCMakePreset +{ +public: + QString name; + QString displayName; + QString description; + QString generator; + QString architecture; + bool setArchitecture; + QString toolset; + bool setToolset; + bool enabled; +}; + +bool operator==(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator!=(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator<(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator<=(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator>(const QCMakePreset& lhs, const QCMakePreset& rhs); +bool operator>=(const QCMakePreset& lhs, const QCMakePreset& rhs); + +Q_DECLARE_METATYPE(QCMakePreset) diff --git a/Source/QtDialog/QCMakePresetComboBox.cxx b/Source/QtDialog/QCMakePresetComboBox.cxx new file mode 100644 index 0000000000..efadb73db6 --- /dev/null +++ b/Source/QtDialog/QCMakePresetComboBox.cxx @@ -0,0 +1,64 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "QCMakePresetComboBox.h" + +#include "QCMakePresetItemModel.h" + +QCMakePresetComboBox::QCMakePresetComboBox(QWidget* parent) + : QComboBox(parent) +{ + this->m_model = new QCMakePresetItemModel(this); + this->setModel(this->m_model); + + QObject::connect(this->m_model, &QCMakePresetItemModel::modelAboutToBeReset, + this, [this]() { this->m_resetting = true; }); + QObject::connect(this->m_model, &QCMakePresetItemModel::modelReset, this, + [this]() { + this->setPresetName(this->m_lastPreset); + this->m_resetting = false; + this->emitPresetChanged(); + }); + QObject::connect( + this, + static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + this, [this](int /*row*/) { + if (!this->m_resetting) { + this->emitPresetChanged(); + } + }); +} + +const QVector<QCMakePreset>& QCMakePresetComboBox::presets() const +{ + return this->m_model->presets(); +} + +QString QCMakePresetComboBox::presetName() const +{ + auto preset = this->currentData(); + if (preset.canConvert<QCMakePreset>()) { + return preset.value<QCMakePreset>().name; + } + return QString{}; +} + +void QCMakePresetComboBox::setPresets(const QVector<QCMakePreset>& presets) +{ + this->m_model->setPresets(presets); +} + +void QCMakePresetComboBox::setPresetName(const QString& name) +{ + this->setCurrentIndex(this->m_model->presetNameToRow(name)); + if (this->signalsBlocked()) { + this->m_lastPreset = this->presetName(); + } +} + +void QCMakePresetComboBox::emitPresetChanged() +{ + if (this->presetName() != this->m_lastPreset) { + emit this->presetChanged(this->presetName()); + this->m_lastPreset = this->presetName(); + } +} diff --git a/Source/QtDialog/QCMakePresetComboBox.h b/Source/QtDialog/QCMakePresetComboBox.h new file mode 100644 index 0000000000..d1eeffe798 --- /dev/null +++ b/Source/QtDialog/QCMakePresetComboBox.h @@ -0,0 +1,35 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include "QCMakePreset.h" +#include <QComboBox> +#include <QObject> +#include <QString> +#include <QVector> + +class QCMakePresetItemModel; + +class QCMakePresetComboBox : public QComboBox +{ + Q_OBJECT +public: + QCMakePresetComboBox(QWidget* parent = nullptr); + + const QVector<QCMakePreset>& presets() const; + QString presetName() const; + +public slots: + void setPresets(const QVector<QCMakePreset>& presets); + void setPresetName(const QString& name); + +signals: + void presetChanged(const QString& name); + +private: + QCMakePresetItemModel* m_model; + bool m_resetting = false; + QString m_lastPreset; + + void emitPresetChanged(); +}; diff --git a/Source/QtDialog/QCMakePresetItemModel.cxx b/Source/QtDialog/QCMakePresetItemModel.cxx new file mode 100644 index 0000000000..00a4e18a0a --- /dev/null +++ b/Source/QtDialog/QCMakePresetItemModel.cxx @@ -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. */ +#include "QCMakePresetItemModel.h" + +#include <QFont> + +QCMakePresetItemModel::QCMakePresetItemModel(QObject* parent) + : QAbstractItemModel(parent) +{ +} + +QVariant QCMakePresetItemModel::data(const QModelIndex& index, int role) const +{ + switch (role) { + case Qt::AccessibleDescriptionRole: + // Separators have to return "separator" for the + // AccessibleDescriptionRole. This was determined by looking at + // QComboBoxDelegate::isSeparator() (located in qcombobox_p.h.) + if (index.internalId() == SEPARATOR_INDEX) { + return QString::fromLocal8Bit("separator"); + } + return QString{}; + case Qt::DisplayRole: { + if (index.internalId() == CUSTOM_INDEX) { + return QString::fromLocal8Bit("<custom>"); + } + if (index.internalId() == SEPARATOR_INDEX) { + return QVariant{}; + } + auto const& preset = this->m_presets[index.internalId()]; + return preset.displayName.isEmpty() ? preset.name : preset.displayName; + } + case Qt::ToolTipRole: + if (index.internalId() == CUSTOM_INDEX) { + return QString::fromLocal8Bit("Specify all settings manually"); + } + if (index.internalId() == SEPARATOR_INDEX) { + return QVariant{}; + } + return this->m_presets[index.internalId()].description; + case Qt::UserRole: + if (index.internalId() == CUSTOM_INDEX) { + return QVariant{}; + } + if (index.internalId() == SEPARATOR_INDEX) { + return QVariant{}; + } + return QVariant::fromValue(this->m_presets[index.internalId()]); + case Qt::FontRole: + if (index.internalId() == CUSTOM_INDEX) { + QFont font; + font.setItalic(true); + return font; + } + return QFont{}; + default: + return QVariant{}; + } +} + +Qt::ItemFlags QCMakePresetItemModel::flags(const QModelIndex& index) const +{ + Qt::ItemFlags flags = + Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + if (index.internalId() != SEPARATOR_INDEX && + (index.internalId() == CUSTOM_INDEX || + this->m_presets[index.internalId()].enabled)) { + flags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled; + } + return flags; +} + +int QCMakePresetItemModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { + return 0; + } + if (this->m_presets.empty()) { + return 1; + } + return this->m_presets.size() + 2; +} + +int QCMakePresetItemModel::columnCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { + return 0; + } + return 1; +} + +QModelIndex QCMakePresetItemModel::index(int row, int column, + const QModelIndex& parent) const +{ + if (parent.isValid() || column != 0 || row < 0 || + row >= this->rowCount(QModelIndex{})) { + return QModelIndex{}; + } + + if (this->m_presets.empty() || row == this->m_presets.size() + 1) { + return this->createIndex(row, column, CUSTOM_INDEX); + } + + if (row == this->m_presets.size()) { + return this->createIndex(row, column, SEPARATOR_INDEX); + } + + return this->createIndex(row, column, static_cast<quintptr>(row)); +} + +QModelIndex QCMakePresetItemModel::parent(const QModelIndex& /*index*/) const +{ + return QModelIndex{}; +} + +QVector<QCMakePreset> const& QCMakePresetItemModel::presets() const +{ + return this->m_presets; +} + +void QCMakePresetItemModel::setPresets(QVector<QCMakePreset> const& presets) +{ + this->beginResetModel(); + this->m_presets = presets; + this->endResetModel(); +} + +int QCMakePresetItemModel::presetNameToRow(const QString& name) const +{ + if (this->m_presets.empty()) { + return 0; + } + + int index = 0; + for (auto const& preset : this->m_presets) { + if (preset.name == name) { + return index; + } + index++; + } + + return this->m_presets.size() + 1; +} diff --git a/Source/QtDialog/QCMakePresetItemModel.h b/Source/QtDialog/QCMakePresetItemModel.h new file mode 100644 index 0000000000..79fba295c3 --- /dev/null +++ b/Source/QtDialog/QCMakePresetItemModel.h @@ -0,0 +1,45 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <cm/optional> + +#include "QCMakePreset.h" +#include <QAbstractItemModel> +#include <QModelIndex> +#include <QString> +#include <QVariant> +#include <QVector> +#include <QtGlobal> + +class QObject; + +class QCMakePresetItemModel : public QAbstractItemModel +{ + Q_OBJECT +public: + QCMakePresetItemModel(QObject* parent = nullptr); + + QVariant data(const QModelIndex& index, int role) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + + int rowCount(const QModelIndex& parent = QModelIndex{}) const override; + int columnCount(const QModelIndex& parent = QModelIndex{}) const override; + + QModelIndex index(int row, int column, + const QModelIndex& parent = QModelIndex{}) const override; + QModelIndex parent(const QModelIndex& index) const override; + + QVector<QCMakePreset> const& presets() const; + + int presetNameToRow(const QString& name) const; + +public slots: + void setPresets(QVector<QCMakePreset> const& presets); + +private: + QVector<QCMakePreset> m_presets; + + static constexpr quintptr SEPARATOR_INDEX = static_cast<quintptr>(-2); + static constexpr quintptr CUSTOM_INDEX = static_cast<quintptr>(-1); +}; diff --git a/Source/QtDialog/QCMakeWidgets.cxx b/Source/QtDialog/QCMakeWidgets.cxx index 332a770ef8..e68faba52a 100644 --- a/Source/QtDialog/QCMakeWidgets.cxx +++ b/Source/QtDialog/QCMakeWidgets.cxx @@ -1,5 +1,10 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ + +// FIXME: Port to QFileSystemModel from the deprecated QDirModel. +// Be sure completion works when incrementally editing existing paths. +#define QT_DEPRECATED_WARNINGS_SINCE QT_VERSION_CHECK(5, 14, 0) + #include "QCMakeWidgets.h" #include <utility> @@ -17,8 +22,8 @@ QCMakeFileEditor::QCMakeFileEditor(QWidget* p, QString var) this->ToolButton = new QToolButton(this); this->ToolButton->setText("..."); this->ToolButton->setCursor(QCursor(Qt::ArrowCursor)); - QObject::connect(this->ToolButton, SIGNAL(clicked(bool)), this, - SLOT(chooseFile())); + QObject::connect(this->ToolButton, &QToolButton::clicked, this, + &QCMakeFileEditor::chooseFile); } QCMakeFilePathEditor::QCMakeFilePathEditor(QWidget* p, const QString& var) diff --git a/Source/QtDialog/QCMakeWidgets.h b/Source/QtDialog/QCMakeWidgets.h index 5d2368eab9..9a2a27e490 100644 --- a/Source/QtDialog/QCMakeWidgets.h +++ b/Source/QtDialog/QCMakeWidgets.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef QCMakeWidgets_h -#define QCMakeWidgets_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -77,5 +76,3 @@ public: } } }; - -#endif diff --git a/Source/QtDialog/RegexExplorer.h b/Source/QtDialog/RegexExplorer.h index 1a1d7700fc..9a42320787 100644 --- a/Source/QtDialog/RegexExplorer.h +++ b/Source/QtDialog/RegexExplorer.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef RegexExplorer_h -#define RegexExplorer_h +#pragma once #include <string> @@ -39,5 +38,3 @@ private: std::string m_regex; bool m_matched; }; - -#endif diff --git a/Source/QtDialog/WarningMessagesDialog.cxx b/Source/QtDialog/WarningMessagesDialog.cxx index f608a84f98..1fcf2b1a48 100644 --- a/Source/QtDialog/WarningMessagesDialog.cxx +++ b/Source/QtDialog/WarningMessagesDialog.cxx @@ -26,18 +26,22 @@ void WarningMessagesDialog::setInitialValues() void WarningMessagesDialog::setupSignals() { - QObject::connect(this->buttonBox, SIGNAL(accepted()), this, - SLOT(doAccept())); + QObject::connect(this->buttonBox, &QDialogButtonBox::accepted, this, + &WarningMessagesDialog::doAccept); - QObject::connect(this->suppressDeveloperWarnings, SIGNAL(stateChanged(int)), - this, SLOT(doSuppressDeveloperWarningsChanged(int))); - QObject::connect(this->suppressDeprecatedWarnings, SIGNAL(stateChanged(int)), - this, SLOT(doSuppressDeprecatedWarningsChanged(int))); + QObject::connect(this->suppressDeveloperWarnings, &QCheckBox::stateChanged, + this, + &WarningMessagesDialog::doSuppressDeveloperWarningsChanged); + QObject::connect( + this->suppressDeprecatedWarnings, &QCheckBox::stateChanged, this, + &WarningMessagesDialog::doSuppressDeprecatedWarningsChanged); - QObject::connect(this->developerWarningsAsErrors, SIGNAL(stateChanged(int)), - this, SLOT(doDeveloperWarningsAsErrorsChanged(int))); - QObject::connect(this->deprecatedWarningsAsErrors, SIGNAL(stateChanged(int)), - this, SLOT(doDeprecatedWarningsAsErrorsChanged(int))); + QObject::connect(this->developerWarningsAsErrors, &QCheckBox::stateChanged, + this, + &WarningMessagesDialog::doDeveloperWarningsAsErrorsChanged); + QObject::connect( + this->deprecatedWarningsAsErrors, &QCheckBox::stateChanged, this, + &WarningMessagesDialog::doDeprecatedWarningsAsErrorsChanged); } void WarningMessagesDialog::doAccept() diff --git a/Source/QtDialog/WarningMessagesDialog.h b/Source/QtDialog/WarningMessagesDialog.h index f209dbdb3b..bb017040b4 100644 --- a/Source/QtDialog/WarningMessagesDialog.h +++ b/Source/QtDialog/WarningMessagesDialog.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef WarningMessagesDialog_h -#define WarningMessagesDialog_h +#pragma once #include "QCMake.h" #include <QDialog> @@ -63,5 +62,3 @@ private: */ void setupSignals(); }; - -#endif /* MessageDialog_h */ diff --git a/Source/bindexplib.h b/Source/bindexplib.h index 538177dff3..bd1398fa7a 100644 --- a/Source/bindexplib.h +++ b/Source/bindexplib.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef bindexplib_h -#define bindexplib_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -25,4 +24,3 @@ private: std::set<std::string> DataSymbols; std::string NmPath; }; -#endif diff --git a/Source/cmAddCompileDefinitionsCommand.h b/Source/cmAddCompileDefinitionsCommand.h index 4bd621c4b4..29282d6c02 100644 --- a/Source/cmAddCompileDefinitionsCommand.h +++ b/Source/cmAddCompileDefinitionsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAddCompileDefinitionsCommand_h -#define cmAddCompileDefinitionsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddCompileDefinitionsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddCompileOptionsCommand.h b/Source/cmAddCompileOptionsCommand.h index b172412be3..076a427a2b 100644 --- a/Source/cmAddCompileOptionsCommand.h +++ b/Source/cmAddCompileOptionsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAddCompileOptionsCommand_h -#define cmAddCompileOptionsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddCompileOptionsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddCustomCommandCommand.h b/Source/cmAddCustomCommandCommand.h index 4f8c58fef8..383d116530 100644 --- a/Source/cmAddCustomCommandCommand.h +++ b/Source/cmAddCustomCommandCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAddCustomCommandCommand_h -#define cmAddCustomCommandCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddCustomCommandCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddCustomTargetCommand.h b/Source/cmAddCustomTargetCommand.h index e23ef9fcc1..3b784cb10a 100644 --- a/Source/cmAddCustomTargetCommand.h +++ b/Source/cmAddCustomTargetCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAddCustomTargetCommand_h -#define cmAddCustomTargetCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddCustomTargetCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddDefinitionsCommand.h b/Source/cmAddDefinitionsCommand.h index a67f095161..45b4554776 100644 --- a/Source/cmAddDefinitionsCommand.h +++ b/Source/cmAddDefinitionsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAddDefinitionsCommand_h -#define cmAddDefinitionsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddDefinitionsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddDependenciesCommand.h b/Source/cmAddDependenciesCommand.h index 0c60e3a2d5..a76755086b 100644 --- a/Source/cmAddDependenciesCommand.h +++ b/Source/cmAddDependenciesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmDependenciessCommand_h -#define cmDependenciessCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddDependenciesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddExecutableCommand.h b/Source/cmAddExecutableCommand.h index f7bc27336f..032c14d12b 100644 --- a/Source/cmAddExecutableCommand.h +++ b/Source/cmAddExecutableCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExecutablesCommand_h -#define cmExecutablesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddExecutableCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx index 3e5d764849..f262facc38 100644 --- a/Source/cmAddLibraryCommand.cxx +++ b/Source/cmAddLibraryCommand.cxx @@ -2,8 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmAddLibraryCommand.h" -#include <cmext/algorithm> - #include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" @@ -111,20 +109,10 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args, "INTERFACE library specified with conflicting ALIAS type."); return false; } - if (excludeFromAll) { - status.SetError( - "INTERFACE library may not be used with EXCLUDE_FROM_ALL."); - return false; - } ++s; type = cmStateEnums::INTERFACE_LIBRARY; haveSpecifiedType = true; } else if (*s == "EXCLUDE_FROM_ALL") { - if (type == cmStateEnums::INTERFACE_LIBRARY) { - status.SetError( - "INTERFACE library may not be used with EXCLUDE_FROM_ALL."); - return false; - } ++s; excludeFromAll = true; } else if (*s == "IMPORTED") { @@ -143,10 +131,6 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args, } if (type == cmStateEnums::INTERFACE_LIBRARY) { - if (s != args.end()) { - status.SetError("INTERFACE library requires no source arguments."); - return false; - } if (importGlobal && !importTarget) { status.SetError( "INTERFACE library specified as GLOBAL, but not as IMPORTED."); @@ -302,8 +286,6 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args, } } - std::vector<std::string> srclists; - if (type == cmStateEnums::INTERFACE_LIBRARY) { if (!cmGeneratorExpression::IsValidTargetName(libName) || libName.find("::") != std::string::npos) { @@ -311,14 +293,10 @@ bool cmAddLibraryCommand(std::vector<std::string> const& args, cmStrCat("Invalid name for INTERFACE library target: ", libName)); return false; } - - mf.AddLibrary(libName, type, srclists, excludeFromAll); - return true; } - cm::append(srclists, s, args.end()); - - mf.AddLibrary(libName, type, srclists, excludeFromAll); + std::vector<std::string> srcs(s, args.end()); + mf.AddLibrary(libName, type, srcs, excludeFromAll); return true; } diff --git a/Source/cmAddLibraryCommand.h b/Source/cmAddLibraryCommand.h index 609449c87e..a4a0ea00b3 100644 --- a/Source/cmAddLibraryCommand.h +++ b/Source/cmAddLibraryCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLibrarysCommand_h -#define cmLibrarysCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddLibraryCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddLinkOptionsCommand.h b/Source/cmAddLinkOptionsCommand.h index 466fc3215b..5c9d5e435c 100644 --- a/Source/cmAddLinkOptionsCommand.h +++ b/Source/cmAddLinkOptionsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAddLinkOptionsCommand_h -#define cmAddLinkOptionsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddLinkOptionsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddSubDirectoryCommand.h b/Source/cmAddSubDirectoryCommand.h index 87da8402bf..ece3b2726b 100644 --- a/Source/cmAddSubDirectoryCommand.h +++ b/Source/cmAddSubDirectoryCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAddSubDirectoryCommand_h -#define cmAddSubDirectoryCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddSubDirectoryCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAddTestCommand.h b/Source/cmAddTestCommand.h index 5877547e8b..8cba2b1b5d 100644 --- a/Source/cmAddTestCommand.h +++ b/Source/cmAddTestCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAddTestCommand_h -#define cmAddTestCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAddTestCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h index c8e8dcb850..87000da526 100644 --- a/Source/cmAlgorithms.h +++ b/Source/cmAlgorithms.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAlgorithms_h -#define cmAlgorithms_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -143,5 +142,3 @@ 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; }); } - -#endif diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx index addfbff3e4..356089b83f 100644 --- a/Source/cmArchiveWrite.cxx +++ b/Source/cmArchiveWrite.cxx @@ -81,7 +81,7 @@ struct cmArchiveWrite::Callback }; cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c, - std::string const& format) + std::string const& format, int compressionLevel) : Stream(os) , Archive(archive_write_new()) , Disk(archive_read_disk_new()) @@ -151,6 +151,41 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c, } break; } + + if (compressionLevel != 0) { + std::string compressionLevelStr = std::to_string(compressionLevel); + std::string archiveFilterName; + switch (c) { + case CompressNone: + case CompressCompress: + break; + case CompressGZip: + archiveFilterName = "gzip"; + break; + case CompressBZip2: + archiveFilterName = "bzip2"; + break; + case CompressLZMA: + archiveFilterName = "lzma"; + break; + case CompressXZ: + archiveFilterName = "xz"; + break; + case CompressZstd: + archiveFilterName = "zstd"; + break; + } + if (!archiveFilterName.empty()) { + if (archive_write_set_filter_option( + this->Archive, archiveFilterName.c_str(), "compression-level", + compressionLevelStr.c_str()) != ARCHIVE_OK) { + this->Error = cmStrCat("archive_write_set_filter_option: ", + cm_archive_error_string(this->Archive)); + return; + } + } + } + #if !defined(_WIN32) || defined(__CYGWIN__) if (archive_read_disk_set_standard_lookup(this->Disk) != ARCHIVE_OK) { this->Error = cmStrCat("archive_read_disk_set_standard_lookup: ", diff --git a/Source/cmArchiveWrite.h b/Source/cmArchiveWrite.h index b643bce87f..0d33758d87 100644 --- a/Source/cmArchiveWrite.h +++ b/Source/cmArchiveWrite.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmArchiveWrite_h -#define cmArchiveWrite_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -55,7 +54,7 @@ public: /** Construct with output stream to which to write archive. */ cmArchiveWrite(std::ostream& os, Compress c = CompressNone, - std::string const& format = "paxr"); + std::string const& format = "paxr", int compressionLevel = 0); ~cmArchiveWrite(); @@ -180,5 +179,3 @@ private: cmArchiveWriteOptional<int> Permissions; cmArchiveWriteOptional<int> PermissionsMask; }; - -#endif diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h index 5d2dfa224a..71ed844811 100644 --- a/Source/cmArgumentParser.h +++ b/Source/cmArgumentParser.h @@ -1,7 +1,6 @@ /* 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 +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -146,5 +145,3 @@ protected: private: ArgumentParser::ActionMap Bindings; }; - -#endif diff --git a/Source/cmAuxSourceDirectoryCommand.cxx b/Source/cmAuxSourceDirectoryCommand.cxx index d6f7500e8f..15edcc5664 100644 --- a/Source/cmAuxSourceDirectoryCommand.cxx +++ b/Source/cmAuxSourceDirectoryCommand.cxx @@ -36,10 +36,7 @@ bool cmAuxSourceDirectoryCommand(std::vector<std::string> const& args, } // was the list already populated - const char* def = mf.GetDefinition(args[1]); - if (def) { - sourceListValue = def; - } + sourceListValue = mf.GetSafeDefinition(args[1]); std::vector<std::string> files; @@ -55,7 +52,7 @@ bool cmAuxSourceDirectoryCommand(std::vector<std::string> const& args, auto ext = cm::string_view(file).substr(dotpos + 1); // Process only source files auto cm = mf.GetCMakeInstance(); - if (dotpos > 0 && cm->IsSourceExtension(ext)) { + if (dotpos > 0 && cm->IsACLikeSourceExtension(ext)) { std::string fullname = cmStrCat(templateDirectory, '/', file); // add the file as a class file so // depends can be done diff --git a/Source/cmAuxSourceDirectoryCommand.h b/Source/cmAuxSourceDirectoryCommand.h index ae26092052..29ee429a9b 100644 --- a/Source/cmAuxSourceDirectoryCommand.h +++ b/Source/cmAuxSourceDirectoryCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmAuxSourceDirectoryCommand_h -#define cmAuxSourceDirectoryCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmAuxSourceDirectoryCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmBase32.h b/Source/cmBase32.h index d85198dd5f..726f45d51c 100644 --- a/Source/cmBase32.h +++ b/Source/cmBase32.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBase32_h -#define cmBase32_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -29,5 +28,3 @@ public: std::string encodeString(const unsigned char* input, size_t len, bool padding = true); }; - -#endif diff --git a/Source/cmBinUtilsLinker.h b/Source/cmBinUtilsLinker.h index 78d517b619..5330070c0b 100644 --- a/Source/cmBinUtilsLinker.h +++ b/Source/cmBinUtilsLinker.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsLinker_h -#define cmBinUtilsLinker_h +#pragma once #include <string> @@ -26,5 +25,3 @@ protected: void SetError(const std::string& e); }; - -#endif // cmBinUtilsLinker_h diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h index d514e7f918..15216a43af 100644 --- a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h +++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h -#define cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h +#pragma once #include <string> #include <vector> @@ -26,5 +25,3 @@ protected: void SetError(const std::string& e); }; - -#endif // cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsLinuxELFLinker.h b/Source/cmBinUtilsLinuxELFLinker.h index b17df11ee6..4e7e36d4c8 100644 --- a/Source/cmBinUtilsLinuxELFLinker.h +++ b/Source/cmBinUtilsLinuxELFLinker.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsLinuxELFLinker_h -#define cmBinUtilsLinuxELFLinker_h +#pragma once #include <memory> #include <string> @@ -40,5 +39,3 @@ private: bool GetLDConfigPaths(); }; - -#endif // cmBinUtilsLinuxELFLinker_h diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h index 969e4d4dc9..def1dd08ef 100644 --- a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h +++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h -#define cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h +#pragma once #include <string> #include <vector> @@ -22,5 +21,3 @@ public: std::vector<std::string>& rpaths, std::vector<std::string>& runpaths) override; }; - -#endif // cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h index dbb2882550..60d34aad18 100644 --- a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h +++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h -#define cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h +#pragma once #include <string> #include <vector> @@ -25,5 +24,3 @@ protected: void SetError(const std::string& error); }; - -#endif // cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsMacOSMachOLinker.h b/Source/cmBinUtilsMacOSMachOLinker.h index 4a24ea35e9..1c4a5fcd3c 100644 --- a/Source/cmBinUtilsMacOSMachOLinker.h +++ b/Source/cmBinUtilsMacOSMachOLinker.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsMacOSMachOLinker_h -#define cmBinUtilsMacOSMachOLinker_h +#pragma once #include <memory> #include <string> @@ -55,5 +54,3 @@ private: std::vector<std::string> const& rpaths, std::string& path, bool& resolved); }; - -#endif // cmBinUtilsMacOSMachOLinker_h diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx index 351d92aa82..6d97720c25 100644 --- a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx +++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx @@ -44,7 +44,7 @@ bool cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::GetFileInfo( std::string line; static const cmsys::RegularExpression rpathRegex("^ *cmd LC_RPATH$"); static const cmsys::RegularExpression loadDylibRegex( - "^ *cmd LC_LOAD_DYLIB$"); + "^ *cmd LC_LOAD(_WEAK)?_DYLIB$"); static const cmsys::RegularExpression pathRegex( "^ *path (.*) \\(offset [0-9]+\\)$"); static const cmsys::RegularExpression nameRegex( diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h index 8ac7e188eb..9d1745056e 100644 --- a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h +++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h -#define cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h +#pragma once #include <string> #include <vector> @@ -21,5 +20,3 @@ public: bool GetFileInfo(std::string const& file, std::vector<std::string>& libs, std::vector<std::string>& rpaths) override; }; - -#endif // cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h index eae22eae8e..8609479e20 100644 --- a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h +++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h -#define cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h +#pragma once #include <string> #include <vector> @@ -21,5 +20,3 @@ public: bool GetFileInfo(const std::string& file, std::vector<std::string>& needed) override; }; - -#endif // cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h index e9e402b0f1..da71aaabd8 100644 --- a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h +++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h -#define cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h +#pragma once #include <string> #include <vector> @@ -24,5 +23,3 @@ protected: void SetError(const std::string& error); }; - -#endif // cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h diff --git a/Source/cmBinUtilsWindowsPELinker.h b/Source/cmBinUtilsWindowsPELinker.h index a8bb596295..6bb787538a 100644 --- a/Source/cmBinUtilsWindowsPELinker.h +++ b/Source/cmBinUtilsWindowsPELinker.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsWindowsPELinker_h -#define cmBinUtilsWindowsPELinker_h +#pragma once #include <memory> #include <string> @@ -29,5 +28,3 @@ private: bool ResolveDependency(std::string const& name, std::string const& origin, std::string& path, bool& resolved); }; - -#endif // cmBinUtilsWindowsPELinker_h diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h index a67cb0cf1f..fe89a2d175 100644 --- a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h +++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h -#define cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h +#pragma once #include <string> #include <vector> @@ -21,5 +20,3 @@ public: bool GetFileInfo(const std::string& file, std::vector<std::string>& needed) override; }; - -#endif // cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h diff --git a/Source/cmBreakCommand.h b/Source/cmBreakCommand.h index e6ce6fe74c..624186718a 100644 --- a/Source/cmBreakCommand.h +++ b/Source/cmBreakCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBreakCommand_h -#define cmBreakCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ class cmExecutionStatus; */ bool cmBreakCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmBuildCommand.cxx b/Source/cmBuildCommand.cxx index b82fb9aac0..2eaf31561f 100644 --- a/Source/cmBuildCommand.cxx +++ b/Source/cmBuildCommand.cxx @@ -6,6 +6,7 @@ #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -94,7 +95,7 @@ bool TwoArgsSignature(std::vector<std::string> const& args, cmMakefile& mf = status.GetMakefile(); std::string const& define = args[0]; - const char* cacheValue = mf.GetDefinition(define); + cmProp cacheValue = mf.GetDefinition(define); std::string configType; if (!cmSystemTools::GetEnv("CMAKE_CONFIG_TYPE", configType) || diff --git a/Source/cmBuildCommand.h b/Source/cmBuildCommand.h index 45aa71da2b..eafe185895 100644 --- a/Source/cmBuildCommand.h +++ b/Source/cmBuildCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBuildCommand_h -#define cmBuildCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmBuildCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmBuildNameCommand.cxx b/Source/cmBuildNameCommand.cxx index ad4d66534a..f9b8f8f5d4 100644 --- a/Source/cmBuildNameCommand.cxx +++ b/Source/cmBuildNameCommand.cxx @@ -8,6 +8,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStateTypes.h" #include "cmSystemTools.h" @@ -19,12 +20,12 @@ bool cmBuildNameCommand(std::vector<std::string> const& args, return false; } cmMakefile& mf = status.GetMakefile(); - const char* cacheValue = mf.GetDefinition(args[0]); + cmProp cacheValue = mf.GetDefinition(args[0]); if (cacheValue) { // do we need to correct the value? cmsys::RegularExpression reg("[()/]"); - if (reg.find(cacheValue)) { - std::string cv = cacheValue; + std::string cv = *cacheValue; + if (reg.find(cv)) { std::replace(cv.begin(), cv.end(), '/', '_'); std::replace(cv.begin(), cv.end(), '(', '_'); std::replace(cv.begin(), cv.end(), ')', '_'); diff --git a/Source/cmBuildNameCommand.h b/Source/cmBuildNameCommand.h index 37a72689cf..650dc74ff8 100644 --- a/Source/cmBuildNameCommand.h +++ b/Source/cmBuildNameCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmBuildNameCommand_h -#define cmBuildNameCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmBuildNameCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmCLocaleEnvironmentScope.h b/Source/cmCLocaleEnvironmentScope.h index aa2827e744..0919acc68e 100644 --- a/Source/cmCLocaleEnvironmentScope.h +++ b/Source/cmCLocaleEnvironmentScope.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCLocaleEnvironmentScope_h -#define cmCLocaleEnvironmentScope_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -25,5 +24,3 @@ private: using backup_map_t = std::map<std::string, std::string>; backup_map_t EnvironmentBackup; }; - -#endif diff --git a/Source/cmCMakeHostSystemInformationCommand.h b/Source/cmCMakeHostSystemInformationCommand.h index 79e3f2702f..8a64f6a013 100644 --- a/Source/cmCMakeHostSystemInformationCommand.h +++ b/Source/cmCMakeHostSystemInformationCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCMakeHostSystemInformationCommand_h -#define cmCMakeHostSystemInformationCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -18,5 +17,3 @@ class cmExecutionStatus; */ bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx index eb9269f7fc..789c78d4bc 100644 --- a/Source/cmCMakeLanguageCommand.cxx +++ b/Source/cmCMakeLanguageCommand.cxx @@ -7,11 +7,14 @@ #include <cstddef> #include <memory> #include <string> +#include <utility> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmExecutionStatus.h" +#include "cmGlobalGenerator.h" #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmRange.h" @@ -19,6 +22,14 @@ #include "cmSystemTools.h" namespace { + +bool FatalError(cmExecutionStatus& status, std::string const& error) +{ + status.SetError(error); + cmSystemTools::SetFatalErrorOccured(); + return false; +} + std::array<cm::static_string_view, 12> InvalidCommands{ { // clang-format off "function"_s, "endfunction"_s, @@ -28,110 +39,323 @@ std::array<cm::static_string_view, 12> InvalidCommands{ "foreach"_s, "endforeach"_s } // clang-format on }; -} -bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, - cmExecutionStatus& status) +std::array<cm::static_string_view, 1> InvalidDeferCommands{ + { + // clang-format off + "return"_s, + } // clang-format on +}; + +struct Defer +{ + std::string Id; + std::string IdVar; + cmMakefile* Directory = nullptr; +}; + +bool cmCMakeLanguageCommandCALL(std::vector<cmListFileArgument> const& args, + std::string const& callCommand, + size_t startArg, cm::optional<Defer> defer, + cmExecutionStatus& status) { - if (args.empty()) { - status.SetError("called with incorrect number of arguments"); - return false; + // ensure specified command is valid + // start/end flow control commands are not allowed + auto cmd = cmSystemTools::LowerCase(callCommand); + if (std::find(InvalidCommands.cbegin(), InvalidCommands.cend(), cmd) != + InvalidCommands.cend()) { + return FatalError(status, + cmStrCat("invalid command specified: "_s, callCommand)); + } + if (defer && + std::find(InvalidDeferCommands.cbegin(), InvalidDeferCommands.cend(), + cmd) != InvalidDeferCommands.cend()) { + return FatalError(status, + cmStrCat("invalid command specified: "_s, callCommand)); } cmMakefile& makefile = status.GetMakefile(); - cmListFileContext context = makefile.GetExecutionContext(); + cmListFileContext context = makefile.GetBacktrace().Top(); - bool result = false; + std::vector<cmListFileArgument> funcArgs; + funcArgs.reserve(args.size() - startArg); - std::vector<std::string> dispatchExpandedArgs; - std::vector<cmListFileArgument> dispatchArgs; - dispatchArgs.emplace_back(args[0]); - makefile.ExpandArguments(dispatchArgs, dispatchExpandedArgs); + // The rest of the arguments are passed to the function call above + for (size_t i = startArg; i < args.size(); ++i) { + funcArgs.emplace_back(args[i].Value, args[i].Delim, context.Line); + } + cmListFileFunction func{ callCommand, context.Line, std::move(funcArgs) }; - if (dispatchExpandedArgs.empty()) { - status.SetError("called with incorrect number of arguments"); - return false; + if (defer) { + if (defer->Id.empty()) { + defer->Id = makefile.NewDeferId(); + } + if (!defer->IdVar.empty()) { + makefile.AddDefinition(defer->IdVar, defer->Id); + } + cmMakefile* deferMakefile = + defer->Directory ? defer->Directory : &makefile; + if (!deferMakefile->DeferCall(defer->Id, context.FilePath, func)) { + return FatalError( + status, + cmStrCat("DEFER CALL may not be scheduled in directory:\n "_s, + deferMakefile->GetCurrentBinaryDirectory(), + "\nat this time."_s)); + } + return true; } + return makefile.ExecuteCommand(func, status); +} - if (dispatchExpandedArgs[0] == "CALL") { - if ((args.size() == 1 && dispatchExpandedArgs.size() != 2) || - dispatchExpandedArgs.size() > 2) { - status.SetError("called with incorrect number of arguments"); - return false; +bool cmCMakeLanguageCommandDEFER(Defer const& defer, + std::vector<std::string> const& args, + size_t arg, cmExecutionStatus& status) +{ + cmMakefile* deferMakefile = + defer.Directory ? defer.Directory : &status.GetMakefile(); + if (args[arg] == "CANCEL_CALL"_s) { + ++arg; // Consume CANCEL_CALL. + auto ids = cmMakeRange(args).advance(arg); + for (std::string const& id : ids) { + if (id[0] >= 'A' && id[0] <= 'Z') { + return FatalError( + status, cmStrCat("DEFER CANCEL_CALL unknown argument:\n "_s, id)); + } + if (!deferMakefile->DeferCancelCall(id)) { + return FatalError( + status, + cmStrCat("DEFER CANCEL_CALL may not update directory:\n "_s, + deferMakefile->GetCurrentBinaryDirectory(), + "\nat this time."_s)); + } + } + return true; + } + if (args[arg] == "GET_CALL_IDS"_s) { + ++arg; // Consume GET_CALL_IDS. + if (arg == args.size()) { + return FatalError(status, "DEFER GET_CALL_IDS missing output variable"); + } + std::string const& var = args[arg++]; + if (arg != args.size()) { + return FatalError(status, "DEFER GET_CALL_IDS given too many arguments"); + } + cm::optional<std::string> ids = deferMakefile->DeferGetCallIds(); + if (!ids) { + return FatalError( + status, + cmStrCat("DEFER GET_CALL_IDS may not access directory:\n "_s, + deferMakefile->GetCurrentBinaryDirectory(), + "\nat this time."_s)); + } + status.GetMakefile().AddDefinition(var, *ids); + return true; + } + if (args[arg] == "GET_CALL"_s) { + ++arg; // Consume GET_CALL. + if (arg == args.size()) { + return FatalError(status, "DEFER GET_CALL missing id"); + } + std::string const& id = args[arg++]; + if (arg == args.size()) { + return FatalError(status, "DEFER GET_CALL missing output variable"); + } + std::string const& var = args[arg++]; + if (arg != args.size()) { + return FatalError(status, "DEFER GET_CALL given too many arguments"); + } + if (id.empty()) { + return FatalError(status, "DEFER GET_CALL id may not be empty"); + } + if (id[0] >= 'A' && id[0] <= 'Z') { + return FatalError(status, + cmStrCat("DEFER GET_CALL unknown argument:\n "_s, id)); + } + cm::optional<std::string> call = deferMakefile->DeferGetCall(id); + if (!call) { + return FatalError( + status, + cmStrCat("DEFER GET_CALL may not access directory:\n "_s, + deferMakefile->GetCurrentBinaryDirectory(), + "\nat this time."_s)); } + status.GetMakefile().AddDefinition(var, *call); + return true; + } + return FatalError(status, + cmStrCat("DEFER operation unknown: "_s, args[arg])); +} - // First argument is the name of the function to call - std::string callCommand; - size_t startArg; - if (dispatchExpandedArgs.size() == 1) { - std::vector<std::string> functionExpandedArg; - std::vector<cmListFileArgument> functionArg; - functionArg.emplace_back(args[1]); - makefile.ExpandArguments(functionArg, functionExpandedArg); +bool cmCMakeLanguageCommandEVAL(std::vector<cmListFileArgument> const& args, + cmExecutionStatus& status) +{ + cmMakefile& makefile = status.GetMakefile(); + cmListFileContext context = makefile.GetBacktrace().Top(); + std::vector<std::string> expandedArgs; + makefile.ExpandArguments(args, expandedArgs); - if (functionExpandedArg.size() != 1) { - status.SetError("called with incorrect number of arguments"); - return false; - } + if (expandedArgs.size() < 2) { + return FatalError(status, "called with incorrect number of arguments"); + } - callCommand = functionExpandedArg[0]; - startArg = 2; - } else { - callCommand = dispatchExpandedArgs[1]; - startArg = 1; + if (expandedArgs[1] != "CODE") { + auto code_iter = + std::find(expandedArgs.begin() + 2, expandedArgs.end(), "CODE"); + if (code_iter == expandedArgs.end()) { + return FatalError(status, "called without CODE argument"); } + return FatalError( + status, + "called with unsupported arguments between EVAL and CODE arguments"); + } - // ensure specified command is valid - // start/end flow control commands are not allowed - auto cmd = cmSystemTools::LowerCase(callCommand); - if (std::find(InvalidCommands.cbegin(), InvalidCommands.cend(), cmd) != - InvalidCommands.cend()) { - status.SetError(cmStrCat("invalid command specified: "_s, callCommand)); - return false; - } + const std::string code = + cmJoin(cmMakeRange(expandedArgs.begin() + 2, expandedArgs.end()), " "); + return makefile.ReadListFileAsString( + code, cmStrCat(context.FilePath, ":", context.Line, ":EVAL")); +} +} - cmListFileFunction func; - func.Name = callCommand; - func.Line = context.Line; +bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, + cmExecutionStatus& status) +{ + std::vector<std::string> expArgs; + size_t rawArg = 0; + size_t expArg = 0; - // The rest of the arguments are passed to the function call above - for (size_t i = startArg; i < args.size(); ++i) { - cmListFileArgument lfarg; - lfarg.Delim = args[i].Delim; - lfarg.Line = context.Line; - lfarg.Value = args[i].Value; - func.Arguments.emplace_back(lfarg); + // Helper to consume and expand one raw argument at a time. + auto moreArgs = [&]() -> bool { + while (expArg >= expArgs.size()) { + if (rawArg >= args.size()) { + return false; + } + std::vector<cmListFileArgument> tmpArg; + tmpArg.emplace_back(args[rawArg++]); + status.GetMakefile().ExpandArguments(tmpArg, expArgs); } + return true; + }; + auto finishArgs = [&]() { + std::vector<cmListFileArgument> tmpArgs(args.begin() + rawArg, args.end()); + status.GetMakefile().ExpandArguments(tmpArgs, expArgs); + rawArg = args.size(); + }; - result = makefile.ExecuteCommand(func, status); - } else if (dispatchExpandedArgs[0] == "EVAL") { - std::vector<std::string> expandedArgs; - makefile.ExpandArguments(args, expandedArgs); + if (!moreArgs()) { + return FatalError(status, "called with incorrect number of arguments"); + } + + cm::optional<Defer> maybeDefer; + if (expArgs[expArg] == "DEFER"_s) { + ++expArg; // Consume "DEFER". - if (expandedArgs.size() < 2) { - status.SetError("called with incorrect number of arguments"); - return false; + if (!moreArgs()) { + return FatalError(status, "DEFER requires at least one argument"); } - if (expandedArgs[1] != "CODE") { - auto code_iter = - std::find(expandedArgs.begin() + 2, expandedArgs.end(), "CODE"); - if (code_iter == expandedArgs.end()) { - status.SetError("called without CODE argument"); + Defer defer; + + // Process optional arguments. + while (moreArgs()) { + if (expArgs[expArg] == "CALL"_s) { + break; + } + if (expArgs[expArg] == "CANCEL_CALL"_s || + expArgs[expArg] == "GET_CALL_IDS"_s || + expArgs[expArg] == "GET_CALL"_s) { + if (!defer.Id.empty() || !defer.IdVar.empty()) { + return FatalError(status, + cmStrCat("DEFER "_s, expArgs[expArg], + " does not accept ID or ID_VAR."_s)); + } + finishArgs(); + return cmCMakeLanguageCommandDEFER(defer, expArgs, expArg, status); + } + if (expArgs[expArg] == "DIRECTORY"_s) { + ++expArg; // Consume "DIRECTORY". + if (defer.Directory) { + return FatalError(status, + "DEFER given multiple DIRECTORY arguments"); + } + if (!moreArgs()) { + return FatalError(status, "DEFER DIRECTORY missing value"); + } + std::string dir = expArgs[expArg++]; + if (dir.empty()) { + return FatalError(status, "DEFER DIRECTORY may not be empty"); + } + dir = cmSystemTools::CollapseFullPath( + dir, status.GetMakefile().GetCurrentSourceDirectory()); + defer.Directory = + status.GetMakefile().GetGlobalGenerator()->FindMakefile(dir); + if (!defer.Directory) { + return FatalError(status, + cmStrCat("DEFER DIRECTORY:\n "_s, dir, + "\nis not known. "_s, + "It may not have been processed yet."_s)); + } + } else if (expArgs[expArg] == "ID"_s) { + ++expArg; // Consume "ID". + if (!defer.Id.empty()) { + return FatalError(status, "DEFER given multiple ID arguments"); + } + if (!moreArgs()) { + return FatalError(status, "DEFER ID missing value"); + } + defer.Id = expArgs[expArg++]; + if (defer.Id.empty()) { + return FatalError(status, "DEFER ID may not be empty"); + } + if (defer.Id[0] >= 'A' && defer.Id[0] <= 'Z') { + return FatalError(status, "DEFER ID may not start in A-Z."); + } + } else if (expArgs[expArg] == "ID_VAR"_s) { + ++expArg; // Consume "ID_VAR". + if (!defer.IdVar.empty()) { + return FatalError(status, "DEFER given multiple ID_VAR arguments"); + } + if (!moreArgs()) { + return FatalError(status, "DEFER ID_VAR missing variable name"); + } + defer.IdVar = expArgs[expArg++]; + if (defer.IdVar.empty()) { + return FatalError(status, "DEFER ID_VAR may not be empty"); + } } else { - status.SetError( - "called with unsupported arguments between EVAL and CODE arguments"); + return FatalError( + status, cmStrCat("DEFER unknown option:\n "_s, expArgs[expArg])); } - return false; } - const std::string code = - cmJoin(cmMakeRange(expandedArgs.begin() + 2, expandedArgs.end()), " "); - result = makefile.ReadListFileAsString( - code, cmStrCat(context.FilePath, ":", context.Line, ":EVAL")); - } else { - status.SetError("called with unknown meta-operation"); + if (!(moreArgs() && expArgs[expArg] == "CALL"_s)) { + return FatalError(status, "DEFER must be followed by a CALL argument"); + } + + maybeDefer = std::move(defer); + } + + if (expArgs[expArg] == "CALL") { + ++expArg; // Consume "CALL". + + // CALL requires a command name. + if (!moreArgs()) { + return FatalError(status, "CALL missing command name"); + } + std::string const& callCommand = expArgs[expArg++]; + + // CALL accepts no further expanded arguments. + if (expArg != expArgs.size()) { + return FatalError(status, "CALL command's arguments must be literal"); + } + + // Run the CALL. + return cmCMakeLanguageCommandCALL(args, callCommand, rawArg, + std::move(maybeDefer), status); + } + + if (expArgs[expArg] == "EVAL") { + return cmCMakeLanguageCommandEVAL(args, status); } - return result; + return FatalError(status, "called with unknown meta-operation"); } diff --git a/Source/cmCMakeLanguageCommand.h b/Source/cmCMakeLanguageCommand.h index 73065156b0..d45003a96d 100644 --- a/Source/cmCMakeLanguageCommand.h +++ b/Source/cmCMakeLanguageCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCMakeLanguageCommand_h -#define cmCMakeLanguageCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -11,10 +10,8 @@ class cmExecutionStatus; struct cmListFileArgument; /** - * \brief Calls a scripted or build-in command + * \brief Calls a scripted or built-in command * */ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmCMakeMinimumRequired.h b/Source/cmCMakeMinimumRequired.h index 53f78f6906..712d6d6234 100644 --- a/Source/cmCMakeMinimumRequired.h +++ b/Source/cmCMakeMinimumRequired.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCMakeMinimumRequired_h -#define cmCMakeMinimumRequired_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ class cmExecutionStatus; */ bool cmCMakeMinimumRequired(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmCMakePath.cxx b/Source/cmCMakePath.cxx new file mode 100644 index 0000000000..b8215dfcfe --- /dev/null +++ b/Source/cmCMakePath.cxx @@ -0,0 +1,146 @@ +/* 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 "cmCMakePath.h" + +#include <string> + +#if defined(_WIN32) +# include <cstdlib> +#endif + +#include <cm/filesystem> +#include <cm/string_view> + +#if defined(_WIN32) +# include "cmStringAlgorithms.h" +#endif + +cmCMakePath& cmCMakePath::ReplaceWideExtension(cm::string_view extension) +{ + auto file = this->Path.filename().string(); + if (!file.empty() && file != "." && file != "..") { + auto pos = file.find('.', file[0] == '.' ? 1 : 0); + if (pos != std::string::npos) { + file.erase(pos); + } + } + if (!extension.empty()) { + if (extension[0] != '.') { + file += '.'; + } + file.append(std::string(extension)); + } + this->Path.replace_filename(file); + return *this; +} + +cmCMakePath cmCMakePath::GetWideExtension() const +{ + auto file = this->Path.filename().string(); + if (file.empty() || file == "." || file == "..") { + return cmCMakePath{}; + } + + auto pos = file.find('.', file[0] == '.' ? 1 : 0); + if (pos != std::string::npos) { + return cm::string_view(file.data() + pos, file.length() - pos); + } + + return cmCMakePath{}; +} + +cmCMakePath cmCMakePath::GetNarrowStem() const +{ + auto stem = this->Path.stem().string(); + if (!stem.empty()) { + auto pos = stem.find('.', stem[0] == '.' ? 1 : 0); + if (pos != std::string::npos) { + return stem.substr(0, pos); + } + } + return stem; +} + +cmCMakePath cmCMakePath::Absolute(const cm::filesystem::path& base) const +{ + if (this->Path.is_relative()) { + auto path = base; + path /= this->Path; + // filesystem::path::operator/= use preferred_separator ('\' on Windows) + // so converts back to '/' + return path.generic_string(); + } + return *this; +} + +bool cmCMakePath::IsPrefix(const cmCMakePath& path) const +{ + auto prefix_it = this->Path.begin(); + auto prefix_end = this->Path.end(); + auto path_it = path.Path.begin(); + auto path_end = path.Path.end(); + + while (prefix_it != prefix_end && path_it != path_end && + *prefix_it == *path_it) { + ++prefix_it; + ++path_it; + } + return prefix_it == prefix_end; +} + +std::string cmCMakePath::FormatPath(std::string path, format fmt) +{ +#if defined(_WIN32) + if (fmt == auto_format || fmt == native_format) { + auto prefix = path.substr(0, 4); + for (auto& c : prefix) { + if (c == '\\') { + c = '/'; + } + } + // remove Windows long filename marker + if (prefix == "//?/"_s) { + path.erase(0, 4); + } + if (cmHasPrefix(path, "UNC/"_s) || cmHasPrefix(path, "UNC\\"_s)) { + path.erase(0, 2); + path[0] = '/'; + } + } +#else + static_cast<void>(fmt); +#endif + return path; +} + +void cmCMakePath::GetNativePath(std::string& path) const +{ + cm::filesystem::path tmp(this->Path); + tmp.make_preferred(); + + path = tmp.string(); +} +void cmCMakePath::GetNativePath(std::wstring& path) const +{ + cm::filesystem::path tmp(this->Path); + tmp.make_preferred(); + + path = tmp.wstring(); + +#if defined(_WIN32) + // Windows long filename + static std::wstring UNC(L"\\\\?\\UNC"); + static std::wstring PREFIX(L"\\\\?\\"); + + if (this->IsAbsolute() && path.length() > _MAX_PATH - 12) { + if (this->HasRootName() && path[0] == L'\\') { + path = UNC + path.substr(1); + } else { + path = PREFIX + path; + } + } +#endif +} diff --git a/Source/cmCMakePath.h b/Source/cmCMakePath.h new file mode 100644 index 0000000000..15aa30cddf --- /dev/null +++ b/Source/cmCMakePath.h @@ -0,0 +1,571 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <cstddef> +#include <string> +#include <utility> + +#include <cm/filesystem> +#include <cm/string_view> +#include <cm/type_traits> +#include <cmext/string_view> + +namespace detail { +#if defined(__SUNPRO_CC) && defined(__sparc) +// Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile +// the full 'is_pathable' and 'is_move_pathable' checks. We use it only to +// improve error messages via 'enable_if' when calling methods with incorrect +// types. Just pretend all types are allowed so we can at least compile valid +// code. +template <typename T> +struct is_pathable : std::true_type +{ +}; + +template <typename T> +struct is_move_pathable : std::true_type +{ +}; + +#else +template <typename T, typename = void> +struct is_pathable : std::false_type +{ +}; + +template <> +struct is_pathable<cm::filesystem::path> : std::true_type +{ +}; +template <> +struct is_pathable<std::string> : std::true_type +{ +}; +template <> +struct is_pathable<cm::string_view> : std::true_type +{ +}; +template <> +struct is_pathable<cm::static_string_view> : std::true_type +{ +}; +template <typename T> +struct is_pathable< + T, + cm::enable_if_t<std::is_same<char*, typename std::decay<T>::type>::value, + void>> + : cm::bool_constant<std::is_same<char*, typename std::decay<T>::type>::value> +{ +}; + +template <typename T> +struct is_move_pathable : std::false_type +{ +}; + +template <> +struct is_move_pathable<cm::filesystem::path> : std::true_type +{ +}; +template <> +struct is_move_pathable<std::string> : std::true_type +{ +}; +#endif +} + +class cmCMakePath +{ +private: + template <typename Source> + using enable_if_move_pathable = + cm::enable_if_t<detail::is_move_pathable<Source>::value, cmCMakePath&>; + + template <typename Source> + using enable_if_pathable = + cm::enable_if_t<detail::is_pathable<Source>::value, cmCMakePath&>; + +public: + using value_type = cm::filesystem::path::value_type; + using string_type = cm::filesystem::path::string_type; + + enum format : unsigned char + { + auto_format = + static_cast<unsigned char>(cm::filesystem::path::format::auto_format), + native_format = + static_cast<unsigned char>(cm::filesystem::path::format::native_format), + generic_format = + static_cast<unsigned char>(cm::filesystem::path::format::generic_format) + }; + + class iterator; + using const_iterator = iterator; + + cmCMakePath() noexcept = default; + + cmCMakePath(const cmCMakePath&) = default; + + cmCMakePath(cmCMakePath&& path) noexcept + : Path(std::forward<cm::filesystem::path>(path.Path)) + { + } + + cmCMakePath(cm::filesystem::path path) noexcept + : Path(std::move(path)) + { + } + cmCMakePath(cm::string_view source, format fmt = generic_format) noexcept + : Path(FormatPath(source, fmt)) + { + } + template <typename Source, typename = enable_if_move_pathable<Source>> + cmCMakePath(Source source, format fmt = generic_format) + : Path(FormatPath(std::move(source), fmt)) + { + } + + template <typename Source, typename = enable_if_move_pathable<Source>> + cmCMakePath& Assign(Source&& source) + { + this->Path = std::forward<Source>(source); + return *this; + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath& Assign(const Source& source) + { + this->Path = source; + return *this; + } + + cmCMakePath& operator=(const cmCMakePath& path) + { + if (this != &path) { + this->Path = path.Path; + } + return *this; + } + cmCMakePath& operator=(cmCMakePath&& path) noexcept + { + if (this != &path) { + this->Path = std::move(path.Path); + } + return *this; + } + template <typename Source, typename = enable_if_move_pathable<Source>> + cmCMakePath& operator=(Source&& source) + { + this->Assign(std::forward<Source>(source)); + return *this; + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath& operator=(const Source& source) + { + this->Assign(source); + return *this; + } + + // Concatenation + cmCMakePath& Append(const cmCMakePath& path) + { + return this->Append(path.Path); + } + cmCMakePath& Append(const cm::filesystem::path& path) + { + this->Path /= path; + // filesystem::path::append use preferred_separator ('\' on Windows) + // so convert back to '/' + this->Path = this->Path.generic_string(); + return *this; + } + + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath& Append(const Source& source) + { + return this->Append(cm::filesystem::path(source)); + } + + cmCMakePath& operator/=(const cmCMakePath& path) + { + return this->Append(path); + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath& operator/=(const Source& source) + { + return this->Append(source); + } + + cmCMakePath& Concat(const cmCMakePath& path) + { + this->Path += path.Path; + return *this; + } + cmCMakePath& Concat(cm::static_string_view source) + { + this->Path.concat(std::string(source)); + return *this; + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath& Concat(const Source& source) + { + this->Path.concat(source); + return *this; + } + + cmCMakePath& operator+=(const cmCMakePath& path) + { + return this->Concat(path); + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath& operator+=(const Source& source) + { + return this->Concat(source); + } + + // Manipulation + void Clear() noexcept { this->Path.clear(); } + + cmCMakePath& RemoveFileName() + { + this->Path.remove_filename(); + return *this; + } + + cmCMakePath& ReplaceFileName(const cmCMakePath& filename) + { + if (this->Path.has_filename()) { + this->Path.replace_filename(filename.Path); + } + return *this; + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath& ReplaceFileName(const Source& filename) + { + if (this->Path.has_filename()) { + this->Path.replace_filename(filename); + } + return *this; + } + + cmCMakePath& ReplaceExtension(const cmCMakePath& extension = cmCMakePath()) + { + this->Path.replace_extension(extension.Path); + return *this; + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath& ReplaceExtension(const Source& extension) + { + this->Path.replace_extension(extension); + return *this; + } + + cmCMakePath& ReplaceWideExtension( + const cmCMakePath& extension = cmCMakePath()) + { + return this->ReplaceWideExtension( + static_cast<cm::string_view>(extension.Path.string())); + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath& ReplaceWideExtension(const Source& extension) + { + return this->ReplaceWideExtension(cm::string_view(extension)); + } + cmCMakePath& ReplaceWideExtension(cm::string_view extension); + + cmCMakePath& RemoveExtension() + { + if (this->Path.has_extension()) { + this->ReplaceExtension(cm::string_view("")); + } + return *this; + } + + cmCMakePath& RemoveWideExtension() + { + if (this->Path.has_extension()) { + this->ReplaceWideExtension(cm::string_view("")); + } + return *this; + } + + void swap(cmCMakePath& other) noexcept { this->Path.swap(other.Path); } + + // Observers + std::string String() const { return this->Path.string(); } + std::wstring WString() const { return this->Path.wstring(); } + + string_type Native() const + { + string_type path; + this->GetNativePath(path); + + return path; + } + std::string NativeString() const + { + std::string path; + this->GetNativePath(path); + + return path; + } + std::wstring NativeWString() const + { + std::wstring path; + this->GetNativePath(path); + + return path; + } + std::string GenericString() const { return this->Path.generic_string(); } + std::wstring GenericWString() const { return this->Path.generic_wstring(); } + + // Decomposition + cmCMakePath GetRootName() const { return this->Path.root_name(); } + cmCMakePath GetRootDirectory() const { return this->Path.root_directory(); } + cmCMakePath GetRootPath() const { return this->Path.root_path(); } + cmCMakePath GetFileName() const { return this->Path.filename(); } + cmCMakePath GetExtension() const { return this->Path.extension(); } + cmCMakePath GetWideExtension() const; + cmCMakePath GetStem() const { return this->Path.stem(); } + cmCMakePath GetNarrowStem() const; + + cmCMakePath GetRelativePath() const { return this->Path.relative_path(); } + cmCMakePath GetParentPath() const { return this->Path.parent_path(); } + + // Generation + cmCMakePath Normal() const + { + auto path = this->Path.lexically_normal(); + // filesystem::path:lexically_normal use preferred_separator ('\') on + // Windows) so convert back to '/' + return path.generic_string(); + } + + cmCMakePath Relative(const cmCMakePath& base) const + { + return this->Relative(base.Path); + } + cmCMakePath Relative(const cm::filesystem::path& base) const + { + auto path = this->Path.lexically_relative(base); + // filesystem::path:lexically_relative use preferred_separator ('\') on + // Windows) so convert back to '/' + return path.generic_string(); + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath Relative(const Source& base) const + { + return this->Relative(cm::filesystem::path(base)); + } + + cmCMakePath Proximate(const cmCMakePath& base) const + { + return this->Proximate(base.Path); + } + cmCMakePath Proximate(const cm::filesystem::path& base) const + { + auto path = this->Path.lexically_proximate(base); + // filesystem::path::lexically_proximate use preferred_separator ('\') on + // Windows) so convert back to '/' + return path.generic_string(); + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath Proximate(const Source& base) const + { + return this->Proximate(cm::filesystem::path(base)); + } + + cmCMakePath Absolute(const cmCMakePath& base) const + { + return this->Absolute(base.Path); + } + template <typename Source, typename = enable_if_pathable<Source>> + cmCMakePath Absolute(const Source& base) const + { + return this->Absolute(cm::filesystem::path(base)); + } + cmCMakePath Absolute(const cm::filesystem::path& base) const; + + // Comparison + int Compare(const cmCMakePath& path) const noexcept + { + return this->Path.compare(path.Path); + } + + // Query + bool IsEmpty() const noexcept { return this->Path.empty(); } + + bool HasRootPath() const { return this->Path.has_root_path(); } + bool HasRootName() const { return this->Path.has_root_name(); } + bool HasRootDirectory() const { return this->Path.has_root_directory(); } + bool HasRelativePath() const { return this->Path.has_relative_path(); } + bool HasParentPath() const { return this->Path.has_parent_path(); } + bool HasFileName() const { return this->Path.has_filename(); } + bool HasStem() const { return this->Path.has_stem(); } + bool HasExtension() const { return this->Path.has_extension(); } + + bool IsAbsolute() const { return this->Path.is_absolute(); } + bool IsRelative() const { return this->Path.is_relative(); } + bool IsPrefix(const cmCMakePath& path) const; + + // Iterators + // ========= + inline iterator begin() const; + inline iterator end() const; + + // Non-members + // =========== + friend inline bool operator==(const cmCMakePath& lhs, + const cmCMakePath& rhs) noexcept + { + return lhs.Compare(rhs) == 0; + } + friend inline bool operator!=(const cmCMakePath& lhs, + const cmCMakePath& rhs) noexcept + { + return lhs.Compare(rhs) != 0; + } + + friend inline cmCMakePath operator/(const cmCMakePath& lhs, + const cmCMakePath& rhs) + { + cmCMakePath result(lhs); + result /= rhs; + + return result; + } + +private: + friend std::size_t hash_value(const cmCMakePath& path) noexcept; + + static std::string FormatPath(std::string path, format fmt = generic_format); + static std::string FormatPath(cm::string_view path, + format fmt = generic_format) + { + return FormatPath(std::string(path), fmt); + } + + void GetNativePath(std::string& path) const; + void GetNativePath(std::wstring& path) const; + + cm::filesystem::path Path; +}; + +class cmCMakePath::iterator +{ +public: + using iterator_category = cm::filesystem::path::iterator::iterator_category; + + using value_type = cmCMakePath; + using difference_type = cm::filesystem::path::iterator::difference_type; + using pointer = const cmCMakePath*; + using reference = const cmCMakePath&; + + iterator() = default; + + iterator(const iterator& other) + : Iterator(other.Iterator) + , Path(other.Path) + , PathElement(*this->Iterator) + { + } + + ~iterator() = default; + + iterator& operator=(const iterator& other) + { + if (this != &other) { + this->Iterator = other.Iterator; + this->Path = other.Path; + this->PathElement = *this->Iterator; + } + + return *this; + } + + reference operator*() const { return this->PathElement; } + + pointer operator->() const { return &this->PathElement; } + + iterator& operator++() + { + ++this->Iterator; + this->PathElement = *this->Iterator; + + return *this; + } + + iterator operator++(int) + { + iterator it(*this); + this->operator++(); + return it; + } + + iterator& operator--() + { + --this->Iterator; + this->PathElement = *this->Iterator; + + return *this; + } + + iterator operator--(int) + { + iterator it(*this); + this->operator--(); + return it; + } + +private: + friend class cmCMakePath; + friend bool operator==(const iterator&, const iterator&); + + iterator(const cmCMakePath* path, const cm::filesystem::path::iterator& it) + : Iterator(it) + , Path(path) + , PathElement(*this->Iterator) + { + } + + cm::filesystem::path::iterator Iterator; + const cmCMakePath* Path = nullptr; + cmCMakePath PathElement; +}; + +inline cmCMakePath::iterator cmCMakePath::begin() const +{ + return iterator(this, this->Path.begin()); +} +inline cmCMakePath::iterator cmCMakePath::end() const +{ + return iterator(this, this->Path.end()); +} + +// Non-member functions +// ==================== +inline bool operator==(const cmCMakePath::iterator& lhs, + const cmCMakePath::iterator& rhs) +{ + return lhs.Path == rhs.Path && lhs.Path != nullptr && + lhs.Iterator == rhs.Iterator; +} + +inline bool operator!=(const cmCMakePath::iterator& lhs, + const cmCMakePath::iterator& rhs) +{ + return !(lhs == rhs); +} + +inline void swap(cmCMakePath& lhs, cmCMakePath& rhs) noexcept +{ + lhs.swap(rhs); +} + +inline std::size_t hash_value(const cmCMakePath& path) noexcept +{ + return cm::filesystem::hash_value(path.Path); +} diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx new file mode 100644 index 0000000000..720f582f9d --- /dev/null +++ b/Source/cmCMakePathCommand.cxx @@ -0,0 +1,1019 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCMakePathCommand.h" + +#include <algorithm> +#include <functional> +#include <iomanip> +#include <map> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmArgumentParser.h" +#include "cmCMakePath.h" +#include "cmExecutionStatus.h" +#include "cmMakefile.h" +#include "cmRange.h" +#include "cmStringAlgorithms.h" +#include "cmSubcommandTable.h" +#include "cmSystemTools.h" + +namespace { +// Helper classes for argument parsing +template <typename Result> +class CMakePathArgumentParser : public cmArgumentParser<Result> +{ +public: + CMakePathArgumentParser() + : cmArgumentParser<Result>() + { + } + + template <typename T> + CMakePathArgumentParser& Bind(cm::static_string_view name, T Result::*member) + { + cmArgumentParser<Result>::Bind(name, member); + return *this; + } + + template <int Advance = 2> + Result Parse(std::vector<std::string> const& args, + std::vector<std::string>* keywordsMissingValue = nullptr, + std::vector<std::string>* parsedKeywords = nullptr) const + { + this->Inputs.clear(); + + return cmArgumentParser<Result>::Parse(cmMakeRange(args).advance(Advance), + &this->Inputs, keywordsMissingValue, + parsedKeywords); + } + + const std::vector<std::string>& GetInputs() const { return Inputs; } + +protected: + mutable std::vector<std::string> Inputs; +}; + +// OUTPUT_VARIABLE is expected +template <typename Result> +class ArgumentParserWithOutputVariable : public CMakePathArgumentParser<Result> +{ +public: + ArgumentParserWithOutputVariable() + : CMakePathArgumentParser<Result>() + { + this->Bind("OUTPUT_VARIABLE"_s, &Result::Output); + } + + template <typename T> + ArgumentParserWithOutputVariable& Bind(cm::static_string_view name, + T Result::*member) + { + cmArgumentParser<Result>::Bind(name, member); + return *this; + } + + template <int Advance = 2> + Result Parse(std::vector<std::string> const& args) const + { + this->KeywordsMissingValue.clear(); + this->ParsedKeywords.clear(); + + return CMakePathArgumentParser<Result>::template Parse<Advance>( + args, &this->KeywordsMissingValue, &this->ParsedKeywords); + } + + const std::vector<std::string>& GetKeywordsMissingValue() const + { + return this->KeywordsMissingValue; + } + const std::vector<std::string>& GetParsedKeywords() const + { + return this->ParsedKeywords; + } + + bool checkOutputVariable(const Result& arguments, + cmExecutionStatus& status) const + { + if (std::find(this->GetKeywordsMissingValue().begin(), + this->GetKeywordsMissingValue().end(), + "OUTPUT_VARIABLE"_s) != + this->GetKeywordsMissingValue().end()) { + status.SetError("OUTPUT_VARIABLE requires an argument."); + return false; + } + + if (std::find(this->GetParsedKeywords().begin(), + this->GetParsedKeywords().end(), + "OUTPUT_VARIABLE"_s) != this->GetParsedKeywords().end() && + arguments.Output.empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + return true; + } + +private: + mutable std::vector<std::string> KeywordsMissingValue; + mutable std::vector<std::string> ParsedKeywords; +}; + +struct OutputVariable +{ + std::string Output; +}; +// Usable when OUTPUT_VARIABLE is the only option +class OutputVariableParser + : public ArgumentParserWithOutputVariable<OutputVariable> +{ +}; + +struct NormalizeOption +{ + bool Normalize = false; +}; +// Usable when NORMALIZE is the only option +class NormalizeParser : public CMakePathArgumentParser<NormalizeOption> +{ +public: + NormalizeParser() { this->Bind("NORMALIZE"_s, &NormalizeOption::Normalize); } +}; + +// retrieve value of input path from specified variable +bool getInputPath(const std::string& arg, cmExecutionStatus& status, + std::string& path) +{ + auto def = status.GetMakefile().GetDefinition(arg); + if (def == nullptr) { + status.SetError("undefined variable for input path."); + return false; + } + + path = *def; + return true; +} + +bool HandleGetCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + static std::map<cm::string_view, + std::function<cmCMakePath(const cmCMakePath&, bool)>> const + actions{ { "ROOT_NAME"_s, + [](const cmCMakePath& path, bool) -> cmCMakePath { + return path.GetRootName(); + } }, + { "ROOT_DIRECTORY"_s, + [](const cmCMakePath& path, bool) -> cmCMakePath { + return path.GetRootDirectory(); + } }, + { "ROOT_PATH"_s, + [](const cmCMakePath& path, bool) -> cmCMakePath { + return path.GetRootPath(); + } }, + { "FILENAME"_s, + [](const cmCMakePath& path, bool) -> cmCMakePath { + return path.GetFileName(); + } }, + { "EXTENSION"_s, + [](const cmCMakePath& path, bool last_only) -> cmCMakePath { + if (last_only) { + return path.GetExtension(); + } + return path.GetWideExtension(); + } }, + { "STEM"_s, + [](const cmCMakePath& path, bool last_only) -> cmCMakePath { + if (last_only) { + return path.GetStem(); + } + return path.GetNarrowStem(); + } }, + { "RELATIVE_PATH"_s, + [](const cmCMakePath& path, bool) -> cmCMakePath { + return path.GetRelativePath(); + } }, + { "PARENT_PATH"_s, + [](const cmCMakePath& path, bool) -> cmCMakePath { + return path.GetParentPath(); + } } }; + + if (args.size() < 4) { + status.SetError("GET must be called with at least three arguments."); + return false; + } + + const auto& action = args[2]; + + if (actions.find(action) == actions.end()) { + status.SetError( + cmStrCat("GET called with an unknown action: ", action, ".")); + return false; + } + + struct Arguments + { + bool LastOnly = false; + }; + + CMakePathArgumentParser<Arguments> parser; + if ((action == "EXTENSION"_s || action == "STEM"_s)) { + parser.Bind("LAST_ONLY"_s, &Arguments::LastOnly); + } + + Arguments const arguments = parser.Parse<3>(args); + + if (parser.GetInputs().size() != 1) { + status.SetError("GET called with unexpected arguments."); + return false; + } + if (parser.GetInputs().front().empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + std::string path; + if (!getInputPath(args[1], status, path)) { + return false; + } + + auto result = actions.at(action)(path, arguments.LastOnly); + + status.GetMakefile().AddDefinition(parser.GetInputs().front(), + result.String()); + + return true; +} + +bool HandleAppendCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + static OutputVariableParser const parser{}; + + const auto arguments = parser.Parse(args); + + if (!parser.checkOutputVariable(arguments, status)) { + return false; + } + + cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1])); + for (const auto& input : parser.GetInputs()) { + path /= input; + } + + status.GetMakefile().AddDefinition( + arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + + return true; +} + +bool HandleConcatCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + static OutputVariableParser const parser{}; + + const auto arguments = parser.Parse(args); + + if (!parser.checkOutputVariable(arguments, status)) { + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + cmCMakePath path(inputPath); + for (const auto& input : parser.GetInputs()) { + path += input; + } + + status.GetMakefile().AddDefinition( + arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + + return true; +} + +bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + static OutputVariableParser const parser{}; + + const auto arguments = parser.Parse(args); + + if (!parser.checkOutputVariable(arguments, status)) { + return false; + } + + if (!parser.GetInputs().empty()) { + status.SetError("REMOVE_FILENAME called with unexpected arguments."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + cmCMakePath path(inputPath); + path.RemoveFileName(); + + status.GetMakefile().AddDefinition( + arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + + return true; +} + +bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + static OutputVariableParser const parser{}; + + const auto arguments = parser.Parse(args); + + if (!parser.checkOutputVariable(arguments, status)) { + return false; + } + + if (parser.GetInputs().size() > 1) { + status.SetError("REPLACE_FILENAME called with unexpected arguments."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + cmCMakePath path(inputPath); + path.ReplaceFileName( + parser.GetInputs().empty() ? "" : parser.GetInputs().front()); + + status.GetMakefile().AddDefinition( + arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + + return true; +} + +bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + struct Arguments + { + std::string Output; + bool LastOnly = false; + }; + + static auto const parser = + ArgumentParserWithOutputVariable<Arguments>{}.Bind("LAST_ONLY"_s, + &Arguments::LastOnly); + + Arguments const arguments = parser.Parse(args); + + if (!parser.checkOutputVariable(arguments, status)) { + return false; + } + + if (!parser.GetInputs().empty()) { + status.SetError("REMOVE_EXTENSION called with unexpected arguments."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + cmCMakePath path(inputPath); + + if (arguments.LastOnly) { + path.RemoveExtension(); + } else { + path.RemoveWideExtension(); + } + + status.GetMakefile().AddDefinition( + arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + + return true; +} + +bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + struct Arguments + { + std::string Output; + bool LastOnly = false; + }; + + static auto const parser = + ArgumentParserWithOutputVariable<Arguments>{}.Bind("LAST_ONLY"_s, + &Arguments::LastOnly); + + Arguments const arguments = parser.Parse(args); + + if (!parser.checkOutputVariable(arguments, status)) { + return false; + } + + if (parser.GetInputs().size() > 1) { + status.SetError("REPLACE_EXTENSION called with unexpected arguments."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + cmCMakePath path(inputPath); + cmCMakePath extension( + parser.GetInputs().empty() ? "" : parser.GetInputs().front()); + + if (arguments.LastOnly) { + path.ReplaceExtension(extension); + } else { + path.ReplaceWideExtension(extension); + } + + status.GetMakefile().AddDefinition( + arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + + return true; +} + +bool HandleNormalPathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + static OutputVariableParser const parser{}; + + const auto arguments = parser.Parse(args); + + if (!parser.checkOutputVariable(arguments, status)) { + return false; + } + + if (!parser.GetInputs().empty()) { + status.SetError("NORMAL_PATH called with unexpected arguments."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + auto path = cmCMakePath(inputPath).Normal(); + + status.GetMakefile().AddDefinition( + arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + + return true; +} + +bool HandleTransformPathCommand( + std::vector<std::string> const& args, cmExecutionStatus& status, + const std::function<cmCMakePath(const cmCMakePath&, + const std::string& base)>& transform, + bool normalizeOption = false) +{ + struct Arguments + { + std::string Output; + std::string BaseDirectory; + bool Normalize = false; + }; + + auto parser = ArgumentParserWithOutputVariable<Arguments>{}.Bind( + "BASE_DIRECTORY"_s, &Arguments::BaseDirectory); + if (normalizeOption) { + parser.Bind("NORMALIZE"_s, &Arguments::Normalize); + } + + Arguments arguments = parser.Parse(args); + + if (!parser.checkOutputVariable(arguments, status)) { + return false; + } + + if (!parser.GetInputs().empty()) { + status.SetError(cmStrCat(args[0], " called with unexpected arguments.")); + return false; + } + + if (std::find(parser.GetKeywordsMissingValue().begin(), + parser.GetKeywordsMissingValue().end(), "BASE_DIRECTORY"_s) != + parser.GetKeywordsMissingValue().end()) { + status.SetError("BASE_DIRECTORY requires an argument."); + return false; + } + + if (std::find(parser.GetParsedKeywords().begin(), + parser.GetParsedKeywords().end(), + "BASE_DIRECTORY"_s) == parser.GetParsedKeywords().end()) { + arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory); + if (arguments.Normalize) { + path = path.Normal(); + } + + status.GetMakefile().AddDefinition( + arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + + return true; +} + +bool HandleRelativePathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleTransformPathCommand( + args, status, + [](const cmCMakePath& path, const std::string& base) -> cmCMakePath { + return path.Relative(base); + }); +} + +bool HandleProximatePathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleTransformPathCommand( + args, status, + [](const cmCMakePath& path, const std::string& base) -> cmCMakePath { + return path.Proximate(base); + }); +} + +bool HandleAbsolutePathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleTransformPathCommand( + args, status, + [](const cmCMakePath& path, const std::string& base) -> cmCMakePath { + return path.Absolute(base); + }, + true); +} + +bool HandleCMakePathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 3 || args.size() > 4) { + status.SetError("CMAKE_PATH must be called with two or three arguments."); + return false; + } + + static NormalizeParser const parser; + + const auto arguments = parser.Parse(args); + + if (parser.GetInputs().size() != 1) { + status.SetError("CMAKE_PATH called with unexpected arguments."); + return false; + } + + if (args[1].empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + auto path = + cmCMakePath(parser.GetInputs().front(), cmCMakePath::native_format); + + if (arguments.Normalize) { + path = path.Normal(); + } + + status.GetMakefile().AddDefinition(args[1], path.GenericString()); + + return true; +} + +bool HandleNativePathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 3 || args.size() > 4) { + status.SetError("NATIVE_PATH must be called with two or three arguments."); + return false; + } + + static NormalizeParser const parser; + + const auto arguments = parser.Parse(args); + + if (parser.GetInputs().size() != 1) { + status.SetError("NATIVE_PATH called with unexpected arguments."); + return false; + } + if (parser.GetInputs().front().empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + cmCMakePath path(inputPath); + if (arguments.Normalize) { + path = path.Normal(); + } + + status.GetMakefile().AddDefinition(parser.GetInputs().front(), + path.NativeString()); + + return true; +} + +bool HandleConvertCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + const auto pathSep = ";"_s; +#else + const auto pathSep = ":"_s; +#endif + const auto cmakePath = "TO_CMAKE_PATH_LIST"_s; + const auto nativePath = "TO_NATIVE_PATH_LIST"_s; + + if (args.size() < 4 || args.size() > 5) { + status.SetError("CONVERT must be called with three or four arguments."); + return false; + } + + const auto& action = args[2]; + + if (action != cmakePath && action != nativePath) { + status.SetError( + cmStrCat("CONVERT called with an unknown action: ", action, ".")); + return false; + } + + if (args[3].empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + static NormalizeParser const parser; + + const auto arguments = parser.Parse<4>(args); + + if (!parser.GetInputs().empty()) { + status.SetError("CONVERT called with unexpected arguments."); + return false; + } + + std::vector<std::string> paths; + + if (action == cmakePath) { + paths = cmSystemTools::SplitString(args[1], pathSep.front()); + } else { + cmExpandList(args[1], paths); + } + + for (auto& path : paths) { + auto p = cmCMakePath(path, + action == cmakePath ? cmCMakePath::native_format + : cmCMakePath::generic_format); + if (arguments.Normalize) { + p = p.Normal(); + } + if (action == cmakePath) { + path = p.GenericString(); + } else { + path = p.NativeString(); + } + } + + auto value = cmJoin(paths, action == cmakePath ? ";"_s : pathSep); + status.GetMakefile().AddDefinition(args[3], value); + + return true; +} + +bool HandleCompareCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() != 5) { + status.SetError("COMPARE must be called with four arguments."); + return false; + } + + static std::map<cm::string_view, + std::function<bool(const cmCMakePath&, + const cmCMakePath&)>> const operators{ + { "EQUAL"_s, + [](const cmCMakePath& path1, const cmCMakePath& path2) -> bool { + return path1 == path2; + } }, + { "NOT_EQUAL"_s, + [](const cmCMakePath& path1, const cmCMakePath& path2) -> bool { + return path1 != path2; + } } + }; + + const auto op = operators.find(args[2]); + if (op == operators.end()) { + status.SetError(cmStrCat( + "COMPARE called with an unknown comparison operator: ", args[2], ".")); + return false; + } + + if (args[4].empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + cmCMakePath path1(inputPath); + cmCMakePath path2(args[3]); + auto result = op->second(path1, path2); + + status.GetMakefile().AddDefinitionBool(args[4], result); + + return true; +} + +bool HandleHasItemCommand( + std::vector<std::string> const& args, cmExecutionStatus& status, + const std::function<bool(const cmCMakePath&)>& has_item) +{ + if (args.size() != 3) { + status.SetError( + cmStrCat(args.front(), " must be called with two arguments.")); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + if (args[2].empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + cmCMakePath path(inputPath); + auto result = has_item(path); + + status.GetMakefile().AddDefinitionBool(args[2], result); + + return true; +} + +bool HandleHasRootNameCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleHasItemCommand( + args, status, + [](const cmCMakePath& path) -> bool { return path.HasRootName(); }); +} + +bool HandleHasRootDirectoryCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleHasItemCommand( + args, status, + [](const cmCMakePath& path) -> bool { return path.HasRootDirectory(); }); +} + +bool HandleHasRootPathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleHasItemCommand( + args, status, + [](const cmCMakePath& path) -> bool { return path.HasRootPath(); }); +} + +bool HandleHasFilenameCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleHasItemCommand( + args, status, + [](const cmCMakePath& path) -> bool { return path.HasFileName(); }); +} + +bool HandleHasExtensionCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleHasItemCommand( + args, status, + [](const cmCMakePath& path) -> bool { return path.HasExtension(); }); +} + +bool HandleHasStemCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleHasItemCommand( + args, status, + [](const cmCMakePath& path) -> bool { return path.HasStem(); }); +} + +bool HandleHasRelativePathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleHasItemCommand( + args, status, + [](const cmCMakePath& path) -> bool { return path.HasRelativePath(); }); +} + +bool HandleHasParentPathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleHasItemCommand( + args, status, + [](const cmCMakePath& path) -> bool { return path.HasParentPath(); }); +} + +bool HandleIsAbsoluteCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() != 3) { + status.SetError("IS_ABSOLUTE must be called with two arguments."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + if (args[2].empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + bool isAbsolute = cmCMakePath(inputPath).IsAbsolute(); + + status.GetMakefile().AddDefinitionBool(args[2], isAbsolute); + + return true; +} + +bool HandleIsRelativeCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() != 3) { + status.SetError("IS_RELATIVE must be called with two arguments."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + if (args[2].empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + bool isRelative = cmCMakePath(inputPath).IsRelative(); + + status.GetMakefile().AddDefinitionBool(args[2], isRelative); + + return true; +} + +bool HandleIsPrefixCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 4 || args.size() > 5) { + status.SetError("IS_PREFIX must be called with three or four arguments."); + return false; + } + + static NormalizeParser const parser; + + const auto arguments = parser.Parse(args); + + if (parser.GetInputs().size() != 2) { + status.SetError("IS_PREFIX called with unexpected arguments."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + const auto& input = parser.GetInputs().front(); + const auto& output = parser.GetInputs().back(); + + if (output.empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + bool isPrefix; + if (arguments.Normalize) { + isPrefix = + cmCMakePath(inputPath).Normal().IsPrefix(cmCMakePath(input).Normal()); + } else { + isPrefix = cmCMakePath(inputPath).IsPrefix(input); + } + + status.GetMakefile().AddDefinitionBool(output, isPrefix); + + return true; +} + +bool HandleHashCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 3 || args.size() > 4) { + status.SetError("HASH must be called with two or three arguments."); + return false; + } + + static NormalizeParser const parser; + + const auto arguments = parser.Parse(args); + + if (parser.GetInputs().size() != 1) { + status.SetError("HASH called with unexpected arguments."); + return false; + } + + std::string inputPath; + if (!getInputPath(args[1], status, inputPath)) { + return false; + } + + const auto& output = parser.GetInputs().front(); + + if (output.empty()) { + status.SetError("Invalid name for output variable."); + return false; + } + + auto hash = hash_value(arguments.Normalize ? cmCMakePath(inputPath).Normal() + : cmCMakePath(inputPath)); + + std::ostringstream out; + out << std::setbase(16) << hash; + + status.GetMakefile().AddDefinition(output, out.str()); + + return true; +} +} // anonymous namespace + +bool cmCMakePathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 2) { + status.SetError("must be called with at least two arguments."); + return false; + } + + static cmSubcommandTable const subcommand{ + { "GET"_s, HandleGetCommand }, + { "APPEND"_s, HandleAppendCommand }, + { "CONCAT"_s, HandleConcatCommand }, + { "REMOVE_FILENAME"_s, HandleRemoveFilenameCommand }, + { "REPLACE_FILENAME"_s, HandleReplaceFilenameCommand }, + { "REMOVE_EXTENSION"_s, HandleRemoveExtensionCommand }, + { "REPLACE_EXTENSION"_s, HandleReplaceExtensionCommand }, + { "NORMAL_PATH"_s, HandleNormalPathCommand }, + { "RELATIVE_PATH"_s, HandleRelativePathCommand }, + { "PROXIMATE_PATH"_s, HandleProximatePathCommand }, + { "ABSOLUTE_PATH"_s, HandleAbsolutePathCommand }, + { "CMAKE_PATH"_s, HandleCMakePathCommand }, + { "NATIVE_PATH"_s, HandleNativePathCommand }, + { "CONVERT"_s, HandleConvertCommand }, + { "COMPARE"_s, HandleCompareCommand }, + { "HAS_ROOT_NAME"_s, HandleHasRootNameCommand }, + { "HAS_ROOT_DIRECTORY"_s, HandleHasRootDirectoryCommand }, + { "HAS_ROOT_PATH"_s, HandleHasRootPathCommand }, + { "HAS_FILENAME"_s, HandleHasFilenameCommand }, + { "HAS_EXTENSION"_s, HandleHasExtensionCommand }, + { "HAS_STEM"_s, HandleHasStemCommand }, + { "HAS_RELATIVE_PATH"_s, HandleHasRelativePathCommand }, + { "HAS_PARENT_PATH"_s, HandleHasParentPathCommand }, + { "IS_ABSOLUTE"_s, HandleIsAbsoluteCommand }, + { "IS_RELATIVE"_s, HandleIsRelativeCommand }, + { "IS_PREFIX"_s, HandleIsPrefixCommand }, + { "HASH"_s, HandleHashCommand } + }; + + return subcommand(args[0], args, status); +} diff --git a/Source/cmCMakePathCommand.h b/Source/cmCMakePathCommand.h new file mode 100644 index 0000000000..49e93807ca --- /dev/null +++ b/Source/cmCMakePathCommand.h @@ -0,0 +1,14 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#pragma once + +#include "cmConfigure.h" // IWYU pragma: keep + +#include <string> +#include <vector> + +class cmExecutionStatus; + +bool cmCMakePathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmCMakePolicyCommand.h b/Source/cmCMakePolicyCommand.h index ba9397d767..7346b6606d 100644 --- a/Source/cmCMakePolicyCommand.h +++ b/Source/cmCMakePolicyCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCMakePolicyCommand_h -#define cmCMakePolicyCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -18,5 +17,3 @@ class cmExecutionStatus; */ bool cmCMakePolicyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmCMakePresetsFile.cxx b/Source/cmCMakePresetsFile.cxx new file mode 100644 index 0000000000..cf5db6ec47 --- /dev/null +++ b/Source/cmCMakePresetsFile.cxx @@ -0,0 +1,885 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCMakePresetsFile.h" + +#include <cstdlib> +#include <functional> +#include <utility> + +#include <cmext/string_view> + +#include <cm3p/json/reader.h> +#include <cm3p/json/value.h> + +#include "cmsys/FStream.hxx" + +#include "cmJSONHelpers.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmVersion.h" + +namespace { +enum class CycleStatus +{ + Unvisited, + InProgress, + Verified, +}; + +using ReadFileResult = cmCMakePresetsFile::ReadFileResult; +using CacheVariable = cmCMakePresetsFile::CacheVariable; +using UnexpandedPreset = cmCMakePresetsFile::UnexpandedPreset; +using ExpandedPreset = cmCMakePresetsFile::ExpandedPreset; +using ArchToolsetStrategy = cmCMakePresetsFile::ArchToolsetStrategy; + +constexpr int MIN_VERSION = 1; +constexpr int MAX_VERSION = 1; + +struct CMakeVersion +{ + unsigned int Major = 0; + unsigned int Minor = 0; + unsigned int Patch = 0; +}; + +struct RootPresets +{ + CMakeVersion CMakeMinimumRequired; + std::vector<cmCMakePresetsFile::UnexpandedPreset> Presets; +}; + +cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error) +{ + return [error](std::nullptr_t& /*out*/, + const Json::Value* value) -> ReadFileResult { + if (!value) { + return ReadFileResult::READ_OK; + } + + if (!value->isObject()) { + return error; + } + + return ReadFileResult::READ_OK; + }; +} + +auto const VersionIntHelper = cmJSONIntHelper<ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION); + +auto const VersionHelper = cmJSONRequiredHelper<int, ReadFileResult>( + ReadFileResult::NO_VERSION, VersionIntHelper); + +auto const RootVersionHelper = + cmJSONObjectHelper<int, ReadFileResult>(ReadFileResult::READ_OK, + ReadFileResult::INVALID_ROOT) + .Bind("version"_s, VersionHelper, false); + +auto const VariableStringHelper = cmJSONStringHelper<ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE); + +ReadFileResult VariableValueHelper(std::string& out, const Json::Value* value) +{ + if (!value) { + out.clear(); + return ReadFileResult::READ_OK; + } + + if (value->isBool()) { + out = value->asBool() ? "TRUE" : "FALSE"; + return ReadFileResult::READ_OK; + } + + return VariableStringHelper(out, value); +} + +auto const VariableObjectHelper = + cmJSONObjectHelper<CacheVariable, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE, false) + .Bind("type"_s, &CacheVariable::Type, VariableStringHelper, false) + .Bind("value"_s, &CacheVariable::Value, VariableValueHelper); + +ReadFileResult VariableHelper(cm::optional<CacheVariable>& out, + const Json::Value* value) +{ + if (value->isBool()) { + out = CacheVariable{ + /*Type=*/"BOOL", + /*Value=*/value->asBool() ? "TRUE" : "FALSE", + }; + return ReadFileResult::READ_OK; + } + if (value->isString()) { + out = CacheVariable{ + /*Type=*/"", + /*Value=*/value->asString(), + }; + return ReadFileResult::READ_OK; + } + if (value->isObject()) { + out.emplace(); + return VariableObjectHelper(*out, value); + } + if (value->isNull()) { + out = cm::nullopt; + return ReadFileResult::READ_OK; + } + return ReadFileResult::INVALID_VARIABLE; +} + +auto const VariablesHelper = + cmJSONMapHelper<cm::optional<CacheVariable>, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper); + +auto const PresetStringHelper = cmJSONStringHelper<ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET); + +ReadFileResult EnvironmentHelper(cm::optional<std::string>& out, + const Json::Value* value) +{ + if (!value || value->isNull()) { + out = cm::nullopt; + return ReadFileResult::READ_OK; + } + if (value->isString()) { + out = value->asString(); + return ReadFileResult::READ_OK; + } + return ReadFileResult::INVALID_PRESET; +} + +auto const EnvironmentMapHelper = + cmJSONMapHelper<cm::optional<std::string>, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, + EnvironmentHelper); + +auto const PresetVectorStringHelper = + cmJSONVectorHelper<std::string, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, + PresetStringHelper); + +ReadFileResult PresetInheritsHelper(std::vector<std::string>& out, + const Json::Value* value) +{ + out.clear(); + if (!value) { + return ReadFileResult::READ_OK; + } + + if (value->isString()) { + out.push_back(value->asString()); + return ReadFileResult::READ_OK; + } + + return PresetVectorStringHelper(out, value); +} + +auto const PresetBoolHelper = cmJSONBoolHelper<ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET); + +auto const PresetOptionalBoolHelper = + cmJSONOptionalHelper<bool, ReadFileResult>(ReadFileResult::READ_OK, + PresetBoolHelper); + +auto const PresetWarningsHelper = + cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("dev"_s, &UnexpandedPreset::WarnDev, PresetOptionalBoolHelper, false) + .Bind("deprecated"_s, &UnexpandedPreset::WarnDeprecated, + PresetOptionalBoolHelper, false) + .Bind("uninitialized"_s, &UnexpandedPreset::WarnUninitialized, + PresetOptionalBoolHelper, false) + .Bind("unusedCli"_s, &UnexpandedPreset::WarnUnusedCli, + PresetOptionalBoolHelper, false) + .Bind("systemVars"_s, &UnexpandedPreset::WarnSystemVars, + PresetOptionalBoolHelper, false); + +auto const PresetErrorsHelper = + cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("dev"_s, &UnexpandedPreset::ErrorDev, PresetOptionalBoolHelper, + false) + .Bind("deprecated"_s, &UnexpandedPreset::ErrorDeprecated, + PresetOptionalBoolHelper, false); + +auto const PresetDebugHelper = + cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("output"_s, &UnexpandedPreset::DebugOutput, PresetOptionalBoolHelper, + false) + .Bind("tryCompile"_s, &UnexpandedPreset::DebugTryCompile, + PresetOptionalBoolHelper, false) + .Bind("find"_s, &UnexpandedPreset::DebugFind, PresetOptionalBoolHelper, + false); + +ReadFileResult ArchToolsetStrategyHelper( + cm::optional<ArchToolsetStrategy>& out, const Json::Value* value) +{ + if (!value) { + out = cm::nullopt; + return ReadFileResult::READ_OK; + } + + if (!value->isString()) { + return ReadFileResult::INVALID_PRESET; + } + + if (value->asString() == "set") { + out = ArchToolsetStrategy::Set; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "external") { + out = ArchToolsetStrategy::External; + return ReadFileResult::READ_OK; + } + + return ReadFileResult::INVALID_PRESET; +} + +std::function<ReadFileResult(UnexpandedPreset&, const Json::Value*)> +ArchToolsetHelper( + std::string UnexpandedPreset::*valueField, + cm::optional<ArchToolsetStrategy> UnexpandedPreset::*strategyField) +{ + auto const objectHelper = + cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("value", valueField, PresetStringHelper, false) + .Bind("strategy", strategyField, ArchToolsetStrategyHelper, false); + return [valueField, strategyField, objectHelper]( + UnexpandedPreset& out, const Json::Value* value) -> ReadFileResult { + if (!value) { + (out.*valueField).clear(); + out.*strategyField = cm::nullopt; + return ReadFileResult::READ_OK; + } + + if (value->isString()) { + out.*valueField = value->asString(); + out.*strategyField = cm::nullopt; + return ReadFileResult::READ_OK; + } + + if (value->isObject()) { + return objectHelper(out, value); + } + + return ReadFileResult::INVALID_PRESET; + }; +} + +auto const ArchitectureHelper = ArchToolsetHelper( + &UnexpandedPreset::Architecture, &UnexpandedPreset::ArchitectureStrategy); +auto const ToolsetHelper = ArchToolsetHelper( + &UnexpandedPreset::Toolset, &UnexpandedPreset::ToolsetStrategy); + +auto const PresetHelper = + cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("name"_s, &UnexpandedPreset::Name, PresetStringHelper) + .Bind("inherits"_s, &UnexpandedPreset::Inherits, PresetInheritsHelper, + false) + .Bind("hidden"_s, &UnexpandedPreset::Hidden, PresetBoolHelper, false) + .Bind<std::nullptr_t>("vendor"_s, nullptr, + VendorHelper(ReadFileResult::INVALID_PRESET), false) + .Bind("displayName"_s, &UnexpandedPreset::DisplayName, PresetStringHelper, + false) + .Bind("description"_s, &UnexpandedPreset::Description, PresetStringHelper, + false) + .Bind("generator"_s, &UnexpandedPreset::Generator, PresetStringHelper, + false) + .Bind("architecture"_s, ArchitectureHelper, false) + .Bind("toolset"_s, ToolsetHelper, false) + .Bind("binaryDir"_s, &UnexpandedPreset::BinaryDir, PresetStringHelper, + false) + .Bind<std::string>("cmakeExecutable"_s, nullptr, PresetStringHelper, false) + .Bind("cacheVariables"_s, &UnexpandedPreset::CacheVariables, + VariablesHelper, false) + .Bind("environment"_s, &UnexpandedPreset::Environment, + EnvironmentMapHelper, false) + .Bind("warnings"_s, PresetWarningsHelper, false) + .Bind("errors"_s, PresetErrorsHelper, false) + .Bind("debug"_s, PresetDebugHelper, false); + +auto const PresetsHelper = + cmJSONVectorHelper<UnexpandedPreset, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, PresetHelper); + +auto const CMakeVersionUIntHelper = cmJSONUIntHelper<ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION); + +auto const CMakeVersionHelper = + cmJSONObjectHelper<CMakeVersion, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_CMAKE_VERSION, false) + .Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false) + .Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false) + .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false); + +auto const RootPresetsHelper = + cmJSONObjectHelper<RootPresets, ReadFileResult>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_ROOT, false) + .Bind<int>("version"_s, nullptr, VersionHelper) + .Bind("configurePresets"_s, &RootPresets::Presets, PresetsHelper, false) + .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired, + CMakeVersionHelper, false) + .Bind<std::nullptr_t>("vendor"_s, nullptr, + VendorHelper(ReadFileResult::INVALID_ROOT), false); + +void InheritString(std::string& child, const std::string& parent) +{ + if (child.empty()) { + child = parent; + } +} + +void InheritOptionalBool(cm::optional<bool>& child, + const cm::optional<bool>& parent) +{ + if (!child) { + child = parent; + } +} + +/** + * Check preset inheritance for cycles (using a DAG check algorithm) while + * also bubbling up fields through the inheritance hierarchy, then verify + * that each preset has the required fields, either directly or through + * inheritance. + */ +ReadFileResult VisitPreset( + std::map<std::string, cmCMakePresetsFile::PresetPair>& presets, + UnexpandedPreset& preset, std::map<std::string, CycleStatus> cycleStatus) +{ + switch (cycleStatus[preset.Name]) { + case CycleStatus::InProgress: + return ReadFileResult::CYCLIC_PRESET_INHERITANCE; + case CycleStatus::Verified: + return ReadFileResult::READ_OK; + default: + break; + } + + cycleStatus[preset.Name] = CycleStatus::InProgress; + + if (preset.CacheVariables.count("") != 0) { + return ReadFileResult::INVALID_PRESET; + } + if (preset.Environment.count("") != 0) { + return ReadFileResult::INVALID_PRESET; + } + + for (auto const& i : preset.Inherits) { + auto parent = presets.find(i); + if (parent == presets.end()) { + return ReadFileResult::INVALID_PRESET; + } + + if (!preset.User && parent->second.Unexpanded.User) { + return ReadFileResult::USER_PRESET_INHERITANCE; + } + + auto result = VisitPreset(presets, parent->second.Unexpanded, cycleStatus); + if (result != ReadFileResult::READ_OK) { + return result; + } + + InheritString(preset.Generator, parent->second.Unexpanded.Generator); + InheritString(preset.Architecture, parent->second.Unexpanded.Architecture); + InheritString(preset.Toolset, parent->second.Unexpanded.Toolset); + if (!preset.ArchitectureStrategy) { + preset.ArchitectureStrategy = + parent->second.Unexpanded.ArchitectureStrategy; + } + if (!preset.ToolsetStrategy) { + preset.ToolsetStrategy = parent->second.Unexpanded.ToolsetStrategy; + } + InheritString(preset.BinaryDir, parent->second.Unexpanded.BinaryDir); + InheritOptionalBool(preset.WarnDev, parent->second.Unexpanded.WarnDev); + InheritOptionalBool(preset.ErrorDev, parent->second.Unexpanded.ErrorDev); + InheritOptionalBool(preset.WarnDeprecated, + parent->second.Unexpanded.WarnDeprecated); + InheritOptionalBool(preset.ErrorDeprecated, + parent->second.Unexpanded.ErrorDeprecated); + InheritOptionalBool(preset.WarnUninitialized, + parent->second.Unexpanded.WarnUninitialized); + InheritOptionalBool(preset.WarnUnusedCli, + parent->second.Unexpanded.WarnUnusedCli); + InheritOptionalBool(preset.WarnSystemVars, + parent->second.Unexpanded.WarnSystemVars); + for (auto const& v : parent->second.Unexpanded.CacheVariables) { + preset.CacheVariables.insert(v); + } + for (auto const& v : parent->second.Unexpanded.Environment) { + preset.Environment.insert(v); + } + } + + if (!preset.Hidden) { + if (preset.Generator.empty()) { + return ReadFileResult::INVALID_PRESET; + } + if (preset.BinaryDir.empty()) { + return ReadFileResult::INVALID_PRESET; + } + if (preset.WarnDev == false && preset.ErrorDev == true) { + return ReadFileResult::INVALID_PRESET; + } + if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) { + return ReadFileResult::INVALID_PRESET; + } + } + + cycleStatus[preset.Name] = CycleStatus::Verified; + return ReadFileResult::READ_OK; +} + +ReadFileResult ComputePresetInheritance( + std::map<std::string, cmCMakePresetsFile::PresetPair>& presets) +{ + std::map<std::string, CycleStatus> cycleStatus; + for (auto const& it : presets) { + cycleStatus[it.first] = CycleStatus::Unvisited; + } + + for (auto& it : presets) { + auto result = VisitPreset(presets, it.second.Unexpanded, cycleStatus); + if (result != ReadFileResult::READ_OK) { + return result; + } + } + + return ReadFileResult::READ_OK; +} + +constexpr const char* ValidPrefixes[] = { + "", + "env", + "penv", + "vendor", +}; + +bool PrefixesValidMacroNamespace(const std::string& str) +{ + for (auto const& prefix : ValidPrefixes) { + if (cmHasPrefix(prefix, str)) { + return true; + } + } + return false; +} + +bool IsValidMacroNamespace(const std::string& str) +{ + for (auto const& prefix : ValidPrefixes) { + if (str == prefix) { + return true; + } + } + return false; +} + +enum class ExpandMacroResult +{ + Ok, + Ignore, + Error, +}; + +ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& value, CycleStatus& status); +ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& out); +ExpandMacroResult ExpandMacro(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& out, + const std::string& macroNamespace, + const std::string& macroName); + +bool ExpandMacros(const cmCMakePresetsFile& file, + const UnexpandedPreset& preset, + cm::optional<ExpandedPreset>& out) +{ + out = preset; + + std::map<std::string, CycleStatus> envCycles; + for (auto const& v : out->Environment) { + envCycles[v.first] = CycleStatus::Unvisited; + } + + for (auto& v : out->Environment) { + if (v.second) { + switch (VisitEnv(file, *out, envCycles, *v.second, envCycles[v.first])) { + case ExpandMacroResult::Error: + return false; + case ExpandMacroResult::Ignore: + out.reset(); + return true; + case ExpandMacroResult::Ok: + break; + } + } + } + + std::string binaryDir = preset.BinaryDir; + switch (ExpandMacros(file, *out, envCycles, binaryDir)) { + case ExpandMacroResult::Error: + return false; + case ExpandMacroResult::Ignore: + out.reset(); + return true; + case ExpandMacroResult::Ok: + break; + } + if (!cmSystemTools::FileIsFullPath(binaryDir)) { + binaryDir = cmStrCat(file.SourceDir, '/', binaryDir); + } + out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir); + cmSystemTools::ConvertToUnixSlashes(out->BinaryDir); + + for (auto& variable : out->CacheVariables) { + if (variable.second) { + switch (ExpandMacros(file, *out, envCycles, variable.second->Value)) { + case ExpandMacroResult::Error: + return false; + case ExpandMacroResult::Ignore: + out.reset(); + return true; + case ExpandMacroResult::Ok: + break; + } + } + } + + return true; +} + +ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& value, CycleStatus& status) +{ + if (status == CycleStatus::Verified) { + return ExpandMacroResult::Ok; + } + if (status == CycleStatus::InProgress) { + return ExpandMacroResult::Error; + } + + status = CycleStatus::InProgress; + auto e = ExpandMacros(file, preset, envCycles, value); + if (e != ExpandMacroResult::Ok) { + return e; + } + status = CycleStatus::Verified; + return ExpandMacroResult::Ok; +} + +ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& out) +{ + std::string result; + std::string macroNamespace; + std::string macroName; + + enum class State + { + Default, + MacroNamespace, + MacroName, + } state = State::Default; + + for (auto c : out) { + switch (state) { + case State::Default: + if (c == '$') { + state = State::MacroNamespace; + } else { + result += c; + } + break; + + case State::MacroNamespace: + if (c == '{') { + if (IsValidMacroNamespace(macroNamespace)) { + state = State::MacroName; + } else { + result += '$'; + result += macroNamespace; + result += '{'; + macroNamespace.clear(); + state = State::Default; + } + } else { + macroNamespace += c; + if (!PrefixesValidMacroNamespace(macroNamespace)) { + result += '$'; + result += macroNamespace; + macroNamespace.clear(); + state = State::Default; + } + } + break; + + case State::MacroName: + if (c == '}') { + auto e = ExpandMacro(file, preset, envCycles, result, macroNamespace, + macroName); + if (e != ExpandMacroResult::Ok) { + return e; + } + macroNamespace.clear(); + macroName.clear(); + state = State::Default; + } else { + macroName += c; + } + break; + } + } + + switch (state) { + case State::Default: + break; + case State::MacroNamespace: + result += '$'; + result += macroNamespace; + break; + case State::MacroName: + return ExpandMacroResult::Error; + } + + out = std::move(result); + return ExpandMacroResult::Ok; +} + +ExpandMacroResult ExpandMacro(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& out, + const std::string& macroNamespace, + const std::string& macroName) +{ + if (macroNamespace.empty()) { + if (macroName == "sourceDir") { + out += file.SourceDir; + return ExpandMacroResult::Ok; + } + if (macroName == "sourceParentDir") { + out += cmSystemTools::GetParentDirectory(file.SourceDir); + return ExpandMacroResult::Ok; + } + if (macroName == "sourceDirName") { + out += cmSystemTools::GetFilenameName(file.SourceDir); + return ExpandMacroResult::Ok; + } + if (macroName == "presetName") { + out += preset.Name; + return ExpandMacroResult::Ok; + } + if (macroName == "generator") { + out += preset.Generator; + return ExpandMacroResult::Ok; + } + if (macroName == "dollar") { + out += '$'; + return ExpandMacroResult::Ok; + } + } + + if (macroNamespace == "env" && !macroName.empty()) { + auto v = preset.Environment.find(macroName); + if (v != preset.Environment.end() && v->second) { + auto e = + VisitEnv(file, preset, envCycles, *v->second, envCycles[macroName]); + if (e != ExpandMacroResult::Ok) { + return e; + } + out += *v->second; + return ExpandMacroResult::Ok; + } + } + + if (macroNamespace == "env" || macroNamespace == "penv") { + if (macroName.empty()) { + return ExpandMacroResult::Error; + } + const char* value = std::getenv(macroName.c_str()); + if (value) { + out += value; + } + return ExpandMacroResult::Ok; + } + + if (macroNamespace == "vendor") { + return ExpandMacroResult::Ignore; + } + + return ExpandMacroResult::Error; +} +} + +std::string cmCMakePresetsFile::GetFilename(const std::string& sourceDir) +{ + return cmStrCat(sourceDir, "/CMakePresets.json"); +} + +std::string cmCMakePresetsFile::GetUserFilename(const std::string& sourceDir) +{ + return cmStrCat(sourceDir, "/CMakeUserPresets.json"); +} + +cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadProjectPresets( + const std::string& sourceDir, bool allowNoFiles) +{ + bool haveOneFile = false; + this->SourceDir = sourceDir; + this->Presets.clear(); + this->PresetOrder.clear(); + + std::vector<std::string> presetOrder; + std::map<std::string, PresetPair> presetMap; + + std::string filename = GetUserFilename(this->SourceDir); + if (cmSystemTools::FileExists(filename)) { + auto result = this->ReadJSONFile(filename, presetOrder, presetMap, true); + if (result != ReadFileResult::READ_OK) { + return result; + } + haveOneFile = true; + } + + filename = GetFilename(this->SourceDir); + if (cmSystemTools::FileExists(filename)) { + auto result = this->ReadJSONFile(filename, presetOrder, presetMap, false); + if (result != ReadFileResult::READ_OK) { + return result; + } + haveOneFile = true; + } + + if (!haveOneFile) { + return allowNoFiles ? ReadFileResult::READ_OK + : ReadFileResult::FILE_NOT_FOUND; + } + + auto result = ComputePresetInheritance(presetMap); + if (result != ReadFileResult::READ_OK) { + return result; + } + + for (auto& it : presetMap) { + if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + return ReadFileResult::INVALID_MACRO_EXPANSION; + } + } + + this->PresetOrder = std::move(presetOrder); + this->Presets = std::move(presetMap); + return ReadFileResult::READ_OK; +} + +const char* cmCMakePresetsFile::ResultToString(ReadFileResult result) +{ + switch (result) { + case ReadFileResult::READ_OK: + return "OK"; + case ReadFileResult::FILE_NOT_FOUND: + return "File not found"; + case ReadFileResult::JSON_PARSE_ERROR: + return "JSON parse error"; + case ReadFileResult::INVALID_ROOT: + return "Invalid root object"; + case ReadFileResult::NO_VERSION: + return "No \"version\" field"; + case ReadFileResult::INVALID_VERSION: + return "Invalid \"version\" field"; + case ReadFileResult::UNRECOGNIZED_VERSION: + return "Unrecognized \"version\" field"; + case ReadFileResult::INVALID_CMAKE_VERSION: + return "Invalid \"cmakeMinimumRequired\" field"; + case ReadFileResult::UNRECOGNIZED_CMAKE_VERSION: + return "\"cmakeMinimumRequired\" version too new"; + case ReadFileResult::INVALID_PRESETS: + return "Invalid \"configurePresets\" field"; + case ReadFileResult::INVALID_PRESET: + return "Invalid preset"; + case ReadFileResult::INVALID_VARIABLE: + return "Invalid CMake variable definition"; + case ReadFileResult::DUPLICATE_PRESETS: + return "Duplicate presets"; + case ReadFileResult::CYCLIC_PRESET_INHERITANCE: + return "Cyclic preset inheritance"; + case ReadFileResult::USER_PRESET_INHERITANCE: + return "Project preset inherits from user preset"; + case ReadFileResult::INVALID_MACRO_EXPANSION: + return "Invalid macro expansion"; + } + + return "Unknown error"; +} + +cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile( + const std::string& filename, std::vector<std::string>& presetOrder, + std::map<std::string, PresetPair>& presetMap, bool user) +{ + cmsys::ifstream fin(filename.c_str()); + if (!fin) { + return ReadFileResult::FILE_NOT_FOUND; + } + // If there's a BOM, toss it. + cmsys::FStream::ReadBOM(fin); + + Json::Value root; + Json::CharReaderBuilder builder; + if (!Json::parseFromStream(builder, fin, &root, nullptr)) { + return ReadFileResult::JSON_PARSE_ERROR; + } + + int v = 0; + auto result = RootVersionHelper(v, &root); + if (result != ReadFileResult::READ_OK) { + return result; + } + if (v < MIN_VERSION || v > MAX_VERSION) { + return ReadFileResult::UNRECOGNIZED_VERSION; + } + + RootPresets presets; + if ((result = RootPresetsHelper(presets, &root)) != + ReadFileResult::READ_OK) { + return result; + } + + unsigned int currentMajor = cmVersion::GetMajorVersion(); + unsigned int currentMinor = cmVersion::GetMinorVersion(); + unsigned int currentPatch = cmVersion::GetPatchVersion(); + auto const& required = presets.CMakeMinimumRequired; + if (required.Major > currentMajor || + (required.Major == currentMajor && + (required.Minor > currentMinor || + (required.Minor == currentMinor && + (required.Patch > currentPatch))))) { + return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION; + } + + for (auto& preset : presets.Presets) { + preset.User = user; + if (preset.Name.empty()) { + return ReadFileResult::INVALID_PRESET; + } + if (!presetMap.insert({ preset.Name, { preset, cm::nullopt } }).second) { + return ReadFileResult::DUPLICATE_PRESETS; + } + presetOrder.push_back(preset.Name); + } + + return ReadFileResult::READ_OK; +} diff --git a/Source/cmCMakePresetsFile.h b/Source/cmCMakePresetsFile.h new file mode 100644 index 0000000000..f6b159a02c --- /dev/null +++ b/Source/cmCMakePresetsFile.h @@ -0,0 +1,148 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include <cm/optional> + +class cmCMakePresetsFile +{ +public: + enum class ArchToolsetStrategy + { + Set, + External, + }; + + class CacheVariable + { + public: + std::string Type; + std::string Value; + }; + + class Preset + { + public: +#if __cplusplus < 201703L && (!defined(_MSVC_LANG) || _MSVC_LANG < 201703L) + Preset() = default; + Preset(const Preset& /*other*/) = default; + Preset(Preset&& /*other*/) = default; + + Preset& operator=(const Preset& /*other*/) = default; + + // The move assignment operators for several STL classes did not become + // noexcept until C++17, which causes some tools to warn about this move + // assignment operator throwing an exception when it shouldn't. Disable the + // move assignment operator until C++17 is enabled. + Preset& operator=(Preset&& /*other*/) = delete; +#endif + + std::string Name; + std::vector<std::string> Inherits; + bool Hidden; + bool User; + std::string DisplayName; + std::string Description; + std::string Generator; + std::string Architecture; + cm::optional<ArchToolsetStrategy> ArchitectureStrategy; + std::string Toolset; + cm::optional<ArchToolsetStrategy> ToolsetStrategy; + std::string BinaryDir; + + std::map<std::string, cm::optional<CacheVariable>> CacheVariables; + std::map<std::string, cm::optional<std::string>> Environment; + + cm::optional<bool> WarnDev; + cm::optional<bool> ErrorDev; + cm::optional<bool> WarnDeprecated; + cm::optional<bool> ErrorDeprecated; + cm::optional<bool> WarnUninitialized; + cm::optional<bool> WarnUnusedCli; + cm::optional<bool> WarnSystemVars; + + cm::optional<bool> DebugOutput; + cm::optional<bool> DebugTryCompile; + cm::optional<bool> DebugFind; + }; + + class UnexpandedPreset : public Preset + { + public: + using Preset::Preset; + + UnexpandedPreset() = default; + UnexpandedPreset(const Preset& preset) + : Preset(preset) + { + } + UnexpandedPreset(Preset&& preset) + : Preset(std::move(preset)) + { + } + }; + + class ExpandedPreset : public Preset + { + public: + using Preset::Preset; + + ExpandedPreset() = default; + ExpandedPreset(const Preset& preset) + : Preset(preset) + { + } + ExpandedPreset(Preset&& preset) + : Preset(std::move(preset)) + { + } + }; + + class PresetPair + { + public: + UnexpandedPreset Unexpanded; + cm::optional<ExpandedPreset> Expanded; + }; + + std::string SourceDir; + std::map<std::string, PresetPair> Presets; + std::vector<std::string> PresetOrder; + + enum class ReadFileResult + { + READ_OK, + FILE_NOT_FOUND, + JSON_PARSE_ERROR, + INVALID_ROOT, + NO_VERSION, + INVALID_VERSION, + UNRECOGNIZED_VERSION, + INVALID_CMAKE_VERSION, + UNRECOGNIZED_CMAKE_VERSION, + INVALID_PRESETS, + INVALID_PRESET, + INVALID_VARIABLE, + DUPLICATE_PRESETS, + CYCLIC_PRESET_INHERITANCE, + USER_PRESET_INHERITANCE, + INVALID_MACRO_EXPANSION, + }; + + static std::string GetFilename(const std::string& sourceDir); + static std::string GetUserFilename(const std::string& sourceDir); + ReadFileResult ReadProjectPresets(const std::string& sourceDir, + bool allowNoFiles = false); + static const char* ResultToString(ReadFileResult result); + +private: + ReadFileResult ReadJSONFile(const std::string& filename, + std::vector<std::string>& presetOrder, + std::map<std::string, PresetPair>& presetMap, + bool user); +}; diff --git a/Source/cmCPackPropertiesGenerator.h b/Source/cmCPackPropertiesGenerator.h index 83392383a4..63c469ae06 100644 --- a/Source/cmCPackPropertiesGenerator.h +++ b/Source/cmCPackPropertiesGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCPackPropertiesGenerator_h -#define cmCPackPropertiesGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -36,5 +35,3 @@ protected: cmLocalGenerator* LG; cmInstalledFile const& InstalledFile; }; - -#endif diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx index 697d435aae..8ebf6d2938 100644 --- a/Source/cmCPluginAPI.cxx +++ b/Source/cmCPluginAPI.cxx @@ -140,7 +140,7 @@ const char* CCONV cmGetCurrentOutputDirectory(void* arg) const char* CCONV cmGetDefinition(void* arg, const char* def) { cmMakefile* mf = static_cast<cmMakefile*>(arg); - return mf->GetDefinition(def); + return cmToCStr(mf->GetDefinition(def)); } int CCONV cmIsOn(void* arg, const char* name) @@ -419,12 +419,15 @@ int CCONV cmExecuteCommand(void* arg, const char* name, int numArgs, const char** args) { cmMakefile* mf = static_cast<cmMakefile*>(arg); - cmListFileFunction lff; - lff.Name = name; + + std::vector<cmListFileArgument> lffArgs; + lffArgs.reserve(numArgs); for (int i = 0; i < numArgs; ++i) { // Assume all arguments are quoted. - lff.Arguments.emplace_back(args[i], cmListFileArgument::Quoted, 0); + lffArgs.emplace_back(args[i], cmListFileArgument::Quoted, 0); } + + cmListFileFunction lff{ name, 0, std::move(lffArgs) }; cmExecutionStatus status(*mf); return mf->ExecuteCommand(lff, status); } @@ -581,13 +584,13 @@ const char* CCONV cmSourceFileGetProperty(void* arg, const char* prop) cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg); if (cmSourceFile* rsf = sf->RealSourceFile) { cmProp p = rsf->GetProperty(prop); - return p ? p->c_str() : nullptr; + return cmToCStr(p); } if (!strcmp(prop, "LOCATION")) { return sf->FullPath.c_str(); } cmProp retVal = sf->Properties.GetPropertyValue(prop); - return retVal ? retVal->c_str() : nullptr; + return cmToCStr(retVal); } int CCONV cmSourceFileGetPropertyAsBool(void* arg, const char* prop) diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index bca75406bd..8cf5ae9aa9 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -394,7 +394,7 @@ bool cmCTest::ShouldCompressTestOutput() return this->Impl->CompressTestOutput; } -cmCTest::Part cmCTest::GetPartFromName(const char* name) +cmCTest::Part cmCTest::GetPartFromName(const std::string& name) { // Look up by lower-case to make names case-insensitive. std::string lower_name = cmSystemTools::LowerCase(name); @@ -458,8 +458,7 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) cm.GetCurrentSnapshot().SetDefaultDefinitions(); cmGlobalGenerator gg(&cm); cmMakefile mf(&gg, cm.GetCurrentSnapshot()); - if (!this->ReadCustomConfigurationFileTree(this->Impl->BinaryDir.c_str(), - &mf)) { + if (!this->ReadCustomConfigurationFileTree(this->Impl->BinaryDir, &mf)) { cmCTestOptionalLog( this, DEBUG, "Cannot find custom configuration file tree" << std::endl, quiet); @@ -523,7 +522,7 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) std::string model; if (cmSystemTools::GetLineFromStream(tfin, model) && !this->Impl->Parts[PartStart] && !command) { - this->Impl->TestModel = GetTestModelFromString(model.c_str()); + this->Impl->TestModel = GetTestModelFromString(model); } tfin.close(); } @@ -579,7 +578,7 @@ int cmCTest::Initialize(const char* binary_dir, cmCTestStartCommand* command) cmSystemTools::GetLineFromStream(tfin, tag); cmSystemTools::GetLineFromStream(tfin, group); if (cmSystemTools::GetLineFromStream(tfin, modelStr)) { - model = GetTestModelFromString(modelStr.c_str()); + model = GetTestModelFromString(modelStr); } tfin.close(); } @@ -705,8 +704,7 @@ bool cmCTest::UpdateCTestConfiguration() if (!cmSystemTools::FileExists(fileName)) { // No need to exit if we are not producing XML if (this->Impl->ProduceXML) { - cmCTestLog(this, ERROR_MESSAGE, - "Cannot find file: " << fileName << std::endl); + cmCTestLog(this, WARNING, "Cannot find file: " << fileName << std::endl); return false; } } else { @@ -793,7 +791,7 @@ int cmCTest::GetTestModel() const return this->Impl->TestModel; } -bool cmCTest::SetTest(const char* ttype, bool report) +bool cmCTest::SetTest(const std::string& ttype, bool report) { if (cmSystemTools::LowerCase(ttype) == "all") { for (Part p = PartStart; p != PartCount; p = Part(p + 1)) { @@ -841,6 +839,7 @@ bool cmCTest::OpenOutputFile(const std::string& path, const std::string& name, } } std::string filename = testingDir + "/" + name; + stream.SetTempExt("tmp"); stream.Open(filename); if (!stream) { cmCTestLog(this, ERROR_MESSAGE, @@ -855,7 +854,7 @@ bool cmCTest::OpenOutputFile(const std::string& path, const std::string& name, return true; } -bool cmCTest::AddIfExists(Part part, const char* file) +bool cmCTest::AddIfExists(Part part, const std::string& file) { if (this->CTestFileExists(file)) { this->AddSubmitFile(part, file); @@ -1007,7 +1006,7 @@ int cmCTest::ProcessSteps() if (this->Impl->Parts[PartNotes]) { this->UpdateCTestConfiguration(); if (!this->Impl->NotesFiles.empty()) { - this->GenerateNotesFile(this->Impl->NotesFiles.c_str()); + this->GenerateNotesFile(this->Impl->NotesFiles); } } if (this->Impl->Parts[PartSubmit]) { @@ -1036,9 +1035,9 @@ std::string cmCTest::GetTestModelString() return "Experimental"; } -int cmCTest::GetTestModelFromString(const char* str) +int cmCTest::GetTestModelFromString(const std::string& str) { - if (!str) { + if (str.empty()) { return cmCTest::EXPERIMENTAL; } std::string rstr = cmSystemTools::LowerCase(str); @@ -1564,9 +1563,9 @@ int cmCTest::GenerateNotesFile(std::vector<std::string> const& files) return 0; } -int cmCTest::GenerateNotesFile(const char* cfiles) +int cmCTest::GenerateNotesFile(const std::string& cfiles) { - if (!cfiles) { + if (cfiles.empty()) { return 1; } @@ -1649,14 +1648,14 @@ bool cmCTest::SubmitExtraFiles(std::vector<std::string> const& files) << std::endl;); return false; } - this->AddSubmitFile(PartExtraFiles, file.c_str()); + this->AddSubmitFile(PartExtraFiles, file); } return true; } -bool cmCTest::SubmitExtraFiles(const char* cfiles) +bool cmCTest::SubmitExtraFiles(const std::string& cfiles) { - if (!cfiles) { + if (cfiles.empty()) { return true; } @@ -1940,7 +1939,7 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, else if (this->CheckArgument(arg, "-C"_s, "--build-config") && i < args.size() - 1) { i++; - this->SetConfigType(args[i].c_str()); + this->SetConfigType(args[i]); } else if (this->CheckArgument(arg, "--debug"_s)) { @@ -2015,7 +2014,7 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, else if (this->CheckArgument(arg, "-O"_s, "--output-log") && i < args.size() - 1) { i++; - this->SetOutputLogFileName(args[i].c_str()); + this->SetOutputLogFileName(args[i]); } else if (this->CheckArgument(arg, "--tomorrow-tag"_s)) { @@ -2047,7 +2046,7 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, this->Impl->ProduceXML = true; this->SetTest("Notes"); i++; - this->SetNotesFiles(args[i].c_str()); + this->SetNotesFiles(args[i]); return true; } @@ -2304,7 +2303,7 @@ int cmCTest::Run(std::vector<std::string>& args, std::string* output) this->Impl->ProduceXML = true; this->SetTest("Submit"); i++; - if (!this->SubmitExtraFiles(args[i].c_str())) { + if (!this->SubmitExtraFiles(args[i])) { return 0; } } @@ -2375,7 +2374,7 @@ bool cmCTest::HandleTestActionArgument(const char* ctestExec, size_t& i, (i < args.size() - 1)) { this->Impl->ProduceXML = true; i++; - if (!this->SetTest(args[i].c_str(), false)) { + if (!this->SetTest(args[i], false)) { success = false; cmCTestLog(this, ERROR_MESSAGE, "CTest -T called with incorrect option: " << args[i] @@ -2490,11 +2489,8 @@ int cmCTest::RunCMakeAndTest(std::string* output) return retv; } -void cmCTest::SetNotesFiles(const char* notes) +void cmCTest::SetNotesFiles(const std::string& notes) { - if (!notes) { - return; - } this->Impl->NotesFiles = notes; } @@ -2560,7 +2556,8 @@ void cmCTest::SetScheduleType(std::string const& type) this->Impl->ScheduleType = type; } -int cmCTest::ReadCustomConfigurationFileTree(const char* dir, cmMakefile* mf) +int cmCTest::ReadCustomConfigurationFileTree(const std::string& dir, + cmMakefile* mf) { bool found = false; cmCTestLog(this, DEBUG, @@ -2623,14 +2620,14 @@ int cmCTest::ReadCustomConfigurationFileTree(const char* dir, cmMakefile* mf) void cmCTest::PopulateCustomVector(cmMakefile* mf, const std::string& def, std::vector<std::string>& vec) { - const char* dval = mf->GetDefinition(def); + cmProp dval = mf->GetDefinition(def); if (!dval) { return; } cmCTestLog(this, DEBUG, "PopulateCustomVector: " << def << std::endl); vec.clear(); - cmExpandList(dval, vec); + cmExpandList(*dval, vec); for (std::string const& it : vec) { cmCTestLog(this, DEBUG, " -- " << it << std::endl); @@ -2640,14 +2637,14 @@ void cmCTest::PopulateCustomVector(cmMakefile* mf, const std::string& def, void cmCTest::PopulateCustomInteger(cmMakefile* mf, const std::string& def, int& val) { - const char* dval = mf->GetDefinition(def); + cmProp dval = mf->GetDefinition(def); if (!dval) { return; } - val = atoi(dval); + val = atoi(dval->c_str()); } -std::string cmCTest::GetShortPathToFile(const char* cfname) +std::string cmCTest::GetShortPathToFile(const std::string& cfname) { const std::string& sourceDir = cmSystemTools::CollapseFullPath( this->GetCTestConfiguration("SourceDirectory")); @@ -2711,18 +2708,17 @@ void cmCTest::EmptyCTestConfiguration() this->Impl->CTestConfiguration.clear(); } -void cmCTest::SetCTestConfiguration(const char* name, const char* value, +void cmCTest::SetCTestConfiguration(const char* name, const std::string& value, bool suppress) { cmCTestOptionalLog(this, HANDLER_VERBOSE_OUTPUT, - "SetCTestConfiguration:" - << name << ":" << (value ? value : "(null)") << "\n", + "SetCTestConfiguration:" << name << ":" << value << "\n", suppress); if (!name) { return; } - if (!value) { + if (value.empty()) { this->Impl->CTestConfiguration.erase(name); return; } @@ -2927,7 +2923,7 @@ std::string cmCTest::GetBuildID() const return this->Impl->BuildID; } -void cmCTest::AddSubmitFile(Part part, const char* name) +void cmCTest::AddSubmitFile(Part part, const std::string& name) { this->Impl->Parts[part].SubmitFiles.emplace_back(name); } @@ -2963,9 +2959,9 @@ void cmCTest::AddCTestConfigurationOverwrite(const std::string& overStr) this->Impl->CTestConfigurationOverwrites[key] = value; } -void cmCTest::SetConfigType(const char* ct) +void cmCTest::SetConfigType(const std::string& ct) { - this->Impl->ConfigType = ct ? ct : ""; + this->Impl->ConfigType = ct; cmSystemTools::ReplaceString(this->Impl->ConfigType, ".\\", ""); std::string confTypeEnv = "CMAKE_CONFIG_TYPE=" + this->Impl->ConfigType; cmSystemTools::PutEnv(confTypeEnv); @@ -2975,8 +2971,7 @@ bool cmCTest::SetCTestConfigurationFromCMakeVariable( cmMakefile* mf, const char* dconfig, const std::string& cmake_var, bool suppress) { - const char* ctvar; - ctvar = mf->GetDefinition(cmake_var); + cmProp ctvar = mf->GetDefinition(cmake_var); if (!ctvar) { return false; } @@ -2984,7 +2979,7 @@ bool cmCTest::SetCTestConfigurationFromCMakeVariable( "SetCTestConfigurationFromCMakeVariable:" << dconfig << ":" << cmake_var << std::endl, suppress); - this->SetCTestConfiguration(dconfig, ctvar, suppress); + this->SetCTestConfiguration(dconfig, *ctvar, suppress); return true; } @@ -3085,9 +3080,9 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args, return result; } -void cmCTest::SetOutputLogFileName(const char* name) +void cmCTest::SetOutputLogFileName(const std::string& name) { - if (name) { + if (!name.empty()) { this->Impl->OutputLogFile = cm::make_unique<cmGeneratedFileStream>(name); } else { this->Impl->OutputLogFile.reset(); diff --git a/Source/cmCTest.h b/Source/cmCTest.h index a39b8fed4a..e12f8b0555 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCTest_h -#define cmCTest_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -64,7 +63,7 @@ public: /** 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); + Part GetPartFromName(const std::string& name); /** Process Command line arguments */ int Run(std::vector<std::string>&, std::string* output = nullptr); @@ -127,12 +126,12 @@ public: * Check if CTest file exists */ bool CTestFileExists(const std::string& filename); - bool AddIfExists(Part part, const char* file); + bool AddIfExists(Part part, const std::string& file); /** * Set the cmake test */ - bool SetTest(const char*, bool report = true); + bool SetTest(const std::string&, bool report = true); /** * Set the cmake test mode (experimental, nightly, continuous). @@ -141,11 +140,11 @@ public: int GetTestModel() const; std::string GetTestModelString(); - static int GetTestModelFromString(const char* str); + static int GetTestModelFromString(const std::string& str); static std::string CleanString(const std::string& str, std::string::size_type spos = 0); std::string GetCTestConfiguration(const std::string& name); - void SetCTestConfiguration(const char* name, const char* value, + void SetCTestConfiguration(const char* name, const std::string& value, bool suppress = false); void EmptyCTestConfiguration(); @@ -161,7 +160,7 @@ public: cmCTest& operator=(const cmCTest&) = delete; /** Set the notes files to be created. */ - void SetNotesFiles(const char* notes); + void SetNotesFiles(const std::string& notes); void PopulateCustomVector(cmMakefile* mf, const std::string& definition, std::vector<std::string>& vec); @@ -272,7 +271,7 @@ public: * This means if the file is in binary or * source directory, it will become /.../relative/path/to/file */ - std::string GetShortPathToFile(const char* fname); + std::string GetShortPathToFile(const std::string& fname); enum { @@ -354,14 +353,14 @@ public: int GenerateDoneFile(); /** Submit extra files to the server */ - bool SubmitExtraFiles(const char* files); + bool SubmitExtraFiles(const std::string& files); bool SubmitExtraFiles(std::vector<std::string> const& files); /** Set the output log file name */ - void SetOutputLogFileName(const char* name); + void SetOutputLogFileName(const std::string& name); /** Set the visual studio or Xcode config type */ - void SetConfigType(const char* ct); + void SetConfigType(const std::string& ct); /** Various log types */ enum @@ -399,14 +398,14 @@ public: std::string GetBuildID() const; /** Add file to be submitted */ - void AddSubmitFile(Part part, const char* name); + void AddSubmitFile(Part part, const std::string& name); 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); + int ReadCustomConfigurationFileTree(const std::string& dir, cmMakefile* mf); std::vector<std::string>& GetInitialCommandLineArguments(); @@ -462,7 +461,7 @@ public: void SetRunCurrentScript(bool value); private: - int GenerateNotesFile(const char* files); + int GenerateNotesFile(const std::string& files); void BlockTestErrorDiagnostics(); @@ -571,5 +570,3 @@ inline std::ostream& operator<<(std::ostream& os, const cmCTestLogWrite& c) (ctSelf)->Log(cmCTest::logType, __FILE__, __LINE__, \ cmCTestLog_msg.str().c_str(), suppress); \ } while (false) - -#endif diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx index 35bd681c95..8d1a5fd3fc 100644 --- a/Source/cmCacheManager.cxx +++ b/Source/cmCacheManager.cxx @@ -162,6 +162,7 @@ bool cmCacheManager::LoadCache(const std::string& path, bool internal, cmSystemTools::Error(message.str()); } } + this->CacheLoaded = true; return true; } @@ -578,10 +579,7 @@ cmProp cmCacheManager::CacheEntry::GetProperty(const std::string& prop) const bool cmCacheManager::CacheEntry::GetPropertyAsBool( const std::string& prop) const { - if (cmProp value = this->GetProperty(prop)) { - return cmIsOn(*value); - } - return false; + return cmIsOn(this->GetProperty(prop)); } void cmCacheManager::CacheEntry::SetProperty(const std::string& prop, diff --git a/Source/cmCacheManager.h b/Source/cmCacheManager.h index f0362585b1..9aebffc0d7 100644 --- a/Source/cmCacheManager.h +++ b/Source/cmCacheManager.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCacheManager_h -#define cmCacheManager_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -67,6 +66,9 @@ public: //! Print the cache to a stream void PrintCache(std::ostream&) const; + //! Get whether or not cache is loaded + bool IsCacheLoaded() const { return this->CacheLoaded; } + //! Get a value from the cache given a key cmProp GetInitializedCacheValue(const std::string& key) const; @@ -205,10 +207,9 @@ private: const CacheEntry& e, cmMessenger* messenger) const; std::map<std::string, CacheEntry> Cache; + bool CacheLoaded = false; // Cache version info unsigned int CacheMajorVersion = 0; unsigned int CacheMinorVersion = 0; }; - -#endif diff --git a/Source/cmCallVisualStudioMacro.h b/Source/cmCallVisualStudioMacro.h index 9b5b3a843a..795b8631fe 100644 --- a/Source/cmCallVisualStudioMacro.h +++ b/Source/cmCallVisualStudioMacro.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCallVisualStudioMacro_h -#define cmCallVisualStudioMacro_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -32,5 +31,3 @@ public: protected: private: }; - -#endif diff --git a/Source/cmCheckCustomOutputs.h b/Source/cmCheckCustomOutputs.h index 9f33d16659..2752ed468a 100644 --- a/Source/cmCheckCustomOutputs.h +++ b/Source/cmCheckCustomOutputs.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCheckCustomOutputs_h -#define cmCheckCustomOutputs_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -14,5 +13,3 @@ class cmExecutionStatus; bool cmCheckCustomOutputs(const std::vector<std::string>& outputs, cm::string_view keyword, cmExecutionStatus& status); - -#endif diff --git a/Source/cmCommand.h b/Source/cmCommand.h index bcb178d381..68c56d90c2 100644 --- a/Source/cmCommand.h +++ b/Source/cmCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCommand_h -#define cmCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -96,5 +95,3 @@ public: private: std::unique_ptr<cmCommand> Command; }; - -#endif diff --git a/Source/cmCommandArgumentParserHelper.cxx b/Source/cmCommandArgumentParserHelper.cxx index 87eb91ce16..d4f502226f 100644 --- a/Source/cmCommandArgumentParserHelper.cxx +++ b/Source/cmCommandArgumentParserHelper.cxx @@ -8,8 +8,11 @@ #include <utility> #include <cm/memory> +#include <cm/optional> +#include <cmext/string_view> #include "cmCommandArgumentLexer.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmProperty.h" #include "cmState.h" @@ -91,9 +94,16 @@ const char* cmCommandArgumentParserHelper::ExpandVariable(const char* var) return nullptr; } if (this->FileLine >= 0 && strcmp(var, "CMAKE_CURRENT_LIST_LINE") == 0) { - return this->AddString(std::to_string(this->FileLine)); + std::string line; + cmListFileContext const& top = this->Makefile->GetBacktrace().Top(); + if (top.DeferId) { + line = cmStrCat("DEFERRED:"_s, *top.DeferId); + } else { + line = std::to_string(this->FileLine); + } + return this->AddString(line); } - const char* value = this->Makefile->GetDefinition(var); + cmProp value = this->Makefile->GetDefinition(var); if (!value) { this->Makefile->MaybeWarnUninitialized(var, this->FileName); if (!this->RemoveEmpty) { @@ -101,9 +111,9 @@ const char* cmCommandArgumentParserHelper::ExpandVariable(const char* var) } } if (this->EscapeQuotes && value) { - return this->AddString(cmEscapeQuotes(value)); + return this->AddString(cmEscapeQuotes(*value)); } - return this->AddString(value ? value : ""); + return this->AddString(cmToCStrSafe(value)); } const char* cmCommandArgumentParserHelper::ExpandVariableForAt(const char* var) @@ -205,23 +215,24 @@ bool cmCommandArgumentParserHelper::HandleEscapeSymbol( void cmCommandArgument_SetupEscapes(yyscan_t yyscanner, bool noEscapes); -int cmCommandArgumentParserHelper::ParseString(const char* str, int verb) +int cmCommandArgumentParserHelper::ParseString(std::string const& str, + int verb) { - if (!str) { + if (str.empty()) { return 0; } + this->InputSize = str.size(); this->Verbose = verb; - this->InputBuffer = str; - this->InputBufferPos = 0; - this->CurrentLine = 0; this->Result.clear(); yyscan_t yyscanner; cmCommandArgument_yylex_init(&yyscanner); + auto scanBuf = cmCommandArgument_yy_scan_string(str.c_str(), yyscanner); cmCommandArgument_yyset_extra(this, yyscanner); cmCommandArgument_SetupEscapes(yyscanner, this->NoEscapeMode); int res = cmCommandArgument_yyparse(yyscanner); + cmCommandArgument_yy_delete_buffer(scanBuf, yyscanner); cmCommandArgument_yylex_destroy(yyscanner); if (res != 0) { return 0; @@ -241,25 +252,14 @@ void cmCommandArgumentParserHelper::CleanupParser() this->Variables.clear(); } -int cmCommandArgumentParserHelper::LexInput(char* buf, int maxlen) +void cmCommandArgumentParserHelper::Error(const char* str) { - if (maxlen < 1) { - return 0; - } - if (this->InputBufferPos < this->InputBuffer.size()) { - buf[0] = this->InputBuffer[this->InputBufferPos++]; - if (buf[0] == '\n') { - this->CurrentLine++; - } - return (1); + auto pos = this->InputBufferPos; + auto const isEof = (this->InputSize < this->InputBufferPos); + if (!isEof) { + pos -= this->LastTokenLength; } - buf[0] = '\n'; - return (0); -} -void cmCommandArgumentParserHelper::Error(const char* str) -{ - unsigned long pos = static_cast<unsigned long>(this->InputBufferPos); std::ostringstream ostr; ostr << str << " (" << pos << ")"; this->SetError(ostr.str()); @@ -286,3 +286,9 @@ void cmCommandArgumentParserHelper::SetError(std::string const& msg) this->ErrorString = msg; } } + +void cmCommandArgumentParserHelper::UpdateInputPosition(int const tokenLength) +{ + this->InputBufferPos += tokenLength; + this->LastTokenLength = tokenLength; +} diff --git a/Source/cmCommandArgumentParserHelper.h b/Source/cmCommandArgumentParserHelper.h index b46edcbeab..f79ca2c506 100644 --- a/Source/cmCommandArgumentParserHelper.h +++ b/Source/cmCommandArgumentParserHelper.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCommandArgumentParserHelper_h -#define cmCommandArgumentParserHelper_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -26,7 +25,7 @@ public: cmCommandArgumentParserHelper& operator=( cmCommandArgumentParserHelper const&) = delete; - int ParseString(const char* str, int verb); + int ParseString(std::string const& str, int verb); // For the lexer: void AllocateParserType(cmCommandArgumentParserHelper::ParserType* pt, @@ -34,7 +33,6 @@ public: bool HandleEscapeSymbol(cmCommandArgumentParserHelper::ParserType* pt, char symbol); - int LexInput(char* buf, int maxlen); void Error(const char* str); // For yacc @@ -47,6 +45,8 @@ public: void SetMakefile(const cmMakefile* mf); + void UpdateInputPosition(int tokenLength); + std::string& GetResult() { return this->Result; } void SetLineFile(long line, const char* file); @@ -58,8 +58,9 @@ public: const char* GetError() { return this->ErrorString.c_str(); } private: - std::string::size_type InputBufferPos; - std::string InputBuffer; + std::string::size_type InputBufferPos{ 1 }; + std::string::size_type LastTokenLength{}; + std::string::size_type InputSize{}; std::vector<char> OutputBuffer; void Print(const char* place, const char* str); @@ -76,7 +77,6 @@ private: std::string ErrorString; const char* FileName; long FileLine; - int CurrentLine; int Verbose; bool EscapeQuotes; bool NoEscapeMode; @@ -89,5 +89,3 @@ private: #define YY_EXTRA_TYPE cmCommandArgumentParserHelper* #define YY_DECL \ int cmCommandArgument_yylex(YYSTYPE* yylvalp, yyscan_t yyscanner) - -#endif diff --git a/Source/cmCommands.h b/Source/cmCommands.h index 1f8fafb466..5605430980 100644 --- a/Source/cmCommands.h +++ b/Source/cmCommands.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCommands_h -#define cmCommands_h +#pragma once class cmState; @@ -13,5 +12,3 @@ class cmState; void GetScriptingCommands(cmState* state); void GetProjectCommands(cmState* state); void GetProjectCommandsInScriptMode(cmState* state); - -#endif diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index 051eff6f7e..2b7c9f683f 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -41,7 +41,7 @@ std::vector<std::string> const& cmCommonTargetGenerator::GetConfigNames() const const char* cmCommonTargetGenerator::GetFeature(const std::string& feature, const std::string& config) { - return this->GeneratorTarget->GetFeature(feature, config); + return this->GeneratorTarget->GetFeature(feature, config)->c_str(); } void cmCommonTargetGenerator::AddModuleDefinitionFlag( @@ -55,7 +55,7 @@ void cmCommonTargetGenerator::AddModuleDefinitionFlag( } // TODO: Create a per-language flag variable. - const char* defFileFlag = + cmProp defFileFlag = this->Makefile->GetDefinition("CMAKE_LINK_DEF_FILE_FLAG"); if (!defFileFlag) { return; @@ -64,7 +64,7 @@ void cmCommonTargetGenerator::AddModuleDefinitionFlag( // Append the flag and value. Use ConvertToLinkReference to help // vs6's "cl -link" pass it to the linker. std::string flag = - cmStrCat(defFileFlag, + cmStrCat(*defFileFlag, this->LocalCommonGenerator->ConvertToOutputFormat( linkLineComputer->ConvertToLinkReference(mdi->DefFile), cmOutputConverter::SHELL)); @@ -270,7 +270,7 @@ void cmCommonTargetGenerator::AppendOSXVerFlag(std::string& flags, { // Lookup the flag to specify the version. std::string fvar = cmStrCat("CMAKE_", lang, "_OSX_", name, "_VERSION_FLAG"); - const char* flag = this->Makefile->GetDefinition(fvar); + cmProp flag = this->Makefile->GetDefinition(fvar); // Skip if no such flag. if (!flag) { @@ -288,7 +288,7 @@ void cmCommonTargetGenerator::AppendOSXVerFlag(std::string& flags, if (major > 0 || minor > 0 || patch > 0) { // Append the flag since a non-zero version is specified. std::ostringstream vflag; - vflag << flag << major << "." << minor << "." << patch; + vflag << *flag << major << "." << minor << "." << patch; this->LocalCommonGenerator->AppendFlags(flags, vflag.str()); } } diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h index c3c3a3a88e..fba6b0a5fc 100644 --- a/Source/cmCommonTargetGenerator.h +++ b/Source/cmCommonTargetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCommonTargetGenerator_h -#define cmCommonTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -75,5 +74,3 @@ private: }; std::map<std::string, ByConfig> Configs; }; - -#endif diff --git a/Source/cmComputeComponentGraph.cxx b/Source/cmComputeComponentGraph.cxx index 81cd8785bf..6591fb1d32 100644 --- a/Source/cmComputeComponentGraph.cxx +++ b/Source/cmComputeComponentGraph.cxx @@ -8,6 +8,12 @@ cmComputeComponentGraph::cmComputeComponentGraph(Graph const& input) : InputGraph(input) { +} + +cmComputeComponentGraph::~cmComputeComponentGraph() = default; + +void cmComputeComponentGraph::Compute() +{ // Identify components. this->Tarjan(); @@ -17,8 +23,6 @@ cmComputeComponentGraph::cmComputeComponentGraph(Graph const& input) this->TransferEdges(); } -cmComputeComponentGraph::~cmComputeComponentGraph() = default; - void cmComputeComponentGraph::Tarjan() { int n = static_cast<int>(this->InputGraph.size()); diff --git a/Source/cmComputeComponentGraph.h b/Source/cmComputeComponentGraph.h index 202888cf9c..1d1d134e5c 100644 --- a/Source/cmComputeComponentGraph.h +++ b/Source/cmComputeComponentGraph.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmComputeComponentGraph_h -#define cmComputeComponentGraph_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -31,6 +30,9 @@ public: cmComputeComponentGraph(Graph const& input); ~cmComputeComponentGraph(); + /** Run the computation. */ + void Compute(); + /** Get the adjacency list of the component graph. */ Graph const& GetComponentGraph() const { return this->ComponentGraph; } EdgeList const& GetComponentGraphEdges(int c) const @@ -75,5 +77,3 @@ private: // Connected components. }; - -#endif diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index e9bf5a5cd0..5341e8d8c1 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -5,7 +5,6 @@ #include <algorithm> #include <cassert> #include <cstdio> -#include <cstring> #include <iterator> #include <sstream> #include <utility> @@ -18,6 +17,7 @@ #include "cmListFileCache.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -253,13 +253,13 @@ cmComputeLinkDepends::Compute() // Compute the final set of link entries. // Iterate in reverse order so we can keep only the last occurrence // of a shared library. - std::set<int> emmitted; + std::set<int> emitted; 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. bool uniquify = t && t->GetType() == cmStateEnums::SHARED_LIBRARY; - if (!uniquify || emmitted.insert(i).second) { + if (!uniquify || emitted.insert(i).second) { this->FinalLinkEntries.push_back(e); } } @@ -315,9 +315,9 @@ int cmComputeLinkDepends::AddLinkEntry(cmLinkItem const& item) } else { // Look for an old-style <item>_LIB_DEPENDS variable. std::string var = cmStrCat(entry.Item.Value, "_LIB_DEPENDS"); - if (const char* val = this->Makefile->GetDefinition(var)) { + if (cmProp val = this->Makefile->GetDefinition(var)) { // The item dependencies are known. Follow them. - BFSEntry qe = { index, val }; + BFSEntry qe = { index, val->c_str() }; this->BFSQueue.push(qe); } else if (!entry.IsFlag) { // The item dependencies are not known. We need to infer them. @@ -454,10 +454,10 @@ void cmComputeLinkDepends::AddVarLinkEntries(int depender_index, // lower. if (!haveLLT) { std::string var = cmStrCat(d, "_LINK_TYPE"); - if (const char* val = this->Makefile->GetDefinition(var)) { - if (strcmp(val, "debug") == 0) { + if (cmProp val = this->Makefile->GetDefinition(var)) { + if (*val == "debug") { llt = DEBUG_LibraryType; - } else if (strcmp(val, "optimized") == 0) { + } else if (*val == "optimized") { llt = OPTIMIZED_LibraryType; } } @@ -626,6 +626,7 @@ void cmComputeLinkDepends::OrderLinkEntires() // constraints disallow it. this->CCG = cm::make_unique<cmComputeComponentGraph>(this->EntryConstraintGraph); + this->CCG->Compute(); // The component graph is guaranteed to be acyclic. Start a DFS // from every entry to compute a topological order for the diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h index e806dff99e..902500aaf3 100644 --- a/Source/cmComputeLinkDepends.h +++ b/Source/cmComputeLinkDepends.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmComputeLinkDepends_h -#define cmComputeLinkDepends_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -160,5 +159,3 @@ private: bool DebugMode; bool OldLinkDirMode; }; - -#endif diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 4c5f57d059..201a9d9bf6 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -4,7 +4,6 @@ #include <algorithm> #include <cctype> -#include <cstring> #include <sstream> #include <utility> @@ -284,27 +283,28 @@ cmComputeLinkInformation::cmComputeLinkInformation( this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) { std::string loader_flag_var = cmStrCat("CMAKE_SHARED_MODULE_LOADER_", this->LinkLanguage, "_FLAG"); - this->LoaderFlag = this->Makefile->GetDefinition(loader_flag_var); + this->LoaderFlag = + cmToCStr(this->Makefile->GetDefinition(loader_flag_var)); } // Get options needed to link libraries. - if (const char* flag = this->Makefile->GetDefinition( + if (cmProp flag = this->Makefile->GetDefinition( "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FLAG")) { - this->LibLinkFlag = flag; + this->LibLinkFlag = *flag; } else { this->LibLinkFlag = this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG"); } - if (const char* flag = this->Makefile->GetDefinition( + if (cmProp flag = this->Makefile->GetDefinition( "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FILE_FLAG")) { - this->LibLinkFileFlag = flag; + this->LibLinkFileFlag = *flag; } else { this->LibLinkFileFlag = this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FILE_FLAG"); } - if (const char* suffix = this->Makefile->GetDefinition( + if (cmProp suffix = this->Makefile->GetDefinition( "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_SUFFIX")) { - this->LibLinkSuffix = suffix; + this->LibLinkSuffix = *suffix; } else { this->LibLinkSuffix = this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"); @@ -515,7 +515,7 @@ bool cmComputeLinkInformation::Compute() // Restore the target link type so the correct system runtime // libraries are found. cmProp lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC"); - if (lss && cmIsOn(*lss)) { + if (cmIsOn(lss)) { this->SetCurrentLinkType(LinkStatic); } else { this->SetCurrentLinkType(this->StartLinkType); @@ -593,9 +593,9 @@ void cmComputeLinkInformation::AddRuntimeLinkLibrary(std::string const& lang) if (runtimeLibrary.empty()) { return; } - if (const char* runtimeLinkOptions = this->Makefile->GetDefinition( + if (cmProp runtimeLinkOptions = this->Makefile->GetDefinition( "CMAKE_" + lang + "_RUNTIME_LIBRARY_LINK_OPTIONS_" + runtimeLibrary)) { - std::vector<std::string> libsVec = cmExpandedList(runtimeLinkOptions); + std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions); for (std::string const& i : libsVec) { if (!cm::contains(this->ImplicitLinkLibs, i)) { this->AddItem(i, nullptr); @@ -609,8 +609,8 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang) // Add libraries for this language that are not implied by the // linker language. std::string libVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_LIBRARIES"); - if (const char* libs = this->Makefile->GetDefinition(libVar)) { - std::vector<std::string> libsVec = cmExpandedList(libs); + if (cmProp libs = this->Makefile->GetDefinition(libVar)) { + std::vector<std::string> libsVec = cmExpandedList(*libs); for (std::string const& i : libsVec) { if (!cm::contains(this->ImplicitLinkLibs, i)) { this->AddItem(i, nullptr); @@ -621,8 +621,8 @@ void cmComputeLinkInformation::AddImplicitLinkInfo(std::string const& lang) // Add linker search paths for this language that are not // implied by the linker language. std::string dirVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_DIRECTORIES"); - if (const char* dirs = this->Makefile->GetDefinition(dirVar)) { - std::vector<std::string> dirsVec = cmExpandedList(dirs); + if (cmProp dirs = this->Makefile->GetDefinition(dirVar)) { + std::vector<std::string> dirsVec = cmExpandedList(*dirs); this->OrderLinkerSearchPath->AddLanguageDirectories(dirsVec); } } @@ -800,8 +800,8 @@ void cmComputeLinkInformation::ComputeLinkTypeInfo() this->LinkTypeEnabled = false; // Lookup link type selection flags. - const char* static_link_type_flag = nullptr; - const char* shared_link_type_flag = nullptr; + cmProp static_link_type_flag = nullptr; + cmProp shared_link_type_flag = nullptr; const char* target_type_str = nullptr; switch (this->Target->GetType()) { case cmStateEnums::EXECUTABLE: @@ -832,16 +832,15 @@ void cmComputeLinkInformation::ComputeLinkTypeInfo() // We can support link type switching only if all needed flags are // known. - if (static_link_type_flag && *static_link_type_flag && - shared_link_type_flag && *shared_link_type_flag) { + if (cmNonempty(static_link_type_flag) && cmNonempty(shared_link_type_flag)) { this->LinkTypeEnabled = true; - this->StaticLinkTypeFlag = static_link_type_flag; - this->SharedLinkTypeFlag = shared_link_type_flag; + this->StaticLinkTypeFlag = *static_link_type_flag; + this->SharedLinkTypeFlag = *shared_link_type_flag; } // Lookup the starting link type from the target (linked statically?). cmProp lss = this->Target->GetProperty("LINK_SEARCH_START_STATIC"); - this->StartLinkType = (lss && cmIsOn(*lss)) ? LinkStatic : LinkShared; + this->StartLinkType = cmIsOn(lss) ? LinkStatic : LinkShared; this->CurrentLinkType = this->StartLinkType; } @@ -849,31 +848,30 @@ void cmComputeLinkInformation::ComputeItemParserInfo() { // Get possible library name prefixes. cmMakefile* mf = this->Makefile; - this->AddLinkPrefix(mf->GetDefinition("CMAKE_STATIC_LIBRARY_PREFIX")); - this->AddLinkPrefix(mf->GetDefinition("CMAKE_SHARED_LIBRARY_PREFIX")); + this->AddLinkPrefix(mf->GetSafeDefinition("CMAKE_STATIC_LIBRARY_PREFIX")); + this->AddLinkPrefix(mf->GetSafeDefinition("CMAKE_SHARED_LIBRARY_PREFIX")); // Import library names should be matched and treated as shared // libraries for the purposes of linking. - this->AddLinkExtension(mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX"), + this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX"), LinkShared); - this->AddLinkExtension(mf->GetDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"), + this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"), LinkStatic); - this->AddLinkExtension(mf->GetDefinition("CMAKE_SHARED_LIBRARY_SUFFIX"), + this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_SHARED_LIBRARY_SUFFIX"), LinkShared); - this->AddLinkExtension(mf->GetDefinition("CMAKE_LINK_LIBRARY_SUFFIX"), + this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"), LinkUnknown); - if (const char* linkSuffixes = - mf->GetDefinition("CMAKE_EXTRA_LINK_EXTENSIONS")) { - std::vector<std::string> linkSuffixVec = cmExpandedList(linkSuffixes); + if (cmProp linkSuffixes = mf->GetDefinition("CMAKE_EXTRA_LINK_EXTENSIONS")) { + std::vector<std::string> linkSuffixVec = cmExpandedList(*linkSuffixes); for (std::string const& i : linkSuffixVec) { - this->AddLinkExtension(i.c_str(), LinkUnknown); + this->AddLinkExtension(i, LinkUnknown); } } - if (const char* sharedSuffixes = + if (cmProp sharedSuffixes = mf->GetDefinition("CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES")) { - std::vector<std::string> sharedSuffixVec = cmExpandedList(sharedSuffixes); + std::vector<std::string> sharedSuffixVec = cmExpandedList(*sharedSuffixes); for (std::string const& i : sharedSuffixVec) { - this->AddLinkExtension(i.c_str(), LinkShared); + this->AddLinkExtension(i, LinkShared); } } @@ -903,7 +901,7 @@ void cmComputeLinkInformation::ComputeItemParserInfo() #ifdef CM_COMPUTE_LINK_INFO_DEBUG fprintf(stderr, "any regex [%s]\n", reg_any.c_str()); #endif - this->ExtractAnyLibraryName.compile(reg_any.c_str()); + this->ExtractAnyLibraryName.compile(reg_any); // Create a regex to match static library names. if (!this->StaticLinkExtensions.empty()) { @@ -912,7 +910,7 @@ void cmComputeLinkInformation::ComputeItemParserInfo() #ifdef CM_COMPUTE_LINK_INFO_DEBUG fprintf(stderr, "static regex [%s]\n", reg_static.c_str()); #endif - this->ExtractStaticLibraryName.compile(reg_static.c_str()); + this->ExtractStaticLibraryName.compile(reg_static); } // Create a regex to match shared library names. @@ -924,20 +922,21 @@ void cmComputeLinkInformation::ComputeItemParserInfo() #ifdef CM_COMPUTE_LINK_INFO_DEBUG fprintf(stderr, "shared regex [%s]\n", reg_shared.c_str()); #endif - this->ExtractSharedLibraryName.compile(reg_shared.c_str()); + this->ExtractSharedLibraryName.compile(reg_shared); } } -void cmComputeLinkInformation::AddLinkPrefix(const char* p) +void cmComputeLinkInformation::AddLinkPrefix(std::string const& p) { - if (p && *p) { + if (!p.empty()) { this->LinkPrefixes.insert(p); } } -void cmComputeLinkInformation::AddLinkExtension(const char* e, LinkType type) +void cmComputeLinkInformation::AddLinkExtension(std::string const& e, + LinkType type) { - if (e && *e) { + if (!e.empty()) { if (type == LinkStatic) { this->StaticLinkExtensions.emplace_back(e); } @@ -962,7 +961,7 @@ std::string cmComputeLinkInformation::CreateExtensionRegex( // Store this extension choice with the "." escaped. libext += "\\"; #if defined(_WIN32) && !defined(__CYGWIN__) - libext += this->NoCaseExpression(i.c_str()); + libext += this->NoCaseExpression(i); #else libext += i; #endif @@ -980,21 +979,19 @@ std::string cmComputeLinkInformation::CreateExtensionRegex( return libext; } -std::string cmComputeLinkInformation::NoCaseExpression(const char* str) +std::string cmComputeLinkInformation::NoCaseExpression(std::string const& str) { std::string ret; - ret.reserve(strlen(str) * 4); - const char* s = str; - while (*s) { - if (*s == '.') { - ret += *s; + ret.reserve(str.size() * 4); + for (char c : str) { + if (c == '.') { + ret += c; } else { ret += '['; - ret += static_cast<char>(tolower(*s)); - ret += static_cast<char>(toupper(*s)); + ret += static_cast<char>(tolower(c)); + ret += static_cast<char>(toupper(c)); ret += ']'; } - s++; } return ret; } @@ -1296,11 +1293,17 @@ void cmComputeLinkInformation::AddFrameworkItem(std::string const& item) // add runtime information this->AddLibraryRuntimeInfo(full_fw); - // Add the item using the -framework option. - this->Items.emplace_back(std::string("-framework"), false); - cmOutputConverter converter(this->Makefile->GetStateSnapshot()); - fw = converter.EscapeForShell(fw); - this->Items.emplace_back(fw, false); + if (this->GlobalGenerator->IsXcode()) { + // Add framework path - it will be handled by Xcode after it's added to + // "Link Binary With Libraries" build phase + this->Items.emplace_back(item, true); + } else { + // Add the item using the -framework option. + this->Items.emplace_back(std::string("-framework"), false); + cmOutputConverter converter(this->Makefile->GetStateSnapshot()); + fw = converter.EscapeForShell(fw); + this->Items.emplace_back(fw, false); + } } void cmComputeLinkInformation::AddDirectoryItem(std::string const& item) @@ -1555,10 +1558,10 @@ void cmComputeLinkInformation::LoadImplicitLinkInfo() // Append library architecture to all implicit platform directories // and add them to the set - if (const char* libraryArch = + if (cmProp libraryArch = this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) { for (std::string const& i : implicitDirVec) { - this->ImplicitLinkDirs.insert(i + "/" + libraryArch); + this->ImplicitLinkDirs.insert(i + "/" + *libraryArch); } } @@ -1688,7 +1691,7 @@ void cmComputeLinkInformation::AddLibraryRuntimeInfo( } } -static void cmCLI_ExpandListUnique(const char* str, +static void cmCLI_ExpandListUnique(std::string const& str, std::vector<std::string>& out, std::set<std::string>& emitted) { @@ -1735,7 +1738,7 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, if (use_install_rpath) { std::string install_rpath; this->Target->GetInstallRPATH(this->Config, install_rpath); - cmCLI_ExpandListUnique(install_rpath.c_str(), runtimeDirs, emitted); + cmCLI_ExpandListUnique(install_rpath, runtimeDirs, emitted); } if (use_build_rpath) { // Add directories explicitly specified by user @@ -1743,19 +1746,18 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, if (this->Target->GetBuildRPATH(this->Config, build_rpath)) { // This will not resolve entries to use $ORIGIN, the user is expected to // do that if necessary. - cmCLI_ExpandListUnique(build_rpath.c_str(), runtimeDirs, emitted); + cmCLI_ExpandListUnique(build_rpath, runtimeDirs, emitted); } } if (use_build_rpath || use_link_rpath) { std::string rootPath; - if (const char* sysrootLink = + if (cmProp sysrootLink = this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) { - rootPath = sysrootLink; + rootPath = *sysrootLink; } else { rootPath = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); } - const char* stagePath = - this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX"); + cmProp stagePath = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX"); std::string const& installPrefix = this->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); cmSystemTools::ConvertToUnixSlashes(rootPath); @@ -1769,8 +1771,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, std::string d = ri; if (!rootPath.empty() && cmHasPrefix(d, rootPath)) { d.erase(0, rootPath.size()); - } else if (stagePath && *stagePath && cmHasPrefix(d, stagePath)) { - d.erase(0, strlen(stagePath)); + } else if (cmNonempty(stagePath) && cmHasPrefix(d, *stagePath)) { + d.erase(0, (*stagePath).size()); d = cmStrCat(installPrefix, '/', d); cmSystemTools::ConvertToUnixSlashes(d); } else if (use_relative_build_rpath) { @@ -1800,8 +1802,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, std::string d = ri; if (!rootPath.empty() && cmHasPrefix(d, rootPath)) { d.erase(0, rootPath.size()); - } else if (stagePath && *stagePath && cmHasPrefix(d, stagePath)) { - d.erase(0, strlen(stagePath)); + } else if (cmNonempty(stagePath) && cmHasPrefix(d, *stagePath)) { + d.erase(0, (*stagePath).size()); d = cmStrCat(installPrefix, '/', d); cmSystemTools::ConvertToUnixSlashes(d); } @@ -1823,8 +1825,8 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, "CMAKE_" + li + "_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH"; if (this->Makefile->IsOn(useVar)) { std::string dirVar = "CMAKE_" + li + "_IMPLICIT_LINK_DIRECTORIES"; - if (const char* dirs = this->Makefile->GetDefinition(dirVar)) { - cmCLI_ExpandListUnique(dirs, runtimeDirs, emitted); + if (cmProp dirs = this->Makefile->GetDefinition(dirVar)) { + cmCLI_ExpandListUnique(*dirs, runtimeDirs, emitted); } } } @@ -1832,7 +1834,7 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs, // Add runtime paths required by the platform to always be // present. This is done even when skipping rpath support. - cmCLI_ExpandListUnique(this->RuntimeAlways.c_str(), runtimeDirs, emitted); + cmCLI_ExpandListUnique(this->RuntimeAlways, runtimeDirs, emitted); } std::string cmComputeLinkInformation::GetRPathString(bool for_install) const diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h index e50d369beb..543b6d7e1b 100644 --- a/Source/cmComputeLinkInformation.h +++ b/Source/cmComputeLinkInformation.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmComputeLinkInformation_h -#define cmComputeLinkInformation_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -144,11 +143,11 @@ private: cmsys::RegularExpression ExtractSharedLibraryName; cmsys::RegularExpression ExtractAnyLibraryName; std::string SharedRegexString; - void AddLinkPrefix(const char* p); - void AddLinkExtension(const char* e, LinkType type); + void AddLinkPrefix(std::string const& p); + void AddLinkExtension(std::string const& e, LinkType type); std::string CreateExtensionRegex(std::vector<std::string> const& exts, LinkType type); - std::string NoCaseExpression(const char* str); + std::string NoCaseExpression(std::string const& str); // Handling of link items. void AddTargetItem(BT<std::string> const& item, @@ -209,5 +208,3 @@ private: const cmGeneratorTarget* target); void AddLibraryRuntimeInfo(std::string const& fullPath); }; - -#endif diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx index 41f5346356..1f22ce6342 100644 --- a/Source/cmComputeTargetDepends.cxx +++ b/Source/cmComputeTargetDepends.cxx @@ -17,10 +17,12 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStateTypes.h" +#include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetDepend.h" @@ -115,18 +117,32 @@ bool cmComputeTargetDepends::Compute() if (this->DebugMode) { this->DisplayGraph(this->InitialGraph, "initial"); } + cmComputeComponentGraph ccg1(this->InitialGraph); + ccg1.Compute(); + if (!this->CheckComponents(ccg1)) { + return false; + } + + // Compute the intermediate graph. + this->CollectSideEffects(); + this->ComputeIntermediateGraph(); + if (this->DebugMode) { + this->DisplaySideEffects(); + this->DisplayGraph(this->IntermediateGraph, "intermediate"); + } // Identify components. - cmComputeComponentGraph ccg(this->InitialGraph); + cmComputeComponentGraph ccg2(this->IntermediateGraph); + ccg2.Compute(); if (this->DebugMode) { - this->DisplayComponents(ccg); + this->DisplayComponents(ccg2, "intermediate"); } - if (!this->CheckComponents(ccg)) { + if (!this->CheckComponents(ccg2)) { return false; } // Compute the final dependency graph. - if (!this->ComputeFinalDepends(ccg)) { + if (!this->ComputeFinalDepends(ccg2)) { return false; } if (this->DebugMode) { @@ -184,7 +200,7 @@ void cmComputeTargetDepends::CollectTargetDepends(int depender_index) { // Get the depender. cmGeneratorTarget const* depender = this->Targets[depender_index]; - if (depender->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!depender->IsInBuildSystem()) { return; } @@ -196,18 +212,20 @@ void cmComputeTargetDepends::CollectTargetDepends(int depender_index) std::set<cmLinkItem> emitted; std::vector<std::string> const& configs = - depender->Makefile->GetGeneratorConfigs(); + depender->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); for (std::string const& it : configs) { - cmLinkImplementation const* impl = depender->GetLinkImplementation(it); - // A target should not depend on itself. emitted.insert(cmLinkItem(depender, false, cmListFileBacktrace())); emitted.insert(cmLinkItem(depender, true, cmListFileBacktrace())); - for (cmLinkImplItem const& lib : impl->Libraries) { - // Don't emit the same library twice for this target. - if (emitted.insert(lib).second) { - this->AddTargetDepend(depender_index, lib, true, false); - this->AddInterfaceDepends(depender_index, lib, it, emitted); + + if (cmLinkImplementation const* impl = + depender->GetLinkImplementation(it)) { + for (cmLinkImplItem const& lib : impl->Libraries) { + // Don't emit the same library twice for this target. + if (emitted.insert(lib).second) { + this->AddTargetDepend(depender_index, lib, true, false); + this->AddInterfaceDepends(depender_index, lib, it, emitted); + } } } @@ -356,10 +374,9 @@ void cmComputeTargetDepends::AddTargetDepend( int depender_index, cmGeneratorTarget const* dependee, cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross) { - if (dependee->IsImported() || - dependee->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - // Skip IMPORTED and INTERFACE targets but follow their utility - // dependencies. + if (!dependee->IsInBuildSystem()) { + // Skip targets that are not in the buildsystem but follow their + // utility dependencies. std::set<cmLinkItem> const& utils = dependee->GetUtilityItems(); for (cmLinkItem const& i : utils) { if (cmGeneratorTarget const* transitive_dependee = i.Target) { @@ -380,6 +397,111 @@ void cmComputeTargetDepends::AddTargetDepend( } } +void cmComputeTargetDepends::CollectSideEffects() +{ + this->SideEffects.resize(0); + this->SideEffects.resize(this->InitialGraph.size()); + + int n = static_cast<int>(this->InitialGraph.size()); + std::set<int> visited; + for (int i = 0; i < n; ++i) { + this->CollectSideEffectsForTarget(visited, i); + } +} + +void cmComputeTargetDepends::CollectSideEffectsForTarget( + std::set<int>& visited, int depender_index) +{ + if (!visited.count(depender_index)) { + auto& se = this->SideEffects[depender_index]; + visited.insert(depender_index); + this->Targets[depender_index]->AppendCustomCommandSideEffects( + se.CustomCommandSideEffects); + this->Targets[depender_index]->AppendLanguageSideEffects( + se.LanguageSideEffects); + + for (auto const& edge : this->InitialGraph[depender_index]) { + this->CollectSideEffectsForTarget(visited, edge); + auto const& dse = this->SideEffects[edge]; + se.CustomCommandSideEffects.insert(dse.CustomCommandSideEffects.cbegin(), + dse.CustomCommandSideEffects.cend()); + for (auto const& it : dse.LanguageSideEffects) { + se.LanguageSideEffects[it.first].insert(it.second.cbegin(), + it.second.cend()); + } + } + } +} + +void cmComputeTargetDepends::ComputeIntermediateGraph() +{ + this->IntermediateGraph.resize(0); + this->IntermediateGraph.resize(this->InitialGraph.size()); + + int n = static_cast<int>(this->InitialGraph.size()); + for (int i = 0; i < n; ++i) { + auto const& initialEdges = this->InitialGraph[i]; + auto& intermediateEdges = this->IntermediateGraph[i]; + cmGeneratorTarget const* gt = this->Targets[i]; + if (gt->GetType() != cmStateEnums::STATIC_LIBRARY && + gt->GetType() != cmStateEnums::OBJECT_LIBRARY) { + intermediateEdges = initialEdges; + } else { + if (cmProp optimizeDependencies = + gt->GetProperty("OPTIMIZE_DEPENDENCIES")) { + if (cmIsOn(optimizeDependencies)) { + this->OptimizeLinkDependencies(gt, intermediateEdges, initialEdges); + } else { + intermediateEdges = initialEdges; + } + } else { + intermediateEdges = initialEdges; + } + } + } +} + +void cmComputeTargetDepends::OptimizeLinkDependencies( + cmGeneratorTarget const* gt, cmGraphEdgeList& outputEdges, + cmGraphEdgeList const& inputEdges) +{ + std::set<int> emitted; + for (auto const& edge : inputEdges) { + if (edge.IsStrong()) { + // Preserve strong edges + outputEdges.push_back(edge); + } else { + auto const& dse = this->SideEffects[edge]; + + // Add edges that have custom command side effects + for (cmGeneratorTarget const* dep : dse.CustomCommandSideEffects) { + auto index = this->TargetIndex[dep]; + if (!emitted.count(index)) { + emitted.insert(index); + outputEdges.push_back( + cmGraphEdge(index, false, edge.IsCross(), edge.GetBacktrace())); + } + } + + // Add edges that have language side effects for languages we + // care about + for (auto const& lang : gt->GetAllConfigCompileLanguages()) { + auto it = dse.LanguageSideEffects.find(lang); + if (it != dse.LanguageSideEffects.end()) { + for (cmGeneratorTarget const* dep : it->second) { + auto index = this->TargetIndex[dep]; + if (!emitted.count(index)) { + emitted.insert(index); + outputEdges.push_back(cmGraphEdge(index, false, edge.IsCross(), + edge.GetBacktrace())); + } + } + } + } + } + } +} + void cmComputeTargetDepends::DisplayGraph(Graph const& graph, const std::string& name) { @@ -400,10 +522,39 @@ void cmComputeTargetDepends::DisplayGraph(Graph const& graph, fprintf(stderr, "\n"); } +void cmComputeTargetDepends::DisplaySideEffects() +{ + fprintf(stderr, "The side effects are:\n"); + int n = static_cast<int>(SideEffects.size()); + for (int depender_index = 0; depender_index < n; ++depender_index) { + cmGeneratorTarget const* depender = this->Targets[depender_index]; + fprintf(stderr, "target %d is [%s]\n", depender_index, + depender->GetName().c_str()); + if (!this->SideEffects[depender_index].CustomCommandSideEffects.empty()) { + fprintf(stderr, " custom commands\n"); + for (auto const* gt : + this->SideEffects[depender_index].CustomCommandSideEffects) { + fprintf(stderr, " from target %d [%s]\n", this->TargetIndex[gt], + gt->GetName().c_str()); + } + } + for (auto const& it : + this->SideEffects[depender_index].LanguageSideEffects) { + fprintf(stderr, " language %s\n", it.first.c_str()); + for (auto const* gt : it.second) { + fprintf(stderr, " from target %d [%s]\n", this->TargetIndex[gt], + gt->GetName().c_str()); + } + } + } + fprintf(stderr, "\n"); +} + void cmComputeTargetDepends::DisplayComponents( - cmComputeComponentGraph const& ccg) + cmComputeComponentGraph const& ccg, const std::string& name) { - fprintf(stderr, "The strongly connected components are:\n"); + fprintf(stderr, "The strongly connected components for the %s graph are:\n", + name.c_str()); std::vector<NodeList> const& components = ccg.GetComponents(); int n = static_cast<int>(components.size()); for (int c = 0; c < n; ++c) { diff --git a/Source/cmComputeTargetDepends.h b/Source/cmComputeTargetDepends.h index e0d625f9a1..3517844d6b 100644 --- a/Source/cmComputeTargetDepends.h +++ b/Source/cmComputeTargetDepends.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmComputeTargetDepends_h -#define cmComputeTargetDepends_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -42,6 +41,13 @@ public: cmTargetDependSet& deps); private: + struct TargetSideEffects + { + std::set<cmGeneratorTarget const*> CustomCommandSideEffects; + std::map<std::string, std::set<cmGeneratorTarget const*>> + LanguageSideEffects; + }; + void CollectTargets(); void CollectDepends(); void CollectTargetDepends(int depender_index); @@ -50,6 +56,12 @@ private: void AddTargetDepend(int depender_index, cmGeneratorTarget const* dependee, cmListFileBacktrace const& dependee_backtrace, bool linking, bool cross); + void CollectSideEffects(); + void CollectSideEffectsForTarget(std::set<int>& visited, int depender_index); + void ComputeIntermediateGraph(); + void OptimizeLinkDependencies(cmGeneratorTarget const* gt, + cmGraphEdgeList& outputEdges, + cmGraphEdgeList const& inputEdges); bool ComputeFinalDepends(cmComputeComponentGraph const& ccg); void AddInterfaceDepends(int depender_index, cmLinkItem const& dependee_name, const std::string& config, @@ -74,11 +86,15 @@ private: using EdgeList = cmGraphEdgeList; using Graph = cmGraphAdjacencyList; Graph InitialGraph; + Graph IntermediateGraph; Graph FinalGraph; + std::vector<TargetSideEffects> SideEffects; void DisplayGraph(Graph const& graph, const std::string& name); + void DisplaySideEffects(); // Deal with connected components. - void DisplayComponents(cmComputeComponentGraph const& ccg); + void DisplayComponents(cmComputeComponentGraph const& ccg, + const std::string& name); bool CheckComponents(cmComputeComponentGraph const& ccg); void ComplainAboutBadComponent(cmComputeComponentGraph const& ccg, int c, bool strong = false); @@ -88,5 +104,3 @@ private: bool IntraComponent(std::vector<int> const& cmap, int c, int i, int* head, std::set<int>& emitted, std::set<int>& visited); }; - -#endif diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx index 7a3a3e8ac8..14f10bd747 100644 --- a/Source/cmConditionEvaluator.cxx +++ b/Source/cmConditionEvaluator.cxx @@ -4,7 +4,6 @@ #include <cstdio> #include <cstdlib> -#include <cstring> #include <functional> #include <sstream> #include <utility> @@ -15,6 +14,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -56,10 +56,8 @@ static std::string const keyVERSION_LESS = "VERSION_LESS"; static std::string const keyVERSION_LESS_EQUAL = "VERSION_LESS_EQUAL"; cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile, - cmListFileContext context, cmListFileBacktrace bt) : Makefile(makefile) - , ExecutionContext(std::move(context)) , Backtrace(std::move(bt)) , Policy12Status(makefile.GetPolicyStatus(cmPolicies::CMP0012)) , Policy54Status(makefile.GetPolicyStatus(cmPolicies::CMP0054)) @@ -134,7 +132,7 @@ bool cmConditionEvaluator::IsTrue( } //========================================================================= -const char* cmConditionEvaluator::GetDefinitionIfUnquoted( +cmProp cmConditionEvaluator::GetDefinitionIfUnquoted( cmExpandedCommandArgument const& argument) const { if ((this->Policy54Status != cmPolicies::WARN && @@ -143,12 +141,11 @@ const char* cmConditionEvaluator::GetDefinitionIfUnquoted( return nullptr; } - const char* def = this->Makefile.GetDefinition(argument.GetValue()); + cmProp def = this->Makefile.GetDefinition(argument.GetValue()); if (def && argument.WasQuoted() && this->Policy54Status == cmPolicies::WARN) { - if (!this->Makefile.HasCMP0054AlreadyBeenReported( - this->ExecutionContext)) { + if (!this->Makefile.HasCMP0054AlreadyBeenReported(this->Backtrace.Top())) { std::ostringstream e; e << (cmPolicies::GetPolicyWarning(cmPolicies::CMP0054)) << "\n"; e << "Quoted variables like \"" << argument.GetValue() @@ -165,13 +162,13 @@ const char* cmConditionEvaluator::GetDefinitionIfUnquoted( } //========================================================================= -const char* cmConditionEvaluator::GetVariableOrString( +cmProp cmConditionEvaluator::GetVariableOrString( const cmExpandedCommandArgument& argument) const { - const char* def = this->GetDefinitionIfUnquoted(argument); + cmProp def = this->GetDefinitionIfUnquoted(argument); if (!def) { - def = argument.c_str(); + def = &argument.GetValue(); } return def; @@ -191,8 +188,7 @@ bool cmConditionEvaluator::IsKeyword(std::string const& keyword, if (isKeyword && argument.WasQuoted() && this->Policy54Status == cmPolicies::WARN) { - if (!this->Makefile.HasCMP0054AlreadyBeenReported( - this->ExecutionContext)) { + if (!this->Makefile.HasCMP0054AlreadyBeenReported(this->Backtrace.Top())) { std::ostringstream e; e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0054) << "\n"; e << "Quoted keywords like \"" << argument.GetValue() @@ -231,7 +227,7 @@ bool cmConditionEvaluator::GetBooleanValue( // Check for numbers. if (!arg.empty()) { char* end; - double d = strtod(arg.c_str(), &end); + double d = strtod(arg.GetValue().c_str(), &end); if (*end == '\0') { // The whole string is a number. Use C conversion to bool. return static_cast<bool>(d); @@ -239,7 +235,7 @@ bool cmConditionEvaluator::GetBooleanValue( } // Check definition. - const char* def = this->GetDefinitionIfUnquoted(arg); + cmProp def = this->GetDefinitionIfUnquoted(arg); return !cmIsOff(def); } @@ -256,13 +252,13 @@ bool cmConditionEvaluator::GetBooleanValueOld( if (arg == "1") { return true; } - const char* def = this->GetDefinitionIfUnquoted(arg); + cmProp def = this->GetDefinitionIfUnquoted(arg); return !cmIsOff(def); } // Old GetVariableOrNumber behavior. - const char* def = this->GetDefinitionIfUnquoted(arg); - if (!def && atoi(arg.c_str())) { - def = arg.c_str(); + cmProp def = this->GetDefinitionIfUnquoted(arg); + if (!def && atoi(arg.GetValue().c_str())) { + def = &arg.GetValue(); } return !cmIsOff(def); } @@ -434,36 +430,38 @@ bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&, this->IncrementArguments(newArgs, argP1, argP2); // does a file exist if (this->IsKeyword(keyEXISTS, *arg) && argP1 != newArgs.end()) { - this->HandlePredicate(cmSystemTools::FileExists(argP1->c_str()), + this->HandlePredicate(cmSystemTools::FileExists(argP1->GetValue()), reducible, arg, newArgs, argP1, argP2); } // does a directory with this name exist if (this->IsKeyword(keyIS_DIRECTORY, *arg) && argP1 != newArgs.end()) { - this->HandlePredicate(cmSystemTools::FileIsDirectory(argP1->c_str()), - reducible, arg, newArgs, argP1, argP2); + this->HandlePredicate( + cmSystemTools::FileIsDirectory(argP1->GetValue()), reducible, arg, + newArgs, argP1, argP2); } // does a symlink with this name exist if (this->IsKeyword(keyIS_SYMLINK, *arg) && argP1 != newArgs.end()) { - this->HandlePredicate(cmSystemTools::FileIsSymlink(argP1->c_str()), + this->HandlePredicate(cmSystemTools::FileIsSymlink(argP1->GetValue()), reducible, arg, newArgs, argP1, argP2); } // is the given path an absolute path ? if (this->IsKeyword(keyIS_ABSOLUTE, *arg) && argP1 != newArgs.end()) { - this->HandlePredicate(cmSystemTools::FileIsFullPath(argP1->c_str()), + this->HandlePredicate(cmSystemTools::FileIsFullPath(argP1->GetValue()), reducible, arg, newArgs, argP1, argP2); } // does a command exist if (this->IsKeyword(keyCOMMAND, *arg) && argP1 != newArgs.end()) { cmState::Command command = - this->Makefile.GetState()->GetCommand(argP1->c_str()); + this->Makefile.GetState()->GetCommand(argP1->GetValue()); this->HandlePredicate(command != nullptr, reducible, arg, newArgs, argP1, argP2); } // does a policy exist if (this->IsKeyword(keyPOLICY, *arg) && argP1 != newArgs.end()) { cmPolicies::PolicyID pid; - this->HandlePredicate(cmPolicies::GetPolicyID(argP1->c_str(), pid), - reducible, arg, newArgs, argP1, argP2); + this->HandlePredicate( + cmPolicies::GetPolicyID(argP1->GetValue().c_str(), pid), reducible, + arg, newArgs, argP1, argP2); } // does a target exist if (this->IsKeyword(keyTARGET, *arg) && argP1 != newArgs.end()) { @@ -475,7 +473,7 @@ bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&, if (this->Policy64Status != cmPolicies::OLD && this->Policy64Status != cmPolicies::WARN) { if (this->IsKeyword(keyTEST, *arg) && argP1 != newArgs.end()) { - const cmTest* haveTest = this->Makefile.GetTest(argP1->c_str()); + const cmTest* haveTest = this->Makefile.GetTest(argP1->GetValue()); this->HandlePredicate(haveTest != nullptr, reducible, arg, newArgs, argP1, argP2); } @@ -522,8 +520,8 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs, { int reducible; std::string def_buf; - const char* def; - const char* def2; + cmProp def; + cmProp def2; do { reducible = 0; auto arg = newArgs.begin(); @@ -534,15 +532,16 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs, this->IncrementArguments(newArgs, argP1, argP2); if (argP1 != newArgs.end() && argP2 != newArgs.end() && IsKeyword(keyMATCHES, *argP1)) { - def = this->GetVariableOrString(*arg); - if (def != arg->c_str() // yes, we compare the pointer value - && cmHasLiteralPrefix(arg->GetValue(), "CMAKE_MATCH_")) { + def = this->GetDefinitionIfUnquoted(*arg); + if (!def) { + def = &arg->GetValue(); + } else if (cmHasLiteralPrefix(arg->GetValue(), "CMAKE_MATCH_")) { // The string to match is owned by our match result variables. // Move it to our own buffer before clearing them. - def_buf = def; - def = def_buf.c_str(); + def_buf = *def; + def = &def_buf; } - const char* rex = argP2->c_str(); + const std::string& rex = argP2->GetValue(); this->Makefile.ClearMatches(); cmsys::RegularExpression regEntry; if (!regEntry.compile(rex)) { @@ -552,7 +551,7 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs, status = MessageType::FATAL_ERROR; return false; } - if (regEntry.find(def)) { + if (regEntry.find(*def)) { this->Makefile.StoreMatches(regEntry); *arg = cmExpandedCommandArgument("1", true); } else { @@ -584,7 +583,8 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs, double lhs; double rhs; bool result; - if (sscanf(def, "%lg", &lhs) != 1 || sscanf(def2, "%lg", &rhs) != 1) { + if (sscanf(def->c_str(), "%lg", &lhs) != 1 || + sscanf(def2->c_str(), "%lg", &rhs) != 1) { result = false; } else if (*(argP1) == keyLESS) { result = (lhs < rhs); @@ -608,7 +608,7 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs, this->IsKeyword(keySTREQUAL, *argP1))) { def = this->GetVariableOrString(*arg); def2 = this->GetVariableOrString(*argP2); - int val = strcmp(def, def2); + int val = (*def).compare(*def2); bool result; if (*(argP1) == keySTRLESS) { result = (val < 0); @@ -645,7 +645,8 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs, } else { // version_equal op = cmSystemTools::OP_EQUAL; } - bool result = cmSystemTools::VersionCompare(op, def, def2); + bool result = + cmSystemTools::VersionCompare(op, def->c_str(), def2->c_str()); this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2); } @@ -670,9 +671,8 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs, def2 = this->Makefile.GetDefinition(argP2->GetValue()); if (def2) { - std::vector<std::string> list = cmExpandedList(def2, true); - - result = cm::contains(list, def); + std::vector<std::string> list = cmExpandedList(*def2, true); + result = cm::contains(list, *def); } this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2); diff --git a/Source/cmConditionEvaluator.h b/Source/cmConditionEvaluator.h index 082534ce84..cf00ede35c 100644 --- a/Source/cmConditionEvaluator.h +++ b/Source/cmConditionEvaluator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmConditionEvaluator_h -#define cmConditionEvaluator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -13,6 +12,7 @@ #include "cmListFileCache.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmProperty.h" class cmMakefile; @@ -21,8 +21,7 @@ class cmConditionEvaluator public: using cmArgumentList = std::list<cmExpandedCommandArgument>; - cmConditionEvaluator(cmMakefile& makefile, cmListFileContext context, - cmListFileBacktrace bt); + cmConditionEvaluator(cmMakefile& makefile, cmListFileBacktrace bt); // this is a shared function for both If and Else to determine if the // arguments were valid, and if so, was the response true. If there is @@ -32,11 +31,10 @@ public: private: // Filter the given variable definition based on policy CMP0054. - const char* GetDefinitionIfUnquoted( + cmProp GetDefinitionIfUnquoted( const cmExpandedCommandArgument& argument) const; - const char* GetVariableOrString( - const cmExpandedCommandArgument& argument) const; + cmProp GetVariableOrString(const cmExpandedCommandArgument& argument) const; bool IsKeyword(std::string const& keyword, cmExpandedCommandArgument& argument) const; @@ -80,12 +78,9 @@ private: MessageType& status); cmMakefile& Makefile; - cmListFileContext ExecutionContext; cmListFileBacktrace Backtrace; cmPolicies::PolicyStatus Policy12Status; cmPolicies::PolicyStatus Policy54Status; cmPolicies::PolicyStatus Policy57Status; cmPolicies::PolicyStatus Policy64Status; }; - -#endif diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in index 4de1c5d6f0..cf32b051d6 100644 --- a/Source/cmConfigure.cmake.h.in +++ b/Source/cmConfigure.cmake.h.in @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmConfigure_h -#define cmConfigure_h +#pragma once #include "cmsys/Configure.hxx" // IWYU pragma: export @@ -19,15 +18,13 @@ #cmakedefine HAVE_UNSETENV #cmakedefine CMAKE_USE_ELF_PARSER #cmakedefine CMAKE_USE_MACH_PARSER -#cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE #define CMake_DEFAULT_RECURSION_LIMIT @CMake_DEFAULT_RECURSION_LIMIT@ #define CMAKE_BIN_DIR "/@CMAKE_BIN_DIR@" #define CMAKE_DATA_DIR "/@CMAKE_DATA_DIR@" +#define CMAKE_DOC_DIR "/@CMAKE_DOC_DIR@" #define CM_FALLTHROUGH cmsys_FALLTHROUGH #if defined(_WIN32) && !defined(NOMINMAX) # define NOMINMAX #endif - -#endif diff --git a/Source/cmConfigureFileCommand.cxx b/Source/cmConfigureFileCommand.cxx index 5b3045db1a..68322cc0ea 100644 --- a/Source/cmConfigureFileCommand.cxx +++ b/Source/cmConfigureFileCommand.cxx @@ -60,6 +60,7 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args, } bool copyOnly = false; bool escapeQuotes = false; + bool use_source_permissions = true; static std::set<cm::string_view> noopOptions = { /* Legacy. */ @@ -87,6 +88,8 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args, escapeQuotes = true; } else if (args[i] == "@ONLY") { atOnly = true; + } else if (args[i] == "NO_SOURCE_PERMISSIONS") { + use_source_permissions = false; } else if (noopOptions.find(args[i]) != noopOptions.end()) { /* Ignore no-op options. */ } else { @@ -102,7 +105,8 @@ bool cmConfigureFileCommand(std::vector<std::string> const& args, } if (!status.GetMakefile().ConfigureFile( - inputFile, outputFile, copyOnly, atOnly, escapeQuotes, newLineStyle)) { + inputFile, outputFile, copyOnly, atOnly, escapeQuotes, + use_source_permissions, newLineStyle)) { status.SetError("Problem configuring file"); return false; } diff --git a/Source/cmConfigureFileCommand.h b/Source/cmConfigureFileCommand.h index c7f95b852e..009c145119 100644 --- a/Source/cmConfigureFileCommand.h +++ b/Source/cmConfigureFileCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmConfigureFileCommand_h -#define cmConfigureFileCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,4 +11,3 @@ class cmExecutionStatus; bool cmConfigureFileCommand(std::vector<std::string> const& args, cmExecutionStatus& status); -#endif diff --git a/Source/cmContinueCommand.h b/Source/cmContinueCommand.h index ff903aaf68..29a219f1c7 100644 --- a/Source/cmContinueCommand.h +++ b/Source/cmContinueCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmContinueCommand_h -#define cmContinueCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ class cmExecutionStatus; */ bool cmContinueCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index 8550d04da0..6672aa6761 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -18,6 +18,7 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -25,77 +26,209 @@ #include "cmVersion.h" #include "cmake.h" -static std::string const kCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN = - "CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN"; -static std::string const kCMAKE_C_COMPILER_TARGET = "CMAKE_C_COMPILER_TARGET"; -static std::string const kCMAKE_C_LINK_NO_PIE_SUPPORTED = - "CMAKE_C_LINK_NO_PIE_SUPPORTED"; -static std::string const kCMAKE_C_LINK_PIE_SUPPORTED = - "CMAKE_C_LINK_PIE_SUPPORTED"; -static std::string const kCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN = - "CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN"; -static std::string const kCMAKE_CXX_COMPILER_TARGET = - "CMAKE_CXX_COMPILER_TARGET"; -static std::string const kCMAKE_CXX_LINK_NO_PIE_SUPPORTED = - "CMAKE_CXX_LINK_NO_PIE_SUPPORTED"; -static std::string const kCMAKE_CXX_LINK_PIE_SUPPORTED = - "CMAKE_CXX_LINK_PIE_SUPPORTED"; -static std::string const kCMAKE_CUDA_ARCHITECTURES = - "CMAKE_CUDA_ARCHITECTURES"; -static std::string const kCMAKE_CUDA_COMPILER_TARGET = - "CMAKE_CUDA_COMPILER_TARGET"; -static std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = - "CMAKE_CUDA_RUNTIME_LIBRARY"; -static std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS"; -static std::string const kCMAKE_LINK_SEARCH_END_STATIC = +namespace { +class LanguageStandardState +{ +public: + LanguageStandardState(std::string&& lang) + : IsEnabled(false) + , DidStandard(false) + , DidStandardRequired(false) + , DidExtensions(false) + , StandardFlag(lang + "_STANDARD") + , RequiredFlag(lang + "_STANDARD_REQUIRED") + , ExtensionFlag(lang + "_EXTENSIONS") + { + } + + void Enabled(bool isEnabled) { this->IsEnabled = isEnabled; } + + bool UpdateIfMatches(std::vector<std::string> const& argv, size_t& index) + { + bool updated = false; + if (argv[index] == this->StandardFlag) { + this->DidStandard = true; + this->StandardValue = argv[++index]; + updated = true; + } else if (argv[index] == this->RequiredFlag) { + this->DidStandardRequired = true; + this->RequiredValue = argv[++index]; + updated = true; + } else if (argv[index] == this->ExtensionFlag) { + this->DidExtensions = true; + this->ExtensionValue = argv[++index]; + updated = true; + } + return updated; + } + + bool Validate(cmMakefile* const makefile) const + { + if (this->DidStandard) { + makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(this->StandardFlag, + " allowed only in source file signature.")); + return false; + } + if (this->DidStandardRequired) { + makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(this->RequiredFlag, + " allowed only in source file signature.")); + return false; + } + if (this->DidExtensions) { + makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(this->ExtensionFlag, + " allowed only in source file signature.")); + return false; + } + + return true; + } + + bool DidNone() const + { + return !this->DidStandard && !this->DidStandardRequired && + !this->DidExtensions; + } + + void LoadUnsetPropertyValues(cmMakefile* const makefile, bool honorStandard, + bool warnCMP0067, + std::vector<std::string>& warnCMP0067Variables) + { + if (!this->IsEnabled) { + return; + } + + auto lookupStdVar = [&](std::string const& var) -> std::string { + std::string value = makefile->GetSafeDefinition(var); + if (warnCMP0067 && !value.empty()) { + value.clear(); + warnCMP0067Variables.push_back(var); + } + return value; + }; + + if (honorStandard || warnCMP0067) { + if (!this->DidStandard) { + this->StandardValue = + lookupStdVar(cmStrCat("CMAKE_", this->StandardFlag)); + } + if (!this->DidStandardRequired) { + this->RequiredValue = + lookupStdVar(cmStrCat("CMAKE_", this->RequiredFlag)); + } + if (!this->DidExtensions) { + this->ExtensionValue = + lookupStdVar(cmStrCat("CMAKE_", this->ExtensionFlag)); + } + } + } + + void WriteProperties(FILE* fout, std::string const& targetName) const + { + if (!this->IsEnabled) { + return; + } + + auto writeProp = [&](std::string const& prop, std::string const& value) { + fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n", + targetName.c_str(), + cmOutputConverter::EscapeForCMake(prop).c_str(), + cmOutputConverter::EscapeForCMake(value).c_str()); + }; + + if (!this->StandardValue.empty()) { + writeProp(this->StandardFlag, this->StandardValue); + } + if (!this->RequiredValue.empty()) { + writeProp(this->RequiredFlag, this->RequiredValue); + } + if (!this->ExtensionValue.empty()) { + writeProp(this->ExtensionFlag, this->ExtensionValue); + } + } + +private: + bool IsEnabled; + bool DidStandard; + bool DidStandardRequired; + bool DidExtensions; + + std::string StandardFlag; + std::string RequiredFlag; + std::string ExtensionFlag; + + std::string StandardValue; + std::string RequiredValue; + std::string ExtensionValue; +}; + +constexpr size_t lang_property_start = 0; +constexpr size_t lang_property_size = 4; +constexpr size_t pie_property_start = 4; +constexpr size_t pie_property_size = 2; +#define SETUP_LANGUAGE(name, lang) \ + static const std::string name[lang_property_size + pie_property_size + 1] = \ + { "CMAKE_" #lang "_COMPILER_EXTERNAL_TOOLCHAIN", \ + "CMAKE_" #lang "_COMPILER_TARGET", \ + "CMAKE_" #lang "_LINK_NO_PIE_SUPPORTED", \ + "CMAKE_" #lang "_PIE_SUPPORTED", "" } + +// NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +SETUP_LANGUAGE(c_properties, C); +// NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +SETUP_LANGUAGE(cxx_properties, CXX); + +// NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +SETUP_LANGUAGE(cuda_properties, CUDA); +// NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +SETUP_LANGUAGE(fortran_properties, Fortran); +// NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +SETUP_LANGUAGE(objc_properties, OBJC); +// NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +SETUP_LANGUAGE(objcxx_properties, OBJCXX); +// NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +SETUP_LANGUAGE(ispc_properties, ISPC); +// NOLINTNEXTLINE(bugprone-suspicious-missing-comma) +SETUP_LANGUAGE(swift_properties, Swift); +#undef SETUP_LANGUAGE + +std::string const kCMAKE_CUDA_ARCHITECTURES = "CMAKE_CUDA_ARCHITECTURES"; +std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = "CMAKE_CUDA_RUNTIME_LIBRARY"; +std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS"; +std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS"; +std::string const kCMAKE_ISPC_HEADER_SUFFIX = "CMAKE_ISPC_HEADER_SUFFIX"; +std::string const kCMAKE_LINK_SEARCH_END_STATIC = "CMAKE_LINK_SEARCH_END_STATIC"; -static std::string const kCMAKE_LINK_SEARCH_START_STATIC = +std::string const kCMAKE_LINK_SEARCH_START_STATIC = "CMAKE_LINK_SEARCH_START_STATIC"; -static std::string const kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT = +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"; -static std::string const kCMAKE_OSX_SYSROOT = "CMAKE_OSX_SYSROOT"; -static std::string const kCMAKE_APPLE_ARCH_SYSROOTS = - "CMAKE_APPLE_ARCH_SYSROOTS"; -static std::string const kCMAKE_POSITION_INDEPENDENT_CODE = +std::string const kCMAKE_OSX_ARCHITECTURES = "CMAKE_OSX_ARCHITECTURES"; +std::string const kCMAKE_OSX_DEPLOYMENT_TARGET = "CMAKE_OSX_DEPLOYMENT_TARGET"; +std::string const kCMAKE_OSX_SYSROOT = "CMAKE_OSX_SYSROOT"; +std::string const kCMAKE_APPLE_ARCH_SYSROOTS = "CMAKE_APPLE_ARCH_SYSROOTS"; +std::string const kCMAKE_POSITION_INDEPENDENT_CODE = "CMAKE_POSITION_INDEPENDENT_CODE"; -static std::string const kCMAKE_SYSROOT = "CMAKE_SYSROOT"; -static std::string const kCMAKE_SYSROOT_COMPILE = "CMAKE_SYSROOT_COMPILE"; -static std::string const kCMAKE_SYSROOT_LINK = "CMAKE_SYSROOT_LINK"; -static std::string const kCMAKE_Swift_COMPILER_TARGET = - "CMAKE_Swift_COMPILER_TARGET"; -static std::string const kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES = +std::string const kCMAKE_SYSROOT = "CMAKE_SYSROOT"; +std::string const kCMAKE_SYSROOT_COMPILE = "CMAKE_SYSROOT_COMPILE"; +std::string const kCMAKE_SYSROOT_LINK = "CMAKE_SYSROOT_LINK"; +std::string const kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES = "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES"; -static std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES = +std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES = "CMAKE_TRY_COMPILE_PLATFORM_VARIABLES"; -static std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED"; +std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED"; /* GHS Multi platform variables */ -static std::set<std::string> ghs_platform_vars{ +std::set<std::string> const ghs_platform_vars{ "GHS_TARGET_PLATFORM", "GHS_PRIMARY_TARGET", "GHS_TOOLSET_ROOT", "GHS_OS_ROOT", "GHS_OS_DIR", "GHS_BSP_NAME", "GHS_OS_DIR_OPTION" }; - -static void writeProperty(FILE* fout, std::string const& targetName, - std::string const& prop, std::string const& value) -{ - fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n", targetName.c_str(), - cmOutputConverter::EscapeForCMake(prop).c_str(), - cmOutputConverter::EscapeForCMake(value).c_str()); -} - -std::string cmCoreTryCompile::LookupStdVar(std::string const& var, - bool warnCMP0067) -{ - std::string value = this->Makefile->GetSafeDefinition(var); - if (warnCMP0067 && !value.empty()) { - value.clear(); - this->WarnCMP0067.push_back(var); - } - return value; } int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, @@ -107,9 +240,8 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, this->SrcFileSignature = true; cmStateEnums::TargetType targetType = cmStateEnums::EXECUTABLE; - const std::string* tt = - this->Makefile->GetDef("CMAKE_TRY_COMPILE_TARGET_TYPE"); - if (!isTryRun && tt && !tt->empty()) { + cmProp tt = this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE"); + if (!isTryRun && cmNonempty(tt)) { if (*tt == cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE)) { targetType = cmStateEnums::EXECUTABLE; } else if (*tt == @@ -137,21 +269,11 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, std::string outputVariable; std::string copyFile; std::string copyFileError; - std::string cStandard; - std::string objcStandard; - std::string cxxStandard; - std::string objcxxStandard; - std::string cudaStandard; - std::string cStandardRequired; - std::string cxxStandardRequired; - std::string objcStandardRequired; - std::string objcxxStandardRequired; - std::string cudaStandardRequired; - std::string cExtensions; - std::string cxxExtensions; - std::string objcExtensions; - std::string objcxxExtensions; - std::string cudaExtensions; + LanguageStandardState cState("C"); + LanguageStandardState cudaState("CUDA"); + LanguageStandardState cxxState("CXX"); + LanguageStandardState objcState("OBJC"); + LanguageStandardState objcxxState("OBJCXX"); std::vector<std::string> targets; std::vector<std::string> linkOptions; std::string libsToLink = " "; @@ -160,21 +282,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, bool didOutputVariable = false; bool didCopyFile = false; bool didCopyFileError = false; - bool didCStandard = false; - bool didCxxStandard = false; - bool didObjCStandard = false; - bool didObjCxxStandard = false; - bool didCudaStandard = false; - bool didCStandardRequired = false; - bool didCxxStandardRequired = false; - bool didObjCStandardRequired = false; - bool didObjCxxStandardRequired = false; - bool didCudaStandardRequired = false; - bool didCExtensions = false; - bool didCxxExtensions = false; - bool didObjCExtensions = false; - bool didObjCxxExtensions = false; - bool didCudaExtensions = false; bool useSources = argv[2] == "SOURCES"; std::vector<std::string> sources; @@ -188,21 +295,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, DoingOutputVariable, DoingCopyFile, DoingCopyFileError, - DoingCStandard, - DoingCxxStandard, - DoingObjCStandard, - DoingObjCxxStandard, - DoingCudaStandard, - DoingCStandardRequired, - DoingCxxStandardRequired, - DoingObjCStandardRequired, - DoingObjCxxStandardRequired, - DoingCudaStandardRequired, - DoingCExtensions, - DoingCxxExtensions, - DoingObjCExtensions, - DoingObjCxxExtensions, - DoingCudaExtensions, DoingSources, DoingCMakeInternal }; @@ -226,51 +318,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } else if (argv[i] == "COPY_FILE_ERROR") { doing = DoingCopyFileError; didCopyFileError = true; - } else if (argv[i] == "C_STANDARD") { - doing = DoingCStandard; - didCStandard = true; - } else if (argv[i] == "CXX_STANDARD") { - doing = DoingCxxStandard; - didCxxStandard = true; - } else if (argv[i] == "OBJC_STANDARD") { - doing = DoingObjCStandard; - didObjCStandard = true; - } else if (argv[i] == "OBJCXX_STANDARD") { - doing = DoingObjCxxStandard; - didObjCxxStandard = true; - } else if (argv[i] == "CUDA_STANDARD") { - doing = DoingCudaStandard; - didCudaStandard = true; - } else if (argv[i] == "C_STANDARD_REQUIRED") { - doing = DoingCStandardRequired; - didCStandardRequired = true; - } else if (argv[i] == "CXX_STANDARD_REQUIRED") { - doing = DoingCxxStandardRequired; - didCxxStandardRequired = true; - } else if (argv[i] == "OBJC_STANDARD_REQUIRED") { - doing = DoingObjCStandardRequired; - didObjCStandardRequired = true; - } else if (argv[i] == "OBJCXX_STANDARD_REQUIRED") { - doing = DoingObjCxxStandardRequired; - didObjCxxStandardRequired = true; - } else if (argv[i] == "CUDA_STANDARD_REQUIRED") { - doing = DoingCudaStandardRequired; - didCudaStandardRequired = true; - } else if (argv[i] == "C_EXTENSIONS") { - doing = DoingCExtensions; - didCExtensions = true; - } else if (argv[i] == "CXX_EXTENSIONS") { - doing = DoingCxxExtensions; - didCxxExtensions = true; - } else if (argv[i] == "OBJC_EXTENSIONS") { - doing = DoingObjCExtensions; - didObjCExtensions = true; - } else if (argv[i] == "OBJCXX_EXTENSIONS") { - doing = DoingObjCxxExtensions; - didObjCxxExtensions = true; - } else if (argv[i] == "CUDA_EXTENSIONS") { - doing = DoingCudaExtensions; - didCudaExtensions = true; + } else if (cState.UpdateIfMatches(argv, i) || + cxxState.UpdateIfMatches(argv, i) || + cudaState.UpdateIfMatches(argv, i) || + objcState.UpdateIfMatches(argv, i) || + objcxxState.UpdateIfMatches(argv, i)) { + continue; } else if (argv[i] == "__CMAKE_INTERNAL") { doing = DoingCMakeInternal; } else if (doing == DoingCMakeFlags) { @@ -315,51 +368,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } else if (doing == DoingCopyFileError) { copyFileError = argv[i]; doing = DoingNone; - } else if (doing == DoingCStandard) { - cStandard = argv[i]; - doing = DoingNone; - } else if (doing == DoingCxxStandard) { - cxxStandard = argv[i]; - doing = DoingNone; - } else if (doing == DoingObjCStandard) { - objcStandard = argv[i]; - doing = DoingNone; - } else if (doing == DoingObjCxxStandard) { - objcxxStandard = argv[i]; - doing = DoingNone; - } else if (doing == DoingCudaStandard) { - cudaStandard = argv[i]; - doing = DoingNone; - } else if (doing == DoingCStandardRequired) { - cStandardRequired = argv[i]; - doing = DoingNone; - } else if (doing == DoingCxxStandardRequired) { - cxxStandardRequired = argv[i]; - doing = DoingNone; - } else if (doing == DoingObjCStandardRequired) { - objcStandardRequired = argv[i]; - doing = DoingNone; - } else if (doing == DoingObjCxxStandardRequired) { - objcxxStandardRequired = argv[i]; - doing = DoingNone; - } else if (doing == DoingCudaStandardRequired) { - cudaStandardRequired = argv[i]; - doing = DoingNone; - } else if (doing == DoingCExtensions) { - cExtensions = argv[i]; - doing = DoingNone; - } else if (doing == DoingCxxExtensions) { - cxxExtensions = argv[i]; - doing = DoingNone; - } else if (doing == DoingObjCExtensions) { - objcExtensions = argv[i]; - doing = DoingNone; - } else if (doing == DoingObjCxxExtensions) { - objcxxExtensions = argv[i]; - doing = DoingNone; - } else if (doing == DoingCudaExtensions) { - cudaExtensions = argv[i]; - doing = DoingNone; } else if (doing == DoingSources) { sources.push_back(argv[i]); } else if (doing == DoingCMakeInternal) { @@ -411,59 +419,22 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, return -1; } - if (didCStandard && !this->SrcFileSignature) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "C_STANDARD allowed only in source file signature."); - return -1; - } - if (didCxxStandard && !this->SrcFileSignature) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "CXX_STANDARD allowed only in source file signature."); - return -1; - } - if (didCudaStandard && !this->SrcFileSignature) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "CUDA_STANDARD allowed only in source file signature."); - return -1; - } - if (didCStandardRequired && !this->SrcFileSignature) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "C_STANDARD_REQUIRED allowed only in source file signature."); - return -1; - } - if (didCxxStandardRequired && !this->SrcFileSignature) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "CXX_STANDARD_REQUIRED allowed only in source file signature."); - return -1; - } - if (didCudaStandardRequired && !this->SrcFileSignature) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "CUDA_STANDARD_REQUIRED allowed only in source file signature."); - return -1; - } - if (didCExtensions && !this->SrcFileSignature) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "C_EXTENSIONS allowed only in source file signature."); - return -1; - } - if (didCxxExtensions && !this->SrcFileSignature) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "CXX_EXTENSIONS allowed only in source file signature."); - return -1; - } - if (didCudaExtensions && !this->SrcFileSignature) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "CUDA_EXTENSIONS allowed only in source file signature."); - return -1; + if (!this->SrcFileSignature) { + if (!cState.Validate(this->Makefile)) { + return -1; + } + if (!cudaState.Validate(this->Makefile)) { + return -1; + } + if (!cxxState.Validate(this->Makefile)) { + return -1; + } + if (!objcState.Validate(this->Makefile)) { + return -1; + } + if (!objcxxState.Validate(this->Makefile)) { + return -1; + } } // compute the binary dir when TRY_COMPILE is called with a src file @@ -532,6 +503,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } + // when the only language is ISPC we know that the output + // type must by a static library + if (testLangs.size() == 1 && testLangs.count("ISPC") == 1) { + targetType = cmStateEnums::STATIC_LIBRARY; + } + std::string const tcConfig = this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"); @@ -552,19 +529,19 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, return -1; } - const char* def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH"); + cmProp def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH"); fprintf(fout, "cmake_minimum_required(VERSION %u.%u.%u.%u)\n", cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion(), cmVersion::GetPatchVersion(), cmVersion::GetTweakVersion()); if (def) { - fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def); + fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def->c_str()); } /* Set MSVC runtime library policy to match our selection. */ - if (const char* msvcRuntimeLibraryDefault = + if (cmProp msvcRuntimeLibraryDefault = this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) { fprintf(fout, "cmake_policy(SET CMP0091 %s)\n", - *msvcRuntimeLibraryDefault ? "NEW" : "OLD"); + !msvcRuntimeLibraryDefault->empty() ? "NEW" : "OLD"); } /* Set CUDA architectures policy to match outer project. */ @@ -580,14 +557,14 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, projectLangs += " " + li; std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE"; std::string rulesOverrideLang = cmStrCat(rulesOverrideBase, "_", li); - if (const char* rulesOverridePath = + if (cmProp rulesOverridePath = this->Makefile->GetDefinition(rulesOverrideLang)) { fprintf(fout, "set(%s \"%s\")\n", rulesOverrideLang.c_str(), - rulesOverridePath); - } else if (const char* rulesOverridePath2 = + rulesOverridePath->c_str()); + } else if (cmProp rulesOverridePath2 = this->Makefile->GetDefinition(rulesOverrideBase)) { fprintf(fout, "set(%s \"%s\")\n", rulesOverrideBase.c_str(), - rulesOverridePath2); + rulesOverridePath2->c_str()); } } fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str()); @@ -602,9 +579,9 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n"); for (std::string const& li : testLangs) { std::string langFlags = "CMAKE_" + li + "_FLAGS"; - const char* flags = this->Makefile->GetDefinition(langFlags); + cmProp flags = this->Makefile->GetDefinition(langFlags); fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li.c_str(), - cmOutputConverter::EscapeForCMake(flags ? flags : "").c_str()); + cmOutputConverter::EscapeForCMake(cmToCStrSafe(flags)).c_str()); fprintf(fout, "set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}" " ${COMPILE_DEFINITIONS}\")\n", @@ -641,10 +618,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, for (std::string const& li : testLangs) { std::string const langFlagsCfg = cmStrCat("CMAKE_", li, "_FLAGS_", cfg); - const char* flagsCfg = this->Makefile->GetDefinition(langFlagsCfg); - fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(), - cmOutputConverter::EscapeForCMake(flagsCfg ? flagsCfg : "") - .c_str()); + cmProp flagsCfg = this->Makefile->GetDefinition(langFlagsCfg); + fprintf( + fout, "set(%s %s)\n", langFlagsCfg.c_str(), + cmOutputConverter::EscapeForCMake(cmToCStrSafe(flagsCfg)).c_str()); } } break; } @@ -674,12 +651,11 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, case cmPolicies::NEW: // NEW behavior is to pass linker flags. { - const char* exeLinkFlags = + cmProp exeLinkFlags = this->Makefile->GetDefinition("CMAKE_EXE_LINKER_FLAGS"); - fprintf( - fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n", - cmOutputConverter::EscapeForCMake(exeLinkFlags ? exeLinkFlags : "") - .c_str()); + fprintf(fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n", + cmOutputConverter::EscapeForCMake(cmToCStrSafe(exeLinkFlags)) + .c_str()); } break; } @@ -721,14 +697,29 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, // Forward a set of variables to the inner project cache. { std::set<std::string> vars; - vars.insert(kCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN); - vars.insert(kCMAKE_C_COMPILER_TARGET); - vars.insert(kCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN); - vars.insert(kCMAKE_CXX_COMPILER_TARGET); + vars.insert(&c_properties[lang_property_start], + &c_properties[lang_property_start + lang_property_size]); + vars.insert(&cxx_properties[lang_property_start], + &cxx_properties[lang_property_start + lang_property_size]); + vars.insert(&cuda_properties[lang_property_start], + &cuda_properties[lang_property_start + lang_property_size]); + vars.insert( + &fortran_properties[lang_property_start], + &fortran_properties[lang_property_start + lang_property_size]); + vars.insert(&objc_properties[lang_property_start], + &objc_properties[lang_property_start + lang_property_size]); + vars.insert( + &objcxx_properties[lang_property_start], + &objcxx_properties[lang_property_start + lang_property_size]); + vars.insert(&ispc_properties[lang_property_start], + &ispc_properties[lang_property_start + lang_property_size]); + vars.insert(&swift_properties[lang_property_start], + &swift_properties[lang_property_start + lang_property_size]); vars.insert(kCMAKE_CUDA_ARCHITECTURES); - vars.insert(kCMAKE_CUDA_COMPILER_TARGET); vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY); vars.insert(kCMAKE_ENABLE_EXPORTS); + vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS); + vars.insert(kCMAKE_ISPC_HEADER_SUFFIX); vars.insert(kCMAKE_LINK_SEARCH_END_STATIC); vars.insert(kCMAKE_LINK_SEARCH_START_STATIC); vars.insert(kCMAKE_OSX_ARCHITECTURES); @@ -739,13 +730,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, vars.insert(kCMAKE_SYSROOT); vars.insert(kCMAKE_SYSROOT_COMPILE); vars.insert(kCMAKE_SYSROOT_LINK); - vars.insert(kCMAKE_Swift_COMPILER_TARGET); vars.insert(kCMAKE_WARN_DEPRECATED); vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s); - if (const char* varListStr = this->Makefile->GetDefinition( + if (cmProp varListStr = this->Makefile->GetDefinition( kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) { - std::vector<std::string> varList = cmExpandedList(varListStr); + std::vector<std::string> varList = cmExpandedList(*varListStr); vars.insert(varList.begin(), varList.end()); } @@ -753,10 +743,24 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, cmPolicies::NEW) { // To ensure full support of PIE, propagate cache variables // driving the link options - vars.insert(kCMAKE_C_LINK_PIE_SUPPORTED); - vars.insert(kCMAKE_C_LINK_NO_PIE_SUPPORTED); - vars.insert(kCMAKE_CXX_LINK_PIE_SUPPORTED); - vars.insert(kCMAKE_CXX_LINK_NO_PIE_SUPPORTED); + vars.insert(&c_properties[pie_property_start], + &c_properties[pie_property_start + pie_property_size]); + vars.insert(&cxx_properties[pie_property_start], + &cxx_properties[pie_property_start + pie_property_size]); + vars.insert(&cuda_properties[pie_property_start], + &cuda_properties[pie_property_start + pie_property_size]); + vars.insert( + &fortran_properties[pie_property_start], + &fortran_properties[pie_property_start + pie_property_size]); + vars.insert(&objc_properties[pie_property_start], + &objc_properties[pie_property_start + pie_property_size]); + vars.insert( + &objcxx_properties[pie_property_start], + &objcxx_properties[pie_property_start + pie_property_size]); + vars.insert(&ispc_properties[pie_property_start], + &ispc_properties[pie_property_start + pie_property_size]); + vars.insert(&swift_properties[pie_property_start], + &swift_properties[pie_property_start + pie_property_size]); } /* for the TRY_COMPILEs we want to be able to specify the architecture. @@ -766,16 +770,16 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, cmLocalGenerator doesn't allow building for "the other" architecture only via CMAKE_OSX_ARCHITECTURES. */ - if (const char* tcArchs = this->Makefile->GetDefinition( + if (cmProp tcArchs = this->Makefile->GetDefinition( kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) { vars.erase(kCMAKE_OSX_ARCHITECTURES); - std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + std::string(tcArchs); + std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs; cmakeFlags.push_back(std::move(flag)); } for (std::string const& var : vars) { - if (const char* val = this->Makefile->GetDefinition(var)) { - std::string flag = "-D" + var + "=" + val; + if (cmProp val = this->Makefile->GetDefinition(var)) { + std::string flag = "-D" + var + "=" + *val; cmakeFlags.push_back(std::move(flag)); } } @@ -819,21 +823,17 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } fprintf(fout, ")\n"); - bool const testC = testLangs.find("C") != testLangs.end(); - bool const testObjC = testLangs.find("OBJC") != testLangs.end(); - bool const testCxx = testLangs.find("CXX") != testLangs.end(); - bool const testObjCxx = testLangs.find("OBJCXX") != testLangs.end(); - bool const testCuda = testLangs.find("CUDA") != testLangs.end(); + cState.Enabled(testLangs.find("C") != testLangs.end()); + cxxState.Enabled(testLangs.find("CXX") != testLangs.end()); + cudaState.Enabled(testLangs.find("CUDA") != testLangs.end()); + objcState.Enabled(testLangs.find("OBJC") != testLangs.end()); + objcxxState.Enabled(testLangs.find("OBJCXX") != testLangs.end()); bool warnCMP0067 = false; bool honorStandard = true; - if (!didCStandard && !didCxxStandard && !didObjCStandard && - !didObjCxxStandard && !didCudaStandard && !didCStandardRequired && - !didCxxStandardRequired && !didObjCStandardRequired && - !didObjCxxStandardRequired && !didCudaStandardRequired && - !didCExtensions && !didCxxExtensions && !didObjCExtensions && - !didObjCxxExtensions && !didCudaExtensions) { + if (cState.DidNone() && cxxState.DidNone() && objcState.DidNone() && + objcxxState.DidNone() && cudaState.DidNone()) { switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) { case cmPolicies::WARN: warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled( @@ -855,46 +855,20 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } - if (honorStandard || warnCMP0067) { + std::vector<std::string> warnCMP0067Variables; - auto testLanguage = - [&](bool testLang, bool didLangStandard, bool didLangStandardRequired, - bool didLangExtensions, std::string& langStandard, - std::string& langStandardRequired, std::string& langExtensions, - const std::string& lang) { - if (testLang) { - if (!didLangStandard) { - langStandard = this->LookupStdVar( - cmStrCat("CMAKE_", lang, "_STANDARD"), warnCMP0067); - } - if (!didLangStandardRequired) { - langStandardRequired = this->LookupStdVar( - cmStrCat("CMAKE_", lang, "_STANDARD_REQUIRED"), warnCMP0067); - } - if (!didLangExtensions) { - langExtensions = this->LookupStdVar( - cmStrCat("CMAKE_", lang, "_EXTENSIONS"), warnCMP0067); - } - } - }; - - testLanguage(testC, didCStandard, didCStandardRequired, didCExtensions, - cStandard, cStandardRequired, cExtensions, "C"); - testLanguage(testObjC, didObjCStandard, didObjCStandardRequired, - didObjCExtensions, objcStandard, objcStandardRequired, - objcExtensions, "OBJC"); - testLanguage(testCxx, didCxxStandard, didCxxStandardRequired, - didCxxExtensions, cxxStandard, cxxStandardRequired, - cxxExtensions, "CXX"); - testLanguage(testObjCxx, didObjCxxStandard, didObjCxxStandardRequired, - didObjCxxExtensions, objcxxStandard, objcxxStandardRequired, - objcxxExtensions, "OBJCXX"); - testLanguage(testCuda, didCudaStandard, didCudaStandardRequired, - didCudaExtensions, cudaStandard, cudaStandardRequired, - cudaExtensions, "CUDA"); - } + cState.LoadUnsetPropertyValues(this->Makefile, honorStandard, warnCMP0067, + warnCMP0067Variables); + cxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard, + warnCMP0067, warnCMP0067Variables); + cudaState.LoadUnsetPropertyValues(this->Makefile, honorStandard, + warnCMP0067, warnCMP0067Variables); + objcState.LoadUnsetPropertyValues(this->Makefile, honorStandard, + warnCMP0067, warnCMP0067Variables); + objcxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard, + warnCMP0067, warnCMP0067Variables); - if (!this->WarnCMP0067.empty()) { + if (!warnCMP0067Variables.empty()) { std::ostringstream w; /* clang-format off */ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0067) << "\n" @@ -902,43 +876,17 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, "is not honoring language standard variables in the test project:\n" ; /* clang-format on */ - for (std::string const& vi : this->WarnCMP0067) { + for (std::string const& vi : warnCMP0067Variables) { w << " " << vi << "\n"; } this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); } - auto writeLanguageProperties = [&](bool testLang, - const std::string& langStandard, - const std::string& langStandardRequired, - const std::string& langExtensions, - const std::string& lang) { - if (testLang) { - if (!langStandard.empty()) { - writeProperty(fout, targetName, cmStrCat(lang, "_STANDARD"), - langStandard); - } - if (!langStandardRequired.empty()) { - writeProperty(fout, targetName, cmStrCat(lang, "_STANDARD_REQUIRED"), - langStandardRequired); - } - if (!langExtensions.empty()) { - writeProperty(fout, targetName, cmStrCat(lang, "_EXTENSIONS"), - langExtensions); - } - } - }; - - writeLanguageProperties(testC, cStandard, cStandardRequired, cExtensions, - "C"); - writeLanguageProperties(testObjC, objcStandard, objcStandardRequired, - objcExtensions, "OBJC"); - writeLanguageProperties(testCxx, cxxStandard, cxxStandardRequired, - cxxExtensions, "CXX"); - writeLanguageProperties(testObjCxx, objcxxStandard, objcxxStandardRequired, - objcxxExtensions, "OBJCXX"); - writeLanguageProperties(testCuda, cudaStandard, cudaStandardRequired, - cudaExtensions, "CUDA"); + cState.WriteProperties(fout, targetName); + cxxState.WriteProperties(fout, targetName); + cudaState.WriteProperties(fout, targetName); + objcState.WriteProperties(fout, targetName); + objcxxState.WriteProperties(fout, targetName); if (!linkOptions.empty()) { std::vector<std::string> options; @@ -971,8 +919,8 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, if (this->Makefile->GetState()->UseGhsMultiIDE()) { // 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 + "'"; + if (cmProp val = this->Makefile->GetDefinition(var)) { + std::string flag = "-D" + var + "=" + "'" + *val + "'"; cmakeFlags.push_back(std::move(flag)); } } @@ -1109,18 +1057,18 @@ void cmCoreTryCompile::FindOutputFile(const std::string& targetName, std::vector<std::string> searchDirs; searchDirs.emplace_back(); - const char* config = + cmProp config = this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"); // if a config was specified try that first - if (config && config[0]) { - std::string tmp = cmStrCat('/', config); + if (cmNonempty(config)) { + std::string tmp = cmStrCat('/', *config); searchDirs.push_back(std::move(tmp)); } searchDirs.emplace_back("/Debug"); #if defined(__APPLE__) std::string app = "/" + targetName + ".app"; - if (config && config[0]) { - std::string tmp = cmStrCat('/', config, app); + if (cmNonempty(config)) { + std::string tmp = cmStrCat('/', *config, app); searchDirs.push_back(std::move(tmp)); } std::string tmp = "/Debug" + app; diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index ae714a63a3..594fd7f9f5 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCoreTryCompile_h -#define cmCoreTryCompile_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -47,10 +46,4 @@ protected: std::string OutputFile; std::string FindErrorMessage; bool SrcFileSignature = false; - -private: - std::vector<std::string> WarnCMP0067; - std::string LookupStdVar(std::string const& var, bool warnCMP0067); }; - -#endif diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx index 9d492ba845..9c4deea1e2 100644 --- a/Source/cmCreateTestSourceList.cxx +++ b/Source/cmCreateTestSourceList.cxx @@ -127,7 +127,7 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args, mf.AddDefinition("CMAKE_FORWARD_DECLARE_TESTS", forwardDeclareCode); mf.AddDefinition("CMAKE_FUNCTION_TABLE_ENTIRES", functionMapCode); bool res = true; - if (!mf.ConfigureFile(configFile, driver, false, true, false)) { + if (!mf.ConfigureFile(configFile, driver, false, true, false, true)) { res = false; } diff --git a/Source/cmCreateTestSourceList.h b/Source/cmCreateTestSourceList.h index 19503f43d5..a7f11a6612 100644 --- a/Source/cmCreateTestSourceList.h +++ b/Source/cmCreateTestSourceList.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCreateTestSourceList_h -#define cmCreateTestSourceList_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmCreateTestSourceList(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmCryptoHash.h b/Source/cmCryptoHash.h index f27bb5db41..a2d45e7b9a 100644 --- a/Source/cmCryptoHash.h +++ b/Source/cmCryptoHash.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCryptoHash_h -#define cmCryptoHash_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -85,5 +84,3 @@ private: unsigned int Id; struct rhash_context* CTX; }; - -#endif diff --git a/Source/cmCurl.h b/Source/cmCurl.h index 7bd036e5df..fb716f80c9 100644 --- a/Source/cmCurl.h +++ b/Source/cmCurl.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCurl_h -#define cmCurl_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ std::string cmCurlSetCAInfo(::CURL* curl, const char* cafile = nullptr); std::string cmCurlSetNETRCOption(::CURL* curl, const std::string& netrc_level, const std::string& netrc_file); - -#endif diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h index aa572ad811..2036e90b7f 100644 --- a/Source/cmCustomCommand.h +++ b/Source/cmCustomCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCustomCommand_h -#define cmCustomCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -114,5 +113,3 @@ private: bool CommandExpandLists = false; bool StdPipesUTF8 = false; }; - -#endif diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h index 67ee9e0e1a..412eba8dd1 100644 --- a/Source/cmCustomCommandGenerator.h +++ b/Source/cmCustomCommandGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCustomCommandGenerator_h -#define cmCustomCommandGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -47,5 +46,3 @@ public: std::vector<std::string> const& GetDepends() const; bool HasOnlyEmptyCommandLines() const; }; - -#endif diff --git a/Source/cmCustomCommandLines.h b/Source/cmCustomCommandLines.h index ead579280a..ee8d080f0b 100644 --- a/Source/cmCustomCommandLines.h +++ b/Source/cmCustomCommandLines.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCustomCommandLines_h -#define cmCustomCommandLines_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -28,5 +27,3 @@ cmCustomCommandLine cmMakeCommandLine( /** Return a command line vector with a single command line. */ cmCustomCommandLines cmMakeSingleCommandLine( std::initializer_list<cm::string_view> ilist); - -#endif diff --git a/Source/cmCustomCommandTypes.h b/Source/cmCustomCommandTypes.h index d4bf1f9c35..5c900ce1e2 100644 --- a/Source/cmCustomCommandTypes.h +++ b/Source/cmCustomCommandTypes.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmCustomCommandTypes_h -#define cmCustomCommandTypes_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -35,5 +34,3 @@ struct cmUtilityOutput std::string Name; std::string NameCMP0049; }; - -#endif diff --git a/Source/cmDefinePropertyCommand.h b/Source/cmDefinePropertyCommand.h index 60dd76a0d1..3c478b3fe8 100644 --- a/Source/cmDefinePropertyCommand.h +++ b/Source/cmDefinePropertyCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmDefinesPropertyCommand_h -#define cmDefinesPropertyCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmDefinePropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx index 69a64276bc..4a4f87d8b2 100644 --- a/Source/cmDefinitions.cxx +++ b/Source/cmDefinitions.cxx @@ -19,7 +19,6 @@ cmDefinitions::Def const& cmDefinitions::GetInternal(const std::string& key, { auto it = begin->Map.find(cm::String::borrow(key)); if (it != begin->Map.end()) { - it->second.Used = true; return it->second; } } @@ -108,16 +107,3 @@ void cmDefinitions::Unset(const std::string& key) { this->Map[key] = Def(); } - -std::vector<std::string> cmDefinitions::UnusedKeys() const -{ - std::vector<std::string> keys; - keys.reserve(this->Map.size()); - // Consider local definitions. - for (auto const& mi : this->Map) { - if (!mi.second.Used) { - keys.push_back(*mi.first.str_if_stable()); - } - } - return keys; -} diff --git a/Source/cmDefinitions.h b/Source/cmDefinitions.h index 0e38fb1af2..b650aa8604 100644 --- a/Source/cmDefinitions.h +++ b/Source/cmDefinitions.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmDefinitions_h -#define cmDefinitions_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -48,9 +47,6 @@ public: /** Unset a definition. */ void Unset(const std::string& key); - /** List of unused keys. */ - std::vector<std::string> UnusedKeys() const; - private: /** String with existence boolean. */ struct Def @@ -62,7 +58,6 @@ private: { } cm::String Value; - bool Used = false; }; static Def NoDef; @@ -71,5 +66,3 @@ private: static Def const& GetInternal(const std::string& key, StackIter begin, StackIter end, bool raise); }; - -#endif diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx index d8aa730547..d092f4f233 100644 --- a/Source/cmDepends.cxx +++ b/Source/cmDepends.cxx @@ -11,6 +11,7 @@ #include "cmGeneratedFileStream.h" #include "cmLocalUnixMakefileGenerator3.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -228,19 +229,19 @@ bool cmDepends::CheckDependencies(std::istream& internalDepends, void cmDepends::SetIncludePathFromLanguage(const std::string& lang) { // Look for the new per "TARGET_" variant first: - const char* includePath = nullptr; + cmProp includePath = nullptr; std::string includePathVar = cmStrCat("CMAKE_", lang, "_TARGET_INCLUDE_PATH"); cmMakefile* mf = this->LocalGenerator->GetMakefile(); includePath = mf->GetDefinition(includePathVar); if (includePath) { - cmExpandList(includePath, this->IncludePath); + cmExpandList(*includePath, this->IncludePath); } else { // Fallback to the old directory level variable if no per-target var: includePathVar = cmStrCat("CMAKE_", lang, "_INCLUDE_PATH"); includePath = mf->GetDefinition(includePathVar); if (includePath) { - cmExpandList(includePath, this->IncludePath); + cmExpandList(*includePath, this->IncludePath); } } } diff --git a/Source/cmDepends.h b/Source/cmDepends.h index 8cf528f736..0240da90b0 100644 --- a/Source/cmDepends.h +++ b/Source/cmDepends.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmDepends_h -#define cmDepends_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -112,5 +111,3 @@ protected: void SetIncludePathFromLanguage(const std::string& lang); }; - -#endif diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx index e05c964442..e6aef9289f 100644 --- a/Source/cmDependsC.cxx +++ b/Source/cmDependsC.cxx @@ -9,6 +9,7 @@ #include "cmFileTime.h" #include "cmLocalUnixMakefileGenerator3.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -38,13 +39,13 @@ cmDependsC::cmDependsC(cmLocalUnixMakefileGenerator3* lg, std::string complainRegex = "^$"; { std::string scanRegexVar = cmStrCat("CMAKE_", lang, "_INCLUDE_REGEX_SCAN"); - if (const char* sr = mf->GetDefinition(scanRegexVar)) { - scanRegex = sr; + if (cmProp sr = mf->GetDefinition(scanRegexVar)) { + scanRegex = *sr; } std::string complainRegexVar = cmStrCat("CMAKE_", lang, "_INCLUDE_REGEX_COMPLAIN"); - if (const char* cr = mf->GetDefinition(complainRegexVar)) { - complainRegex = cr; + if (cmProp cr = mf->GetDefinition(complainRegexVar)) { + complainRegex = *cr; } } diff --git a/Source/cmDependsC.h b/Source/cmDependsC.h index e01faa435a..c79da1a3e2 100644 --- a/Source/cmDependsC.h +++ b/Source/cmDependsC.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmDependsC_h -#define cmDependsC_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -93,5 +92,3 @@ protected: void WriteCacheFile() const; void ReadCacheFile(); }; - -#endif diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx index 8f02d95515..a239418fd0 100644 --- a/Source/cmDependsFortran.cxx +++ b/Source/cmDependsFortran.cxx @@ -15,6 +15,7 @@ #include "cmLocalUnixMakefileGenerator3.h" #include "cmMakefile.h" #include "cmOutputConverter.h" +#include "cmProperty.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" @@ -394,9 +395,9 @@ bool cmDependsFortran::WriteDependenciesReal(std::string const& obj, makeDepends << "\t$(CMAKE_COMMAND) -E cmake_copy_f90_mod " << modFile << ' ' << stampFileForShell; cmMakefile* mf = this->LocalGenerator->GetMakefile(); - const char* cid = mf->GetDefinition("CMAKE_Fortran_COMPILER_ID"); - if (cid && *cid) { - makeDepends << ' ' << cid; + cmProp cid = mf->GetDefinition("CMAKE_Fortran_COMPILER_ID"); + if (cmNonempty(cid)) { + makeDepends << ' ' << *cid; } makeDepends << '\n'; } diff --git a/Source/cmDependsFortran.h b/Source/cmDependsFortran.h index 3e306dd618..e377a2c338 100644 --- a/Source/cmDependsFortran.h +++ b/Source/cmDependsFortran.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFortran_h -#define cmFortran_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -91,5 +90,3 @@ private: std::string MaybeConvertToRelativePath(std::string const& base, std::string const& path); }; - -#endif diff --git a/Source/cmDependsJava.h b/Source/cmDependsJava.h index 2a90251016..1db7ce16c9 100644 --- a/Source/cmDependsJava.h +++ b/Source/cmDependsJava.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmDependsJava_h -#define cmDependsJava_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -36,5 +35,3 @@ protected: const std::string& internalDependsFileName, DependencyMap& validDeps) override; }; - -#endif diff --git a/Source/cmDependsJavaParserHelper.h b/Source/cmDependsJavaParserHelper.h index c545ee2b7e..869b7d4e46 100644 --- a/Source/cmDependsJavaParserHelper.h +++ b/Source/cmDependsJavaParserHelper.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmDependsJavaParserHelper_h -#define cmDependsJavaParserHelper_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -97,5 +96,3 @@ private: #define YYSTYPE_IS_DECLARED #define YY_EXTRA_TYPE cmDependsJavaParserHelper* #define YY_DECL int cmDependsJava_yylex(YYSTYPE* yylvalp, yyscan_t yyscanner) - -#endif diff --git a/Source/cmDocumentation.h b/Source/cmDocumentation.h index 3768e1abe6..313be328af 100644 --- a/Source/cmDocumentation.h +++ b/Source/cmDocumentation.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef _cmDocumentation_h -#define _cmDocumentation_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -125,5 +124,3 @@ private: static void WarnFormFromFilename(RequestedHelpItem& request, bool& result); }; - -#endif diff --git a/Source/cmDocumentationEntry.h b/Source/cmDocumentationEntry.h index afbca5e4bb..89a2899da4 100644 --- a/Source/cmDocumentationEntry.h +++ b/Source/cmDocumentationEntry.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmDocumentationEntry_h -#define cmDocumentationEntry_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -33,5 +32,3 @@ struct cmDocumentationEntry } } }; - -#endif diff --git a/Source/cmDocumentationFormatter.h b/Source/cmDocumentationFormatter.h index 17b63dacc4..cb3038aaa4 100644 --- a/Source/cmDocumentationFormatter.h +++ b/Source/cmDocumentationFormatter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef _cmDocumentationFormatter_h -#define _cmDocumentationFormatter_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -62,5 +61,3 @@ private: int TextWidth = 77; const char* TextIndent = ""; }; - -#endif diff --git a/Source/cmDocumentationSection.h b/Source/cmDocumentationSection.h index 641263de78..276e520315 100644 --- a/Source/cmDocumentationSection.h +++ b/Source/cmDocumentationSection.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef _cmDocumentationSection_h -#define _cmDocumentationSection_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -67,5 +66,3 @@ private: std::string Name; std::vector<cmDocumentationEntry> Entries; }; - -#endif diff --git a/Source/cmDynamicLoader.h b/Source/cmDynamicLoader.h index 4b89388e24..53ea5cbe71 100644 --- a/Source/cmDynamicLoader.h +++ b/Source/cmDynamicLoader.h @@ -5,8 +5,7 @@ // cmDynamicLoader provides a portable interface to loading dynamic // libraries into a process. -#ifndef cmDynamicLoader_h -#define cmDynamicLoader_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -29,5 +28,3 @@ protected: cmDynamicLoader() = default; ~cmDynamicLoader() = default; }; - -#endif diff --git a/Source/cmELF.h b/Source/cmELF.h index 123bf9b1f3..99eb4f48b5 100644 --- a/Source/cmELF.h +++ b/Source/cmELF.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmELF_h -#define cmELF_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -112,5 +111,3 @@ private: std::unique_ptr<cmELFInternal> Internal; std::string ErrorMessage; }; - -#endif diff --git a/Source/cmEnableLanguageCommand.h b/Source/cmEnableLanguageCommand.h index 1f8c4ce7e6..730ba656c6 100644 --- a/Source/cmEnableLanguageCommand.h +++ b/Source/cmEnableLanguageCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmEnableLanguageCommand_h -#define cmEnableLanguageCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmEnableLanguageCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmEnableTestingCommand.h b/Source/cmEnableTestingCommand.h index e4593f2fe2..1722511b21 100644 --- a/Source/cmEnableTestingCommand.h +++ b/Source/cmEnableTestingCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmEnableTestingCommand_h -#define cmEnableTestingCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -25,5 +24,3 @@ class cmExecutionStatus; */ bool cmEnableTestingCommand(std::vector<std::string> const&, cmExecutionStatus&); - -#endif diff --git a/Source/cmExecProgramCommand.h b/Source/cmExecProgramCommand.h index 7c751e1546..111a56ed9c 100644 --- a/Source/cmExecProgramCommand.h +++ b/Source/cmExecProgramCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExecProgramCommand_h -#define cmExecProgramCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -19,5 +18,3 @@ class cmExecutionStatus; */ bool cmExecProgramCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 9c53bdfbc6..5a85b7de4d 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -6,9 +6,13 @@ #include <cctype> /* isspace */ #include <cstdio> #include <iostream> +#include <map> #include <memory> +#include <sstream> +#include <utility> #include <vector> +#include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -63,6 +67,7 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, bool EchoOutputVariable = false; bool EchoErrorVariable = false; std::string Encoding; + std::string CommandErrorIsFatal; }; static auto const parser = @@ -86,7 +91,8 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, &Arguments::ErrorStripTrailingWhitespace) .Bind("ENCODING"_s, &Arguments::Encoding) .Bind("ECHO_OUTPUT_VARIABLE"_s, &Arguments::EchoOutputVariable) - .Bind("ECHO_ERROR_VARIABLE"_s, &Arguments::EchoErrorVariable); + .Bind("ECHO_ERROR_VARIABLE"_s, &Arguments::EchoErrorVariable) + .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal); std::vector<std::string> unparsedArguments; std::vector<std::string> keywordsMissingValue; @@ -131,6 +137,14 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, return false; } } + + if (!arguments.CommandErrorIsFatal.empty()) { + if (arguments.CommandErrorIsFatal != "ANY"_s && + arguments.CommandErrorIsFatal != "LAST"_s) { + status.SetError("COMMAND_ERROR_IS_FATAL option can be ANY or LAST"); + return false; + } + } // Create a process instance. std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr( cmsysProcess_New(), cmsysProcess_Delete); @@ -363,6 +377,104 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, } } + auto queryProcessStatusByIndex = [&cp](int index) -> std::string { + std::string processStatus; + switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(index))) { + case kwsysProcess_StateByIndex_Exited: { + int exitCode = cmsysProcess_GetExitValueByIndex(cp, index); + if (exitCode) { + processStatus = "Child return code: " + std::to_string(exitCode); + } + } break; + case kwsysProcess_StateByIndex_Exception: { + processStatus = cmStrCat( + "Abnormal exit with child return code: ", + cmsysProcess_GetExceptionStringByIndex(cp, static_cast<int>(index))); + break; + } + case kwsysProcess_StateByIndex_Error: + default: + processStatus = "Error getting the child return code"; + break; + } + return processStatus; + }; + + if (arguments.CommandErrorIsFatal == "ANY"_s) { + bool ret = true; + switch (cmsysProcess_GetState(cp)) { + case cmsysProcess_State_Exited: { + std::map<int, std::string> failureIndices; + for (int i = 0; i < static_cast<int>(arguments.Commands.size()); ++i) { + std::string processStatus = queryProcessStatusByIndex(i); + if (!processStatus.empty()) { + failureIndices[i] = processStatus; + } + if (!failureIndices.empty()) { + std::ostringstream oss; + oss << "failed command indexes:\n"; + for (auto const& e : failureIndices) { + oss << " " << e.first + 1 << ": \"" << e.second << "\"\n"; + } + status.SetError(oss.str()); + ret = false; + } + } + } break; + case cmsysProcess_State_Exception: + status.SetError( + cmStrCat("abnormal exit: ", cmsysProcess_GetExceptionString(cp))); + ret = false; + break; + case cmsysProcess_State_Error: + status.SetError(cmStrCat("error getting child return code: ", + cmsysProcess_GetErrorString(cp))); + ret = false; + break; + case cmsysProcess_State_Expired: + status.SetError("Process terminated due to timeout"); + ret = false; + break; + } + + if (!ret) { + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } + + if (arguments.CommandErrorIsFatal == "LAST"_s) { + bool ret = true; + switch (cmsysProcess_GetState(cp)) { + case cmsysProcess_State_Exited: { + int lastIndex = static_cast<int>(arguments.Commands.size() - 1); + const std::string processStatus = queryProcessStatusByIndex(lastIndex); + if (!processStatus.empty()) { + status.SetError("last command failed"); + ret = false; + } + } break; + case cmsysProcess_State_Exception: + status.SetError( + cmStrCat("Abnormal exit: ", cmsysProcess_GetExceptionString(cp))); + ret = false; + break; + case cmsysProcess_State_Error: + status.SetError(cmStrCat("Error getting child return code: ", + cmsysProcess_GetErrorString(cp))); + ret = false; + break; + case cmsysProcess_State_Expired: + status.SetError("Process terminated due to timeout"); + ret = false; + break; + } + if (!ret) { + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } + return true; } diff --git a/Source/cmExecuteProcessCommand.h b/Source/cmExecuteProcessCommand.h index 9c4b6007fd..cc8cc38fed 100644 --- a/Source/cmExecuteProcessCommand.h +++ b/Source/cmExecuteProcessCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExecuteProcessCommand_h -#define cmExecuteProcessCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -18,5 +17,3 @@ class cmExecutionStatus; */ bool cmExecuteProcessCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmExecutionStatus.h b/Source/cmExecutionStatus.h index d2cc9b87aa..0feaedf33f 100644 --- a/Source/cmExecutionStatus.h +++ b/Source/cmExecutionStatus.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExecutionStatus_h -#define cmExecutionStatus_h +#pragma once #include <cmConfigure.h> // IWYU pragma: keep @@ -48,5 +47,3 @@ private: bool ContinueInvoked = false; bool NestedError = false; }; - -#endif diff --git a/Source/cmExpandedCommandArgument.cxx b/Source/cmExpandedCommandArgument.cxx index 43f648bc3a..1f14fc4367 100644 --- a/Source/cmExpandedCommandArgument.cxx +++ b/Source/cmExpandedCommandArgument.cxx @@ -37,8 +37,3 @@ bool cmExpandedCommandArgument::empty() const { return this->Value.empty(); } - -const char* cmExpandedCommandArgument::c_str() const -{ - return this->Value.c_str(); -} diff --git a/Source/cmExpandedCommandArgument.h b/Source/cmExpandedCommandArgument.h index 69d35debed..1ff6ed18dc 100644 --- a/Source/cmExpandedCommandArgument.h +++ b/Source/cmExpandedCommandArgument.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExpandedCommandArgument_h -#define cmExpandedCommandArgument_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -29,11 +28,7 @@ public: bool empty() const; - const char* c_str() const; - private: std::string Value; bool Quoted = false; }; - -#endif diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h index a9b6107f82..250564f511 100644 --- a/Source/cmExportBuildAndroidMKGenerator.h +++ b/Source/cmExportBuildAndroidMKGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExportBuildAndroidMKGenerator_h -#define cmExportBuildAndroidMKGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -63,5 +62,3 @@ protected: cmGeneratorTarget const* target, std::ostream& os, const ImportPropertyMap& properties) override; }; - -#endif diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index dd700c5edf..1a31ae4afd 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -288,6 +288,9 @@ void cmExportBuildFileGenerator::GetTargets( if (this->ExportSet) { for (std::unique_ptr<cmTargetExport> const& te : this->ExportSet->GetTargetExports()) { + if (te->NamelinkOnly) { + continue; + } targets.push_back(te->TargetName); } return; diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 66e8cbb424..264494dc5a 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExportBuildFileGenerator_h -#define cmExportBuildFileGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -85,5 +84,3 @@ protected: std::vector<cmGeneratorTarget*> Exports; cmLocalGenerator* LG; }; - -#endif diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 9f8a821e6f..352eaf2911 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -223,11 +223,9 @@ bool cmExportCommand(std::vector<std::string> const& args, ebfg->SetExportOld(arguments.ExportOld); // Compute the set of configurations exported. - std::vector<std::string> configurationTypes; - mf.GetConfigurations(configurationTypes); - if (configurationTypes.empty()) { - configurationTypes.emplace_back(); - } + std::vector<std::string> configurationTypes = + mf.GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& ct : configurationTypes) { ebfg->AddConfiguration(ct); } diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h index 9655628203..3f87bcfdea 100644 --- a/Source/cmExportCommand.h +++ b/Source/cmExportCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExportCommand_h -#define cmExportCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmExportCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 4d0e0996af..cbae4e59ab 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -565,10 +565,9 @@ void cmExportFileGenerator::PopulateCompatibleInterfaceProperties( ifaceProperties); if (gtarget->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - getCompatibleInterfaceProperties(gtarget, ifaceProperties, ""); - - std::vector<std::string> configNames; - gtarget->Target->GetMakefile()->GetConfigurations(configNames); + std::vector<std::string> configNames = + gtarget->Target->GetMakefile()->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig); for (std::string const& cn : configNames) { getCompatibleInterfaceProperties(gtarget, ifaceProperties, cn); diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index e9d0da7397..45eaed04f6 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExportFileGenerator_h -#define cmExportFileGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -223,5 +222,3 @@ private: virtual std::string InstallNameDir(cmGeneratorTarget* target, const std::string& config) = 0; }; - -#endif diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h index 8883ffafbc..40978e0f49 100644 --- a/Source/cmExportInstallAndroidMKGenerator.h +++ b/Source/cmExportInstallAndroidMKGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExportInstallAndroidMKGenerator_h -#define cmExportInstallAndroidMKGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -69,5 +68,3 @@ protected: bool GenerateImportFileConfig(const std::string& config, std::vector<std::string>&) override; }; - -#endif diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index 987ec9ea7b..0b9b183d54 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -42,6 +42,9 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) std::string sep; for (std::unique_ptr<cmTargetExport> const& te : this->IEGen->GetExportSet()->GetTargetExports()) { + if (te->NamelinkOnly) { + continue; + } expectedTargets += sep + this->Namespace + te->Target->GetExportName(); sep = " "; if (this->ExportedTargets.insert(te->Target).second) { diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index 5fa812c586..2d8de9d83e 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExportInstallFileGenerator_h -#define cmExportInstallFileGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -103,5 +102,3 @@ protected: // The import file generated for each configuration. std::map<std::string, std::string> ConfigImportFiles; }; - -#endif diff --git a/Source/cmExportLibraryDependenciesCommand.h b/Source/cmExportLibraryDependenciesCommand.h index 230c90616d..1834bfa239 100644 --- a/Source/cmExportLibraryDependenciesCommand.h +++ b/Source/cmExportLibraryDependenciesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExportLibraryDependenciesCommand_h -#define cmExportLibraryDependenciesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmExportLibraryDependenciesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmExportSet.h b/Source/cmExportSet.h index f0d921f5e4..07deb1124d 100644 --- a/Source/cmExportSet.h +++ b/Source/cmExportSet.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExportSet_h -#define cmExportSet_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -61,5 +60,3 @@ public: */ cmExportSet& operator[](const std::string& name); }; - -#endif diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h index 7573427f6c..6bf5781e88 100644 --- a/Source/cmExportTryCompileFileGenerator.h +++ b/Source/cmExportTryCompileFileGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExportTryCompileFileGenerator_h -#define cmExportTryCompileFileGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -58,5 +57,3 @@ private: std::string Config; std::vector<std::string> Languages; }; - -#endif diff --git a/Source/cmExprParserHelper.h b/Source/cmExprParserHelper.h index 717acdcc0a..54dd6a4d11 100644 --- a/Source/cmExprParserHelper.h +++ b/Source/cmExprParserHelper.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExprParserHelper_h -#define cmExprParserHelper_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -58,5 +57,3 @@ private: #define YYSTYPE_IS_DECLARED #define YY_EXTRA_TYPE cmExprParserHelper* #define YY_DECL int cmExpr_yylex(YYSTYPE* yylvalp, yyscan_t yyscanner) - -#endif diff --git a/Source/cmExternalMakefileProjectGenerator.h b/Source/cmExternalMakefileProjectGenerator.h index 2b8d505a34..3ade67bea1 100644 --- a/Source/cmExternalMakefileProjectGenerator.h +++ b/Source/cmExternalMakefileProjectGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExternalMakefileProjectGenerator_h -#define cmExternalMakefileProjectGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -109,5 +108,3 @@ public: return p; } }; - -#endif diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx index 32b0ca93bf..87b8f9bd36 100644 --- a/Source/cmExtraCodeBlocksGenerator.cxx +++ b/Source/cmExtraCodeBlocksGenerator.cxx @@ -16,6 +16,7 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmSourceFile.h" #include "cmStateTypes.h" @@ -95,7 +96,7 @@ struct Tree std::string path; // only one component of the path std::vector<Tree> folders; std::set<std::string> files; - void InsertPath(const std::vector<std::string>& splitted, + void InsertPath(const std::vector<std::string>& split, std::vector<std::string>::size_type start, const std::string& fileName); void BuildVirtualFolder(cmXMLWriter& xml) const; @@ -106,34 +107,34 @@ struct Tree const std::string& fsPath) const; }; -void Tree::InsertPath(const std::vector<std::string>& splitted, +void Tree::InsertPath(const std::vector<std::string>& split, std::vector<std::string>::size_type start, const std::string& fileName) { - if (start == splitted.size()) { + if (start == split.size()) { files.insert(fileName); return; } for (Tree& folder : folders) { - if (folder.path == splitted[start]) { - if (start + 1 < splitted.size()) { - folder.InsertPath(splitted, start + 1, fileName); + if (folder.path == split[start]) { + if (start + 1 < split.size()) { + folder.InsertPath(split, start + 1, fileName); return; } - // last part of splitted + // last part of split folder.files.insert(fileName); return; } } // Not found in folders, thus insert Tree newFolder; - newFolder.path = splitted[start]; - if (start + 1 < splitted.size()) { - newFolder.InsertPath(splitted, start + 1, fileName); + newFolder.path = split[start]; + if (start + 1 < split.size()) { + newFolder.InsertPath(split, start + 1, fileName); folders.push_back(newFolder); return; } - // last part of splitted + // last part of split newFolder.files.insert(fileName); folders.push_back(newFolder); } @@ -224,11 +225,11 @@ void cmExtraCodeBlocksGenerator::CreateNewProjectFile( const std::string& relative = cmSystemTools::RelativePath( it.second[0]->GetSourceDirectory(), listFile); - std::vector<std::string> splitted; - cmSystemTools::SplitPath(relative, splitted, false); + std::vector<std::string> split; + cmSystemTools::SplitPath(relative, split, false); // Split filename from path - std::string fileName = *(splitted.end() - 1); - splitted.erase(splitted.end() - 1, splitted.end()); + std::string fileName = *(split.end() - 1); + split.erase(split.end() - 1, split.end()); // We don't want paths with CMakeFiles in them // or do we? @@ -236,13 +237,12 @@ void cmExtraCodeBlocksGenerator::CreateNewProjectFile( // // Also we can disable external (outside the project) files by setting ON // CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable. - const bool excludeExternal = - cmIsOn(it.second[0]->GetMakefile()->GetSafeDefinition( - "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES")); - if (!splitted.empty() && + const bool excludeExternal = it.second[0]->GetMakefile()->IsOn( + "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES"); + if (!split.empty() && (!excludeExternal || (relative.find("..") == std::string::npos)) && relative.find("CMakeFiles") == std::string::npos) { - tree.InsertPath(splitted, 1, fileName); + tree.InsertPath(split, 1, fileName); } } } @@ -370,7 +370,7 @@ void cmExtraCodeBlocksGenerator::CreateNewProjectFile( std::string lang = s->GetOrDetermineLanguage(); if (lang == "C" || lang == "CXX" || lang == "CUDA") { std::string const& srcext = s->GetExtension(); - isCFile = cm->IsSourceExtension(srcext); + isCFile = cm->IsACLikeSourceExtension(srcext); } std::string const& fullPath = s->ResolveFullPath(); @@ -380,9 +380,8 @@ void cmExtraCodeBlocksGenerator::CreateNewProjectFile( cmSystemTools::RelativePath(lg->GetSourceDirectory(), fullPath); // Do not add this file if it has ".." in relative path and // if CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable is on. - const bool excludeExternal = - cmIsOn(lg->GetMakefile()->GetSafeDefinition( - "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES")); + const bool excludeExternal = lg->GetMakefile()->IsOn( + "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES"); if (excludeExternal && (relative.find("..") != std::string::npos)) { continue; @@ -498,15 +497,15 @@ void cmExtraCodeBlocksGenerator::AppendTarget( if (target->GetType() == cmStateEnums::EXECUTABLE) { // Determine the directory where the executable target is created, and // set the working directory to this dir. - const char* runtimeOutputDir = + cmProp runtimeOutputDir = makefile->GetDefinition("CMAKE_RUNTIME_OUTPUT_DIRECTORY"); - if (runtimeOutputDir != nullptr) { - workingDir = runtimeOutputDir; + if (runtimeOutputDir) { + workingDir = *runtimeOutputDir; } else { - const char* executableOutputDir = + cmProp executableOutputDir = makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH"); - if (executableOutputDir != nullptr) { - workingDir = executableOutputDir; + if (executableOutputDir) { + workingDir = *executableOutputDir; } } } @@ -691,7 +690,8 @@ int cmExtraCodeBlocksGenerator::GetCBTargetType(cmGeneratorTarget* target) { switch (target->GetType()) { case cmStateEnums::EXECUTABLE: - if ((target->GetPropertyAsBool("WIN32_EXECUTABLE")) || + if ((target->IsWin32Executable( + target->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"))) || (target->GetPropertyAsBool("MACOSX_BUNDLE"))) { return 0; } diff --git a/Source/cmExtraCodeBlocksGenerator.h b/Source/cmExtraCodeBlocksGenerator.h index d9f92bde5e..cada5dd44c 100644 --- a/Source/cmExtraCodeBlocksGenerator.h +++ b/Source/cmExtraCodeBlocksGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExtraCodeBlocksGenerator_h -#define cmExtraCodeBlocksGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -51,5 +50,3 @@ private: const cmLocalGenerator* lg, const std::string& compiler, const std::string& makeFlags); }; - -#endif diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx index bf7555d98a..95cfb0a451 100644 --- a/Source/cmExtraCodeLiteGenerator.cxx +++ b/Source/cmExtraCodeLiteGenerator.cxx @@ -227,8 +227,7 @@ std::string cmExtraCodeLiteGenerator::CollectSourceFiles( cmSystemTools::LowerCase(s->GetExtension()); // check whether it is a source or a include file // then put it accordingly into one of the two containers - if (cm->IsSourceExtension(extLower) || cm->IsCudaExtension(extLower) || - cm->IsFortranExtension(extLower)) { + if (cm->IsAKnownSourceExtension(extLower)) { cFiles[fullPath] = s; } else { otherFiles.insert(fullPath); diff --git a/Source/cmExtraCodeLiteGenerator.h b/Source/cmExtraCodeLiteGenerator.h index 0ce90b093a..2478585b69 100644 --- a/Source/cmExtraCodeLiteGenerator.h +++ b/Source/cmExtraCodeLiteGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalCodeLiteGenerator_h -#define cmGlobalCodeLiteGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -68,5 +67,3 @@ public: void CreateNewProjectFile(const cmGeneratorTarget* lg, const std::string& filename); }; - -#endif diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx index 7bc45360d1..ccfd72798a 100644 --- a/Source/cmExtraEclipseCDT4Generator.cxx +++ b/Source/cmExtraEclipseCDT4Generator.cxx @@ -189,9 +189,9 @@ void cmExtraEclipseCDT4Generator::CreateSettingsResourcePrefsFile() } fout << "eclipse.preferences.version=1\n"; - const char* encoding = mf->GetDefinition("CMAKE_ECLIPSE_RESOURCE_ENCODING"); + cmProp encoding = mf->GetDefinition("CMAKE_ECLIPSE_RESOURCE_ENCODING"); if (encoding) { - fout << "encoding/<project>=" << encoding << '\n'; + fout << "encoding/<project>=" << *encoding << '\n'; } } @@ -604,7 +604,7 @@ void cmExtraEclipseCDT4Generator::AppendIncludeDirectories( void cmExtraEclipseCDT4Generator::CreateCProjectFile() const { - std::set<std::string> emmited; + std::set<std::string> emitted; const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0]; const cmMakefile* mf = lg->GetMakefile(); @@ -751,7 +751,7 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const xml.EndElement(); // add pre-processor definitions to allow eclipse to gray out sections - emmited.clear(); + emitted.clear(); for (const auto& lgen : this->GlobalGenerator->GetLocalGenerators()) { if (cmProp cdefs = @@ -780,8 +780,8 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const } // insert the definition if not already added. - if (emmited.find(def) == emmited.end()) { - emmited.insert(def); + if (emitted.find(def) == emitted.end()) { + emitted.insert(def); xml.StartElement("pathentry"); xml.Attribute("kind", "mac"); xml.Attribute("name", def); @@ -793,11 +793,11 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const } } // add system defined c macros - const char* cDefs = + cmProp cDefs = mf->GetDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS"); if (this->CEnabled && cDefs) { // Expand the list. - std::vector<std::string> defs = cmExpandedList(cDefs, true); + std::vector<std::string> defs = cmExpandedList(*cDefs, true); // the list must contain only definition-value pairs: if ((defs.size() % 2) == 0) { @@ -812,8 +812,8 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const } // insert the definition if not already added. - if (emmited.find(def) == emmited.end()) { - emmited.insert(def); + if (emitted.find(def) == emitted.end()) { + emitted.insert(def); xml.StartElement("pathentry"); xml.Attribute("kind", "mac"); xml.Attribute("name", def); @@ -825,11 +825,11 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const } } // add system defined c++ macros - const char* cxxDefs = + cmProp cxxDefs = mf->GetDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS"); if (this->CXXEnabled && cxxDefs) { // Expand the list. - std::vector<std::string> defs = cmExpandedList(cxxDefs, true); + std::vector<std::string> defs = cmExpandedList(*cxxDefs, true); // the list must contain only definition-value pairs: if ((defs.size() % 2) == 0) { @@ -844,8 +844,8 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const } // insert the definition if not already added. - if (emmited.find(def) == emmited.end()) { - emmited.insert(def); + if (emitted.find(def) == emitted.end()) { + emitted.insert(def); xml.StartElement("pathentry"); xml.Attribute("kind", "mac"); xml.Attribute("name", def); @@ -858,7 +858,7 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const } // include dirs - emmited.clear(); + emitted.clear(); for (const auto& lgen : this->GlobalGenerator->GetLocalGenerators()) { const auto& targets = lgen->GetGeneratorTargets(); for (const auto& target : targets) { @@ -868,7 +868,7 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const std::vector<std::string> includeDirs; std::string config = mf->GetSafeDefinition("CMAKE_BUILD_TYPE"); lgen->GetIncludeDirectories(includeDirs, target.get(), "C", config); - this->AppendIncludeDirectories(xml, includeDirs, emmited); + this->AppendIncludeDirectories(xml, includeDirs, emitted); } } // now also the system include directories, in case we found them in @@ -879,14 +879,14 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const std::string systemIncludeDirs = mf->GetSafeDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS"); std::vector<std::string> dirs = cmExpandedList(systemIncludeDirs); - this->AppendIncludeDirectories(xml, dirs, emmited); + this->AppendIncludeDirectories(xml, dirs, emitted); } compiler = mf->GetSafeDefinition("CMAKE_CXX_COMPILER"); if (this->CXXEnabled && !compiler.empty()) { std::string systemIncludeDirs = mf->GetSafeDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS"); std::vector<std::string> dirs = cmExpandedList(systemIncludeDirs); - this->AppendIncludeDirectories(xml, dirs, emmited); + this->AppendIncludeDirectories(xml, dirs, emitted); } xml.EndElement(); // storageModule @@ -895,7 +895,7 @@ void cmExtraEclipseCDT4Generator::CreateCProjectFile() const xml.StartElement("storageModule"); xml.Attribute("moduleId", "org.eclipse.cdt.make.core.buildtargets"); xml.StartElement("buildTargets"); - emmited.clear(); + emitted.clear(); const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); const std::string& makeArgs = mf->GetSafeDefinition("CMAKE_ECLIPSE_MAKE_ARGUMENTS"); diff --git a/Source/cmExtraEclipseCDT4Generator.h b/Source/cmExtraEclipseCDT4Generator.h index a7aa549ddb..c4ed577b33 100644 --- a/Source/cmExtraEclipseCDT4Generator.h +++ b/Source/cmExtraEclipseCDT4Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExtraEclipseCDT4Generator_h -#define cmExtraEclipseCDT4Generator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -106,5 +105,3 @@ private: bool CEnabled; bool CXXEnabled; }; - -#endif diff --git a/Source/cmExtraKateGenerator.cxx b/Source/cmExtraKateGenerator.cxx index 01fac5a73c..54c311452c 100644 --- a/Source/cmExtraKateGenerator.cxx +++ b/Source/cmExtraKateGenerator.cxx @@ -13,6 +13,7 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmSourceFile.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -127,10 +128,10 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg, // only add the "edit_cache" target if it's not ccmake, because // this will not work within the IDE if (targetName == "edit_cache") { - const char* editCommand = + cmProp editCommand = localGen->GetMakefile()->GetDefinition("CMAKE_EDIT_COMMAND"); if (editCommand == nullptr || - strstr(editCommand, "ccmake") != nullptr) { + strstr(editCommand->c_str(), "ccmake") != nullptr) { insertTarget = false; } } diff --git a/Source/cmExtraKateGenerator.h b/Source/cmExtraKateGenerator.h index 1fb81b482d..c66ddbf0c3 100644 --- a/Source/cmExtraKateGenerator.h +++ b/Source/cmExtraKateGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExtraKateGenerator_h -#define cmExtraKateGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -43,5 +42,3 @@ private: std::string ProjectName; bool UseNinja; }; - -#endif diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx index 613a943747..7c3614435b 100644 --- a/Source/cmExtraSublimeTextGenerator.cxx +++ b/Source/cmExtraSublimeTextGenerator.cxx @@ -439,13 +439,13 @@ bool cmExtraSublimeTextGenerator::Open(const std::string& bindir, const std::string& projectName, bool dryRun) { - const char* sublExecutable = + cmProp sublExecutable = this->GlobalGenerator->GetCMakeInstance()->GetCacheDefinition( "CMAKE_SUBLIMETEXT_EXECUTABLE"); if (!sublExecutable) { return false; } - if (cmIsNOTFOUND(sublExecutable)) { + if (cmIsNOTFOUND(*sublExecutable)) { return false; } @@ -455,5 +455,5 @@ bool cmExtraSublimeTextGenerator::Open(const std::string& bindir, } return cmSystemTools::RunSingleCommand( - { sublExecutable, "--project", filename }); + { *sublExecutable, "--project", filename }); } diff --git a/Source/cmExtraSublimeTextGenerator.h b/Source/cmExtraSublimeTextGenerator.h index 078cfe7c8d..671b65af8b 100644 --- a/Source/cmExtraSublimeTextGenerator.h +++ b/Source/cmExtraSublimeTextGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmExtraSublimeTextGenerator_h -#define cmExtraSublimeTextGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -75,5 +74,3 @@ private: bool ExcludeBuildFolder; std::string EnvSettings; }; - -#endif diff --git a/Source/cmFLTKWrapUICommand.h b/Source/cmFLTKWrapUICommand.h index bb56dbd44f..7c1fc52965 100644 --- a/Source/cmFLTKWrapUICommand.h +++ b/Source/cmFLTKWrapUICommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFLTKWrapUICommand_h -#define cmFLTKWrapUICommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmFLTKWrapUICommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmFSPermissions.h b/Source/cmFSPermissions.h index fef72e6164..78f22405fd 100644 --- a/Source/cmFSPermissions.h +++ b/Source/cmFSPermissions.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFSPermissions_h -#define cmFSPermissions_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -41,5 +40,3 @@ static const mode_t mode_setgid = S_ISGID; bool stringToModeT(std::string const& arg, mode_t& permissions); } // ns - -#endif diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx index 594969b1b1..c2ab2f10b0 100644 --- a/Source/cmFileAPI.cxx +++ b/Source/cmFileAPI.cxx @@ -665,7 +665,7 @@ std::string cmFileAPI::NoSupportedVersion( // The "codemodel" object kind. -static unsigned int const CodeModelV2Minor = 1; +static unsigned int const CodeModelV2Minor = 2; void cmFileAPI::BuildClientRequestCodeModel( ClientRequest& r, std::vector<RequestVersion> const& versions) diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h index ae076129aa..086a92a2be 100644 --- a/Source/cmFileAPI.h +++ b/Source/cmFileAPI.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileAPI_h -#define cmFileAPI_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -205,5 +204,3 @@ private: ClientRequest& r, std::vector<RequestVersion> const& versions); Json::Value BuildInternalTest(Object const& object); }; - -#endif diff --git a/Source/cmFileAPICMakeFiles.h b/Source/cmFileAPICMakeFiles.h index 1ae1e4f1ab..5b48ed3032 100644 --- a/Source/cmFileAPICMakeFiles.h +++ b/Source/cmFileAPICMakeFiles.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileAPICMakeFiles_h -#define cmFileAPICMakeFiles_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -11,5 +10,3 @@ class cmFileAPI; extern Json::Value cmFileAPICMakeFilesDump(cmFileAPI& fileAPI, unsigned long version); - -#endif diff --git a/Source/cmFileAPICache.h b/Source/cmFileAPICache.h index 2f30c76d5d..bd9feeb4f9 100644 --- a/Source/cmFileAPICache.h +++ b/Source/cmFileAPICache.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileAPICache_h -#define cmFileAPICache_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -11,5 +10,3 @@ class cmFileAPI; extern Json::Value cmFileAPICacheDump(cmFileAPI& fileAPI, unsigned long version); - -#endif diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index fe331ec8d4..4a53c8a8c0 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -175,6 +175,38 @@ public: } }; +template <typename T> +class JBTs +{ +public: + JBTs(T v = T(), std::vector<JBTIndex> ids = std::vector<JBTIndex>()) + : Value(std::move(v)) + , Backtraces(std::move(ids)) + { + } + T Value; + std::vector<JBTIndex> Backtraces; + friend bool operator==(JBTs<T> const& l, JBTs<T> const& r) + { + if ((l.Value == r.Value) && (l.Backtraces.size() == r.Backtraces.size())) { + for (size_t i = 0; i < l.Backtraces.size(); i++) { + if (l.Backtraces[i].Index != r.Backtraces[i].Index) { + return false; + } + } + } + return true; + } + static bool ValueEq(JBTs<T> const& l, JBTs<T> const& r) + { + return l.Value == r.Value; + } + static bool ValueLess(JBTs<T> const& l, JBTs<T> const& r) + { + return l.Value < r.Value; + } +}; + class BacktraceData { std::string TopSource; @@ -277,6 +309,7 @@ struct CompileData std::string Language; std::string Sysroot; + JBTs<std::string> LanguageStandard; std::vector<JBT<std::string>> Flags; std::vector<JBT<std::string>> Defines; std::vector<JBT<std::string>> PrecompileHeaders; @@ -287,6 +320,7 @@ struct CompileData return (l.Language == r.Language && l.Sysroot == r.Sysroot && l.Flags == r.Flags && l.Defines == r.Defines && l.PrecompileHeaders == r.PrecompileHeaders && + l.LanguageStandard == r.LanguageStandard && l.Includes == r.Includes); } }; @@ -320,6 +354,12 @@ struct hash<CompileData> result = result ^ hash<std::string>()(i.Value) ^ hash<Json::ArrayIndex>()(i.Backtrace.Index); } + if (!in.LanguageStandard.Value.empty()) { + result = result ^ hash<std::string>()(in.LanguageStandard.Value); + for (JBTIndex backtrace : in.LanguageStandard.Backtraces) { + result = result ^ hash<Json::ArrayIndex>()(backtrace.Index); + } + } return result; } }; @@ -363,6 +403,16 @@ class Target return JBT<T>(bt.Value, this->Backtraces.Add(bt.Backtrace)); } + template <typename T> + JBTs<T> ToJBTs(BTs<T> const& bts) + { + std::vector<JBTIndex> ids; + for (cmListFileBacktrace const& backtrace : bts.Backtraces) { + ids.emplace_back(this->Backtraces.Add(backtrace)); + } + return JBTs<T>(bts.Value, ids); + } + void ProcessLanguages(); void ProcessLanguage(std::string const& lang); @@ -377,6 +427,7 @@ class Target Json::Value DumpCompileData(CompileData const& cd); Json::Value DumpInclude(CompileData::IncludeEntry const& inc); Json::Value DumpPrecompileHeader(JBT<std::string> const& header); + Json::Value DumpLanguageStandard(JBTs<std::string> const& standard); Json::Value DumpDefine(JBT<std::string> const& def); Json::Value DumpSources(); Json::Value DumpSource(cmGeneratorTarget::SourceAndKind const& sk, @@ -438,7 +489,7 @@ Json::Value Codemodel::DumpConfigurations() const auto& makefiles = gg->GetMakefiles(); if (!makefiles.empty()) { std::vector<std::string> const& configs = - makefiles[0]->GetGeneratorConfigs(); + makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); for (std::string const& config : configs) { configurations.append(this->DumpConfiguration(config)); } @@ -574,7 +625,7 @@ Json::Value CodemodelConfig::DumpTargets() for (cmGeneratorTarget* gt : targetList) { if (gt->GetType() == cmStateEnums::GLOBAL_TARGET || - gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + !gt->IsInBuildSystem()) { continue; } @@ -801,12 +852,12 @@ void Target::ProcessLanguage(std::string const& lang) { CompileData& cd = this->CompileDataMap[lang]; cd.Language = lang; - if (const char* sysrootCompile = + if (cmProp sysrootCompile = this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) { - cd.Sysroot = sysrootCompile; - } else if (const char* sysroot = + cd.Sysroot = *sysrootCompile; + } else if (cmProp sysroot = this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) { - cd.Sysroot = sysroot; + cd.Sysroot = *sysroot; } cmLocalGenerator* lg = this->GT->GetLocalGenerator(); { @@ -838,6 +889,11 @@ void Target::ProcessLanguage(std::string const& lang) for (BT<std::string> const& pch : precompileHeaders) { cd.PrecompileHeaders.emplace_back(this->ToJBT(pch)); } + BTs<std::string> const* languageStandard = + this->GT->GetLanguageStandardProperty(lang, this->Config); + if (languageStandard) { + cd.LanguageStandard = this->ToJBTs(*languageStandard); + } } Json::ArrayIndex Target::AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si) @@ -996,6 +1052,9 @@ CompileData Target::MergeCompileData(CompileData const& fd) // All compile groups share the precompile headers of the target. cd.PrecompileHeaders = td.PrecompileHeaders; + // All compile groups share the language standard of the target. + cd.LanguageStandard = td.LanguageStandard; + // Use target-wide flags followed by source-specific flags. cd.Flags.reserve(td.Flags.size() + fd.Flags.size()); cd.Flags.insert(cd.Flags.end(), td.Flags.begin(), td.Flags.end()); @@ -1153,6 +1212,10 @@ Json::Value Target::DumpCompileData(CompileData const& cd) } result["precompileHeaders"] = std::move(precompileHeaders); } + if (!cd.LanguageStandard.Value.empty()) { + result["languageStandard"] = + this->DumpLanguageStandard(cd.LanguageStandard); + } return result; } @@ -1176,6 +1239,20 @@ Json::Value Target::DumpPrecompileHeader(JBT<std::string> const& header) return precompileHeader; } +Json::Value Target::DumpLanguageStandard(JBTs<std::string> const& standard) +{ + Json::Value languageStandard = Json::objectValue; + languageStandard["standard"] = standard.Value; + if (!standard.Backtraces.empty()) { + Json::Value backtraces = Json::arrayValue; + for (JBTIndex backtrace : standard.Backtraces) { + backtraces.append(backtrace.Index); + } + languageStandard["backtraces"] = backtraces; + } + return languageStandard; +} + Json::Value Target::DumpDefine(JBT<std::string> const& def) { Json::Value define = Json::objectValue; @@ -1327,12 +1404,12 @@ Json::Value Target::DumpLink() link["commandFragments"] = std::move(commandFragments); } } - if (const char* sysrootLink = + if (cmProp sysrootLink = this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) { - link["sysroot"] = this->DumpSysroot(sysrootLink); - } else if (const char* sysroot = + link["sysroot"] = this->DumpSysroot(*sysrootLink); + } else if (cmProp sysroot = this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) { - link["sysroot"] = this->DumpSysroot(sysroot); + link["sysroot"] = this->DumpSysroot(*sysroot); } if (this->GT->IsIPOEnabled(lang, this->Config)) { link["lto"] = true; diff --git a/Source/cmFileAPICodemodel.h b/Source/cmFileAPICodemodel.h index a6c6bddae5..263f6758d2 100644 --- a/Source/cmFileAPICodemodel.h +++ b/Source/cmFileAPICodemodel.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileAPICodemodel_h -#define cmFileAPICodemodel_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -11,5 +10,3 @@ class cmFileAPI; extern Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, unsigned long version); - -#endif diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 7101e229fa..8a3aad2dfa 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -28,8 +28,10 @@ #include "cmAlgorithms.h" #include "cmArgumentParser.h" +#include "cmCMakePath.h" #include "cmCryptoHash.h" #include "cmExecutionStatus.h" +#include "cmFSPermissions.h" #include "cmFileCopier.h" #include "cmFileInstaller.h" #include "cmFileLockPool.h" @@ -43,6 +45,7 @@ #include "cmMessageType.h" #include "cmNewLineStyle.h" #include "cmPolicies.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmRuntimeDependencyArchive.h" #include "cmState.h" @@ -1232,6 +1235,50 @@ bool HandleInstallCommand(std::vector<std::string> const& args, return installer.Run(args); } +bool HandleRealPathCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + if (args.size() < 3) { + status.SetError("REAL_PATH requires a path and an output variable"); + return false; + } + + struct Arguments + { + std::string BaseDirectory; + }; + static auto const parser = cmArgumentParser<Arguments>{}.Bind( + "BASE_DIRECTORY"_s, &Arguments::BaseDirectory); + + std::vector<std::string> unparsedArguments; + std::vector<std::string> keywordsMissingValue; + std::vector<std::string> parsedKeywords; + auto arguments = + parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments, + &keywordsMissingValue, &parsedKeywords); + + if (!unparsedArguments.empty()) { + status.SetError("REAL_PATH called with unexpected arguments"); + return false; + } + if (!keywordsMissingValue.empty()) { + status.SetError("BASE_DIRECTORY requires a value"); + return false; + } + + if (parsedKeywords.empty()) { + arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); + } + + cmCMakePath path(args[1]); + path = path.Absolute(arguments.BaseDirectory).Normal(); + auto realPath = cmSystemTools::GetRealPath(path.GenericString()); + + status.GetMakefile().AddDefinition(args[2], realPath); + + return true; +} + bool HandleRelativePathCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { @@ -1394,8 +1441,10 @@ size_t cmWriteToFileCallback(void* ptr, size_t size, size_t nmemb, void* data) { int realsize = static_cast<int>(size * nmemb); cmsys::ofstream* fout = static_cast<cmsys::ofstream*>(data); - const char* chPtr = static_cast<char*>(ptr); - fout->write(chPtr, realsize); + if (fout) { + const char* chPtr = static_cast<char*>(ptr); + fout->write(chPtr, realsize); + } return realsize; } @@ -1551,22 +1600,21 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, { #if !defined(CMAKE_BOOTSTRAP) auto i = args.begin(); - if (args.size() < 3) { - status.SetError("DOWNLOAD must be called with at least three arguments."); + if (args.size() < 2) { + status.SetError("DOWNLOAD must be called with at least two arguments."); return false; } ++i; // Get rid of subcommand std::string url = *i; ++i; - std::string file = *i; - ++i; + std::string file; long timeout = 0; long inactivity_timeout = 0; std::string logVar; std::string statusVar; bool tls_verify = status.GetMakefile().IsOn("CMAKE_TLS_VERIFY"); - const char* cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO"); + cmProp cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO"); std::string netrc_level = status.GetMakefile().GetSafeDefinition("CMAKE_NETRC"); std::string netrc_file = @@ -1621,7 +1669,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, } else if (*i == "TLS_CAINFO") { ++i; if (i != args.end()) { - cainfo = i->c_str(); + cainfo = &(*i); } else { status.SetError("DOWNLOAD missing file value for TLS_CAINFO."); return false; @@ -1690,6 +1738,8 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, return false; } curl_headers.push_back(*i); + } else if (file.empty()) { + file = *i; } else { // Do not return error for compatibility reason. std::string err = cmStrCat("Unexpected argument: ", *i); @@ -1697,11 +1747,18 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, } ++i; } + // Can't calculate hash if we don't save the file. + // TODO Incrementally calculate hash in the write callback as the file is + // being downloaded so this check can be relaxed. + if (file.empty() && hash) { + status.SetError("DOWNLOAD cannot calculate hash if file is not saved."); + return false; + } // If file exists already, and caller specified an expected md5 or sha, // and the existing file already has the expected hash, then simply // return. // - if (cmSystemTools::FileExists(file) && hash.get()) { + if (!file.empty() && cmSystemTools::FileExists(file) && hash.get()) { std::string msg; std::string actualHash = hash->HashFile(file); if (actualHash == expectedHash) { @@ -1716,20 +1773,26 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, // Make sure parent directory exists so we can write to the file // as we receive downloaded bits from curl... // - std::string dir = cmSystemTools::GetFilenamePath(file); - if (!dir.empty() && !cmSystemTools::FileExists(dir) && - !cmSystemTools::MakeDirectory(dir)) { - std::string errstring = "DOWNLOAD error: cannot create directory '" + dir + - "' - Specify file by full path name and verify that you " - "have directory creation and file write privileges."; - status.SetError(errstring); - return false; + if (!file.empty()) { + std::string dir = cmSystemTools::GetFilenamePath(file); + if (!dir.empty() && !cmSystemTools::FileExists(dir) && + !cmSystemTools::MakeDirectory(dir)) { + std::string errstring = "DOWNLOAD error: cannot create directory '" + + dir + + "' - Specify file by full path name and verify that you " + "have directory creation and file write privileges."; + status.SetError(errstring); + return false; + } } - cmsys::ofstream fout(file.c_str(), std::ios::binary); - if (!fout) { - status.SetError("DOWNLOAD cannot open file for write."); - return false; + cmsys::ofstream fout; + if (!file.empty()) { + fout.open(file.c_str(), std::ios::binary); + if (!fout) { + status.SetError("DOWNLOAD cannot open file for write."); + return false; + } } # if defined(_WIN32) @@ -1773,7 +1836,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, // check to see if a CAINFO file has been specified // command arg comes first - std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo); + std::string const& cainfo_err = cmCurlSetCAInfo(curl, cmToCStr(cainfo)); if (!cainfo_err.empty()) { status.SetError(cainfo_err); return false; @@ -1791,7 +1854,8 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, cmFileCommandVectorOfChar chunkDebug; - res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, &fout); + res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, + file.empty() ? nullptr : &fout); check_curl_result(res, "DOWNLOAD cannot set write data: "); res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &chunkDebug); @@ -1865,8 +1929,10 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, // Explicitly flush/close so we can measure the md5 accurately. // - fout.flush(); - fout.close(); + if (!file.empty()) { + fout.flush(); + fout.close(); + } // Verify MD5 sum if requested: // @@ -1936,7 +2002,7 @@ bool HandleUploadCommand(std::vector<std::string> const& args, std::string statusVar; bool showProgress = false; bool tls_verify = status.GetMakefile().IsOn("CMAKE_TLS_VERIFY"); - const char* cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO"); + cmProp cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO"); std::string userpwd; std::string netrc_level = status.GetMakefile().GetSafeDefinition("CMAKE_NETRC"); @@ -1989,7 +2055,7 @@ bool HandleUploadCommand(std::vector<std::string> const& args, } else if (*i == "TLS_CAINFO") { ++i; if (i != args.end()) { - cainfo = i->c_str(); + cainfo = &(*i); } else { status.SetError("UPLOAD missing file value for TLS_CAINFO."); return false; @@ -2090,7 +2156,7 @@ bool HandleUploadCommand(std::vector<std::string> const& args, // check to see if a CAINFO file has been specified // command arg comes first - std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo); + std::string const& cainfo_err = cmCurlSetCAInfo(curl, cmToCStr(cainfo)); if (!cainfo_err.empty()) { status.SetError(cainfo_err); return false; @@ -2221,6 +2287,7 @@ bool HandleUploadCommand(std::vector<std::string> const& args, } void AddEvaluationFile(const std::string& inputName, + const std::string& targetName, const std::string& outputExpr, const std::string& condition, bool inputIsContent, cmExecutionStatus& status) @@ -2236,7 +2303,8 @@ void AddEvaluationFile(const std::string& inputName, conditionGe.Parse(condition); status.GetMakefile().AddEvaluationFile( - inputName, std::move(outputCge), std::move(conditionCge), inputIsContent); + inputName, targetName, std::move(outputCge), std::move(conditionCge), + inputIsContent); } bool HandleGenerateCommand(std::vector<std::string> const& args, @@ -2250,23 +2318,36 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, status.SetError("Incorrect arguments to GENERATE subcommand."); return false; } + std::string condition; - if (args.size() > 5) { - if (args[5] != "CONDITION") { + std::string target; + + for (std::size_t i = 5; i < args.size();) { + const std::string& arg = args[i++]; + + if (args.size() - i == 0) { status.SetError("Incorrect arguments to GENERATE subcommand."); return false; } - if (args.size() != 7) { - status.SetError("Incorrect arguments to GENERATE subcommand."); + + const std::string& value = args[i++]; + + if (value.empty()) { + status.SetError( + arg + " of sub-command GENERATE must not be empty if specified."); return false; } - condition = args[6]; - if (condition.empty()) { - status.SetError("CONDITION of sub-command GENERATE must not be empty if " - "specified."); + + if (arg == "CONDITION") { + condition = value; + } else if (arg == "TARGET") { + target = value; + } else { + status.SetError("Unknown argument to GENERATE subcommand."); return false; } } + std::string output = args[2]; const bool inputIsContent = args[3] != "INPUT"; if (inputIsContent && args[3] != "CONTENT") { @@ -2275,7 +2356,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, } std::string input = args[4]; - AddEvaluationFile(input, output, condition, inputIsContent, status); + AddEvaluationFile(input, target, output, condition, inputIsContent, status); return true; } @@ -2864,15 +2945,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, std::string outputFile = cmSystemTools::CollapseFullPath( args[2], status.GetMakefile().GetCurrentBinaryDirectory()); - std::string::size_type pos = input.find_first_of("<>"); - if (pos != std::string::npos) { - status.SetError(cmStrCat("CONFIGURE called with CONTENT containing a \"", - input[pos], - "\". This character is not allowed.")); - return false; - } - - pos = outputFile.find_first_of("<>"); + std::string::size_type pos = outputFile.find_first_of("<>"); if (pos != std::string::npos) { status.SetError(cmStrCat("CONFIGURE called with OUTPUT containing a \"", outputFile[pos], @@ -2918,7 +2991,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, } fout.SetCopyIfDifferent(true); - // copy intput to output and expand variables from input at the same time + // copy input to output and expand variables from input at the same time std::stringstream sin(input, std::ios::in); std::string inLine; std::string outLine; @@ -2942,18 +3015,21 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, std::string Output; std::string Format; std::string Compression; + std::string CompressionLevel; std::string MTime; bool Verbose = false; std::vector<std::string> Paths; }; - static auto const parser = cmArgumentParser<Arguments>{} - .Bind("OUTPUT"_s, &Arguments::Output) - .Bind("FORMAT"_s, &Arguments::Format) - .Bind("COMPRESSION"_s, &Arguments::Compression) - .Bind("MTIME"_s, &Arguments::MTime) - .Bind("VERBOSE"_s, &Arguments::Verbose) - .Bind("PATHS"_s, &Arguments::Paths); + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("OUTPUT"_s, &Arguments::Output) + .Bind("FORMAT"_s, &Arguments::Format) + .Bind("COMPRESSION"_s, &Arguments::Compression) + .Bind("COMPRESSION_LEVEL"_s, &Arguments::CompressionLevel) + .Bind("MTIME"_s, &Arguments::MTime) + .Bind("VERBOSE"_s, &Arguments::Verbose) + .Bind("PATHS"_s, &Arguments::Paths); std::vector<std::string> unrecognizedArguments; std::vector<std::string> keywordsMissingValues; @@ -2967,9 +3043,9 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, return false; } - const std::vector<std::string> LIST_ARGS = { "OUTPUT", "FORMAT", - "COMPRESSION", "MTIME", - "PATHS" }; + const std::vector<std::string> LIST_ARGS = { + "OUTPUT", "FORMAT", "COMPRESSION", "COMPRESSION_LEVEL", "MTIME", "PATHS" + }; auto kwbegin = keywordsMissingValues.cbegin(); auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); if (kwend != kwbegin) { @@ -3018,6 +3094,33 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, return false; } + int compressionLevel = 0; + if (!parsedArgs.CompressionLevel.empty()) { + if (parsedArgs.CompressionLevel.size() != 1 && + !std::isdigit(parsedArgs.CompressionLevel[0])) { + status.SetError(cmStrCat("compression level ", + parsedArgs.CompressionLevel, + " should be in range 0 to 9")); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + compressionLevel = std::stoi(parsedArgs.CompressionLevel); + if (compressionLevel < 0 || compressionLevel > 9) { + status.SetError(cmStrCat("compression level ", + parsedArgs.CompressionLevel, + " should be in range 0 to 9")); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + if (compress == cmSystemTools::TarCompressNone) { + status.SetError(cmStrCat("compression level is not supported for " + "compression \"None\"", + parsedArgs.Compression)); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } + if (parsedArgs.Paths.empty()) { status.SetError("ARCHIVE_CREATE requires a non-empty list of PATHS"); cmSystemTools::SetFatalErrorOccured(); @@ -3026,7 +3129,7 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, if (!cmSystemTools::CreateTar(parsedArgs.Output, parsedArgs.Paths, compress, parsedArgs.Verbose, parsedArgs.MTime, - parsedArgs.Format)) { + parsedArgs.Format, compressionLevel)) { status.SetError(cmStrCat("failed to compress: ", parsedArgs.Output)); cmSystemTools::SetFatalErrorOccured(); return false; @@ -3126,6 +3229,163 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return true; } +bool ValidateAndConvertPermissions(const std::vector<std::string>& permissions, + mode_t& perms, cmExecutionStatus& status) +{ + for (const auto& i : permissions) { + if (!cmFSPermissions::stringToModeT(i, perms)) { + status.SetError(i + " is an invalid permission specifier"); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + } + return true; +} + +bool SetPermissions(const std::string& filename, const mode_t& perms, + cmExecutionStatus& status) +{ + if (!cmSystemTools::SetPermissions(filename, perms)) { + status.SetError("Failed to set permissions for " + filename); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + return true; +} + +bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, + cmExecutionStatus& status) +{ + mode_t perms = 0; + mode_t fperms = 0; + mode_t dperms = 0; + cmsys::Glob globber; + + globber.SetRecurse(recurse); + globber.SetRecurseListDirs(recurse); + + struct Arguments + { + std::vector<std::string> Permissions; + std::vector<std::string> FilePermissions; + std::vector<std::string> DirectoryPermissions; + }; + + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("PERMISSIONS"_s, &Arguments::Permissions) + .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions) + .Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions); + + std::vector<std::string> pathEntries; + std::vector<std::string> keywordsMissingValues; + Arguments parsedArgs = parser.Parse(cmMakeRange(args).advance(1), + &pathEntries, &keywordsMissingValues); + + // check validity of arguments + if (parsedArgs.Permissions.empty() && parsedArgs.FilePermissions.empty() && + parsedArgs.DirectoryPermissions.empty()) // no permissions given + { + status.SetError("No permissions given"); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (!parsedArgs.Permissions.empty() && !parsedArgs.FilePermissions.empty() && + !parsedArgs.DirectoryPermissions.empty()) // all keywords are used + { + status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or " + "DIRECTORY_PERMISSIONS from the invocation"); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (!keywordsMissingValues.empty()) { + for (const auto& i : keywordsMissingValues) { + status.SetError(i + " is not given any arguments"); + cmSystemTools::SetFatalErrorOccured(); + } + return false; + } + + // validate permissions + bool validatePermissions = + ValidateAndConvertPermissions(parsedArgs.Permissions, perms, status) && + ValidateAndConvertPermissions(parsedArgs.FilePermissions, fperms, + status) && + ValidateAndConvertPermissions(parsedArgs.DirectoryPermissions, dperms, + status); + if (!validatePermissions) { + return false; + } + + std::vector<std::string> allPathEntries; + + if (recurse) { + std::vector<std::string> tempPathEntries; + for (const auto& i : pathEntries) { + if (cmSystemTools::FileIsDirectory(i)) { + globber.FindFiles(i + "/*"); + tempPathEntries = globber.GetFiles(); + allPathEntries.insert(allPathEntries.end(), tempPathEntries.begin(), + tempPathEntries.end()); + allPathEntries.emplace_back(i); + } else { + allPathEntries.emplace_back(i); // We validate path entries below + } + } + } else { + allPathEntries = std::move(pathEntries); + } + + // chmod + for (const auto& i : allPathEntries) { + if (!(cmSystemTools::FileExists(i) || cmSystemTools::FileIsDirectory(i))) { + status.SetError(cmStrCat("does not exist:\n ", i)); + cmSystemTools::SetFatalErrorOccured(); + return false; + } + + if (cmSystemTools::FileExists(i, true)) { + bool success = true; + const mode_t& filePermissions = + parsedArgs.FilePermissions.empty() ? perms : fperms; + if (filePermissions) { + success = SetPermissions(i, filePermissions, status); + } + if (!success) { + return false; + } + } + + else if (cmSystemTools::FileIsDirectory(i)) { + bool success = true; + const mode_t& directoryPermissions = + parsedArgs.DirectoryPermissions.empty() ? perms : dperms; + if (directoryPermissions) { + success = SetPermissions(i, directoryPermissions, status); + } + if (!success) { + return false; + } + } + } + + return true; +} + +bool HandleChmodCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleChmodCommandImpl(args, false, status); +} + +bool HandleChmodRecurseCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + return HandleChmodCommandImpl(args, true, status); +} + } // namespace bool cmFileCommand(std::vector<std::string> const& args, @@ -3167,6 +3427,7 @@ bool cmFileCommand(std::vector<std::string> const& args, { "RPATH_CHECK"_s, HandleRPathCheckCommand }, { "RPATH_REMOVE"_s, HandleRPathRemoveCommand }, { "READ_ELF"_s, HandleReadElfCommand }, + { "REAL_PATH"_s, HandleRealPathCommand }, { "RELATIVE_PATH"_s, HandleRelativePathCommand }, { "TO_CMAKE_PATH"_s, HandleCMakePathCommand }, { "TO_NATIVE_PATH"_s, HandleNativePathCommand }, @@ -3182,6 +3443,8 @@ bool cmFileCommand(std::vector<std::string> const& args, { "CONFIGURE"_s, HandleConfigureCommand }, { "ARCHIVE_CREATE"_s, HandleArchiveCreateCommand }, { "ARCHIVE_EXTRACT"_s, HandleArchiveExtractCommand }, + { "CHMOD"_s, HandleChmodCommand }, + { "CHMOD_RECURSE"_s, HandleChmodRecurseCommand }, }; return subcommand(args[0], args, status); diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h index 8c9b21990a..ec9ee473c5 100644 --- a/Source/cmFileCommand.h +++ b/Source/cmFileCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileCommand_h -#define cmFileCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmFileCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmFileCopier.cxx b/Source/cmFileCopier.cxx index 627e05b587..48fc286829 100644 --- a/Source/cmFileCopier.cxx +++ b/Source/cmFileCopier.cxx @@ -10,6 +10,7 @@ #include "cmFSPermissions.h" #include "cmFileTimes.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -171,11 +172,11 @@ void cmFileCopier::DefaultDirectoryPermissions() bool cmFileCopier::GetDefaultDirectoryPermissions(mode_t** mode) { // check if default dir creation permissions were set - const char* default_dir_install_permissions = this->Makefile->GetDefinition( + cmProp default_dir_install_permissions = this->Makefile->GetDefinition( "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS"); - if (default_dir_install_permissions && *default_dir_install_permissions) { + if (cmNonempty(default_dir_install_permissions)) { std::vector<std::string> items = - cmExpandedList(default_dir_install_permissions); + cmExpandedList(*default_dir_install_permissions); for (const auto& arg : items) { if (!this->CheckPermissions(arg, **mode)) { this->Status.SetError( diff --git a/Source/cmFileCopier.h b/Source/cmFileCopier.h index 612a57fef2..217d58d28d 100644 --- a/Source/cmFileCopier.h +++ b/Source/cmFileCopier.h @@ -1,7 +1,6 @@ /* 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 +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -120,5 +119,3 @@ protected: bool GetDefaultDirectoryPermissions(mode_t** mode); }; - -#endif diff --git a/Source/cmFileInstaller.h b/Source/cmFileInstaller.h index 537cd53cb6..3a905d320d 100644 --- a/Source/cmFileInstaller.h +++ b/Source/cmFileInstaller.h @@ -1,7 +1,6 @@ /* 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 +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -50,5 +49,3 @@ protected: bool GetTargetTypeFromString(const std::string& stype); bool HandleInstallDestination(); }; - -#endif diff --git a/Source/cmFileLock.h b/Source/cmFileLock.h index 5fe068e8f1..2b125afcc7 100644 --- a/Source/cmFileLock.h +++ b/Source/cmFileLock.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileLock_h -#define cmFileLock_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -63,5 +62,3 @@ private: std::string Filename; }; - -#endif // cmFileLock_h diff --git a/Source/cmFileLockPool.h b/Source/cmFileLockPool.h index d45c82cc41..f2f9f23a7a 100644 --- a/Source/cmFileLockPool.h +++ b/Source/cmFileLockPool.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileLockPool_h -#define cmFileLockPool_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -86,5 +85,3 @@ private: List FileScopes; ScopePool ProcessScope; }; - -#endif // cmFileLockPool_h diff --git a/Source/cmFileLockResult.h b/Source/cmFileLockResult.h index 81c19064d4..8a58d1f0a3 100644 --- a/Source/cmFileLockResult.h +++ b/Source/cmFileLockResult.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileLockResult_h -#define cmFileLockResult_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -73,5 +72,3 @@ private: ErrorType Type; Error ErrorValue; }; - -#endif // cmFileLockResult_h diff --git a/Source/cmFilePathChecksum.h b/Source/cmFilePathChecksum.h index b7d5cd24c6..a6f7bd31c9 100644 --- a/Source/cmFilePathChecksum.h +++ b/Source/cmFilePathChecksum.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFilePathChecksum_h -#define cmFilePathChecksum_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -60,5 +59,3 @@ private: /// List of (directory name, seed name) pairs std::array<std::pair<std::string, std::string>, 4> parentDirs; }; - -#endif diff --git a/Source/cmFileTime.cxx b/Source/cmFileTime.cxx index 96c70fef6b..0606baf71f 100644 --- a/Source/cmFileTime.cxx +++ b/Source/cmFileTime.cxx @@ -24,13 +24,13 @@ bool cmFileTime::Load(std::string const& fileName) } # if CMake_STAT_HAS_ST_MTIM // Nanosecond resolution - this->NS = fst.st_mtim.tv_sec * NsPerS + fst.st_mtim.tv_nsec; + this->Time = fst.st_mtim.tv_sec * UtPerS + 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; + this->Time = fst.st_mtimespec.tv_sec * UtPerS + fst.st_mtimespec.tv_nsec; # else // Second resolution - this->NS = fst.st_mtime * NsPerS; + this->Time = fst.st_mtime * UtPerS; # endif #else // Windows version. Get the modification time from extended file attributes. @@ -41,10 +41,11 @@ bool cmFileTime::Load(std::string const& fileName) } // 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; + using uint64 = unsigned long long; + + this->Time = static_cast<TimeType>( + (uint64(fdata.ftLastWriteTime.dwHighDateTime) << 32) + + fdata.ftLastWriteTime.dwLowDateTime); #endif return true; } diff --git a/Source/cmFileTime.h b/Source/cmFileTime.h index e9a85598ac..4419880658 100644 --- a/Source/cmFileTime.h +++ b/Source/cmFileTime.h @@ -1,7 +1,6 @@ /* 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 +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -14,9 +13,15 @@ class cmFileTime { public: - using NSC = long long; - static constexpr NSC NsPerS = 1000000000; - + using TimeType = long long; + // unit time per second +#if !defined(_WIN32) || defined(__CYGWIN__) + // unit time is one nanosecond + static constexpr TimeType UtPerS = 1000000000; +#else + // unit time is 100 nanosecond + static constexpr TimeType UtPerS = 10000000; +#endif cmFileTime() = default; ~cmFileTime() = default; @@ -29,22 +34,28 @@ public: /** * @brief Return true if this is older than ftm */ - bool Older(cmFileTime const& ftm) const { return (this->NS - ftm.NS) < 0; } + bool Older(cmFileTime const& ftm) const + { + return (this->Time - ftm.Time) < 0; + } /** * @brief Return true if this is newer than ftm */ - bool Newer(cmFileTime const& ftm) const { return (ftm.NS - this->NS) < 0; } + bool Newer(cmFileTime const& ftm) const + { + return (ftm.Time - this->Time) < 0; + } /** * @brief Return true if this is the same as ftm */ - bool Equal(cmFileTime const& ftm) const { return this->NS == ftm.NS; } + bool Equal(cmFileTime const& ftm) const { return this->Time == ftm.Time; } /** * @brief Return true if this is not the same as ftm */ - bool Differ(cmFileTime const& ftm) const { return this->NS != ftm.NS; } + bool Differ(cmFileTime const& ftm) const { return this->Time != ftm.Time; } /** * @brief Compare file modification times. @@ -52,7 +63,7 @@ public: */ int Compare(cmFileTime const& ftm) const { - NSC const diff = this->NS - ftm.NS; + TimeType const diff = this->Time - ftm.Time; if (diff == 0) { return 0; } @@ -66,7 +77,7 @@ public: */ bool OlderS(cmFileTime const& ftm) const { - return (ftm.NS - this->NS) >= cmFileTime::NsPerS; + return (ftm.Time - this->Time) >= cmFileTime::UtPerS; } /** @@ -74,7 +85,7 @@ public: */ bool NewerS(cmFileTime const& ftm) const { - return (this->NS - ftm.NS) >= cmFileTime::NsPerS; + return (this->Time - ftm.Time) >= cmFileTime::UtPerS; } /** @@ -82,11 +93,11 @@ public: */ bool EqualS(cmFileTime const& ftm) const { - NSC diff = this->NS - ftm.NS; + TimeType diff = this->Time - ftm.Time; if (diff < 0) { diff = -diff; } - return (diff < cmFileTime::NsPerS); + return (diff < cmFileTime::UtPerS); } /** @@ -94,11 +105,11 @@ public: */ bool DifferS(cmFileTime const& ftm) const { - NSC diff = this->NS - ftm.NS; + TimeType diff = this->Time - ftm.Time; if (diff < 0) { diff = -diff; } - return (diff >= cmFileTime::NsPerS); + return (diff >= cmFileTime::UtPerS); } /** @@ -108,23 +119,21 @@ public: */ int CompareS(cmFileTime const& ftm) const { - NSC const diff = this->NS - ftm.NS; - if (diff <= -cmFileTime::NsPerS) { + TimeType const diff = this->Time - ftm.Time; + if (diff <= -cmFileTime::UtPerS) { return -1; } - if (diff >= cmFileTime::NsPerS) { + if (diff >= cmFileTime::UtPerS) { return 1; } return 0; } /** - * @brief The file modification time in nanoseconds + * @brief The file modification time in unit time per second */ - NSC GetNS() const { return this->NS; } + TimeType GetTime() const { return this->Time; } private: - NSC NS = 0; + TimeType Time = 0; }; - -#endif diff --git a/Source/cmFileTimeCache.h b/Source/cmFileTimeCache.h index 83b77b651c..336136e0e7 100644 --- a/Source/cmFileTimeCache.h +++ b/Source/cmFileTimeCache.h @@ -1,7 +1,6 @@ /* 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 +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -53,5 +52,3 @@ public: private: std::unordered_map<std::string, cmFileTime> Cache; }; - -#endif diff --git a/Source/cmFileTimes.h b/Source/cmFileTimes.h index 191d89e94c..f1916f7912 100644 --- a/Source/cmFileTimes.h +++ b/Source/cmFileTimes.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFileTimes_h -#define cmFileTimes_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -36,5 +35,3 @@ private: class Times; std::unique_ptr<Times> times; }; - -#endif diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 743ac75fab..bf52d7513a 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -294,11 +294,10 @@ void cmFindBase::FillUserGuessPath() bool cmFindBase::CheckForVariableInCache() { - if (const char* cacheValue = - this->Makefile->GetDefinition(this->VariableName)) { + if (cmProp cacheValue = this->Makefile->GetDefinition(this->VariableName)) { cmState* state = this->Makefile->GetState(); cmProp cacheEntry = state->GetCacheEntryValue(this->VariableName); - bool found = !cmIsNOTFOUND(cacheValue); + bool found = !cmIsNOTFOUND(*cacheValue); bool cached = cacheEntry != nullptr; if (found) { // If the user specifies the entry on the command line without a diff --git a/Source/cmFindBase.h b/Source/cmFindBase.h index 4cbf09ef3a..57a40be9b6 100644 --- a/Source/cmFindBase.h +++ b/Source/cmFindBase.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFindBase_h -#define cmFindBase_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -94,5 +93,3 @@ private: std::vector<DebugLibState> FailedSearchLocations; DebugLibState FoundSearchLocation; }; - -#endif diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx index 3e97150e79..dee91d75d4 100644 --- a/Source/cmFindCommon.cxx +++ b/Source/cmFindCommon.cxx @@ -11,6 +11,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmake.h" @@ -182,9 +183,9 @@ void cmFindCommon::SelectDefaultSearchModes() }; for (auto& path : search_paths) { - const char* def = this->Makefile->GetDefinition(path.second); + cmProp def = this->Makefile->GetDefinition(path.second); if (def) { - path.first = !cmIsOn(def); + path.first = !cmIsOn(*def); } } } @@ -202,16 +203,15 @@ void cmFindCommon::RerootPaths(std::vector<std::string>& paths) return; } - const char* sysroot = this->Makefile->GetDefinition("CMAKE_SYSROOT"); - const char* sysrootCompile = + cmProp sysroot = this->Makefile->GetDefinition("CMAKE_SYSROOT"); + cmProp sysrootCompile = this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE"); - const char* sysrootLink = - this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK"); - const char* rootPath = this->Makefile->GetDefinition("CMAKE_FIND_ROOT_PATH"); - const bool noSysroot = !sysroot || !*sysroot; - const bool noCompileSysroot = !sysrootCompile || !*sysrootCompile; - const bool noLinkSysroot = !sysrootLink || !*sysrootLink; - const bool noRootPath = !rootPath || !*rootPath; + cmProp sysrootLink = this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK"); + cmProp rootPath = this->Makefile->GetDefinition("CMAKE_FIND_ROOT_PATH"); + const bool noSysroot = !cmNonempty(sysroot); + const bool noCompileSysroot = !cmNonempty(sysrootCompile); + const bool noLinkSysroot = !cmNonempty(sysrootLink); + const bool noRootPath = !cmNonempty(rootPath); if (noSysroot && noCompileSysroot && noLinkSysroot && noRootPath) { return; } @@ -219,23 +219,22 @@ void cmFindCommon::RerootPaths(std::vector<std::string>& paths) // Construct the list of path roots with no trailing slashes. std::vector<std::string> roots; if (rootPath) { - cmExpandList(rootPath, roots); + cmExpandList(*rootPath, roots); } if (sysrootCompile) { - roots.emplace_back(sysrootCompile); + roots.emplace_back(*sysrootCompile); } if (sysrootLink) { - roots.emplace_back(sysrootLink); + roots.emplace_back(*sysrootLink); } if (sysroot) { - roots.emplace_back(sysroot); + roots.emplace_back(*sysroot); } for (std::string& r : roots) { cmSystemTools::ConvertToUnixSlashes(r); } - const char* stagePrefix = - this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX"); + cmProp stagePrefix = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX"); // Copy the original set of unrooted paths. std::vector<std::string> unrootedPaths = paths; @@ -248,7 +247,7 @@ void cmFindCommon::RerootPaths(std::vector<std::string>& paths) // a user home directory or is empty. std::string rootedDir; if (cmSystemTools::IsSubDirectory(up, r) || - (stagePrefix && cmSystemTools::IsSubDirectory(up, stagePrefix))) { + (stagePrefix && cmSystemTools::IsSubDirectory(up, *stagePrefix))) { rootedDir = up; } else if (!up.empty() && up[0] != '~') { // Start with the new root. diff --git a/Source/cmFindCommon.h b/Source/cmFindCommon.h index 916f3bc9ba..f84242e150 100644 --- a/Source/cmFindCommon.h +++ b/Source/cmFindCommon.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFindCommon_h -#define cmFindCommon_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -143,5 +142,3 @@ protected: cmMakefile* Makefile; cmExecutionStatus& Status; }; - -#endif diff --git a/Source/cmFindFileCommand.h b/Source/cmFindFileCommand.h index 7dc6e55699..368a7c93b4 100644 --- a/Source/cmFindFileCommand.h +++ b/Source/cmFindFileCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFindFileCommand_h -#define cmFindFileCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -28,5 +27,3 @@ public: bool cmFindFile(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx index 3242b6d978..b87dfe396f 100644 --- a/Source/cmFindLibraryCommand.cxx +++ b/Source/cmFindLibraryCommand.cxx @@ -13,6 +13,7 @@ #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -50,9 +51,9 @@ bool cmFindLibraryCommand::InitialPass(std::vector<std::string> const& argsIn) // add custom lib<qual> paths instead of using fixed lib32, lib64 or // libx32 - if (const char* customLib = this->Makefile->GetDefinition( + if (cmProp customLib = this->Makefile->GetDefinition( "CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX")) { - this->AddArchitecturePaths(customLib); + this->AddArchitecturePaths(customLib->c_str()); } // add special 32 bit paths if this is a 32 bit compile. else if (this->Makefile->PlatformIs32Bit() && diff --git a/Source/cmFindLibraryCommand.h b/Source/cmFindLibraryCommand.h index b2f71b3b80..f3874ff29b 100644 --- a/Source/cmFindLibraryCommand.h +++ b/Source/cmFindLibraryCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFindLibraryCommand_h -#define cmFindLibraryCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -44,5 +43,3 @@ private: bool cmFindLibrary(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 8d5b177f2b..92b1e80b8a 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -13,6 +13,7 @@ #include <utility> #include <cm/memory> +#include <cmext/string_view> #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" @@ -48,6 +49,11 @@ cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::Builds( cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::SystemRegistry("SYSTEM_PACKAGE_REGISTRY"); +const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_INCLUDED( + "INCLUDE"); +const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_EXCLUDED( + "EXCLUDE"); + struct StrverscmpGreater { bool operator()(const std::string& lhs, const std::string& rhs) const @@ -89,34 +95,11 @@ void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin, cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status) : cmFindCommon(status) + , VersionRangeMin(VERSION_ENDPOINT_INCLUDED) + , VersionRangeMax(VERSION_ENDPOINT_INCLUDED) { this->CMakePathName = "PACKAGE"; - this->Quiet = false; - this->Required = false; - this->NoUserRegistry = false; - this->NoSystemRegistry = false; - this->UseConfigFiles = true; - this->UseFindModules = true; this->DebugMode = false; - this->UseLib32Paths = false; - this->UseLib64Paths = false; - this->UseLibx32Paths = false; - this->UseRealPath = false; - this->PolicyScope = true; - this->VersionMajor = 0; - this->VersionMinor = 0; - this->VersionPatch = 0; - this->VersionTweak = 0; - this->VersionCount = 0; - this->VersionExact = false; - this->VersionFoundMajor = 0; - this->VersionFoundMinor = 0; - this->VersionFoundPatch = 0; - this->VersionFoundTweak = 0; - this->VersionFoundCount = 0; - this->RequiredCMakeVersion = 0; - this->SortOrder = None; - this->SortDirection = Asc; this->AppendSearchPathGroups(); this->DeprecatedFindModules["Qt"] = cmPolicies::CMP0084; @@ -154,10 +137,10 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Lookup required version of CMake. - if (const char* rv = + if (cmProp rv = this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) { unsigned int v[3] = { 0, 0, 0 }; - sscanf(rv, "%u.%u.%u", &v[0], &v[1], &v[2]); + sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]); this->RequiredCMakeVersion = CMake_VERSION_ENCODE(v[0], v[1], v[2]); } @@ -165,9 +148,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->DebugBuffer.clear(); // Lookup target architecture, if any. - if (const char* arch = + if (cmProp arch = this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) { - this->LibraryArchitecture = arch; + this->LibraryArchitecture = *arch; } // Lookup whether lib32 paths should be used. @@ -194,9 +177,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // Check if User Package Registry should be disabled // The `CMAKE_FIND_USE_PACKAGE_REGISTRY` has // priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY - if (const char* def = + if (cmProp def = this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) { - this->NoUserRegistry = !cmIsOn(def); + this->NoUserRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) { this->NoUserRegistry = true; } @@ -204,9 +187,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // Check if System Package Registry should be disabled // The `CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY` has // priority over the deprecated CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY - if (const char* def = this->Makefile->GetDefinition( + if (cmProp def = this->Makefile->GetDefinition( "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) { - this->NoSystemRegistry = !cmIsOn(def); + this->NoSystemRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn( "CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY")) { this->NoSystemRegistry = true; @@ -218,20 +201,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Check if Sorting should be enabled - if (const char* so = + if (cmProp so = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) { - if (strcmp(so, "NAME") == 0) { + if (*so == "NAME") { this->SortOrder = Name_order; - } else if (strcmp(so, "NATURAL") == 0) { + } else if (*so == "NATURAL") { this->SortOrder = Natural; } else { this->SortOrder = None; } } - if (const char* sd = + if (cmProp sd = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) { - this->SortDirection = strcmp(sd, "ASC") == 0 ? Asc : Dec; + this->SortDirection = (*sd == "ASC") ? Asc : Dec; } // Find what search path locations have been enabled/disable @@ -266,7 +249,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) DoingHints }; Doing doing = DoingNone; - cmsys::RegularExpression version("^[0-9.]+$"); + cmsys::RegularExpression versionRegex( + R"V(^([0-9]+(\.[0-9]+)*)(\.\.\.(<?)([0-9]+(\.[0-9]+)*))?$)V"); bool haveVersion = false; std::set<unsigned int> configArgs; std::set<unsigned int> moduleArgs; @@ -369,9 +353,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return false; } this->Configs.push_back(args[i]); - } else if (!haveVersion && version.find(args[i])) { + } else if (!haveVersion && versionRegex.find(args[i])) { haveVersion = true; - this->Version = args[i]; + this->VersionComplete = args[i]; } else { this->SetError( cmStrCat("called with invalid argument \"", args[i], "\"")); @@ -410,23 +394,23 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Ignore EXACT with no version. - if (this->Version.empty() && this->VersionExact) { + if (this->VersionComplete.empty() && this->VersionExact) { this->VersionExact = false; this->Makefile->IssueMessage( MessageType::AUTHOR_WARNING, "Ignoring EXACT since no version is requested."); } - if (this->Version.empty() || components.empty()) { + if (this->VersionComplete.empty() || components.empty()) { // Check whether we are recursing inside "Find<name>.cmake" within // another find_package(<name>) call. std::string mod = cmStrCat(this->Name, "_FIND_MODULE"); if (this->Makefile->IsOn(mod)) { - if (this->Version.empty()) { + if (this->VersionComplete.empty()) { // Get version information from the outer call if necessary. // Requested version string. - std::string ver = cmStrCat(this->Name, "_FIND_VERSION"); - this->Version = this->Makefile->GetSafeDefinition(ver); + std::string ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE"); + this->VersionComplete = this->Makefile->GetSafeDefinition(ver); // Whether an exact version is required. std::string exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); @@ -439,32 +423,59 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } } + // fill various parts of version specification + if (!this->VersionComplete.empty()) { + if (!versionRegex.find(this->VersionComplete)) { + this->SetError("called with invalid version specification."); + return false; + } + + this->Version = versionRegex.match(1); + this->VersionMax = versionRegex.match(5); + if (versionRegex.match(4) == "<"_s) { + this->VersionRangeMax = VERSION_ENDPOINT_EXCLUDED; + } + if (!this->VersionMax.empty()) { + this->VersionRange = this->VersionComplete; + } + } + + if (!this->VersionRange.empty()) { + // version range must not be empty + if ((this->VersionRangeMax == VERSION_ENDPOINT_INCLUDED && + cmSystemTools::VersionCompareGreater(this->Version, + this->VersionMax)) || + (this->VersionRangeMax == VERSION_ENDPOINT_EXCLUDED && + cmSystemTools::VersionCompareGreaterEq(this->Version, + this->VersionMax))) { + this->SetError("specified version range is empty."); + return false; + } + } + + if (this->VersionExact && !this->VersionRange.empty()) { + this->SetError("EXACT cannot be specified with a version range."); + return false; + } + + // Parse the version number and store the results that were + // successfully parsed. + auto parseVersion = [](const std::string& version, unsigned int& major, + unsigned int& minor, unsigned int& patch, + unsigned int& tweak) -> unsigned int { + return sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch, + &tweak); + }; + if (!this->Version.empty()) { - // Try to parse the version number and store the results that were - // successfully parsed. - unsigned int parsed_major; - unsigned int parsed_minor; - unsigned int parsed_patch; - unsigned int parsed_tweak; this->VersionCount = - sscanf(this->Version.c_str(), "%u.%u.%u.%u", &parsed_major, - &parsed_minor, &parsed_patch, &parsed_tweak); - switch (this->VersionCount) { - case 4: - this->VersionTweak = parsed_tweak; - CM_FALLTHROUGH; - case 3: - this->VersionPatch = parsed_patch; - CM_FALLTHROUGH; - case 2: - this->VersionMinor = parsed_minor; - CM_FALLTHROUGH; - case 1: - this->VersionMajor = parsed_major; - CM_FALLTHROUGH; - default: - break; - } + parseVersion(this->Version, this->VersionMajor, this->VersionMinor, + this->VersionPatch, this->VersionTweak); + } + if (!this->VersionMax.empty()) { + this->VersionMaxCount = parseVersion( + this->VersionMax, this->VersionMaxMajor, this->VersionMaxMinor, + this->VersionMaxPatch, this->VersionMaxTweak); } std::string disableFindPackageVar = @@ -578,6 +589,11 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) loadedPackage = true; } } + + if (this->DebugMode) { + this->DebugMessage(this->DebugBuffer); + this->DebugBuffer.clear(); + } } this->AppendSuccessInformation(); @@ -627,63 +643,105 @@ bool cmFindPackageCommand::FindPackageUsingConfigMode() return this->HandlePackageMode(HandlePackageModeType::Config); } +void cmFindPackageCommand::SetVersionVariables( + const std::function<void(const std::string&, cm::string_view)>& + addDefinition, + const std::string& prefix, const std::string& version, unsigned int count, + unsigned int major, unsigned int minor, unsigned int patch, + unsigned int tweak) +{ + addDefinition(prefix, version); + + char buf[64]; + sprintf(buf, "%u", major); + addDefinition(prefix + "_MAJOR", buf); + sprintf(buf, "%u", minor); + addDefinition(prefix + "_MINOR", buf); + sprintf(buf, "%u", patch); + addDefinition(prefix + "_PATCH", buf); + sprintf(buf, "%u", tweak); + addDefinition(prefix + "_TWEAK", buf); + sprintf(buf, "%u", count); + addDefinition(prefix + "_COUNT", buf); +} + void cmFindPackageCommand::SetModuleVariables(const std::string& components) { - this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name.c_str()); + this->AddFindDefinition("CMAKE_FIND_PACKAGE_NAME", this->Name); // Store the list of components. std::string components_var = this->Name + "_FIND_COMPONENTS"; - this->AddFindDefinition(components_var, components.c_str()); + this->AddFindDefinition(components_var, components); if (this->Quiet) { // Tell the module that is about to be read that it should find // quietly. std::string quietly = cmStrCat(this->Name, "_FIND_QUIETLY"); - this->AddFindDefinition(quietly, "1"); + this->AddFindDefinition(quietly, "1"_s); } if (this->Required) { // Tell the module that is about to be read that it should report // a fatal error if the package is not found. std::string req = cmStrCat(this->Name, "_FIND_REQUIRED"); - this->AddFindDefinition(req, "1"); + this->AddFindDefinition(req, "1"_s); + } + + if (!this->VersionComplete.empty()) { + std::string req = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE"); + this->AddFindDefinition(req, this->VersionComplete); } + // Tell the module that is about to be read what version of the + // package has been requested. + auto addDefinition = [this](const std::string& variable, + cm::string_view value) { + this->AddFindDefinition(variable, value); + }; + if (!this->Version.empty()) { - // Tell the module that is about to be read what version of the - // package has been requested. - std::string ver = cmStrCat(this->Name, "_FIND_VERSION"); - this->AddFindDefinition(ver, this->Version.c_str()); - char buf[64]; - sprintf(buf, "%u", this->VersionMajor); - this->AddFindDefinition(ver + "_MAJOR", buf); - sprintf(buf, "%u", this->VersionMinor); - this->AddFindDefinition(ver + "_MINOR", buf); - sprintf(buf, "%u", this->VersionPatch); - this->AddFindDefinition(ver + "_PATCH", buf); - sprintf(buf, "%u", this->VersionTweak); - this->AddFindDefinition(ver + "_TWEAK", buf); - sprintf(buf, "%u", this->VersionCount); - this->AddFindDefinition(ver + "_COUNT", buf); + auto prefix = cmStrCat(this->Name, "_FIND_VERSION"_s); + this->SetVersionVariables(addDefinition, prefix, this->Version, + this->VersionCount, this->VersionMajor, + this->VersionMinor, this->VersionPatch, + this->VersionTweak); // Tell the module whether an exact version has been requested. - std::string exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); - this->AddFindDefinition(exact, this->VersionExact ? "1" : "0"); + auto exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); + this->AddFindDefinition(exact, this->VersionExact ? "1"_s : "0"_s); + } + if (!this->VersionRange.empty()) { + auto prefix = cmStrCat(this->Name, "_FIND_VERSION_MIN"_s); + this->SetVersionVariables(addDefinition, prefix, this->Version, + this->VersionCount, this->VersionMajor, + this->VersionMinor, this->VersionPatch, + this->VersionTweak); + + prefix = cmStrCat(this->Name, "_FIND_VERSION_MAX"_s); + this->SetVersionVariables(addDefinition, prefix, this->VersionMax, + this->VersionMaxCount, this->VersionMaxMajor, + this->VersionMaxMinor, this->VersionMaxPatch, + this->VersionMaxTweak); + + auto id = cmStrCat(this->Name, "_FIND_VERSION_RANGE"); + this->AddFindDefinition(id, this->VersionRange); + id = cmStrCat(this->Name, "_FIND_VERSION_RANGE_MIN"); + this->AddFindDefinition(id, this->VersionRangeMin); + id = cmStrCat(this->Name, "_FIND_VERSION_RANGE_MAX"); + this->AddFindDefinition(id, this->VersionRangeMax); } } void cmFindPackageCommand::AddFindDefinition(const std::string& var, - const char* val) + cm::string_view value) { - if (const char* old = this->Makefile->GetDefinition(var)) { + if (cmProp old = this->Makefile->GetDefinition(var)) { this->OriginalDefs[var].exists = true; - this->OriginalDefs[var].value = old; + this->OriginalDefs[var].value = *old; } else { this->OriginalDefs[var].exists = false; } - if (val) { - this->Makefile->AddDefinition(var, val); - } + this->Makefile->AddDefinition(var, value); } void cmFindPackageCommand::RestoreFindDefinitions() @@ -748,6 +806,17 @@ bool cmFindPackageCommand::FindModule(bool& found) this->Makefile->AddDefinition(var, "1"); bool result = this->ReadListFile(mfile, DoPolicyScope); this->Makefile->RemoveDefinition(var); + + if (this->DebugMode) { + std::string foundVar = cmStrCat(this->Name, "_FOUND"); + if (this->Makefile->IsDefinitionSet(foundVar) && + !this->Makefile->IsOn(foundVar)) { + + this->DebugBuffer = cmStrCat( + this->DebugBuffer, "The module is considered not found due to ", + foundVar, " being FALSE."); + } + } return result; } return true; @@ -759,14 +828,14 @@ bool cmFindPackageCommand::HandlePackageMode( this->ConsideredConfigs.clear(); // Try to find the config file. - const char* def = this->Makefile->GetDefinition(this->Variable); + cmProp def = this->Makefile->GetDefinition(this->Variable); // Try to load the config file if the directory is known bool fileFound = false; if (this->UseConfigFiles) { if (!cmIsOff(def)) { // Get the directory from the variable value. - std::string dir = def; + std::string dir = *def; cmSystemTools::ConvertToUnixSlashes(dir); // Treat relative paths with respect to the current source dir. @@ -881,7 +950,9 @@ bool cmFindPackageCommand::HandlePackageMode( e << "Could not find a configuration file for package \"" << this->Name << "\" that " << (this->VersionExact ? "exactly matches" : "is compatible with") - << " requested version \"" << this->Version << "\".\n" + << " requested version " + << (this->VersionRange.empty() ? "" : "range ") << "\"" + << this->VersionComplete << "\".\n" << "The following configuration files were considered but not " "accepted:\n"; @@ -891,9 +962,9 @@ bool cmFindPackageCommand::HandlePackageMode( } } else { std::string requestedVersionString; - if (!this->Version.empty()) { + if (!this->VersionComplete.empty()) { requestedVersionString = - cmStrCat(" (requested version ", this->Version, ')'); + cmStrCat(" (requested version ", this->VersionComplete, ')'); } if (this->UseConfigFiles) { @@ -1121,7 +1192,7 @@ void cmFindPackageCommand::AppendToFoundProperty(bool found) std::vector<std::string> foundContents; cmProp foundProp = this->Makefile->GetState()->GetGlobalProperty("PACKAGES_FOUND"); - if (foundProp && !foundProp->empty()) { + if (cmNonempty(foundProp)) { cmExpandList(*foundProp, foundContents, false); auto nameIt = std::find(foundContents.begin(), foundContents.end(), this->Name); @@ -1133,7 +1204,7 @@ void cmFindPackageCommand::AppendToFoundProperty(bool found) std::vector<std::string> notFoundContents; cmProp notFoundProp = this->Makefile->GetState()->GetGlobalProperty("PACKAGES_NOT_FOUND"); - if (notFoundProp && !notFoundProp->empty()) { + if (cmNonempty(notFoundProp)) { cmExpandList(*notFoundProp, notFoundContents, false); auto nameIt = std::find(notFoundContents.begin(), notFoundContents.end(), this->Name); @@ -1166,9 +1237,9 @@ void cmFindPackageCommand::AppendSuccessInformation() std::string found = cmStrCat(this->Name, "_FOUND"); std::string upperFound = cmSystemTools::UpperCase(found); - const char* upperResult = this->Makefile->GetDefinition(upperFound); - const char* result = this->Makefile->GetDefinition(found); - bool packageFound = ((cmIsOn(result)) || (cmIsOn(upperResult))); + bool upperResult = this->Makefile->IsOn(upperFound); + bool result = this->Makefile->IsOn(found); + bool packageFound = (result || upperResult); this->AppendToFoundProperty(packageFound); @@ -1182,7 +1253,9 @@ void cmFindPackageCommand::AppendSuccessInformation() std::string versionInfoPropName = cmStrCat("_CMAKE_", this->Name, "_REQUIRED_VERSION"); std::string versionInfo; - if (!this->Version.empty()) { + if (!this->VersionRange.empty()) { + versionInfo = this->VersionRange; + } else if (!this->Version.empty()) { versionInfo = cmStrCat(this->VersionExact ? "==" : ">=", ' ', this->Version); } @@ -1725,18 +1798,34 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, // Set the input variables. this->Makefile->AddDefinition("PACKAGE_FIND_NAME", this->Name); - this->Makefile->AddDefinition("PACKAGE_FIND_VERSION", this->Version); - char buf[64]; - sprintf(buf, "%u", this->VersionMajor); - this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_MAJOR", buf); - sprintf(buf, "%u", this->VersionMinor); - this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_MINOR", buf); - sprintf(buf, "%u", this->VersionPatch); - this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_PATCH", buf); - sprintf(buf, "%u", this->VersionTweak); - this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_TWEAK", buf); - sprintf(buf, "%u", this->VersionCount); - this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_COUNT", buf); + this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_COMPLETE", + this->VersionComplete); + + auto addDefinition = [this](const std::string& variable, + cm::string_view value) { + this->Makefile->AddDefinition(variable, value); + }; + this->SetVersionVariables(addDefinition, "PACKAGE_FIND_VERSION", + this->Version, this->VersionCount, + this->VersionMajor, this->VersionMinor, + this->VersionPatch, this->VersionTweak); + if (!this->VersionRange.empty()) { + this->SetVersionVariables(addDefinition, "PACKAGE_FIND_VERSION_MIN", + this->Version, this->VersionCount, + this->VersionMajor, this->VersionMinor, + this->VersionPatch, this->VersionTweak); + this->SetVersionVariables(addDefinition, "PACKAGE_FIND_VERSION_MAX", + this->VersionMax, this->VersionMaxCount, + this->VersionMaxMajor, this->VersionMaxMinor, + this->VersionMaxPatch, this->VersionMaxTweak); + + this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_RANGE", + this->VersionComplete); + this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_RANGE_MIN", + this->VersionRangeMin); + this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_RANGE_MAX", + this->VersionRangeMax); + } // Load the version check file. Pass NoPolicyScope because we do // our own policy push/pop independent of CMP0011. @@ -1798,24 +1887,19 @@ void cmFindPackageCommand::StoreVersionFound() { // Store the whole version string. std::string ver = cmStrCat(this->Name, "_VERSION"); + auto addDefinition = [this](const std::string& variable, + cm::string_view value) { + this->Makefile->AddDefinition(variable, value); + }; + + this->SetVersionVariables(addDefinition, ver, this->VersionFound, + this->VersionFoundCount, this->VersionFoundMajor, + this->VersionFoundMinor, this->VersionFoundPatch, + this->VersionFoundTweak); + if (this->VersionFound.empty()) { this->Makefile->RemoveDefinition(ver); - } else { - this->Makefile->AddDefinition(ver, this->VersionFound); } - - // Store the version components. - char buf[64]; - sprintf(buf, "%u", this->VersionFoundMajor); - this->Makefile->AddDefinition(ver + "_MAJOR", buf); - sprintf(buf, "%u", this->VersionFoundMinor); - this->Makefile->AddDefinition(ver + "_MINOR", buf); - sprintf(buf, "%u", this->VersionFoundPatch); - this->Makefile->AddDefinition(ver + "_PATCH", buf); - sprintf(buf, "%u", this->VersionFoundTweak); - this->Makefile->AddDefinition(ver + "_TWEAK", buf); - sprintf(buf, "%u", this->VersionFoundCount); - this->Makefile->AddDefinition(ver + "_COUNT", buf); } class cmFileListGeneratorBase @@ -1900,6 +1984,9 @@ cmFileListGeneratorBase* cmFileListGeneratorBase::SetNext( bool cmFileListGeneratorBase::Consider(std::string const& fullPath, cmFileList& listing) { + if (!fullPath.empty() && !cmSystemTools::FileIsDirectory(fullPath)) { + return false; + } if (this->Next) { return this->Next->Search(fullPath + "/", listing); } @@ -2154,10 +2241,8 @@ private: // Look for directories among the matches. for (std::string const& f : files) { - if (cmSystemTools::FileIsDirectory(f)) { - if (this->Consider(f, lister)) { - return true; - } + if (this->Consider(f, lister)) { + return true; } } return false; diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 7058a54eab..edf32d4ccc 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFindPackageCommand_h -#define cmFindPackageCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,6 +11,8 @@ #include <string> #include <vector> +#include <cm/string_view> + #include <cm3p/kwiml/int.h> #include "cmFindCommon.h" @@ -89,9 +90,15 @@ private: void AppendSuccessInformation(); void AppendToFoundProperty(bool found); + void SetVersionVariables( + const std::function<void(const std::string&, cm::string_view)>& + addDefinition, + const std::string& prefix, const std::string& version, unsigned int count, + unsigned int major, unsigned int minor, unsigned int patch, + unsigned int tweak); void SetModuleVariables(const std::string& components); bool FindModule(bool& found); - void AddFindDefinition(const std::string& var, const char* val); + void AddFindDefinition(const std::string& var, cm::string_view value); void RestoreFindDefinitions(); enum /*class*/ HandlePackageModeType @@ -151,34 +158,47 @@ private: std::map<std::string, cmPolicies::PolicyID> DeprecatedFindModules; + static const cm::string_view VERSION_ENDPOINT_INCLUDED; + static const cm::string_view VERSION_ENDPOINT_EXCLUDED; + std::string Name; std::string Variable; + std::string VersionComplete; + std::string VersionRange; + cm::string_view VersionRangeMin; + cm::string_view VersionRangeMax; std::string Version; - unsigned int VersionMajor; - unsigned int VersionMinor; - unsigned int VersionPatch; - unsigned int VersionTweak; - unsigned int VersionCount; - bool VersionExact; + unsigned int VersionMajor = 0; + unsigned int VersionMinor = 0; + unsigned int VersionPatch = 0; + unsigned int VersionTweak = 0; + unsigned int VersionCount = 0; + std::string VersionMax; + unsigned int VersionMaxMajor = 0; + unsigned int VersionMaxMinor = 0; + unsigned int VersionMaxPatch = 0; + unsigned int VersionMaxTweak = 0; + unsigned int VersionMaxCount = 0; + bool VersionExact = false; std::string FileFound; std::string VersionFound; - unsigned int VersionFoundMajor; - unsigned int VersionFoundMinor; - unsigned int VersionFoundPatch; - unsigned int VersionFoundTweak; - unsigned int VersionFoundCount; - KWIML_INT_uint64_t RequiredCMakeVersion; - bool Quiet; - bool Required; - bool UseConfigFiles; - bool UseFindModules; - bool NoUserRegistry; - bool NoSystemRegistry; - bool UseLib32Paths; - bool UseLib64Paths; - bool UseLibx32Paths; - bool UseRealPath; - bool PolicyScope; + unsigned int VersionFoundMajor = 0; + unsigned int VersionFoundMinor = 0; + unsigned int VersionFoundPatch = 0; + unsigned int VersionFoundTweak = 0; + unsigned int VersionFoundCount = 0; + KWIML_INT_uint64_t RequiredCMakeVersion = 0; + bool Quiet = false; + bool Required = false; + bool UseConfigFiles = true; + bool UseFindModules = true; + bool NoUserRegistry = false; + bool NoSystemRegistry = false; + bool UseLib32Paths = false; + bool UseLib64Paths = false; + bool UseLibx32Paths = false; + bool UseRealPath = false; + bool PolicyScope = true; std::string LibraryArchitecture; std::vector<std::string> Names; std::vector<std::string> Configs; @@ -186,9 +206,9 @@ private: std::string DebugBuffer; /*! the selected sortOrder (None by default)*/ - SortOrderType SortOrder; + SortOrderType SortOrder = None; /*! the selected sortDirection (Asc by default)*/ - SortDirectionType SortDirection; + SortDirectionType SortDirection = Asc; struct ConfigFileInfo { @@ -233,5 +253,3 @@ struct hash<cmFindPackageCommand::ConfigFileInfo> bool cmFindPackage(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmFindPathCommand.h b/Source/cmFindPathCommand.h index 18bfb7dd86..b9fe67358e 100644 --- a/Source/cmFindPathCommand.h +++ b/Source/cmFindPathCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFindPathCommand_h -#define cmFindPathCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -38,5 +37,3 @@ private: bool cmFindPath(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx index 4b88bea4d3..77728ec8a6 100644 --- a/Source/cmFindProgramCommand.cxx +++ b/Source/cmFindProgramCommand.cxx @@ -4,6 +4,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -19,6 +20,7 @@ struct cmFindProgramHelper cmFindProgramHelper(cmMakefile* makefile, cmFindBase const* base) : DebugSearches("find_program", base) , Makefile(makefile) + , PolicyCMP0109(makefile->GetPolicyStatus(cmPolicies::CMP0109)) { #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) // Consider platform-specific extensions. @@ -48,6 +50,8 @@ struct cmFindProgramHelper cmFindBaseDebugState DebugSearches; cmMakefile* Makefile; + cmPolicies::PolicyStatus PolicyCMP0109; + void AddName(std::string const& name) { this->Names.push_back(name); } void SetName(std::string const& name) { @@ -85,7 +89,7 @@ struct cmFindProgramHelper this->TestNameExt = cmStrCat(name, ext); this->TestPath = cmSystemTools::CollapseFullPath(this->TestNameExt, path); - bool exists = cmSystemTools::FileExists(this->TestPath, true); + bool exists = this->FileIsExecutable(this->TestPath); exists ? this->DebugSearches.FoundAt(this->TestPath) : this->DebugSearches.FailedAt(this->TestPath); if (exists) { @@ -95,6 +99,48 @@ struct cmFindProgramHelper } return false; } + bool FileIsExecutable(std::string const& file) const + { + switch (this->PolicyCMP0109) { + case cmPolicies::OLD: + return cmSystemTools::FileExists(file, true); + case cmPolicies::NEW: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::REQUIRED_IF_USED: + return cmSystemTools::FileIsExecutable(file); + default: + break; + } + bool const isExeOld = cmSystemTools::FileExists(file, true); + bool const isExeNew = cmSystemTools::FileIsExecutable(file); + if (isExeNew == isExeOld) { + return isExeNew; + } + if (isExeNew) { + this->Makefile->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0109), + "\n" + "The file\n" + " ", + file, + "\n" + "is executable but not readable. " + "CMake is ignoring it for compatibility.")); + } else { + this->Makefile->IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0109), + "\n" + "The file\n" + " ", + file, + "\n" + "is readable but not executable. " + "CMake is using it for compatibility.")); + } + return isExeOld; + } }; cmFindProgramCommand::cmFindProgramCommand(cmExecutionStatus& status) @@ -266,14 +312,13 @@ std::string cmFindProgramCommand::GetBundleExecutable( if (executableURL != nullptr) { const int MAX_OSX_PATH_SIZE = 1024; - char buffer[MAX_OSX_PATH_SIZE]; + UInt8 buffer[MAX_OSX_PATH_SIZE]; - // Convert the CFString to a C string - CFStringGetCString(CFURLGetString(executableURL), buffer, - MAX_OSX_PATH_SIZE, kCFStringEncodingUTF8); - - // And finally to a c++ string - executable = bundlePath + "/Contents/MacOS/" + std::string(buffer); + if (CFURLGetFileSystemRepresentation(executableURL, false, buffer, + MAX_OSX_PATH_SIZE)) { + executable = bundlePath + "/Contents/MacOS/" + + std::string(reinterpret_cast<char*>(buffer)); + } // Only release CFURLRef if it's not null CFRelease(executableURL); } diff --git a/Source/cmFindProgramCommand.h b/Source/cmFindProgramCommand.h index 043b43c5ea..161a680084 100644 --- a/Source/cmFindProgramCommand.h +++ b/Source/cmFindProgramCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFindProgramCommand_h -#define cmFindProgramCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -38,5 +37,3 @@ private: bool cmFindProgram(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx index c68b78599c..bcacb15f9c 100644 --- a/Source/cmForEachCommand.cxx +++ b/Source/cmForEachCommand.cxx @@ -25,6 +25,7 @@ #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -89,7 +90,7 @@ bool cmForEachFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff, cmMakefile& mf) const { std::vector<std::string> expandedArguments; - mf.ExpandArguments(lff.Arguments, expandedArguments); + mf.ExpandArguments(lff.Arguments(), expandedArguments); return expandedArguments.empty() || expandedArguments.front() == this->Args.front(); } @@ -113,8 +114,8 @@ bool cmForEachFunctionBlocker::ReplayItems( // At end of for each execute recorded commands // store the old value std::string oldDef; - if (auto d = mf.GetDefinition(this->Args.front())) { - oldDef = d; + if (cmProp d = mf.GetDefinition(this->Args.front())) { + oldDef = *d; } auto restore = false; @@ -186,8 +187,8 @@ bool cmForEachFunctionBlocker::ReplayZipLists( // Store old values for iteration variables std::map<std::string, std::string> oldDefs; for (auto i = 0u; i < values.size(); ++i) { - if (auto d = mf.GetDefinition(iterationVars[i])) { - oldDefs.emplace(iterationVars[i], d); + if (cmProp d = mf.GetDefinition(iterationVars[i])) { + oldDefs.emplace(iterationVars[i], *d); } } diff --git a/Source/cmForEachCommand.h b/Source/cmForEachCommand.h index 1feb965cef..6476fea354 100644 --- a/Source/cmForEachCommand.h +++ b/Source/cmForEachCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmForEachCommand_h -#define cmForEachCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -13,4 +12,3 @@ class cmExecutionStatus; /// Starts foreach() ... endforeach() block bool cmForEachCommand(std::vector<std::string> const& args, cmExecutionStatus& status); -#endif diff --git a/Source/cmFortranParser.h b/Source/cmFortranParser.h index 6f97b42433..1b14d17772 100644 --- a/Source/cmFortranParser.h +++ b/Source/cmFortranParser.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFortranParser_h -#define cmFortranParser_h +#pragma once #if !defined(cmFortranLexer_cxx) && !defined(cmFortranParser_cxx) # include "cmConfigure.h" // IWYU pragma: keep @@ -181,5 +180,3 @@ struct cmFortranParser_s cmFortranSourceInfo& Info; }; #endif - -#endif diff --git a/Source/cmFunctionBlocker.cxx b/Source/cmFunctionBlocker.cxx index 643cd823f8..d4666d73fc 100644 --- a/Source/cmFunctionBlocker.cxx +++ b/Source/cmFunctionBlocker.cxx @@ -15,9 +15,9 @@ bool cmFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, cmExecutionStatus& status) { - if (lff.Name.Lower == this->StartCommandName()) { + if (lff.LowerCaseName() == this->StartCommandName()) { this->ScopeDepth++; - } else if (lff.Name.Lower == this->EndCommandName()) { + } else if (lff.LowerCaseName() == this->EndCommandName()) { this->ScopeDepth--; if (this->ScopeDepth == 0U) { cmMakefile& mf = status.GetMakefile(); diff --git a/Source/cmFunctionBlocker.h b/Source/cmFunctionBlocker.h index 59bb8927fb..b4b493b7d3 100644 --- a/Source/cmFunctionBlocker.h +++ b/Source/cmFunctionBlocker.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFunctionBlocker_h -#define cmFunctionBlocker_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -50,5 +49,3 @@ private: std::vector<cmListFileFunction> Functions; unsigned int ScopeDepth = 1; }; - -#endif diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx index b6f58bd054..71c82d6085 100644 --- a/Source/cmFunctionCommand.cxx +++ b/Source/cmFunctionCommand.cxx @@ -147,8 +147,7 @@ bool cmFunctionFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff, cmMakefile& mf) const { std::vector<std::string> expandedArguments; - mf.ExpandArguments(lff.Arguments, expandedArguments, - this->GetStartingContext().FilePath.c_str()); + mf.ExpandArguments(lff.Arguments(), expandedArguments); return expandedArguments.empty() || expandedArguments.front() == this->Args.front(); } diff --git a/Source/cmFunctionCommand.h b/Source/cmFunctionCommand.h index d6b549c673..07ff4f7158 100644 --- a/Source/cmFunctionCommand.h +++ b/Source/cmFunctionCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmFunctionCommand_h -#define cmFunctionCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -13,5 +12,3 @@ class cmExecutionStatus; /// Starts function() ... endfunction() block bool cmFunctionCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmGccDepfileLexerHelper.h b/Source/cmGccDepfileLexerHelper.h index e6b2fcf5b2..07ca61d181 100644 --- a/Source/cmGccDepfileLexerHelper.h +++ b/Source/cmGccDepfileLexerHelper.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGccDepfileLexerHelper_h -#define cmGccDepfileLexerHelper_h +#pragma once #include <utility> @@ -36,5 +35,3 @@ private: }; #define YY_EXTRA_TYPE cmGccDepfileLexerHelper* - -#endif diff --git a/Source/cmGccDepfileReader.h b/Source/cmGccDepfileReader.h index 9313010c43..395dd779a3 100644 --- a/Source/cmGccDepfileReader.h +++ b/Source/cmGccDepfileReader.h @@ -1,10 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGccDepfileReader_h -#define cmGccDepfileReader_h +#pragma once #include "cmGccDepfileReaderTypes.h" cmGccDepfileContent cmReadGccDepfile(const char* filePath); - -#endif diff --git a/Source/cmGccDepfileReaderTypes.h b/Source/cmGccDepfileReaderTypes.h index 8b15c73757..246e3556da 100644 --- a/Source/cmGccDepfileReaderTypes.h +++ b/Source/cmGccDepfileReaderTypes.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGccDepfileReaderTypes_h -#define cmGccDepfileReaderTypes_h +#pragma once #include <string> #include <vector> @@ -13,5 +12,3 @@ struct cmGccStyleDependency }; using cmGccDepfileContent = std::vector<cmGccStyleDependency>; - -#endif diff --git a/Source/cmGeneratedFileStream.cxx b/Source/cmGeneratedFileStream.cxx index 9cee0e6e96..276854742b 100644 --- a/Source/cmGeneratedFileStream.cxx +++ b/Source/cmGeneratedFileStream.cxx @@ -14,10 +14,11 @@ #endif cmGeneratedFileStream::cmGeneratedFileStream(Encoding encoding) + : OriginalLocale(getloc()) { #ifndef CMAKE_BOOTSTRAP if (encoding != codecvt::None) { - imbue(std::locale(getloc(), new codecvt(encoding))); + imbue(std::locale(OriginalLocale, new codecvt(encoding))); } #else static_cast<void>(encoding); @@ -122,10 +123,17 @@ void cmGeneratedFileStreamBase::Open(std::string const& name) // Create the name of the temporary file. this->TempName = name; #if defined(__VMS) - this->TempName += "_tmp"; + this->TempName += "_"; #else - this->TempName += ".tmp"; + this->TempName += "."; #endif + if (!this->TempExt.empty()) { + this->TempName += this->TempExt; + } else { + char buf[64]; + sprintf(buf, "tmp%05x", cmSystemTools::RandomSeed() & 0xFFFFF); + this->TempName += buf; + } // Make sure the temporary file that will be used is not present. cmSystemTools::RemoveFile(this->TempName); @@ -216,3 +224,19 @@ void cmGeneratedFileStream::SetName(const std::string& fname) { this->Name = fname; } + +void cmGeneratedFileStream::SetTempExt(std::string const& ext) +{ + this->TempExt = ext; +} + +void cmGeneratedFileStream::WriteRaw(std::string const& data) +{ +#ifndef CMAKE_BOOTSTRAP + std::locale activeLocale = this->imbue(this->OriginalLocale); + this->write(data.data(), data.size()); + this->imbue(activeLocale); +#else + this->write(data.data(), data.size()); +#endif +} diff --git a/Source/cmGeneratedFileStream.h b/Source/cmGeneratedFileStream.h index a9088ac8af..bb7e3bf52f 100644 --- a/Source/cmGeneratedFileStream.h +++ b/Source/cmGeneratedFileStream.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratedFileStream_h -#define cmGeneratedFileStream_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -43,6 +42,9 @@ protected: // The name of the final destination file for the output. std::string Name; + // The extension of the temporary file. + std::string TempExt; + // The name of the temporary file. std::string TempName; @@ -138,6 +140,20 @@ public: * the output file to be changed during the use of cmGeneratedFileStream. */ void SetName(const std::string& fname); -}; -#endif + /** + * Set set a custom temporary file extension used with 'Open'. + * This does not work if the file was opened by the constructor. + */ + void SetTempExt(std::string const& ext); + + /** + * Writes the given string directly to the file without changing the + * encoding. + */ + void WriteRaw(std::string const& data); + +private: + // The original locale of the stream (performs no encoding conversion). + std::locale OriginalLocale; +}; diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx index 6e293d51a8..840f5112d6 100644 --- a/Source/cmGeneratorExpression.cxx +++ b/Source/cmGeneratorExpression.cxx @@ -406,9 +406,3 @@ const std::string& cmGeneratorExpressionInterpreter::Evaluate( this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr, this->Language); } - -const std::string& cmGeneratorExpressionInterpreter::Evaluate( - const char* expression, const std::string& property) -{ - return this->Evaluate(std::string(expression ? expression : ""), property); -} diff --git a/Source/cmGeneratorExpression.h b/Source/cmGeneratorExpression.h index 75bba02d2e..03be782caa 100644 --- a/Source/cmGeneratorExpression.h +++ b/Source/cmGeneratorExpression.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratorExpression_h -#define cmGeneratorExpression_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -199,8 +198,6 @@ public: const std::string& Evaluate(std::string expression, const std::string& property); - const std::string& Evaluate(const char* expression, - const std::string& property); protected: cmGeneratorExpression GeneratorExpression; @@ -210,5 +207,3 @@ protected: cmGeneratorTarget const* HeadTarget = nullptr; std::string Language; }; - -#endif diff --git a/Source/cmGeneratorExpressionContext.h b/Source/cmGeneratorExpressionContext.h index bceff12bdb..22e74630ef 100644 --- a/Source/cmGeneratorExpressionContext.h +++ b/Source/cmGeneratorExpressionContext.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratorExpressionContext_h -#define cmGeneratorExpressionContext_h +#pragma once #include <map> #include <set> @@ -43,5 +42,3 @@ struct cmGeneratorExpressionContext bool HadLinkLanguageSensitiveCondition; bool EvaluateForBuildsystem; }; - -#endif diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx index 4f379cd7bd..e223f15453 100644 --- a/Source/cmGeneratorExpressionDAGChecker.cxx +++ b/Source/cmGeneratorExpressionDAGChecker.cxx @@ -154,6 +154,14 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingPICExpression() const return this->Top()->Property == "INTERFACE_POSITION_INDEPENDENT_CODE"; } +bool cmGeneratorExpressionDAGChecker::EvaluatingCompileExpression() const +{ + cm::string_view property(this->Top()->Property); + + return property == "INCLUDE_DIRECTORIES"_s || + property == "COMPILE_DEFINITIONS"_s || property == "COMPILE_OPTIONS"_s; +} + bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const { cm::string_view property(this->Top()->Property); diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h index c2c5b6b6bd..53225cd241 100644 --- a/Source/cmGeneratorExpressionDAGChecker.h +++ b/Source/cmGeneratorExpressionDAGChecker.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratorExpressionDAGChecker_h -#define cmGeneratorExpressionDAGChecker_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -68,6 +67,7 @@ struct cmGeneratorExpressionDAGChecker bool EvaluatingGenexExpression() const; bool EvaluatingPICExpression() const; + bool EvaluatingCompileExpression() const; bool EvaluatingLinkExpression() const; bool EvaluatingLinkOptionsExpression() const; @@ -99,5 +99,3 @@ private: Result CheckResult; bool TransitivePropertiesOnly; }; - -#endif diff --git a/Source/cmGeneratorExpressionEvaluationFile.cxx b/Source/cmGeneratorExpressionEvaluationFile.cxx index 9e8707d9d9..9e5023d39f 100644 --- a/Source/cmGeneratorExpressionEvaluationFile.cxx +++ b/Source/cmGeneratorExpressionEvaluationFile.cxx @@ -19,11 +19,12 @@ #include "cmSystemTools.h" cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile( - std::string input, + std::string input, std::string target, std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr, std::unique_ptr<cmCompiledGeneratorExpression> condition, bool inputIsContent, cmPolicies::PolicyStatus policyStatusCMP0070) : Input(std::move(input)) + , Target(std::move(target)) , OutputFileExpr(std::move(outputFileExpr)) , Condition(std::move(condition)) , InputIsContent(inputIsContent) @@ -37,9 +38,10 @@ void cmGeneratorExpressionEvaluationFile::Generate( std::map<std::string, std::string>& outputFiles, mode_t perm) { std::string rawCondition = this->Condition->GetInput(); + cmGeneratorTarget* target = lg->FindGeneratorTargetToUse(Target); if (!rawCondition.empty()) { std::string condResult = - this->Condition->Evaluate(lg, config, nullptr, nullptr, nullptr, lang); + this->Condition->Evaluate(lg, config, target, nullptr, nullptr, lang); if (condResult == "0") { return; } @@ -54,16 +56,10 @@ void cmGeneratorExpressionEvaluationFile::Generate( } } - std::string outputFileName = this->OutputFileExpr->Evaluate( - lg, config, nullptr, nullptr, nullptr, lang); + const std::string outputFileName = + this->GetOutputFileName(lg, target, config, lang); const std::string& outputContent = - inputExpression->Evaluate(lg, config, nullptr, nullptr, nullptr, lang); - - if (cmSystemTools::FileIsFullPath(outputFileName)) { - outputFileName = cmSystemTools::CollapseFullPath(outputFileName); - } else { - outputFileName = this->FixRelativePath(outputFileName, PathForOutput, lg); - } + inputExpression->Evaluate(lg, config, target, nullptr, nullptr, lang); auto it = outputFiles.find(outputFileName); @@ -98,11 +94,11 @@ void cmGeneratorExpressionEvaluationFile::CreateOutputFile( { std::vector<std::string> enabledLanguages; cmGlobalGenerator* gg = lg->GetGlobalGenerator(); + cmGeneratorTarget* target = lg->FindGeneratorTargetToUse(Target); gg->GetEnabledLanguages(enabledLanguages); for (std::string const& le : enabledLanguages) { - std::string name = this->OutputFileExpr->Evaluate(lg, config, nullptr, - nullptr, nullptr, le); + std::string const name = this->GetOutputFileName(lg, target, config, le); cmSourceFile* sf = lg->GetMakefile()->GetOrCreateSource( name, false, cmSourceFileLocationKind::Known); // Tell TraceDependencies that the file is not expected to exist @@ -125,12 +121,7 @@ void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg) if (this->InputIsContent) { inputContent = this->Input; } else { - std::string inputFileName = this->Input; - if (cmSystemTools::FileIsFullPath(inputFileName)) { - inputFileName = cmSystemTools::CollapseFullPath(inputFileName); - } else { - inputFileName = this->FixRelativePath(inputFileName, PathForInput, lg); - } + const std::string inputFileName = this->GetInputFileName(lg); lg->GetMakefile()->AddCMakeDependFile(inputFileName); cmSystemTools::GetPermissions(inputFileName.c_str(), perm); cmsys::ifstream fin(inputFileName.c_str()); @@ -157,12 +148,8 @@ void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg) std::map<std::string, std::string> outputFiles; - std::vector<std::string> allConfigs; - lg->GetMakefile()->GetConfigurations(allConfigs); - - if (allConfigs.empty()) { - allConfigs.emplace_back(); - } + std::vector<std::string> allConfigs = + lg->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); std::vector<std::string> enabledLanguages; cmGlobalGenerator* gg = lg->GetGlobalGenerator(); @@ -178,6 +165,36 @@ void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg) } } +std::string cmGeneratorExpressionEvaluationFile::GetInputFileName( + cmLocalGenerator* lg) +{ + std::string inputFileName = this->Input; + + if (cmSystemTools::FileIsFullPath(inputFileName)) { + inputFileName = cmSystemTools::CollapseFullPath(inputFileName); + } else { + inputFileName = this->FixRelativePath(inputFileName, PathForInput, lg); + } + + return inputFileName; +} + +std::string cmGeneratorExpressionEvaluationFile::GetOutputFileName( + cmLocalGenerator* lg, cmGeneratorTarget* target, const std::string& config, + const std::string& lang) +{ + std::string outputFileName = + this->OutputFileExpr->Evaluate(lg, config, target, nullptr, nullptr, lang); + + if (cmSystemTools::FileIsFullPath(outputFileName)) { + outputFileName = cmSystemTools::CollapseFullPath(outputFileName); + } else { + outputFileName = this->FixRelativePath(outputFileName, PathForOutput, lg); + } + + return outputFileName; +} + std::string cmGeneratorExpressionEvaluationFile::FixRelativePath( std::string const& relativePath, PathRole role, cmLocalGenerator* lg) { diff --git a/Source/cmGeneratorExpressionEvaluationFile.h b/Source/cmGeneratorExpressionEvaluationFile.h index c3bc4c85b9..2cd35ae945 100644 --- a/Source/cmGeneratorExpressionEvaluationFile.h +++ b/Source/cmGeneratorExpressionEvaluationFile.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratorExpressionEvaluationFile_h -#define cmGeneratorExpressionEvaluationFile_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -15,13 +14,14 @@ #include "cmGeneratorExpression.h" #include "cmPolicies.h" +class cmGeneratorTarget; class cmLocalGenerator; class cmGeneratorExpressionEvaluationFile { public: cmGeneratorExpressionEvaluationFile( - std::string input, + std::string input, std::string target, std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr, std::unique_ptr<cmCompiledGeneratorExpression> condition, bool inputIsContent, cmPolicies::PolicyStatus policyStatusCMP0070); @@ -38,6 +38,11 @@ private: cmCompiledGeneratorExpression* inputExpression, std::map<std::string, std::string>& outputFiles, mode_t perm); + std::string GetInputFileName(cmLocalGenerator* lg); + std::string GetOutputFileName(cmLocalGenerator* lg, + cmGeneratorTarget* target, + const std::string& config, + const std::string& lang); enum PathRole { PathForInput, @@ -48,11 +53,10 @@ private: private: const std::string Input; + const std::string Target; const std::unique_ptr<cmCompiledGeneratorExpression> OutputFileExpr; const std::unique_ptr<cmCompiledGeneratorExpression> Condition; std::vector<std::string> Files; const bool InputIsContent; cmPolicies::PolicyStatus PolicyStatusCMP0070; }; - -#endif diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h index 10496fd682..3e7737e6a8 100644 --- a/Source/cmGeneratorExpressionEvaluator.h +++ b/Source/cmGeneratorExpressionEvaluator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratorExpressionEvaluator_h -#define cmGeneratorExpressionEvaluator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -115,5 +114,3 @@ private: const char* StartContent; size_t ContentLength; }; - -#endif diff --git a/Source/cmGeneratorExpressionLexer.h b/Source/cmGeneratorExpressionLexer.h index 9e0194861b..a4321d1ae7 100644 --- a/Source/cmGeneratorExpressionLexer.h +++ b/Source/cmGeneratorExpressionLexer.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratorExpressionLexer_h -#define cmGeneratorExpressionLexer_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -49,5 +48,3 @@ private: bool SawBeginExpression = false; bool SawGeneratorExpression = false; }; - -#endif diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index e4fb67e699..4ca7405b5b 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -37,6 +37,7 @@ #include "cmPolicies.h" #include "cmProperty.h" #include "cmRange.h" +#include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" @@ -714,7 +715,8 @@ struct CompilerIdNode : public cmGeneratorExpressionNode static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"), cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"), - objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"); + objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"), + ispcCompilerIdNode("ISPC"); struct CompilerVersionNode : public cmGeneratorExpressionNode { @@ -779,7 +781,7 @@ struct CompilerVersionNode : public cmGeneratorExpressionNode static const CompilerVersionNode cCompilerVersionNode("C"), cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"), objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"), - fortranCompilerVersionNode("Fortran"); + fortranCompilerVersionNode("Fortran"), ispcCompilerVersionNode("ISPC"); struct PlatformIdNode : public cmGeneratorExpressionNode { @@ -881,7 +883,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode { ConfigurationTestNode() {} // NOLINT(modernize-use-equals-default) - int NumExpectedParameters() const override { return OneOrZeroParameters; } + int NumExpectedParameters() const override { return ZeroOrMoreParameters; } std::string Evaluate( const std::vector<std::string>& parameters, @@ -899,13 +901,15 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode return std::string(); } context->HadContextSensitiveCondition = true; - if (context->Config.empty()) { - return parameters.front().empty() ? "1" : "0"; - } - - if (cmsysString_strcasecmp(parameters.front().c_str(), - context->Config.c_str()) == 0) { - return "1"; + for (auto& param : parameters) { + if (context->Config.empty()) { + if (param.empty()) { + return "1"; + } + } else if (cmsysString_strcasecmp(param.c_str(), + context->Config.c_str()) == 0) { + return "1"; + } } if (context->CurrentTarget && context->CurrentTarget->IsImported()) { @@ -922,10 +926,12 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(context->Config)); if (cmProp mapValue = context->CurrentTarget->GetProperty(mapProp)) { cmExpandList(cmSystemTools::UpperCase(*mapValue), mappedConfigs); - return cm::contains(mappedConfigs, - cmSystemTools::UpperCase(parameters.front())) - ? "1" - : "0"; + + for (auto& param : parameters) { + if (cm::contains(mappedConfigs, cmSystemTools::UpperCase(param))) { + return "1"; + } + } } } } @@ -962,9 +968,10 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override + cmGeneratorExpressionDAGChecker* dagChecker) const override { - if (context->Language.empty()) { + if (context->Language.empty() && + (!dagChecker || !dagChecker->EvaluatingCompileExpression())) { reportError( context, content->GetOriginalExpression(), "$<COMPILE_LANGUAGE:...> may only be used to specify include " @@ -1009,7 +1016,9 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { - if (!context->HeadTarget || context->Language.empty()) { + if (!context->HeadTarget || + (context->Language.empty() && + (!dagChecker || !dagChecker->EvaluatingCompileExpression()))) { // reportError(context, content->GetOriginalExpression(), ""); reportError( context, content->GetOriginalExpression(), @@ -1468,8 +1477,9 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode } if (isInterfaceProperty) { - return target->EvaluateInterfaceProperty(propertyName, context, - dagCheckerParent); + return cmGeneratorExpression::StripEmptyListElements( + target->EvaluateInterfaceProperty(propertyName, context, + dagCheckerParent)); } cmGeneratorExpressionDAGChecker dagChecker( @@ -1555,8 +1565,9 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode } if (!interfacePropertyName.empty()) { - result = this->EvaluateDependentExpression(result, context->LG, context, - target, &dagChecker, target); + result = cmGeneratorExpression::StripEmptyListElements( + this->EvaluateDependentExpression(result, context->LG, context, target, + &dagChecker, target)); std::string linkedTargetsContent = getLinkedTargetsContent( target, interfacePropertyName, context, &dagChecker); if (!linkedTargetsContent.empty()) { @@ -1653,9 +1664,8 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode if (context->EvaluateForBuildsystem) { // Use object file directory with buildsystem placeholder. obj_dir = gt->ObjectDirectory; - // Here we assume that the set of object files produced - // by an object library does not vary with configuration - // and do not set HadContextSensitiveCondition to true. + context->HadContextSensitiveCondition = + gt->HasContextDependentSources(); } else { // Use object file directory with per-config location. obj_dir = gt->GetObjectDirectory(context->Config); @@ -1703,12 +1713,12 @@ static const struct CompileFeaturesNode : public cmGeneratorExpressionNode static LangMap availableFeatures; LangMap testedFeatures; - + cmStandardLevelResolver standardResolver(context->LG->GetMakefile()); for (std::string const& p : parameters) { std::string error; std::string lang; - if (!context->LG->GetMakefile()->CompileFeatureKnown( - context->HeadTarget->Target, p, lang, &error)) { + if (!standardResolver.CompileFeatureKnown( + context->HeadTarget->Target->GetName(), p, lang, &error)) { reportError(context, content->GetOriginalExpression(), error); return std::string(); } @@ -1716,7 +1726,7 @@ static const struct CompileFeaturesNode : public cmGeneratorExpressionNode if (availableFeatures.find(lang) == availableFeatures.end()) { const char* featuresKnown = - context->LG->GetMakefile()->CompileFeaturesAvailable(lang, &error); + standardResolver.CompileFeaturesAvailable(lang, &error); if (!featuresKnown) { reportError(context, content->GetOriginalExpression(), error); return std::string(); @@ -1730,7 +1740,7 @@ static const struct CompileFeaturesNode : public cmGeneratorExpressionNode for (auto const& lit : testedFeatures) { std::vector<std::string> const& langAvailable = availableFeatures[lit.first]; - cmProp standardDefault = context->LG->GetMakefile()->GetDef( + cmProp standardDefault = context->LG->GetMakefile()->GetDefinition( "CMAKE_" + lit.first + "_STANDARD_DEFAULT"); for (std::string const& it : lit.second) { if (!cm::contains(langAvailable, it)) { @@ -1741,10 +1751,10 @@ static const struct CompileFeaturesNode : public cmGeneratorExpressionNode // All features known for the language are always available. continue; } - if (!context->LG->GetMakefile()->HaveStandardAvailable( - target->Target, lit.first, it)) { + if (!standardResolver.HaveStandardAvailable(target, lit.first, + context->Config, it)) { if (evalLL) { - cmProp l = target->GetProperty(lit.first + "_STANDARD"); + cmProp l = target->GetLanguageStandard(lit.first, context->Config); if (!l) { l = standardDefault; } @@ -1891,6 +1901,70 @@ class ArtifactSonameTag; class ArtifactBundleDirTag; class ArtifactBundleContentDirTag; +template <typename ArtifactT, typename ComponentT> +struct TargetFilesystemArtifactDependency +{ + static void AddDependency(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context) + { + context->DependTargets.insert(target); + context->AllTargets.insert(target); + } +}; + +struct TargetFilesystemArtifactDependencyCMP0112 +{ + static void AddDependency(cmGeneratorTarget* target, + cmGeneratorExpressionContext* context) + { + context->AllTargets.insert(target); + cmLocalGenerator* lg = context->LG; + switch (target->GetPolicyStatusCMP0112()) { + case cmPolicies::WARN: + if (lg->GetMakefile()->PolicyOptionalWarningEnabled( + "CMAKE_POLICY_WARNING_CMP0112")) { + std::string err = + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0112), + "\nDependency being added to target:\n \"", + target->GetName(), "\"\n"); + lg->GetCMakeInstance()->IssueMessage(MessageType ::AUTHOR_WARNING, + err, context->Backtrace); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + context->DependTargets.insert(target); + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + break; + } + } +}; + +template <typename ArtifactT> +struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactNameTag> + : TargetFilesystemArtifactDependencyCMP0112 +{ +}; +template <typename ArtifactT> +struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactDirTag> + : TargetFilesystemArtifactDependencyCMP0112 +{ +}; +template <> +struct TargetFilesystemArtifactDependency<ArtifactBundleDirTag, + ArtifactPathTag> + : TargetFilesystemArtifactDependencyCMP0112 +{ +}; +template <> +struct TargetFilesystemArtifactDependency<ArtifactBundleContentDirTag, + ArtifactPathTag> + : TargetFilesystemArtifactDependencyCMP0112 +{ +}; + template <typename ArtifactT> struct TargetFilesystemArtifactResultCreator { @@ -2143,8 +2217,10 @@ struct TargetFilesystemArtifact : public TargetArtifactBase if (!target) { return std::string(); } - context->DependTargets.insert(target); - context->AllTargets.insert(target); + // Not a dependent target if we are querying for ArtifactDirTag, + // ArtifactNameTag, ArtifactBundleDirTag, and ArtifactBundleContentDirTag + TargetFilesystemArtifactDependency<ArtifactT, ComponentT>::AddDependency( + target, context); std::string result = TargetFilesystemArtifactResultCreator<ArtifactT>::Create(target, context, diff --git a/Source/cmGeneratorExpressionNode.h b/Source/cmGeneratorExpressionNode.h index 13e8484ef7..f068b02d3a 100644 --- a/Source/cmGeneratorExpressionNode.h +++ b/Source/cmGeneratorExpressionNode.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratorExpressionNode_h -#define cmGeneratorExpressionNode_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -52,5 +51,3 @@ struct cmGeneratorExpressionNode void reportError(cmGeneratorExpressionContext* context, const std::string& expr, const std::string& result); - -#endif diff --git a/Source/cmGeneratorExpressionParser.h b/Source/cmGeneratorExpressionParser.h index 1ba1654187..d49bf3e22a 100644 --- a/Source/cmGeneratorExpressionParser.h +++ b/Source/cmGeneratorExpressionParser.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratorExpressionParser_h -#define cmGeneratorExpressionParser_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -30,5 +29,3 @@ private: const std::vector<cmGeneratorExpressionToken> Tokens; unsigned int NestingLevel; }; - -#endif diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index b7bf4a6f7e..c299dadf04 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -43,6 +43,7 @@ #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" +#include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -275,8 +276,8 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg) , DebugLinkDirectoriesDone(false) , DebugPrecompileHeadersDone(false) , DebugSourcesDone(false) - , LinkImplementationLanguageIsContextDependent(true) , UtilityItemsDone(false) + , SourcesAreContextDependent(Tribool::Indeterminate) { this->Makefile = this->Target->GetMakefile(); this->LocalGenerator = lg; @@ -364,7 +365,7 @@ std::string cmGeneratorTarget::GetExportName() const { cmProp exportName = this->GetProperty("EXPORT_NAME"); - if (exportName && !exportName->empty()) { + if (cmNonempty(exportName)) { if (!cmGeneratorExpression::IsValidTargetName(*exportName)) { std::ostringstream e; e << "EXPORT_NAME property \"" << *exportName << "\" for \"" @@ -379,11 +380,6 @@ std::string cmGeneratorTarget::GetExportName() const cmProp cmGeneratorTarget::GetProperty(const std::string& prop) const { - if (!cmTargetPropertyComputer::PassesWhitelist( - this->GetType(), prop, this->Makefile->GetMessenger(), - this->GetBacktrace())) { - return nullptr; - } if (cmProp result = cmTargetPropertyComputer::GetProperty( this, prop, this->Makefile->GetMessenger(), this->GetBacktrace())) { return result; @@ -520,9 +516,8 @@ std::string cmGeneratorTarget::GetFilePrefix( const std::string& config, cmStateEnums::ArtifactType artifact) const { if (this->IsImported()) { - const char* prefix = this->GetFilePrefixInternal(config, artifact); - - return prefix ? prefix : std::string(); + cmProp prefix = this->GetFilePrefixInternal(config, artifact); + return prefix ? *prefix : std::string(); } std::string prefix; @@ -535,9 +530,8 @@ std::string cmGeneratorTarget::GetFileSuffix( const std::string& config, cmStateEnums::ArtifactType artifact) const { if (this->IsImported()) { - const char* suffix = this->GetFileSuffixInternal(config, artifact); - - return suffix ? suffix : std::string(); + cmProp suffix = this->GetFileSuffixInternal(config, artifact); + return suffix ? *suffix : std::string(); } std::string prefix; @@ -591,7 +585,7 @@ std::string cmGeneratorTarget::GetFrameworkMultiConfigPostfix( return postfix ? *postfix : std::string(); } -const char* cmGeneratorTarget::GetFilePrefixInternal( +cmProp cmGeneratorTarget::GetFilePrefixInternal( std::string const& config, cmStateEnums::ArtifactType artifact, const std::string& language) const { @@ -627,21 +621,22 @@ const char* cmGeneratorTarget::GetFilePrefixInternal( if (!targetPrefix) { const char* prefixVar = this->Target->GetPrefixVariableInternal(artifact); - if (!language.empty() && prefixVar && *prefixVar) { - std::string langPrefix = prefixVar + std::string("_") + language; - targetPrefix = this->Makefile->GetDef(langPrefix); + if (!language.empty() && cmNonempty(prefixVar)) { + std::string langPrefix = cmStrCat(prefixVar, "_", 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->GetDef(prefixVar); + targetPrefix = this->Makefile->GetDefinition(prefixVar); } } - return targetPrefix ? targetPrefix->c_str() : nullptr; + return targetPrefix; } -const char* cmGeneratorTarget::GetFileSuffixInternal( + +cmProp cmGeneratorTarget::GetFileSuffixInternal( std::string const& config, cmStateEnums::ArtifactType artifact, const std::string& language) const { @@ -677,26 +672,26 @@ const char* cmGeneratorTarget::GetFileSuffixInternal( if (!targetSuffix) { const char* suffixVar = this->Target->GetSuffixVariableInternal(artifact); - if (!language.empty() && suffixVar && *suffixVar) { - std::string langSuffix = suffixVar + std::string("_") + language; - targetSuffix = this->Makefile->GetDef(langSuffix); + if (!language.empty() && cmNonempty(suffixVar)) { + std::string langSuffix = cmStrCat(suffixVar, "_", 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->GetDef(suffixVar); + targetSuffix = this->Makefile->GetDefinition(suffixVar); } } - return targetSuffix ? targetSuffix->c_str() : nullptr; + return targetSuffix; } void cmGeneratorTarget::ClearSourcesCache() { this->AllConfigSources.clear(); this->KindedSourcesMap.clear(); - this->LinkImplementationLanguageIsContextDependent = true; + this->SourcesAreContextDependent = Tribool::Indeterminate; this->Objects.clear(); this->VisitedConfigsForObjects.clear(); } @@ -801,7 +796,8 @@ void cmGeneratorTarget::GetObjectSources( void cmGeneratorTarget::ComputeObjectMapping() { - auto const& configs = this->Makefile->GetGeneratorConfigs(); + auto const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); std::set<std::string> configSet(configs.begin(), configs.end()); if (configSet == this->VisitedConfigsForObjects) { return; @@ -813,18 +809,18 @@ void cmGeneratorTarget::ComputeObjectMapping() } } -const char* cmGeneratorTarget::GetFeature(const std::string& feature, - const std::string& config) const +cmProp cmGeneratorTarget::GetFeature(const std::string& feature, + const std::string& config) const { if (!config.empty()) { std::string featureConfig = cmStrCat(feature, '_', cmSystemTools::UpperCase(config)); if (cmProp value = this->GetProperty(featureConfig)) { - return value->c_str(); + return value; } } if (cmProp value = this->GetProperty(feature)) { - return value->c_str(); + return value; } return this->LocalGenerator->GetFeature(feature, config); } @@ -851,10 +847,9 @@ const char* cmGeneratorTarget::GetLinkPIEProperty( bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang, std::string const& config) const { - const char* feature = "INTERPROCEDURAL_OPTIMIZATION"; - const bool result = cmIsOn(this->GetFeature(feature, config)); + cmProp feature = this->GetFeature("INTERPROCEDURAL_OPTIMIZATION", config); - if (!result) { + if (!cmIsOn(feature)) { // 'INTERPROCEDURAL_OPTIMIZATION' is off, no need to check policies return false; } @@ -947,6 +942,61 @@ bool cmGeneratorTarget::HasExplicitObjectName(cmSourceFile const* file) const return it != this->ExplicitObjectName.end(); } +BTs<std::string> const* cmGeneratorTarget::GetLanguageStandardProperty( + std::string const& lang, std::string const& config) const +{ + std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang); + auto langStandardIter = this->LanguageStandardMap.find(key); + if (langStandardIter != this->LanguageStandardMap.end()) { + return &langStandardIter->second; + } + + return this->Target->GetLanguageStandardProperty( + cmStrCat(lang, "_STANDARD")); +} + +cmProp cmGeneratorTarget::GetLanguageStandard(std::string const& lang, + std::string const& config) const +{ + BTs<std::string> const* languageStandard = + this->GetLanguageStandardProperty(lang, config); + + if (languageStandard) { + return &(languageStandard->Value); + } + + return nullptr; +} + +cmProp cmGeneratorTarget::GetPropertyWithPairedLanguageSupport( + std::string const& lang, const char* suffix) const +{ + cmProp propertyValue = this->Target->GetProperty(cmStrCat(lang, suffix)); + if (propertyValue == nullptr) { + // Check if we should use the value set by another language. + if (lang == "OBJC") { + propertyValue = this->GetPropertyWithPairedLanguageSupport("C", suffix); + } else if (lang == "OBJCXX" || lang == "CUDA") { + propertyValue = + this->GetPropertyWithPairedLanguageSupport("CXX", suffix); + } + } + return propertyValue; +} + +cmProp cmGeneratorTarget::GetLanguageExtensions(std::string const& lang) const +{ + return this->GetPropertyWithPairedLanguageSupport(lang, "_EXTENSIONS"); +} + +bool cmGeneratorTarget::GetLanguageStandardRequired( + std::string const& lang) const +{ + cmProp p = + this->GetPropertyWithPairedLanguageSupport(lang, "_STANDARD_REQUIRED"); + return cmIsOn(p); +} + void cmGeneratorTarget::GetModuleDefinitionSources( std::vector<cmSourceFile const*>& data, const std::string& config) const { @@ -1033,6 +1083,62 @@ std::vector<cmCustomCommand> const& cmGeneratorTarget::GetPostBuildCommands() return this->Target->GetPostBuildCommands(); } +void cmGeneratorTarget::AppendCustomCommandSideEffects( + std::set<cmGeneratorTarget const*>& sideEffects) const +{ + if (!this->GetPreBuildCommands().empty() || + !this->GetPreLinkCommands().empty() || + !this->GetPostBuildCommands().empty()) { + sideEffects.insert(this); + } else { + for (auto const& source : this->GetAllConfigSources()) { + if (source.Source->GetCustomCommand() != nullptr) { + sideEffects.insert(this); + break; + } + } + } +} + +void cmGeneratorTarget::AppendLanguageSideEffects( + std::map<std::string, std::set<cmGeneratorTarget const*>>& sideEffects) const +{ + static const std::set<cm::string_view> LANGS_WITH_NO_SIDE_EFFECTS = { + "C"_s, "CXX"_s, "OBJC"_s, "OBJCXX"_s, "ASM"_s, "CUDA"_s, + }; + + for (auto const& lang : this->GetAllConfigCompileLanguages()) { + if (!LANGS_WITH_NO_SIDE_EFFECTS.count(lang)) { + sideEffects[lang].insert(this); + } + } +} + +bool cmGeneratorTarget::IsInBuildSystem() const +{ + if (this->IsImported()) { + return false; + } + switch (this->Target->GetType()) { + case cmStateEnums::EXECUTABLE: + case cmStateEnums::STATIC_LIBRARY: + case cmStateEnums::SHARED_LIBRARY: + case cmStateEnums::MODULE_LIBRARY: + case cmStateEnums::OBJECT_LIBRARY: + case cmStateEnums::UTILITY: + case cmStateEnums::GLOBAL_TARGET: + return true; + case cmStateEnums::INTERFACE_LIBRARY: + // An INTERFACE library is in the build system if it has SOURCES. + if (!this->SourceEntries.empty()) { + return true; + } + case cmStateEnums::UNKNOWN_LIBRARY: + break; + } + return false; +} + bool cmGeneratorTarget::IsImported() const { return this->Target->IsImported(); @@ -1043,6 +1149,11 @@ bool cmGeneratorTarget::IsImportedGloballyVisible() const return this->Target->IsImportedGloballyVisible(); } +bool cmGeneratorTarget::CanCompileSources() const +{ + return this->Target->CanCompileSources(); +} + const std::string& cmGeneratorTarget::GetLocationForBuild() const { static std::string location; @@ -1055,10 +1166,10 @@ const std::string& cmGeneratorTarget::GetLocationForBuild() const // Now handle the deprecated build-time configuration location. std::string const noConfig; location = this->GetDirectory(noConfig); - const char* cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR"); - if (cfgid && strcmp(cfgid, ".") != 0) { + cmProp cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR"); + if (cfgid && (*cfgid != ".")) { location += "/"; - location += cfgid; + location += *cfgid; } if (this->IsAppBundleOnApple()) { @@ -1083,8 +1194,8 @@ bool cmGeneratorTarget::IsSystemIncludeDirectory( config_upper = cmSystemTools::UpperCase(config); } - using IncludeCacheType = std::map<std::string, std::vector<std::string>>; - auto iter = this->SystemIncludesCache.find(config_upper); + std::string key = cmStrCat(config_upper, "/", language); + auto iter = this->SystemIncludesCache.find(key); if (iter == this->SystemIncludesCache.end()) { cmGeneratorExpressionDAGChecker dagChecker( @@ -1112,8 +1223,7 @@ bool cmGeneratorTarget::IsSystemIncludeDirectory( std::sort(result.begin(), result.end()); result.erase(std::unique(result.begin(), result.end()), result.end()); - IncludeCacheType::value_type entry(config_upper, result); - iter = this->SystemIncludesCache.insert(entry).first; + iter = this->SystemIncludesCache.emplace(key, result).first; } return std::binary_search(iter->second.begin(), iter->second.end(), dir); @@ -1136,8 +1246,7 @@ bool cmGeneratorTarget::MaybeHaveInterfaceProperty( bool& maybeInterfaceProp = i->second; // If this target itself has a non-empty property value, we are done. - cmProp p = this->GetProperty(prop); - maybeInterfaceProp = p && !p->empty(); + maybeInterfaceProp = cmNonempty(this->GetProperty(prop)); // Otherwise, recurse to interface dependencies. if (!maybeInterfaceProp) { @@ -1251,18 +1360,25 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty( } namespace { -std::string AddSwiftInterfaceIncludeDirectories( + +enum class IncludeDirectoryFallBack +{ + BINARY, + OBJECT +}; + +std::string AddLangSpecificInterfaceIncludeDirectories( const cmGeneratorTarget* root, const cmGeneratorTarget* target, - const std::string& config, cmGeneratorExpressionDAGChecker* context) + const std::string& lang, const std::string& config, + const std::string& propertyName, IncludeDirectoryFallBack mode, + cmGeneratorExpressionDAGChecker* context) { cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target, - "Swift_MODULE_DIRECTORY", nullptr, - context }; + propertyName, nullptr, context }; switch (dag.Check()) { case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: - dag.ReportError(nullptr, - "$<TARGET_PROPERTY:" + target->GetName() + - ",Swift_MODULE_DIRECTORY>"); + dag.ReportError( + nullptr, "$<TARGET_PROPERTY:" + target->GetName() + ",propertyName"); CM_FALLTHROUGH; case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: // No error. We just skip cyclic references. @@ -1278,13 +1394,16 @@ std::string AddSwiftInterfaceIncludeDirectories( target->GetLinkInterfaceLibraries(config, root, true)) { for (const cmLinkItem& library : interface->Libraries) { if (const cmGeneratorTarget* dependency = library.Target) { - if (cm::contains(dependency->GetAllConfigCompileLanguages(), - "Swift")) { - std::string value = - dependency->GetSafeProperty("Swift_MODULE_DIRECTORY"); + if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { + auto* lg = dependency->GetLocalGenerator(); + std::string value = dependency->GetSafeProperty(propertyName); if (value.empty()) { - value = - dependency->GetLocalGenerator()->GetCurrentBinaryDirectory(); + if (mode == IncludeDirectoryFallBack::BINARY) { + value = lg->GetCurrentBinaryDirectory(); + } else if (mode == IncludeDirectoryFallBack::OBJECT) { + value = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', + lg->GetTargetDirectory(dependency)); + } } if (!directories.empty()) { @@ -1298,35 +1417,39 @@ std::string AddSwiftInterfaceIncludeDirectories( return directories; } -void AddSwiftImplicitIncludeDirectories( - const cmGeneratorTarget* target, const std::string& config, - EvaluatedTargetPropertyEntries& entries) +void AddLangSpecificImplicitIncludeDirectories( + const cmGeneratorTarget* target, const std::string& lang, + const std::string& config, const std::string& propertyName, + IncludeDirectoryFallBack mode, EvaluatedTargetPropertyEntries& entries) { if (const auto* libraries = target->GetLinkImplementationLibraries(config)) { cmGeneratorExpressionDAGChecker dag{ target->GetBacktrace(), target, - "Swift_MODULE_DIRECTORY", nullptr, - nullptr }; + propertyName, nullptr, nullptr }; for (const cmLinkImplItem& library : libraries->Libraries) { if (const cmGeneratorTarget* dependency = library.Target) { - if (dependency->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!dependency->IsInBuildSystem()) { continue; } - if (cm::contains(dependency->GetAllConfigCompileLanguages(), - "Swift")) { + if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) { + auto* lg = dependency->GetLocalGenerator(); EvaluatedTargetPropertyEntry entry{ library, library.Backtrace }; - if (cmProp val = dependency->GetProperty("Swift_MODULE_DIRECTORY")) { + if (cmProp val = dependency->GetProperty(propertyName)) { entry.Values.emplace_back(*val); } else { - entry.Values.emplace_back( - dependency->GetLocalGenerator()->GetCurrentBinaryDirectory()); + if (mode == IncludeDirectoryFallBack::BINARY) { + entry.Values.emplace_back(lg->GetCurrentBinaryDirectory()); + } else if (mode == IncludeDirectoryFallBack::OBJECT) { + entry.Values.emplace_back( + dependency->GetObjectDirectory(config)); + } } - cmExpandList(AddSwiftInterfaceIncludeDirectories(target, dependency, - config, &dag), - entry.Values); - + cmExpandList( + AddLangSpecificInterfaceIncludeDirectories( + target, dependency, lang, config, propertyName, mode, &dag), + entry.Values); entries.Entries.emplace_back(std::move(entry)); } } @@ -1466,7 +1589,6 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths( std::string const& config) const { std::vector<BT<std::string>> files; - assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY); if (!this->LocalGenerator->GetGlobalGenerator()->GetConfigureDoneCMP0026()) { // At configure-time, this method can be called as part of getting the @@ -1528,10 +1650,13 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths( uniqueSrcs, debugSources); } + // Determine if sources are context-dependent or not. if (!contextDependentDirectSources && !(contextDependentInterfaceSources && numFilesBefore < files.size()) && !(contextDependentObjects && numFilesBefore2 < files.size())) { - this->LinkImplementationLanguageIsContextDependent = false; + this->SourcesAreContextDependent = Tribool::False; + } else { + this->SourcesAreContextDependent = Tribool::True; } return files; @@ -1606,9 +1731,9 @@ cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries( cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources( std::string const& config) const { - // If we already processed one configuration and found no dependenc + // If we already processed one configuration and found no dependency // on configuration then always use the one result. - if (!this->LinkImplementationLanguageIsContextDependent) { + if (this->SourcesAreContextDependent == Tribool::False) { return this->KindedSourcesMap.begin()->second; } @@ -1658,9 +1783,11 @@ void cmGeneratorTarget::ComputeKindedSources(KindedSources& files, std::string ext = cmSystemTools::LowerCase(sf->GetExtension()); if (sf->GetCustomCommand()) { kind = SourceKindCustomCommand; - // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 - // NOLINTNEXTLINE(bugprone-branch-clone) - } else if (this->Target->GetType() == cmStateEnums::UTILITY) { + } else if (this->Target->GetType() == cmStateEnums::UTILITY || + this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY + // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 + // NOLINTNEXTLINE(bugprone-branch-clone) + ) { kind = SourceKindExtra; } else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) { kind = SourceKindUnityBatched; @@ -1726,8 +1853,8 @@ cmGeneratorTarget::GetAllConfigSources() const void cmGeneratorTarget::ComputeAllConfigSources() const { - std::vector<std::string> configs; - this->Makefile->GetConfigurations(configs); + std::vector<std::string> configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); std::map<cmSourceFile const*, size_t> index; @@ -1787,12 +1914,12 @@ std::string cmGeneratorTarget::GetCompilePDBName( std::string configUpper = cmSystemTools::UpperCase(config); std::string configProp = cmStrCat("COMPILE_PDB_NAME_", configUpper); cmProp config_name = this->GetProperty(configProp); - if (config_name && !config_name->empty()) { + if (cmNonempty(config_name)) { return prefix + *config_name + ".pdb"; } cmProp name = this->GetProperty("COMPILE_PDB_NAME"); - if (name && !name->empty()) { + if (cmNonempty(name)) { return prefix + *name + ".pdb"; } @@ -1938,13 +2065,13 @@ bool cmGeneratorTarget::IsChrpathUsed(const std::string& config) const if (!ll.empty()) { std::string sepVar = cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_FLAG_SEP"); - const char* sep = this->Makefile->GetDefinition(sepVar); - if (sep && *sep) { + cmProp sep = this->Makefile->GetDefinition(sepVar); + if (cmNonempty(sep)) { // TODO: Add ELF check to ABI detection and get rid of // CMAKE_EXECUTABLE_FORMAT. - if (const char* fmt = + if (cmProp fmt = this->Makefile->GetDefinition("CMAKE_EXECUTABLE_FORMAT")) { - return strcmp(fmt, "ELF") == 0; + return (*fmt == "ELF"); } } } @@ -2153,6 +2280,12 @@ bool cmGeneratorTarget::IsBundleOnApple() const this->IsCFBundleOnApple(); } +bool cmGeneratorTarget::IsWin32Executable(const std::string& config) const +{ + return cmIsOn(cmGeneratorExpression::Evaluate( + this->GetSafeProperty("WIN32_EXECUTABLE"), this->LocalGenerator, config)); +} + std::string cmGeneratorTarget::GetCFBundleDirectory( const std::string& config, BundleDirectoryLevel level) const { @@ -2239,7 +2372,7 @@ std::string cmGeneratorTarget::GetInstallNameDirForInstallTree( cmProp install_name_dir = this->GetProperty("INSTALL_NAME_DIR"); if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_INSTALL)) { - if (install_name_dir && !install_name_dir->empty()) { + if (cmNonempty(install_name_dir)) { dir = *install_name_dir; cmGeneratorExpression::ReplaceInstallPrefix(dir, installPrefix); dir = @@ -2385,6 +2518,12 @@ private: cmGeneratorTarget::LinkClosure const* cmGeneratorTarget::GetLinkClosure( const std::string& config) const { + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + static LinkClosure const empty = { {}, {} }; + return ∅ + } + std::string key(cmSystemTools::UpperCase(config)); auto i = this->LinkClosureMap.find(key); if (i == this->LinkClosureMap.end()) { @@ -2701,6 +2840,12 @@ const std::vector<const cmGeneratorTarget*>& cmGeneratorTarget::GetLinkImplementationClosure( const std::string& config) const { + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { + static std::vector<const cmGeneratorTarget*> const empty; + return empty; + } + LinkImplClosure& tgts = this->LinkImplClosureMap[config]; if (!tgts.Done) { tgts.Done = true; @@ -2708,6 +2853,7 @@ cmGeneratorTarget::GetLinkImplementationClosure( cmLinkImplementationLibraries const* impl = this->GetLinkImplementationLibraries(config); + assert(impl); for (cmLinkImplItem const& lib : impl->Libraries) { processILibs(config, this, lib, @@ -2757,29 +2903,26 @@ cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) this->CurrentEntry = nullptr; // Queue all the source files already specified for the target. - if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - std::set<cmSourceFile*> emitted; - std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(); - for (std::string const& c : configs) { - std::vector<cmSourceFile*> sources; - this->GeneratorTarget->GetSourceFiles(sources, c); - for (cmSourceFile* sf : sources) { - const std::set<cmGeneratorTarget const*> tgts = - this->GlobalGenerator->GetFilenameTargetDepends(sf); - if (cm::contains(tgts, this->GeneratorTarget)) { - std::ostringstream e; - e << "Evaluation output file\n \"" << sf->ResolveFullPath() - << "\"\ndepends on the sources of a target it is used in. This " - "is a dependency loop and is not allowed."; - this->GeneratorTarget->LocalGenerator->IssueMessage( - MessageType::FATAL_ERROR, e.str()); - return; - } - if (emitted.insert(sf).second && - this->SourcesQueued.insert(sf).second) { - this->SourceQueue.push(sf); - } + std::set<cmSourceFile*> emitted; + std::vector<std::string> const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& c : configs) { + std::vector<cmSourceFile*> sources; + this->GeneratorTarget->GetSourceFiles(sources, c); + for (cmSourceFile* sf : sources) { + const std::set<cmGeneratorTarget const*> tgts = + this->GlobalGenerator->GetFilenameTargetDepends(sf); + if (cm::contains(tgts, this->GeneratorTarget)) { + std::ostringstream e; + e << "Evaluation output file\n \"" << sf->ResolveFullPath() + << "\"\ndepends on the sources of a target it is used in. This " + "is a dependency loop and is not allowed."; + this->GeneratorTarget->LocalGenerator->IssueMessage( + MessageType::FATAL_ERROR, e.str()); + return; + } + if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) { + this->SourceQueue.push(sf); } } } @@ -2955,12 +3098,17 @@ void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) } // Check for target references in generator expressions. - for (std::string const& cl : cCmdLine) { - const std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(cl); - cge->SetQuiet(true); - cge->Evaluate(this->GeneratorTarget->GetLocalGenerator(), ""); - std::set<cmGeneratorTarget*> geTargets = cge->GetTargets(); - targets.insert(geTargets.begin(), geTargets.end()); + std::vector<std::string> const& configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& c : configs) { + for (std::string const& cl : cCmdLine) { + const std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(cl); + cge->SetQuiet(true); + cge->Evaluate(this->GeneratorTarget->GetLocalGenerator(), c); + std::set<cmGeneratorTarget*> geTargets = cge->GetTargets(); + targets.insert(geTargets.begin(), geTargets.end()); + } } } @@ -2971,7 +3119,7 @@ void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) // Queue the custom command dependencies. std::set<std::string> emitted; std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(); + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); for (std::string const& conf : configs) { this->FollowCommandDepends(cc, conf, emitted); } @@ -3047,6 +3195,9 @@ void cmGeneratorTarget::GetAppleArchs(const std::string& config, if (archs) { cmExpandList(*archs, archVec); } + if (archVec.empty()) { + this->Makefile->GetDefExpandList("_CMAKE_APPLE_ARCHS_DEFAULT", archVec); + } } void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const @@ -3111,7 +3262,7 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const } else { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - "Uknown CUDA architecture specifier \"" + std::string(specifier) + + "Unknown CUDA architecture specifier \"" + std::string(specifier) + "\"."); } } @@ -3159,6 +3310,27 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const } } +void cmGeneratorTarget::AddISPCTargetFlags(std::string& flags) const +{ + const std::string& property = this->GetSafeProperty("ISPC_INSTRUCTION_SETS"); + + // If ISPC_TARGET is false we don't add any architectures. + if (cmIsOff(property)) { + return; + } + + std::string const& compiler = + this->Makefile->GetSafeDefinition("CMAKE_ISPC_COMPILER_ID"); + + if (compiler == "Intel") { + std::vector<std::string> targets; + cmExpandList(property, targets); + if (!targets.empty()) { + flags += cmStrCat(" --target=", cmWrap("", targets, "", ",")); + } + } +} + void cmGeneratorTarget::AddCUDAToolkitFlags(std::string& flags) const { std::string const& compiler = @@ -3353,30 +3525,52 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories( this, config, lang, &dagChecker, this->IncludeDirectoriesEntries); if (lang == "Swift") { - AddSwiftImplicitIncludeDirectories(this, config, entries); + AddLangSpecificImplicitIncludeDirectories( + this, lang, config, "Swift_MODULE_DIRECTORY", + IncludeDirectoryFallBack::BINARY, entries); + } + + if (this->CanCompileSources() && (lang != "Swift" && lang != "Fortran")) { + + const std::string propertyName = "ISPC_HEADER_DIRECTORY"; + + // If this target has ISPC sources make sure to add the header + // directory to other compilation units + if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) { + if (cmProp val = this->GetProperty(propertyName)) { + includes.emplace_back(*val); + } else { + includes.emplace_back(this->GetObjectDirectory(config)); + } + } + + AddLangSpecificImplicitIncludeDirectories( + this, "ISPC", config, propertyName, IncludeDirectoryFallBack::OBJECT, + entries); } AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang, &dagChecker, entries); if (this->Makefile->IsOn("APPLE")) { - cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibraries(config); - for (cmLinkImplItem const& lib : impl->Libraries) { - std::string libDir = cmSystemTools::CollapseFullPath( - lib.AsStr(), this->Makefile->GetHomeOutputDirectory()); - - static cmsys::RegularExpression frameworkCheck( - "(.*\\.framework)(/Versions/[^/]+)?/[^/]+$"); - if (!frameworkCheck.find(libDir)) { - continue; - } + if (cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibraries(config)) { + for (cmLinkImplItem const& lib : impl->Libraries) { + std::string libDir = cmSystemTools::CollapseFullPath( + lib.AsStr(), this->Makefile->GetHomeOutputDirectory()); + + static cmsys::RegularExpression frameworkCheck( + "(.*\\.framework)(/Versions/[^/]+)?/[^/]+$"); + if (!frameworkCheck.find(libDir)) { + continue; + } - libDir = frameworkCheck.match(1); + libDir = frameworkCheck.match(1); - EvaluatedTargetPropertyEntry ee(lib, cmListFileBacktrace()); - ee.Values.emplace_back(std::move(libDir)); - entries.Entries.emplace_back(std::move(ee)); + EvaluatedTargetPropertyEntry ee(lib, cmListFileBacktrace()); + ee.Values.emplace_back(std::move(libDir)); + entries.Entries.emplace_back(std::move(ee)); + } } } @@ -3744,8 +3938,8 @@ std::string cmGeneratorTarget::GetPchHeader(const std::string& config, const std::string filename_tmp = cmStrCat(filename, ".tmp"); if (!pchReuseFrom) { - auto pchPrologue = this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE"); - auto pchEpilogue = this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE"); + cmProp pchPrologue = this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE"); + cmProp pchEpilogue = this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE"); std::string firstHeaderOnDisk; { @@ -3754,7 +3948,7 @@ std::string cmGeneratorTarget::GetPchHeader(const std::string& config, this->GetGlobalGenerator()->GetMakefileEncoding()); file << "/* generated by CMake */\n\n"; if (pchPrologue) { - file << pchPrologue << "\n"; + file << *pchPrologue << "\n"; } if (this->GetGlobalGenerator()->IsXcode()) { file << "#ifndef CMAKE_SKIP_PRECOMPILE_HEADERS\n"; @@ -3784,7 +3978,7 @@ std::string cmGeneratorTarget::GetPchHeader(const std::string& config, file << "#endif // CMAKE_SKIP_PRECOMPILE_HEADERS\n"; } if (pchEpilogue) { - file << pchEpilogue << "\n"; + file << *pchEpilogue << "\n"; } } @@ -3827,7 +4021,7 @@ std::string cmGeneratorTarget::GetPchSource(const std::string& config, cmStrCat(generatorTarget->LocalGenerator->GetCurrentBinaryDirectory(), "/CMakeFiles/", generatorTarget->GetName(), ".dir/cmake_pch"); - // For GCC the source extension will be tranformed into .h[xx].gch + // For GCC the source extension will be transformed into .h[xx].gch if (!this->Makefile->IsOn("CMAKE_LINK_PCH")) { const std::map<std::string, std::string> languageToExtension = { { "C", ".h.c" }, @@ -3948,6 +4142,16 @@ std::string cmGeneratorTarget::GetPchCreateCompileOptions( cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_INVALID_PCH")); } + if (this->GetPropertyAsBool("PCH_INSTANTIATE_TEMPLATES")) { + std::string varName = cmStrCat( + "CMAKE_", language, "_COMPILE_OPTIONS_INSTANTIATE_TEMPLATES_PCH"); + std::string instantiateOption = + this->Makefile->GetSafeDefinition(varName); + if (!instantiateOption.empty()) { + createOptionList = cmStrCat(createOptionList, ";", instantiateOption); + } + } + const std::string createOptVar = cmStrCat("CMAKE_", language, "_COMPILE_OPTIONS_CREATE_PCH"); @@ -4414,12 +4618,75 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const { + // Compute the language standard based on the compile features. + cmStandardLevelResolver standardResolver(this->Makefile); std::vector<BT<std::string>> features = this->GetCompileFeatures(config); for (BT<std::string> const& f : features) { - if (!this->Makefile->AddRequiredTargetFeature(this->Target, f.Value)) { + std::string lang; + if (!standardResolver.CompileFeatureKnown(this->Target->GetName(), f.Value, + lang, nullptr)) { return false; } + + std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang); + cmProp currentLanguageStandard = this->GetLanguageStandard(lang, config); + + std::string newRequiredStandard; + if (!standardResolver.GetNewRequiredStandard( + this->Target->GetName(), f.Value, currentLanguageStandard, + newRequiredStandard)) { + return false; + } + + if (!newRequiredStandard.empty()) { + BTs<std::string>& languageStandardProperty = + this->LanguageStandardMap[key]; + if (languageStandardProperty.Value != newRequiredStandard) { + languageStandardProperty.Value = newRequiredStandard; + languageStandardProperty.Backtraces.clear(); + } + languageStandardProperty.Backtraces.emplace_back(f.Backtrace); + } + } + + return true; +} + +bool cmGeneratorTarget::ComputeCompileFeatures( + std::string const& config, std::set<LanguagePair> const& languagePairs) const +{ + for (const auto& language : languagePairs) { + BTs<std::string> const* generatorTargetLanguageStandard = + this->GetLanguageStandardProperty(language.first, config); + if (!generatorTargetLanguageStandard) { + // If the standard isn't explicitly set we copy it over from the + // specified paired language. + std::string key = + cmStrCat(cmSystemTools::UpperCase(config), '-', language.first); + BTs<std::string> const* standardToCopy = + this->GetLanguageStandardProperty(language.second, config); + if (standardToCopy != nullptr) { + this->LanguageStandardMap[key] = *standardToCopy; + generatorTargetLanguageStandard = &this->LanguageStandardMap[key]; + } else { + cmProp defaultStandard = this->Makefile->GetDefinition( + cmStrCat("CMAKE_", language.second, "_STANDARD_DEFAULT")); + if (defaultStandard != nullptr) { + this->LanguageStandardMap[key] = BTs<std::string>(*defaultStandard); + generatorTargetLanguageStandard = &this->LanguageStandardMap[key]; + } + } + + // Custom updates for the CUDA standard. + if (generatorTargetLanguageStandard != nullptr && + language.first == "CUDA") { + if (generatorTargetLanguageStandard->Value == "98") { + this->LanguageStandardMap[key].Value = "03"; + } + } + } } + return true; } @@ -4547,12 +4814,11 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames( // The library's soname. this->ComputeVersionedName(targetNames.SharedObject, prefix, targetNames.Base, suffix, targetNames.Output, - (soversion ? soversion->c_str() : nullptr)); + cmToCStr(soversion)); // The library's real name on disk. this->ComputeVersionedName(targetNames.Real, prefix, targetNames.Base, - suffix, targetNames.Output, - (version ? version->c_str() : nullptr)); + suffix, targetNames.Output, cmToCStr(version)); } // The import library name. @@ -4588,10 +4854,7 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames( const char* version = nullptr; #else // Check for executable version properties. - const char* version = nullptr; - if (cmProp p = this->GetProperty("VERSION")) { - version = p->c_str(); - } + const char* version = cmToCStr(this->GetProperty("VERSION")); if (this->GetType() != cmStateEnums::EXECUTABLE || this->Makefile->IsOn("XCODE")) { version = nullptr; @@ -4685,8 +4948,8 @@ void cmGeneratorTarget::GetFullNameInternal( // retrieve prefix and suffix std::string ll = this->GetLinkerLanguage(config); - const char* targetPrefix = this->GetFilePrefixInternal(config, artifact, ll); - const char* targetSuffix = this->GetFileSuffixInternal(config, artifact, ll); + cmProp targetPrefix = this->GetFilePrefixInternal(config, artifact, ll); + cmProp targetSuffix = this->GetFileSuffixInternal(config, artifact, ll); // The implib option is only allowed for shared libraries, module // libraries, and executables. @@ -4704,18 +4967,18 @@ void cmGeneratorTarget::GetFullNameInternal( if (this->IsFrameworkOnApple()) { fw_prefix = cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/'); - targetPrefix = fw_prefix.c_str(); + targetPrefix = &fw_prefix; targetSuffix = nullptr; } if (this->IsCFBundleOnApple()) { fw_prefix = cmStrCat(this->GetCFBundleDirectory(config, FullLevel), '/'); - targetPrefix = fw_prefix.c_str(); + targetPrefix = &fw_prefix; targetSuffix = nullptr; } // Begin the final name with the prefix. - outPrefix = targetPrefix ? targetPrefix : ""; + outPrefix = targetPrefix ? *targetPrefix : ""; // Append the target name or property-specified name. outBase += this->GetOutputName(config, artifact); @@ -4726,7 +4989,7 @@ void cmGeneratorTarget::GetFullNameInternal( // EXECUTABLE_SUFFIX attribute. if (this->IsFrameworkOnApple() && GetGlobalGenerator()->GetName() == "Xcode") { - targetSuffix = configPostfix.c_str(); + targetSuffix = &configPostfix; } else { outBase += configPostfix; } @@ -4742,7 +5005,7 @@ void cmGeneratorTarget::GetFullNameInternal( } // Append the suffix. - outSuffix = targetSuffix ? targetSuffix : ""; + outSuffix = targetSuffix ? *targetSuffix : ""; } std::string cmGeneratorTarget::GetLinkerLanguage( @@ -4843,6 +5106,16 @@ void cmGeneratorTarget::GetTargetObjectNames( assert(!map_it->second.empty()); objects.push_back(map_it->second); } + + // We need to compute the relative path from the root of + // of the object directory to handle subdirectory paths + std::string rootObjectDir = this->GetObjectDirectory(config); + rootObjectDir = cmSystemTools::CollapseFullPath(rootObjectDir); + auto ispcObjects = this->GetGeneratedISPCObjects(config); + for (std::string const& output : ispcObjects) { + auto relativePathFromObjectDir = output.substr(rootObjectDir.size()); + objects.push_back(relativePathFromObjectDir); + } } bool cmGeneratorTarget::StrictTargetComparison::operator()( @@ -5297,8 +5570,7 @@ bool getTypedProperty<bool>(cmGeneratorTarget const* tgt, } cmProp value = tgt->GetProperty(prop); - return cmIsOn( - genexInterpreter->Evaluate(value ? value->c_str() : nullptr, prop)); + return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop)); } template <> @@ -5309,11 +5581,10 @@ const char* getTypedProperty<const char*>( cmProp value = tgt->GetProperty(prop); if (genexInterpreter == nullptr) { - return value ? value->c_str() : nullptr; + return cmToCStr(value); } - return genexInterpreter->Evaluate(value ? value->c_str() : nullptr, prop) - .c_str(); + return genexInterpreter->Evaluate(value ? *value : "", prop).c_str(); } template <> @@ -5324,10 +5595,10 @@ std::string getTypedProperty<std::string>( cmProp value = tgt->GetProperty(prop); if (genexInterpreter == nullptr) { - return valueAsString(value ? value->c_str() : nullptr); + return valueAsString(cmToCStr(value)); } - return genexInterpreter->Evaluate(value ? value->c_str() : nullptr, prop); + return genexInterpreter->Evaluate(value ? *value : "", prop); } template <typename PropertyType> @@ -5726,9 +5997,9 @@ std::string cmGeneratorTarget::GetRuntimeLinkLibrary( { // This is activated by the presence of a default selection whether or // not it is overridden by a property. - cmProp runtimeLibraryDefault = this->Makefile->GetDef( + cmProp runtimeLibraryDefault = this->Makefile->GetDefinition( cmStrCat("CMAKE_", lang, "_RUNTIME_LIBRARY_DEFAULT")); - if (!runtimeLibraryDefault || runtimeLibraryDefault->empty()) { + if (!cmNonempty(runtimeLibraryDefault)) { return std::string(); } cmProp runtimeLibraryValue = @@ -5766,7 +6037,7 @@ std::string cmGeneratorTarget::CreateFortranModuleDirectory( target_mod_dir = default_mod_dir; } } - const char* moddir_flag = + cmProp moddir_flag = this->Makefile->GetDefinition("CMAKE_Fortran_MODDIR_FLAG"); if (!target_mod_dir.empty() && moddir_flag) { // Compute the full path to the module directory. @@ -5785,6 +6056,66 @@ std::string cmGeneratorTarget::CreateFortranModuleDirectory( return mod_dir; } +void cmGeneratorTarget::AddISPCGeneratedHeader(std::string const& header, + std::string const& config) +{ + std::string config_upper; + if (!config.empty()) { + config_upper = cmSystemTools::UpperCase(config); + } + auto iter = this->ISPCGeneratedHeaders.find(config_upper); + if (iter == this->ISPCGeneratedHeaders.end()) { + std::vector<std::string> headers; + headers.emplace_back(header); + this->ISPCGeneratedHeaders.insert({ config_upper, headers }); + } else { + iter->second.emplace_back(header); + } +} + +std::vector<std::string> cmGeneratorTarget::GetGeneratedISPCHeaders( + std::string const& config) const +{ + std::string config_upper; + if (!config.empty()) { + config_upper = cmSystemTools::UpperCase(config); + } + auto iter = this->ISPCGeneratedHeaders.find(config_upper); + if (iter == this->ISPCGeneratedHeaders.end()) { + return std::vector<std::string>{}; + } + return iter->second; +} + +void cmGeneratorTarget::AddISPCGeneratedObject(std::vector<std::string>&& objs, + std::string const& config) +{ + std::string config_upper; + if (!config.empty()) { + config_upper = cmSystemTools::UpperCase(config); + } + auto iter = this->ISPCGeneratedObjects.find(config_upper); + if (iter == this->ISPCGeneratedObjects.end()) { + this->ISPCGeneratedObjects.insert({ config_upper, objs }); + } else { + iter->second.insert(iter->second.end(), objs.begin(), objs.end()); + } +} + +std::vector<std::string> cmGeneratorTarget::GetGeneratedISPCObjects( + std::string const& config) const +{ + std::string config_upper; + if (!config.empty()) { + config_upper = cmSystemTools::UpperCase(config); + } + auto iter = this->ISPCGeneratedObjects.find(config_upper); + if (iter == this->ISPCGeneratedObjects.end()) { + return std::vector<std::string>{}; + } + return iter->second; +} + std::string cmGeneratorTarget::GetFrameworkVersion() const { assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY); @@ -6164,21 +6495,16 @@ bool cmGeneratorTarget::ComputeOutputDir(const std::string& config, // Look for a target property defining the target output directory // based on the target type. std::string targetTypeName = this->GetOutputTargetType(artifact); - const char* propertyName = nullptr; - std::string propertyNameStr = targetTypeName; - if (!propertyNameStr.empty()) { - propertyNameStr += "_OUTPUT_DIRECTORY"; - propertyName = propertyNameStr.c_str(); + std::string propertyName; + if (!targetTypeName.empty()) { + propertyName = cmStrCat(targetTypeName, "_OUTPUT_DIRECTORY"); } // Check for a per-configuration output directory target property. std::string configUpper = cmSystemTools::UpperCase(conf); - const char* configProp = nullptr; - std::string configPropStr = targetTypeName; - if (!configPropStr.empty()) { - configPropStr += "_OUTPUT_DIRECTORY_"; - configPropStr += configUpper; - configProp = configPropStr.c_str(); + std::string configProp; + if (!targetTypeName.empty()) { + configProp = cmStrCat(targetTypeName, "_OUTPUT_DIRECTORY_", configUpper); } // Select an output directory. @@ -6239,22 +6565,17 @@ bool cmGeneratorTarget::ComputePDBOutputDir(const std::string& kind, { // Look for a target property defining the target output directory // based on the target type. - const char* propertyName = nullptr; - std::string propertyNameStr = kind; - if (!propertyNameStr.empty()) { - propertyNameStr += "_OUTPUT_DIRECTORY"; - propertyName = propertyNameStr.c_str(); + std::string propertyName; + if (!kind.empty()) { + propertyName = cmStrCat(kind, "_OUTPUT_DIRECTORY"); } std::string conf = config; // Check for a per-configuration output directory target property. std::string configUpper = cmSystemTools::UpperCase(conf); - const char* configProp = nullptr; - std::string configPropStr = kind; - if (!configPropStr.empty()) { - configPropStr += "_OUTPUT_DIRECTORY_"; - configPropStr += configUpper; - configProp = configPropStr.c_str(); + std::string configProp; + if (!kind.empty()) { + configProp = cmStrCat(kind, "_OUTPUT_DIRECTORY_", configUpper); } // Select an output directory. @@ -6410,15 +6731,20 @@ void cmGeneratorTarget::ComputeLinkInterfaceLibraries( iface.HadHeadSensitiveCondition, iface.HadContextSensitiveCondition, iface.HadLinkLanguageSensitiveCondition); - } else if (!cmp0022NEW) + return; + } + // If CMP0022 is NEW then the plain tll signature sets the // INTERFACE_LINK_LIBRARIES, so if we get here then the project // cleared the property explicitly and we should not fall back // to the link implementation. - { - // The link implementation is the default link interface. - cmLinkImplementationLibraries const* impl = - this->GetLinkImplementationLibrariesInternal(config, headTarget); + if (cmp0022NEW) { + return; + } + + // The link implementation is the default link interface. + if (cmLinkImplementationLibraries const* impl = + this->GetLinkImplementationLibrariesInternal(config, headTarget)) { iface.Libraries.insert(iface.Libraries.end(), impl->Libraries.begin(), impl->Libraries.end()); if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN && @@ -6712,8 +7038,8 @@ const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation( const std::string& config, bool secondPass) const { - // There is no link implementation for imported targets. - if (this->IsImported()) { + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { return nullptr; } @@ -6737,7 +7063,7 @@ bool cmGeneratorTarget::GetConfigCommonSourceFiles( std::vector<cmSourceFile*>& files) const { std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(); + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); auto it = configs.begin(); const std::string& firstConfig = *it; @@ -6861,7 +7187,7 @@ std::string cmGeneratorTarget::CheckCMP0004(std::string const& item) const bool cmGeneratorTarget::IsDeprecated() const { cmProp deprecation = this->GetProperty("DEPRECATION"); - return deprecation && !deprecation->empty(); + return cmNonempty(deprecation); } std::string cmGeneratorTarget::GetDeprecation() const @@ -6876,6 +7202,11 @@ std::string cmGeneratorTarget::GetDeprecation() const void cmGeneratorTarget::GetLanguages(std::set<std::string>& languages, const std::string& config) const { + // Targets that do not compile anything have no languages. + if (!this->CanCompileSources()) { + return; + } + std::vector<cmSourceFile*> sourceFiles; this->GetSourceFiles(sourceFiles, config); for (cmSourceFile* src : sourceFiles) { @@ -6934,7 +7265,7 @@ bool cmGeneratorTarget::IsCSharpOnly() const // Consider an explicit linker language property, but *not* the // computed linker language that may depend on linked targets. cmProp linkLang = this->GetProperty("LINKER_LANGUAGE"); - if (linkLang && !linkLang->empty()) { + if (cmNonempty(linkLang)) { languages.insert(*linkLang); } return languages.size() == 1 && languages.count("CSharp") > 0; @@ -6979,8 +7310,8 @@ cmLinkImplementationLibraries const* cmGeneratorTarget::GetLinkImplementationLibrariesInternal( const std::string& config, cmGeneratorTarget const* head) const { - // There is no link implementation for imported targets. - if (this->IsImported()) { + // There is no link implementation for targets that cannot compile sources. + if (!this->CanCompileSources()) { return nullptr; } @@ -7208,6 +7539,11 @@ bool cmGeneratorTarget::GetImplibGNUtoMS(std::string const& config, return false; } +bool cmGeneratorTarget::HasContextDependentSources() const +{ + return this->SourcesAreContextDependent == Tribool::True; +} + bool cmGeneratorTarget::IsExecutableWithExports() const { return (this->GetType() == cmStateEnums::EXECUTABLE && diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index ea3a684a13..2517b7205e 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGeneratorTarget_h -#define cmGeneratorTarget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -45,14 +44,22 @@ public: cmGlobalGenerator* GetGlobalGenerator() const; + bool IsInBuildSystem() const; bool IsImported() const; bool IsImportedGloballyVisible() const; + bool CanCompileSources() const; const std::string& GetLocation(const std::string& config) const; std::vector<cmCustomCommand> const& GetPreBuildCommands() const; std::vector<cmCustomCommand> const& GetPreLinkCommands() const; std::vector<cmCustomCommand> const& GetPostBuildCommands() const; + void AppendCustomCommandSideEffects( + std::set<cmGeneratorTarget const*>& sideEffects) const; + void AppendLanguageSideEffects( + std::map<std::string, std::set<cmGeneratorTarget const*>>& sideEffects) + const; + #define DECLARE_TARGET_POLICY(POLICY) \ cmPolicies::PolicyStatus GetPolicyStatus##POLICY() const \ { \ @@ -148,6 +155,16 @@ public: bool HasExplicitObjectName(cmSourceFile const* file) const; void AddExplicitObjectName(cmSourceFile const* sf); + BTs<std::string> const* GetLanguageStandardProperty( + std::string const& lang, std::string const& config) const; + + cmProp GetLanguageStandard(std::string const& lang, + std::string const& config) const; + + cmProp GetLanguageExtensions(std::string const& lang) const; + + bool GetLanguageStandardRequired(std::string const& lang) const; + void GetModuleDefinitionSources(std::vector<cmSourceFile const*>&, const std::string& config) const; void GetExternalObjects(std::vector<cmSourceFile const*>&, @@ -165,8 +182,8 @@ public: void ComputeObjectMapping(); - const char* GetFeature(const std::string& feature, - const std::string& config) const; + cmProp GetFeature(const std::string& feature, + const std::string& config) const; const char* GetLinkPIEProperty(const std::string& config) const; @@ -266,6 +283,9 @@ public: or CFBundle on Apple. */ bool IsBundleOnApple() const; + /** Return whether this target is a Win32 executable */ + bool IsWin32Executable(const std::string& config) const; + /** Get the full name of the target according to the settings in its makefile. */ std::string GetFullName(const std::string& config, @@ -430,6 +450,8 @@ public: void AddCUDAArchitectureFlags(std::string& flags) const; void AddCUDAToolkitFlags(std::string& flags) const; + void AddISPCTargetFlags(std::string& flags) const; + std::string GetFeatureSpecificLinkRuleVariable( std::string const& var, std::string const& lang, std::string const& config) const; @@ -517,6 +539,11 @@ public: bool ComputeCompileFeatures(std::string const& config) const; + using LanguagePair = std::pair<std::string, std::string>; + bool ComputeCompileFeatures( + std::string const& config, + std::set<LanguagePair> const& languagePairs) const; + /** * Trace through the source files in this target and add al source files * that they depend on, used by all generators @@ -688,6 +715,10 @@ public: bool GetImplibGNUtoMS(std::string const& config, std::string const& gnuName, std::string& out, const char* newExt = nullptr) const; + /** Can only ever return true if GetSourceFilePaths() was called before. + Otherwise, this is indeterminate and false will be assumed/returned! */ + bool HasContextDependentSources() const; + bool IsExecutableWithExports() const; /** Return whether or not the target has a DLL import library. */ @@ -792,6 +823,16 @@ public: const std::string& GetSourcesProperty() const; + void AddISPCGeneratedHeader(std::string const& header, + std::string const& config); + std::vector<std::string> GetGeneratedISPCHeaders( + std::string const& config) const; + + void AddISPCGeneratedObject(std::vector<std::string>&& objs, + std::string const& config); + std::vector<std::string> GetGeneratedISPCObjects( + std::string const& config) const; + private: void AddSourceCommon(const std::string& src, bool before = false); @@ -810,6 +851,8 @@ private: mutable std::set<std::string> VisitedConfigsForObjects; mutable std::map<cmSourceFile const*, std::string> Objects; std::set<cmSourceFile const*> ExplicitObjectName; + + // "config/language" is the key mutable std::map<std::string, std::vector<std::string>> SystemIncludesCache; mutable std::string ExportMacro; @@ -822,12 +865,12 @@ private: bool NeedImportLibraryName(std::string const& config) const; - const char* GetFilePrefixInternal(std::string const& config, - cmStateEnums::ArtifactType artifact, - const std::string& language = "") const; - const char* GetFileSuffixInternal(std::string const& config, - cmStateEnums::ArtifactType artifact, - const std::string& language = "") const; + cmProp GetFilePrefixInternal(std::string const& config, + cmStateEnums::ArtifactType artifact, + const std::string& language = "") const; + cmProp GetFileSuffixInternal(std::string const& config, + cmStateEnums::ArtifactType artifact, + const std::string& language = "") const; std::string GetFullNameInternal(const std::string& config, cmStateEnums::ArtifactType artifact) const; @@ -970,6 +1013,11 @@ private: std::unordered_set<std::string> UnityBatchedSourceFiles; + std::unordered_map<std::string, std::vector<std::string>> + ISPCGeneratedHeaders; + std::unordered_map<std::string, std::vector<std::string>> + ISPCGeneratedObjects; + bool IsLinkLookupScope(std::string const& n, cmLocalGenerator const*& lg) const; @@ -1029,8 +1077,14 @@ private: mutable bool DebugLinkDirectoriesDone; mutable bool DebugPrecompileHeadersDone; mutable bool DebugSourcesDone; - mutable bool LinkImplementationLanguageIsContextDependent; mutable bool UtilityItemsDone; + enum class Tribool + { + False = 0x0, + True = 0x1, + Indeterminate = 0x2 + }; + mutable Tribool SourcesAreContextDependent; bool ComputePDBOutputDir(const std::string& kind, const std::string& config, std::string& out) const; @@ -1040,6 +1094,11 @@ private: bool GetRPATH(const std::string& config, const std::string& prop, std::string& rpath) const; + mutable std::map<std::string, BTs<std::string>> LanguageStandardMap; + + cmProp GetPropertyWithPairedLanguageSupport(std::string const& lang, + const char* suffix) const; + public: const std::vector<const cmGeneratorTarget*>& GetLinkImplementationClosure( const std::string& config) const; @@ -1056,5 +1115,3 @@ public: cmGeneratorTarget const* t2) const; }; }; - -#endif diff --git a/Source/cmGetCMakePropertyCommand.h b/Source/cmGetCMakePropertyCommand.h index 7a6728cfe6..3a2e7028f8 100644 --- a/Source/cmGetCMakePropertyCommand.h +++ b/Source/cmGetCMakePropertyCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGetCMakePropertyCommand_h -#define cmGetCMakePropertyCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmGetCMakePropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmGetDirectoryPropertyCommand.cxx b/Source/cmGetDirectoryPropertyCommand.cxx index fa4a40bef0..c2098c0f23 100644 --- a/Source/cmGetDirectoryPropertyCommand.cxx +++ b/Source/cmGetDirectoryPropertyCommand.cxx @@ -86,9 +86,7 @@ bool cmGetDirectoryPropertyCommand(std::vector<std::string> const& args, break; } } - if (cmProp p = dir->GetProperty(*i)) { - prop = p->c_str(); - } + prop = cmToCStr(dir->GetProperty(*i)); } StoreResult(status.GetMakefile(), variable, prop); return true; diff --git a/Source/cmGetDirectoryPropertyCommand.h b/Source/cmGetDirectoryPropertyCommand.h index f356ea5b4d..4b0883c656 100644 --- a/Source/cmGetDirectoryPropertyCommand.h +++ b/Source/cmGetDirectoryPropertyCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGetDirectoryPropertyCommand_h -#define cmGetDirectoryPropertyCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmGetDirectoryPropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmGetFilenameComponentCommand.cxx b/Source/cmGetFilenameComponentCommand.cxx index 811421a6dd..40e8a05d65 100644 --- a/Source/cmGetFilenameComponentCommand.cxx +++ b/Source/cmGetFilenameComponentCommand.cxx @@ -4,6 +4,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -14,14 +15,15 @@ bool cmGetFilenameComponentCommand(std::vector<std::string> const& args, { if (args.size() < 3) { status.SetError("called with incorrect number of arguments"); + cmSystemTools::SetFatalErrorOccured(); return false; } // Check and see if the value has been stored in the cache // already, if so use that value if (args.size() >= 4 && args.back() == "CACHE") { - const char* cacheValue = status.GetMakefile().GetDefinition(args.front()); - if (cacheValue && !cmIsNOTFOUND(cacheValue)) { + cmProp cacheValue = status.GetMakefile().GetDefinition(args.front()); + if (cacheValue && !cmIsNOTFOUND(*cacheValue)) { return true; } } @@ -114,6 +116,7 @@ bool cmGetFilenameComponentCommand(std::vector<std::string> const& args, } else { std::string err = "unknown component " + args[2]; status.SetError(err); + cmSystemTools::SetFatalErrorOccured(); return false; } diff --git a/Source/cmGetFilenameComponentCommand.h b/Source/cmGetFilenameComponentCommand.h index db5293b2ae..4e1addf828 100644 --- a/Source/cmGetFilenameComponentCommand.h +++ b/Source/cmGetFilenameComponentCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGetFilenameComponentCommand_h -#define cmGetFilenameComponentCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -18,5 +17,3 @@ class cmExecutionStatus; */ bool cmGetFilenameComponentCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmGetPipes.h b/Source/cmGetPipes.h index 2a46b51b3a..6b1b4951da 100644 --- a/Source/cmGetPipes.h +++ b/Source/cmGetPipes.h @@ -1,8 +1,5 @@ /* 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 +#pragma once int cmGetPipes(int* fds); - -#endif diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx index cba770479c..3a5b39da65 100644 --- a/Source/cmGetPropertyCommand.cxx +++ b/Source/cmGetPropertyCommand.cxx @@ -17,7 +17,6 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" -#include "cmTargetPropertyComputer.h" #include "cmTest.h" #include "cmake.h" @@ -282,8 +281,7 @@ bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name, // Get the property. cmake* cm = status.GetMakefile().GetCMakeInstance(); cmProp p = cm->GetState()->GetGlobalProperty(propertyName); - return StoreResult(infoType, status.GetMakefile(), variable, - p ? p->c_str() : nullptr); + return StoreResult(infoType, status.GetMakefile(), variable, cmToCStr(p)); } bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name, @@ -330,8 +328,7 @@ bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name, // Get the property. cmProp p = mf->GetProperty(propertyName); - return StoreResult(infoType, status.GetMakefile(), variable, - p ? p->c_str() : nullptr); + return StoreResult(infoType, status.GetMakefile(), variable, cmToCStr(p)); } bool HandleTargetMode(cmExecutionStatus& status, const std::string& name, @@ -364,12 +361,9 @@ bool HandleTargetMode(cmExecutionStatus& status, const std::string& name, cmProp prop_cstr = nullptr; cmListFileBacktrace bt = status.GetMakefile().GetBacktrace(); cmMessenger* messenger = status.GetMakefile().GetMessenger(); - if (cmTargetPropertyComputer::PassesWhitelist( - target->GetType(), propertyName, messenger, bt)) { - prop_cstr = target->GetComputedProperty(propertyName, messenger, bt); - if (!prop_cstr) { - prop_cstr = target->GetProperty(propertyName); - } + prop_cstr = target->GetComputedProperty(propertyName, messenger, bt); + if (!prop_cstr) { + prop_cstr = target->GetProperty(propertyName); } return StoreResult(infoType, status.GetMakefile(), variable, prop_cstr ? prop_cstr->c_str() : nullptr); @@ -434,8 +428,9 @@ bool HandleVariableMode(cmExecutionStatus& status, const std::string& name, return false; } - return StoreResult(infoType, status.GetMakefile(), variable, - status.GetMakefile().GetDefinition(propertyName)); + return StoreResult( + infoType, status.GetMakefile(), variable, + cmToCStr(status.GetMakefile().GetDefinition(propertyName))); } bool HandleCacheMode(cmExecutionStatus& status, const std::string& name, @@ -452,8 +447,7 @@ bool HandleCacheMode(cmExecutionStatus& status, const std::string& name, value = status.GetMakefile().GetState()->GetCacheEntryProperty( name, propertyName); } - StoreResult(infoType, status.GetMakefile(), variable, - value ? value->c_str() : nullptr); + StoreResult(infoType, status.GetMakefile(), variable, cmToCStr(value)); return true; } diff --git a/Source/cmGetPropertyCommand.h b/Source/cmGetPropertyCommand.h index cc600f4eef..fac320253f 100644 --- a/Source/cmGetPropertyCommand.h +++ b/Source/cmGetPropertyCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGetPropertyCommand_h -#define cmGetPropertyCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmGetPropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmGetSourceFilePropertyCommand.h b/Source/cmGetSourceFilePropertyCommand.h index f0c319b0a0..4f8eab2efa 100644 --- a/Source/cmGetSourceFilePropertyCommand.h +++ b/Source/cmGetSourceFilePropertyCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGetSourceFilePropertyCommand_h -#define cmGetSourceFilePropertyCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmGetSourceFilePropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmGetTargetPropertyCommand.cxx b/Source/cmGetTargetPropertyCommand.cxx index 8a304be0de..78a17d2c3e 100644 --- a/Source/cmGetTargetPropertyCommand.cxx +++ b/Source/cmGetTargetPropertyCommand.cxx @@ -12,7 +12,6 @@ #include "cmPolicies.h" #include "cmProperty.h" #include "cmTarget.h" -#include "cmTargetPropertyComputer.h" class cmMessenger; @@ -46,12 +45,9 @@ bool cmGetTargetPropertyCommand(std::vector<std::string> const& args, cmProp prop_cstr = nullptr; cmListFileBacktrace bt = mf.GetBacktrace(); cmMessenger* messenger = mf.GetMessenger(); - if (cmTargetPropertyComputer::PassesWhitelist(tgt->GetType(), args[2], - messenger, bt)) { - prop_cstr = tgt->GetComputedProperty(args[2], messenger, bt); - if (!prop_cstr) { - prop_cstr = tgt->GetProperty(args[2]); - } + prop_cstr = tgt->GetComputedProperty(args[2], messenger, bt); + if (!prop_cstr) { + prop_cstr = tgt->GetProperty(args[2]); } if (prop_cstr) { prop = *prop_cstr; diff --git a/Source/cmGetTargetPropertyCommand.h b/Source/cmGetTargetPropertyCommand.h index c13078f8c6..0fbd23dfc8 100644 --- a/Source/cmGetTargetPropertyCommand.h +++ b/Source/cmGetTargetPropertyCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGetTargetPropertyCommand_h -#define cmGetTargetPropertyCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmGetTargetPropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmGetTestPropertyCommand.h b/Source/cmGetTestPropertyCommand.h index 30beb8f0f6..f1d60106e3 100644 --- a/Source/cmGetTestPropertyCommand.h +++ b/Source/cmGetTestPropertyCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGetTestPropertyCommand_h -#define cmGetTestPropertyCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmGetTestPropertyCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmGhsMultiGpj.h b/Source/cmGhsMultiGpj.h index fbbef5d8f7..1cae660ce2 100644 --- a/Source/cmGhsMultiGpj.h +++ b/Source/cmGhsMultiGpj.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGhsMultiGpj_h -#define cmGhsMultiGpj_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -25,5 +24,3 @@ public: static const char* GetGpjTag(Types gpjType); }; - -#endif // ! cmGhsMultiGpjType_h diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index 97580d6ec1..a8f8f573d6 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -43,9 +43,9 @@ cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target) #endif { // Store the configuration name that is being used - if (const char* config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) { + if (cmProp config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) { // Use the build type given by the user. - this->ConfigName = config; + this->ConfigName = *config; } else { // No configuration type given. this->ConfigName.clear(); @@ -550,10 +550,9 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) */ for (auto& sg : groupFilesList) { std::ostream* fout; - cmProp noSourceGroupFile = - this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE"); - bool useProjectFile = (noSourceGroupFile && cmIsOn(*noSourceGroupFile)) || - cmIsOn(this->Makefile->GetDefinition("CMAKE_GHS_NO_SOURCE_GROUP_FILE")); + bool useProjectFile = + cmIsOn(this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE")) || + this->Makefile->IsOn("CMAKE_GHS_NO_SOURCE_GROUP_FILE"); if (useProjectFile || sg.empty()) { fout = &fout_proj; } else { diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h index f03ca44a20..e9d7537200 100644 --- a/Source/cmGhsMultiTargetGenerator.h +++ b/Source/cmGhsMultiTargetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGhsMultiTargetGenerator_h -#define cmGhsMultiTargetGenerator_h +#pragma once #include <iosfwd> #include <map> @@ -43,7 +42,7 @@ private: void SetCompilerFlags(std::string const& config, const std::string& language); - std::string GetDefines(const std::string& langugae, + std::string GetDefines(const std::string& language, std::string const& config); void WriteIncludes(std::ostream& fout, const std::string& config, @@ -82,5 +81,3 @@ private: 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.h b/Source/cmGlobVerificationManager.h index 2e7e1ca70c..b618fb0707 100644 --- a/Source/cmGlobVerificationManager.h +++ b/Source/cmGlobVerificationManager.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobVerificationManager_h -#define cmGlobVerificationManager_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -81,5 +80,3 @@ private: // cmGlobVerificationManager should never be used directly. friend class cmState; // allow access to add cache values }; - -#endif diff --git a/Source/cmGlobalBorlandMakefileGenerator.h b/Source/cmGlobalBorlandMakefileGenerator.h index 3c979550c9..5a4e8c2399 100644 --- a/Source/cmGlobalBorlandMakefileGenerator.h +++ b/Source/cmGlobalBorlandMakefileGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalBorlandMakefileGenerator_h -#define cmGlobalBorlandMakefileGenerator_h +#pragma once #include <iosfwd> #include <memory> @@ -58,5 +57,3 @@ protected: void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override; }; - -#endif diff --git a/Source/cmGlobalCommonGenerator.cxx b/Source/cmGlobalCommonGenerator.cxx index 9dc86f4e42..9e5bbca003 100644 --- a/Source/cmGlobalCommonGenerator.cxx +++ b/Source/cmGlobalCommonGenerator.cxx @@ -5,8 +5,12 @@ #include <memory> #include <utility> +#include <cmext/algorithm> + +#include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmLocalGenerator.h" +#include "cmMakefile.h" #include "cmProperty.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" @@ -31,26 +35,30 @@ cmGlobalCommonGenerator::ComputeDirectoryTargets() const lg->GetStateSnapshot().GetDirectory().GetCurrentBinary()); DirectoryTarget& dirTarget = dirTargets[currentBinaryDir]; dirTarget.LG = lg.get(); + const std::vector<std::string>& configs = + lg->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); // The directory-level rule should depend on the target-level rules // for all targets in the directory. for (const auto& gt : lg->GetGeneratorTargets()) { cmStateEnums::TargetType const type = gt->GetType(); - if (type != cmStateEnums::EXECUTABLE && - type != cmStateEnums::STATIC_LIBRARY && - type != cmStateEnums::SHARED_LIBRARY && - type != cmStateEnums::MODULE_LIBRARY && - type != cmStateEnums::OBJECT_LIBRARY && - type != cmStateEnums::UTILITY) { + if (type == cmStateEnums::GLOBAL_TARGET || !gt->IsInBuildSystem()) { continue; } DirectoryTarget::Target t; t.GT = gt.get(); - if (cmProp exclude = gt->GetProperty("EXCLUDE_FROM_ALL")) { - if (cmIsOn(*exclude)) { - // This target has been explicitly excluded. - t.ExcludeFromAll = true; - } else { + const std::string EXCLUDE_FROM_ALL("EXCLUDE_FROM_ALL"); + if (cmProp exclude = gt->GetProperty(EXCLUDE_FROM_ALL)) { + for (const std::string& config : configs) { + cmGeneratorExpressionInterpreter genexInterpreter(lg.get(), config, + gt.get()); + if (cmIsOn(genexInterpreter.Evaluate(*exclude, EXCLUDE_FROM_ALL))) { + // This target has been explicitly excluded. + t.ExcludedFromAllInConfigs.push_back(config); + } + } + + if (t.ExcludedFromAllInConfigs.empty()) { // This target has been explicitly un-excluded. The directory-level // rule for every directory between this and the root should depend // on the target-level rule for this target. @@ -78,3 +86,12 @@ cmGlobalCommonGenerator::ComputeDirectoryTargets() const return dirTargets; } + +bool cmGlobalCommonGenerator::IsExcludedFromAllInConfig( + const DirectoryTarget::Target& t, const std::string& config) +{ + if (this->IsMultiConfig()) { + return cm::contains(t.ExcludedFromAllInConfigs, config); + } + return !t.ExcludedFromAllInConfigs.empty(); +} diff --git a/Source/cmGlobalCommonGenerator.h b/Source/cmGlobalCommonGenerator.h index 7d16daca2a..2aa9d27829 100644 --- a/Source/cmGlobalCommonGenerator.h +++ b/Source/cmGlobalCommonGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalCommonGenerator_h -#define cmGlobalCommonGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -30,7 +29,7 @@ public: struct Target { cmGeneratorTarget const* GT = nullptr; - bool ExcludeFromAll = false; + std::vector<std::string> ExcludedFromAllInConfigs; }; std::vector<Target> Targets; struct Dir @@ -41,6 +40,6 @@ public: std::vector<Dir> Children; }; std::map<std::string, DirectoryTarget> ComputeDirectoryTargets() const; + bool IsExcludedFromAllInConfig(const DirectoryTarget::Target& t, + const std::string& config); }; - -#endif diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index bf8f6cb959..fc40d63b3f 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -15,6 +15,7 @@ #include <cm/memory> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmsys/Directory.hxx" #include "cmsys/FStream.hxx" @@ -197,12 +198,12 @@ std::string cmGlobalGenerator::SelectMakeProgram( { std::string makeProgram = inMakeProgram; if (cmIsOff(makeProgram)) { - const char* makeProgramCSTR = + cmProp makeProgramCSTR = this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM"); if (cmIsOff(makeProgramCSTR)) { makeProgram = makeDefault; } else { - makeProgram = makeProgramCSTR; + makeProgram = *makeProgramCSTR; } if (cmIsOff(makeProgram) && !makeProgram.empty()) { makeProgram = "CMAKE_MAKE_PROGRAM-NOTFOUND"; @@ -235,6 +236,14 @@ void cmGlobalGenerator::ResolveLanguageCompiler(const std::string& lang, } cmProp cname = this->GetCMakeInstance()->GetState()->GetInitializedCacheValue(langComp); + + // Split compiler from arguments + std::vector<std::string> cnameArgVec; + if (cname && !cname->empty()) { + cmExpandList(*cname, cnameArgVec); + cname = &cnameArgVec.front(); + } + std::string changeVars; if (cname && !optional) { std::string cnameString; @@ -302,31 +311,12 @@ bool cmGlobalGenerator::CheckTargetsForMissingSources() const bool failed = false; for (const auto& localGen : this->LocalGenerators) { for (const auto& target : localGen->GetGeneratorTargets()) { - if (target->GetType() == cmStateEnums::TargetType::GLOBAL_TARGET || - target->GetType() == cmStateEnums::TargetType::INTERFACE_LIBRARY || - target->GetType() == cmStateEnums::TargetType::UTILITY) { + if (!target->CanCompileSources() || + cmIsOn(target->GetProperty("ghs_integrity_app"))) { continue; } - if (cmProp p = target->GetProperty("ghs_integrity_app")) { - if (cmIsOn(*p)) { - continue; - } - } - std::vector<std::string> configs; - target->Makefile->GetConfigurations(configs); - std::vector<cmSourceFile*> srcs; - if (configs.empty()) { - target->GetSourceFiles(srcs, ""); - } else { - for (std::string const& config : configs) { - target->GetSourceFiles(srcs, config); - if (!srcs.empty()) { - break; - } - } - } - if (srcs.empty()) { + if (target->GetAllConfigSources().empty()) { std::ostringstream e; e << "No SOURCES given to target: " << target->GetName(); this->GetCMakeInstance()->IssueMessage( @@ -346,12 +336,13 @@ bool cmGlobalGenerator::CheckTargetsForType() const bool failed = false; for (const auto& generator : this->LocalGenerators) { for (const auto& target : generator->GetGeneratorTargets()) { - if (target->GetType() == cmStateEnums::EXECUTABLE && - target->GetPropertyAsBool("WIN32_EXECUTABLE")) { + if (target->GetType() == cmStateEnums::EXECUTABLE) { std::vector<std::string> const& configs = - target->Makefile->GetGeneratorConfigs(); + target->Makefile->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig); for (std::string const& config : configs) { - if (target->GetLinkerLanguage(config) == "Swift") { + if (target->IsWin32Executable(config) && + target->GetLinkerLanguage(config) == "Swift") { this->GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR, "WIN32_EXECUTABLE property is not supported on Swift " @@ -374,16 +365,10 @@ bool cmGlobalGenerator::CheckTargetsForPchCompilePdb() const bool failed = false; for (const auto& generator : this->LocalGenerators) { for (const auto& target : generator->GetGeneratorTargets()) { - if (target->GetType() == cmStateEnums::TargetType::GLOBAL_TARGET || - target->GetType() == cmStateEnums::TargetType::INTERFACE_LIBRARY || - target->GetType() == cmStateEnums::TargetType::UTILITY) { + if (!target->CanCompileSources() || + cmIsOn(target->GetProperty("ghs_integrity_app"))) { continue; } - if (cmProp p = target->GetProperty("ghs_integrity_app")) { - if (cmIsOn(*p)) { - continue; - } - } std::string const& reuseFrom = target->GetSafeProperty("PRECOMPILE_HEADERS_REUSE_FROM"); @@ -425,15 +410,13 @@ bool cmGlobalGenerator::FindMakeProgram(cmMakefile* mf) "all generators must specify this->FindMakeProgramFile"); return false; } - if (!mf->GetDefinition("CMAKE_MAKE_PROGRAM") || - cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) { + if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) { std::string setMakeProgram = mf->GetModulesFile(this->FindMakeProgramFile); if (!setMakeProgram.empty()) { mf->ReadListFile(setMakeProgram); } } - if (!mf->GetDefinition("CMAKE_MAKE_PROGRAM") || - cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) { + if (cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) { std::ostringstream err; err << "CMake was unable to find a build program corresponding to \"" << this->GetName() << "\". CMAKE_MAKE_PROGRAM is not set. You " @@ -596,6 +579,17 @@ void cmGlobalGenerator::EnableLanguage( mf->ReadListFile(fpath); } } + + if (readCMakeSystem) { + // Find the native build tool for this generator. + // This has to be done early so that MSBuild can be used to examine the + // cross-compilation environment. + if (this->GetFindMakeProgramStage() == FindMakeProgramStage::Early && + !this->FindMakeProgram(mf)) { + return; + } + } + // Load the CMakeDetermineSystem.cmake file and find out // what platform we are running on if (!mf->GetDefinition("CMAKE_SYSTEM")) { @@ -669,7 +663,8 @@ void cmGlobalGenerator::EnableLanguage( } // Find the native build tool for this generator. - if (!this->FindMakeProgram(mf)) { + if (this->GetFindMakeProgramStage() == FindMakeProgramStage::Late && + !this->FindMakeProgram(mf)) { return; } } @@ -793,27 +788,27 @@ void cmGlobalGenerator::EnableLanguage( std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER"); std::string compilerEnv = cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR"); std::ostringstream noCompiler; - const char* compilerFile = mf->GetDefinition(compilerName); - if (!compilerFile || !*compilerFile || cmIsNOTFOUND(compilerFile)) { + cmProp compilerFile = mf->GetDefinition(compilerName); + if (!cmNonempty(compilerFile) || cmIsNOTFOUND(*compilerFile)) { /* clang-format off */ noCompiler << "No " << compilerName << " could be found.\n" ; /* clang-format on */ } else if ((lang != "RC") && (lang != "ASM_MASM")) { - if (!cmSystemTools::FileIsFullPath(compilerFile)) { + if (!cmSystemTools::FileIsFullPath(*compilerFile)) { /* clang-format off */ noCompiler << "The " << compilerName << ":\n" - " " << compilerFile << "\n" + " " << *compilerFile << "\n" "is not a full path and was not found in the PATH.\n" ; /* clang-format on */ - } else if (!cmSystemTools::FileExists(compilerFile)) { + } else if (!cmSystemTools::FileExists(*compilerFile)) { /* clang-format off */ noCompiler << "The " << compilerName << ":\n" - " " << compilerFile << "\n" + " " << *compilerFile << "\n" "is not a full path to an existing compiler tool.\n" ; /* clang-format on */ @@ -830,7 +825,7 @@ void cmGlobalGenerator::EnableLanguage( cmSystemTools::RemoveFile(compilerLangFile); if (!this->CMakeInstance->GetIsInTryCompile()) { this->PrintCompilerAdvice(noCompiler, lang, - mf->GetDefinition(compilerEnv)); + cmToCStr(mf->GetDefinition(compilerEnv))); mf->IssueMessage(MessageType::FATAL_ERROR, noCompiler.str()); fatalError = true; } @@ -1108,17 +1103,16 @@ void cmGlobalGenerator::SetLanguageEnabledMaps(const std::string& l, return; } - std::string linkerPrefVar = - std::string("CMAKE_") + std::string(l) + std::string("_LINKER_PREFERENCE"); - const char* linkerPref = mf->GetDefinition(linkerPrefVar); + std::string linkerPrefVar = "CMAKE_" + l + "_LINKER_PREFERENCE"; + cmProp linkerPref = mf->GetDefinition(linkerPrefVar); int preference = 0; - if (linkerPref) { - if (sscanf(linkerPref, "%d", &preference) != 1) { + if (cmNonempty(linkerPref)) { + if (sscanf(linkerPref->c_str(), "%d", &preference) != 1) { // backward compatibility: before 2.6 LINKER_PREFERENCE // was either "None" or "Preferred", and only the first character was // tested. So if there is a custom language out there and it is // "Preferred", set its preference high - if (linkerPref[0] == 'P') { + if ((*linkerPref)[0] == 'P') { preference = 100; } else { preference = 0; @@ -1135,14 +1129,14 @@ void cmGlobalGenerator::SetLanguageEnabledMaps(const std::string& l, this->LanguageToLinkerPreference[l] = preference; - std::string outputExtensionVar = - std::string("CMAKE_") + std::string(l) + std::string("_OUTPUT_EXTENSION"); - const char* outputExtension = mf->GetDefinition(outputExtensionVar); - if (outputExtension) { + std::string outputExtensionVar = "CMAKE_" + l + "_OUTPUT_EXTENSION"; + if (cmProp p = mf->GetDefinition(outputExtensionVar)) { + std::string outputExtension = *p; this->LanguageToOutputExtension[l] = outputExtension; this->OutputExtensions[outputExtension] = outputExtension; - if (outputExtension[0] == '.') { - this->OutputExtensions[outputExtension + 1] = outputExtension + 1; + if (cmHasPrefix(outputExtension, ".")) { + outputExtension = outputExtension.substr(1); + this->OutputExtensions[outputExtension] = outputExtension; } } @@ -1175,7 +1169,7 @@ void cmGlobalGenerator::FillExtensionToLanguageMap(const std::string& l, const char* cmGlobalGenerator::GetGlobalSetting(std::string const& name) const { assert(!this->Makefiles.empty()); - return this->Makefiles[0]->GetDefinition(name); + return cmToCStr(this->Makefiles[0]->GetDefinition(name)); } bool cmGlobalGenerator::GlobalSettingIsOn(std::string const& name) const @@ -1225,6 +1219,7 @@ void cmGlobalGenerator::Configure() { this->FirstTimeProgress = 0.0f; this->ClearGeneratorMembers(); + this->NextDeferId = 0; cmStateSnapshot snapshot = this->CMakeInstance->GetCurrentSnapshot(); @@ -1442,12 +1437,10 @@ bool cmGlobalGenerator::Compute() localGen->AddHelperCommands(); } - // Finalize the set of compile features for each target. - // FIXME: This turns into calls to cmMakefile::AddRequiredTargetFeature - // which actually modifies the <lang>_STANDARD target property - // on the original cmTarget instance. It accumulates features - // across all configurations. Some refactoring is needed to - // compute a per-config resulta purely during generation. + // Perform up-front computation in order to handle errors (such as unknown + // features) at this point. While processing the compile features we also + // calculate and cache the language standard required by the compile + // features. for (const auto& localGen : this->LocalGenerators) { if (!localGen->ComputeTargetCompileFeatures()) { return false; @@ -1487,6 +1480,7 @@ bool cmGlobalGenerator::Compute() if (!this->ComputeTargetDepends()) { return false; } + this->ComputeTargetOrder(); if (this->CheckTargetsForType()) { return false; @@ -1597,6 +1591,50 @@ bool cmGlobalGenerator::ComputeTargetDepends() return true; } +std::vector<cmGeneratorTarget*> +cmGlobalGenerator::GetLocalGeneratorTargetsInOrder(cmLocalGenerator* lg) const +{ + std::vector<cmGeneratorTarget*> gts; + cm::append(gts, lg->GetGeneratorTargets()); + std::sort(gts.begin(), gts.end(), + [this](cmGeneratorTarget const* l, cmGeneratorTarget const* r) { + return this->TargetOrderIndex.at(l) < + this->TargetOrderIndex.at(r); + }); + return gts; +} + +void cmGlobalGenerator::ComputeTargetOrder() +{ + size_t index = 0; + auto const& lgens = this->GetLocalGenerators(); + for (auto const& lgen : lgens) { + const auto& targets = lgen->GetGeneratorTargets(); + for (const auto& gt : targets) { + this->ComputeTargetOrder(gt.get(), index); + } + } + assert(index == this->TargetOrderIndex.size()); +} + +void cmGlobalGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt, + size_t& index) +{ + std::map<cmGeneratorTarget const*, size_t>::value_type value(gt, 0); + auto insertion = this->TargetOrderIndex.insert(value); + if (!insertion.second) { + return; + } + auto entry = insertion.first; + + auto& deps = this->GetTargetDirectDepends(gt); + for (auto& d : deps) { + this->ComputeTargetOrder(d, index); + } + + entry->second = index++; +} + bool cmGlobalGenerator::QtAutoGen() { #ifndef CMAKE_BOOTSTRAP @@ -1614,12 +1652,11 @@ bool cmGlobalGenerator::AddAutomaticSources() } for (const auto& lg : this->LocalGenerators) { for (const auto& gt : lg->GetGeneratorTargets()) { - if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY || - gt->GetType() == cmStateEnums::UTILITY || - gt->GetType() == cmStateEnums::GLOBAL_TARGET) { + if (!gt->CanCompileSources()) { continue; } lg->AddUnityBuild(gt.get()); + lg->AddISPCDependencies(gt.get()); // Targets that re-use a PCH are handled below. if (!gt->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) { lg->AddPchDependencies(gt.get()); @@ -1628,9 +1665,7 @@ bool cmGlobalGenerator::AddAutomaticSources() } for (const auto& lg : this->LocalGenerators) { for (const auto& gt : lg->GetGeneratorTargets()) { - if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY || - gt->GetType() == cmStateEnums::UTILITY || - gt->GetType() == cmStateEnums::GLOBAL_TARGET) { + if (!gt->CanCompileSources()) { continue; } // Handle targets that re-use a PCH from an above-handled target. @@ -1700,8 +1735,8 @@ void cmGlobalGenerator::FinalizeTargetCompileInfo() cmPolicies::PolicyStatus polSt = mf->GetPolicyStatus(cmPolicies::CMP0043); if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { - std::vector<std::string> configs; - mf->GetConfigurations(configs); + std::vector<std::string> configs = + mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); for (std::string const& c : configs) { std::string defPropName = @@ -1781,6 +1816,7 @@ void cmGlobalGenerator::ClearGeneratorMembers() this->GeneratorTargetSearchIndex.clear(); this->MakefileSearchIndex.clear(); this->LocalGeneratorSearchIndex.clear(); + this->TargetOrderIndex.clear(); this->ProjectMap.clear(); this->RuleHashes.clear(); this->DirectoryContentMap.clear(); @@ -1990,8 +2026,9 @@ int cmGlobalGenerator::Build( std::string makeCommandStr; output += "\nRun Build Command(s):"; - for (auto command = makeCommand.begin(); command != makeCommand.end(); - ++command) { + retVal = 0; + for (auto command = makeCommand.begin(); + command != makeCommand.end() && retVal == 0; ++command) { makeCommandStr = command->Printable(); if (command != makeCommand.end()) { makeCommandStr += " && "; @@ -2125,10 +2162,11 @@ void cmGlobalGenerator::EnableLanguagesFromGenerator(cmGlobalGenerator* gen, { this->SetConfiguredFilesPath(gen); this->TryCompileOuterMakefile = mf; - const char* make = + cmProp make = gen->GetCMakeInstance()->GetCacheDefinition("CMAKE_MAKE_PROGRAM"); - this->GetCMakeInstance()->AddCacheEntry( - "CMAKE_MAKE_PROGRAM", make, "make program", cmStateEnums::FILEPATH); + this->GetCMakeInstance()->AddCacheEntry("CMAKE_MAKE_PROGRAM", cmToCStr(make), + "make program", + cmStateEnums::FILEPATH); // copy the enabled languages this->GetCMakeInstance()->GetState()->SetEnabledLanguages( gen->GetCMakeInstance()->GetState()->GetEnabledLanguages()); @@ -2181,13 +2219,38 @@ bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root, } bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root, - cmGeneratorTarget* target) const + const cmGeneratorTarget* target) const { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!target->IsInBuildSystem()) { return true; } - if (cmProp exclude = target->GetProperty("EXCLUDE_FROM_ALL")) { - return cmIsOn(*exclude); + cmMakefile* mf = root->GetMakefile(); + const std::string EXCLUDE_FROM_ALL = "EXCLUDE_FROM_ALL"; + if (cmProp exclude = target->GetProperty(EXCLUDE_FROM_ALL)) { + // Expand the property value per configuration. + unsigned int trueCount = 0; + unsigned int falseCount = 0; + const std::vector<std::string>& configs = + mf->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (const std::string& config : configs) { + cmGeneratorExpressionInterpreter genexInterpreter(root, config, target); + if (cmIsOn(genexInterpreter.Evaluate(*exclude, EXCLUDE_FROM_ALL))) { + ++trueCount; + } else { + ++falseCount; + } + } + + // Check whether the genex expansion of the property agrees in all + // configurations. + if (trueCount && falseCount) { + std::ostringstream e; + e << "The EXCLUDE_FROM_ALL property of target \"" << target->GetName() + << "\" varies by configuration. This is not supported by the \"" + << root->GetGlobalGenerator()->GetName() << "\" generator."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } + return trueCount; } // This target is included in its directory. Check whether the // directory is excluded. @@ -2300,13 +2363,13 @@ std::string cmGlobalGenerator::IndexGeneratorTargetUniquely( void cmGlobalGenerator::IndexMakefile(cmMakefile* mf) { - // FIXME: add_subdirectory supports multiple build directories - // sharing the same source directory. We currently index only the - // first one, because that is what FindMakefile has always returned. - // All of its callers will need to be modified to support looking - // up directories by build directory path. + // We index by both source and binary directory. add_subdirectory + // supports multiple build directories sharing the same source directory. + // The source directory index will reference only the first time it is used. this->MakefileSearchIndex.insert( MakefileMap::value_type(mf->GetCurrentSourceDirectory(), mf)); + this->MakefileSearchIndex.insert( + MakefileMap::value_type(mf->GetCurrentBinaryDirectory(), mf)); } void cmGlobalGenerator::IndexLocalGenerator(cmLocalGenerator* lg) @@ -2447,7 +2510,7 @@ void cmGlobalGenerator::AddGlobalTarget_Package( gti.WorkingDir = mf->GetCurrentBinaryDirectory(); cmCustomCommandLine singleLine; singleLine.push_back(cmSystemTools::GetCPackCommand()); - if (cmakeCfgIntDir && *cmakeCfgIntDir && cmakeCfgIntDir[0] != '.') { + if (cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.') { singleLine.push_back("-C"); singleLine.push_back(cmakeCfgIntDir); } @@ -2457,9 +2520,9 @@ void cmGlobalGenerator::AddGlobalTarget_Package( if (this->GetPreinstallTargetName()) { gti.Depends.emplace_back(this->GetPreinstallTargetName()); } else { - const char* noPackageAll = + cmProp noPackageAll = mf->GetDefinition("CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY"); - if (!noPackageAll || cmIsOff(noPackageAll)) { + if (cmIsOff(noPackageAll)) { gti.Depends.emplace_back(this->GetAllTargetName()); } } @@ -2532,7 +2595,7 @@ void cmGlobalGenerator::AddGlobalTarget_Test( singleLine.push_back(arg); } } - if (cmakeCfgIntDir && *cmakeCfgIntDir && cmakeCfgIntDir[0] != '.') { + if (cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.') { singleLine.push_back("-C"); singleLine.push_back(cmakeCfgIntDir); } else // TODO: This is a hack. Should be something to do with the @@ -2613,7 +2676,7 @@ void cmGlobalGenerator::AddGlobalTarget_Install( "installation rules have been specified", mf->GetBacktrace()); } else if (this->InstallTargetEnabled && !skipInstallRules) { - if (!cmakeCfgIntDir || !*cmakeCfgIntDir || cmakeCfgIntDir[0] == '.') { + if (!(cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.')) { std::set<std::string>* componentsSet = &this->InstallComponents; std::ostringstream ostr; if (!componentsSet->empty()) { @@ -2638,9 +2701,8 @@ void cmGlobalGenerator::AddGlobalTarget_Install( if (this->GetPreinstallTargetName()) { gti.Depends.emplace_back(this->GetPreinstallTargetName()); } else { - const char* noall = - mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY"); - if (!noall || cmIsOff(noall)) { + cmProp noall = mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY"); + if (cmIsOff(noall)) { gti.Depends.emplace_back(this->GetAllTargetName()); } } @@ -2652,7 +2714,7 @@ void cmGlobalGenerator::AddGlobalTarget_Install( cmd = "cmake"; } singleLine.push_back(cmd); - if (cmakeCfgIntDir && *cmakeCfgIntDir && cmakeCfgIntDir[0] != '.') { + if (cmNonempty(cmakeCfgIntDir) && cmakeCfgIntDir[0] != '.') { std::string cfgArg = "-DBUILD_TYPE="; bool useEPN = this->UseEffectivePlatformName(mf.get()); if (useEPN) { @@ -2660,7 +2722,7 @@ void cmGlobalGenerator::AddGlobalTarget_Install( singleLine.push_back(cfgArg); cfgArg = "-DEFFECTIVE_PLATFORM_NAME=$(EFFECTIVE_PLATFORM_NAME)"; } else { - cfgArg += mf->GetDefinition("CMAKE_CFG_INTDIR"); + cfgArg += cmToCStr(mf->GetDefinition("CMAKE_CFG_INTDIR")); } singleLine.push_back(cfgArg); } @@ -3047,7 +3109,7 @@ void cmGlobalGenerator::WriteSummary() for (const auto& lg : this->LocalGenerators) { for (const auto& tgt : lg->GetGeneratorTargets()) { - if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!tgt->IsInBuildSystem()) { continue; } this->WriteSummary(tgt.get()); @@ -3068,7 +3130,7 @@ void cmGlobalGenerator::WriteSummary(cmGeneratorTarget* target) cmProp targetLabels = target->GetProperty("LABELS"); cmProp directoryLabels = target->Target->GetMakefile()->GetProperty("LABELS"); - const char* cmakeDirectoryLabels = + cmProp cmakeDirectoryLabels = target->Target->GetMakefile()->GetDefinition("CMAKE_DIRECTORY_LABELS"); if (targetLabels || directoryLabels || cmakeDirectoryLabels) { Json::Value lj_root(Json::objectValue); @@ -3104,7 +3166,7 @@ void cmGlobalGenerator::WriteSummary(cmGeneratorTarget* target) } if (cmakeDirectoryLabels) { - cmExpandList(cmakeDirectoryLabels, cmakeDirectoryLabelsList); + cmExpandList(*cmakeDirectoryLabels, cmakeDirectoryLabelsList); } if (!directoryLabelsList.empty() || !cmakeDirectoryLabelsList.empty()) { @@ -3125,7 +3187,8 @@ void cmGlobalGenerator::WriteSummary(cmGeneratorTarget* target) fout << "# Source files and their labels\n"; std::vector<cmSourceFile*> sources; std::vector<std::string> const& configs = - target->Target->GetMakefile()->GetGeneratorConfigs(); + target->Target->GetMakefile()->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig); for (std::string const& c : configs) { target->GetSourceFiles(sources, c); } @@ -3202,6 +3265,11 @@ const std::string& cmGlobalGenerator::GetRealPath(const std::string& dir) return i->second; } +std::string cmGlobalGenerator::NewDeferId() +{ + return cmStrCat("__"_s, std::to_string(this->NextDeferId++)); +} + void cmGlobalGenerator::ProcessEvaluationFiles() { std::vector<std::string> generatedFiles; @@ -3224,8 +3292,9 @@ bool cmGlobalGenerator::GenerateCPackPropertiesFile() const auto& lg = this->LocalGenerators[0]; cmMakefile* mf = lg->GetMakefile(); - std::vector<std::string> configs; - std::string config = mf->GetConfigurations(configs, false); + std::vector<std::string> configs = + mf->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig); + std::string config = mf->GetDefaultConfiguration(); std::string path = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), "/CPackProperties.cmake"); diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 57c780807d..c106258adb 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -1,10 +1,10 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalGenerator_h -#define cmGlobalGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep +#include <cstddef> #include <iosfwd> #include <map> #include <memory> @@ -265,6 +265,9 @@ public: return this->LocalGenerators; } + std::vector<cmGeneratorTarget*> GetLocalGeneratorTargetsInOrder( + cmLocalGenerator* lg) const; + cmMakefile* GetCurrentMakefile() const { return this->CurrentConfigureMakefile; @@ -505,6 +508,8 @@ public: std::string const& GetRealPath(std::string const& dir); + std::string NewDeferId(); + protected: // for a project collect all its targets by following depend // information, and also collect all the targets @@ -542,7 +547,8 @@ protected: bool IsExcluded(cmStateSnapshot const& root, cmStateSnapshot const& snp) const; bool IsExcluded(cmLocalGenerator* root, cmLocalGenerator* gen) const; - bool IsExcluded(cmLocalGenerator* root, cmGeneratorTarget* target) const; + bool IsExcluded(cmLocalGenerator* root, + const cmGeneratorTarget* target) const; virtual void InitializeProgressMarks() {} struct GlobalTargetInfo @@ -591,6 +597,17 @@ protected: std::string GetPredefinedTargetsFolder(); + enum class FindMakeProgramStage + { + Early, + Late, + }; + + virtual FindMakeProgramStage GetFindMakeProgramStage() const + { + return FindMakeProgramStage::Late; + } + private: using TargetMap = std::unordered_map<std::string, cmTarget*>; using GeneratorTargetMap = @@ -613,6 +630,10 @@ private: // Its order is not deterministic. LocalGeneratorMap LocalGeneratorSearchIndex; + void ComputeTargetOrder(); + void ComputeTargetOrder(cmGeneratorTarget const* gt, size_t& index); + std::map<cmGeneratorTarget const*, size_t> TargetOrderIndex; + cmMakefile* TryCompileOuterMakefile; // If you add a new map here, make sure it is copied // in EnableLanguagesFromGenerator @@ -625,6 +646,9 @@ private: std::map<std::string, int> LanguageToLinkerPreference; std::map<std::string, std::string> LanguageToOriginalSharedLibFlags; + // Deferral id generation. + size_t NextDeferId = 0; + // Record hashes for rules and outputs. struct RuleHash { @@ -716,5 +740,3 @@ protected: bool InstallTargetEnabled; bool ConfigureDoneCMP0026AndCMP0024; }; - -#endif diff --git a/Source/cmGlobalGeneratorFactory.h b/Source/cmGlobalGeneratorFactory.h index 3709365970..d6ababbfce 100644 --- a/Source/cmGlobalGeneratorFactory.h +++ b/Source/cmGlobalGeneratorFactory.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalGeneratorFactory_h -#define cmGlobalGeneratorFactory_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -26,7 +25,7 @@ public: /** Create a GlobalGenerator */ virtual std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& n, cmake* cm) const = 0; + const std::string& n, bool allowArch, cmake* cm) const = 0; /** Get the documentation entry for this factory */ virtual void GetDocumentation(cmDocumentationEntry& entry) const = 0; @@ -44,7 +43,7 @@ public: /** Get the list of supported platforms name for this generator */ virtual std::vector<std::string> GetKnownPlatforms() const = 0; - /** If the generator suports platforms, get its default. */ + /** If the generator supports platforms, get its default. */ virtual std::string GetDefaultPlatformName() const = 0; }; @@ -54,7 +53,7 @@ class cmGlobalGeneratorSimpleFactory : public cmGlobalGeneratorFactory public: /** Create a GlobalGenerator */ std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, cmake* cm) const override + const std::string& name, bool /*allowArch*/, cmake* cm) const override { if (name != T::GetActualName()) { return std::unique_ptr<cmGlobalGenerator>(); @@ -95,5 +94,3 @@ public: std::string GetDefaultPlatformName() const override { return std::string(); } }; - -#endif diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index d36adfb746..c08c9cf5b0 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -3,7 +3,6 @@ #include "cmGlobalGhsMultiGenerator.h" #include <algorithm> -#include <cstring> #include <map> #include <ostream> #include <utility> @@ -100,13 +99,13 @@ bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts, /* set the build tool to use */ std::string gbuild(tsp + ((tsp.back() == '/') ? "" : "/") + DEFAULT_BUILD_PROGRAM); - const char* prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM"); + cmProp prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM"); /* check if the toolset changed from last generate */ - if (prevTool != nullptr && (gbuild != prevTool)) { + if (prevTool && (gbuild != *prevTool)) { std::string message = cmStrCat("toolset build tool: ", gbuild, - "\nDoes not match the previously used build tool: ", prevTool, + "\nDoes not match the previously used build tool: ", *prevTool, "\nEither remove the CMakeCache.txt file and CMakeFiles " "directory or choose a different binary directory."); cmSystemTools::Error(message); @@ -187,7 +186,8 @@ void cmGlobalGhsMultiGenerator::EnableLanguage( mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files - const char* tgtPlatform = mf->GetDefinition("GHS_TARGET_PLATFORM"); + const char* tgtPlatform = + cmToCStrSafe(mf->GetDefinition("GHS_TARGET_PLATFORM")); if (!tgtPlatform) { cmSystemTools::Message("Green Hills MULTI: GHS_TARGET_PLATFORM not " "specified; defaulting to \"integrity\""); @@ -216,12 +216,13 @@ bool cmGlobalGhsMultiGenerator::FindMakeProgram(cmMakefile* /*mf*/) void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsd, const std::string& ts) { - const char* ghsRoot = mf->GetDefinition("GHS_TOOLSET_ROOT"); + cmProp ghsRoot = mf->GetDefinition("GHS_TOOLSET_ROOT"); - if (!ghsRoot || ghsRoot[0] == '\0') { - ghsRoot = DEFAULT_TOOLSET_ROOT; + if (cmNonempty(ghsRoot)) { + tsd = *ghsRoot; + } else { + tsd = DEFAULT_TOOLSET_ROOT; } - tsd = ghsRoot; if (ts.empty()) { std::vector<std::string> output; @@ -333,23 +334,23 @@ void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout, fout << "# Top Level Project File\n"; // Specify BSP option if supplied by user - const char* bspName = + cmProp bspName = this->GetCMakeInstance()->GetCacheDefinition("GHS_BSP_NAME"); if (!cmIsOff(bspName)) { - fout << " -bsp " << bspName << '\n'; + fout << " -bsp " << *bspName << '\n'; } // Specify OS DIR if supplied by user // -- not all platforms require this entry in the project file if (!cmIsOff(this->OsDir)) { - const char* osDirOption = + cmProp osDirOption = this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR_OPTION"); std::replace(this->OsDir.begin(), this->OsDir.end(), '\\', '/'); fout << " "; if (cmIsOff(osDirOption)) { fout << ""; } else { - fout << osDirOption; + fout << *osDirOption; } fout << "\"" << this->OsDir << "\"\n"; } @@ -467,11 +468,10 @@ void cmGlobalGhsMultiGenerator::WriteAllTarget( this->ProjectTargets.push_back(t); } for (cmGeneratorTarget const* t : sortedProjectTargets) { - if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!t->IsInBuildSystem()) { continue; } - cmProp p = t->GetProperty("EXCLUDE_FROM_ALL"); - if (!(p && cmIsOn(*p))) { + if (!IsExcluded(t->GetLocalGenerator(), t)) { defaultTargets.push_back(t); } } @@ -564,9 +564,9 @@ cmGlobalGhsMultiGenerator::GenerateBuildCommand( { GeneratedMakeCommand makeCommand = {}; std::string gbuild; - if (const char* gbuildCached = + if (cmProp gbuildCached = this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM")) { - gbuild = gbuildCached; + gbuild = *gbuildCached; } makeCommand.Add(this->SelectMakeProgram(makeProgram, gbuild)); @@ -617,11 +617,10 @@ void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout, cmLocalGenerator* root) { fout << "macro PROJ_NAME=" << root->GetProjectName() << '\n'; - char const* ghsGpjMacros = + cmProp ghsGpjMacros = this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS"); - if (nullptr != ghsGpjMacros) { - std::vector<std::string> expandedList = - cmExpandedList(std::string(ghsGpjMacros)); + if (ghsGpjMacros) { + std::vector<std::string> expandedList = cmExpandedList(*ghsGpjMacros); for (std::string const& arg : expandedList) { fout << "macro " << arg << '\n'; } @@ -633,17 +632,17 @@ void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives( { /* set primary target */ std::string tgt; - const char* t = + cmProp t = this->GetCMakeInstance()->GetCacheDefinition("GHS_PRIMARY_TARGET"); - if (t && *t != '\0') { - tgt = t; + if (cmNonempty(t)) { + tgt = *t; this->GetCMakeInstance()->MarkCliAsUsed("GHS_PRIMARY_TARGET"); } else { - const char* a = + cmProp a = this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM"); - const char* p = + cmProp p = this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM"); - tgt = cmStrCat((a ? a : ""), '_', (p ? p : ""), ".tgt"); + tgt = cmStrCat((a ? *a : ""), '_', (p ? *p : ""), ".tgt"); } /* clang-format off */ @@ -654,11 +653,11 @@ void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives( << "/CMakeFiles/custom_target.bod" << '\n'; /* clang-format on */ - char const* const customization = + cmProp const customization = this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION"); - if (nullptr != customization && strlen(customization) > 0) { + if (cmNonempty(customization)) { fout << "customization=" - << cmGlobalGhsMultiGenerator::TrimQuotes(customization) << '\n'; + << cmGlobalGhsMultiGenerator::TrimQuotes(*customization) << '\n'; this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION"); } } diff --git a/Source/cmGlobalGhsMultiGenerator.h b/Source/cmGlobalGhsMultiGenerator.h index 12ca8b6481..7753b311b7 100644 --- a/Source/cmGlobalGhsMultiGenerator.h +++ b/Source/cmGlobalGhsMultiGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGhsMultiGenerator_h -#define cmGhsMultiGenerator_h +#pragma once #include <iosfwd> #include <memory> @@ -158,5 +157,3 @@ public: using TargetDependSet = cmGlobalGenerator::TargetDependSet; OrderedTargetDependSet(TargetDependSet const&, std::string const& first); }; - -#endif diff --git a/Source/cmGlobalJOMMakefileGenerator.h b/Source/cmGlobalJOMMakefileGenerator.h index 9f1ec8bd44..2d58f91195 100644 --- a/Source/cmGlobalJOMMakefileGenerator.h +++ b/Source/cmGlobalJOMMakefileGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalJOMMakefileGenerator_h -#define cmGlobalJOMMakefileGenerator_h +#pragma once #include <iosfwd> #include <memory> @@ -53,5 +52,3 @@ private: void PrintCompilerAdvice(std::ostream& os, std::string const& lang, const char* envVar) const override; }; - -#endif diff --git a/Source/cmGlobalMSYSMakefileGenerator.h b/Source/cmGlobalMSYSMakefileGenerator.h index b2de4ff50c..1a47b4f182 100644 --- a/Source/cmGlobalMSYSMakefileGenerator.h +++ b/Source/cmGlobalMSYSMakefileGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalMSYSMakefileGenerator_h -#define cmGlobalMSYSMakefileGenerator_h +#pragma once #include <memory> @@ -42,5 +41,3 @@ public: private: std::string FindMinGW(std::string const& makeloc); }; - -#endif diff --git a/Source/cmGlobalMinGWMakefileGenerator.h b/Source/cmGlobalMinGWMakefileGenerator.h index a9f92a1081..ffc9ebe033 100644 --- a/Source/cmGlobalMinGWMakefileGenerator.h +++ b/Source/cmGlobalMinGWMakefileGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalMinGWMakefileGenerator_h -#define cmGlobalMinGWMakefileGenerator_h +#pragma once #include <memory> @@ -38,5 +37,3 @@ public: virtual void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*, bool optional); }; - -#endif diff --git a/Source/cmGlobalNMakeMakefileGenerator.h b/Source/cmGlobalNMakeMakefileGenerator.h index fdf6006c2c..abe64ff963 100644 --- a/Source/cmGlobalNMakeMakefileGenerator.h +++ b/Source/cmGlobalNMakeMakefileGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalNMakeMakefileGenerator_h -#define cmGlobalNMakeMakefileGenerator_h +#pragma once #include <iosfwd> #include <memory> @@ -59,5 +58,3 @@ private: void PrintCompilerAdvice(std::ostream& os, std::string const& lang, const char* envVar) const override; }; - -#endif diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 7f01b9c445..02ffaf7d5c 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -5,9 +5,9 @@ #include <algorithm> #include <cctype> #include <cstdio> -#include <iterator> #include <sstream> +#include <cm/iterator> #include <cm/memory> #include <cmext/algorithm> #include <cmext/memory> @@ -32,6 +32,7 @@ #include "cmMessageType.h" #include "cmNinjaLinkLineComputer.h" #include "cmOutputConverter.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmState.h" #include "cmStateDirectory.h" @@ -239,6 +240,12 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os, } } + if (build.Variables.count("dyndep") > 0) { + // The ninja 'cleandead' operation does not account for outputs + // discovered by 'dyndep' bindings. Avoid removing them. + this->DisableCleandead = true; + } + os << buildStr << arguments << assignments << "\n"; } @@ -500,6 +507,7 @@ void cmGlobalNinjaGenerator::Generate() this->InitOutputPathPrefix(); this->TargetAll = this->NinjaOutputPath("all"); this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt"); + this->DisableCleandead = false; this->PolicyCMP0058 = this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus( @@ -518,7 +526,8 @@ void cmGlobalNinjaGenerator::Generate() if (cmSystemTools::GetErrorOccuredFlag()) { this->RulesFileStream->setstate(std::ios::failbit); - for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs()) { + for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig)) { this->GetImplFileStream(config)->setstate(std::ios::failbit); this->GetConfigFileStream(config)->setstate(std::ios::failbit); } @@ -530,10 +539,11 @@ void cmGlobalNinjaGenerator::Generate() this->CloseBuildFileStreams(); #ifdef _WIN32 - // The ninja tools will not be able to update metadata on Windows + // Older ninja tools will not be able to update metadata on Windows // when we are re-generating inside an existing 'ninja' invocation // because the outer tool has the files open for write. - if (!this->GetCMakeInstance()->GetRegenerateDuringBuild()) + if (this->NinjaSupportsMetadataOnRegeneration || + !this->GetCMakeInstance()->GetRegenerateDuringBuild()) #endif { this->CleanMetaData(); @@ -604,8 +614,8 @@ bool cmGlobalNinjaGenerator::FindMakeProgram(cmMakefile* mf) if (!this->cmGlobalGenerator::FindMakeProgram(mf)) { return false; } - if (const char* ninjaCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) { - this->NinjaCommand = ninjaCommand; + if (cmProp ninjaCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) { + this->NinjaCommand = *ninjaCommand; std::vector<std::string> command; command.push_back(this->NinjaCommand); command.emplace_back("--version"); @@ -667,6 +677,12 @@ void cmGlobalNinjaGenerator::CheckNinjaFeatures() this->NinjaSupportsRestatTool = !cmSystemTools::VersionCompare( cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), RequiredNinjaVersionForRestatTool().c_str()); + this->NinjaSupportsMultipleOutputs = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + RequiredNinjaVersionForMultipleOutputs().c_str()); + this->NinjaSupportsMetadataOnRegeneration = !cmSystemTools::VersionCompare( + cmSystemTools::OP_LESS, this->NinjaVersion.c_str(), + RequiredNinjaVersionForMetadataOnRegeneration().c_str()); } bool cmGlobalNinjaGenerator::CheckLanguages( @@ -675,6 +691,9 @@ bool cmGlobalNinjaGenerator::CheckLanguages( if (cm::contains(languages, "Fortran")) { return this->CheckFortran(mf); } + if (cm::contains(languages, "ISPC")) { + return this->CheckISPC(mf); + } if (cm::contains(languages, "Swift")) { const std::string architectures = mf->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES"); @@ -708,6 +727,25 @@ bool cmGlobalNinjaGenerator::CheckFortran(cmMakefile* mf) const return false; } +bool cmGlobalNinjaGenerator::CheckISPC(cmMakefile* mf) const +{ + if (this->NinjaSupportsMultipleOutputs) { + return true; + } + + std::ostringstream e; + /* clang-format off */ + e << + "The Ninja generator does not support ISPC using Ninja version\n" + " " << this->NinjaVersion << "\n" + "due to lack of required features. Ninja 1.10 or higher is required." + ; + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + cmSystemTools::SetFatalErrorOccured(); + return false; +} + void cmGlobalNinjaGenerator::EnableLanguage( std::vector<std::string> const& langs, cmMakefile* mf, bool optional) { @@ -1080,6 +1118,7 @@ void cmGlobalNinjaGenerator::AppendTargetOutputs( } // FALLTHROUGH case cmStateEnums::GLOBAL_TARGET: + case cmStateEnums::INTERFACE_LIBRARY: case cmStateEnums::UTILITY: { std::string path = cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/', @@ -1092,8 +1131,8 @@ void cmGlobalNinjaGenerator::AppendTargetOutputs( break; } - default: - return; + case cmStateEnums::UNKNOWN_LIBRARY: + break; } } @@ -1113,15 +1152,38 @@ void cmGlobalNinjaGenerator::AppendTargetDepends( } } else { cmNinjaDeps outs; + + auto computeISPCOuputs = [](cmGlobalNinjaGenerator* gg, + cmGeneratorTarget const* depTarget, + cmNinjaDeps& outputDeps, + const std::string& targetConfig) { + if (depTarget->CanCompileSources()) { + auto headers = depTarget->GetGeneratedISPCHeaders(targetConfig); + if (!headers.empty()) { + std::transform(headers.begin(), headers.end(), headers.begin(), + gg->MapToNinjaPath()); + outputDeps.insert(outputDeps.end(), headers.begin(), headers.end()); + } + auto objs = depTarget->GetGeneratedISPCObjects(targetConfig); + if (!objs.empty()) { + std::transform(objs.begin(), objs.end(), objs.begin(), + gg->MapToNinjaPath()); + outputDeps.insert(outputDeps.end(), objs.begin(), objs.end()); + } + } + }; + for (cmTargetDepend const& targetDep : this->GetTargetDirectDepends(target)) { - if (targetDep->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!targetDep->IsInBuildSystem()) { continue; } if (targetDep.IsCross()) { this->AppendTargetOutputs(targetDep, outs, fileConfig, depends); + computeISPCOuputs(this, targetDep, outs, fileConfig); } else { this->AppendTargetOutputs(targetDep, outs, config, depends); + computeISPCOuputs(this, targetDep, outs, config); } } std::sort(outs.begin(), outs.end()); @@ -1157,7 +1219,7 @@ void cmGlobalNinjaGenerator::AppendTargetDependsClosure( cmNinjaOuts this_outs; // this will be the new cache entry for (auto const& dep_target : this->GetTargetDirectDepends(target)) { - if (dep_target->GetType() == cmStateEnums::INTERFACE_LIBRARY || + if (!dep_target->IsInBuildSystem() || (target->GetType() != cmStateEnums::UTILITY && dep_target->GetType() != cmStateEnums::UTILITY && this->EnableCrossConfigBuild() && !dep_target.IsCross())) { @@ -1178,7 +1240,7 @@ void cmGlobalNinjaGenerator::AppendTargetDependsClosure( // finally generate the outputs of the target itself, if applicable cmNinjaDeps outs; if (!omit_self) { - this->AppendTargetOutputs(target, outs, config); + this->AppendTargetOutputs(target, outs, config, DependOnTargetArtifact); } outputs.insert(outs.begin(), outs.end()); } @@ -1191,7 +1253,7 @@ void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias, std::string buildAlias = this->BuildAlias(outputPath, config); cmNinjaDeps outputs; if (config != "all") { - this->AppendTargetOutputs(target, outputs, config); + this->AppendTargetOutputs(target, outputs, config, DependOnTargetArtifact); } // Mark the target's outputs as ambiguous to ensure that no other target // uses the output as an alias. @@ -1199,7 +1261,8 @@ void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias, this->TargetAliases[output].GeneratorTarget = nullptr; this->DefaultTargetAliases[output].GeneratorTarget = nullptr; for (const std::string& config2 : - this->Makefiles.front()->GetGeneratorConfigs()) { + this->Makefiles.front()->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig)) { this->Configs[config2].TargetAliases[output].GeneratorTarget = nullptr; } } @@ -1257,11 +1320,12 @@ void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os) if (ta.second.Config == "all") { for (auto const& config : this->CrossConfigs) { this->AppendTargetOutputs(ta.second.GeneratorTarget, - build.ExplicitDeps, config); + build.ExplicitDeps, config, + DependOnTargetArtifact); } } else { this->AppendTargetOutputs(ta.second.GeneratorTarget, build.ExplicitDeps, - ta.second.Config); + ta.second.Config, DependOnTargetArtifact); } this->WriteBuild(this->EnableCrossConfigBuild() && (ta.second.Config == "all" || @@ -1272,7 +1336,8 @@ void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os) } if (this->IsMultiConfig()) { - for (auto const& config : this->Makefiles.front()->GetGeneratorConfigs()) { + for (auto const& config : this->Makefiles.front()->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig)) { for (auto const& ta : this->Configs[config].TargetAliases) { // Don't write ambiguous aliases. if (!ta.second.GeneratorTarget) { @@ -1288,7 +1353,8 @@ void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os) build.Outputs.front() = ta.first; build.ExplicitDeps.clear(); this->AppendTargetOutputs(ta.second.GeneratorTarget, - build.ExplicitDeps, config); + build.ExplicitDeps, config, + DependOnTargetArtifact); this->WriteBuild(*this->GetConfigFileStream(config), build); } } @@ -1310,7 +1376,8 @@ void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os) build.ExplicitDeps.clear(); for (auto const& config : this->DefaultConfigs) { this->AppendTargetOutputs(ta.second.GeneratorTarget, - build.ExplicitDeps, config); + build.ExplicitDeps, config, + DependOnTargetArtifact); } this->WriteBuild(*this->GetDefaultFileStream(), build); } @@ -1331,11 +1398,9 @@ void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os) cmGlobalNinjaGenerator::WriteDivider(os); std::string const& currentBinaryDir = it.first; DirectoryTarget const& dt = it.second; - std::vector<std::string> configs; - dt.LG->GetMakefile()->GetConfigurations(configs, true); - if (configs.empty()) { - configs.emplace_back(); - } + std::vector<std::string> configs = + dt.LG->GetMakefile()->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig); // Setup target cmNinjaDeps configDeps; @@ -1348,8 +1413,9 @@ void cmGlobalNinjaGenerator::WriteFolderTargets(std::ostream& os) build.Outputs.front() = this->BuildAlias(buildDirAllTarget, config); configDeps.emplace_back(build.Outputs.front()); for (DirectoryTarget::Target const& t : dt.Targets) { - if (!t.ExcludeFromAll) { - this->AppendTargetOutputs(t.GT, build.ExplicitDeps, config); + if (!IsExcludedFromAllInConfig(t, config)) { + this->AppendTargetOutputs(t.GT, build.ExplicitDeps, config, + DependOnTargetArtifact); } } for (DirectoryTarget::Dir const& d : dt.Children) { @@ -1530,7 +1596,8 @@ void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os) this->WriteTargetClean(os); this->WriteTargetHelp(os); - for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs()) { + for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig)) { this->WriteTargetDefault(*this->GetConfigFileStream(config)); } @@ -1704,11 +1771,8 @@ bool cmGlobalNinjaGenerator::WriteTargetCleanAdditional(std::ostream& os) std::string cleanScriptRel = "CMakeFiles/clean_additional.cmake"; std::string cleanScriptAbs = cmStrCat(lgr->GetBinaryDirectory(), '/', cleanScriptRel); - std::vector<std::string> configs; - this->Makefiles[0]->GetConfigurations(configs, true); - if (configs.empty()) { - configs.emplace_back(); - } + std::vector<std::string> configs = + this->Makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); // Check if there are additional files to clean bool empty = true; @@ -1802,7 +1866,8 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os) WriteRule(*this->RulesFileStream, rule); } - auto const configs = this->Makefiles.front()->GetGeneratorConfigs(); + auto const configs = this->Makefiles.front()->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig); // Write build { @@ -2468,21 +2533,22 @@ bool cmGlobalNinjaMultiGenerator::OpenBuildFileStreams() return false; } - if (!this->DefaultFileConfig.empty()) { - if (!this->OpenFileStream(this->DefaultFileStream, NINJA_BUILD_FILE)) { - return false; - } - *this->DefaultFileStream - << "# Build using rules for '" << this->DefaultFileConfig << "'.\n\n" - << "include " << GetNinjaImplFilename(this->DefaultFileConfig) << "\n\n"; + if (!this->OpenFileStream(this->DefaultFileStream, NINJA_BUILD_FILE)) { + return false; } + *this->DefaultFileStream << "# Build using rules for '" + << this->DefaultFileConfig << "'.\n\n" + << "include " + << GetNinjaImplFilename(this->DefaultFileConfig) + << "\n\n"; // Write a comment about this file. *this->CommonFileStream << "# This file contains build statements common to all " "configurations.\n\n"; - for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs()) { + for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig)) { // Open impl file. if (!this->OpenFileStream(this->ImplFileStreams[config], GetNinjaImplFilename(config))) { @@ -2522,7 +2588,8 @@ void cmGlobalNinjaMultiGenerator::CloseBuildFileStreams() this->DefaultFileStream.reset(); } // No error if it wasn't open - for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs()) { + for (auto const& config : this->Makefiles[0]->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig)) { if (this->ImplFileStreams[config]) { this->ImplFileStreams[config].reset(); } else { @@ -2564,7 +2631,8 @@ std::string cmGlobalNinjaMultiGenerator::GetNinjaConfigFilename( void cmGlobalNinjaMultiGenerator::AddRebuildManifestOutputs( cmNinjaDeps& outputs) const { - for (auto const& config : this->Makefiles.front()->GetGeneratorConfigs()) { + for (auto const& config : this->Makefiles.front()->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig)) { outputs.push_back(this->NinjaOutputPath(GetNinjaImplFilename(config))); outputs.push_back(this->NinjaOutputPath(GetNinjaConfigFilename(config))); } @@ -2576,11 +2644,9 @@ void cmGlobalNinjaMultiGenerator::AddRebuildManifestOutputs( void cmGlobalNinjaMultiGenerator::GetQtAutoGenConfigs( std::vector<std::string>& configs) const { - auto const oldSize = configs.size(); - this->Makefiles.front()->GetConfigurations(configs); - if (configs.size() == oldSize) { - configs.emplace_back(); - } + auto allConfigs = + this->Makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + configs.insert(configs.end(), cm::cbegin(allConfigs), cm::cend(allConfigs)); } bool cmGlobalNinjaMultiGenerator::InspectConfigTypeVariables() diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 8373de3b99..b6687733e2 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalNinjaGenerator_h -#define cmGlobalNinjaGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -318,14 +317,13 @@ public: virtual std::string OrderDependsTargetForTarget( cmGeneratorTarget const* target, const std::string& config) const; - void AppendTargetOutputs( - cmGeneratorTarget const* target, cmNinjaDeps& outputs, - const std::string& config, - cmNinjaTargetDepends depends = DependOnTargetArtifact); - void AppendTargetDepends( - cmGeneratorTarget const* target, cmNinjaDeps& outputs, - const std::string& config, const std::string& fileConfig, - cmNinjaTargetDepends depends = DependOnTargetArtifact); + void AppendTargetOutputs(cmGeneratorTarget const* target, + cmNinjaDeps& outputs, const std::string& config, + cmNinjaTargetDepends depends); + void AppendTargetDepends(cmGeneratorTarget const* target, + cmNinjaDeps& outputs, const std::string& config, + const std::string& fileConfig, + cmNinjaTargetDepends depends); void AppendTargetDependsClosure(cmGeneratorTarget const* target, cmNinjaDeps& outputs, const std::string& config); @@ -370,6 +368,14 @@ public: { return "1.10"; } + static std::string RequiredNinjaVersionForMultipleOutputs() + { + return "1.10"; + } + static std::string RequiredNinjaVersionForMetadataOnRegeneration() + { + return "1.10.2"; + } bool SupportsConsolePool() const; bool SupportsImplicitOuts() const; bool SupportsManifestRestat() const; @@ -447,6 +453,7 @@ private: bool CheckLanguages(std::vector<std::string> const& languages, cmMakefile* mf) const override; bool CheckFortran(cmMakefile* mf) const; + bool CheckISPC(cmMakefile* mf) const; void CloseCompileCommandsStream(); @@ -532,6 +539,8 @@ private: bool NinjaSupportsDyndeps = false; bool NinjaSupportsRestatTool = false; bool NinjaSupportsUnconditionalRecompactTool = false; + bool NinjaSupportsMultipleOutputs = false; + bool NinjaSupportsMetadataOnRegeneration = false; private: void InitOutputPathPrefix(); @@ -539,6 +548,7 @@ private: std::string OutputPathPrefix; std::string TargetAll; std::string CMakeCacheFile; + bool DisableCleandead = false; struct ByConfig { @@ -662,5 +672,3 @@ private: std::unique_ptr<cmGeneratedFileStream> CommonFileStream; std::unique_ptr<cmGeneratedFileStream> DefaultFileStream; }; - -#endif // ! cmGlobalNinjaGenerator_h diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index c31983ba5c..2c934e1d35 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -104,8 +104,8 @@ std::string cmGlobalUnixMakefileGenerator3::GetEditCacheCommand() const cmStateEnums::INTERNAL); } } - const char* edit_cmd = cm->GetCacheDefinition("CMAKE_EDIT_COMMAND"); - return edit_cmd ? edit_cmd : ""; + cmProp edit_cmd = cm->GetCacheDefinition("CMAKE_EDIT_COMMAND"); + return edit_cmd ? *edit_cmd : std::string(); } void cmGlobalUnixMakefileGenerator3::ComputeTargetObjectDirectory( @@ -143,7 +143,7 @@ void cmGlobalUnixMakefileGenerator3::Generate() total += pmi.second.NumberOfActions; } - // write each target's progress.make this loop is done twice. Bascially the + // write each target's progress.make this loop is done twice. Basically the // Generate pass counts all the actions, the first loop below determines // how many actions have progress updates for each target and writes to // corrrect variable values for everything except the all targets. The @@ -387,12 +387,8 @@ void cmGlobalUnixMakefileGenerator3::WriteMainCMakefileLanguageRules( cm::static_reference_cast<cmLocalUnixMakefileGenerator3>(lGenerator); // for all of out targets for (const auto& tgt : lg.GetGeneratorTargets()) { - if ((tgt->GetType() == cmStateEnums::EXECUTABLE) || - (tgt->GetType() == cmStateEnums::STATIC_LIBRARY) || - (tgt->GetType() == cmStateEnums::SHARED_LIBRARY) || - (tgt->GetType() == cmStateEnums::MODULE_LIBRARY) || - (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) || - (tgt->GetType() == cmStateEnums::UTILITY)) { + if (tgt->IsInBuildSystem() && + tgt->GetType() != cmStateEnums::GLOBAL_TARGET) { std::string tname = cmStrCat(lg.GetRelativeTargetDirectory(tgt.get()), "/DependInfo.cmake"); cmSystemTools::ConvertToUnixSlashes(tname); @@ -416,7 +412,7 @@ void cmGlobalUnixMakefileGenerator3::WriteDirectoryRule2( std::vector<std::string> depends; for (DirectoryTarget::Target const& t : dt.Targets) { // Add this to the list of depends rules in this directory. - if ((!check_all || !t.ExcludeFromAll) && + if ((!check_all || t.ExcludedFromAllInConfigs.empty()) && (!check_relink || t.GT->NeedRelinkBeforeInstall(lg->GetConfigName()))) { // The target may be from a different directory; use its local gen. @@ -635,17 +631,12 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules( for (const auto& gtarget : lg.GetGeneratorTargets()) { // Don't emit the same rule twice (e.g. two targets with the same // simple name) - int type = gtarget->GetType(); std::string name = gtarget->GetName(); if (!name.empty() && emitted.insert(name).second && // Handle user targets here. Global targets are handled in // the local generator on a per-directory basis. - ((type == cmStateEnums::EXECUTABLE) || - (type == cmStateEnums::STATIC_LIBRARY) || - (type == cmStateEnums::SHARED_LIBRARY) || - (type == cmStateEnums::MODULE_LIBRARY) || - (type == cmStateEnums::OBJECT_LIBRARY) || - (type == cmStateEnums::UTILITY))) { + (gtarget->IsInBuildSystem() && + gtarget->GetType() != cmStateEnums::GLOBAL_TARGET)) { // Add a rule to build the target by name. lg.WriteDivider(ruleFileStream); ruleFileStream << "# Target rules for targets named " << name @@ -709,15 +700,10 @@ void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules2( // for each target Generate the rule files for each target. for (const auto& gtarget : lg.GetGeneratorTargets()) { - int type = gtarget->GetType(); std::string name = gtarget->GetName(); if (!name.empty() && - ((type == cmStateEnums::EXECUTABLE) || - (type == cmStateEnums::STATIC_LIBRARY) || - (type == cmStateEnums::SHARED_LIBRARY) || - (type == cmStateEnums::MODULE_LIBRARY) || - (type == cmStateEnums::OBJECT_LIBRARY) || - (type == cmStateEnums::UTILITY))) { + (gtarget->IsInBuildSystem() && + gtarget->GetType() != cmStateEnums::GLOBAL_TARGET)) { std::string makefileName; // Add a rule to build the target by name. localName = lg.GetRelativeTargetDirectory(gtarget.get()); @@ -845,8 +831,7 @@ void cmGlobalUnixMakefileGenerator3::InitializeProgressMarks() for (const auto& gt : lg->GetGeneratorTargets()) { cmLocalGenerator* tlg = gt->GetLocalGenerator(); - if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY || - gt->GetPropertyAsBool("EXCLUDE_FROM_ALL")) { + if (!gt->IsInBuildSystem() || IsExcluded(lg.get(), gt.get())) { continue; } @@ -881,7 +866,7 @@ size_t cmGlobalUnixMakefileGenerator3::CountProgressMarksInTarget( if (emitted.insert(target).second) { count = this->ProgressMap[target].Marks.size(); for (cmTargetDepend const& depend : this->GetTargetDirectDepends(target)) { - if (depend->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!depend->IsInBuildSystem()) { continue; } count += this->CountProgressMarksInTarget(depend, emitted); @@ -938,7 +923,7 @@ void cmGlobalUnixMakefileGenerator3::AppendGlobalTargetDepends( for (cmTargetDepend const& i : this->GetTargetDirectDepends(target)) { // Create the target-level dependency. cmGeneratorTarget const* dep = i; - if (dep->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!dep->IsInBuildSystem()) { continue; } cmLocalUnixMakefileGenerator3* lg3 = @@ -986,7 +971,9 @@ void cmGlobalUnixMakefileGenerator3::WriteHelpRule( (type == cmStateEnums::STATIC_LIBRARY) || (type == cmStateEnums::SHARED_LIBRARY) || (type == cmStateEnums::MODULE_LIBRARY) || - (type == cmStateEnums::OBJECT_LIBRARY)) { + (type == cmStateEnums::OBJECT_LIBRARY) || + (type == cmStateEnums::INTERFACE_LIBRARY && + target->IsInBuildSystem())) { project_targets.insert(target->GetName()); } else if (type == cmStateEnums::GLOBAL_TARGET) { globals_targets.insert(target->GetName()); diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h index 1caa4b7362..77d0827b5e 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.h +++ b/Source/cmGlobalUnixMakefileGenerator3.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalUnixMakefileGenerator3_h -#define cmGlobalUnixMakefileGenerator3_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -259,5 +258,3 @@ private: DirectoryTargetsMap; void InitializeProgressMarks() override; }; - -#endif diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 5dac072c2f..7794df3fa0 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -59,7 +59,7 @@ class cmGlobalVisualStudio10Generator::Factory { public: std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, cmake* cm) const override + const std::string& name, bool allowArch, cmake* cm) const override { std::string genName; const char* p = cmVS10GenName(name, genName); @@ -70,7 +70,7 @@ public: return std::unique_ptr<cmGlobalGenerator>( new cmGlobalVisualStudio10Generator(cm, genName, "")); } - if (*p++ != ' ') { + if (!allowArch || *p++ != ' ') { return std::unique_ptr<cmGlobalGenerator>(); } if (strcmp(p, "Win64") == 0) { @@ -138,9 +138,6 @@ cmGlobalVisualStudio10Generator::cmGlobalVisualStudio10Generator( "ProductDir", vc10Express, cmSystemTools::KeyWOW64_32); this->CudaEnabled = false; - this->SystemIsWindowsCE = false; - this->SystemIsWindowsPhone = false; - this->SystemIsWindowsStore = false; this->MSBuildCommandInitialized = false; { std::string envPlatformToolset; @@ -511,18 +508,16 @@ bool cmGlobalVisualStudio10Generator::InitializeSystem(cmMakefile* mf) mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); return false; } - std::string v = this->GetInstalledNsightTegraVersion(); - if (v.empty()) { - mf->IssueMessage(MessageType::FATAL_ERROR, - "CMAKE_SYSTEM_NAME is 'Android' but " - "'NVIDIA Nsight Tegra Visual Studio Edition' " - "is not installed."); - return false; + if (mf->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM") == "Tegra-Android") { + if (!this->InitializeTegraAndroid(mf)) { + return false; + } + } else { + this->SystemIsAndroid = true; + if (!this->InitializeAndroid(mf)) { + return false; + } } - this->DefaultPlatformName = "Tegra-Android"; - this->DefaultPlatformToolset = "Default"; - this->NsightTegraVersion = v; - mf->AddDefinition("CMAKE_VS_NsightTegra_VERSION", v); } return true; @@ -564,6 +559,31 @@ bool cmGlobalVisualStudio10Generator::InitializeWindowsStore(cmMakefile* mf) return false; } +bool cmGlobalVisualStudio10Generator::InitializeTegraAndroid(cmMakefile* mf) +{ + std::string v = this->GetInstalledNsightTegraVersion(); + if (v.empty()) { + mf->IssueMessage(MessageType::FATAL_ERROR, + "CMAKE_SYSTEM_NAME is 'Android' but " + "'NVIDIA Nsight Tegra Visual Studio Edition' " + "is not installed."); + return false; + } + this->DefaultPlatformName = "Tegra-Android"; + this->DefaultPlatformToolset = "Default"; + this->NsightTegraVersion = v; + mf->AddDefinition("CMAKE_VS_NsightTegra_VERSION", v); + return true; +} + +bool cmGlobalVisualStudio10Generator::InitializeAndroid(cmMakefile* mf) +{ + std::ostringstream e; + e << this->GetName() << " does not support Android."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return false; +} + bool cmGlobalVisualStudio10Generator::SelectWindowsPhoneToolset( std::string& toolset) const { @@ -598,6 +618,28 @@ void cmGlobalVisualStudio10Generator::Generate() { this->LongestSource = LongestSourcePath(); this->cmGlobalVisualStudio8Generator::Generate(); + if (!this->AndroidExecutableWarnings.empty() && + !this->CMakeInstance->GetIsInTryCompile()) { + std::ostringstream e; + /* clang-format off */ + e << + "You are using Visual Studio tools for Android, which does not support " + "standalone executables. However, the following executable targets do " + "not have the ANDROID_GUI property set, and thus will not be built as " + "expected. They will be built as shared libraries with executable " + "filenames:\n" + " "; + /* clang-format on */ + bool first = true; + for (auto const& name : this->AndroidExecutableWarnings) { + if (!first) { + e << ", "; + } + first = false; + e << name; + } + this->CMakeInstance->IssueMessage(MessageType::WARNING, e.str()); + } if (this->LongestSource.Length > 0) { cmLocalGenerator* lg = this->LongestSource.Target->GetLocalGenerator(); std::ostringstream e; @@ -664,8 +706,14 @@ std::string const& cmGlobalVisualStudio10Generator::GetPlatformToolsetString() if (!this->GeneratorToolset.empty()) { return this->GeneratorToolset; } - if (!this->DefaultPlatformToolset.empty()) { - return this->DefaultPlatformToolset; + if (this->SystemIsAndroid) { + if (!this->DefaultAndroidToolset.empty()) { + return this->DefaultAndroidToolset; + } + } else { + if (!this->DefaultPlatformToolset.empty()) { + return this->DefaultPlatformToolset; + } } static std::string const empty; return empty; @@ -879,7 +927,10 @@ bool cmGlobalVisualStudio10Generator::FindVCTargetsPath(cmMakefile* mf) epg.Attribute("Label", "Globals"); cmXMLElement(epg, "ProjectGuid") .Content("{F3FC6D86-508D-3FB1-96D2-995F08B142EC}"); - cmXMLElement(epg, "Keyword").Content("Win32Proj"); + cmXMLElement(epg, "Keyword") + .Content(mf->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android" + ? "Android" + : "Win32Proj"); cmXMLElement(epg, "Platform").Content(this->GetPlatformName()); if (this->GetSystemName() == "WindowsPhone") { cmXMLElement(epg, "ApplicationType").Content("Windows Phone"); @@ -889,15 +940,21 @@ bool cmGlobalVisualStudio10Generator::FindVCTargetsPath(cmMakefile* mf) cmXMLElement(epg, "ApplicationType").Content("Windows Store"); cmXMLElement(epg, "ApplicationTypeRevision") .Content(this->GetApplicationTypeRevision()); + } else if (this->GetSystemName() == "Android") { + cmXMLElement(epg, "ApplicationType").Content("Android"); + cmXMLElement(epg, "ApplicationTypeRevision") + .Content(this->GetApplicationTypeRevision()); } if (!this->WindowsTargetPlatformVersion.empty()) { cmXMLElement(epg, "WindowsTargetPlatformVersion") .Content(this->WindowsTargetPlatformVersion); } - if (this->GetPlatformName() == "ARM64") { - cmXMLElement(epg, "WindowsSDKDesktopARM64Support").Content("true"); - } else if (this->GetPlatformName() == "ARM") { - cmXMLElement(epg, "WindowsSDKDesktopARMSupport").Content("true"); + if (this->GetSystemName() != "Android") { + if (this->GetPlatformName() == "ARM64") { + cmXMLElement(epg, "WindowsSDKDesktopARM64Support").Content("true"); + } else if (this->GetPlatformName() == "ARM") { + cmXMLElement(epg, "WindowsSDKDesktopARMSupport").Content("true"); + } } } cmXMLElement(eprj, "Import") @@ -1209,6 +1266,10 @@ std::string cmGlobalVisualStudio10Generator::GetInstalledNsightTegraVersion() std::string cmGlobalVisualStudio10Generator::GetApplicationTypeRevision() const { + if (this->GetSystemName() == "Android") { + return this->GetAndroidApplicationTypeRevision(); + } + // Return the first two '.'-separated components of the Windows version. std::string::size_type end1 = this->SystemVersion.find('.'); std::string::size_type end2 = diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index b8c18b4ac4..65ea33fc9d 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -1,9 +1,9 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudio10Generator_h -#define cmGlobalVisualStudio10Generator_h +#pragma once #include <memory> +#include <set> #include "cmGlobalVisualStudio8Generator.h" #include "cmVisualStudio10ToolsetOptions.h" @@ -43,6 +43,11 @@ public: void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*, bool optional) override; + void AddAndroidExecutableWarning(const std::string& name) + { + this->AndroidExecutableWarnings.insert(name); + } + bool IsCudaEnabled() const { return this->CudaEnabled; } /** Generating for Nsight Tegra VS plugin? */ @@ -100,6 +105,9 @@ public: /** Return true if building for WindowsStore */ bool TargetsWindowsStore() const { return this->SystemIsWindowsStore; } + /** Return true if building for Android */ + bool TargetsAndroid() const { return this->SystemIsAndroid; } + const char* GetCMakeCFGIntDir() const override { return "$(Configuration)"; } bool Find64BitTools(cmMakefile* mf); @@ -128,6 +136,8 @@ public: /** Return the first two components of CMAKE_SYSTEM_VERSION. */ std::string GetApplicationTypeRevision() const; + virtual const char* GetAndroidApplicationTypeRevision() const { return ""; } + cmIDEFlagTable const* GetClFlagTable() const; cmIDEFlagTable const* GetCSharpFlagTable() const; cmIDEFlagTable const* GetRcFlagTable() const; @@ -148,6 +158,8 @@ protected: virtual bool InitializeWindowsCE(cmMakefile* mf); virtual bool InitializeWindowsPhone(cmMakefile* mf); virtual bool InitializeWindowsStore(cmMakefile* mf); + virtual bool InitializeTegraAndroid(cmMakefile* mf); + virtual bool InitializeAndroid(cmMakefile* mf); virtual bool ProcessGeneratorToolsetField(std::string const& key, std::string const& value); @@ -171,6 +183,7 @@ protected: std::string GeneratorToolsetCudaCustomDir; std::string DefaultPlatformToolset; std::string DefaultPlatformToolsetHostArchitecture; + std::string DefaultAndroidToolset; std::string WindowsTargetPlatformVersion; std::string SystemName; std::string SystemVersion; @@ -185,9 +198,10 @@ protected: std::string DefaultNasmFlagTableName; std::string DefaultRCFlagTableName; bool SupportsUnityBuilds = false; - bool SystemIsWindowsCE; - bool SystemIsWindowsPhone; - bool SystemIsWindowsStore; + bool SystemIsWindowsCE = false; + bool SystemIsWindowsPhone = false; + bool SystemIsWindowsStore = false; + bool SystemIsAndroid = false; private: class Factory; @@ -211,6 +225,7 @@ private: std::string MSBuildCommand; bool MSBuildCommandInitialized; cmVisualStudio10ToolsetOptions ToolsetOptions; + std::set<std::string> AndroidExecutableWarnings; virtual std::string FindMSBuildCommand(); std::string FindDevEnvCommand() override; std::string GetVSMakeProgram() override { return this->GetMSBuildCommand(); } @@ -228,4 +243,3 @@ private: // We do not use the reload macros for VS >= 10. std::string GetUserMacrosDirectory() override { return ""; } }; -#endif diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx index a385375d64..a5ffcf0f88 100644 --- a/Source/cmGlobalVisualStudio11Generator.cxx +++ b/Source/cmGlobalVisualStudio11Generator.cxx @@ -31,7 +31,7 @@ class cmGlobalVisualStudio11Generator::Factory { public: std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, cmake* cm) const override + const std::string& name, bool allowArch, cmake* cm) const override { std::string genName; const char* p = cmVS11GenName(name, genName); @@ -42,7 +42,7 @@ public: return std::unique_ptr<cmGlobalGenerator>( new cmGlobalVisualStudio11Generator(cm, genName, "")); } - if (*p++ != ' ') { + if (!allowArch || *p++ != ' ') { return std::unique_ptr<cmGlobalGenerator>(); } if (strcmp(p, "Win64") == 0) { diff --git a/Source/cmGlobalVisualStudio11Generator.h b/Source/cmGlobalVisualStudio11Generator.h index 5f1ff737e0..6e409cfa9e 100644 --- a/Source/cmGlobalVisualStudio11Generator.h +++ b/Source/cmGlobalVisualStudio11Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudio11Generator_h -#define cmGlobalVisualStudio11Generator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -53,4 +52,3 @@ private: class Factory; friend class Factory; }; -#endif diff --git a/Source/cmGlobalVisualStudio12Generator.cxx b/Source/cmGlobalVisualStudio12Generator.cxx index 5a27994872..8bdf356606 100644 --- a/Source/cmGlobalVisualStudio12Generator.cxx +++ b/Source/cmGlobalVisualStudio12Generator.cxx @@ -29,7 +29,7 @@ class cmGlobalVisualStudio12Generator::Factory { public: std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, cmake* cm) const override + const std::string& name, bool allowArch, cmake* cm) const override { std::string genName; const char* p = cmVS12GenName(name, genName); @@ -40,7 +40,7 @@ public: return std::unique_ptr<cmGlobalGenerator>( new cmGlobalVisualStudio12Generator(cm, genName, "")); } - if (*p++ != ' ') { + if (!allowArch || *p++ != ' ') { return std::unique_ptr<cmGlobalGenerator>(); } if (strcmp(p, "Win64") == 0) { diff --git a/Source/cmGlobalVisualStudio12Generator.h b/Source/cmGlobalVisualStudio12Generator.h index bdd40ff6eb..c220d403a9 100644 --- a/Source/cmGlobalVisualStudio12Generator.h +++ b/Source/cmGlobalVisualStudio12Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudio12Generator_h -#define cmGlobalVisualStudio12Generator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -48,4 +47,3 @@ private: class Factory; friend class Factory; }; -#endif diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx index f549b6aae6..b46f1b9b0f 100644 --- a/Source/cmGlobalVisualStudio14Generator.cxx +++ b/Source/cmGlobalVisualStudio14Generator.cxx @@ -30,7 +30,7 @@ class cmGlobalVisualStudio14Generator::Factory { public: std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, cmake* cm) const override + const std::string& name, bool allowArch, cmake* cm) const override { std::string genName; const char* p = cmVS14GenName(name, genName); @@ -41,7 +41,7 @@ public: return std::unique_ptr<cmGlobalGenerator>( new cmGlobalVisualStudio14Generator(cm, genName, "")); } - if (*p++ != ' ') { + if (!allowArch || *p++ != ' ') { return std::unique_ptr<cmGlobalGenerator>(); } if (strcmp(p, "Win64") == 0) { @@ -109,6 +109,7 @@ cmGlobalVisualStudio14Generator::cmGlobalVisualStudio14Generator( "ProductDir", vc14Express, cmSystemTools::KeyWOW64_32); this->DefaultPlatformToolset = "v140"; + this->DefaultAndroidToolset = "Clang_3_8"; this->DefaultCLFlagTableName = "v140"; this->DefaultCSharpFlagTableName = "v140"; this->DefaultLibFlagTableName = "v14"; @@ -159,11 +160,17 @@ bool cmGlobalVisualStudio14Generator::InitializeWindowsStore(cmMakefile* mf) return true; } +bool cmGlobalVisualStudio14Generator::InitializeAndroid(cmMakefile*) +{ + return true; +} + bool cmGlobalVisualStudio14Generator::SelectWindows10SDK(cmMakefile* mf, bool required) { // Find the default version of the Windows 10 SDK. - std::string const version = this->GetWindows10SDKVersion(); + std::string const version = this->GetWindows10SDKVersion(mf); + if (required && version.empty()) { std::ostringstream e; e << "Could not find an appropriate version of the Windows 10 SDK" @@ -227,7 +234,30 @@ bool cmGlobalVisualStudio14Generator::IsWindowsStoreToolsetInstalled() const cmSystemTools::KeyWOW64_32); } -std::string cmGlobalVisualStudio14Generator::GetWindows10SDKMaxVersion() const +std::string cmGlobalVisualStudio14Generator::GetWindows10SDKMaxVersion( + cmMakefile* mf) const +{ + // if the given value is set, it can either be OFF/FALSE or a valid SDK + // string + if (cmProp value = mf->GetDefinition( + "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM")) { + + // If the value is some off/false value, then there is NO maximum set. + if (cmIsOff(value)) { + return std::string(); + } + // If the value is something else, trust that it is a valid SDK value. + else if (value) { + return *value; + } + // If value is an invalid pointer, leave result unchanged. + } + + return this->GetWindows10SDKMaxVersionDefault(mf); +} + +std::string cmGlobalVisualStudio14Generator::GetWindows10SDKMaxVersionDefault( + cmMakefile*) const { // The last Windows 10 SDK version that VS 2015 can target is 10.0.14393.0. // @@ -261,7 +291,8 @@ public: }; #endif -std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion() +std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion( + cmMakefile* mf) { #if defined(_WIN32) && !defined(__CYGWIN__) std::vector<std::string> win10Roots; @@ -311,8 +342,10 @@ std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion() i = cmSystemTools::GetFilenameName(i); } - // Skip SDKs that cannot be used with our toolset. - std::string maxVersion = this->GetWindows10SDKMaxVersion(); + // Skip SDKs that cannot be used with our toolset, unless the user does not + // want to limit the highest supported SDK according to the Microsoft + // documentation. + std::string maxVersion = this->GetWindows10SDKMaxVersion(mf); if (!maxVersion.empty()) { cm::erase_if(sdks, WindowsSDKTooRecent(maxVersion)); } diff --git a/Source/cmGlobalVisualStudio14Generator.h b/Source/cmGlobalVisualStudio14Generator.h index ccc29177ef..7804b83cbf 100644 --- a/Source/cmGlobalVisualStudio14Generator.h +++ b/Source/cmGlobalVisualStudio14Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudio14Generator_h -#define cmGlobalVisualStudio14Generator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -23,21 +22,31 @@ public: bool MatchesGeneratorName(const std::string& name) const override; + const char* GetAndroidApplicationTypeRevision() const override + { + return "2.0"; + } + protected: cmGlobalVisualStudio14Generator(cmake* cm, const std::string& name, std::string const& platformInGeneratorName); bool InitializeWindows(cmMakefile* mf) override; bool InitializeWindowsStore(cmMakefile* mf) override; + bool InitializeAndroid(cmMakefile* mf) override; bool SelectWindowsStoreToolset(std::string& toolset) const override; // These aren't virtual because we need to check if the selected version // of the toolset is installed bool IsWindowsStoreToolsetInstalled() const; + // Used to adjust the max-SDK-version calculation to accommodate user + // configuration. + std::string GetWindows10SDKMaxVersion(cmMakefile* mf) const; + // Used to make sure that the Windows 10 SDK selected can work with the // version of the toolset. - virtual std::string GetWindows10SDKMaxVersion() const; + virtual std::string GetWindows10SDKMaxVersionDefault(cmMakefile* mf) const; virtual bool SelectWindows10SDK(cmMakefile* mf, bool required); @@ -48,10 +57,9 @@ protected: // installed on the machine. bool IsWindowsDesktopToolsetInstalled() const override; - std::string GetWindows10SDKVersion(); + std::string GetWindows10SDKVersion(cmMakefile* mf); private: class Factory; friend class Factory; }; -#endif diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index 7ada325118..0083c407d5 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -19,8 +19,8 @@ void cmGlobalVisualStudio71Generator::WriteSLNFile( std::ostream& fout, cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators) { - std::vector<std::string> configs; - root->GetMakefile()->GetConfigurations(configs); + std::vector<std::string> configs = + root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); // Write out the header for a SLN file this->WriteSLNHeader(fout); diff --git a/Source/cmGlobalVisualStudio71Generator.h b/Source/cmGlobalVisualStudio71Generator.h index 7eadaf3bf4..7d38199661 100644 --- a/Source/cmGlobalVisualStudio71Generator.h +++ b/Source/cmGlobalVisualStudio71Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudio71Generator_h -#define cmGlobalVisualStudio71Generator_h +#pragma once #include "cmGlobalVisualStudio7Generator.h" @@ -42,4 +41,3 @@ protected: std::string ProjectConfigurationSectionName; }; -#endif diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 428c748633..6267205ab1 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -339,7 +339,7 @@ void cmGlobalVisualStudio7Generator::WriteTargetConfigurations( // loop over again and write out configurations for each target // in the solution for (cmGeneratorTarget const* target : projectTargets) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!target->IsInBuildSystem()) { continue; } cmProp expath = target->GetProperty("EXTERNAL_MSPROJECT"); @@ -369,7 +369,7 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( std::string rootBinaryDir = root->GetCurrentBinaryDirectory(); for (cmGeneratorTarget const* target : projectTargets) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!target->IsInBuildSystem()) { continue; } bool written = false; @@ -381,8 +381,7 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( std::string location = *expath; cmProp p = target->GetProperty("VS_PROJECT_TYPE"); - this->WriteExternalProject(fout, project, location, - p ? p->c_str() : nullptr, + this->WriteExternalProject(fout, project, location, cmToCStr(p), target->GetUtilities()); written = true; } else { @@ -436,7 +435,7 @@ void cmGlobalVisualStudio7Generator::WriteTargetDepends( std::ostream& fout, OrderedTargetDependSet const& projectTargets) { for (cmGeneratorTarget const* target : projectTargets) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!target->IsInBuildSystem()) { continue; } cmProp vcprojName = target->GetProperty("GENERATOR_FILE_NAME"); @@ -568,8 +567,9 @@ void cmGlobalVisualStudio7Generator::WriteSLNFooter(std::ostream& fout) std::string cmGlobalVisualStudio7Generator::WriteUtilityDepend( cmGeneratorTarget const* target) { - std::vector<std::string> configs; - target->Target->GetMakefile()->GetConfigurations(configs); + std::vector<std::string> configs = + target->Target->GetMakefile()->GetGeneratorConfigs( + cmMakefile::ExcludeEmptyConfig); std::string pname = cmStrCat(target->GetName(), "_UTILITY"); std::string fname = cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/', @@ -625,9 +625,9 @@ std::string cmGlobalVisualStudio7Generator::WriteUtilityDepend( std::string cmGlobalVisualStudio7Generator::GetGUID(std::string const& name) { std::string const& guidStoreName = name + "_GUID_CMAKE"; - if (const char* storedGUID = + if (cmProp storedGUID = this->CMakeInstance->GetCacheDefinition(guidStoreName)) { - return std::string(storedGUID); + return *storedGUID; } // Compute a GUID that is deterministic but unique to the build tree. std::string input = @@ -675,11 +675,11 @@ std::set<std::string> cmGlobalVisualStudio7Generator::IsPartOfDefaultBuild( "CMAKE_VS_INCLUDE_" + t + "_TO_DEFAULT_BUILD"; // inspect CMAKE_VS_INCLUDE_<t>_TO_DEFAULT_BUILD properties for (std::string const& i : configs) { - const char* propertyValue = + cmProp propertyValue = target->Target->GetMakefile()->GetDefinition(propertyName); if (propertyValue && cmIsOn(cmGeneratorExpression::Evaluate( - propertyValue, target->GetLocalGenerator(), i))) { + *propertyValue, target->GetLocalGenerator(), i))) { activeConfigs.insert(i); } } @@ -693,9 +693,7 @@ std::set<std::string> cmGlobalVisualStudio7Generator::IsPartOfDefaultBuild( } // inspect EXCLUDE_FROM_DEFAULT_BUILD[_<CONFIG>] properties for (std::string const& i : configs) { - const char* propertyValue = - target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i); - if (cmIsOff(propertyValue)) { + if (cmIsOff(target->GetFeature("EXCLUDE_FROM_DEFAULT_BUILD", i))) { activeConfigs.insert(i); } } diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index 6cc1cf86d8..148762eacd 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudio7Generator_h -#define cmGlobalVisualStudio7Generator_h +#pragma once #include <memory> @@ -174,5 +173,3 @@ private: }; #define CMAKE_CHECK_BUILD_SYSTEM_TARGET "ZERO_CHECK" - -#endif diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index 29ca1549c5..fcdfc5064e 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -325,7 +325,7 @@ void cmGlobalVisualStudio8Generator::WriteProjectDepends( TargetDependSet const& unordered = this->GetTargetDirectDepends(gt); OrderedTargetDependSet depends(unordered, std::string()); for (cmTargetDepend const& i : depends) { - if (i->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!i->IsInBuildSystem()) { continue; } std::string guid = this->GetGUID(i->GetName()); @@ -341,7 +341,7 @@ bool cmGlobalVisualStudio8Generator::NeedLinkLibraryDependencies( if (cmGeneratorTarget* depTarget = target->GetLocalGenerator()->FindGeneratorTargetToUse( ui.Value.first)) { - if (depTarget->GetType() != cmStateEnums::INTERFACE_LIBRARY && + if (depTarget->IsInBuildSystem() && depTarget->GetProperty("EXTERNAL_MSPROJECT")) { // This utility dependency names an external .vcproj target. // We use LinkLibraryDependencies="true" to link to it without diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h index 6ce67d3e7a..96e3553f26 100644 --- a/Source/cmGlobalVisualStudio8Generator.h +++ b/Source/cmGlobalVisualStudio8Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudio8Generator_h -#define cmGlobalVisualStudio8Generator_h +#pragma once #include "cmGlobalVisualStudio71Generator.h" @@ -78,4 +77,3 @@ protected: std::string Name; std::string WindowsCEVersion; }; -#endif diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx index 9f73c15f0f..2339a80527 100644 --- a/Source/cmGlobalVisualStudio9Generator.cxx +++ b/Source/cmGlobalVisualStudio9Generator.cxx @@ -16,7 +16,7 @@ class cmGlobalVisualStudio9Generator::Factory : public cmGlobalGeneratorFactory { public: std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, cmake* cm) const override + const std::string& name, bool allowArch, cmake* cm) const override { if (strncmp(name.c_str(), vs9generatorName, sizeof(vs9generatorName) - 1) != 0) { @@ -29,7 +29,7 @@ public: new cmGlobalVisualStudio9Generator(cm, name, "")); } - if (p[0] != ' ') { + if (!allowArch || p[0] != ' ') { return std::unique_ptr<cmGlobalGenerator>(); } diff --git a/Source/cmGlobalVisualStudio9Generator.h b/Source/cmGlobalVisualStudio9Generator.h index 53318a62b1..6f4d159ecc 100644 --- a/Source/cmGlobalVisualStudio9Generator.h +++ b/Source/cmGlobalVisualStudio9Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudio9Generator_h -#define cmGlobalVisualStudio9Generator_h +#pragma once #include <memory> @@ -38,4 +37,3 @@ private: class Factory; friend class Factory; }; -#endif diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index c688da2a39..001d876650 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -368,7 +368,7 @@ cmGlobalVisualStudioGenerator::GetTargetLinkClosure(cmGeneratorTarget* target) void cmGlobalVisualStudioGenerator::FollowLinkDepends( const cmGeneratorTarget* target, std::set<const cmGeneratorTarget*>& linked) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!target->IsInBuildSystem()) { return; } if (linked.insert(target).second && @@ -509,7 +509,7 @@ std::string cmGlobalVisualStudioGenerator::GetStartupProjectName( cmLocalGenerator const* root) const { cmProp n = root->GetMakefile()->GetProperty("VS_STARTUP_PROJECT"); - if (n && !n->empty()) { + if (cmNonempty(n)) { std::string startup = *n; if (this->FindTarget(startup)) { return startup; @@ -810,7 +810,7 @@ bool cmGlobalVisualStudioGenerator::TargetIsFortranOnly( // a target with none of its own sources, e.g. when also using // object libraries. cmProp linkLang = gt->GetProperty("LINKER_LANGUAGE"); - if (linkLang && !linkLang->empty()) { + if (cmNonempty(linkLang)) { languages.insert(*linkLang); } diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 29a5c2cdc1..3bfcbd04d9 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudioGenerator_h -#define cmGlobalVisualStudioGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -167,6 +166,11 @@ protected: void WriteSLNHeader(std::ostream& fout); + FindMakeProgramStage GetFindMakeProgramStage() const override + { + return FindMakeProgramStage::Early; + } + bool ComputeTargetDepends() override; class VSDependSet : public std::set<std::string> { @@ -224,5 +228,3 @@ public: OrderedTargetDependSet(TargetDependSet const&, std::string const& first); OrderedTargetDependSet(TargetSet const&, std::string const& first); }; - -#endif diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index 605dc8b2a6..84f870ea6d 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx @@ -100,6 +100,24 @@ static const char* VSVersionToToolset( return ""; } +static const char* VSVersionToAndroidToolset( + cmGlobalVisualStudioGenerator::VSVersion v) +{ + switch (v) { + case cmGlobalVisualStudioGenerator::VS9: + case cmGlobalVisualStudioGenerator::VS10: + case cmGlobalVisualStudioGenerator::VS11: + case cmGlobalVisualStudioGenerator::VS12: + return ""; + case cmGlobalVisualStudioGenerator::VS14: + return "Clang_3_8"; + case cmGlobalVisualStudioGenerator::VS15: + case cmGlobalVisualStudioGenerator::VS16: + return "Clang_5_0"; + } + return ""; +} + static const char vs15generatorName[] = "Visual Studio 15 2017"; // Map generator name without year to name with year. @@ -122,7 +140,7 @@ class cmGlobalVisualStudioVersionedGenerator::Factory15 { public: std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, cmake* cm) const override + const std::string& name, bool allowArch, cmake* cm) const override { std::string genName; const char* p = cmVS15GenName(name, genName); @@ -134,7 +152,7 @@ public: new cmGlobalVisualStudioVersionedGenerator( cmGlobalVisualStudioGenerator::VS15, cm, genName, "")); } - if (*p++ != ' ') { + if (!allowArch || *p++ != ' ') { return std::unique_ptr<cmGlobalGenerator>(); } if (strcmp(p, "Win64") == 0) { @@ -216,7 +234,7 @@ class cmGlobalVisualStudioVersionedGenerator::Factory16 { public: std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, cmake* cm) const override + const std::string& name, bool /*allowArch*/, cmake* cm) const override { std::string genName; const char* p = cmVS16GenName(name, genName); @@ -284,6 +302,7 @@ cmGlobalVisualStudioVersionedGenerator::cmGlobalVisualStudioVersionedGenerator( this->Version = version; this->ExpressEdition = false; this->DefaultPlatformToolset = VSVersionToToolset(this->Version); + this->DefaultAndroidToolset = VSVersionToAndroidToolset(this->Version); this->DefaultCLFlagTableName = VSVersionToToolset(this->Version); this->DefaultCSharpFlagTableName = VSVersionToToolset(this->Version); this->DefaultLinkFlagTableName = VSVersionToToolset(this->Version); @@ -408,6 +427,25 @@ bool cmGlobalVisualStudioVersionedGenerator::IsStdOutEncodingSupported() const vsInstanceVersion > vsInstanceVersion16_7_P2); } +const char* +cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision() + const +{ + switch (this->Version) { + case cmGlobalVisualStudioGenerator::VS9: + case cmGlobalVisualStudioGenerator::VS10: + case cmGlobalVisualStudioGenerator::VS11: + case cmGlobalVisualStudioGenerator::VS12: + return ""; + case cmGlobalVisualStudioGenerator::VS14: + return "2.0"; + case cmGlobalVisualStudioGenerator::VS15: + case cmGlobalVisualStudioGenerator::VS16: + return "3.0"; + } + return ""; +} + std::string cmGlobalVisualStudioVersionedGenerator::GetAuxiliaryToolset() const { const char* version = this->GetPlatformToolsetVersion(); @@ -502,8 +540,9 @@ bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const return false; } -std::string cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersion() - const +std::string +cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersionDefault( + cmMakefile*) const { return std::string(); } diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.h b/Source/cmGlobalVisualStudioVersionedGenerator.h index cbd3ba77cb..46a5f40e53 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.h +++ b/Source/cmGlobalVisualStudioVersionedGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalVisualStudioVersionedGenerator_h -#define cmGlobalVisualStudioVersionedGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -36,6 +35,8 @@ public: bool IsStdOutEncodingSupported() const override; + const char* GetAndroidApplicationTypeRevision() const override; + protected: cmGlobalVisualStudioVersionedGenerator( VSVersion version, cmake* cm, const std::string& name, @@ -55,7 +56,7 @@ protected: // Check for a Win 8 SDK known to the registry or VS installer tool. bool IsWin81SDKInstalled() const; - std::string GetWindows10SDKMaxVersion() const override; + std::string GetWindows10SDKMaxVersionDefault(cmMakefile*) const override; std::string FindMSBuildCommand() override; std::string FindDevEnvCommand() override; @@ -67,4 +68,3 @@ private: friend class Factory16; mutable cmVSSetupAPIHelper vsSetupAPIHelper; }; -#endif diff --git a/Source/cmGlobalWatcomWMakeGenerator.h b/Source/cmGlobalWatcomWMakeGenerator.h index c47127febe..da39d3f5db 100644 --- a/Source/cmGlobalWatcomWMakeGenerator.h +++ b/Source/cmGlobalWatcomWMakeGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalWatcomWMakeGenerator_h -#define cmGlobalWatcomWMakeGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -64,5 +63,3 @@ protected: void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override; }; - -#endif diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index b28395a569..df45b35230 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -8,6 +8,7 @@ #include <cstring> #include <iomanip> #include <sstream> +#include <unordered_set> #include <utility> #include <cm/memory> @@ -17,6 +18,7 @@ #include "cmsys/RegularExpression.hxx" #include "cmComputeLinkInformation.h" +#include "cmCryptoHash.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" #include "cmCustomCommandLines.h" @@ -134,7 +136,7 @@ class cmGlobalXCodeGenerator::Factory : public cmGlobalGeneratorFactory { public: std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, cmake* cm) const override; + const std::string& name, bool allowArch, cmake* cm) const override; void GetDocumentation(cmDocumentationEntry& entry) const override { @@ -170,9 +172,15 @@ cmGlobalXCodeGenerator::cmGlobalXCodeGenerator( { this->VersionString = version_string; this->XcodeVersion = version_number; + if (this->XcodeVersion >= 120) { + this->XcodeBuildSystem = BuildSystem::Twelve; + } else { + this->XcodeBuildSystem = BuildSystem::One; + } this->RootObject = nullptr; this->MainGroupChildren = nullptr; + this->FrameworkGroup = nullptr; this->CurrentMakefile = nullptr; this->CurrentLocalGenerator = nullptr; this->XcodeBuildCommandInitialized = false; @@ -190,6 +198,7 @@ std::unique_ptr<cmGlobalGeneratorFactory> cmGlobalXCodeGenerator::NewFactory() std::unique_ptr<cmGlobalGenerator> cmGlobalXCodeGenerator::Factory::CreateGlobalGenerator(const std::string& name, + bool /*allowArch*/, cmake* cm) const { if (name != GetActualName()) { @@ -279,32 +288,140 @@ bool cmGlobalXCodeGenerator::SetSystemName(std::string const& s, return this->cmGlobalGenerator::SetSystemName(s, mf); } +namespace { +cm::string_view cmXcodeBuildSystemString(cmGlobalXCodeGenerator::BuildSystem b) +{ + switch (b) { + case cmGlobalXCodeGenerator::BuildSystem::One: + return "1"_s; + case cmGlobalXCodeGenerator::BuildSystem::Twelve: + return "12"_s; + } + return {}; +} +} + bool cmGlobalXCodeGenerator::SetGeneratorToolset(std::string const& ts, bool build, cmMakefile* mf) { - if (ts.find_first_of(",=") != std::string::npos) { - std::ostringstream e; - /* clang-format off */ - e << - "Generator\n" - " " << this->GetName() << "\n" - "does not recognize the toolset\n" - " " << ts << "\n" - "that was specified."; - /* clang-format on */ - mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + if (!this->ParseGeneratorToolset(ts, mf)) { return false; } - this->GeneratorToolset = ts; if (build) { return true; } if (!this->GeneratorToolset.empty()) { mf->AddDefinition("CMAKE_XCODE_PLATFORM_TOOLSET", this->GeneratorToolset); } + mf->AddDefinition("CMAKE_XCODE_BUILD_SYSTEM", + cmXcodeBuildSystemString(this->XcodeBuildSystem)); + return true; +} + +bool cmGlobalXCodeGenerator::ParseGeneratorToolset(std::string const& ts, + cmMakefile* mf) +{ + std::vector<std::string> const fields = cmTokenize(ts, ","); + auto fi = fields.cbegin(); + if (fi == fields.cend()) { + return true; + } + + // The first field may be the Xcode GCC_VERSION. + if (fi->find('=') == fi->npos) { + this->GeneratorToolset = *fi; + ++fi; + } + + std::unordered_set<std::string> handled; + + // The rest of the fields must be key=value pairs. + for (; fi != fields.cend(); ++fi) { + std::string::size_type pos = fi->find('='); + if (pos == fi->npos) { + /* clang-format off */ + std::string const& e = cmStrCat( + "Generator\n" + " ", this->GetName(), "\n" + "given toolset specification\n" + " ", ts, "\n" + "that contains a field after the first ',' with no '='." + ); + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e); + return false; + } + std::string const key = fi->substr(0, pos); + std::string const value = fi->substr(pos + 1); + if (!handled.insert(key).second) { + /* clang-format off */ + std::string const& e = cmStrCat( + "Generator\n" + " ", this->GetName(), "\n" + "given toolset specification\n" + " ", ts, "\n" + "that contains duplicate field key '", key, "'." + ); + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e); + return false; + } + if (!this->ProcessGeneratorToolsetField(key, value, mf)) { + return false; + } + } + return true; } +bool cmGlobalXCodeGenerator::ProcessGeneratorToolsetField( + std::string const& key, std::string const& value, cmMakefile* mf) +{ + if (key == "buildsystem") { + if (value == "1"_s) { + this->XcodeBuildSystem = BuildSystem::One; + } else if (value == "12"_s) { + this->XcodeBuildSystem = BuildSystem::Twelve; + } else { + /* clang-format off */ + std::string const& e = cmStrCat( + "Generator\n" + " ", this->GetName(), "\n" + "toolset specification field\n" + " buildsystem=", value, "\n" + "value is unkonwn. It must be '1' or '12'." + ); + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e); + return false; + } + if (this->XcodeBuildSystem == BuildSystem::Twelve && + this->XcodeVersion < 120) { + /* clang-format off */ + std::string const& e = cmStrCat( + "Generator\n" + " ", this->GetName(), "\n" + "toolset specification field\n" + " buildsystem=", value, "\n" + "is not allowed with Xcode ", this->VersionString, '.' + ); + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e); + return false; + } + return true; + } + /* clang-format off */ + std::string const& e = cmStrCat( + "Generator\n" + " ", this->GetName(), "\n" + "given toolset specification that contains invalid field '", key, "'." + ); + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e); + return false; +} + void cmGlobalXCodeGenerator::EnableLanguage( std::vector<std::string> const& lang, cmMakefile* mf, bool optional) { @@ -387,6 +504,9 @@ cmGlobalXCodeGenerator::GenerateBuildCommand( } } + if (this->XcodeBuildSystem >= BuildSystem::Twelve) { + makeCommand.Add("-parallelizeTargets"); + } makeCommand.Add("-configuration", (config.empty() ? "Debug" : config)); if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { @@ -407,8 +527,15 @@ cmGlobalXCodeGenerator::GenerateBuildCommand( std::unique_ptr<cmLocalGenerator> cmGlobalXCodeGenerator::CreateLocalGenerator( cmMakefile* mf) { - return std::unique_ptr<cmLocalGenerator>( + std::unique_ptr<cmLocalGenerator> lg( cm::make_unique<cmLocalXCodeGenerator>(this, mf)); + if (this->XcodeBuildSystem >= BuildSystem::Twelve) { + // For this build system variant we generate custom commands as + // shell scripts directly rather than inside Makefiles. + // FIXME: Rename or refactor this option for clarity. + lg->SetLinkScriptShell(true); + } + return lg; } void cmGlobalXCodeGenerator::AddExtraIDETargets() @@ -423,37 +550,6 @@ void cmGlobalXCodeGenerator::AddExtraIDETargets() } } -void cmGlobalXCodeGenerator::ComputeTargetOrder() -{ - size_t index = 0; - auto const& lgens = this->GetLocalGenerators(); - for (auto const& lgen : lgens) { - const auto& targets = lgen->GetGeneratorTargets(); - for (const auto& gt : targets) { - this->ComputeTargetOrder(gt.get(), index); - } - } - assert(index == this->TargetOrderIndex.size()); -} - -void cmGlobalXCodeGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt, - size_t& index) -{ - std::map<cmGeneratorTarget const*, size_t>::value_type value(gt, 0); - auto insertion = this->TargetOrderIndex.insert(value); - if (!insertion.second) { - return; - } - auto entry = insertion.first; - - auto& deps = this->GetTargetDirectDepends(gt); - for (auto& d : deps) { - this->ComputeTargetOrder(d, index); - } - - entry->second = index++; -} - void cmGlobalXCodeGenerator::Generate() { this->cmGlobalGenerator::Generate(); @@ -461,8 +557,6 @@ void cmGlobalXCodeGenerator::Generate() return; } - this->ComputeTargetOrder(); - for (auto keyVal : this->ProjectMap) { cmLocalGenerator* root = keyVal.second[0]; @@ -476,6 +570,9 @@ void cmGlobalXCodeGenerator::Generate() } } + // cache the enabled languages for source file type queries + this->GetEnabledLanguages(this->EnabledLangs); + this->SetGenerationRoot(root); // now create the project this->OutputXCodeProject(root, keyVal.second); @@ -526,10 +623,14 @@ void cmGlobalXCodeGenerator::AddExtraTargets( root->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(allbuild, root)); // Add XCODE depend helper - std::string dir = root->GetCurrentBinaryDirectory(); - cmCustomCommandLines commandLines = cmMakeSingleCommandLine( - { "make", "-C", dir, "-f", this->CurrentXCodeHackMakefile, - "OBJDIR=$(OBJDIR)", /* placeholder, see below */ "" }); + std::string legacyDependHelperDir = root->GetCurrentBinaryDirectory(); + cmCustomCommandLines legacyDependHelperCommandLines; + if (this->XcodeBuildSystem == BuildSystem::One) { + legacyDependHelperCommandLines = cmMakeSingleCommandLine( + { "make", "-C", legacyDependHelperDir, "-f", + this->CurrentXCodeHackMakefile, "OBJDIR=$(OBJDIR)", + /* placeholder, see below */ "" }); + } // Add ZERO_CHECK bool regenerate = !this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION"); @@ -568,15 +669,15 @@ 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 (isGenerateProject && + if (this->XcodeBuildSystem == BuildSystem::One && isGenerateProject && target->GetType() == cmStateEnums::OBJECT_LIBRARY) { - commandLines.front().back() = // fill placeholder + legacyDependHelperCommandLines.front().back() = // fill placeholder this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)"); gen->AddCustomCommandToTarget( - target->GetName(), no_byproducts, no_depends, commandLines, - cmCustomCommandType::POST_BUILD, "Depend check for xcode", - dir.c_str(), true, false, "", "", false, - cmObjectLibraryCommands::Accept); + target->GetName(), no_byproducts, no_depends, + legacyDependHelperCommandLines, cmCustomCommandType::POST_BUILD, + "Depend check for xcode", legacyDependHelperDir.c_str(), true, false, + "", "", false, cmObjectLibraryCommands::Accept); } if (!this->IsExcluded(gens[0], target.get())) { @@ -673,6 +774,9 @@ void cmGlobalXCodeGenerator::ClearXCodeObjects() this->GroupNameMap.clear(); this->TargetGroup.clear(); this->FileRefs.clear(); + this->ExternalLibRefs.clear(); + this->FileRefToBuildFileMap.clear(); + this->CommandsVisited.clear(); } void cmGlobalXCodeGenerator::addObject(std::unique_ptr<cmXCodeObject> obj) @@ -694,9 +798,10 @@ void cmGlobalXCodeGenerator::addObject(std::unique_ptr<cmXCodeObject> obj) } cmXCodeObject* cmGlobalXCodeGenerator::CreateObject( - cmXCodeObject::PBXType ptype) + cmXCodeObject::PBXType ptype, cm::string_view key) { - auto obj = cm::make_unique<cmXCode21Object>(ptype, cmXCodeObject::OBJECT); + auto obj = cm::make_unique<cmXCode21Object>(ptype, cmXCodeObject::OBJECT, + this->GetObjectId(ptype, key)); auto ptr = obj.get(); this->addObject(std::move(obj)); return ptr; @@ -704,7 +809,9 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateObject( cmXCodeObject* cmGlobalXCodeGenerator::CreateObject(cmXCodeObject::Type type) { - auto obj = cm::make_unique<cmXCodeObject>(cmXCodeObject::None, type); + auto obj = cm::make_unique<cmXCodeObject>( + cmXCodeObject::None, type, + "Temporary cmake object, should not be referred to in Xcode file"); auto ptr = obj.get(); this->addObject(std::move(obj)); return ptr; @@ -741,21 +848,28 @@ std::string GetGroupMapKeyFromPath(cmGeneratorTarget* target, return key; } -cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFileFromPath( +cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeBuildFileFromPath( const std::string& fullpath, cmGeneratorTarget* target, const std::string& lang, cmSourceFile* sf) { // Using a map and the full path guarantees that we will always get the same - // fileRef object for any given full path. - // + // fileRef object for any given full path. Same goes for the buildFile + // object. cmXCodeObject* fileRef = this->CreateXCodeFileReferenceFromPath(fullpath, target, lang, sf); - - cmXCodeObject* buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile); - buildFile->SetComment(fileRef->GetComment()); - buildFile->AddAttribute("fileRef", this->CreateObjectReference(fileRef)); - - return buildFile; + if (fileRef) { + auto it = this->FileRefToBuildFileMap.find(fileRef); + if (it == this->FileRefToBuildFileMap.end()) { + cmXCodeObject* buildFile = + this->CreateObject(cmXCodeObject::PBXBuildFile); + buildFile->SetComment(fileRef->GetComment()); + buildFile->AddAttribute("fileRef", this->CreateObjectReference(fileRef)); + this->FileRefToBuildFileMap[fileRef] = buildFile; + return buildFile; + } + return it->second; + } + return nullptr; } class XCodeGeneratorExpressionInterpreter @@ -869,7 +983,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile( lg->AppendFlags(flags, lg->GetIncludeFlags(includes, gtgt, lang, true)); cmXCodeObject* buildFile = - this->CreateXCodeSourceFileFromPath(sf->ResolveFullPath(), gtgt, lang, sf); + this->CreateXCodeBuildFileFromPath(sf->ResolveFullPath(), gtgt, lang, sf); cmXCodeObject* settings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); settings->AddAttributeIfNotEmpty("COMPILER_FLAGS", @@ -905,7 +1019,9 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeSourceFile( settings->AddAttributeIfNotEmpty("ATTRIBUTES", attrs); - buildFile->AddAttributeIfNotEmpty("settings", settings); + if (buildFile) { + buildFile->AddAttributeIfNotEmpty("settings", settings); + } return buildFile; } @@ -922,14 +1038,45 @@ void cmGlobalXCodeGenerator::AddXCodeProjBuildRule( } } -std::string GetSourcecodeValueFromFileExtension(const std::string& _ext, - const std::string& lang, - bool& keepLastKnownFileType) +namespace { + +bool IsLinkPhaseLibraryExtension(const std::string& fileExt) +{ + // Empty file extension is a special case for paths to framework's + // internal binary which could be MyFw.framework/Versions/*/MyFw + return (fileExt == ".framework" || fileExt == ".a" || fileExt == ".o" || + fileExt == ".dylib" || fileExt == ".tbd" || fileExt.empty()); +} +bool IsLibraryType(const std::string& fileType) +{ + return (fileType == "wrapper.framework" || fileType == "archive.ar" || + fileType == "compiled.mach-o.objfile" || + fileType == "compiled.mach-o.dylib" || + fileType == "compiled.mach-o.executable" || + fileType == "sourcecode.text-based-dylib-definition"); +} + +std::string GetDirectoryValueFromFileExtension(const std::string& dirExt) +{ + std::string ext = cmSystemTools::LowerCase(dirExt); + if (ext == "framework") { + return "wrapper.framework"; + } + if (ext == "xcassets") { + return "folder.assetcatalog"; + } + return "folder"; +} + +std::string GetSourcecodeValueFromFileExtension( + const std::string& _ext, const std::string& lang, + bool& keepLastKnownFileType, const std::vector<std::string>& enabled_langs) { std::string ext = cmSystemTools::LowerCase(_ext); std::string sourcecode = "sourcecode"; if (ext == "o") { + keepLastKnownFileType = true; sourcecode = "compiled.mach-o.objfile"; } else if (ext == "xctest") { sourcecode = "wrapper.cfbundle"; @@ -939,9 +1086,9 @@ std::string GetSourcecodeValueFromFileExtension(const std::string& _ext, } else if (ext == "storyboard") { keepLastKnownFileType = true; sourcecode = "file.storyboard"; - } else if (ext == "mm") { + } else if (ext == "mm" && !cm::contains(enabled_langs, "OBJCXX")) { sourcecode += ".cpp.objcpp"; - } else if (ext == "m") { + } else if (ext == "m" && !cm::contains(enabled_langs, "OBJC")) { sourcecode += ".c.objc"; } else if (ext == "swift") { sourcecode += ".swift"; @@ -973,6 +1120,20 @@ std::string GetSourcecodeValueFromFileExtension(const std::string& _ext, sourcecode += ".metal"; } else if (ext == "mig") { sourcecode += ".mig"; + } else if (ext == "tbd") { + sourcecode += ".text-based-dylib-definition"; + } else if (ext == "a") { + keepLastKnownFileType = true; + sourcecode = "archive.ar"; + } else if (ext == "dylib") { + keepLastKnownFileType = true; + sourcecode = "compiled.mach-o.dylib"; + } else if (ext == "framework") { + keepLastKnownFileType = true; + sourcecode = "wrapper.framework"; + } else if (ext == "xcassets") { + keepLastKnownFileType = true; + sourcecode = "folder.assetcatalog"; } // else // { @@ -985,24 +1146,51 @@ std::string GetSourcecodeValueFromFileExtension(const std::string& _ext, return sourcecode; } +// If the file has no extension it's either a raw executable or might +// be a direct reference to a binary within a framework (bad practice!). +// This is where we change the path to point to the framework directory. +// .tbd files also can be located in SDK frameworks (they are +// placeholders for actual libraries shipped with the OS) +std::string GetLibraryOrFrameworkPath(const std::string& path) +{ + auto ext = cmSystemTools::GetFilenameLastExtension(path); + if (ext.empty() || ext == ".tbd") { + auto name = cmSystemTools::GetFilenameWithoutExtension(path); + // Check for iOS framework structure: + // FwName.framework/FwName (and also on macOS where FwName lib is a + // symlink) + auto parentDir = cmSystemTools::GetParentDirectory(path); + auto parentName = cmSystemTools::GetFilenameWithoutExtension(parentDir); + ext = cmSystemTools::GetFilenameLastExtension(parentDir); + if (ext == ".framework" && name == parentName) { + return parentDir; + } + // Check for macOS framework structure: + // FwName.framework/Versions/*/FwName + std::vector<std::string> components; + cmSystemTools::SplitPath(path, components); + if (components.size() > 3 && + components[components.size() - 3] == "Versions") { + ext = cmSystemTools::GetFilenameLastExtension( + components[components.size() - 4]); + parentName = cmSystemTools::GetFilenameWithoutExtension( + components[components.size() - 4]); + if (ext == ".framework" && name == parentName) { + components.erase(components.begin() + components.size() - 3, + components.end()); + return cmSystemTools::JoinPath(components); + } + } + } + return path; +} + +} // anonymous + cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( const std::string& fullpath, cmGeneratorTarget* target, const std::string& lang, cmSourceFile* sf) { - std::string key = GetGroupMapKeyFromPath(target, fullpath); - cmXCodeObject* fileRef = this->FileRefs[key]; - if (!fileRef) { - fileRef = this->CreateObject(cmXCodeObject::PBXFileReference); - fileRef->SetComment(fullpath); - this->FileRefs[key] = fileRef; - } - cmXCodeObject* group = this->GroupMap[key]; - cmXCodeObject* children = group->GetObject("children"); - if (!children->HasObject(fileRef)) { - children->AddObject(fileRef); - } - fileRef->AddAttribute("fileEncoding", this->CreateString("4")); - bool useLastKnownFileType = false; std::string fileType; if (sf) { @@ -1013,38 +1201,76 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReferenceFromPath( fileType = *l; } } + // Make a copy so that we can override it later + std::string path = fullpath; + // Compute the extension without leading '.'. + std::string ext = cmSystemTools::GetFilenameLastExtension(path); + if (!ext.empty()) { + ext = ext.substr(1); + } if (fileType.empty()) { - // Compute the extension without leading '.'. - std::string ext = cmSystemTools::GetFilenameLastExtension(fullpath); + path = GetLibraryOrFrameworkPath(path); + ext = cmSystemTools::GetFilenameLastExtension(path); if (!ext.empty()) { ext = ext.substr(1); } - // If fullpath references a directory, then we need to specify // lastKnownFileType as folder in order for Xcode to be able to // open the contents of the folder. // (Xcode 4.6 does not like explicitFileType=folder). - if (cmSystemTools::FileIsDirectory(fullpath)) { - fileType = (ext == "xcassets" ? "folder.assetcatalog" : "folder"); + if (cmSystemTools::FileIsDirectory(path)) { + fileType = GetDirectoryValueFromFileExtension(ext); useLastKnownFileType = true; } else { - fileType = - GetSourcecodeValueFromFileExtension(ext, lang, useLastKnownFileType); + if (ext.empty() && !sf) { + // Special case for executable or library without extension + // that is not a source file. We can't tell which without reading + // its Mach-O header, but the file might not exist yet, so we + // have to pick one here. + useLastKnownFileType = true; + fileType = "compiled.mach-o.executable"; + } else { + fileType = GetSourcecodeValueFromFileExtension( + ext, lang, useLastKnownFileType, this->EnabledLangs); + } } } + std::string key = GetGroupMapKeyFromPath(target, path); + cmXCodeObject* fileRef = this->FileRefs[key]; + if (!fileRef) { + fileRef = this->CreateObject(cmXCodeObject::PBXFileReference); + fileRef->SetComment(path); + this->FileRefs[key] = fileRef; + } + fileRef->AddAttribute("fileEncoding", this->CreateString("4")); fileRef->AddAttribute(useLastKnownFileType ? "lastKnownFileType" : "explicitFileType", this->CreateString(fileType)); - // Store the file path relative to the top of the source tree. - std::string path = this->RelativeToSource(fullpath); + if (!IsLibraryType(fileType)) { + path = this->RelativeToSource(path); + } std::string name = cmSystemTools::GetFilenameName(path); const char* sourceTree = cmSystemTools::FileIsFullPath(path) ? "<absolute>" : "SOURCE_ROOT"; fileRef->AddAttribute("name", this->CreateString(name)); fileRef->AddAttribute("path", this->CreateString(path)); fileRef->AddAttribute("sourceTree", this->CreateString(sourceTree)); + + cmXCodeObject* group = this->GroupMap[key]; + if (!group && IsLibraryType(fileType)) { + group = this->FrameworkGroup; + this->GroupMap[key] = group; + } + if (!group) { + cmSystemTools::Error("Could not find a PBX group for " + key); + return nullptr; + } + cmXCodeObject* children = group->GetAttribute("children"); + if (!children->HasObject(fileRef)) { + children->AddObject(fileRef); + } return fileRef; } @@ -1059,9 +1285,8 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeFileReference( bool cmGlobalXCodeGenerator::SpecialTargetEmitted(std::string const& tname) { - if (tname == "ALL_BUILD" || tname == "XCODE_DEPEND_HELPER" || - tname == "install" || tname == "package" || tname == "RUN_TESTS" || - tname == CMAKE_CHECK_BUILD_SYSTEM_TARGET) { + if (tname == "ALL_BUILD" || tname == "install" || tname == "package" || + tname == "RUN_TESTS" || tname == CMAKE_CHECK_BUILD_SYSTEM_TARGET) { if (this->TargetDoneSet.find(tname) != this->TargetDoneSet.end()) { return true; } @@ -1077,11 +1302,8 @@ void cmGlobalXCodeGenerator::SetCurrentLocalGenerator(cmLocalGenerator* gen) this->CurrentMakefile = gen->GetMakefile(); // Select the current set of configuration types. - this->CurrentConfigurationTypes.clear(); - this->CurrentMakefile->GetConfigurations(this->CurrentConfigurationTypes); - if (this->CurrentConfigurationTypes.empty()) { - this->CurrentConfigurationTypes.emplace_back(); - } + this->CurrentConfigurationTypes = + this->CurrentMakefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); } struct cmSourceFilePathCompare @@ -1112,12 +1334,8 @@ bool cmGlobalXCodeGenerator::CreateXCodeTargets( cmLocalGenerator* gen, std::vector<cmXCodeObject*>& targets) { this->SetCurrentLocalGenerator(gen); - std::vector<cmGeneratorTarget*> gts; - cm::append(gts, this->CurrentLocalGenerator->GetGeneratorTargets()); - std::sort(gts.begin(), gts.end(), - [this](cmGeneratorTarget const* l, cmGeneratorTarget const* r) { - return this->TargetOrderIndex[l] < this->TargetOrderIndex[r]; - }); + std::vector<cmGeneratorTarget*> gts = + this->GetLocalGeneratorTargetsInOrder(gen); for (auto gtgt : gts) { if (!this->CreateXCodeTarget(gtgt, targets)) { return false; @@ -1137,11 +1355,22 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( return true; } - if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!gtgt->IsInBuildSystem()) { return true; } + auto& gtgt_visited = this->CommandsVisited[gtgt]; + auto& deps = this->GetTargetDirectDepends(gtgt); + for (auto& d : deps) { + // Take the union of visited source files of custom commands so far. + // ComputeTargetOrder ensures our dependencies already visited their + // custom commands and updated CommandsVisited. + auto& dep_visited = this->CommandsVisited[d]; + gtgt_visited.insert(dep_visited.begin(), dep_visited.end()); + } + if (gtgt->GetType() == cmStateEnums::UTILITY || + gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY || gtgt->GetType() == cmStateEnums::GLOBAL_TARGET) { cmXCodeObject* t = this->CreateUtilityTarget(gtgt); if (!t) { @@ -1152,23 +1381,24 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( } // organize the sources - std::vector<cmSourceFile*> classes; - if (!gtgt->GetConfigCommonSourceFiles(classes)) { + std::vector<cmSourceFile*> commonSourceFiles; + if (!gtgt->GetConfigCommonSourceFiles(commonSourceFiles)) { return false; } // Add CMakeLists.txt file for user convenience. - this->AddXCodeProjBuildRule(gtgt, classes); + this->AddXCodeProjBuildRule(gtgt, commonSourceFiles); // Add the Info.plist we are about to generate for an App Bundle. if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) { std::string plist = this->ComputeInfoPListLocation(gtgt); cmSourceFile* sf = gtgt->Makefile->GetOrCreateSource( plist, true, cmSourceFileLocationKind::Known); - classes.push_back(sf); + commonSourceFiles.push_back(sf); } - std::sort(classes.begin(), classes.end(), cmSourceFilePathCompare()); + std::sort(commonSourceFiles.begin(), commonSourceFiles.end(), + cmSourceFilePathCompare()); gtgt->ComputeObjectMapping(); @@ -1176,11 +1406,15 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( std::vector<cmXCodeObject*> headerFiles; std::vector<cmXCodeObject*> resourceFiles; std::vector<cmXCodeObject*> sourceFiles; - for (auto sourceFile : classes) { + for (auto sourceFile : commonSourceFiles) { cmXCodeObject* xsf = this->CreateXCodeSourceFile( this->CurrentLocalGenerator, sourceFile, gtgt); - cmXCodeObject* fr = xsf->GetObject("fileRef"); - cmXCodeObject* filetype = fr->GetObject()->GetObject("explicitFileType"); + cmXCodeObject* fr = xsf->GetAttribute("fileRef"); + cmXCodeObject* filetype = + fr->GetObject()->GetAttribute("explicitFileType"); + if (!filetype) { + filetype = fr->GetObject()->GetAttribute("lastKnownFileType"); + } cmGeneratorTarget::SourceFileFlags tsFlags = gtgt->GetTargetSourceFileFlags(sourceFile); @@ -1275,7 +1509,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( using mapOfVectorOfSourceFiles = std::map<std::string, std::vector<cmSourceFile*>>; mapOfVectorOfSourceFiles bundleFiles; - for (auto sourceFile : classes) { + for (auto sourceFile : commonSourceFiles) { cmGeneratorTarget::SourceFileFlags tsFlags = gtgt->GetTargetSourceFileFlags(sourceFile); if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeMacContent) { @@ -1323,7 +1557,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( using mapOfVectorOfSourceFiles = std::map<std::string, std::vector<cmSourceFile*>>; mapOfVectorOfSourceFiles bundleFiles; - for (auto sourceFile : classes) { + for (auto sourceFile : commonSourceFiles) { cmGeneratorTarget::SourceFileFlags tsFlags = gtgt->GetTargetSourceFileFlags(sourceFile); if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeDeepResource) { @@ -1353,22 +1587,21 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( } } - // create framework build phase + // Always create Link Binary With Libraries build phase cmXCodeObject* frameworkBuildPhase = nullptr; - if (!externalObjFiles.empty()) { - frameworkBuildPhase = - this->CreateObject(cmXCodeObject::PBXFrameworksBuildPhase); - frameworkBuildPhase->SetComment("Frameworks"); - frameworkBuildPhase->AddAttribute("buildActionMask", - this->CreateString("2147483647")); - buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); - frameworkBuildPhase->AddAttribute("files", buildFiles); - for (auto& externalObjFile : externalObjFiles) { - buildFiles->AddObject(externalObjFile); - } - frameworkBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", - this->CreateString("0")); - } + frameworkBuildPhase = + this->CreateObject(cmXCodeObject::PBXFrameworksBuildPhase); + frameworkBuildPhase->SetComment("Frameworks"); + frameworkBuildPhase->AddAttribute("buildActionMask", + this->CreateString("2147483647")); + buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); + frameworkBuildPhase->AddAttribute("files", buildFiles); + // Add all collected .o files to this build phase + for (auto& externalObjFile : externalObjFiles) { + buildFiles->AddObject(externalObjFile); + } + frameworkBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", + this->CreateString("0")); // create list of build phases and create the Xcode target cmXCodeObject* buildPhases = this->CreateObject(cmXCodeObject::OBJECT_LIST); @@ -1437,7 +1670,7 @@ bool cmGlobalXCodeGenerator::IsHeaderFile(cmSourceFile* sf) sf->GetExtension()); } -cmXCodeObject* cmGlobalXCodeGenerator::CreateBuildPhase( +cmXCodeObject* cmGlobalXCodeGenerator::CreateLegacyRunScriptBuildPhase( const char* name, const char* name2, cmGeneratorTarget* target, const std::vector<cmCustomCommand>& commands) { @@ -1488,29 +1721,51 @@ void cmGlobalXCodeGenerator::CreateCustomCommands( postbuild.push_back(std::move(command)); } - std::vector<cmSourceFile*> classes; - if (!gtgt->GetConfigCommonSourceFiles(classes)) { - return; + cmXCodeObject* legacyCustomCommandsBuildPhase = nullptr; + cmXCodeObject* preBuildPhase = nullptr; + cmXCodeObject* preLinkPhase = nullptr; + cmXCodeObject* postBuildPhase = nullptr; + + if (this->XcodeBuildSystem >= BuildSystem::Twelve) { + // create prebuild phase + preBuildPhase = + this->CreateRunScriptBuildPhase("CMake PreBuild Rules", gtgt, prebuild); + // create prelink phase + preLinkPhase = + this->CreateRunScriptBuildPhase("CMake PreLink Rules", gtgt, prelink); + // create postbuild phase + postBuildPhase = this->CreateRunScriptBuildPhase("CMake PostBuild Rules", + gtgt, postbuild); + } else { + std::vector<cmSourceFile*> classes; + if (!gtgt->GetConfigCommonSourceFiles(classes)) { + return; + } + // add all the sources + std::vector<cmCustomCommand> commands; + auto& visited = this->CommandsVisited[gtgt]; + for (auto sourceFile : classes) { + if (sourceFile->GetCustomCommand() && + visited.insert(sourceFile).second) { + commands.push_back(*sourceFile->GetCustomCommand()); + if (this->XcodeBuildSystem >= BuildSystem::Twelve) { + this->CustomCommandRoots[sourceFile].insert(gtgt); + } + } + } + // create custom commands phase + legacyCustomCommandsBuildPhase = this->CreateLegacyRunScriptBuildPhase( + "CMake Rules", "cmakeRulesBuildPhase", gtgt, commands); + // create prebuild phase + preBuildPhase = this->CreateLegacyRunScriptBuildPhase( + "CMake PreBuild Rules", "preBuildCommands", gtgt, prebuild); + // create prelink phase + preLinkPhase = this->CreateLegacyRunScriptBuildPhase( + "CMake PreLink Rules", "preLinkCommands", gtgt, prelink); + // create postbuild phase + postBuildPhase = this->CreateLegacyRunScriptBuildPhase( + "CMake PostBuild Rules", "postBuildPhase", gtgt, postbuild); } - // add all the sources - std::vector<cmCustomCommand> commands; - for (auto sourceFile : classes) { - if (sourceFile->GetCustomCommand()) { - commands.push_back(*sourceFile->GetCustomCommand()); - } - } - // create prebuild phase - cmXCodeObject* cmakeRulesBuildPhase = this->CreateBuildPhase( - "CMake Rules", "cmakeRulesBuildPhase", gtgt, commands); - // create prebuild phase - cmXCodeObject* preBuildPhase = this->CreateBuildPhase( - "CMake PreBuild Rules", "preBuildCommands", gtgt, prebuild); - // create prelink phase - cmXCodeObject* preLinkPhase = this->CreateBuildPhase( - "CMake PreLink Rules", "preLinkCommands", gtgt, prelink); - // create postbuild phase - cmXCodeObject* postBuildPhase = this->CreateBuildPhase( - "CMake PostBuild Rules", "postBuildPhase", gtgt, postbuild); // The order here is the order they will be built in. // The order "headers, resources, sources" mimics a native project generated @@ -1519,8 +1774,11 @@ void cmGlobalXCodeGenerator::CreateCustomCommands( if (preBuildPhase) { buildPhases->AddObject(preBuildPhase); } - if (cmakeRulesBuildPhase) { - buildPhases->AddObject(cmakeRulesBuildPhase); + if (legacyCustomCommandsBuildPhase) { + buildPhases->AddObject(legacyCustomCommandsBuildPhase); + } + if (this->XcodeBuildSystem >= BuildSystem::Twelve) { + this->CreateRunScriptBuildPhases(buildPhases, gtgt); } if (headerBuildPhase) { buildPhases->AddObject(headerBuildPhase); @@ -1545,6 +1803,210 @@ void cmGlobalXCodeGenerator::CreateCustomCommands( } } +void cmGlobalXCodeGenerator::CreateRunScriptBuildPhases( + cmXCodeObject* buildPhases, cmGeneratorTarget const* gt) +{ + std::vector<cmSourceFile*> sources; + if (!gt->GetConfigCommonSourceFiles(sources)) { + return; + } + auto& visited = this->CommandsVisited[gt]; + for (auto sf : sources) { + this->CreateRunScriptBuildPhases(buildPhases, sf, gt, visited); + } +} + +void cmGlobalXCodeGenerator::CreateRunScriptBuildPhases( + cmXCodeObject* buildPhases, cmSourceFile const* sf, + cmGeneratorTarget const* gt, std::set<cmSourceFile const*>& visited) +{ + cmCustomCommand const* cc = sf->GetCustomCommand(); + if (cc && visited.insert(sf).second) { + this->CustomCommandRoots[sf].insert(gt); + if (std::vector<cmSourceFile*> const* depends = gt->GetSourceDepends(sf)) { + for (cmSourceFile const* di : *depends) { + this->CreateRunScriptBuildPhases(buildPhases, di, gt, visited); + } + } + cmXCodeObject* buildPhase = this->CreateRunScriptBuildPhase(sf, gt, *cc); + buildPhases->AddObject(buildPhase); + } +} + +cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase( + cmSourceFile const* sf, cmGeneratorTarget const* gt, + cmCustomCommand const& cc) +{ + std::set<std::string> allConfigInputs; + std::set<std::string> allConfigOutputs; + + std::string shellScript = "set -e\n"; + for (std::string const& configName : this->CurrentConfigurationTypes) { + cmCustomCommandGenerator ccg(cc, configName, this->CurrentLocalGenerator); + std::vector<std::string> realDepends; + realDepends.reserve(ccg.GetDepends().size()); + for (auto const& d : ccg.GetDepends()) { + std::string dep; + if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) { + realDepends.emplace_back(std::move(dep)); + } + } + + allConfigInputs.insert(realDepends.begin(), realDepends.end()); + allConfigOutputs.insert(ccg.GetByproducts().begin(), + ccg.GetByproducts().end()); + allConfigOutputs.insert(ccg.GetOutputs().begin(), ccg.GetOutputs().end()); + + shellScript = + cmStrCat(shellScript, R"(if test "$CONFIGURATION" = ")", configName, + "\"; then :\n", this->ConstructScript(ccg), "fi\n"); + } + + cmXCodeObject* buildPhase = + this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase, + cmStrCat(gt->GetName(), ':', sf->GetFullPath())); + buildPhase->AddAttribute("buildActionMask", + this->CreateString("2147483647")); + cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); + buildPhase->AddAttribute("files", buildFiles); + { + std::string name; + if (!allConfigOutputs.empty()) { + name = cmStrCat("Generate ", + this->RelativeToBinary(*allConfigOutputs.begin())); + } else { + name = sf->GetLocation().GetName(); + } + buildPhase->AddAttribute("name", this->CreateString(name)); + } + buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", + this->CreateString("0")); + buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh")); + buildPhase->AddAttribute("shellScript", this->CreateString(shellScript)); + buildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0")); + + bool symbolic = false; + { + cmXCodeObject* inputPaths = this->CreateObject(cmXCodeObject::OBJECT_LIST); + for (std::string const& i : allConfigInputs) { + inputPaths->AddUniqueObject(this->CreateString(i)); + if (!symbolic) { + if (cmSourceFile* isf = + gt->GetLocalGenerator()->GetMakefile()->GetSource( + i, cmSourceFileLocationKind::Known)) { + symbolic = isf->GetPropertyAsBool("SYMBOLIC"); + } + } + } + buildPhase->AddAttribute("inputPaths", inputPaths); + } + { + cmXCodeObject* outputPaths = + this->CreateObject(cmXCodeObject::OBJECT_LIST); + for (std::string const& o : allConfigOutputs) { + outputPaths->AddUniqueObject(this->CreateString(o)); + if (!symbolic) { + if (cmSourceFile* osf = + gt->GetLocalGenerator()->GetMakefile()->GetSource( + o, cmSourceFileLocationKind::Known)) { + symbolic = osf->GetPropertyAsBool("SYMBOLIC"); + } + } + } + buildPhase->AddAttribute("outputPaths", outputPaths); + } + if (symbolic) { + buildPhase->AddAttribute("alwaysOutOfDate", this->CreateString("1")); + } + + return buildPhase; +} + +cmXCodeObject* cmGlobalXCodeGenerator::CreateRunScriptBuildPhase( + std::string const& name, cmGeneratorTarget const* gt, + std::vector<cmCustomCommand> const& commands) +{ + if (commands.empty()) { + return nullptr; + } + + std::set<std::string> allConfigOutputs; + + std::string shellScript = "set -e\n"; + for (std::string const& configName : this->CurrentConfigurationTypes) { + shellScript = cmStrCat(shellScript, R"(if test "$CONFIGURATION" = ")", + configName, "\"; then :\n"); + for (cmCustomCommand const& cc : commands) { + cmCustomCommandGenerator ccg(cc, configName, + this->CurrentLocalGenerator); + shellScript = cmStrCat(shellScript, this->ConstructScript(ccg)); + allConfigOutputs.insert(ccg.GetByproducts().begin(), + ccg.GetByproducts().end()); + } + shellScript = cmStrCat(shellScript, "fi\n"); + } + + cmXCodeObject* buildPhase = + this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase, + cmStrCat(gt->GetName(), ':', name)); + buildPhase->AddAttribute("buildActionMask", + this->CreateString("2147483647")); + cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); + buildPhase->AddAttribute("files", buildFiles); + buildPhase->AddAttribute("name", this->CreateString(name)); + buildPhase->AddAttribute("runOnlyForDeploymentPostprocessing", + this->CreateString("0")); + buildPhase->AddAttribute("shellPath", this->CreateString("/bin/sh")); + buildPhase->AddAttribute("shellScript", this->CreateString(shellScript)); + buildPhase->AddAttribute("showEnvVarsInLog", this->CreateString("0")); + { + cmXCodeObject* outputPaths = + this->CreateObject(cmXCodeObject::OBJECT_LIST); + for (std::string const& o : allConfigOutputs) { + outputPaths->AddUniqueObject(this->CreateString(o)); + } + buildPhase->AddAttribute("outputPaths", outputPaths); + } + buildPhase->AddAttribute("alwaysOutOfDate", this->CreateString("1")); + + return buildPhase; +} + +namespace { +void ReplaceScriptVars(std::string& cmd) +{ + cmSystemTools::ReplaceString(cmd, "$(CONFIGURATION)", "$CONFIGURATION"); + cmSystemTools::ReplaceString(cmd, "$(EFFECTIVE_PLATFORM_NAME)", + "$EFFECTIVE_PLATFORM_NAME"); +} +} + +std::string cmGlobalXCodeGenerator::ConstructScript( + cmCustomCommandGenerator const& ccg) +{ + std::string script; + cmLocalGenerator* lg = this->CurrentLocalGenerator; + std::string wd = ccg.GetWorkingDirectory(); + if (wd.empty()) { + wd = lg->GetCurrentBinaryDirectory(); + } + wd = lg->ConvertToOutputFormat(wd, cmOutputConverter::SHELL); + ReplaceScriptVars(wd); + script = cmStrCat(script, " cd ", wd, "\n"); + for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) { + std::string cmd = ccg.GetCommand(c); + if (cmd.empty()) { + continue; + } + cmSystemTools::ReplaceString(cmd, "/./", "/"); + cmd = lg->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL); + ccg.AppendArguments(c, cmd); + ReplaceScriptVars(cmd); + script = cmStrCat(script, " ", cmd, '\n'); + } + return script; +} + // This function removes each occurrence of the flag and returns the last one // (i.e., the dominant flag in GCC) std::string cmGlobalXCodeGenerator::ExtractFlag(const char* flag, @@ -1705,6 +2167,15 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile( for (auto const& command : commands) { cmCustomCommandGenerator ccg(command, configName, this->CurrentLocalGenerator); + std::vector<std::string> realDepends; + realDepends.reserve(ccg.GetDepends().size()); + for (auto const& d : ccg.GetDepends()) { + std::string dep; + if (this->CurrentLocalGenerator->GetRealDependency(d, configName, dep)) { + realDepends.emplace_back(std::move(dep)); + } + } + if (ccg.GetNumberOfCommands() > 0) { makefileStream << "\n"; const std::vector<std::string>& outputs = ccg.GetOutputs(); @@ -1720,12 +2191,8 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile( // There are no outputs. Use the generated force rule name. makefileStream << tname[&ccg.GetCC()] << ": "; } - for (auto const& d : ccg.GetDepends()) { - std::string dep; - if (this->CurrentLocalGenerator->GetRealDependency(d, configName, - dep)) { - makefileStream << "\\\n" << this->ConvertToRelativeForMake(dep); - } + for (auto const& dep : realDepends) { + makefileStream << "\\\n" << this->ConvertToRelativeForMake(dep); } makefileStream << "\n"; @@ -1754,6 +2221,17 @@ void cmGlobalXCodeGenerator::CreateCustomRulesMakefile( ccg.AppendArguments(c, cmd); makefileStream << "\t" << cmd << "\n"; } + + // Symbolic inputs are not expected to exist, so add dummy rules. + for (auto const& dep : realDepends) { + if (cmSourceFile* dsf = + target->GetLocalGenerator()->GetMakefile()->GetSource( + dep, cmSourceFileLocationKind::Known)) { + if (dsf->GetPropertyAsBool("SYMBOLIC")) { + makefileStream << this->ConvertToRelativeForMake(dep) << ":\n"; + } + } + } } } } @@ -1781,7 +2259,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, cmXCodeObject* buildSettings, const std::string& configName) { - if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!gtgt->IsInBuildSystem()) { return; } @@ -2256,7 +2734,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, if (stdlib.size() > 8) { const auto cxxLibrary = stdlib.substr(8); if (language == "CXX" || - !buildSettings->GetObject("CLANG_CXX_LIBRARY")) { + !buildSettings->GetAttribute("CLANG_CXX_LIBRARY")) { buildSettings->AddAttribute("CLANG_CXX_LIBRARY", this->CreateString(cxxLibrary)); } @@ -2278,7 +2756,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, std::string flags = cflags[language] + " " + defFlags; if (language == "CXX" || language == "OBJCXX") { if (language == "CXX" || - !buildSettings->GetObject("OTHER_CPLUSPLUSFLAGS")) { + !buildSettings->GetAttribute("OTHER_CPLUSPLUSFLAGS")) { buildSettings->AddAttribute("OTHER_CPLUSPLUSFLAGS", this->CreateString(flags)); } @@ -2286,7 +2764,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, buildSettings->AddAttribute("IFORT_OTHER_FLAGS", this->CreateString(flags)); } else if (language == "C" || language == "OBJC") { - if (language == "C" || !buildSettings->GetObject("OTHER_CFLAGS")) { + if (language == "C" || !buildSettings->GetAttribute("OTHER_CFLAGS")) { buildSettings->AddAttribute("OTHER_CFLAGS", this->CreateString(flags)); } } else if (language == "Swift") { @@ -2438,8 +2916,8 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, cmXCodeObject* cmGlobalXCodeGenerator::CreateUtilityTarget( cmGeneratorTarget* gtgt) { - cmXCodeObject* shellBuildPhase = - this->CreateObject(cmXCodeObject::PBXShellScriptBuildPhase); + cmXCodeObject* shellBuildPhase = this->CreateObject( + cmXCodeObject::PBXShellScriptBuildPhase, gtgt->GetName()); shellBuildPhase->AddAttribute("buildActionMask", this->CreateString("2147483647")); cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST); @@ -2472,7 +2950,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateUtilityTarget( this->XCodeObjectMap[gtgt] = target; // Add source files without build rules for editing convenience. - if (gtgt->GetType() == cmStateEnums::UTILITY && + if (gtgt->GetType() != cmStateEnums::GLOBAL_TARGET && gtgt->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET) { std::vector<cmSourceFile*> sources; if (!gtgt->GetConfigCommonSourceFiles(sources)) { @@ -2614,7 +3092,7 @@ const char* cmGlobalXCodeGenerator::GetTargetProductType( cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeTarget( cmGeneratorTarget* gtgt, cmXCodeObject* buildPhases) { - if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!gtgt->IsInBuildSystem()) { return nullptr; } cmXCodeObject* target = this->CreateObject(cmXCodeObject::PBXNativeTarget); @@ -2667,15 +3145,40 @@ cmXCodeObject* cmGlobalXCodeGenerator::FindXCodeTarget( return i->second; } +std::string cmGlobalXCodeGenerator::GetObjectId(cmXCodeObject::PBXType ptype, + cm::string_view key) +{ + std::string objectId; + if (!key.empty()) { + cmCryptoHash hash(cmCryptoHash::AlgoSHA256); + hash.Initialize(); + hash.Append(&ptype, sizeof(ptype)); + hash.Append(key); + objectId = cmSystemTools::UpperCase(hash.FinalizeHex().substr(0, 24)); + } else { + char cUuid[40] = { 0 }; + CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); + CFStringRef s = CFUUIDCreateString(kCFAllocatorDefault, uuid); + CFStringGetCString(s, cUuid, sizeof(cUuid), kCFStringEncodingUTF8); + objectId = cUuid; + CFRelease(s); + CFRelease(uuid); + cmSystemTools::ReplaceString(objectId, "-", ""); + if (objectId.size() > 24) { + objectId = objectId.substr(0, 24); + } + } + return objectId; +} + std::string cmGlobalXCodeGenerator::GetOrCreateId(const std::string& name, const std::string& id) { std::string guidStoreName = cmStrCat(name, "_GUID_CMAKE"); - const char* storedGUID = - this->CMakeInstance->GetCacheDefinition(guidStoreName); + cmProp storedGUID = this->CMakeInstance->GetCacheDefinition(guidStoreName); if (storedGUID) { - return storedGUID; + return *storedGUID; } this->CMakeInstance->AddCacheEntry(guidStoreName, id.c_str(), @@ -2706,7 +3209,7 @@ void cmGlobalXCodeGenerator::AddDependTarget(cmXCodeObject* target, targetdep->AddAttribute("targetProxy", this->CreateObjectReference(container)); - cmXCodeObject* depends = target->GetObject("dependencies"); + cmXCodeObject* depends = target->GetAttribute("dependencies"); if (!depends) { cmSystemTools::Error( "target does not have dependencies attribute error.."); @@ -2718,33 +3221,60 @@ void cmGlobalXCodeGenerator::AddDependTarget(cmXCodeObject* target, void cmGlobalXCodeGenerator::AppendOrAddBuildSetting(cmXCodeObject* settings, const char* attribute, - const char* value) + cmXCodeObject* value) { if (settings) { - cmXCodeObject* attr = settings->GetObject(attribute); + cmXCodeObject* attr = settings->GetAttribute(attribute); if (!attr) { - settings->AddAttribute(attribute, this->CreateString(value)); + settings->AddAttribute(attribute, value); } else { - std::string oldValue = cmStrCat(attr->GetString(), ' ', value); - attr->SetString(oldValue); + if (value->GetType() != cmXCodeObject::OBJECT_LIST && + value->GetType() != cmXCodeObject::STRING) { + cmSystemTools::Error("Unsupported value type for appending: " + + std::string(attribute)); + return; + } + if (attr->GetType() == cmXCodeObject::OBJECT_LIST) { + if (value->GetType() == cmXCodeObject::OBJECT_LIST) { + for (auto* obj : value->GetObjectList()) { + attr->AddObject(obj); + } + } else { + attr->AddObject(value); + } + } else if (attr->GetType() == cmXCodeObject::STRING) { + if (value->GetType() == cmXCodeObject::OBJECT_LIST) { + // Add old value as a list item to new object list + // and replace the attribute with the new list + value->PrependObject(attr); + settings->AddAttribute(attribute, value); + } else { + std::string newValue = + cmStrCat(attr->GetString(), ' ', value->GetString()); + attr->SetString(newValue); + } + } else { + cmSystemTools::Error("Unsupported attribute type for appending: " + + std::string(attribute)); + } } } } void cmGlobalXCodeGenerator::AppendBuildSettingAttribute( - cmXCodeObject* target, const char* attribute, const char* value, + cmXCodeObject* target, const char* attribute, cmXCodeObject* value, const std::string& configName) { // There are multiple configurations. Add the setting to the // buildSettings of the configuration name given. cmXCodeObject* configurationList = - target->GetObject("buildConfigurationList")->GetObject(); + target->GetAttribute("buildConfigurationList")->GetObject(); cmXCodeObject* buildConfigs = - configurationList->GetObject("buildConfigurations"); + configurationList->GetAttribute("buildConfigurations"); for (auto obj : buildConfigs->GetObjectList()) { if (configName.empty() || - obj->GetObject("name")->GetString() == configName) { - cmXCodeObject* settings = obj->GetObject("buildSettings"); + obj->GetAttribute("name")->GetString() == configName) { + cmXCodeObject* settings = obj->GetAttribute("buildSettings"); this->AppendOrAddBuildSetting(settings, attribute, value); } } @@ -2757,7 +3287,7 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) cmSystemTools::Error("Error no target on xobject\n"); return; } - if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!gt->IsInBuildSystem()) { return; } @@ -2768,24 +3298,269 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) } } + // Separate libraries into ones that can be linked using "Link Binary With + // Libraries" build phase and the ones that can't. Only targets that build + // Apple bundles (.app, .framework, .bundle), executables and dylibs can use + // this feature and only targets that represent actual libraries (object, + // static, dynamic or bundle, excluding executables) will be used. These are + // limitations imposed by CMake use-cases - otherwise a lot of things break. + // The rest will be linked using linker flags (OTHER_LDFLAGS setting in Xcode + // project). + std::map<std::string, std::vector<cmComputeLinkInformation::Item const*>> + configItemMap; + auto addToLinkerArguments = + [&configItemMap](const std::string& configName, + cmComputeLinkInformation::Item const* libItemPtr) { + auto& linkVector = configItemMap[configName]; + if (std::find_if(linkVector.begin(), linkVector.end(), + [libItemPtr](cmComputeLinkInformation::Item const* p) { + return p == libItemPtr; + }) == linkVector.end()) { + linkVector.push_back(libItemPtr); + } + }; + std::vector<cmComputeLinkInformation::Item const*> linkPhaseTargetVector; + std::map<std::string, std::vector<std::string>> targetConfigMap; + using ConfigItemPair = + std::pair<std::string, cmComputeLinkInformation::Item const*>; + std::map<std::string, std::vector<ConfigItemPair>> targetItemMap; + std::map<std::string, std::vector<std::string>> targetProductNameMap; + bool useLinkPhase = false; + bool forceLinkPhase = false; + cmProp prop = + target->GetTarget()->GetProperty("XCODE_LINK_BUILD_PHASE_MODE"); + if (prop) { + if (*prop == "BUILT_ONLY") { + useLinkPhase = true; + } else if (*prop == "KNOWN_LOCATION") { + useLinkPhase = true; + forceLinkPhase = true; + } else if (*prop != "NONE") { + cmSystemTools::Error("Invalid value for XCODE_LINK_BUILD_PHASE_MODE: " + + *prop); + return; + } + } + for (auto const& configName : this->CurrentConfigurationTypes) { + cmComputeLinkInformation* cli = gt->GetLinkInformation(configName); + if (!cli) { + continue; + } + for (auto const& libItem : cli->GetItems()) { + // We want to put only static libraries, dynamic libraries, frameworks + // and bundles that are built from targets that are not imported in "Link + // Binary With Libraries" build phase. Except if the target property + // XCODE_LINK_BUILD_PHASE_MODE is KNOWN_LOCATION then all imported and + // non-target libraries will be added as well. + if (useLinkPhase && + (gt->GetType() == cmStateEnums::EXECUTABLE || + gt->GetType() == cmStateEnums::SHARED_LIBRARY || + gt->GetType() == cmStateEnums::MODULE_LIBRARY) && + ((libItem.Target && + (!libItem.Target->IsImported() || forceLinkPhase) && + (libItem.Target->GetType() == cmStateEnums::STATIC_LIBRARY || + libItem.Target->GetType() == cmStateEnums::SHARED_LIBRARY || + libItem.Target->GetType() == cmStateEnums::MODULE_LIBRARY || + libItem.Target->GetType() == cmStateEnums::UNKNOWN_LIBRARY)) || + (!libItem.Target && libItem.IsPath && forceLinkPhase))) { + std::string libName; + bool canUseLinkPhase = true; + if (libItem.Target) { + if (libItem.Target->GetType() == cmStateEnums::UNKNOWN_LIBRARY) { + canUseLinkPhase = canUseLinkPhase && forceLinkPhase; + } else { + // If a library target uses custom build output directory Xcode + // won't pick it up so we have to resort back to linker flags, but + // that's OK as long as the custom output dir is absolute path. + for (auto const& libConfigName : this->CurrentConfigurationTypes) { + canUseLinkPhase = canUseLinkPhase && + libItem.Target->UsesDefaultOutputDir( + libConfigName, cmStateEnums::RuntimeBinaryArtifact); + } + } + libName = libItem.Target->GetName(); + } else { + libName = cmSystemTools::GetFilenameName(libItem.Value.Value); + // We don't want all the possible files here, just standard libraries + const auto libExt = cmSystemTools::GetFilenameExtension(libName); + if (!IsLinkPhaseLibraryExtension(libExt)) { + canUseLinkPhase = false; + } + } + if (canUseLinkPhase) { + // Add unique configuration name to target-config map for later + // checks + auto& configVector = targetConfigMap[libName]; + if (std::find(configVector.begin(), configVector.end(), + configName) == configVector.end()) { + configVector.push_back(configName); + } + // Add a pair of config and item to target-item map + auto& itemVector = targetItemMap[libName]; + itemVector.emplace_back(ConfigItemPair(configName, &libItem)); + // Add product file-name to a lib-product map + auto productName = + cmSystemTools::GetFilenameName(libItem.Value.Value); + auto& productVector = targetProductNameMap[libName]; + if (std::find(productVector.begin(), productVector.end(), + productName) == productVector.end()) { + productVector.push_back(productName); + } + continue; + } + } + // Add this library item to a regular linker flag list + addToLinkerArguments(configName, &libItem); + } + } + + // Go through target library map and separate libraries that are linked + // in all configurations and produce only single product, from the rest. + // Only these will be linked through "Link Binary With Libraries" build + // phase. + for (auto const& targetLibConfigs : targetConfigMap) { + // Add this library to "Link Binary With Libraries" build phase if it's + // linked in all configurations and it has only one product name + auto& itemVector = targetItemMap[targetLibConfigs.first]; + auto& productVector = targetProductNameMap[targetLibConfigs.first]; + if (targetLibConfigs.second == this->CurrentConfigurationTypes && + productVector.size() == 1) { + // Add this library to "Link Binary With Libraries" list + linkPhaseTargetVector.push_back(itemVector[0].second); + } else { + for (auto const& libItem : targetItemMap[targetLibConfigs.first]) { + // Add this library item to a regular linker flag list + addToLinkerArguments(libItem.first, libItem.second); + } + } + } + + // Add libraries to "Link Binary With Libraries" build phase and collect + // their search paths. Xcode does not support per-configuration linking + // in this build phase so we don't have to do this for each configuration + // separately. + std::vector<std::string> linkSearchPaths; + std::vector<std::string> frameworkSearchPaths; + for (auto const& libItem : linkPhaseTargetVector) { + // Add target output directory as a library search path + std::string linkDir; + if (libItem->Target) { + linkDir = libItem->Target->GetLocationForBuild(); + } else { + linkDir = libItem->Value.Value; + } + linkDir = GetLibraryOrFrameworkPath(linkDir); + bool isFramework = cmSystemTools::IsPathToFramework(linkDir); + linkDir = cmSystemTools::GetParentDirectory(linkDir); + if (isFramework) { + if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(), + linkDir) == frameworkSearchPaths.end()) { + frameworkSearchPaths.push_back(linkDir); + } + } else { + if (std::find(linkSearchPaths.begin(), linkSearchPaths.end(), linkDir) == + linkSearchPaths.end()) { + linkSearchPaths.push_back(linkDir); + } + } + // Add target dependency + if (libItem->Target && !libItem->Target->IsImported()) { + for (auto const& configName : this->CurrentConfigurationTypes) { + target->AddDependTarget(configName, libItem->Target->GetName()); + } + } + // Get the library target + auto* libTarget = FindXCodeTarget(libItem->Target); + cmXCodeObject* buildFile; + if (!libTarget) { + if (libItem->IsPath) { + // Get or create a direct file ref in the root project + auto cleanPath = libItem->Value.Value; + if (cmSystemTools::FileIsFullPath(cleanPath)) { + // Some arguments are reported as paths, but they are actually not, + // so we can't collapse them, and neither can we collapse relative + // paths + cleanPath = cmSystemTools::CollapseFullPath(cleanPath); + } + auto it = this->ExternalLibRefs.find(cleanPath); + if (it == this->ExternalLibRefs.end()) { + buildFile = CreateXCodeBuildFileFromPath(cleanPath, gt, "", nullptr); + if (!buildFile) { + // Add this library item back to a regular linker flag list + for (const auto& conf : configItemMap) { + addToLinkerArguments(conf.first, libItem); + } + continue; + } + this->ExternalLibRefs.emplace(cleanPath, buildFile); + } else { + buildFile = it->second; + } + } else { + // Add this library item back to a regular linker flag list + for (const auto& conf : configItemMap) { + addToLinkerArguments(conf.first, libItem); + } + continue; + } + } else { + // Add the target output file as a build reference for other targets + // to link against + auto* fileRefObject = libTarget->GetAttribute("productReference"); + if (!fileRefObject) { + // Add this library item back to a regular linker flag list + for (const auto& conf : configItemMap) { + addToLinkerArguments(conf.first, libItem); + } + continue; + } + auto it = FileRefToBuildFileMap.find(fileRefObject); + if (it == FileRefToBuildFileMap.end()) { + buildFile = this->CreateObject(cmXCodeObject::PBXBuildFile); + buildFile->AddAttribute("fileRef", fileRefObject); + FileRefToBuildFileMap[fileRefObject] = buildFile; + } else { + buildFile = it->second; + } + } + // Add this reference to current target + auto* buildPhases = target->GetAttribute("buildPhases"); + if (!buildPhases) { + cmSystemTools::Error("Missing buildPhase of target"); + continue; + } + auto* frameworkBuildPhase = + buildPhases->GetObject(cmXCodeObject::PBXFrameworksBuildPhase); + if (!frameworkBuildPhase) { + cmSystemTools::Error("Missing PBXFrameworksBuildPhase of buildPhase"); + continue; + } + auto* buildFiles = frameworkBuildPhase->GetAttribute("files"); + if (!buildFiles) { + cmSystemTools::Error("Missing files of PBXFrameworksBuildPhase"); + continue; + } + if (buildFile && !buildFiles->HasObject(buildFile)) { + buildFiles->AddObject(buildFile); + } + } + // Loop over configuration types and set per-configuration info. for (auto const& configName : this->CurrentConfigurationTypes) { { // Add object library contents as link flags. - std::string linkObjs; - const char* sep = ""; + BuildObjectListOrString libSearchPaths(this, true); std::vector<cmSourceFile const*> objs; gt->GetExternalObjects(objs, configName); for (auto sourceFile : objs) { if (sourceFile->GetObjectLibrary().empty()) { continue; } - linkObjs += sep; - sep = " "; - linkObjs += this->XCodeEscapePath(sourceFile->GetFullPath()); + libSearchPaths.Add(this->XCodeEscapePath(sourceFile->GetFullPath())); } this->AppendBuildSettingAttribute( - target, this->GetTargetLinkFlagsVar(gt), linkObjs.c_str(), configName); + target, this->GetTargetLinkFlagsVar(gt), libSearchPaths.CreateList(), + configName); } // Skip link information for object libraries. @@ -2795,55 +3570,98 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) } // Compute the link library and directory information. - cmComputeLinkInformation* pcli = gt->GetLinkInformation(configName); - if (!pcli) { + cmComputeLinkInformation* cli = gt->GetLinkInformation(configName); + if (!cli) { continue; } - cmComputeLinkInformation& cli = *pcli; // Add dependencies directly on library files. - for (auto const& libDep : cli.GetDepends()) { + for (auto const& libDep : cli->GetDepends()) { target->AddDependLibrary(configName, libDep); } // add the library search paths { + BuildObjectListOrString libSearchPaths(this, true); std::string linkDirs; - for (auto const& libDir : cli.GetDirectories()) { + for (auto const& libDir : cli->GetDirectories()) { if (!libDir.empty() && libDir != "/usr/lib") { - // Now add the same one but append - // $(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) to it: - linkDirs += " "; - linkDirs += this->XCodeEscapePath( - libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"); - linkDirs += " "; - linkDirs += this->XCodeEscapePath(libDir); + libSearchPaths.Add(this->XCodeEscapePath( + libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)")); + libSearchPaths.Add(this->XCodeEscapePath(libDir)); } } + // Add previously collected paths where to look for libraries + // that were added to "Link Binary With Libraries" + for (auto& libDir : linkSearchPaths) { + libSearchPaths.Add(this->XCodeEscapePath(libDir)); + } + // Add paths defined in project-wide build settings + libSearchPaths.Add("$(inherited)"); this->AppendBuildSettingAttribute(target, "LIBRARY_SEARCH_PATHS", - linkDirs.c_str(), configName); + libSearchPaths.CreateList(), + configName); + } + + // add framework search paths + { + BuildObjectListOrString fwSearchPaths(this, true); + // Add previously collected paths where to look for frameworks + // that were added to "Link Binary With Libraries" + for (auto& fwDir : frameworkSearchPaths) { + fwSearchPaths.Add(this->XCodeEscapePath(fwDir)); + } + // Add paths defined in project-wide build settings + fwSearchPaths.Add("$(inherited)"); + this->AppendBuildSettingAttribute(target, "FRAMEWORK_SEARCH_PATHS", + fwSearchPaths.CreateList(), + configName); } - // now add the link libraries + // now add the left-over link libraries { - std::string linkLibs; - const char* sep = ""; - for (auto const& libName : cli.GetItems()) { - linkLibs += sep; - sep = " "; + BuildObjectListOrString libPaths(this, true); + for (auto const& libItem : configItemMap[configName]) { + auto const& libName = *libItem; if (libName.IsPath) { - linkLibs += this->XCodeEscapePath(libName.Value.Value); + auto cleanPath = libName.Value.Value; + if (cmSystemTools::FileIsFullPath(cleanPath)) { + cleanPath = cmSystemTools::CollapseFullPath(cleanPath); + } + const auto libPath = GetLibraryOrFrameworkPath(cleanPath); + if (cmSystemTools::StringEndsWith(libPath.c_str(), ".framework")) { + const auto fwName = + cmSystemTools::GetFilenameWithoutExtension(libPath); + const auto fwDir = cmSystemTools::GetParentDirectory(libPath); + libPaths.Add("-F " + this->XCodeEscapePath(fwDir)); + libPaths.Add("-framework " + fwName); + } else { + libPaths.Add(this->XCodeEscapePath(cleanPath)); + } + if ((!libName.Target || libName.Target->IsImported()) && + IsLinkPhaseLibraryExtension(libPath)) { + // Create file reference for embedding + auto it = this->ExternalLibRefs.find(cleanPath); + if (it == this->ExternalLibRefs.end()) { + auto* buildFile = + this->CreateXCodeBuildFileFromPath(cleanPath, gt, "", nullptr); + if (buildFile) { + this->ExternalLibRefs.emplace(cleanPath, buildFile); + } + } + } } else if (!libName.Target || libName.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - linkLibs += libName.Value.Value; + libPaths.Add(libName.Value.Value); } if (libName.Target && !libName.Target->IsImported()) { target->AddDependTarget(configName, libName.Target->GetName()); } } - this->AppendBuildSettingAttribute( - target, this->GetTargetLinkFlagsVar(gt), linkLibs.c_str(), configName); + this->AppendBuildSettingAttribute(target, + this->GetTargetLinkFlagsVar(gt), + libPaths.CreateList(), configName); } } } @@ -2859,13 +3677,9 @@ bool cmGlobalXCodeGenerator::CreateGroups( // end up with (empty anyhow) ZERO_CHECK, install, or test source // groups: // - if (gtgt->GetType() == cmStateEnums::GLOBAL_TARGET) { - continue; - } - if (gtgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - continue; - } - if (gtgt->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET) { + if (!gtgt->IsInBuildSystem() || + gtgt->GetType() == cmStateEnums::GLOBAL_TARGET || + gtgt->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET) { continue; } @@ -2915,7 +3729,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreatePBXGroup(cmXCodeObject* parent, { cmXCodeObject* parentChildren = nullptr; if (parent) { - parentChildren = parent->GetObject("children"); + parentChildren = parent->GetAttribute("children"); } cmXCodeObject* group = this->CreateObject(cmXCodeObject::PBXGroup); cmXCodeObject* groupChildren = @@ -3009,6 +3823,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( this->ClearXCodeObjects(); this->RootObject = nullptr; this->MainGroupChildren = nullptr; + this->FrameworkGroup = nullptr; cmXCodeObject* group = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); group->AddAttribute("COPY_PHASE_STRIP", this->CreateString("NO")); cmXCodeObject* listObjs = this->CreateObject(cmXCodeObject::OBJECT_LIST); @@ -3043,6 +3858,15 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( productGroup->AddAttribute("children", productGroupChildren); this->MainGroupChildren->AddObject(productGroup); + this->FrameworkGroup = this->CreateObject(cmXCodeObject::PBXGroup); + this->FrameworkGroup->AddAttribute("name", this->CreateString("Frameworks")); + this->FrameworkGroup->AddAttribute("sourceTree", + this->CreateString("<group>")); + cmXCodeObject* frameworkGroupChildren = + this->CreateObject(cmXCodeObject::OBJECT_LIST); + this->FrameworkGroup->AddAttribute("children", frameworkGroupChildren); + this->MainGroupChildren->AddObject(this->FrameworkGroup); + this->RootObject = this->CreateObject(cmXCodeObject::PBXProject); this->RootObject->SetComment("Project object"); @@ -3106,12 +3930,11 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( this->CreateString(defaultConfigName)); cmXCodeObject* buildSettings = this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); - const char* sysroot = - this->CurrentMakefile->GetDefinition("CMAKE_OSX_SYSROOT"); - const char* deploymentTarget = + cmProp sysroot = this->CurrentMakefile->GetDefinition("CMAKE_OSX_SYSROOT"); + cmProp deploymentTarget = this->CurrentMakefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET"); if (sysroot) { - buildSettings->AddAttribute("SDKROOT", this->CreateString(sysroot)); + buildSettings->AddAttribute("SDKROOT", this->CreateString(*sysroot)); } // recompute this as it may have been changed since enable language this->ComputeArchitectures(this->CurrentMakefile); @@ -3121,8 +3944,8 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( buildSettings->AddAttribute("ONLY_ACTIVE_ARCH", this->CreateString("YES")); // When targeting macOS, use only the host architecture. if (this->SystemName == "Darwin"_s && - (!sysroot || !*sysroot || - cmSystemTools::LowerCase(sysroot).find("macos") != + (!cmNonempty(sysroot) || + cmSystemTools::LowerCase(*sysroot).find("macos") != std::string::npos)) { buildSettings->AddAttribute("ARCHS", this->CreateString("$(NATIVE_ARCH_ACTUAL)")); @@ -3131,9 +3954,9 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( // Tell Xcode to use ARCHS (ONLY_ACTIVE_ARCH defaults to NO). buildSettings->AddAttribute("ARCHS", this->CreateString(archs)); } - if (deploymentTarget && *deploymentTarget) { + if (cmNonempty(deploymentTarget)) { buildSettings->AddAttribute(GetDeploymentPlatform(root->GetMakefile()), - this->CreateString(deploymentTarget)); + this->CreateString(*deploymentTarget)); } if (!this->GeneratorToolset.empty()) { buildSettings->AddAttribute("GCC_VERSION", @@ -3141,9 +3964,9 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( } if (this->GetLanguageEnabled("Swift")) { std::string swiftVersion; - if (const char* vers = this->CurrentMakefile->GetDefinition( + if (cmProp vers = this->CurrentMakefile->GetDefinition( "CMAKE_Swift_LANGUAGE_VERSION")) { - swiftVersion = vers; + swiftVersion = *vers; } else if (this->XcodeVersion >= 102) { swiftVersion = "4.0"; } else if (this->XcodeVersion >= 83) { @@ -3194,17 +4017,42 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( if (!this->CreateXCodeTargets(generator, targets)) { return false; } + for (auto const& ccRoot : this->CustomCommandRoots) { + if (ccRoot.second.size() > 1) { + std::string e = "The custom command "; + std::vector<std::string> const& outputs = + ccRoot.first->GetCustomCommand()->GetOutputs(); + if (!outputs.empty()) { + e = cmStrCat(e, "generating\n ", outputs[0]); + } else { + e = cmStrCat(e, "driven by\n ", ccRoot.first->GetFullPath()); + } + e = cmStrCat(e, "\nis attached to multiple targets:"); + for (cmGeneratorTarget const* gt : ccRoot.second) { + e = cmStrCat(e, "\n ", gt->GetName()); + } + e = cmStrCat( + e, + "\nbut none of these is a common dependency of the other(s). " + "This is not allowed by the Xcode \"new build system\"."); + generator->IssueMessage(MessageType::FATAL_ERROR, e); + return false; + } + } + this->CustomCommandRoots.clear(); } // loop over all targets and add link and depend info for (auto t : targets) { this->AddDependAndLinkInformation(t); } - this->CreateXCodeDependHackTarget(targets); + if (this->XcodeBuildSystem == BuildSystem::One) { + this->CreateXCodeDependHackMakefile(targets); + } // now add all targets to the root object cmXCodeObject* allTargets = this->CreateObject(cmXCodeObject::OBJECT_LIST); for (auto t : targets) { allTargets->AddObject(t); - cmXCodeObject* productRef = t->GetObject("productReference"); + cmXCodeObject* productRef = t->GetAttribute("productReference"); if (productRef) { productGroupChildren->AddObject(productRef->GetObject()); } @@ -3226,17 +4074,21 @@ std::string cmGlobalXCodeGenerator::GetObjectsDirectory( void cmGlobalXCodeGenerator::ComputeArchitectures(cmMakefile* mf) { this->Architectures.clear(); - const char* sysroot = mf->GetDefinition("CMAKE_OSX_SYSROOT"); + cmProp sysroot = mf->GetDefinition("CMAKE_OSX_SYSROOT"); if (sysroot) { mf->GetDefExpandList("CMAKE_OSX_ARCHITECTURES", this->Architectures); } if (this->Architectures.empty()) { + mf->GetDefExpandList("_CMAKE_APPLE_ARCHS_DEFAULT", this->Architectures); + } + + if (this->Architectures.empty()) { // With no ARCHS we use ONLY_ACTIVE_ARCH and possibly a // platform-specific default ARCHS placeholder value. // Look up the arch that Xcode chooses in this case. - if (const char* arch = mf->GetDefinition("CMAKE_XCODE_ARCHS")) { - this->ObjectDirArchDefault = arch; + if (cmProp arch = mf->GetDefinition("CMAKE_XCODE_ARCHS")) { + this->ObjectDirArchDefault = *arch; // We expect only one arch but choose the first just in case. std::string::size_type pos = this->ObjectDirArchDefault.find(';'); if (pos != std::string::npos) { @@ -3259,7 +4111,7 @@ void cmGlobalXCodeGenerator::ComputeObjectDirArch(cmMakefile* mf) } } -void cmGlobalXCodeGenerator::CreateXCodeDependHackTarget( +void cmGlobalXCodeGenerator::CreateXCodeDependHackMakefile( std::vector<cmXCodeObject*>& targets) { cmGeneratedFileStream makefileStream(this->CurrentXCodeHackMakefile); @@ -3488,9 +4340,16 @@ void cmGlobalXCodeGenerator::OutputXCodeWorkspaceSettings( xout.StartElement("dict"); if (this->XcodeVersion >= 100) { xout.Element("key", "BuildSystemType"); - xout.Element("string", "Original"); - xout.Element("key", "DisableBuildSystemDeprecationWarning"); - xout.Element("true"); + switch (this->XcodeBuildSystem) { + case BuildSystem::One: + xout.Element("string", "Original"); + xout.Element("key", "DisableBuildSystemDeprecationWarning"); + xout.Element("true"); + break; + case BuildSystem::Twelve: + xout.Element("string", "Latest"); + break; + } } if (hasGeneratedSchemes) { xout.Element("key", @@ -3602,9 +4461,9 @@ std::string cmGlobalXCodeGenerator::LookupFlags( { if (!varNameLang.empty()) { std::string varName = cmStrCat(varNamePrefix, varNameLang, varNameSuffix); - if (const char* varValue = this->CurrentMakefile->GetDefinition(varName)) { - if (*varValue) { - return varValue; + if (cmProp varValue = this->CurrentMakefile->GetDefinition(varName)) { + if (!varValue->empty()) { + return *varValue; } } } diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index e2d1b3a82a..8ff6846e96 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGlobalXCodeGenerator_h -#define cmGlobalXCodeGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,10 +11,13 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmGlobalGenerator.h" #include "cmXCodeObject.h" class cmCustomCommand; +class cmCustomCommandGenerator; class cmGeneratorTarget; class cmGlobalGeneratorFactory; class cmLocalGenerator; @@ -28,7 +30,7 @@ struct cmDocumentationEntry; /** \class cmGlobalXCodeGenerator * \brief Write a Unix makefiles. * - * cmGlobalXCodeGenerator manages UNIX build process for a tree + * cmGlobalXCodeGenerator manages Xcode build process for a tree */ class cmGlobalXCodeGenerator : public cmGlobalGenerator { @@ -114,13 +116,26 @@ public: cmMakefile* mf) override; void AppendFlag(std::string& flags, std::string const& flag) const; + enum class BuildSystem + { + One = 1, + Twelve = 12, + }; + protected: void AddExtraIDETargets() override; - void ComputeTargetOrder(); - void ComputeTargetOrder(cmGeneratorTarget const* gt, size_t& index); void Generate() override; + FindMakeProgramStage GetFindMakeProgramStage() const override + { + return FindMakeProgramStage::Early; + } + private: + bool ParseGeneratorToolset(std::string const& ts, cmMakefile* mf); + bool ProcessGeneratorToolsetField(std::string const& key, + std::string const& value, cmMakefile* mf); + cmXCodeObject* CreateOrGetPBXGroup(cmGeneratorTarget* gtgt, cmSourceGroup* sg); cmXCodeObject* CreatePBXGroup(cmXCodeObject* parent, @@ -149,11 +164,13 @@ private: const std::string& configName); cmXCodeObject* FindXCodeTarget(const cmGeneratorTarget*); + std::string GetObjectId(cmXCodeObject::PBXType ptype, cm::string_view key); std::string GetOrCreateId(const std::string& name, const std::string& id); // create cmXCodeObject from these functions so that memory can be managed // correctly. All objects created are stored in this->XCodeObjects. - cmXCodeObject* CreateObject(cmXCodeObject::PBXType ptype); + cmXCodeObject* CreateObject(cmXCodeObject::PBXType ptype, + cm::string_view key = {}); cmXCodeObject* CreateObject(cmXCodeObject::Type type); cmXCodeObject* CreateString(const std::string& s); cmXCodeObject* CreateObjectReference(cmXCodeObject*); @@ -168,9 +185,9 @@ private: std::string AddConfigurations(cmXCodeObject* target, cmGeneratorTarget* gtgt); void AppendOrAddBuildSetting(cmXCodeObject* settings, const char* attr, - const char* value); + cmXCodeObject* value); void AppendBuildSettingAttribute(cmXCodeObject* target, const char* attr, - const char* value, + cmXCodeObject* value, const std::string& configName); cmXCodeObject* CreateUtilityTarget(cmGeneratorTarget* gtgt); void AddDependAndLinkInformation(cmXCodeObject* target); @@ -204,10 +221,10 @@ private: cmGeneratorTarget* target, const std::string& lang, cmSourceFile* sf); - cmXCodeObject* CreateXCodeSourceFileFromPath(const std::string& fullpath, - cmGeneratorTarget* target, - const std::string& lang, - cmSourceFile* sf); + cmXCodeObject* CreateXCodeBuildFileFromPath(const std::string& fullpath, + cmGeneratorTarget* target, + const std::string& lang, + cmSourceFile* sf); cmXCodeObject* CreateXCodeFileReference(cmSourceFile* sf, cmGeneratorTarget* target); cmXCodeObject* CreateXCodeSourceFile(cmLocalGenerator* gen, cmSourceFile* sf, @@ -219,14 +236,27 @@ private: std::vector<cmXCodeObject*>&); bool IsHeaderFile(cmSourceFile*); void AddDependTarget(cmXCodeObject* target, cmXCodeObject* dependTarget); - void CreateXCodeDependHackTarget(std::vector<cmXCodeObject*>& targets); + void CreateXCodeDependHackMakefile(std::vector<cmXCodeObject*>& targets); bool SpecialTargetEmitted(std::string const& tname); void SetGenerationRoot(cmLocalGenerator* root); void AddExtraTargets(cmLocalGenerator* root, std::vector<cmLocalGenerator*>& gens); - cmXCodeObject* CreateBuildPhase(const char* name, const char* name2, - cmGeneratorTarget* target, - const std::vector<cmCustomCommand>&); + cmXCodeObject* CreateLegacyRunScriptBuildPhase( + const char* name, const char* name2, cmGeneratorTarget* target, + const std::vector<cmCustomCommand>&); + void CreateRunScriptBuildPhases(cmXCodeObject* buildPhases, + cmGeneratorTarget const* gt); + void CreateRunScriptBuildPhases(cmXCodeObject* buildPhases, + cmSourceFile const* sf, + cmGeneratorTarget const* gt, + std::set<cmSourceFile const*>& visited); + cmXCodeObject* CreateRunScriptBuildPhase(cmSourceFile const* sf, + cmGeneratorTarget const* gt, + cmCustomCommand const& cc); + cmXCodeObject* CreateRunScriptBuildPhase( + std::string const& name, cmGeneratorTarget const* gt, + std::vector<cmCustomCommand> const& commands); + std::string ConstructScript(cmCustomCommandGenerator const& ccg); void CreateReRunCMakeFile(cmLocalGenerator* root, std::vector<cmLocalGenerator*> const& gens); @@ -257,6 +287,8 @@ protected: std::vector<std::unique_ptr<cmXCodeObject>> XCodeObjects; cmXCodeObject* RootObject; + BuildSystem XcodeBuildSystem = BuildSystem::One; + private: std::string const& GetXcodeBuildCommand(); std::string FindXcodeBuildCommand(); @@ -282,6 +314,7 @@ private: std::string PostBuildMakeTarget(std::string const& tName, std::string const& configName); cmXCodeObject* MainGroupChildren; + cmXCodeObject* FrameworkGroup; cmMakefile* CurrentMakefile; cmLocalGenerator* CurrentLocalGenerator; std::vector<std::string> CurrentConfigurationTypes; @@ -295,13 +328,17 @@ private: std::map<std::string, cmXCodeObject*> GroupNameMap; std::map<std::string, cmXCodeObject*> TargetGroup; std::map<std::string, cmXCodeObject*> FileRefs; + std::map<std::string, cmXCodeObject*> ExternalLibRefs; std::map<cmGeneratorTarget const*, cmXCodeObject*> XCodeObjectMap; + std::map<cmXCodeObject*, cmXCodeObject*> FileRefToBuildFileMap; std::vector<std::string> Architectures; std::string ObjectDirArchDefault; std::string ObjectDirArch; std::string SystemName; std::string GeneratorToolset; - std::map<cmGeneratorTarget const*, size_t> TargetOrderIndex; + std::vector<std::string> EnabledLangs; + std::map<cmGeneratorTarget const*, std::set<cmSourceFile const*>> + CommandsVisited; + std::map<cmSourceFile const*, std::set<cmGeneratorTarget const*>> + CustomCommandRoots; }; - -#endif diff --git a/Source/cmGraphAdjacencyList.h b/Source/cmGraphAdjacencyList.h index 4e1f1284ff..fe9fbe221a 100644 --- a/Source/cmGraphAdjacencyList.h +++ b/Source/cmGraphAdjacencyList.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmGraphAdjacencyList_h -#define cmGraphAdjacencyList_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -48,5 +47,3 @@ struct cmGraphNodeList : public std::vector<int> struct cmGraphAdjacencyList : public std::vector<cmGraphEdgeList> { }; - -#endif diff --git a/Source/cmGraphVizWriter.cxx b/Source/cmGraphVizWriter.cxx index c23156d667..cf4ba93c54 100644 --- a/Source/cmGraphVizWriter.cxx +++ b/Source/cmGraphVizWriter.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGraphVizWriter.h" +#include <algorithm> #include <cctype> #include <iostream> #include <memory> @@ -16,6 +17,7 @@ #include "cmLinkItem.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" @@ -125,14 +127,6 @@ cmGraphVizWriter::cmGraphVizWriter(std::string const& fileName, cmGraphVizWriter::~cmGraphVizWriter() { this->WriteFooter(this->GlobalFileStream); - - for (auto& fileStream : this->PerTargetFileStreams) { - this->WriteFooter(*fileStream.second); - } - - for (auto& fileStream : this->TargetDependersFileStreams) { - this->WriteFooter(*fileStream.second); - } } void cmGraphVizWriter::VisitGraph(std::string const&) @@ -151,20 +145,10 @@ void cmGraphVizWriter::OnItem(cmLinkItem const& item) ++NextNodeId; this->WriteNode(this->GlobalFileStream, item); - - if (this->GeneratePerTarget) { - this->CreateTargetFile(this->PerTargetFileStreams, item); - } - - if (this->GenerateDependers) { - this->CreateTargetFile(this->TargetDependersFileStreams, item, - ".dependers"); - } } -void cmGraphVizWriter::CreateTargetFile(FileStreamMap& fileStreamMap, - cmLinkItem const& item, - std::string const& fileNameSuffix) +std::unique_ptr<cmGeneratedFileStream> cmGraphVizWriter::CreateTargetFile( + cmLinkItem const& item, std::string const& fileNameSuffix) { auto const pathSafeItemName = PathSafeString(item.AsStr()); auto const perTargetFileName = @@ -175,7 +159,7 @@ void cmGraphVizWriter::CreateTargetFile(FileStreamMap& fileStreamMap, this->WriteHeader(*perTargetFileStream, item.AsStr()); this->WriteNode(*perTargetFileStream, item); - fileStreamMap.emplace(item.AsStr(), std::move(perTargetFileStream)); + return perTargetFileStream; } void cmGraphVizWriter::OnDirectLink(cmLinkItem const& depender, @@ -246,9 +230,9 @@ void cmGraphVizWriter::ReadSettings( #define __set_if_set(var, cmakeDefinition) \ do { \ - const char* value = mf.GetDefinition(cmakeDefinition); \ + cmProp value = mf.GetDefinition(cmakeDefinition); \ if (value) { \ - (var) = value; \ + (var) = *value; \ } \ } while (false) @@ -258,9 +242,9 @@ void cmGraphVizWriter::ReadSettings( #define __set_bool_if_set(var, cmakeDefinition) \ do { \ - const char* value = mf.GetDefinition(cmakeDefinition); \ + cmProp value = mf.GetDefinition(cmakeDefinition); \ if (value) { \ - (var) = mf.IsOn(cmakeDefinition); \ + (var) = cmIsOn(*value); \ } \ } while (false) @@ -323,13 +307,12 @@ void cmGraphVizWriter::Write() } if (this->GeneratePerTarget) { - WritePerTargetConnections<DependeesDir>(PerTargetConnections, - PerTargetFileStreams); + WritePerTargetConnections<DependeesDir>(PerTargetConnections); } if (this->GenerateDependers) { WritePerTargetConnections<DependersDir>(TargetDependersConnections, - TargetDependersFileStreams); + ".dependers"); } } @@ -368,7 +351,7 @@ void cmGraphVizWriter::FindAllConnections(const ConnectionsMap& connectionMap, template <typename DirFunc> void cmGraphVizWriter::WritePerTargetConnections( - const ConnectionsMap& connections, const FileStreamMap& streams) + const ConnectionsMap& connections, const std::string& fileNameSuffix) { // the per target connections must be extended by indirect dependencies ConnectionsMap extendedConnections; @@ -387,7 +370,9 @@ void cmGraphVizWriter::WritePerTargetConnections( } const Connections& cons = conPerTarget.second; - auto fileStream = streams.at(rootItem.AsStr()).get(); + + std::unique_ptr<cmGeneratedFileStream> fileStream = + this->CreateTargetFile(rootItem, fileNameSuffix); for (const Connection& con : cons) { const cmLinkItem& src = DirFunc::src(con); @@ -395,6 +380,8 @@ void cmGraphVizWriter::WritePerTargetConnections( this->WriteNode(*fileStream, con.dst); this->WriteConnection(*fileStream, src, dst, con.scopeType); } + + this->WriteFooter(*fileStream); } } @@ -567,16 +554,23 @@ bool cmGraphVizWriter::TargetTypeEnabled( std::string cmGraphVizWriter::ItemNameWithAliases( std::string const& itemName) const { - auto nameWithAliases = itemName; - + std::vector<std::string> items; for (auto const& lg : this->GlobalGenerator->GetLocalGenerators()) { for (auto const& aliasTargets : lg->GetMakefile()->GetAliasTargets()) { if (aliasTargets.second == itemName) { - nameWithAliases += "\\n(" + aliasTargets.first + ")"; + items.push_back(aliasTargets.first); } } } + std::sort(items.begin(), items.end()); + items.erase(std::unique(items.begin(), items.end()), items.end()); + + auto nameWithAliases = itemName; + for(auto const& item : items) { + nameWithAliases += "\\n(" + item + ")"; + } + return nameWithAliases; } diff --git a/Source/cmGraphVizWriter.h b/Source/cmGraphVizWriter.h index 9766068a2a..0912fc8b1d 100644 --- a/Source/cmGraphVizWriter.h +++ b/Source/cmGraphVizWriter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef CMGRAPHVIZWRITER_H -#define CMGRAPHVIZWRITER_H +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -46,9 +45,6 @@ public: void Write(); private: - using FileStreamMap = - std::map<std::string, std::unique_ptr<cmGeneratedFileStream>>; - struct Connection { Connection(cmLinkItem s, cmLinkItem d, std::string scope) @@ -76,8 +72,8 @@ private: void WriteNode(cmGeneratedFileStream& fs, cmLinkItem const& item); - void CreateTargetFile(FileStreamMap& fileStreamMap, cmLinkItem const& target, - std::string const& fileNameSuffix = ""); + std::unique_ptr<cmGeneratedFileStream> CreateTargetFile( + cmLinkItem const& target, std::string const& fileNameSuffix = ""); void WriteConnection(cmGeneratedFileStream& fs, cmLinkItem const& dependerTargetName, @@ -95,7 +91,7 @@ private: template <typename DirFunc> void WritePerTargetConnections(const ConnectionsMap& connections, - const FileStreamMap& streams); + const std::string& fileNameSuffix = ""); bool ItemExcluded(cmLinkItem const& item); bool ItemNameFilteredOut(std::string const& itemName); @@ -111,8 +107,6 @@ private: std::string FileName; cmGeneratedFileStream GlobalFileStream; - FileStreamMap PerTargetFileStreams; - FileStreamMap TargetDependersFileStreams; ConnectionsMap PerTargetConnections; ConnectionsMap TargetDependersConnections; @@ -141,5 +135,3 @@ private: bool GeneratePerTarget; bool GenerateDependers; }; - -#endif diff --git a/Source/cmHexFileConverter.h b/Source/cmHexFileConverter.h index 26f9ea8ba0..35a91ed330 100644 --- a/Source/cmHexFileConverter.h +++ b/Source/cmHexFileConverter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmHexFileConverter_h -#define cmHexFileConverter_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -25,5 +24,3 @@ public: static bool TryConvert(const std::string& inFileName, const std::string& outFileName); }; - -#endif diff --git a/Source/cmIDEFlagTable.h b/Source/cmIDEFlagTable.h index ff93432cb0..5901771569 100644 --- a/Source/cmIDEFlagTable.h +++ b/Source/cmIDEFlagTable.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmIDEFlagTable_h -#define cmIDEFlagTable_h +#pragma once #include <string> @@ -37,5 +36,3 @@ struct cmIDEFlagTable UserValueRequired = UserValue | UserRequired }; }; - -#endif diff --git a/Source/cmIDEOptions.h b/Source/cmIDEOptions.h index f949ae3ddd..fbe9c3737b 100644 --- a/Source/cmIDEOptions.h +++ b/Source/cmIDEOptions.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmIDEOptions_h -#define cmIDEOptions_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -112,5 +111,3 @@ protected: std::string const& new_value); virtual void StoreUnknownFlag(std::string const& flag) = 0; }; - -#endif diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index 5808f90eb6..55f6453608 100644 --- a/Source/cmIfCommand.cxx +++ b/Source/cmIfCommand.cxx @@ -54,7 +54,7 @@ public: bool cmIfFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff, cmMakefile&) const { - return lff.Arguments.empty() || lff.Arguments == this->Args; + return lff.Arguments().empty() || lff.Arguments() == this->Args; } bool cmIfFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, @@ -65,20 +65,22 @@ bool cmIfFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, int scopeDepth = 0; for (cmListFileFunction const& func : functions) { // keep track of scope depth - if (func.Name.Lower == "if") { + if (func.LowerCaseName() == "if") { scopeDepth++; } - if (func.Name.Lower == "endif") { + if (func.LowerCaseName() == "endif") { scopeDepth--; } // watch for our state change - if (scopeDepth == 0 && func.Name.Lower == "else") { + if (scopeDepth == 0 && func.LowerCaseName() == "else") { if (this->ElseSeen) { - cmListFileBacktrace bt = mf.GetBacktrace(func); + cmListFileBacktrace elseBT = mf.GetBacktrace().Push(cmListFileContext{ + func.OriginalName(), this->GetStartingContext().FilePath, + func.Line() }); mf.GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR, - "A duplicate ELSE command was found inside an IF block.", bt); + "A duplicate ELSE command was found inside an IF block.", elseBT); cmSystemTools::SetFatalErrorOccured(); return true; } @@ -92,12 +94,14 @@ bool cmIfFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, if (!this->IsBlocking && mf.GetCMakeInstance()->GetTrace()) { mf.PrintCommandTrace(func); } - } else if (scopeDepth == 0 && func.Name.Lower == "elseif") { + } else if (scopeDepth == 0 && func.LowerCaseName() == "elseif") { + cmListFileBacktrace elseifBT = mf.GetBacktrace().Push( + cmListFileContext{ func.OriginalName(), + this->GetStartingContext().FilePath, func.Line() }); if (this->ElseSeen) { - cmListFileBacktrace bt = mf.GetBacktrace(func); mf.GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR, - "An ELSEIF command was found after an ELSE command.", bt); + "An ELSEIF command was found after an ELSE command.", elseifBT); cmSystemTools::SetFatalErrorOccured(); return true; } @@ -113,16 +117,11 @@ bool cmIfFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, std::string errorString; std::vector<cmExpandedCommandArgument> expandedArguments; - mf.ExpandArguments(func.Arguments, expandedArguments); + mf.ExpandArguments(func.Arguments(), expandedArguments); MessageType messType; - cmListFileContext conditionContext = - cmListFileContext::FromCommandContext( - func, this->GetStartingContext().FilePath); - - cmConditionEvaluator conditionEvaluator(mf, conditionContext, - mf.GetBacktrace(func)); + cmConditionEvaluator conditionEvaluator(mf, elseifBT); bool isTrue = conditionEvaluator.IsTrue(expandedArguments, errorString, messType); @@ -130,8 +129,7 @@ bool cmIfFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, if (!errorString.empty()) { std::string err = cmStrCat(cmIfCommandError(expandedArguments), errorString); - cmListFileBacktrace bt = mf.GetBacktrace(func); - mf.GetCMakeInstance()->IssueMessage(messType, err, bt); + mf.GetCMakeInstance()->IssueMessage(messType, err, elseifBT); if (messType == MessageType::FATAL_ERROR) { cmSystemTools::SetFatalErrorOccured(); return true; @@ -178,8 +176,7 @@ bool cmIfCommand(std::vector<cmListFileArgument> const& args, MessageType status; - cmConditionEvaluator conditionEvaluator( - makefile, makefile.GetExecutionContext(), makefile.GetBacktrace()); + cmConditionEvaluator conditionEvaluator(makefile, makefile.GetBacktrace()); bool isTrue = conditionEvaluator.IsTrue(expandedArguments, errorString, status); diff --git a/Source/cmIfCommand.h b/Source/cmIfCommand.h index 820ffa4c60..f05658701b 100644 --- a/Source/cmIfCommand.h +++ b/Source/cmIfCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmIfCommand_h -#define cmIfCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -13,5 +12,3 @@ struct cmListFileArgument; /// Starts an if block bool cmIfCommand(std::vector<cmListFileArgument> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmIncludeCommand.h b/Source/cmIncludeCommand.h index b0dd779560..af261638a6 100644 --- a/Source/cmIncludeCommand.h +++ b/Source/cmIncludeCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmIncludeCommand_h -#define cmIncludeCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -18,5 +17,3 @@ class cmExecutionStatus; */ bool cmIncludeCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmIncludeDirectoryCommand.h b/Source/cmIncludeDirectoryCommand.h index 66caff7214..d830dbf1fa 100644 --- a/Source/cmIncludeDirectoryCommand.h +++ b/Source/cmIncludeDirectoryCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmIncludeDirectoryCommand_h -#define cmIncludeDirectoryCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmIncludeDirectoryCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmIncludeExternalMSProjectCommand.h b/Source/cmIncludeExternalMSProjectCommand.h index 1013c4435b..fd77407890 100644 --- a/Source/cmIncludeExternalMSProjectCommand.h +++ b/Source/cmIncludeExternalMSProjectCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmIncludeExternalMSProjectCommand_h -#define cmIncludeExternalMSProjectCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmIncludeExternalMSProjectCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmIncludeGuardCommand.cxx b/Source/cmIncludeGuardCommand.cxx index ccb4496e6a..aefd0985fd 100644 --- a/Source/cmIncludeGuardCommand.cxx +++ b/Source/cmIncludeGuardCommand.cxx @@ -75,7 +75,7 @@ bool cmIncludeGuardCommand(std::vector<std::string> const& args, } std::string includeGuardVar = GetIncludeGuardVariableName( - status.GetMakefile().GetDefinition("CMAKE_CURRENT_LIST_FILE")); + *status.GetMakefile().GetDefinition("CMAKE_CURRENT_LIST_FILE")); cmMakefile* const mf = &status.GetMakefile(); diff --git a/Source/cmIncludeGuardCommand.h b/Source/cmIncludeGuardCommand.h index b86b76015b..c4de3d4be3 100644 --- a/Source/cmIncludeGuardCommand.h +++ b/Source/cmIncludeGuardCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmIncludeGuardCommand_h -#define cmIncludeGuardCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -18,5 +17,3 @@ class cmExecutionStatus; */ bool cmIncludeGuardCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmIncludeRegularExpressionCommand.h b/Source/cmIncludeRegularExpressionCommand.h index ca152b046e..a402f971c2 100644 --- a/Source/cmIncludeRegularExpressionCommand.h +++ b/Source/cmIncludeRegularExpressionCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmIncludeRegularExpressionCommand_h -#define cmIncludeRegularExpressionCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmIncludeRegularExpressionCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 178af738a5..ff08ee41ef 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -468,6 +468,27 @@ bool HandleTargetsMode(std::vector<std::string> const& args, // Track whether this is a namelink-only rule. bool namelinkOnly = false; + auto addTargetExport = [&]() { + // Add this install rule to an export if one was specified. + if (!exports.empty()) { + auto te = cm::make_unique<cmTargetExport>(); + te->TargetName = target.GetName(); + te->ArchiveGenerator = archiveGenerator.get(); + te->BundleGenerator = bundleGenerator.get(); + te->FrameworkGenerator = frameworkGenerator.get(); + te->HeaderGenerator = publicHeaderGenerator.get(); + te->LibraryGenerator = libraryGenerator.get(); + te->RuntimeGenerator = runtimeGenerator.get(); + te->ObjectsGenerator = objectGenerator.get(); + te->InterfaceIncludeDirectories = + cmJoin(includesArgs.GetIncludeDirs(), ";"); + te->NamelinkOnly = namelinkOnly; + helper.Makefile->GetGlobalGenerator() + ->GetExportSets()[exports] + .AddTargetExport(std::move(te)); + } + }; + switch (target.GetType()) { case cmStateEnums::SHARED_LIBRARY: { // Shared libraries are handled differently on DLL and non-DLL @@ -476,6 +497,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, if (target.IsDLLPlatform()) { // When in namelink only mode skip all libraries on Windows. if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) { + namelinkOnly = true; + addTargetExport(); continue; } @@ -507,6 +530,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, if (target.IsFrameworkOnApple()) { // When in namelink only mode skip frameworks. if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) { + namelinkOnly = true; + addTargetExport(); continue; } @@ -551,6 +576,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, if (target.IsFrameworkOnApple()) { // When in namelink only mode skip frameworks. if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) { + namelinkOnly = true; + addTargetExport(); continue; } @@ -680,7 +707,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, if (createInstallGeneratorsForTargetFileSets && !namelinkOnly) { cmProp files = target.GetProperty("PRIVATE_HEADER"); - if (files && !files->empty()) { + if (cmNonempty(files)) { std::vector<std::string> relFiles = cmExpandedList(*files); std::vector<std::string> absFiles; if (!helper.MakeFilesFullPath("PRIVATE_HEADER", relFiles, absFiles)) { @@ -702,7 +729,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } files = target.GetProperty("PUBLIC_HEADER"); - if (files && !files->empty()) { + if (cmNonempty(files)) { std::vector<std::string> relFiles = cmExpandedList(*files); std::vector<std::string> absFiles; if (!helper.MakeFilesFullPath("PUBLIC_HEADER", relFiles, absFiles)) { @@ -724,7 +751,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } files = target.GetProperty("RESOURCE"); - if (files && !files->empty()) { + if (cmNonempty(files)) { std::vector<std::string> relFiles = cmExpandedList(*files); std::vector<std::string> absFiles; if (!helper.MakeFilesFullPath("RESOURCE", relFiles, absFiles)) { @@ -744,25 +771,8 @@ bool 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.empty() && !namelinkOnly) { - auto te = cm::make_unique<cmTargetExport>(); - te->TargetName = target.GetName(); - te->ArchiveGenerator = archiveGenerator.get(); - te->BundleGenerator = bundleGenerator.get(); - te->FrameworkGenerator = frameworkGenerator.get(); - te->HeaderGenerator = publicHeaderGenerator.get(); - te->LibraryGenerator = libraryGenerator.get(); - te->RuntimeGenerator = runtimeGenerator.get(); - te->ObjectsGenerator = objectGenerator.get(); - te->InterfaceIncludeDirectories = - cmJoin(includesArgs.GetIncludeDirs(), ";"); - - helper.Makefile->GetGlobalGenerator() - ->GetExportSets()[exports] - .AddTargetExport(std::move(te)); - } + // Add this install rule to an export if one was specified. + addTargetExport(); // Keep track of whether we're installing anything in each category installsArchive = installsArchive || archiveGenerator; @@ -1157,7 +1167,7 @@ bool HandleDirectoryMode(std::vector<std::string> const& args, } else if (doing == DoingRegex) { literal_args += " REGEX \""; // Match rules are case-insensitive on some platforms. -#if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__) +#if defined(_WIN32) || defined(__APPLE__) std::string regex = cmSystemTools::LowerCase(args[i]); #else std::string regex = args[i]; diff --git a/Source/cmInstallCommand.h b/Source/cmInstallCommand.h index 32f00ce49a..f0ba44e65c 100644 --- a/Source/cmInstallCommand.h +++ b/Source/cmInstallCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallCommand_h -#define cmInstallCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmInstallCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 5d2ee0ab48..f318a1a881 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallCommandArguments_h -#define cmInstallCommandArguments_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -72,5 +71,3 @@ public: private: std::vector<std::string> IncludeDirs; }; - -#endif diff --git a/Source/cmInstallDirectoryGenerator.h b/Source/cmInstallDirectoryGenerator.h index bec89df733..af310f3999 100644 --- a/Source/cmInstallDirectoryGenerator.h +++ b/Source/cmInstallDirectoryGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallDirectoryGenerator_h -#define cmInstallDirectoryGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -48,5 +47,3 @@ protected: std::string const LiteralArguments; bool const Optional; }; - -#endif diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index 43dd00da4a..dd8624bbdb 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallExportGenerator_h -#define cmInstallExportGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -68,5 +67,3 @@ protected: std::string MainImportFile; std::unique_ptr<cmExportInstallFileGenerator> EFGen; }; - -#endif diff --git a/Source/cmInstallFilesCommand.h b/Source/cmInstallFilesCommand.h index f4ebbdec94..219bb972a1 100644 --- a/Source/cmInstallFilesCommand.h +++ b/Source/cmInstallFilesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallFilesCommand_h -#define cmInstallFilesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmInstallFilesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmInstallFilesGenerator.h b/Source/cmInstallFilesGenerator.h index 82666033b8..b5a1ef42a4 100644 --- a/Source/cmInstallFilesGenerator.h +++ b/Source/cmInstallFilesGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallFilesGenerator_h -#define cmInstallFilesGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -48,5 +47,3 @@ protected: bool const Programs; bool const Optional; }; - -#endif diff --git a/Source/cmInstallGenerator.h b/Source/cmInstallGenerator.h index d786d24dbe..ee55ee935f 100644 --- a/Source/cmInstallGenerator.h +++ b/Source/cmInstallGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallGenerator_h -#define cmInstallGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -74,5 +73,3 @@ protected: MessageLevel const Message; bool const ExcludeFromAll; }; - -#endif diff --git a/Source/cmInstallProgramsCommand.h b/Source/cmInstallProgramsCommand.h index c567f3bac8..e3c3e81fbd 100644 --- a/Source/cmInstallProgramsCommand.h +++ b/Source/cmInstallProgramsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallProgramsCommand_h -#define cmInstallProgramsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmInstallProgramsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmInstallScriptGenerator.h b/Source/cmInstallScriptGenerator.h index 0a9c4ba69c..338d866ef8 100644 --- a/Source/cmInstallScriptGenerator.h +++ b/Source/cmInstallScriptGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallScriptGenerator_h -#define cmInstallScriptGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -38,5 +37,3 @@ protected: cmLocalGenerator* LocalGenerator; bool AllowGenex; }; - -#endif diff --git a/Source/cmInstallSubdirectoryGenerator.h b/Source/cmInstallSubdirectoryGenerator.h index f9cd0f1f9d..3e46d6bbbd 100644 --- a/Source/cmInstallSubdirectoryGenerator.h +++ b/Source/cmInstallSubdirectoryGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallSubdirectoryGenerator_h -#define cmInstallSubdirectoryGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -37,5 +36,3 @@ protected: std::string const BinaryDirectory; cmLocalGenerator* LocalGenerator; }; - -#endif diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 178d5df319..b3da202c14 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -820,7 +820,7 @@ void cmInstallTargetGenerator::AddStripRule(std::ostream& os, Indent indent, os << indent << "if(CMAKE_INSTALL_DO_STRIP)\n"; os << indent << " execute_process(COMMAND \"" - << this->Target->Target->GetMakefile()->GetDefinition("CMAKE_STRIP") + << this->Target->Target->GetMakefile()->GetSafeDefinition("CMAKE_STRIP") << "\" " << stripArgs << "\"" << toDestDirPath << "\")\n"; os << indent << "endif()\n"; } @@ -858,9 +858,9 @@ void cmInstallTargetGenerator::AddUniversalInstallRule( return; } - const char* xcodeVersion = mf->GetDefinition("XCODE_VERSION"); + cmProp xcodeVersion = mf->GetDefinition("XCODE_VERSION"); if (!xcodeVersion || - cmSystemTools::VersionCompareGreater("6", xcodeVersion)) { + cmSystemTools::VersionCompareGreater("6", *xcodeVersion)) { return; } diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h index e21001f888..a53a75a283 100644 --- a/Source/cmInstallTargetGenerator.h +++ b/Source/cmInstallTargetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallTargetGenerator_h -#define cmInstallTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -114,5 +113,3 @@ protected: bool const Optional; cmListFileBacktrace const Backtrace; }; - -#endif diff --git a/Source/cmInstallTargetsCommand.h b/Source/cmInstallTargetsCommand.h index 0c5850c91f..716e7cebef 100644 --- a/Source/cmInstallTargetsCommand.h +++ b/Source/cmInstallTargetsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallTargetsCommand_h -#define cmInstallTargetsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmInstallTargetsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmInstallType.h b/Source/cmInstallType.h index e2602cb01a..33fa7a96b5 100644 --- a/Source/cmInstallType.h +++ b/Source/cmInstallType.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstallType_h -#define cmInstallType_h +#pragma once /** * Enumerate types known to file(INSTALL). @@ -16,5 +15,3 @@ enum cmInstallType cmInstallType_PROGRAMS, cmInstallType_DIRECTORY }; - -#endif diff --git a/Source/cmInstalledFile.h b/Source/cmInstalledFile.h index 07f70816ef..82474f5203 100644 --- a/Source/cmInstalledFile.h +++ b/Source/cmInstalledFile.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmInstalledFile_h -#define cmInstalledFile_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -76,5 +75,3 @@ private: CompiledGeneratorExpressionPtrType NameExpression; PropertyMapType Properties; }; - -#endif diff --git a/Source/cmJSONHelpers.h b/Source/cmJSONHelpers.h new file mode 100644 index 0000000000..a63347d654 --- /dev/null +++ b/Source/cmJSONHelpers.h @@ -0,0 +1,315 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <algorithm> +#include <cstddef> +#include <functional> +#include <map> +#include <string> +#include <vector> + +#include <cm/optional> +#include <cm/string_view> + +#include <cm3p/json/value.h> + +template <typename T, typename E> +using cmJSONHelper = std::function<E(T& out, const Json::Value* value)>; + +template <typename T, typename E> +class cmJSONObjectHelper +{ +public: + cmJSONObjectHelper(E&& success, E&& fail, bool allowExtra = true); + + template <typename U, typename M, typename F> + cmJSONObjectHelper& Bind(const cm::string_view& name, M U::*member, F func, + bool required = true); + template <typename M, typename F> + cmJSONObjectHelper& Bind(const cm::string_view& name, std::nullptr_t, F func, + bool required = true); + template <typename F> + cmJSONObjectHelper& Bind(const cm::string_view& name, F func, + bool required = true); + + E operator()(T& out, const Json::Value* value) const; + +private: + // Not a true cmJSONHelper, it just happens to match the signature + using MemberFunction = std::function<E(T& out, const Json::Value* value)>; + struct Member + { + cm::string_view Name; + MemberFunction Function; + bool Required; + }; + std::vector<Member> Members; + bool AnyRequired = false; + E Success; + E Fail; + bool AllowExtra; + + cmJSONObjectHelper& BindPrivate(const cm::string_view& name, + MemberFunction&& func, bool required); +}; + +template <typename T, typename E> +cmJSONObjectHelper<T, E>::cmJSONObjectHelper(E&& success, E&& fail, + bool allowExtra) + : Success(std::move(success)) + , Fail(std::move(fail)) + , AllowExtra(allowExtra) +{ +} + +template <typename T, typename E> +template <typename U, typename M, typename F> +cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind( + const cm::string_view& name, M U::*member, F func, bool required) +{ + return this->BindPrivate( + name, + [func, member](T& out, const Json::Value* value) -> E { + return func(out.*member, value); + }, + required); +} + +template <typename T, typename E> +template <typename M, typename F> +cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind( + const cm::string_view& name, std::nullptr_t, F func, bool required) +{ + return this->BindPrivate(name, + [func](T& /*out*/, const Json::Value* value) -> E { + M dummy; + return func(dummy, value); + }, + required); +} + +template <typename T, typename E> +template <typename F> +cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::Bind( + const cm::string_view& name, F func, bool required) +{ + return this->BindPrivate(name, MemberFunction(func), required); +} + +template <typename T, typename E> +cmJSONObjectHelper<T, E>& cmJSONObjectHelper<T, E>::BindPrivate( + const cm::string_view& name, MemberFunction&& func, bool required) +{ + Member m; + m.Name = name; + m.Function = std::move(func); + m.Required = required; + this->Members.push_back(std::move(m)); + if (required) { + this->AnyRequired = true; + } + return *this; +} + +template <typename T, typename E> +E cmJSONObjectHelper<T, E>::operator()(T& out, const Json::Value* value) const +{ + if (!value && this->AnyRequired) { + return this->Fail; + } + if (value && !value->isObject()) { + return this->Fail; + } + Json::Value::Members extraFields; + if (value) { + extraFields = value->getMemberNames(); + } + + for (auto const& m : this->Members) { + std::string name(m.Name.data(), m.Name.size()); + if (value && value->isMember(name)) { + E result = m.Function(out, &(*value)[name]); + if (result != this->Success) { + return result; + } + extraFields.erase( + std::find(extraFields.begin(), extraFields.end(), name)); + } else if (!m.Required) { + E result = m.Function(out, nullptr); + if (result != this->Success) { + return result; + } + } else { + return this->Fail; + } + } + + return this->AllowExtra || extraFields.empty() ? this->Success : this->Fail; +} + +template <typename E> +cmJSONHelper<std::string, E> cmJSONStringHelper(E success, E fail, + const std::string& defval = "") +{ + return + [success, fail, defval](std::string& out, const Json::Value* value) -> E { + if (!value) { + out = defval; + return success; + } + if (!value->isString()) { + return fail; + } + out = value->asString(); + return success; + }; +} + +template <typename E> +cmJSONHelper<int, E> cmJSONIntHelper(E success, E fail, int defval = 0) +{ + return [success, fail, defval](int& out, const Json::Value* value) -> E { + if (!value) { + out = defval; + return success; + } + if (!value->isInt()) { + return fail; + } + out = value->asInt(); + return success; + }; +} + +template <typename E> +cmJSONHelper<unsigned int, E> cmJSONUIntHelper(E success, E fail, + unsigned int defval = 0) +{ + return + [success, fail, defval](unsigned int& out, const Json::Value* value) -> E { + if (!value) { + out = defval; + return success; + } + if (!value->isUInt()) { + return fail; + } + out = value->asUInt(); + return success; + }; +} + +template <typename E> +cmJSONHelper<bool, E> cmJSONBoolHelper(E success, E fail, bool defval = false) +{ + return [success, fail, defval](bool& out, const Json::Value* value) -> E { + if (!value) { + out = defval; + return success; + } + if (!value->isBool()) { + return fail; + } + out = value->asBool(); + return success; + }; +} + +template <typename T, typename E, typename F, typename Filter> +cmJSONHelper<std::vector<T>, E> cmJSONVectorFilterHelper(E success, E fail, + F func, Filter filter) +{ + return [success, fail, func, filter](std::vector<T>& out, + const Json::Value* value) -> E { + if (!value) { + out.clear(); + return success; + } + if (!value->isArray()) { + return fail; + } + out.clear(); + for (auto const& item : *value) { + T t; + E result = func(t, &item); + if (result != success) { + return result; + } + if (!filter(t)) { + continue; + } + out.push_back(t); + } + return success; + }; +} + +template <typename T, typename E, typename F> +cmJSONHelper<std::vector<T>, E> cmJSONVectorHelper(E success, E fail, F func) +{ + return cmJSONVectorFilterHelper<T, E, F>(success, fail, func, + [](const T&) { return true; }); +} + +template <typename T, typename E, typename F, typename Filter> +cmJSONHelper<std::map<std::string, T>, E> cmJSONMapFilterHelper(E success, + E fail, F func, + Filter filter) +{ + return [success, fail, func, filter](std::map<std::string, T>& out, + const Json::Value* value) -> E { + if (!value) { + out.clear(); + return success; + } + if (!value->isObject()) { + return fail; + } + out.clear(); + for (auto const& key : value->getMemberNames()) { + if (!filter(key)) { + continue; + } + T t; + E result = func(t, &(*value)[key]); + if (result != success) { + return result; + } + out[key] = std::move(t); + } + return success; + }; +} + +template <typename T, typename E, typename F> +cmJSONHelper<std::map<std::string, T>, E> cmJSONMapHelper(E success, E fail, + F func) +{ + return cmJSONMapFilterHelper<T, E, F>( + success, fail, func, [](const std::string&) { return true; }); +} + +template <typename T, typename E, typename F> +cmJSONHelper<cm::optional<T>, E> cmJSONOptionalHelper(E success, F func) +{ + return [success, func](cm::optional<T>& out, const Json::Value* value) -> E { + if (!value) { + out.reset(); + return success; + } + out.emplace(); + return func(*out, value); + }; +} + +template <typename T, typename E, typename F> +cmJSONHelper<T, E> cmJSONRequiredHelper(E fail, F func) +{ + return [fail, func](T& out, const Json::Value* value) -> E { + if (!value) { + return fail; + } + return func(out, value); + }; +} diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx index 9f17f15577..3a7ae0cd32 100644 --- a/Source/cmJsonObjects.cxx +++ b/Source/cmJsonObjects.cxx @@ -51,11 +51,7 @@ std::vector<std::string> getConfigurations(const cmake* cm) return configurations; } - makefiles[0]->GetConfigurations(configurations); - if (configurations.empty()) { - configurations.emplace_back(); - } - return configurations; + return makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); } bool hasString(const Json::Value& v, const std::string& s) @@ -635,8 +631,8 @@ static Json::Value DumpProjectList(const cmake* cm, std::string const& config) // Project structure information: const cmMakefile* mf = lg->GetMakefile(); - auto minVersion = mf->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); - pObj[kMINIMUM_CMAKE_VERSION] = minVersion ? minVersion : ""; + auto minVersion = mf->GetSafeDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); + pObj[kMINIMUM_CMAKE_VERSION] = minVersion; pObj[kSOURCE_DIRECTORY_KEY] = mf->GetCurrentSourceDirectory(); pObj[kBUILD_DIRECTORY_KEY] = mf->GetCurrentBinaryDirectory(); pObj[kTARGETS_KEY] = DumpTargetsList(projectIt.second, config); diff --git a/Source/cmJsonObjects.h b/Source/cmJsonObjects.h index 2fd4b26f32..80a4834610 100644 --- a/Source/cmJsonObjects.h +++ b/Source/cmJsonObjects.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmJsonObjects_h -#define cmJsonObjects_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -23,5 +22,3 @@ extern void cmGetCMakeInputs(const cmGlobalGenerator* gg, extern Json::Value cmDumpCodeModel(const cmake* cm); extern Json::Value cmDumpCTestInfo(const cmake* cm); extern Json::Value cmDumpCMakeInputs(const cmake* cm); - -#endif diff --git a/Source/cmLDConfigLDConfigTool.h b/Source/cmLDConfigLDConfigTool.h index 34bf6c61fc..eeb86dd4ea 100644 --- a/Source/cmLDConfigLDConfigTool.h +++ b/Source/cmLDConfigLDConfigTool.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLDConfigLDConfigTool_h -#define cmLDConfigLDConfigTool_h +#pragma once #include <string> #include <vector> @@ -18,5 +17,3 @@ public: bool GetLDConfigPaths(std::vector<std::string>& paths) override; }; - -#endif diff --git a/Source/cmLDConfigTool.h b/Source/cmLDConfigTool.h index c816562d4f..3116f80820 100644 --- a/Source/cmLDConfigTool.h +++ b/Source/cmLDConfigTool.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLDConfigTool_h -#define cmLDConfigTool_h +#pragma once #include <string> #include <vector> @@ -20,5 +19,3 @@ public: protected: cmRuntimeDependencyArchive* Archive; }; - -#endif diff --git a/Source/cmLinkDirectoriesCommand.h b/Source/cmLinkDirectoriesCommand.h index a7caa5c637..2a3499dc1e 100644 --- a/Source/cmLinkDirectoriesCommand.h +++ b/Source/cmLinkDirectoriesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLinkDirectoriesCommand_h -#define cmLinkDirectoriesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmLinkDirectoriesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmLinkItem.h b/Source/cmLinkItem.h index 3d9293528e..5a90e7ec65 100644 --- a/Source/cmLinkItem.h +++ b/Source/cmLinkItem.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLinkItem_h -#define cmLinkItem_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -140,5 +139,3 @@ inline cmTargetLinkLibraryType CMP0003_ComputeLinkType( // The current configuration is not a debug configuration. return OPTIMIZED_LibraryType; } - -#endif diff --git a/Source/cmLinkItemGraphVisitor.cxx b/Source/cmLinkItemGraphVisitor.cxx index acc23c8db3..dfdd3ff65c 100644 --- a/Source/cmLinkItemGraphVisitor.cxx +++ b/Source/cmLinkItemGraphVisitor.cxx @@ -24,15 +24,12 @@ void cmLinkItemGraphVisitor::VisitItem(cmLinkItem const& item) void cmLinkItemGraphVisitor::VisitLinks(cmLinkItem const& item, cmLinkItem const& rootItem) { - if (this->LinkVisited(item, rootItem)) { - return; - } - if (item.Target == nullptr) { return; } - for (auto const& config : item.Target->Makefile->GetGeneratorConfigs()) { + for (auto const& config : item.Target->Makefile->GetGeneratorConfigs( + cmMakefile::IncludeEmptyConfig)) { this->VisitLinks(item, rootItem, config); } } diff --git a/Source/cmLinkItemGraphVisitor.h b/Source/cmLinkItemGraphVisitor.h index 21dc659ad3..0d6676ad64 100644 --- a/Source/cmLinkItemGraphVisitor.h +++ b/Source/cmLinkItemGraphVisitor.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLinkItemGraphVisitor_h -#define cmLinkItemGraphVisitor_h +#pragma once #include <map> #include <set> @@ -71,5 +70,3 @@ private: std::string const& config, DependencyMap& dependencies); }; - -#endif diff --git a/Source/cmLinkLibrariesCommand.h b/Source/cmLinkLibrariesCommand.h index 34122513c0..27c410f0f2 100644 --- a/Source/cmLinkLibrariesCommand.h +++ b/Source/cmLinkLibrariesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLinkLibrariesCommand_h -#define cmLinkLibrariesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmLinkLibrariesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmLinkLineComputer.h b/Source/cmLinkLineComputer.h index df424683fd..a1dafc4c83 100644 --- a/Source/cmLinkLineComputer.h +++ b/Source/cmLinkLineComputer.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLinkLineComputer_h -#define cmLinkLineComputer_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -73,5 +72,3 @@ protected: bool UseNinjaMulti; bool Relink; }; - -#endif diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx index c50a786843..5739feca1f 100644 --- a/Source/cmLinkLineDeviceComputer.cxx +++ b/Source/cmLinkLineDeviceComputer.cxx @@ -191,21 +191,18 @@ bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg, target.GetLinkClosure(config); if (cm::contains(closure->Languages, "CUDA")) { - if (cmProp separableCompilation = - target.GetProperty("CUDA_SEPARABLE_COMPILATION")) { - if (cmIsOn(*separableCompilation)) { - bool doDeviceLinking = false; - switch (target.GetType()) { - case cmStateEnums::SHARED_LIBRARY: - case cmStateEnums::MODULE_LIBRARY: - case cmStateEnums::EXECUTABLE: - doDeviceLinking = true; - break; - default: - break; - } - return doDeviceLinking; + if (cmIsOn(target.GetProperty("CUDA_SEPARABLE_COMPILATION"))) { + bool doDeviceLinking = false; + switch (target.GetType()) { + case cmStateEnums::SHARED_LIBRARY: + case cmStateEnums::MODULE_LIBRARY: + case cmStateEnums::EXECUTABLE: + doDeviceLinking = true; + break; + default: + break; } + return doDeviceLinking; } cmComputeLinkInformation* pcli = target.GetLinkInformation(config); diff --git a/Source/cmLinkLineDeviceComputer.h b/Source/cmLinkLineDeviceComputer.h index a9b01cd9c1..dee625b682 100644 --- a/Source/cmLinkLineDeviceComputer.h +++ b/Source/cmLinkLineDeviceComputer.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLinkLineDeviceComputer_h -#define cmLinkLineDeviceComputer_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -42,5 +41,3 @@ public: bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg, const std::string& config); - -#endif diff --git a/Source/cmLinkedTree.h b/Source/cmLinkedTree.h index c7453eac92..d70176dfca 100644 --- a/Source/cmLinkedTree.h +++ b/Source/cmLinkedTree.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLinkedTree_h -#define cmLinkedTree_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -188,5 +187,3 @@ private: std::vector<T> Data; std::vector<PositionType> UpPositions; }; - -#endif diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx index edec613ede..a2c14bd1a8 100644 --- a/Source/cmListCommand.cxx +++ b/Source/cmListCommand.cxx @@ -27,6 +27,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmStringReplaceHelper.h" @@ -44,11 +45,11 @@ bool GetListString(std::string& listString, const std::string& var, const cmMakefile& makefile) { // get the old value - const char* cacheValue = makefile.GetDefinition(var); + cmProp cacheValue = makefile.GetDefinition(var); if (!cacheValue) { return false; } - listString = cacheValue; + listString = *cacheValue; return true; } diff --git a/Source/cmListCommand.h b/Source/cmListCommand.h index 274d9fdbd6..6efab16629 100644 --- a/Source/cmListCommand.h +++ b/Source/cmListCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmListCommand_h -#define cmListCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -16,5 +15,3 @@ class cmExecutionStatus; */ bool cmListCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 7ebb02f492..70ef5af032 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -15,14 +15,6 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -cmCommandContext::cmCommandName& cmCommandContext::cmCommandName::operator=( - std::string const& name) -{ - this->Original = name; - this->Lower = cmSystemTools::LowerCase(name); - return *this; -} - struct cmListFileParser { cmListFileParser(cmListFile* lf, cmListFileBacktrace lfbt, @@ -43,7 +35,9 @@ struct cmListFileParser cmMessenger* Messenger; const char* FileName; cmListFileLexer* Lexer; - cmListFileFunction Function; + std::string FunctionName; + long FunctionLine; + std::vector<cmListFileArgument> FunctionArguments; enum { SeparationOkay, @@ -141,7 +135,9 @@ bool cmListFileParser::Parse() if (haveNewline) { haveNewline = false; if (this->ParseFunction(token->text, token->line)) { - this->ListFile->Functions.push_back(this->Function); + this->ListFile->Functions.emplace_back( + std::move(this->FunctionName), this->FunctionLine, + std::move(this->FunctionArguments)); } else { return false; } @@ -200,9 +196,8 @@ bool cmListFile::ParseString(const char* str, const char* virtual_filename, bool cmListFileParser::ParseFunction(const char* name, long line) { // Ininitialize a new function call. - this->Function = cmListFileFunction(); - this->Function.Name = name; - this->Function.Line = line; + this->FunctionName = name; + this->FunctionLine = line; // Command name has already been parsed. Read the left paren. cmListFileLexer_Token* token; @@ -297,7 +292,7 @@ bool cmListFileParser::ParseFunction(const char* name, long line) bool cmListFileParser::AddArgument(cmListFileLexer_Token* token, cmListFileArgument::Delimiter delim) { - this->Function.Arguments.emplace_back(token->text, delim, token->line); + this->FunctionArguments.emplace_back(token->text, delim, token->line); if (this->Separation == SeparationOkay) { return true; } @@ -446,7 +441,8 @@ void cmListFileBacktrace::PrintCallStack(std::ostream& out) const cmStateSnapshot bottom = this->GetBottom(); for (Entry const* cur = this->TopEntry->Parent.get(); !cur->IsBottom(); cur = cur->Parent.get()) { - if (cur->Context.Name.empty()) { + if (cur->Context.Name.empty() && + cur->Context.Line != cmListFileContext::DeferPlaceholderLine) { // Skip this whole-file scope. When we get here we already will // have printed a more-specific context within the file. continue; @@ -483,11 +479,13 @@ bool cmListFileBacktrace::Empty() const std::ostream& operator<<(std::ostream& os, cmListFileContext const& lfc) { os << lfc.FilePath; - if (lfc.Line) { + if (lfc.Line > 0) { os << ":" << lfc.Line; if (!lfc.Name.empty()) { os << " (" << lfc.Name << ")"; } + } else if (lfc.Line == cmListFileContext::DeferPlaceholderLine) { + os << ":DEFERRED"; } return os; } diff --git a/Source/cmListFileCache.h b/Source/cmListFileCache.h index 89902ff30b..727fc60313 100644 --- a/Source/cmListFileCache.h +++ b/Source/cmListFileCache.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmListFileCache_h -#define cmListFileCache_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,7 +11,10 @@ #include <utility> #include <vector> +#include <cm/optional> + #include "cmStateSnapshot.h" +#include "cmSystemTools.h" /** \class cmListFileCache * \brief A class to cache list file contents. @@ -27,16 +29,19 @@ struct cmCommandContext { struct cmCommandName { - std::string Lower; std::string Original; + std::string Lower; cmCommandName() = default; - cmCommandName(std::string const& name) { *this = name; } - cmCommandName& operator=(std::string const& name); + cmCommandName(std::string name) + : Original(std::move(name)) + , Lower(cmSystemTools::LowerCase(this->Original)) + { + } } Name; long Line = 0; cmCommandContext() = default; - cmCommandContext(const char* name, int line) - : Name(name) + cmCommandContext(std::string name, long line) + : Name(std::move(name)) , Line(line) { } @@ -73,14 +78,26 @@ public: std::string Name; std::string FilePath; long Line = 0; + static long const DeferPlaceholderLine = -1; + cm::optional<std::string> DeferId; - static cmListFileContext FromCommandContext(cmCommandContext const& lfcc, - std::string const& fileName) + cmListFileContext() = default; + cmListFileContext(std::string name, std::string filePath, long line) + : Name(std::move(name)) + , FilePath(std::move(filePath)) + , Line(line) + { + } + + static cmListFileContext FromCommandContext( + cmCommandContext const& lfcc, std::string const& fileName, + cm::optional<std::string> deferId = {}) { cmListFileContext lfc; lfc.FilePath = fileName; lfc.Line = lfcc.Line; lfc.Name = lfcc.Name.Original; + lfc.DeferId = std::move(deferId); return lfc; } }; @@ -90,9 +107,48 @@ bool operator<(const cmListFileContext& lhs, const cmListFileContext& rhs); bool operator==(cmListFileContext const& lhs, cmListFileContext const& rhs); bool operator!=(cmListFileContext const& lhs, cmListFileContext const& rhs); -struct cmListFileFunction : public cmCommandContext +class cmListFileFunction { - std::vector<cmListFileArgument> Arguments; +public: + cmListFileFunction(std::string name, long line, + std::vector<cmListFileArgument> args) + : Impl{ std::make_shared<Implementation>(std::move(name), line, + std::move(args)) } + { + } + + std::string const& OriginalName() const noexcept + { + return this->Impl->Name.Original; + } + + std::string const& LowerCaseName() const noexcept + { + return this->Impl->Name.Lower; + } + + long Line() const noexcept { return this->Impl->Line; } + + std::vector<cmListFileArgument> const& Arguments() const noexcept + { + return this->Impl->Arguments; + } + + operator cmCommandContext const&() const noexcept { return *this->Impl; } + +private: + struct Implementation : public cmCommandContext + { + Implementation(std::string name, long line, + std::vector<cmListFileArgument> args) + : cmCommandContext{ std::move(name), line } + , Arguments{ std::move(args) } + { + } + std::vector<cmListFileArgument> Arguments; + }; + + std::shared_ptr<Implementation const> Impl; }; // Represent a backtrace (call stack). Provide value semantics @@ -175,6 +231,32 @@ public: std::ostream& operator<<(std::ostream& os, BT<std::string> const& s); +// Wrap type T as a value with potentially multiple backtraces. For purposes +// of ordering and equality comparison, only the original value is used. The +// backtrace is considered incidental. +template <typename T> +class BTs +{ +public: + BTs(T v = T(), cmListFileBacktrace bt = cmListFileBacktrace()) + : Value(std::move(v)) + { + Backtraces.emplace_back(std::move(bt)); + } + T Value; + std::vector<cmListFileBacktrace> Backtraces; + friend bool operator==(BTs<T> const& l, BTs<T> const& r) + { + return l.Value == r.Value; + } + friend bool operator<(BTs<T> const& l, BTs<T> const& r) + { + return l.Value < r.Value; + } + friend bool operator==(BTs<T> const& l, T const& r) { return l.Value == r; } + friend bool operator==(T const& l, BTs<T> const& r) { return l == r.Value; } +}; + std::vector<BT<std::string>> ExpandListWithBacktrace( std::string const& list, cmListFileBacktrace const& bt = cmListFileBacktrace()); @@ -189,5 +271,3 @@ struct cmListFile std::vector<cmListFileFunction> Functions; }; - -#endif diff --git a/Source/cmListFileLexer.h b/Source/cmListFileLexer.h index ec6b3cd77f..3c89f63416 100644 --- a/Source/cmListFileLexer.h +++ b/Source/cmListFileLexer.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmListFileLexer_h -#define cmListFileLexer_h +#pragma once #ifdef __cplusplus extern "C" { @@ -67,5 +66,3 @@ void cmListFileLexer_Delete(cmListFileLexer*); #ifdef __cplusplus } /* extern "C" */ #endif - -#endif diff --git a/Source/cmLoadCacheCommand.h b/Source/cmLoadCacheCommand.h index 7cee6637c3..5f5b705594 100644 --- a/Source/cmLoadCacheCommand.h +++ b/Source/cmLoadCacheCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLoadCacheCommand_h -#define cmLoadCacheCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmLoadCacheCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmLoadCommandCommand.h b/Source/cmLoadCommandCommand.h index f5fd754b1d..d30ba169e0 100644 --- a/Source/cmLoadCommandCommand.h +++ b/Source/cmLoadCommandCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLoadCommandCommand_h -#define cmLoadCommandCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmLoadCommandCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmLocalCommonGenerator.cxx b/Source/cmLocalCommonGenerator.cxx index 278ec8b147..5daaeff490 100644 --- a/Source/cmLocalCommonGenerator.cxx +++ b/Source/cmLocalCommonGenerator.cxx @@ -8,6 +8,7 @@ #include "cmGeneratorTarget.h" #include "cmMakefile.h" #include "cmOutputConverter.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" class cmGlobalGenerator; @@ -17,10 +18,8 @@ cmLocalCommonGenerator::cmLocalCommonGenerator(cmGlobalGenerator* gg, : cmLocalGenerator(gg, mf) , WorkingDirectory(std::move(wd)) { - this->Makefile->GetConfigurations(this->ConfigNames); - if (this->ConfigNames.empty()) { - this->ConfigNames.emplace_back(); - } + this->ConfigNames = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); } cmLocalCommonGenerator::~cmLocalCommonGenerator() = default; @@ -64,13 +63,13 @@ std::string cmLocalCommonGenerator::GetTargetFortranFlags( // If there is a separate module path flag then duplicate the // include path with it. This compiler does not search the include // path for modules. - if (const char* modpath_flag = + if (cmProp modpath_flag = this->Makefile->GetDefinition("CMAKE_Fortran_MODPATH_FLAG")) { std::vector<std::string> includes; this->GetIncludeDirectories(includes, target, "C", config); for (std::string const& id : includes) { std::string flg = - cmStrCat(modpath_flag, + cmStrCat(*modpath_flag, this->ConvertToOutputFormat(id, cmOutputConverter::SHELL)); this->AppendFlags(flags, flg); } diff --git a/Source/cmLocalCommonGenerator.h b/Source/cmLocalCommonGenerator.h index 378ca634d1..f1eaf61fa7 100644 --- a/Source/cmLocalCommonGenerator.h +++ b/Source/cmLocalCommonGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocalCommonGenerator_h -#define cmLocalCommonGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -47,5 +46,3 @@ protected: friend class cmCommonTargetGenerator; }; - -#endif diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index d1ab62b347..2239192c8b 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -39,6 +39,7 @@ #include "cmSourceFile.h" #include "cmSourceFileLocation.h" #include "cmSourceFileLocationKind.h" +#include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateTypes.h" @@ -115,22 +116,22 @@ cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile) std::vector<std::string> enabledLanguages = this->GetState()->GetEnabledLanguages(); - if (const char* sysrootCompile = + if (cmProp sysrootCompile = this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) { - this->CompilerSysroot = sysrootCompile; + this->CompilerSysroot = *sysrootCompile; } else { this->CompilerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); } - if (const char* sysrootLink = + if (cmProp sysrootLink = this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) { - this->LinkerSysroot = sysrootLink; + this->LinkerSysroot = *sysrootLink; } else { this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); } - if (std::string const* appleArchSysroots = - this->Makefile->GetDef("CMAKE_APPLE_ARCH_SYSROOTS")) { + if (cmProp appleArchSysroots = + this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) { std::string const& appleArchs = this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES"); std::vector<std::string> archs; @@ -219,10 +220,10 @@ void cmLocalGenerator::ComputeObjectMaxPath() #else this->ObjectPathMax = 1000; #endif - const char* plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX"); - if (plen && *plen) { + cmProp plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX"); + if (cmNonempty(plen)) { unsigned int pmax; - if (sscanf(plen, "%u", &pmax) == 1) { + if (sscanf(plen->c_str(), "%u", &pmax) == 1) { if (pmax >= 128) { this->ObjectPathMax = pmax; } else { @@ -234,7 +235,7 @@ void cmLocalGenerator::ComputeObjectMaxPath() } } else { std::ostringstream w; - w << "CMAKE_OBJECT_PATH_MAX is set to \"" << plen + w << "CMAKE_OBJECT_PATH_MAX is set to \"" << *plen << "\", which fails to parse as a positive integer. " << "The value will be ignored."; this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); @@ -283,7 +284,7 @@ void cmLocalGenerator::TraceDependencies() // Generate the rule files for each target. const auto& targets = this->GetGeneratorTargets(); for (const auto& target : targets) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!target->IsInBuildSystem()) { continue; } target->TraceDependencies(); @@ -297,9 +298,9 @@ void cmLocalGenerator::GenerateTestFiles() } // Compute the set of configurations. - std::vector<std::string> configurationTypes; - const std::string& config = - this->Makefile->GetConfigurations(configurationTypes, false); + std::vector<std::string> configurationTypes = + this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig); + std::string config = this->Makefile->GetDefaultConfiguration(); std::string file = cmStrCat(this->StateSnapshot.GetDirectory().GetCurrentBinary(), @@ -357,7 +358,7 @@ void cmLocalGenerator::GenerateTestFiles() } // Add directory labels property - const char* directoryLabels = + cmProp directoryLabels = this->Makefile->GetDefinition("CMAKE_DIRECTORY_LABELS"); cmProp labels = this->Makefile->GetProperty("LABELS"); @@ -370,7 +371,7 @@ void cmLocalGenerator::GenerateTestFiles() fout << ";"; } if (directoryLabels) { - fout << cmOutputConverter::EscapeForCMake(directoryLabels); + fout << cmOutputConverter::EscapeForCMake(*directoryLabels); } fout << ")\n"; } @@ -379,7 +380,7 @@ void cmLocalGenerator::GenerateTestFiles() void cmLocalGenerator::CreateEvaluationFileOutputs() { std::vector<std::string> const& configs = - this->Makefile->GetGeneratorConfigs(); + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); for (std::string const& c : configs) { this->CreateEvaluationFileOutputs(c); } @@ -424,7 +425,8 @@ void cmLocalGenerator::ProcessEvaluationFiles( void cmLocalGenerator::GenerateInstallRules() { // Compute the install prefix. - const char* prefix = this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX"); + const char* prefix = + cmToCStr(this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX")); #if defined(_WIN32) && !defined(__CYGWIN__) std::string prefix_win32; @@ -432,10 +434,10 @@ void cmLocalGenerator::GenerateInstallRules() if (!cmSystemTools::GetEnv("SystemDrive", prefix_win32)) { prefix_win32 = "C:"; } - const char* project_name = this->Makefile->GetDefinition("PROJECT_NAME"); - if (project_name && project_name[0]) { + cmProp project_name = this->Makefile->GetDefinition("PROJECT_NAME"); + if (cmNonempty(project_name)) { prefix_win32 += "/Program Files/"; - prefix_win32 += project_name; + prefix_win32 += *project_name; } else { prefix_win32 += "/InstalledCMakeProject"; } @@ -456,15 +458,15 @@ void cmLocalGenerator::GenerateInstallRules() prefix = "/usr/local"; } #endif - if (const char* stagingPrefix = + if (cmProp stagingPrefix = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX")) { - prefix = stagingPrefix; + prefix = stagingPrefix->c_str(); } // Compute the set of configurations. - std::vector<std::string> configurationTypes; - const std::string& config = - this->Makefile->GetConfigurations(configurationTypes, false); + std::vector<std::string> configurationTypes = + this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig); + std::string config = this->Makefile->GetDefaultConfiguration(); // Choose a default install configuration. std::string default_config = config; @@ -538,40 +540,40 @@ void cmLocalGenerator::GenerateInstallRules() /* clang-format on */ // Copy user-specified install options to the install code. - if (const char* so_no_exe = + if (cmProp so_no_exe = this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) { /* clang-format off */ fout << "# Install shared libraries without execute permission?\n" "if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n" - " set(CMAKE_INSTALL_SO_NO_EXE \"" << so_no_exe << "\")\n" + " set(CMAKE_INSTALL_SO_NO_EXE \"" << *so_no_exe << "\")\n" "endif()\n" "\n"; /* clang-format on */ } // Copy cmake cross compile state to install code. - if (const char* crosscompiling = + if (cmProp crosscompiling = this->Makefile->GetDefinition("CMAKE_CROSSCOMPILING")) { /* clang-format off */ fout << "# Is this installation the result of a crosscompile?\n" "if(NOT DEFINED CMAKE_CROSSCOMPILING)\n" - " set(CMAKE_CROSSCOMPILING \"" << crosscompiling << "\")\n" + " set(CMAKE_CROSSCOMPILING \"" << *crosscompiling << "\")\n" "endif()\n" "\n"; /* clang-format on */ } // Write default directory permissions. - if (const char* defaultDirPermissions = this->Makefile->GetDefinition( + if (cmProp defaultDirPermissions = this->Makefile->GetDefinition( "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS")) { /* clang-format off */ fout << "# Set default install directory permissions.\n" "if(NOT DEFINED CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS)\n" " set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS \"" - << defaultDirPermissions << "\")\n" + << *defaultDirPermissions << "\")\n" "endif()\n" "\n"; /* clang-format on */ @@ -580,14 +582,14 @@ void cmLocalGenerator::GenerateInstallRules() // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM so that // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)` // has same platform variable as when running cmake - if (const char* platform = this->Makefile->GetDefinition( + if (cmProp platform = this->Makefile->GetDefinition( "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM")) { /* clang-format off */ fout << "# Set default install directory permissions.\n" "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM)\n" " set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM \"" - << platform << "\")\n" + << *platform << "\")\n" "endif()\n" "\n"; /* clang-format on */ @@ -596,14 +598,14 @@ void cmLocalGenerator::GenerateInstallRules() // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL so that // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)` // has same tool selected as when running cmake - if (const char* command = + if (cmProp command = this->Makefile->GetDefinition("CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL")) { /* clang-format off */ fout << "# Set default install directory permissions.\n" "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL)\n" " set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL \"" - << command << "\")\n" + << *command << "\")\n" "endif()\n" "\n"; /* clang-format on */ @@ -612,14 +614,14 @@ void cmLocalGenerator::GenerateInstallRules() // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND so that // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)` // has same path to the tool as when running cmake - if (const char* command = this->Makefile->GetDefinition( + if (cmProp command = this->Makefile->GetDefinition( "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND")) { /* clang-format off */ fout << "# Set default install directory permissions.\n" "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND)\n" " set(CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND \"" - << command << "\")\n" + << *command << "\")\n" "endif()\n" "\n"; /* clang-format on */ @@ -630,13 +632,13 @@ void cmLocalGenerator::GenerateInstallRules() // CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND has consistent // logic to fallback to CMAKE_OBJDUMP when `objdump` is // not on the path - if (const char* command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) { + if (cmProp command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) { /* clang-format off */ fout << "# Set default install directory permissions.\n" "if(NOT DEFINED CMAKE_OBJDUMP)\n" " set(CMAKE_OBJDUMP \"" - << command << "\")\n" + << *command << "\")\n" "endif()\n" "\n"; /* clang-format on */ @@ -753,16 +755,13 @@ cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget( void cmLocalGenerator::ComputeTargetManifest() { // Collect the set of configuration types. - std::vector<std::string> configNames; - this->Makefile->GetConfigurations(configNames); - if (configNames.empty()) { - configNames.emplace_back(); - } + std::vector<std::string> configNames = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); // Add our targets to the manifest for each configuration. const auto& targets = this->GetGeneratorTargets(); for (const auto& target : targets) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!target->IsInBuildSystem()) { continue; } for (std::string const& c : configNames) { @@ -774,11 +773,8 @@ void cmLocalGenerator::ComputeTargetManifest() bool cmLocalGenerator::ComputeTargetCompileFeatures() { // Collect the set of configuration types. - std::vector<std::string> configNames; - this->Makefile->GetConfigurations(configNames); - if (configNames.empty()) { - configNames.emplace_back(); - } + std::vector<std::string> configNames = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); using LanguagePair = std::pair<std::string, std::string>; std::vector<LanguagePair> pairedLanguages{ { "OBJC", "C" }, @@ -802,40 +798,9 @@ bool cmLocalGenerator::ComputeTargetCompileFeatures() // Now that C/C++ _STANDARD values have been computed // set the values to ObjC/ObjCXX _STANDARD variables - if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - auto copyStandardToObjLang = [&](LanguagePair const& lang) -> bool { - if (!target->GetProperty(cmStrCat(lang.first, "_STANDARD"))) { - cmProp standard = - target->GetProperty(cmStrCat(lang.second, "_STANDARD")); - if (!standard) { - standard = this->Makefile->GetDef( - cmStrCat("CMAKE_", lang.second, "_STANDARD_DEFAULT")); - } - target->Target->SetProperty(cmStrCat(lang.first, "_STANDARD"), - standard ? standard->c_str() : nullptr); - return true; - } - return false; - }; - auto copyPropertyToObjLang = [&](LanguagePair const& lang, - const char* property) { - if (!target->GetProperty(cmStrCat(lang.first, property)) && - target->GetProperty(cmStrCat(lang.second, property))) { - cmProp p = target->GetProperty(cmStrCat(lang.second, property)); - target->Target->SetProperty(cmStrCat(lang.first, property), - p ? p->c_str() : nullptr); - } - }; - for (auto const& lang : pairedLanguages) { - if (copyStandardToObjLang(lang)) { - copyPropertyToObjLang(lang, "_STANDARD_REQUIRED"); - copyPropertyToObjLang(lang, "_EXTENSIONS"); - } - } - if (cmProp standard = target->GetProperty("CUDA_STANDARD")) { - if (*standard == "98") { - target->Target->SetProperty("CUDA_STANDARD", "03"); - } + if (target->CanCompileSources()) { + for (std::string const& c : configNames) { + target->ComputeCompileFeatures(c, inferredEnabledLanguages); } } } @@ -894,8 +859,8 @@ std::string cmLocalGenerator::GetIncludeFlags( std::string const& includeFlag = this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang)); - const char* sep = - this->Makefile->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang)); + const char* sep = cmToCStr( + this->Makefile->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang))); bool quotePaths = false; if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) { quotePaths = true; @@ -912,15 +877,15 @@ std::string cmLocalGenerator::GetIncludeFlags( // Support special system include flag if it is available and the // normal flag is repeated for each directory. - const char* sysIncludeFlag = nullptr; + cmProp sysIncludeFlag = nullptr; if (repeatFlag) { sysIncludeFlag = this->Makefile->GetDefinition( cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang)); } - const char* fwSearchFlag = this->Makefile->GetDefinition( + cmProp fwSearchFlag = this->Makefile->GetDefinition( cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG")); - const char* sysFwSearchFlag = this->Makefile->GetDefinition( + cmProp sysFwSearchFlag = this->Makefile->GetDefinition( cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG")); bool flagUsed = false; @@ -929,16 +894,16 @@ std::string cmLocalGenerator::GetIncludeFlags( emitted.insert("/System/Library/Frameworks"); #endif for (std::string const& i : includes) { - if (fwSearchFlag && *fwSearchFlag && this->Makefile->IsOn("APPLE") && + if (cmNonempty(fwSearchFlag) && this->Makefile->IsOn("APPLE") && cmSystemTools::IsPathToFramework(i)) { std::string const frameworkDir = cmSystemTools::CollapseFullPath(cmStrCat(i, "/../")); if (emitted.insert(frameworkDir).second) { if (sysFwSearchFlag && target && target->IsSystemIncludeDirectory(i, config, lang)) { - includeFlags << sysFwSearchFlag; + includeFlags << *sysFwSearchFlag; } else { - includeFlags << fwSearchFlag; + includeFlags << *fwSearchFlag; } includeFlags << this->ConvertToOutputFormat(frameworkDir, shellFormat) << " "; @@ -949,7 +914,7 @@ std::string cmLocalGenerator::GetIncludeFlags( if (!flagUsed || repeatFlag) { if (sysIncludeFlag && target && target->IsSystemIncludeDirectory(i, config, lang)) { - includeFlags << sysIncludeFlag; + includeFlags << *sysIncludeFlag; } else { includeFlags << includeFlag; } @@ -989,9 +954,9 @@ void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags, const std::string& lang, const std::string& config) { - std::string langFlagRegexVar = std::string("CMAKE_") + lang + "_FLAG_REGEX"; + std::string langFlagRegexVar = cmStrCat("CMAKE_", lang, "_FLAG_REGEX"); - if (const char* langFlagRegexStr = + if (cmProp langFlagRegexStr = this->Makefile->GetDefinition(langFlagRegexVar)) { // Filter flags acceptable to this language. if (cmProp targetFlags = target->GetProperty("COMPILE_FLAGS")) { @@ -1000,7 +965,7 @@ void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags, // Re-escape these flags since COMPILE_FLAGS were already parsed // as a command line above. std::string compileOpts; - this->AppendCompileOptions(compileOpts, opts, langFlagRegexStr); + this->AppendCompileOptions(compileOpts, opts, langFlagRegexStr->c_str()); if (!compileOpts.empty()) { flags.emplace_back(std::move(compileOpts)); } @@ -1008,7 +973,8 @@ void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags, std::vector<BT<std::string>> targetCompileOpts = target->GetCompileOptions(config, lang); // COMPILE_OPTIONS are escaped. - this->AppendCompileOptions(flags, targetCompileOpts, langFlagRegexStr); + this->AppendCompileOptions(flags, targetCompileOpts, + langFlagRegexStr->c_str()); } else { // Use all flags. if (cmProp targetFlags = target->GetProperty("COMPILE_FLAGS")) { @@ -1025,12 +991,13 @@ void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags, this->AppendCompileOptions(flags, targetCompileOpts); } + cmStandardLevelResolver standardResolver(this->Makefile); for (auto const& it : target->GetMaxLanguageStandards()) { - cmProp standard = target->GetProperty(it.first + "_STANDARD"); + cmProp standard = target->GetLanguageStandard(it.first, config); if (!standard) { continue; } - if (this->Makefile->IsLaterStandard(it.first, *standard, it.second)) { + if (standardResolver.IsLaterStandard(it.first, *standard, it.second)) { std::ostringstream e; e << "The COMPILE_FEATURES property of target \"" << target->GetName() << "\" was evaluated when computing the link " @@ -1050,14 +1017,14 @@ void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags, } std::string compReqFlag; - this->AddCompilerRequirementFlag(compReqFlag, target, lang); + this->AddCompilerRequirementFlag(compReqFlag, target, lang, config); if (!compReqFlag.empty()) { flags.emplace_back(std::move(compReqFlag)); } // Add compile flag for the MSVC compiler only. cmMakefile* mf = this->GetMakefile(); - if (const char* jmc = + if (cmProp jmc = mf->GetDefinition("CMAKE_" + lang + "_COMPILE_OPTIONS_JMC")) { // Handle Just My Code debugging flags, /JMC. @@ -1071,7 +1038,7 @@ void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags, std::string isJMCEnabled = cmGeneratorExpression::Evaluate(*jmcExprGen, this, config); if (cmIsOn(isJMCEnabled)) { - std::vector<std::string> optVec = cmExpandedList(jmc); + std::vector<std::string> optVec = cmExpandedList(*jmc); std::string jmcFlags; this->AppendCompileOptions(jmcFlags, optVec); if (!jmcFlags.empty()) { @@ -1308,7 +1275,7 @@ std::vector<BT<std::string>> cmLocalGenerator::GetIncludeDirectoriesImplicit( } } - // Emit remaining non implicit user direcories. + // Emit remaining non implicit user directories. for (BT<std::string> const& udr : userDirs) { if (notExcluded(udr.Value)) { emitBT(udr); @@ -1555,7 +1522,7 @@ void cmLocalGenerator::GetTargetFlags( return; } - if (target->GetPropertyAsBool("WIN32_EXECUTABLE")) { + if (target->IsWin32Executable(config)) { exeFlags += this->Makefile->GetSafeDefinition("CMAKE_CREATE_WIN32_EXE"); exeFlags += " "; @@ -1578,9 +1545,8 @@ void cmLocalGenerator::GetTargetFlags( frameworkPath, linkPath); } - if (cmIsOn(this->Makefile->GetDefinition("BUILD_SHARED_LIBS"))) { - std::string sFlagVar = std::string("CMAKE_SHARED_BUILD_") + - linkLanguage + std::string("_FLAGS"); + if (this->Makefile->IsOn("BUILD_SHARED_LIBS")) { + std::string sFlagVar = "CMAKE_SHARED_BUILD_" + linkLanguage + "_FLAGS"; exeFlags += this->Makefile->GetSafeDefinition(sFlagVar); exeFlags += " "; } @@ -1688,8 +1654,8 @@ static std::string GetFrameworkFlags(const std::string& lang, } std::string fwSearchFlagVar = "CMAKE_" + lang + "_FRAMEWORK_SEARCH_FLAG"; - const char* fwSearchFlag = mf->GetDefinition(fwSearchFlagVar); - if (!(fwSearchFlag && *fwSearchFlag)) { + cmProp fwSearchFlag = mf->GetDefinition(fwSearchFlagVar); + if (!cmNonempty(fwSearchFlag)) { return std::string(); } @@ -1715,7 +1681,7 @@ static std::string GetFrameworkFlags(const std::string& lang, std::vector<std::string> const& frameworks = cli->GetFrameworkPaths(); for (std::string const& framework : frameworks) { if (emitted.insert(framework).second) { - flags += fwSearchFlag; + flags += *fwSearchFlag; flags += lg->ConvertToOutputFormat(framework, cmOutputConverter::SHELL); flags += " "; @@ -1797,18 +1763,18 @@ void cmLocalGenerator::OutputLinkLibraries( std::string linkLanguage = cli.GetLinkLanguage(); std::string libPathFlag; - if (const char* value = this->Makefile->GetDefinition( + if (cmProp value = this->Makefile->GetDefinition( "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_FLAG")) { - libPathFlag = value; + libPathFlag = *value; } else { libPathFlag = this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); } std::string libPathTerminator; - if (const char* value = this->Makefile->GetDefinition( + if (cmProp value = this->Makefile->GetDefinition( "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_TERMINATOR")) { - libPathTerminator = value; + libPathTerminator = *value; } else { libPathTerminator = this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); @@ -1911,8 +1877,9 @@ void cmLocalGenerator::AddArchitectureFlags(std::string& flags, if (this->Makefile->IsOn("APPLE") && this->EmitUniversalBinaryFlags) { std::vector<std::string> archs; target->GetAppleArchs(config, archs); - if (!archs.empty() && !lang.empty() && - (lang[0] == 'C' || lang[0] == 'F' || lang[0] == 'O')) { + if (!archs.empty() && + (lang == "C" || lang == "CXX" || lang == "OBJ" || lang == "OBJCXX" || + lang == "ASM")) { for (std::string const& arch : archs) { if (filterArch.empty() || filterArch == arch) { flags += " -arch "; @@ -1921,16 +1888,15 @@ void cmLocalGenerator::AddArchitectureFlags(std::string& flags, } } - const char* sysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT"); - if (sysroot && sysroot[0] == '/' && !sysroot[1]) { + cmProp sysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT"); + if (sysroot && *sysroot == "/") { sysroot = nullptr; } - std::string sysrootFlagVar = - std::string("CMAKE_") + lang + "_SYSROOT_FLAG"; - const char* sysrootFlag = this->Makefile->GetDefinition(sysrootFlagVar); - if (sysrootFlag && *sysrootFlag) { + std::string sysrootFlagVar = "CMAKE_" + lang + "_SYSROOT_FLAG"; + cmProp sysrootFlag = this->Makefile->GetDefinition(sysrootFlagVar); + if (cmNonempty(sysrootFlag)) { if (!this->AppleArchSysroots.empty() && - !this->AllAppleArchSysrootsAreTheSame(archs, sysroot)) { + !this->AllAppleArchSysrootsAreTheSame(archs, cmToCStr(sysroot))) { for (std::string const& arch : archs) { std::string const& archSysroot = this->AppleArchSysroots[arch]; if (cmIsOff(archSysroot)) { @@ -1939,29 +1905,28 @@ void cmLocalGenerator::AddArchitectureFlags(std::string& flags, if (filterArch.empty() || filterArch == arch) { flags += " -Xarch_" + arch + " "; // Combine sysroot flag and path to work with -Xarch - std::string arch_sysroot = sysrootFlag + archSysroot; + std::string arch_sysroot = *sysrootFlag + archSysroot; flags += this->ConvertToOutputFormat(arch_sysroot, SHELL); } } - } else if (sysroot && *sysroot) { + } else if (cmNonempty(sysroot)) { flags += " "; - flags += sysrootFlag; + flags += *sysrootFlag; flags += " "; - flags += this->ConvertToOutputFormat(sysroot, SHELL); + flags += this->ConvertToOutputFormat(*sysroot, SHELL); } } - const char* deploymentTarget = + cmProp deploymentTarget = this->Makefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET"); std::string deploymentTargetFlagVar = - std::string("CMAKE_") + lang + "_OSX_DEPLOYMENT_TARGET_FLAG"; - const char* deploymentTargetFlag = + "CMAKE_" + lang + "_OSX_DEPLOYMENT_TARGET_FLAG"; + cmProp deploymentTargetFlag = this->Makefile->GetDefinition(deploymentTargetFlagVar); - if (deploymentTargetFlag && *deploymentTargetFlag && deploymentTarget && - *deploymentTarget) { + if (cmNonempty(deploymentTargetFlag) && cmNonempty(deploymentTarget)) { flags += " "; - flags += deploymentTargetFlag; - flags += deploymentTarget; + flags += *deploymentTargetFlag; + flags += *deploymentTarget; } } } @@ -1975,39 +1940,59 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, this->AddConfigVariableFlags(flags, cmStrCat("CMAKE_", lang, "_FLAGS"), config); + std::string compiler = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_COMPILER_ID")); + + std::string compilerSimulateId = this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_SIMULATE_ID")); if (lang == "Swift") { if (cmProp v = target->GetProperty("Swift_LANGUAGE_VERSION")) { - if (cmSystemTools::VersionCompare( - cmSystemTools::OP_GREATER_EQUAL, - this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"), - "4.2")) { + if (cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, + cmToCStr(this->Makefile->GetDefinition( + "CMAKE_Swift_COMPILER_VERSION")), + "4.2")) { this->AppendFlags(flags, "-swift-version " + *v); } } } else if (lang == "CUDA") { target->AddCUDAArchitectureFlags(flags); target->AddCUDAToolkitFlags(flags); + } else if (lang == "ISPC") { + target->AddISPCTargetFlags(flags); + } else if (lang == "RC" && + this->Makefile->GetSafeDefinition("CMAKE_RC_COMPILER") + .find("llvm-rc") != std::string::npos) { + compiler = this->Makefile->GetSafeDefinition("CMAKE_C_COMPILER_ID"); + if (!compiler.empty()) { + compilerSimulateId = + this->Makefile->GetSafeDefinition("CMAKE_C_SIMULATE_ID"); + } else { + compiler = this->Makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_ID"); + compilerSimulateId = + this->Makefile->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID"); + } + } - std::string const& compiler = - this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID"); - - if (compiler == "Clang") { - bool separable = target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION"); - - if (separable) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "CUDA_SEPARABLE_COMPILATION isn't supported on Clang. " - "See CMake issue #20726."); + // Add VFS Overlay for Clang compiliers + if (compiler == "Clang") { + if (cmProp vfsOverlay = + this->Makefile->GetDefinition("CMAKE_CLANG_VFS_OVERLAY")) { + if (compilerSimulateId == "MSVC") { + this->AppendCompileOptions( + flags, + std::vector<std::string>{ "-Xclang", "-ivfsoverlay", "-Xclang", + *vfsOverlay }); + } else { + this->AppendCompileOptions( + flags, std::vector<std::string>{ "-ivfsoverlay", *vfsOverlay }); } } } - // Add MSVC runtime library flags. This is activated by the presence // of a default selection whether or not it is overridden by a property. cmProp msvcRuntimeLibraryDefault = - this->Makefile->GetDef("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT"); - if (msvcRuntimeLibraryDefault && !msvcRuntimeLibraryDefault->empty()) { + this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT"); + if (cmNonempty(msvcRuntimeLibraryDefault)) { cmProp msvcRuntimeLibraryValue = target->GetProperty("MSVC_RUNTIME_LIBRARY"); if (!msvcRuntimeLibraryValue) { @@ -2016,11 +2001,10 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, std::string const msvcRuntimeLibrary = cmGeneratorExpression::Evaluate( *msvcRuntimeLibraryValue, this, config, target); if (!msvcRuntimeLibrary.empty()) { - if (const char* msvcRuntimeLibraryOptions = - this->Makefile->GetDefinition( - "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" + - msvcRuntimeLibrary)) { - this->AppendCompileOptions(flags, msvcRuntimeLibraryOptions); + if (cmProp 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( @@ -2046,7 +2030,7 @@ void cmLocalGenerator::AddLanguageFlagsForLinking( // when linking in order to use the matching standard library. // FIXME: If CMake gains an abstraction for standard library // selection, this will have to be reconciled with it. - this->AddCompilerRequirementFlag(flags, target, lang); + this->AddCompilerRequirementFlag(flags, target, lang, config); } this->AddLanguageFlags(flags, target, lang, config); @@ -2189,156 +2173,20 @@ void cmLocalGenerator::AddSharedFlags(std::string& flags, } void cmLocalGenerator::AddCompilerRequirementFlag( - std::string& flags, cmGeneratorTarget const* target, const std::string& lang) + std::string& flags, cmGeneratorTarget const* target, const std::string& lang, + const std::string& config) { - if (lang.empty()) { - return; - } - const char* defaultStd = - this->Makefile->GetDefinition("CMAKE_" + lang + "_STANDARD_DEFAULT"); - if (!defaultStd || !*defaultStd) { - // This compiler has no notion of language standard levels. - return; - } - std::string extProp = lang + "_EXTENSIONS"; - bool ext = true; - if (cmProp extPropValue = target->GetProperty(extProp)) { - if (cmIsOff(*extPropValue)) { - ext = false; - } - } - std::string stdProp = lang + "_STANDARD"; - cmProp standardProp = target->GetProperty(stdProp); - if (!standardProp) { - if (ext) { - // No language standard is specified and extensions are not disabled. - // Check if this compiler needs a flag to enable extensions. - std::string const option_flag = - "CMAKE_" + lang + "_EXTENSION_COMPILE_OPTION"; - if (const char* opt = - target->Target->GetMakefile()->GetDefinition(option_flag)) { - std::vector<std::string> optVec = cmExpandedList(opt); - for (std::string const& i : optVec) { - this->AppendFlagEscape(flags, i); - } - } - } - return; - } - - std::string const type = ext ? "EXTENSION" : "STANDARD"; - - if (target->GetPropertyAsBool(lang + "_STANDARD_REQUIRED")) { - std::string option_flag = - "CMAKE_" + lang + *standardProp + "_" + type + "_COMPILE_OPTION"; - - const char* opt = - target->Target->GetMakefile()->GetDefinition(option_flag); - if (!opt) { - std::ostringstream e; - e << "Target \"" << target->GetName() - << "\" requires the language " - "dialect \"" - << lang << *standardProp << "\" " - << (ext ? "(with compiler extensions)" : "") - << ", but CMake " - "does not know the compile flags to use to enable it."; - this->IssueMessage(MessageType::FATAL_ERROR, e.str()); - } else { - std::vector<std::string> optVec = cmExpandedList(opt); - for (std::string const& i : optVec) { - this->AppendFlagEscape(flags, i); - } - } - return; - } - - static std::map<std::string, std::vector<std::string>> langStdMap; - if (langStdMap.empty()) { - // Maintain sorted order, most recent first. - langStdMap["CXX"].emplace_back("20"); - langStdMap["CXX"].emplace_back("17"); - langStdMap["CXX"].emplace_back("14"); - langStdMap["CXX"].emplace_back("11"); - langStdMap["CXX"].emplace_back("98"); - - langStdMap["OBJCXX"].emplace_back("20"); - langStdMap["OBJCXX"].emplace_back("17"); - langStdMap["OBJCXX"].emplace_back("14"); - langStdMap["OBJCXX"].emplace_back("11"); - langStdMap["OBJCXX"].emplace_back("98"); - - langStdMap["C"].emplace_back("11"); - langStdMap["C"].emplace_back("99"); - langStdMap["C"].emplace_back("90"); - - langStdMap["OBJC"].emplace_back("11"); - langStdMap["OBJC"].emplace_back("99"); - langStdMap["OBJC"].emplace_back("90"); - - langStdMap["CUDA"].emplace_back("20"); - langStdMap["CUDA"].emplace_back("17"); - langStdMap["CUDA"].emplace_back("14"); - langStdMap["CUDA"].emplace_back("11"); - langStdMap["CUDA"].emplace_back("03"); - } - - std::string standard(*standardProp); - if (lang == "CUDA" && standard == "98") { - standard = "03"; - } - std::vector<std::string>& stds = langStdMap[lang]; - - auto stdIt = std::find(stds.begin(), stds.end(), standard); - if (stdIt == stds.end()) { - - std::string e = - lang + "_STANDARD is set to invalid value '" + standard + "'"; - this->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage( - MessageType::FATAL_ERROR, e, target->GetBacktrace()); - return; - } - - auto defaultStdIt = std::find(stds.begin(), stds.end(), defaultStd); - if (defaultStdIt == stds.end()) { - std::string e = "CMAKE_" + lang + - "_STANDARD_DEFAULT is set to invalid value '" + std::string(defaultStd) + - "'"; - this->IssueMessage(MessageType::INTERNAL_ERROR, e); - return; - } - - // If the standard requested is older than the compiler's default - // then we need to use a flag to change it. The comparison is - // greater-or-equal because the standards are stored in backward - // chronological order. - if (stdIt >= defaultStdIt) { - std::string option_flag = - "CMAKE_" + lang + *stdIt + "_" + type + "_COMPILE_OPTION"; - - std::string const& opt = - target->Target->GetMakefile()->GetRequiredDefinition(option_flag); - std::vector<std::string> optVec = cmExpandedList(opt); - for (std::string const& i : optVec) { - this->AppendFlagEscape(flags, i); - } - return; - } - - // The standard requested is at least as new as the compiler's default, - // and the standard request is not required. Decay to the newest standard - // for which a flag is defined. - for (; stdIt < defaultStdIt; ++stdIt) { - std::string option_flag = - cmStrCat("CMAKE_", lang, *stdIt, "_", type, "_COMPILE_OPTION"); - - if (const char* opt = - target->Target->GetMakefile()->GetDefinition(option_flag)) { - std::vector<std::string> optVec = cmExpandedList(opt); + cmStandardLevelResolver standardResolver(this->Makefile); + + std::string const& optionFlagDef = + standardResolver.GetCompileOptionDef(target, lang, config); + if (!optionFlagDef.empty()) { + cmProp opt = target->Target->GetMakefile()->GetDefinition(optionFlagDef); + if (opt) { + std::vector<std::string> optVec = cmExpandedList(*opt); for (std::string const& i : optVec) { this->AppendFlagEscape(flags, i); } - return; } } } @@ -2350,7 +2198,7 @@ static void AddVisibilityCompileOption(std::string& flags, std::string* warnCMP0063) { std::string compileOption = "CMAKE_" + lang + "_COMPILE_OPTIONS_VISIBILITY"; - const char* opt = lg->GetMakefile()->GetDefinition(compileOption); + cmProp opt = lg->GetMakefile()->GetDefinition(compileOption); if (!opt) { return; } @@ -2374,7 +2222,7 @@ static void AddVisibilityCompileOption(std::string& flags, cmSystemTools::Error(e.str()); return; } - std::string option = opt + *prop; + std::string option = *opt + *prop; lg->AppendFlags(flags, option); } @@ -2386,7 +2234,7 @@ static void AddInlineVisibilityCompileOption(std::string& flags, { std::string compileOption = cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN"); - const char* opt = lg->GetMakefile()->GetDefinition(compileOption); + cmProp opt = lg->GetMakefile()->GetDefinition(compileOption); if (!opt) { return; } @@ -2399,7 +2247,7 @@ static void AddInlineVisibilityCompileOption(std::string& flags, *warnCMP0063 += " VISIBILITY_INLINES_HIDDEN\n"; return; } - lg->AppendFlags(flags, opt); + lg->AppendFlags(flags, *opt); } void cmLocalGenerator::AddVisibilityPresetFlags( @@ -2581,13 +2429,67 @@ void cmLocalGenerator::AppendFlagEscape(std::string& flags, this->EscapeForShell(rawFlag, false, false, false, this->IsNinjaMulti())); } -void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) +void cmLocalGenerator::AddISPCDependencies(cmGeneratorTarget* target) { - std::vector<std::string> configsList; - std::string configDefault = this->Makefile->GetConfigurations(configsList); - if (configsList.empty()) { - configsList.push_back(configDefault); + std::vector<std::string> enabledLanguages = + this->GetState()->GetEnabledLanguages(); + if (std::find(enabledLanguages.begin(), enabledLanguages.end(), "ISPC") == + enabledLanguages.end()) { + return; + } + + cmProp ispcHeaderSuffixProp = target->GetProperty("ISPC_HEADER_SUFFIX"); + assert(ispcHeaderSuffixProp != nullptr); + + std::vector<std::string> ispcArchSuffixes = + detail::ComputeISPCObjectSuffixes(target); + const bool extra_objects = (ispcArchSuffixes.size() > 1); + + std::vector<std::string> configsList = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); + for (std::string const& config : configsList) { + + std::string rootObjectDir = target->GetObjectDirectory(config); + std::string headerDir = rootObjectDir; + if (cmProp prop = target->GetProperty("ISPC_HEADER_DIRECTORY")) { + headerDir = cmSystemTools::CollapseFullPath( + cmStrCat(this->GetBinaryDirectory(), '/', *prop)); + } + + std::vector<cmSourceFile*> sources; + target->GetSourceFiles(sources, config); + + // build up the list of ispc headers and extra objects that this target is + // generating + for (cmSourceFile const* sf : sources) { + // Generate this object file's rule file. + const std::string& lang = sf->GetLanguage(); + if (lang == "ISPC") { + std::string const& objectName = target->GetObjectName(sf); + + // Drop both ".obj" and the source file extension + std::string ispcSource = + cmSystemTools::GetFilenameWithoutLastExtension(objectName); + ispcSource = + cmSystemTools::GetFilenameWithoutLastExtension(ispcSource); + + auto headerPath = + cmStrCat(headerDir, '/', ispcSource, *ispcHeaderSuffixProp); + target->AddISPCGeneratedHeader(headerPath, config); + if (extra_objects) { + std::vector<std::string> objs = detail::ComputeISPCExtraObjects( + objectName, rootObjectDir, ispcArchSuffixes); + target->AddISPCGeneratedObject(std::move(objs), config); + } + } + } } +} + +void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) +{ + std::vector<std::string> configsList = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); for (std::string const& config : configsList) { // FIXME: Refactor collection of sources to not evaluate object @@ -2625,8 +2527,10 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) } if (!useMultiArchPch.empty()) { - target->Target->SetProperty( - cmStrCat(lang, "_COMPILE_OPTIONS_USE_PCH"), useMultiArchPch); + + target->Target->AppendProperty( + cmStrCat(lang, "_COMPILE_OPTIONS_USE_PCH"), + cmStrCat("$<$<CONFIG:", config, ">:", useMultiArchPch, ">")); } } @@ -3055,12 +2959,12 @@ void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags, } const std::string name = "CMAKE_" + lang + "_LINK_OPTIONS_IPO"; - const char* rawFlagsList = this->Makefile->GetDefinition(name); + cmProp rawFlagsList = this->Makefile->GetDefinition(name); if (rawFlagsList == nullptr) { return; } - std::vector<std::string> flagsList = cmExpandedList(rawFlagsList); + std::vector<std::string> flagsList = cmExpandedList(*rawFlagsList); for (std::string const& o : flagsList) { this->AppendFlagEscape(flags, o); } @@ -3246,10 +3150,10 @@ void cmLocalGenerator::JoinDefines(const std::set<std::string>& defines, // Lookup the define flag for the current language. std::string dflag = "-D"; if (!lang.empty()) { - const char* df = + cmProp df = this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_DEFINE_FLAG")); - if (df && *df) { - dflag = df; + if (cmNonempty(df)) { + dflag = *df; } } const char* itemSeparator = definesString.empty() ? "" : " "; @@ -3293,18 +3197,18 @@ void cmLocalGenerator::AppendFeatureOptions(std::string& flags, const std::string& lang, const char* feature) { - const char* optionList = this->Makefile->GetDefinition( + cmProp optionList = this->Makefile->GetDefinition( cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_", feature)); if (optionList != nullptr) { - std::vector<std::string> options = cmExpandedList(optionList); + std::vector<std::string> options = cmExpandedList(*optionList); for (std::string const& o : options) { this->AppendFlagEscape(flags, o); } } } -const char* cmLocalGenerator::GetFeature(const std::string& feature, - const std::string& config) +cmProp cmLocalGenerator::GetFeature(const std::string& feature, + const std::string& config) { std::string featureName = feature; // TODO: Define accumulation policy for features (prepend, append, @@ -3316,7 +3220,7 @@ const char* cmLocalGenerator::GetFeature(const std::string& feature, cmStateSnapshot snp = this->StateSnapshot; while (snp.IsValid()) { if (cmProp value = snp.GetDirectory().GetProperty(featureName)) { - return value->c_str(); + return value; } snp = snp.GetBuildsystemDirectoryParent(); } @@ -3751,9 +3655,9 @@ KWIML_INT_uint64_t cmLocalGenerator::GetBackwardsCompatibility() unsigned int major = 0; unsigned int minor = 0; unsigned int patch = 0; - if (const char* value = + if (cmProp value = this->Makefile->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY")) { - switch (sscanf(value, "%u.%u.%u", &major, &minor, &patch)) { + switch (sscanf(value->c_str(), "%u.%u.%u", &major, &minor, &patch)) { case 2: patch = 0; break; @@ -3857,8 +3761,7 @@ void cmLocalGenerator::GenerateAppleInfoPList(cmGeneratorTarget* target, { // Find the Info.plist template. cmProp in = target->GetProperty("MACOSX_BUNDLE_INFO_PLIST"); - std::string inFile = - (in && !in->empty()) ? *in : "MacOSXBundleInfo.plist.in"; + std::string inFile = cmNonempty(in) ? *in : "MacOSXBundleInfo.plist.in"; if (!cmSystemTools::FileIsFullPath(inFile)) { std::string inMod = this->Makefile->GetModulesFile(inFile); if (!inMod.empty()) { @@ -3888,7 +3791,7 @@ 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, fname, false, false, false); + mf->ConfigureFile(inFile, fname, false, false, false, true); } void cmLocalGenerator::GenerateFrameworkInfoPList( @@ -3897,8 +3800,7 @@ void cmLocalGenerator::GenerateFrameworkInfoPList( { // Find the Info.plist template. cmProp in = target->GetProperty("MACOSX_FRAMEWORK_INFO_PLIST"); - std::string inFile = - (in && !in->empty()) ? *in : "MacOSXFrameworkInfo.plist.in"; + std::string inFile = cmNonempty(in) ? *in : "MacOSXFrameworkInfo.plist.in"; if (!cmSystemTools::FileIsFullPath(inFile)) { std::string inMod = this->Makefile->GetModulesFile(inFile); if (!inMod.empty()) { @@ -3924,7 +3826,7 @@ 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, fname, false, false, false); + mf->ConfigureFile(inFile, fname, false, false, false, true); } namespace { @@ -4168,4 +4070,52 @@ void AddUtilityCommand(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, target->AddSource(force.NameCMP0049); } } + +std::vector<std::string> ComputeISPCObjectSuffixes(cmGeneratorTarget* target) +{ + const std::string& targetProperty = + target->GetSafeProperty("ISPC_INSTRUCTION_SETS"); + std::vector<std::string> ispcTargets; + + if (!cmIsOff(targetProperty)) { + cmExpandList(targetProperty, ispcTargets); + for (auto& ispcTarget : ispcTargets) { + // transform targets into the suffixes + auto pos = ispcTarget.find('-'); + auto target_suffix = ispcTarget.substr(0, pos); + if (target_suffix == + "avx1") { // when targetting avx1 ISPC uses the 'avx' output string + target_suffix = "avx"; + } + ispcTarget = target_suffix; + } + } + return ispcTargets; +} + +std::vector<std::string> ComputeISPCExtraObjects( + std::string const& objectName, std::string const& buildDirectory, + std::vector<std::string> const& ispcSuffixes) +{ + auto normalizedDir = cmSystemTools::CollapseFullPath(buildDirectory); + std::vector<std::string> computedObjects; + computedObjects.reserve(ispcSuffixes.size()); + + auto extension = cmSystemTools::GetFilenameLastExtension(objectName); + + // We can't use cmSystemTools::GetFilenameWithoutLastExtension as it + // drops any directories in objectName + auto objNameNoExt = objectName; + std::string::size_type dot_pos = objectName.rfind('.'); + if (dot_pos != std::string::npos) { + objNameNoExt.resize(dot_pos); + } + + for (const auto& ispcTarget : ispcSuffixes) { + computedObjects.emplace_back( + cmStrCat(normalizedDir, "/", objNameNoExt, "_", ispcTarget, extension)); + } + + return computedObjects; +} } diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index f2d914519b..22d3599e5a 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocalGenerator_h -#define cmLocalGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -20,6 +19,7 @@ #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" +#include "cmProperty.h" #include "cmStateSnapshot.h" class cmComputeLinkInformation; @@ -123,7 +123,8 @@ public: const std::string& config); void AddCompilerRequirementFlag(std::string& flags, cmGeneratorTarget const* target, - const std::string& lang); + const std::string& lang, + const std::string& config); //! Append flags to a string. virtual void AppendFlags(std::string& flags, const std::string& newFlags) const; @@ -131,6 +132,7 @@ public: const std::vector<BT<std::string>>& newFlags) const; virtual void AppendFlagEscape(std::string& flags, const std::string& rawFlag) const; + void AddISPCDependencies(cmGeneratorTarget* target); void AddPchDependencies(cmGeneratorTarget* target); void AddUnityBuild(cmGeneratorTarget* target); void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target, @@ -208,8 +210,7 @@ public: void AppendFeatureOptions(std::string& flags, const std::string& lang, const char* feature); - const char* GetFeature(const std::string& feature, - const std::string& config); + cmProp GetFeature(const std::string& feature, const std::string& config); /** \brief Get absolute path to dependency \a name * @@ -445,7 +446,7 @@ public: void GetTargetCompileFlags(cmGeneratorTarget* target, std::string const& config, std::string const& lang, std::string& flags, - std::string const& arch = std::string()); + std::string const& arch); std::vector<BT<std::string>> GetTargetCompileFlags( cmGeneratorTarget* target, std::string const& config, std::string const& lang, std::string const& arch = std::string()); @@ -593,6 +594,9 @@ void AddUtilityCommand(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt, bool escapeOldStyle, const char* comment, bool uses_terminal, bool command_expand_lists, const std::string& job_pool, bool stdPipesUTF8); -} -#endif +std::vector<std::string> ComputeISPCObjectSuffixes(cmGeneratorTarget* target); +std::vector<std::string> ComputeISPCExtraObjects( + std::string const& objectName, std::string const& buildDirectory, + std::vector<std::string> const& ispcSuffixes); +} diff --git a/Source/cmLocalGhsMultiGenerator.cxx b/Source/cmLocalGhsMultiGenerator.cxx index 098fa5a577..b223813874 100644 --- a/Source/cmLocalGhsMultiGenerator.cxx +++ b/Source/cmLocalGhsMultiGenerator.cxx @@ -2,16 +2,13 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmLocalGhsMultiGenerator.h" -#include <algorithm> #include <utility> - -#include <cmext/algorithm> +#include <vector> #include "cmGeneratorTarget.h" #include "cmGhsMultiTargetGenerator.h" #include "cmGlobalGenerator.h" #include "cmSourceFile.h" -#include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -30,34 +27,16 @@ std::string cmLocalGhsMultiGenerator::GetTargetDirectory( return dir; } -void cmLocalGhsMultiGenerator::GenerateTargetsDepthFirst( - cmGeneratorTarget* target, std::vector<cmGeneratorTarget*>& remaining) -{ - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return; - } - // Find this target in the list of remaining targets. - auto it = std::find(remaining.begin(), remaining.end(), target); - if (it == remaining.end()) { - // This target was already handled. - return; - } - // Remove this target from the list of remaining targets because - // we are handling it now. - *it = nullptr; - - cmGhsMultiTargetGenerator tg(target); - tg.Generate(); -} - void cmLocalGhsMultiGenerator::Generate() { - std::vector<cmGeneratorTarget*> remaining; - cm::append(remaining, this->GetGeneratorTargets()); - for (auto& t : remaining) { - if (t) { - this->GenerateTargetsDepthFirst(t, remaining); + for (cmGeneratorTarget* gt : + this->GlobalGenerator->GetLocalGeneratorTargetsInOrder(this)) { + if (!gt->IsInBuildSystem()) { + continue; } + + cmGhsMultiTargetGenerator tg(gt); + tg.Generate(); } } diff --git a/Source/cmLocalGhsMultiGenerator.h b/Source/cmLocalGhsMultiGenerator.h index 2250e5745c..be32a945a7 100644 --- a/Source/cmLocalGhsMultiGenerator.h +++ b/Source/cmLocalGhsMultiGenerator.h @@ -1,11 +1,9 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocalGhsMultiGenerator_h -#define cmLocalGhsMultiGenerator_h +#pragma once #include <map> #include <string> -#include <vector> #include "cmLocalGenerator.h" @@ -38,10 +36,4 @@ public: void ComputeObjectFilenames( std::map<cmSourceFile const*, std::string>& mapping, cmGeneratorTarget const* gt = nullptr) override; - -private: - void GenerateTargetsDepthFirst(cmGeneratorTarget* target, - std::vector<cmGeneratorTarget*>& remaining); }; - -#endif diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 87e8aa4459..ad782ee392 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -84,13 +84,31 @@ void cmLocalNinjaGenerator::Generate() if (!showIncludesPrefix.empty()) { cmGlobalNinjaGenerator::WriteComment(this->GetRulesFileStream(), "localized /showIncludes string"); - this->GetRulesFileStream() - << "msvc_deps_prefix = " << showIncludesPrefix << "\n\n"; + this->GetRulesFileStream() << "msvc_deps_prefix = "; +#ifdef WIN32 + // Ninja uses the ANSI Windows APIs, so strings in the rules file + // typically need to be ANSI encoded. However, in this case the compiler + // is being invoked using the UTF-8 codepage so the /showIncludes prefix + // will be UTF-8 encoded on stdout. Ninja can't successfully compare this + // UTF-8 encoded prefix to the ANSI encoded msvc_deps_prefix if it + // contains any non-ASCII characters and dependency checking will fail. + // As a workaround, leave the msvc_deps_prefix UTF-8 encoded even though + // the rest of the file is ANSI encoded. + if (GetConsoleOutputCP() == CP_UTF8 && GetACP() != CP_UTF8) { + this->GetRulesFileStream().WriteRaw(showIncludesPrefix); + } else { + this->GetRulesFileStream() << showIncludesPrefix; + } +#else + // It's safe to use the standard encoding on other platforms. + this->GetRulesFileStream() << showIncludesPrefix; +#endif + this->GetRulesFileStream() << "\n\n"; } } for (const auto& target : this->GetGeneratorTargets()) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!target->IsInBuildSystem()) { continue; } auto tg = cmNinjaTargetGenerator::New(target.get()); @@ -102,9 +120,10 @@ void cmLocalNinjaGenerator::Generate() this->GetGlobalGenerator()->IsMultiConfig()) { cmNinjaBuild phonyAlias("phony"); this->GetGlobalNinjaGenerator()->AppendTargetOutputs( - target.get(), phonyAlias.Outputs, ""); + target.get(), phonyAlias.Outputs, "", DependOnTargetArtifact); this->GetGlobalNinjaGenerator()->AppendTargetOutputs( - target.get(), phonyAlias.ExplicitDeps, config); + target.get(), phonyAlias.ExplicitDeps, config, + DependOnTargetArtifact); this->GetGlobalNinjaGenerator()->WriteBuild( *this->GetGlobalNinjaGenerator()->GetConfigFileStream(config), phonyAlias); @@ -115,11 +134,12 @@ void cmLocalNinjaGenerator::Generate() if (!this->GetGlobalNinjaGenerator()->GetDefaultConfigs().empty()) { cmNinjaBuild phonyAlias("phony"); this->GetGlobalNinjaGenerator()->AppendTargetOutputs( - target.get(), phonyAlias.Outputs, ""); + target.get(), phonyAlias.Outputs, "", DependOnTargetArtifact); for (auto const& config : this->GetGlobalNinjaGenerator()->GetDefaultConfigs()) { this->GetGlobalNinjaGenerator()->AppendTargetOutputs( - target.get(), phonyAlias.ExplicitDeps, config); + target.get(), phonyAlias.ExplicitDeps, config, + DependOnTargetArtifact); } this->GetGlobalNinjaGenerator()->WriteBuild( *this->GetGlobalNinjaGenerator()->GetDefaultFileStream(), @@ -127,10 +147,11 @@ void cmLocalNinjaGenerator::Generate() } cmNinjaBuild phonyAlias("phony"); this->GetGlobalNinjaGenerator()->AppendTargetOutputs( - target.get(), phonyAlias.Outputs, "all"); + target.get(), phonyAlias.Outputs, "all", DependOnTargetArtifact); for (auto const& config : this->GetConfigNames()) { this->GetGlobalNinjaGenerator()->AppendTargetOutputs( - target.get(), phonyAlias.ExplicitDeps, config); + target.get(), phonyAlias.ExplicitDeps, config, + DependOnTargetArtifact); } this->GetGlobalNinjaGenerator()->WriteBuild( *this->GetGlobalNinjaGenerator()->GetDefaultFileStream(), @@ -291,7 +312,7 @@ void cmLocalNinjaGenerator::WritePools(std::ostream& os) cmProp jobpools = this->GetCMakeInstance()->GetState()->GetGlobalProperty("JOB_POOLS"); if (!jobpools) { - jobpools = this->GetMakefile()->GetDef("CMAKE_JOB_POOLS"); + jobpools = this->GetMakefile()->GetDefinition("CMAKE_JOB_POOLS"); } if (jobpools) { cmGlobalNinjaGenerator::WriteComment( @@ -342,7 +363,7 @@ void cmLocalNinjaGenerator::WriteProcessedMakefile(std::ostream& os) { cmGlobalNinjaGenerator::WriteDivider(os); os << "# Write statements declared in CMakeLists.txt:\n" - << "# " << this->Makefile->GetDefinition("CMAKE_CURRENT_LIST_FILE") + << "# " << this->Makefile->GetSafeDefinition("CMAKE_CURRENT_LIST_FILE") << '\n'; if (this->IsRootMakefile()) { os << "# Which is the root file.\n"; @@ -355,8 +376,8 @@ void cmLocalNinjaGenerator::AppendTargetOutputs(cmGeneratorTarget* target, cmNinjaDeps& outputs, const std::string& config) { - this->GetGlobalNinjaGenerator()->AppendTargetOutputs(target, outputs, - config); + this->GetGlobalNinjaGenerator()->AppendTargetOutputs(target, outputs, config, + DependOnTargetArtifact); } void cmLocalNinjaGenerator::AppendTargetDepends(cmGeneratorTarget* target, @@ -668,7 +689,7 @@ std::string cmLocalNinjaGenerator::MakeCustomLauncher( { cmProp property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM"); - if (!property_value || property_value->empty()) { + if (!cmNonempty(property_value)) { return std::string(); } diff --git a/Source/cmLocalNinjaGenerator.h b/Source/cmLocalNinjaGenerator.h index ef160e70f8..e81402cb49 100644 --- a/Source/cmLocalNinjaGenerator.h +++ b/Source/cmLocalNinjaGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocalNinjaGenerator_h -#define cmLocalNinjaGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -66,10 +65,10 @@ public: void AppendTargetOutputs(cmGeneratorTarget* target, cmNinjaDeps& outputs, const std::string& config); - void AppendTargetDepends( - cmGeneratorTarget* target, cmNinjaDeps& outputs, const std::string& config, - const std::string& fileConfig, - cmNinjaTargetDepends depends = DependOnTargetArtifact); + void AppendTargetDepends(cmGeneratorTarget* target, cmNinjaDeps& outputs, + const std::string& config, + const std::string& fileConfig, + cmNinjaTargetDepends depends); void AddCustomCommandTarget(cmCustomCommand const* cc, cmGeneratorTarget* target); @@ -121,5 +120,3 @@ private: CustomCommandTargetMap CustomCommandTargets; std::vector<cmCustomCommand const*> CustomCommands; }; - -#endif // ! cmLocalNinjaGenerator_h diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index de1461a79b..c877cf8593 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -38,6 +38,7 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTargetDepend.h" #include "cmVersion.h" #include "cmake.h" @@ -100,12 +101,22 @@ void cmLocalUnixMakefileGenerator3::Generate() // Generate the rule files for each target. cmGlobalUnixMakefileGenerator3* gg = static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator); - for (const auto& target : this->GetGeneratorTargets()) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + for (cmGeneratorTarget* gt : + this->GlobalGenerator->GetLocalGeneratorTargetsInOrder(this)) { + if (!gt->IsInBuildSystem()) { continue; } + + auto& gtVisited = this->GetCommandsVisited(gt); + auto& deps = this->GlobalGenerator->GetTargetDirectDepends(gt); + for (auto& d : deps) { + // Take the union of visited source files of custom commands + auto depVisited = this->GetCommandsVisited(d); + gtVisited.insert(depVisited.begin(), depVisited.end()); + } + std::unique_ptr<cmMakefileTargetGenerator> tg( - cmMakefileTargetGenerator::New(target.get())); + cmMakefileTargetGenerator::New(gt)); if (tg) { tg->WriteRuleFiles(); gg->RecordTargetProgress(tg.get()); @@ -137,7 +148,7 @@ void cmLocalUnixMakefileGenerator3::GetLocalObjectFiles( std::map<std::string, LocalObjectInfo>& localObjectFiles) { for (const auto& gt : this->GetGeneratorTargets()) { - if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!gt->CanCompileSources()) { continue; } std::vector<cmSourceFile const*> objectSources; @@ -235,7 +246,8 @@ void cmLocalUnixMakefileGenerator3::WriteLocalMakefile() for (LocalObjectEntry const& entry : localObjectFile.second) { if (entry.Language == "C" || entry.Language == "CXX" || - entry.Language == "CUDA" || entry.Language == "Fortran") { + entry.Language == "CUDA" || entry.Language == "Fortran" || + entry.Language == "ISPC") { // Right now, C, C++, Fortran and CUDA have both a preprocessor and the // ability to generate assembly code lang_has_preprocessor = true; @@ -518,9 +530,9 @@ void cmLocalUnixMakefileGenerator3::WriteMakeRule( // Mark the rule as symbolic if requested. if (symbolic) { - if (const char* sym = + if (cmProp sym = this->Makefile->GetDefinition("CMAKE_MAKE_SYMBOLIC_RULE")) { - os << tgt << space << ": " << sym << "\n"; + os << tgt << space << ": " << *sym << "\n"; } } @@ -832,9 +844,8 @@ void cmLocalUnixMakefileGenerator3::AppendRuleDepend( { // Add a dependency on the rule file itself unless an option to skip // it is specifically enabled by the user or project. - const char* nodep = - this->Makefile->GetDefinition("CMAKE_SKIP_RULE_DEPENDENCY"); - if (!nodep || cmIsOff(nodep)) { + cmProp nodep = this->Makefile->GetDefinition("CMAKE_SKIP_RULE_DEPENDENCY"); + if (cmIsOff(nodep)) { depends.emplace_back(ruleFileName); } } @@ -950,7 +961,7 @@ void cmLocalUnixMakefileGenerator3::AppendCustomCommand( std::string launcher; // Short-circuit if there is no launcher. const char* val = this->GetRuleLauncher(target, "RULE_LAUNCH_CUSTOM"); - if (val && *val) { + if (cmNonempty(val)) { // Expand rule variables referenced in the given launcher command. cmRulePlaceholderExpander::RuleVariables vars; vars.CMTargetName = target->GetName().c_str(); @@ -1394,22 +1405,22 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies( // Lookup useful directory information. if (haveDirectoryInfo) { // Test whether we need to force Unix paths. - if (const char* force = mf->GetDefinition("CMAKE_FORCE_UNIX_PATHS")) { + if (cmProp force = mf->GetDefinition("CMAKE_FORCE_UNIX_PATHS")) { if (!cmIsOff(force)) { cmSystemTools::SetForceUnixPaths(true); } } // Setup relative path top directories. - if (const char* relativePathTopSource = + if (cmProp relativePathTopSource = mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_SOURCE")) { this->StateSnapshot.GetDirectory().SetRelativePathTopSource( - relativePathTopSource); + relativePathTopSource->c_str()); } - if (const char* relativePathTopBinary = + if (cmProp relativePathTopBinary = mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_BINARY")) { this->StateSnapshot.GetDirectory().SetRelativePathTopBinary( - relativePathTopBinary); + relativePathTopBinary->c_str()); } } else { cmSystemTools::Error("Directory Information file not found"); @@ -1444,7 +1455,8 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies( // Create the scanner for this language std::unique_ptr<cmDepends> scanner; if (lang == "C" || lang == "CXX" || lang == "RC" || lang == "ASM" || - lang == "OBJC" || lang == "OBJCXX" || lang == "CUDA") { + lang == "OBJC" || lang == "OBJCXX" || lang == "CUDA" || + lang == "ISPC") { // TODO: Handle RC (resource files) dependencies correctly. scanner = cm::make_unique<cmDependsC>(this, targetDir, lang, &validDeps); } @@ -1476,13 +1488,13 @@ void cmLocalUnixMakefileGenerator3::CheckMultipleOutputs(bool verbose) cmMakefile* mf = this->Makefile; // Get the string listing the multiple output pairs. - const char* pairs_string = mf->GetDefinition("CMAKE_MULTIPLE_OUTPUT_PAIRS"); + cmProp pairs_string = mf->GetDefinition("CMAKE_MULTIPLE_OUTPUT_PAIRS"); if (!pairs_string) { return; } // Convert the string to a list and preserve empty entries. - std::vector<std::string> pairs = cmExpandedList(pairs_string, true); + std::vector<std::string> pairs = cmExpandedList(*pairs_string, true); for (auto i = pairs.begin(); i != pairs.end() && (i + 1) != pairs.end();) { const std::string& depender = *i++; const std::string& dependee = *i++; @@ -1648,9 +1660,9 @@ void cmLocalUnixMakefileGenerator3::WriteLocalAllRules( recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/preinstall"); commands.clear(); depends.clear(); - const char* noall = + cmProp noall = this->Makefile->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY"); - if (!noall || cmIsOff(noall)) { + if (cmIsOff(noall)) { // Drive the build before installing. depends.emplace_back("all"); } else if (regenerate) { @@ -1698,11 +1710,11 @@ void cmLocalUnixMakefileGenerator3::ClearDependencies(cmMakefile* mf, bool verbose) { // Get the list of target files to check - const char* infoDef = mf->GetDefinition("CMAKE_DEPEND_INFO_FILES"); + cmProp infoDef = mf->GetDefinition("CMAKE_DEPEND_INFO_FILES"); if (!infoDef) { return; } - std::vector<std::string> files = cmExpandedList(infoDef); + std::vector<std::string> files = cmExpandedList(*infoDef); // Each depend information file corresponds to a target. Clear the // dependencies for that target. @@ -1744,7 +1756,7 @@ public: return false; } // If it's an absolute path, check if it starts with the source - // direcotory: + // directory: return ( !(IsInDirectory(SourceDir, path) || IsInDirectory(BinaryDir, path))); } @@ -1806,10 +1818,10 @@ void cmLocalUnixMakefileGenerator3::WriteDependLanguageInfo( // Tell the dependency scanner what compiler is used. std::string cidVar = cmStrCat("CMAKE_", implicitLang.first, "_COMPILER_ID"); - const char* cid = this->Makefile->GetDefinition(cidVar); - if (cid && *cid) { + cmProp cid = this->Makefile->GetDefinition(cidVar); + if (cmNonempty(cid)) { cmakefileStream << "set(CMAKE_" << implicitLang.first - << "_COMPILER_ID \"" << cid << "\")\n"; + << "_COMPILER_ID \"" << *cid << "\")\n"; } if (implicitLang.first == "Fortran") { diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h index 2b07952bbc..8286d673fc 100644 --- a/Source/cmLocalUnixMakefileGenerator3.h +++ b/Source/cmLocalUnixMakefileGenerator3.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocalUnixMakefileGenerator3_h -#define cmLocalUnixMakefileGenerator3_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -20,6 +19,7 @@ class cmCustomCommandGenerator; class cmGeneratorTarget; class cmGlobalGenerator; class cmMakefile; +class cmSourceFile; /** \class cmLocalUnixMakefileGenerator3 * \brief Write a LocalUnix makefiles. @@ -295,6 +295,13 @@ private: bool ColorMakefile; bool SkipPreprocessedSourceRules; bool SkipAssemblySourceRules; -}; -#endif + std::set<cmSourceFile const*>& GetCommandsVisited( + cmGeneratorTarget const* target) + { + return this->CommandsVisited[target]; + }; + + std::map<cmGeneratorTarget const*, std::set<cmSourceFile const*>> + CommandsVisited; +}; diff --git a/Source/cmLocalVisualStudio10Generator.cxx b/Source/cmLocalVisualStudio10Generator.cxx index 9076e26d12..3ed49a03f4 100644 --- a/Source/cmLocalVisualStudio10Generator.cxx +++ b/Source/cmLocalVisualStudio10Generator.cxx @@ -66,33 +66,11 @@ cmLocalVisualStudio10Generator::~cmLocalVisualStudio10Generator() { } -void cmLocalVisualStudio10Generator::GenerateTargetsDepthFirst( - cmGeneratorTarget* target, std::vector<cmGeneratorTarget*>& remaining) +void cmLocalVisualStudio10Generator::GenerateTarget(cmGeneratorTarget* target) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - return; - } - // Find this target in the list of remaining targets. - auto it = std::find(remaining.begin(), remaining.end(), target); - if (it == remaining.end()) { - // This target was already handled. - return; - } - // Remove this target from the list of remaining targets because - // we are handling it now. - *it = nullptr; - auto& deps = this->GlobalGenerator->GetTargetDirectDepends(target); - for (auto& d : deps) { - // FIXME: Revise CreateSingleVCProj so we do not have to drop `const` here. - auto dependee = const_cast<cmGeneratorTarget*>(&*d); - GenerateTargetsDepthFirst(dependee, remaining); - // Take the union of visited source files of custom commands - auto visited = GetSourcesVisited(dependee); - GetSourcesVisited(target).insert(visited.begin(), visited.end()); - } if (static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator) ->TargetIsFortranOnly(target)) { - this->CreateSingleVCProj(target->GetName(), target); + this->cmLocalVisualStudio7Generator::GenerateTarget(target); } else { cmVisualStudio10TargetGenerator tg( target, @@ -102,18 +80,6 @@ void cmLocalVisualStudio10Generator::GenerateTargetsDepthFirst( } } -void cmLocalVisualStudio10Generator::Generate() -{ - std::vector<cmGeneratorTarget*> remaining; - cm::append(remaining, this->GetGeneratorTargets()); - for (auto& t : remaining) { - if (t) { - this->GenerateTargetsDepthFirst(t, remaining); - } - } - this->WriteStampFiles(); -} - void cmLocalVisualStudio10Generator::ReadAndStoreExternalGUID( const std::string& name, const char* path) { diff --git a/Source/cmLocalVisualStudio10Generator.h b/Source/cmLocalVisualStudio10Generator.h index 5c6400e9f6..45ee0822c3 100644 --- a/Source/cmLocalVisualStudio10Generator.h +++ b/Source/cmLocalVisualStudio10Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocalVisualStudio10Generator_h -#define cmLocalVisualStudio10Generator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -26,26 +25,13 @@ public: virtual ~cmLocalVisualStudio10Generator(); - /** - * Generate the makefile for this directory. - */ - void Generate() override; void ReadAndStoreExternalGUID(const std::string& name, const char* path) override; - std::set<cmSourceFile const*>& GetSourcesVisited(cmGeneratorTarget* target) - { - return SourcesVisited[target]; - }; - protected: const char* ReportErrorLabel() const override; bool CustomCommandUseLocal() const override { return true; } private: - void GenerateTargetsDepthFirst(cmGeneratorTarget* target, - std::vector<cmGeneratorTarget*>& remaining); - - std::map<cmGeneratorTarget*, std::set<cmSourceFile const*>> SourcesVisited; + void GenerateTarget(cmGeneratorTarget* target) override; }; -#endif diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index 5d50e2d2b3..d2cdb99a0a 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -66,7 +66,7 @@ void cmLocalVisualStudio7Generator::AddHelperCommands() // Now create GUIDs for targets const auto& tgts = this->GetGeneratorTargets(); for (const auto& l : tgts) { - if (l->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!l->IsInBuildSystem()) { continue; } cmProp path = l->GetProperty("EXTERNAL_MSPROJECT"); @@ -80,7 +80,24 @@ void cmLocalVisualStudio7Generator::AddHelperCommands() void cmLocalVisualStudio7Generator::Generate() { - this->WriteProjectFiles(); + // Create the project file for each target. + for (cmGeneratorTarget* gt : + this->GlobalGenerator->GetLocalGeneratorTargetsInOrder(this)) { + if (!gt->IsInBuildSystem() || gt->GetProperty("EXTERNAL_MSPROJECT")) { + continue; + } + + auto& gtVisited = this->GetSourcesVisited(gt); + auto& deps = this->GlobalGenerator->GetTargetDirectDepends(gt); + for (auto& d : deps) { + // Take the union of visited source files of custom commands + auto depVisited = this->GetSourcesVisited(d); + gtVisited.insert(depVisited.begin(), depVisited.end()); + } + + this->GenerateTarget(gt); + } + this->WriteStampFiles(); } @@ -111,35 +128,6 @@ void cmLocalVisualStudio7Generator::FixGlobalTargets() } } -// TODO -// for CommandLine= need to repleace quotes with " -// write out configurations -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()); - } - } - - // Get the set of targets in this directory. - const auto& tgts = this->GetGeneratorTargets(); - - // Create the project file for each target. - for (const auto& l : tgts) { - if (l->GetType() == cmStateEnums::INTERFACE_LIBRARY) { - continue; - } - // INCLUDE_EXTERNAL_MSPROJECT command only affects the workspace - // so don't build a projectfile for it - if (!l->GetProperty("EXTERNAL_MSPROJECT")) { - this->CreateSingleVCProj(l->GetName(), l.get()); - } - } -} - void cmLocalVisualStudio7Generator::WriteStampFiles() { // Touch a timestamp file used to determine when the project file is @@ -178,9 +166,9 @@ void cmLocalVisualStudio7Generator::WriteStampFiles() } } -void cmLocalVisualStudio7Generator::CreateSingleVCProj( - const std::string& lname, cmGeneratorTarget* target) +void cmLocalVisualStudio7Generator::GenerateTarget(cmGeneratorTarget* target) { + std::string const& lname = target->GetName(); cmGlobalVisualStudioGenerator* gg = static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator); this->FortranProject = gg->TargetIsFortranOnly(target); @@ -287,10 +275,9 @@ cmVS7FlagTable cmLocalVisualStudio7GeneratorFortranFlagTable[] = { { "SuppressStartupBanner", "nologo", "SuppressStartupBanner", "true", 0 }, { "SourceFileFormat", "fixed", "Use Fixed Format", "fileFormatFixed", 0 }, { "SourceFileFormat", "free", "Use Free Format", "fileFormatFree", 0 }, - { "DebugInformationFormat", "Zi", "full debug", "debugEnabled", 0 }, { "DebugInformationFormat", "debug:full", "full debug", "debugEnabled", 0 }, - { "DebugInformationFormat", "Z7", "c7 compat", "debugOldStyleInfo", 0 }, - { "DebugInformationFormat", "Zd", "line numbers", "debugLineInfoOnly", 0 }, + { "DebugInformationFormat", "debug:minimal", "line numbers", + "debugLineInfoOnly", 0 }, { "Optimization", "Od", "disable optimization", "optimizeDisabled", 0 }, { "Optimization", "O1", "min space", "optimizeMinSpace", 0 }, { "Optimization", "O3", "full optimize", "optimizeFull", 0 }, @@ -593,8 +580,10 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( std::ostream& fout, const std::string& configName, const std::string& libName, cmGeneratorTarget* target) { - const char* mfcFlag = this->Makefile->GetDefinition("CMAKE_MFC_FLAG"); - if (!mfcFlag) { + std::string mfcFlag; + if (cmProp p = this->Makefile->GetDefinition("CMAKE_MFC_FLAG")) { + mfcFlag = cmGeneratorExpression::Evaluate(*p, this, configName); + } else { mfcFlag = "0"; } cmGlobalVisualStudio7Generator* gg = @@ -629,9 +618,10 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( break; case cmStateEnums::UTILITY: case cmStateEnums::GLOBAL_TARGET: + case cmStateEnums::INTERFACE_LIBRARY: configType = "10"; CM_FALLTHROUGH; - default: + case cmStateEnums::UNKNOWN_LIBRARY: targetBuilds = false; break; } @@ -1086,15 +1076,17 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( } } std::string stackVar = cmStrCat("CMAKE_", linkLanguage, "_STACK_SIZE"); - const char* stackVal = this->Makefile->GetDefinition(stackVar); + cmProp stackVal = this->Makefile->GetDefinition(stackVar); if (stackVal) { - fout << "\t\t\t\tStackReserveSize=\"" << stackVal << "\"\n"; + fout << "\t\t\t\tStackReserveSize=\"" << *stackVal << "\"\n"; + } + if (!targetNames.ImportLibrary.empty()) { + temp = cmStrCat(target->GetDirectory( + configName, cmStateEnums::ImportLibraryArtifact), + '/', targetNames.ImportLibrary); + fout << "\t\t\t\tImportLibrary=\"" + << this->ConvertToXMLOutputPathSingle(temp) << "\""; } - temp = cmStrCat( - target->GetDirectory(configName, cmStateEnums::ImportLibraryArtifact), - '/', targetNames.ImportLibrary); - fout << "\t\t\t\tImportLibrary=\"" - << this->ConvertToXMLOutputPathSingle(temp) << "\""; if (this->FortranProject) { fout << "\n\t\t\t\tLinkDLL=\"true\""; } @@ -1112,7 +1104,7 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( cmComputeLinkInformation& cli = *pcli; std::string linkLanguage = cli.GetLinkLanguage(); - bool isWin32Executable = target->GetPropertyAsBool("WIN32_EXECUTABLE"); + bool isWin32Executable = target->IsWin32Executable(configName); // Compute the variable name to lookup standard libraries for this // language. @@ -1175,9 +1167,9 @@ void cmLocalVisualStudio7Generator::OutputBuildTool( << "\"\n"; } std::string stackVar = cmStrCat("CMAKE_", linkLanguage, "_STACK_SIZE"); - const char* stackVal = this->Makefile->GetDefinition(stackVar); + cmProp stackVal = this->Makefile->GetDefinition(stackVar); if (stackVal) { - fout << "\t\t\t\tStackReserveSize=\"" << stackVal << "\""; + fout << "\t\t\t\tStackReserveSize=\"" << *stackVal << "\""; } temp = cmStrCat( target->GetDirectory(configName, cmStateEnums::ImportLibraryArtifact), @@ -1331,8 +1323,8 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout, const std::string& libName, cmGeneratorTarget* target) { - std::vector<std::string> configs; - this->Makefile->GetConfigurations(configs); + std::vector<std::string> configs = + this->Makefile->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); // We may be modifying the source groups temporarily, so make a copy. std::vector<cmSourceGroup> sourceGroups = this->Makefile->GetSourceGroups(); @@ -1580,8 +1572,9 @@ cmLocalVisualStudio7GeneratorFCInfo::cmLocalVisualStudio7GeneratorFCInfo( std::string cmLocalVisualStudio7Generator::ComputeLongestObjectDirectory( cmGeneratorTarget const* target) const { - std::vector<std::string> configs; - target->Target->GetMakefile()->GetConfigurations(configs); + std::vector<std::string> configs = + target->Target->GetMakefile()->GetGeneratorConfigs( + cmMakefile::ExcludeEmptyConfig); // Compute the maximum length configuration name. std::string config_max; @@ -1632,12 +1625,15 @@ bool cmLocalVisualStudio7Generator::WriteGroup( this->WriteVCProjBeginGroup(fout, name.c_str(), ""); } + auto& sourcesVisited = this->GetSourcesVisited(target); + // Loop through each source in the source group. for (const cmSourceFile* sf : sourceFiles) { std::string source = sf->GetFullPath(); if (source != libName || target->GetType() == cmStateEnums::UTILITY || - target->GetType() == cmStateEnums::GLOBAL_TARGET) { + target->GetType() == cmStateEnums::GLOBAL_TARGET || + target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { // Look up the source kind and configs. std::map<cmSourceFile const*, size_t>::const_iterator map_it = sources.Index.find(sf); @@ -1654,7 +1650,10 @@ bool cmLocalVisualStudio7Generator::WriteGroup( // build it, then it will. fout << "\t\t\t\tRelativePath=\"" << d << "\">\n"; if (cmCustomCommand const* command = sf->GetCustomCommand()) { - this->WriteCustomRule(fout, configs, source.c_str(), *command, fcinfo); + if (sourcesVisited.insert(sf).second) { + this->WriteCustomRule(fout, configs, source.c_str(), *command, + fcinfo); + } } else if (!fcinfo.FileConfigMap.empty()) { const char* aCompilerTool = "VCCLCompilerTool"; std::string ppLang = "CXX"; @@ -1936,6 +1935,7 @@ void cmLocalVisualStudio7Generator::WriteProjectStartFortran( const char* keyword = p ? p->c_str() : "Console Application"; const char* projectType = 0; switch (target->GetType()) { + case cmStateEnums::OBJECT_LIBRARY: case cmStateEnums::STATIC_LIBRARY: projectType = "typeStaticLibrary"; if (keyword) { @@ -1957,7 +1957,8 @@ void cmLocalVisualStudio7Generator::WriteProjectStartFortran( break; case cmStateEnums::UTILITY: case cmStateEnums::GLOBAL_TARGET: - default: + case cmStateEnums::INTERFACE_LIBRARY: + case cmStateEnums::UNKNOWN_LIBRARY: break; } if (projectType) { diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h index 8b9b8ad2aa..6e06c09879 100644 --- a/Source/cmLocalVisualStudio7Generator.h +++ b/Source/cmLocalVisualStudio7Generator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocalVisualStudio7Generator_h -#define cmLocalVisualStudio7Generator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -84,8 +83,14 @@ public: virtual void ReadAndStoreExternalGUID(const std::string& name, const char* path); + std::set<cmSourceFile const*>& GetSourcesVisited( + cmGeneratorTarget const* target) + { + return this->SourcesVisited[target]; + }; + protected: - void CreateSingleVCProj(const std::string& lname, cmGeneratorTarget* tgt); + virtual void GenerateTarget(cmGeneratorTarget* target); private: using Options = cmVS7GeneratorOptions; @@ -93,7 +98,6 @@ private: std::string GetBuildTypeLinkerFlags(std::string rootLinkerFlags, const std::string& configName); void FixGlobalTargets(); - void WriteProjectFiles(); void WriteVCProjHeader(std::ostream& fout, const std::string& libName, cmGeneratorTarget* tgt, std::vector<cmSourceGroup>& sgs); @@ -150,6 +154,7 @@ private: bool FortranProject; bool WindowsCEProject; std::unique_ptr<cmLocalVisualStudio7GeneratorInternals> Internal; -}; -#endif + std::map<cmGeneratorTarget const*, std::set<cmSourceFile const*>> + SourcesVisited; +}; diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx index ebd4f96a15..6d6ed9f400 100644 --- a/Source/cmLocalVisualStudioGenerator.cxx +++ b/Source/cmLocalVisualStudioGenerator.cxx @@ -172,13 +172,12 @@ std::string cmLocalVisualStudioGenerator::ConstructScript( // for visual studio IDE add extra stuff to the PATH // if CMAKE_MSVCIDE_RUN_PATH is set. if (this->Makefile->GetDefinition("MSVC_IDE")) { - const char* extraPath = - this->Makefile->GetDefinition("CMAKE_MSVCIDE_RUN_PATH"); + cmProp extraPath = this->Makefile->GetDefinition("CMAKE_MSVCIDE_RUN_PATH"); if (extraPath) { script += newline; newline = newline_text; script += "set PATH="; - script += extraPath; + script += *extraPath; script += ";%PATH%"; } } diff --git a/Source/cmLocalVisualStudioGenerator.h b/Source/cmLocalVisualStudioGenerator.h index 585eb3c335..91fb6b0619 100644 --- a/Source/cmLocalVisualStudioGenerator.h +++ b/Source/cmLocalVisualStudioGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocalVisualStudioGenerator_h -#define cmLocalVisualStudioGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -56,5 +55,3 @@ protected: std::unique_ptr<cmCustomCommand> MaybeCreateImplibDir( cmGeneratorTarget* target, const std::string& config, bool isFortran); }; - -#endif diff --git a/Source/cmLocalXCodeGenerator.h b/Source/cmLocalXCodeGenerator.h index 42de20b470..dd038b462d 100644 --- a/Source/cmLocalXCodeGenerator.h +++ b/Source/cmLocalXCodeGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocalXCodeGenerator_h -#define cmLocalXCodeGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -40,5 +39,3 @@ public: private: }; - -#endif diff --git a/Source/cmLocale.h b/Source/cmLocale.h index c44a42db7f..f7636acb71 100644 --- a/Source/cmLocale.h +++ b/Source/cmLocale.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmLocale_h -#define cmLocale_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -25,5 +24,3 @@ public: private: std::string OldLocale; }; - -#endif diff --git a/Source/cmMSVC60LinkLineComputer.h b/Source/cmMSVC60LinkLineComputer.h index d767914b92..0a303abd24 100644 --- a/Source/cmMSVC60LinkLineComputer.h +++ b/Source/cmMSVC60LinkLineComputer.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMSVC60LinkLineComputer_h -#define cmMSVC60LinkLineComputer_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -25,5 +24,3 @@ public: std::string ConvertToLinkReference(std::string const& input) const override; }; - -#endif diff --git a/Source/cmMachO.h b/Source/cmMachO.h index 0c44b5564e..be92c9550c 100644 --- a/Source/cmMachO.h +++ b/Source/cmMachO.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMachO_h -#define cmMachO_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -43,5 +42,3 @@ private: bool Valid() const; std::unique_ptr<cmMachOInternal> Internal; }; - -#endif diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index c88b343182..98f88c1057 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -81,17 +81,14 @@ bool cmMacroHelperCommand::operator()( argVs.emplace_back(argvName); } // Invoke all the functions that were collected in the block. - cmListFileFunction newLFF; // for each function for (cmListFileFunction const& func : this->Functions) { // Replace the formal arguments and then invoke the command. - newLFF.Arguments.clear(); - newLFF.Arguments.reserve(func.Arguments.size()); - newLFF.Name = func.Name; - newLFF.Line = func.Line; + std::vector<cmListFileArgument> newLFFArgs; + newLFFArgs.reserve(func.Arguments().size()); // for each argument of the current function - for (cmListFileArgument const& k : func.Arguments) { + for (cmListFileArgument const& k : func.Arguments()) { cmListFileArgument arg; arg.Value = k.Value; if (k.Delim != cmListFileArgument::Bracket) { @@ -116,8 +113,10 @@ bool cmMacroHelperCommand::operator()( } arg.Delim = k.Delim; arg.Line = k.Line; - newLFF.Arguments.push_back(std::move(arg)); + newLFFArgs.push_back(std::move(arg)); } + cmListFileFunction newLFF{ func.OriginalName(), func.Line(), + std::move(newLFFArgs) }; cmExecutionStatus status(makefile); if (!makefile.ExecuteCommand(newLFF, status) || status.GetNestedError()) { // The error message should have already included the call stack @@ -157,8 +156,7 @@ bool cmMacroFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff, cmMakefile& mf) const { std::vector<std::string> expandedArguments; - mf.ExpandArguments(lff.Arguments, expandedArguments, - this->GetStartingContext().FilePath.c_str()); + mf.ExpandArguments(lff.Arguments(), expandedArguments); return expandedArguments.empty() || expandedArguments[0] == this->Args[0]; } diff --git a/Source/cmMacroCommand.h b/Source/cmMacroCommand.h index 25091eacf7..b65a887d59 100644 --- a/Source/cmMacroCommand.h +++ b/Source/cmMacroCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMacroCommand_h -#define cmMacroCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -13,5 +12,3 @@ class cmExecutionStatus; /// Starts macro() ... endmacro() block bool cmMacroCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmMakeDirectoryCommand.h b/Source/cmMakeDirectoryCommand.h index 2474383bb0..340bca8887 100644 --- a/Source/cmMakeDirectoryCommand.h +++ b/Source/cmMakeDirectoryCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMakeDirectoryCommand_h -#define cmMakeDirectoryCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -21,5 +20,3 @@ class cmExecutionStatus; */ bool cmMakeDirectoryCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index db5cee9bda..39468417e5 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -16,12 +16,15 @@ #include <cm/iterator> #include <cm/memory> #include <cm/optional> +#include <cm/type_traits> // IWYU pragma: keep #include <cm/vector> #include <cmext/algorithm> #include <cmext/string_view> -#include <cm3p/json/value.h> -#include <cm3p/json/writer.h> +#ifndef CMAKE_BOOTSTRAP +# include <cm3p/json/value.h> +# include <cm3p/json/writer.h> +#endif #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" @@ -34,6 +37,7 @@ #include "cmExecutionStatus.h" #include "cmExpandedCommandArgument.h" // IWYU pragma: keep #include "cmExportBuildFileGenerator.h" +#include "cmFSPermissions.h" #include "cmFileLockPool.h" #include "cmFunctionBlocker.h" #include "cmGeneratedFileStream.h" @@ -68,6 +72,8 @@ class cmMessenger; +using namespace cmFSPermissions; + cmDirectoryId::cmDirectoryId(std::string s) : String(std::move(s)) { @@ -82,7 +88,6 @@ cmMakefile::cmMakefile(cmGlobalGenerator* globalGenerator, { this->IsSourceFileTryCompile = false; - this->WarnUnused = this->GetCMakeInstance()->GetWarnUnused(); this->CheckSystemVars = this->GetCMakeInstance()->GetCheckSystemVars(); this->SuppressSideEffects = false; @@ -190,18 +195,18 @@ void cmMakefile::MaybeWarnCMP0074(std::string const& pkg) { // Warn if a <pkg>_ROOT variable we may use is set. std::string const varName = pkg + "_ROOT"; - const char* var = this->GetDefinition(varName); + cmProp var = this->GetDefinition(varName); std::string env; cmSystemTools::GetEnv(varName, env); - bool const haveVar = var && *var; + bool const haveVar = cmNonempty(var); bool const haveEnv = !env.empty(); if ((haveVar || haveEnv) && this->WarnedCMP0074.insert(varName).second) { std::ostringstream w; w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0074) << "\n"; if (haveVar) { w << "CMake variable " << varName << " is set to:\n" - << " " << var << "\n"; + << " " << *var << "\n"; } if (haveEnv) { w << "Environment variable " << varName << " is set to:\n" @@ -270,31 +275,14 @@ cmListFileBacktrace cmMakefile::GetBacktrace() const return this->Backtrace; } -cmListFileBacktrace cmMakefile::GetBacktrace(cmCommandContext const& cc) const -{ - cmListFileContext lfc; - lfc.Name = cc.Name.Original; - lfc.Line = cc.Line; - lfc.FilePath = this->StateSnapshot.GetExecutionListFile(); - return this->Backtrace.Push(lfc); -} - -cmListFileContext cmMakefile::GetExecutionContext() const -{ - cmListFileContext const& cur = this->Backtrace.Top(); - cmListFileContext lfc; - lfc.Name = cur.Name; - lfc.Line = cur.Line; - lfc.FilePath = this->StateSnapshot.GetExecutionListFile(); - return lfc; -} - -void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const +void cmMakefile::PrintCommandTrace( + cmListFileFunction const& lff, + cm::optional<std::string> const& deferId) const { // Check if current file in the list of requested to trace... std::vector<std::string> const& trace_only_this_files = this->GetCMakeInstance()->GetTraceSources(); - std::string const& full_path = this->GetExecutionFilePath(); + std::string const& full_path = this->GetBacktrace().Top().FilePath; std::string const& only_filename = cmSystemTools::GetFilenameName(full_path); bool trace = trace_only_this_files.empty(); if (!trace) { @@ -318,9 +306,9 @@ void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const std::string temp; bool expand = this->GetCMakeInstance()->GetTraceExpand(); - args.reserve(lff.Arguments.size()); - for (cmListFileArgument const& arg : lff.Arguments) { - if (expand) { + args.reserve(lff.Arguments().size()); + for (cmListFileArgument const& arg : lff.Arguments()) { + if (expand && arg.Delim != cmListFileArgument::Bracket) { temp = arg.Value; this->ExpandVariablesInString(temp); args.push_back(temp); @@ -336,8 +324,11 @@ void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const Json::StreamWriterBuilder builder; builder["indentation"] = ""; val["file"] = full_path; - val["line"] = static_cast<Json::Value::Int64>(lff.Line); - val["cmd"] = lff.Name.Original; + val["line"] = static_cast<Json::Value::Int64>(lff.Line()); + if (deferId) { + val["defer"] = *deferId; + } + val["cmd"] = lff.OriginalName(); val["args"] = Json::Value(Json::arrayValue); for (std::string const& arg : args) { val["args"].append(arg); @@ -350,8 +341,11 @@ void cmMakefile::PrintCommandTrace(const cmListFileFunction& lff) const break; } case cmake::TraceFormat::TRACE_HUMAN: - msg << full_path << "(" << lff.Line << "): "; - msg << lff.Name.Original << "("; + msg << full_path << "(" << lff.Line() << "):"; + if (deferId) { + msg << "DEFERRED:" << *deferId << ":"; + } + msg << " " << lff.OriginalName() << "("; for (std::string const& arg : args) { msg << arg << " "; @@ -376,11 +370,12 @@ class cmMakefileCall { public: cmMakefileCall(cmMakefile* mf, cmListFileFunction const& lff, - cmExecutionStatus& status) + cm::optional<std::string> deferId, cmExecutionStatus& status) : Makefile(mf) { cmListFileContext const& lfc = cmListFileContext::FromCommandContext( - lff, this->Makefile->StateSnapshot.GetExecutionListFile()); + lff, this->Makefile->StateSnapshot.GetExecutionListFile(), + std::move(deferId)); this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc); ++this->Makefile->RecursionDepth; this->Makefile->ExecutionStatusStack.push_back(&status); @@ -417,7 +412,8 @@ void cmMakefile::OnExecuteCommand(std::function<void()> callback) } bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, - cmExecutionStatus& status) + cmExecutionStatus& status, + cm::optional<std::string> deferId) { bool result = true; @@ -432,14 +428,14 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, } // Place this call on the call stack. - cmMakefileCall stack_manager(this, lff, status); + cmMakefileCall stack_manager(this, lff, std::move(deferId), status); static_cast<void>(stack_manager); // Check for maximum recursion depth. int depth = CMake_DEFAULT_RECURSION_LIMIT; - const char* depthStr = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH"); + cmProp depthStr = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH"); if (depthStr) { - std::istringstream s(depthStr); + std::istringstream s(*depthStr); int d; if (s >> d) { depth = d; @@ -455,21 +451,21 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, // Lookup the command prototype. if (cmState::Command command = - this->GetState()->GetCommandByExactName(lff.Name.Lower)) { + this->GetState()->GetCommandByExactName(lff.LowerCaseName())) { // Decide whether to invoke the command. if (!cmSystemTools::GetFatalErrorOccured()) { // if trace is enabled, print out invoke information if (this->GetCMakeInstance()->GetTrace()) { - this->PrintCommandTrace(lff); + this->PrintCommandTrace(lff, this->Backtrace.Top().DeferId); } // Try invoking the command. - bool invokeSucceeded = command(lff.Arguments, status); + bool invokeSucceeded = command(lff.Arguments(), status); bool hadNestedError = status.GetNestedError(); if (!invokeSucceeded || hadNestedError) { if (!hadNestedError) { // The command invocation requested that we report an error. std::string const error = - std::string(lff.Name.Original) + " " + status.GetError(); + std::string(lff.OriginalName()) + " " + status.GetError(); this->IssueMessage(MessageType::FATAL_ERROR, error); } result = false; @@ -481,7 +477,7 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff, } else { if (!cmSystemTools::GetFatalErrorOccured()) { std::string error = - cmStrCat("Unknown CMake command \"", lff.Name.Original, "\"."); + cmStrCat("Unknown CMake command \"", lff.OriginalName(), "\"."); this->IssueMessage(MessageType::FATAL_ERROR, error); result = false; cmSystemTools::SetFatalErrorOccured(); @@ -593,7 +589,7 @@ void cmMakefile::IncludeScope::EnforceCMP0011() std::ostringstream w; w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0011) << "\n" << "The included script\n " - << this->Makefile->GetExecutionFilePath() << "\n" + << this->Makefile->GetBacktrace().Top().FilePath << "\n" << "affects policy settings. " << "CMake is implying the NO_POLICY_SCOPE option for compatibility, " << "so the effects are applied to the including context."; @@ -606,7 +602,7 @@ void cmMakefile::IncludeScope::EnforceCMP0011() /* clang-format off */ e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0011) << "\n" << "The included script\n " - << this->Makefile->GetExecutionFilePath() << "\n" + << this->Makefile->GetBacktrace().Top().FilePath << "\n" << "affects policy settings, so it requires this policy to be set."; /* clang-format on */ this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); @@ -623,8 +619,8 @@ void cmMakefile::IncludeScope::EnforceCMP0011() bool cmMakefile::ReadDependentFile(const std::string& filename, bool noPolicyScope) { - if (const char* def = this->GetDefinition("CMAKE_CURRENT_LIST_FILE")) { - this->AddDefinition("CMAKE_PARENT_LIST_FILE", def); + if (cmProp def = this->GetDefinition("CMAKE_CURRENT_LIST_FILE")) { + this->AddDefinition("CMAKE_PARENT_LIST_FILE", *def); } std::string filenametoread = cmSystemTools::CollapseFullPath( filename, this->GetCurrentSourceDirectory()); @@ -637,7 +633,7 @@ bool cmMakefile::ReadDependentFile(const std::string& filename, return false; } - this->ReadListFile(listFile, filenametoread); + this->RunListFile(listFile, filenametoread); if (cmSystemTools::GetFatalErrorOccured()) { incScope.Quiet(); } @@ -678,6 +674,53 @@ private: bool ReportError; }; +class cmMakefile::DeferScope +{ +public: + DeferScope(cmMakefile* mf, std::string const& deferredInFile) + : Makefile(mf) + { + cmListFileContext lfc; + lfc.Line = cmListFileContext::DeferPlaceholderLine; + lfc.FilePath = deferredInFile; + this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc); + this->Makefile->DeferRunning = true; + } + + ~DeferScope() + { + this->Makefile->DeferRunning = false; + this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); + } + + DeferScope(const DeferScope&) = delete; + DeferScope& operator=(const DeferScope&) = delete; + +private: + cmMakefile* Makefile; +}; + +class cmMakefile::DeferCallScope +{ +public: + DeferCallScope(cmMakefile* mf, std::string const& deferredFromFile) + : Makefile(mf) + { + this->Makefile->StateSnapshot = + this->Makefile->GetState()->CreateDeferCallSnapshot( + this->Makefile->StateSnapshot, deferredFromFile); + assert(this->Makefile->StateSnapshot.IsValid()); + } + + ~DeferCallScope() { this->Makefile->PopSnapshot(); } + + DeferCallScope(const DeferCallScope&) = delete; + DeferCallScope& operator=(const DeferCallScope&) = delete; + +private: + cmMakefile* Makefile; +}; + bool cmMakefile::ReadListFile(const std::string& filename) { std::string filenametoread = cmSystemTools::CollapseFullPath( @@ -691,7 +734,7 @@ bool cmMakefile::ReadListFile(const std::string& filename) return false; } - this->ReadListFile(listFile, filenametoread); + this->RunListFile(listFile, filenametoread); if (cmSystemTools::GetFatalErrorOccured()) { scope.Quiet(); } @@ -712,15 +755,16 @@ bool cmMakefile::ReadListFileAsString(const std::string& content, return false; } - this->ReadListFile(listFile, filenametoread); + this->RunListFile(listFile, filenametoread); if (cmSystemTools::GetFatalErrorOccured()) { scope.Quiet(); } return true; } -void cmMakefile::ReadListFile(cmListFile const& listFile, - std::string const& filenametoread) +void cmMakefile::RunListFile(cmListFile const& listFile, + std::string const& filenametoread, + DeferCommands* defer) { // add this list file to the list of dependencies this->ListFiles.push_back(filenametoread); @@ -750,7 +794,33 @@ void cmMakefile::ReadListFile(cmListFile const& listFile, break; } } - this->CheckForUnusedVariables(); + + // Run any deferred commands. + if (defer) { + // Add a backtrace level indicating calls are deferred. + DeferScope scope(this, filenametoread); + + // Iterate by index in case one deferred call schedules another. + // NOLINTNEXTLINE(modernize-loop-convert) + for (size_t i = 0; i < defer->Commands.size(); ++i) { + DeferCommand& d = defer->Commands[i]; + if (d.Id.empty()) { + // Cancelled. + continue; + } + // Mark as executed. + std::string id = std::move(d.Id); + + // The deferred call may have come from another file. + DeferCallScope callScope(this, d.FilePath); + + cmExecutionStatus status(*this); + this->ExecuteCommand(d.Command, status, std::move(id)); + if (cmSystemTools::GetFatalErrorOccured()) { + break; + } + } + } this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentParentFile); this->AddDefinition("CMAKE_CURRENT_LIST_FILE", currentFile); @@ -798,15 +868,15 @@ void cmMakefile::EnforceDirectoryLevelRules() const } void cmMakefile::AddEvaluationFile( - const std::string& inputFile, + const std::string& inputFile, const std::string& targetName, std::unique_ptr<cmCompiledGeneratorExpression> outputName, std::unique_ptr<cmCompiledGeneratorExpression> condition, bool inputIsContent) { this->EvaluationFiles.push_back( cm::make_unique<cmGeneratorExpressionEvaluationFile>( - inputFile, std::move(outputName), std::move(condition), inputIsContent, - this->GetPolicyStatus(cmPolicies::CMP0070))); + inputFile, targetName, std::move(outputName), std::move(condition), + inputIsContent, this->GetPolicyStatus(cmPolicies::CMP0070))); } const std::vector<std::unique_ptr<cmGeneratorExpressionEvaluationFile>>& @@ -888,9 +958,10 @@ void cmMakefile::DoGenerate(cmLocalGenerator& lg) void cmMakefile::Generate(cmLocalGenerator& lg) { this->DoGenerate(lg); - const char* oldValue = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY"); + cmProp oldValue = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY"); if (oldValue && - cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, oldValue, "2.4")) { + cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, oldValue->c_str(), + "2.4")) { this->GetCMakeInstance()->IssueMessage( MessageType::FATAL_ERROR, "You have set CMAKE_BACKWARDS_COMPATIBILITY to a CMake version less " @@ -1426,31 +1497,31 @@ void cmMakefile::InitializeFromParent(cmMakefile* parent) { const char* prop = "IMPLICIT_DEPENDS_INCLUDE_TRANSFORM"; cmProp p = parent->GetProperty(prop); - this->SetProperty(prop, p ? p->c_str() : nullptr); + this->SetProperty(prop, cmToCStr(p)); } // compile definitions property and per-config versions cmPolicies::PolicyStatus polSt = this->GetPolicyStatus(cmPolicies::CMP0043); if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { cmProp p = parent->GetProperty("COMPILE_DEFINITIONS"); - this->SetProperty("COMPILE_DEFINITIONS", p ? p->c_str() : nullptr); - std::vector<std::string> configs; - this->GetConfigurations(configs); + this->SetProperty("COMPILE_DEFINITIONS", cmToCStr(p)); + std::vector<std::string> configs = + this->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); for (std::string const& config : configs) { std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config)); cmProp prop = parent->GetProperty(defPropName); - this->SetProperty(defPropName, prop ? prop->c_str() : nullptr); + this->SetProperty(defPropName, cmToCStr(prop)); } } // labels cmProp p = parent->GetProperty("LABELS"); - this->SetProperty("LABELS", p ? p->c_str() : nullptr); + this->SetProperty("LABELS", cmToCStr(p)); // link libraries p = parent->GetProperty("LINK_LIBRARIES"); - this->SetProperty("LINK_LIBRARIES", p ? p->c_str() : nullptr); + this->SetProperty("LINK_LIBRARIES", cmToCStr(p)); // the initial project name this->StateSnapshot.SetProjectName(parent->StateSnapshot.GetProjectName()); @@ -1513,8 +1584,6 @@ void cmMakefile::PopFunctionScope(bool reportError) #endif this->PopLoopBlockBarrier(); - - this->CheckForUnusedVariables(); } void cmMakefile::PushMacroScope(std::string const& fileName, @@ -1621,7 +1690,7 @@ void cmMakefile::Configure() bool hasVersion = false; // search for the right policy command for (cmListFileFunction const& func : listFile.Functions) { - if (func.Name.Lower == "cmake_minimum_required") { + if (func.LowerCaseName() == "cmake_minimum_required") { hasVersion = true; break; } @@ -1648,7 +1717,7 @@ void cmMakefile::Configure() allowedCommands.insert("message"); isProblem = false; for (cmListFileFunction const& func : listFile.Functions) { - if (!cm::contains(allowedCommands, func.Name.Lower)) { + if (!cm::contains(allowedCommands, func.LowerCaseName())) { isProblem = true; break; } @@ -1661,13 +1730,14 @@ void cmMakefile::Configure() this->SetCheckCMP0000(true); // Implicitly set the version for the user. - this->SetPolicyVersion("2.4", std::string()); + cmPolicies::ApplyPolicyVersion(this, 2, 4, 0, + cmPolicies::WarnCompat::Off); } } bool hasProject = false; // search for a project command for (cmListFileFunction const& func : listFile.Functions) { - if (func.Name.Lower == "project") { + if (func.LowerCaseName() == "project") { hasProject = true; break; } @@ -1684,17 +1754,19 @@ void cmMakefile::Configure() "CMake is pretending there is a \"project(Project)\" command on " "the first line.", this->Backtrace); - cmListFileFunction project; - project.Name.Lower = "project"; - project.Arguments.emplace_back("Project", cmListFileArgument::Unquoted, - 0); - project.Arguments.emplace_back("__CMAKE_INJECTED_PROJECT_COMMAND__", - cmListFileArgument::Unquoted, 0); + cmListFileFunction project{ "project", + 0, + { { "Project", cmListFileArgument::Unquoted, + 0 }, + { "__CMAKE_INJECTED_PROJECT_COMMAND__", + cmListFileArgument::Unquoted, 0 } } }; listFile.Functions.insert(listFile.Functions.begin(), project); } } - this->ReadListFile(listFile, currentStart); + this->Defer = cm::make_unique<DeferCommands>(); + this->RunListFile(listFile, currentStart, this->Defer.get()); + this->Defer.reset(); if (cmSystemTools::GetFatalErrorOccured()) { scope.Quiet(); } @@ -1769,6 +1841,13 @@ void cmMakefile::AddSubDirectory(const std::string& srcPath, const std::string& binPath, bool excludeFromAll, bool immediate) { + if (this->DeferRunning) { + this->IssueMessage( + MessageType::FATAL_ERROR, + "Subdirectories may not be created during deferred execution."); + return; + } + // Make sure the binary directory is unique. if (!this->EnforceUniqueDir(srcPath, binPath)) { return; @@ -1860,9 +1939,6 @@ void cmMakefile::AddSystemIncludeDirectories(const std::set<std::string>& incs) void cmMakefile::AddDefinition(const std::string& name, cm::string_view value) { - if (this->VariableInitialized(name)) { - this->LogUnused("changing definition", name); - } this->StateSnapshot.SetDefinition(name, value); #ifndef CMAKE_BOOTSTRAP @@ -1923,16 +1999,6 @@ void cmMakefile::AddCacheDefinition(const std::string& name, const char* value, this->StateSnapshot.RemoveDefinition(name); } -void cmMakefile::CheckForUnusedVariables() const -{ - if (!this->WarnUnused) { - return; - } - for (const std::string& key : this->StateSnapshot.UnusedKeys()) { - this->LogUnused("out of scope", key); - } -} - void cmMakefile::MarkVariableAsUsed(const std::string& var) { this->StateSnapshot.GetDefinition(var); @@ -1960,29 +2026,8 @@ void cmMakefile::MaybeWarnUninitialized(std::string const& variable, } } -void cmMakefile::LogUnused(const char* reason, const std::string& name) const -{ - if (this->WarnUnused) { - std::string path; - if (!this->ExecutionStatusStack.empty()) { - path = this->GetExecutionContext().FilePath; - } else { - path = cmStrCat(this->GetCurrentSourceDirectory(), "/CMakeLists.txt"); - } - - if (this->CheckSystemVars || this->IsProjectFile(path.c_str())) { - std::ostringstream msg; - msg << "unused variable (" << reason << ") \'" << name << "\'"; - this->IssueMessage(MessageType::AUTHOR_WARNING, msg.str()); - } - } -} - void cmMakefile::RemoveDefinition(const std::string& name) { - if (this->VariableInitialized(name)) { - this->LogUnused("unsetting", name); - } this->StateSnapshot.RemoveDefinition(name); #ifndef CMAKE_BOOTSTRAP cmVariableWatch* vv = this->GetVariableWatch(); @@ -2410,8 +2455,10 @@ cmSourceGroup* cmMakefile::GetOrCreateSourceGroup( cmSourceGroup* cmMakefile::GetOrCreateSourceGroup(const std::string& name) { - const char* delimiters = this->GetDefinition("SOURCE_GROUP_DELIMITER"); - if (delimiters == nullptr) { + std::string delimiters; + if (cmProp p = this->GetDefinition("SOURCE_GROUP_DELIMITER")) { + delimiters = *p; + } else { delimiters = "/\\"; } return this->GetOrCreateSourceGroup(cmTokenize(name, delimiters)); @@ -2555,22 +2602,21 @@ void cmMakefile::ExpandVariablesCMP0019() bool cmMakefile::IsOn(const std::string& name) const { - const char* value = this->GetDefinition(name); - return cmIsOn(value); + return cmIsOn(this->GetDefinition(name)); } bool cmMakefile::IsSet(const std::string& name) const { - const char* value = this->GetDefinition(name); + cmProp value = this->GetDefinition(name); if (!value) { return false; } - if (!*value) { + if (value->empty()) { return false; } - if (cmIsNOTFOUND(value)) { + if (cmIsNOTFOUND(*value)) { return false; } @@ -2579,31 +2625,29 @@ bool cmMakefile::IsSet(const std::string& name) const bool cmMakefile::PlatformIs32Bit() const { - if (const char* plat_abi = - this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { - if (strcmp(plat_abi, "ELF X32") == 0) { + if (cmProp plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { + if (*plat_abi == "ELF X32") { return false; } } - if (const char* sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { - return atoi(sizeof_dptr) == 4; + if (cmProp sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { + return atoi(sizeof_dptr->c_str()) == 4; } return false; } bool cmMakefile::PlatformIs64Bit() const { - if (const char* sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { - return atoi(sizeof_dptr) == 8; + if (cmProp sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { + return atoi(sizeof_dptr->c_str()) == 8; } return false; } bool cmMakefile::PlatformIsx32() const { - if (const char* plat_abi = - this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { - if (strcmp(plat_abi, "ELF X32") == 0) { + if (cmProp plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { + if (*plat_abi == "ELF X32") { return true; } } @@ -2652,7 +2696,7 @@ const char* cmMakefile::GetSONameFlag(const std::string& language) const name += language; } name += "_FLAG"; - return GetDefinition(name); + return cmToCStr(GetDefinition(name)); } bool cmMakefile::CanIWriteThisFile(std::string const& fileName) const @@ -2675,7 +2719,7 @@ const std::string& cmMakefile::GetRequiredDefinition( const std::string& name) const { static std::string const empty; - const std::string* def = GetDef(name); + const std::string* def = GetDefinition(name); if (!def) { cmSystemTools::Error("Error required internal CMake variable not " "set, cmake may not be built correctly.\n" @@ -2703,7 +2747,7 @@ bool cmMakefile::IsDefinitionSet(const std::string& name) const return def != nullptr; } -const std::string* cmMakefile::GetDef(const std::string& name) const +cmProp cmMakefile::GetDefinition(const std::string& name) const { cmProp def = this->StateSnapshot.GetDefinition(name); if (!def) { @@ -2716,7 +2760,7 @@ const std::string* cmMakefile::GetDef(const std::string& name) const vv->VariableAccessed(name, def ? cmVariableWatch::VARIABLE_READ_ACCESS : cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS, - (def ? def->c_str() : nullptr), this); + cmToCStr(def), this); if (watch_function_executed) { // A callback was executed and may have caused re-allocation of the @@ -2732,19 +2776,10 @@ const std::string* cmMakefile::GetDef(const std::string& name) const return def; } -const char* cmMakefile::GetDefinition(const std::string& name) const -{ - const std::string* def = GetDef(name); - if (!def) { - return nullptr; - } - return def->c_str(); -} - const std::string& cmMakefile::GetSafeDefinition(const std::string& name) const { static std::string const empty; - const std::string* def = GetDef(name); + const std::string* def = GetDefinition(name); if (!def) { return empty; } @@ -2755,7 +2790,7 @@ bool cmMakefile::GetDefExpandList(const std::string& name, std::vector<std::string>& out, bool emptyArgs) const { - cmProp def = this->GetDef(name); + cmProp def = this->GetDefinition(name); if (!def) { return false; } @@ -2908,12 +2943,12 @@ MessageType cmMakefile::ExpandVariablesInStringOld( // Lookup the definition of VAR. std::string var(first + 1, last - first - 2); - if (const char* val = this->GetDefinition(var)) { + if (cmProp val = this->GetDefinition(var)) { // Store the value in the output escaping as requested. if (escapeQuotes) { - source.append(cmEscapeQuotes(val)); + source.append(cmEscapeQuotes(*val)); } else { - source.append(val); + source.append(*val); } } @@ -2939,7 +2974,7 @@ MessageType cmMakefile::ExpandVariablesInStringOld( parser.SetNoEscapeMode(noEscapes); parser.SetReplaceAtSyntax(replaceAt); parser.SetRemoveEmpty(removeEmpty); - int res = parser.ParseString(source.c_str(), 0); + int res = parser.ParseString(source, 0); const char* emsg = parser.GetError(); MessageType mtype = MessageType::LOG; if (res && !emsg[0]) { @@ -3020,6 +3055,68 @@ void cmMakefile::SetRecursionDepth(int recursionDepth) this->RecursionDepth = recursionDepth; } +std::string cmMakefile::NewDeferId() +{ + return this->GetGlobalGenerator()->NewDeferId(); +} + +bool cmMakefile::DeferCall(std::string id, std::string file, + cmListFileFunction lff) +{ + if (!this->Defer) { + return false; + } + this->Defer->Commands.emplace_back( + DeferCommand{ std::move(id), std::move(file), std::move(lff) }); + return true; +} + +bool cmMakefile::DeferCancelCall(std::string const& id) +{ + if (!this->Defer) { + return false; + } + for (DeferCommand& dc : this->Defer->Commands) { + if (dc.Id == id) { + dc.Id.clear(); + } + } + return true; +} + +cm::optional<std::string> cmMakefile::DeferGetCallIds() const +{ + cm::optional<std::string> ids; + if (this->Defer) { + ids = cmJoin( + cmMakeRange(this->Defer->Commands) + .filter([](DeferCommand const& dc) -> bool { return !dc.Id.empty(); }) + .transform( + [](DeferCommand const& dc) -> std::string const& { return dc.Id; }), + ";"); + } + return ids; +} + +cm::optional<std::string> cmMakefile::DeferGetCall(std::string const& id) const +{ + cm::optional<std::string> call; + if (this->Defer) { + std::string tmp; + for (DeferCommand const& dc : this->Defer->Commands) { + if (dc.Id == id) { + tmp = dc.Command.OriginalName(); + for (cmListFileArgument const& arg : dc.Command.Arguments()) { + tmp = cmStrCat(tmp, ';', arg.Value); + } + break; + } + } + call = std::move(tmp); + } + return call; +} + MessageType cmMakefile::ExpandVariablesInStringNew( std::string& errorstr, std::string& source, bool escapeQuotes, bool noEscapes, bool atOnly, const char* filename, long line, @@ -3057,9 +3154,14 @@ MessageType cmMakefile::ExpandVariablesInStringNew( switch (var.domain) { case NORMAL: if (filename && lookup == lineVar) { - varresult = std::to_string(line); + cmListFileContext const& top = this->Backtrace.Top(); + if (top.DeferId) { + varresult = cmStrCat("DEFERRED:"_s, *top.DeferId); + } else { + varresult = std::to_string(line); + } } else { - value = this->GetDef(lookup); + value = this->GetDefinition(lookup); } break; case ENVIRONMENT: @@ -3191,7 +3293,7 @@ MessageType cmMakefile::ExpandVariablesInStringNew( if (filename && variable == lineVar) { varresult = std::to_string(line); } else { - const std::string* def = this->GetDef(variable); + const std::string* def = this->GetDefinition(variable); if (def) { varresult = *def; } else if (!this->SuppressSideEffects) { @@ -3287,25 +3389,27 @@ void cmMakefile::RemoveVariablesInString(std::string& source, } } -std::string cmMakefile::GetConfigurations(std::vector<std::string>& configs, - bool singleConfig) const +std::string cmMakefile::GetDefaultConfiguration() const { if (this->GetGlobalGenerator()->IsMultiConfig()) { - this->GetDefExpandList("CMAKE_CONFIGURATION_TYPES", configs); - return ""; + return std::string{}; } - const std::string& buildType = this->GetSafeDefinition("CMAKE_BUILD_TYPE"); - if (singleConfig && !buildType.empty()) { - configs.push_back(buildType); - } - return buildType; + return this->GetSafeDefinition("CMAKE_BUILD_TYPE"); } -std::vector<std::string> cmMakefile::GetGeneratorConfigs() const +std::vector<std::string> cmMakefile::GetGeneratorConfigs( + GeneratorConfigQuery mode) const { std::vector<std::string> configs; - GetConfigurations(configs); - if (configs.empty()) { + if (this->GetGlobalGenerator()->IsMultiConfig()) { + this->GetDefExpandList("CMAKE_CONFIGURATION_TYPES", configs); + } else if (mode != cmMakefile::OnlyMultiConfig) { + const std::string& buildType = this->GetSafeDefinition("CMAKE_BUILD_TYPE"); + if (!buildType.empty()) { + configs.emplace_back(buildType); + } + } + if (mode == cmMakefile::IncludeEmptyConfig && configs.empty()) { configs.emplace_back(); } return configs; @@ -3385,20 +3489,10 @@ bool cmMakefile::IsLoopBlock() const return !this->LoopBlockCounter.empty() && this->LoopBlockCounter.top() > 0; } -std::string cmMakefile::GetExecutionFilePath() const -{ - assert(this->StateSnapshot.IsValid()); - return this->StateSnapshot.GetExecutionListFile(); -} - bool cmMakefile::ExpandArguments(std::vector<cmListFileArgument> const& inArgs, - std::vector<std::string>& outArgs, - const char* filename) const + std::vector<std::string>& outArgs) const { - std::string efp = this->GetExecutionFilePath(); - if (!filename) { - filename = efp.c_str(); - } + std::string const& filename = this->GetBacktrace().Top().FilePath; std::string value; outArgs.reserve(inArgs.size()); for (cmListFileArgument const& i : inArgs) { @@ -3409,8 +3503,8 @@ bool cmMakefile::ExpandArguments(std::vector<cmListFileArgument> const& inArgs, } // Expand the variables in the argument. value = i.Value; - this->ExpandVariablesInString(value, false, false, false, filename, i.Line, - false, false); + this->ExpandVariablesInString(value, false, false, false, filename.c_str(), + i.Line, false, false); // If the argument is quoted, it should be one argument. // Otherwise, it may be a list of arguments. @@ -3425,12 +3519,9 @@ bool cmMakefile::ExpandArguments(std::vector<cmListFileArgument> const& inArgs, bool cmMakefile::ExpandArguments( std::vector<cmListFileArgument> const& inArgs, - std::vector<cmExpandedCommandArgument>& outArgs, const char* filename) const + std::vector<cmExpandedCommandArgument>& outArgs) const { - std::string efp = this->GetExecutionFilePath(); - if (!filename) { - filename = efp.c_str(); - } + std::string const& filename = this->GetBacktrace().Top().FilePath; std::string value; outArgs.reserve(inArgs.size()); for (cmListFileArgument const& i : inArgs) { @@ -3441,8 +3532,8 @@ bool cmMakefile::ExpandArguments( } // Expand the variables in the argument. value = i.Value; - this->ExpandVariablesInString(value, false, false, false, filename, i.Line, - false, false); + this->ExpandVariablesInString(value, false, false, false, filename.c_str(), + i.Line, false, false); // If the argument is quoted, it should be one argument. // Otherwise, it may be a list of arguments. @@ -3462,7 +3553,7 @@ void cmMakefile::AddFunctionBlocker(std::unique_ptr<cmFunctionBlocker> fb) { if (!this->ExecutionStatusStack.empty()) { // Record the context in which the blocker is created. - fb->SetStartingContext(this->GetExecutionContext()); + fb->SetStartingContext(this->Backtrace.Top()); } this->FunctionBlockers.push(std::move(fb)); @@ -3631,6 +3722,12 @@ void cmMakefile::AddTargetObject(std::string const& tgtName, void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, bool optional) { + if (this->DeferRunning) { + this->IssueMessage( + MessageType::FATAL_ERROR, + "Languages may not be enabled during deferred execution."); + return; + } if (const char* def = this->GetGlobalGenerator()->GetCMakeCFGIntDir()) { this->AddDefinition("CMAKE_CFG_INTDIR", def); } @@ -3706,26 +3803,25 @@ int cmMakefile::TryCompile(const std::string& srcdir, cm.SetGeneratorToolset(this->GetSafeDefinition("CMAKE_GENERATOR_TOOLSET")); cm.LoadCache(); if (!cm.GetGlobalGenerator()->IsMultiConfig()) { - if (const char* config = + if (cmProp config = this->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION")) { // Tell the single-configuration generator which one to use. // Add this before the user-provided CMake arguments in case // one of the arguments is -DCMAKE_BUILD_TYPE=... - cm.AddCacheEntry("CMAKE_BUILD_TYPE", config, "Build configuration", - cmStateEnums::STRING); + cm.AddCacheEntry("CMAKE_BUILD_TYPE", config->c_str(), + "Build configuration", cmStateEnums::STRING); } } - const char* recursionDepth = - this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH"); + cmProp recursionDepth = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH"); if (recursionDepth) { - cm.AddCacheEntry("CMAKE_MAXIMUM_RECURSION_DEPTH", recursionDepth, + cm.AddCacheEntry("CMAKE_MAXIMUM_RECURSION_DEPTH", recursionDepth->c_str(), "Maximum recursion depth", cmStateEnums::STRING); } // if cmake args were provided then pass them in if (cmakeArgs) { // FIXME: Workaround to ignore unused CLI variables in try-compile. // - // Ideally we should use SetArgs to honor options like --warn-unused-vars. + // Ideally we should use SetArgs for options like --no-warn-unused-cli. // However, there is a subtle problem when certain arguments are passed to // a macro wrapping around try_compile or try_run that does not escape // semicolons in its parameters but just passes ${ARGV} or ${ARGN}. In @@ -3744,7 +3840,7 @@ int cmMakefile::TryCompile(const std::string& srcdir, // the value VAR=a is sufficient for the try_compile or try_run to get the // correct result. Calling SetArgs here would break such projects that // previously built. Instead we work around the issue by never reporting - // unused arguments and ignoring options such as --warn-unused-vars. + // unused arguments and ignoring options such as --no-warn-unused-cli. cm.SetWarnUnusedCli(false); // cm.SetArgs(*cmakeArgs, true); @@ -3851,9 +3947,9 @@ std::string cmMakefile::GetModulesFile(const std::string& filename, std::string moduleInCMakeModulePath; // Always search in CMAKE_MODULE_PATH: - const char* cmakeModulePath = this->GetDefinition("CMAKE_MODULE_PATH"); + cmProp cmakeModulePath = this->GetDefinition("CMAKE_MODULE_PATH"); if (cmakeModulePath) { - std::vector<std::string> modulePath = cmExpandedList(cmakeModulePath); + std::vector<std::string> modulePath = cmExpandedList(*cmakeModulePath); // Look through the possible module directories. for (std::string itempl : modulePath) { @@ -3892,14 +3988,14 @@ std::string cmMakefile::GetModulesFile(const std::string& filename, } if (!moduleInCMakeModulePath.empty() && !moduleInCMakeRoot.empty()) { - const char* currentFile = this->GetDefinition("CMAKE_CURRENT_LIST_FILE"); + cmProp currentFile = this->GetDefinition("CMAKE_CURRENT_LIST_FILE"); std::string mods = cmSystemTools::GetCMakeRoot() + "/Modules/"; - if (currentFile && cmSystemTools::IsSubDirectory(currentFile, mods)) { + if (currentFile && cmSystemTools::IsSubDirectory(*currentFile, mods)) { switch (this->GetPolicyStatus(cmPolicies::CMP0017)) { case cmPolicies::WARN: { std::ostringstream e; /* clang-format off */ - e << "File " << currentFile << " includes " + e << "File " << *currentFile << " includes " << moduleInCMakeModulePath << " (found via CMAKE_MODULE_PATH) which shadows " << moduleInCMakeRoot << ". This may cause errors later on .\n" @@ -3949,7 +4045,7 @@ void cmMakefile::ConfigureString(const std::string& input, std::string& output, // Replace #cmakedefine instances. if (this->cmDefineRegex.find(line)) { - const char* def = this->GetDefinition(this->cmDefineRegex.match(2)); + cmProp def = this->GetDefinition(this->cmDefineRegex.match(2)); if (!cmIsOff(def)) { const std::string indentation = this->cmDefineRegex.match(1); cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine", @@ -3962,7 +4058,7 @@ void cmMakefile::ConfigureString(const std::string& input, std::string& output, } } else if (this->cmDefine01Regex.find(line)) { const std::string indentation = this->cmDefine01Regex.match(1); - const char* def = this->GetDefinition(this->cmDefine01Regex.match(2)); + cmProp def = this->GetDefinition(this->cmDefine01Regex.match(2)); cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine01", "#" + indentation + "define"); output += line; @@ -3998,6 +4094,7 @@ void cmMakefile::ConfigureString(const std::string& input, std::string& output, int cmMakefile::ConfigureFile(const std::string& infile, const std::string& outfile, bool copyonly, bool atOnly, bool escapeQuotes, + bool use_source_permissions, cmNewLineStyle newLine) { int res = 1; @@ -4021,7 +4118,13 @@ int cmMakefile::ConfigureFile(const std::string& infile, this->AddCMakeOutputFile(soutfile); mode_t perm = 0; - cmSystemTools::GetPermissions(sinfile, perm); + if (!use_source_permissions) { + perm = perm | mode_owner_read | mode_owner_write | mode_group_read | + mode_world_read; + } else { + cmSystemTools::GetPermissions(sinfile, perm); + } + std::string::size_type pos = soutfile.rfind('/'); if (pos != std::string::npos) { std::string path = soutfile.substr(0, pos); @@ -4030,6 +4133,13 @@ int cmMakefile::ConfigureFile(const std::string& infile, if (copyonly) { if (!cmSystemTools::CopyFileIfDifferent(sinfile, soutfile)) { + this->IssueMessage(MessageType::FATAL_ERROR, + cmSystemTools::GetLastSystemError()); + return 0; + } + if (!cmSystemTools::SetPermissions(soutfile, perm)) { + this->IssueMessage(MessageType::FATAL_ERROR, + cmSystemTools::GetLastSystemError()); return 0; } } else { @@ -4080,9 +4190,15 @@ int cmMakefile::ConfigureFile(const std::string& infile, fin.close(); fout.close(); if (!cmSystemTools::CopyFileIfDifferent(tempOutputFile, soutfile)) { + this->IssueMessage(MessageType::FATAL_ERROR, + cmSystemTools::GetLastSystemError()); res = 0; } else { - cmSystemTools::SetPermissions(soutfile, perm); + if (!cmSystemTools::SetPermissions(soutfile, perm)) { + this->IssueMessage(MessageType::FATAL_ERROR, + cmSystemTools::GetLastSystemError()); + res = 0; + } } cmSystemTools::RemoveFile(tempOutputFile); } @@ -4127,8 +4243,7 @@ cmProp cmMakefile::GetProperty(const std::string& prop, bool chain) const bool cmMakefile::GetPropertyAsBool(const std::string& prop) const { - cmProp p = this->GetProperty(prop); - return p && cmIsOn(*p); + return cmIsOn(this->GetProperty(prop)); } std::vector<std::string> cmMakefile::GetPropertyKeys() const @@ -4240,8 +4355,6 @@ void cmMakefile::PopScope() this->PopLoopBlockBarrier(); - this->CheckForUnusedVariables(); - this->PopSnapshot(); } @@ -4470,11 +4583,11 @@ static std::string const nMatchesVariable = "CMAKE_MATCH_COUNT"; void cmMakefile::ClearMatches() { - const char* nMatchesStr = this->GetDefinition(nMatchesVariable); + cmProp nMatchesStr = this->GetDefinition(nMatchesVariable); if (!nMatchesStr) { return; } - int nMatches = atoi(nMatchesStr); + int nMatches = atoi(nMatchesStr->c_str()); for (int i = 0; i <= nMatches; i++) { std::string const& var = matchVariables[i]; std::string const& s = this->GetSafeDefinition(var); @@ -4523,7 +4636,7 @@ cmPolicies::PolicyStatus cmMakefile::GetPolicyStatus(cmPolicies::PolicyID id, bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) { // Check for an explicit CMAKE_POLICY_WARNING_CMP<NNNN> setting. - if (const char* val = this->GetDefinition(var)) { + if (cmProp val = this->GetDefinition(var)) { return cmIsOn(val); } // Enable optional policy warnings with --debug-output, --trace, @@ -4557,7 +4670,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::CMP0071 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0072 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. @@ -4599,7 +4712,7 @@ void cmMakefile::PopSnapshot(bool reportError) // cmStateSnapshot manages nested policy scopes within it. // Since the scope corresponding to the snapshot is closing, // reject any still-open nested policy scopes with an error. - while (!this->StateSnapshot.CanPopPolicyScope()) { + while (this->StateSnapshot.CanPopPolicyScope()) { if (reportError) { this->IssueMessage(MessageType::FATAL_ERROR, "cmake_policy PUSH without matching POP"); @@ -4615,7 +4728,8 @@ void cmMakefile::PopSnapshot(bool reportError) bool cmMakefile::SetPolicyVersion(std::string const& version_min, std::string const& version_max) { - return cmPolicies::ApplyPolicyVersion(this, version_min, version_max); + return cmPolicies::ApplyPolicyVersion(this, version_min, version_max, + cmPolicies::WarnCompat::On); } bool cmMakefile::HasCMP0054AlreadyBeenReported( @@ -4651,674 +4765,6 @@ bool cmMakefile::IgnoreErrorsCMP0061() const return ignoreErrors; } -#define FEATURE_STRING(F) , #F -static const char* const C_FEATURES[] = { nullptr FOR_EACH_C_FEATURE( - FEATURE_STRING) }; - -static const char* const CXX_FEATURES[] = { nullptr FOR_EACH_CXX_FEATURE( - FEATURE_STRING) }; - -static const char* const CUDA_FEATURES[] = { nullptr FOR_EACH_CUDA_FEATURE( - FEATURE_STRING) }; -#undef FEATURE_STRING - -static const char* const C_STANDARDS[] = { "90", "99", "11" }; -static const char* const CXX_STANDARDS[] = { "98", "11", "14", "17", "20" }; -static const char* const CUDA_STANDARDS[] = { "03", "11", "14", "17", "20" }; - -bool cmMakefile::AddRequiredTargetFeature(cmTarget* target, - const std::string& feature, - std::string* error) const -{ - if (cmGeneratorExpression::Find(feature) != std::string::npos) { - target->AppendProperty("COMPILE_FEATURES", feature); - return true; - } - - std::string lang; - if (!this->CompileFeatureKnown(target, feature, lang, error)) { - return false; - } - - const char* features = this->CompileFeaturesAvailable(lang, error); - if (!features) { - return false; - } - - std::vector<std::string> availableFeatures = cmExpandedList(features); - if (!cm::contains(availableFeatures, feature)) { - std::ostringstream e; - e << "The compiler feature \"" << feature << "\" is not known to " << lang - << " compiler\n\"" - << this->GetDefinition("CMAKE_" + lang + "_COMPILER_ID") - << "\"\nversion " - << this->GetDefinition("CMAKE_" + lang + "_COMPILER_VERSION") << "."; - if (error) { - *error = e.str(); - } else { - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(), - this->Backtrace); - } - return false; - } - - target->AppendProperty("COMPILE_FEATURES", feature); - - if (lang == "C" || lang == "OBJC") { - return this->AddRequiredTargetCFeature(target, feature, lang, error); - } - if (lang == "CUDA") { - return this->AddRequiredTargetCudaFeature(target, feature, lang, error); - } - return this->AddRequiredTargetCxxFeature(target, feature, lang, error); -} - -bool cmMakefile::CompileFeatureKnown(cmTarget const* target, - const std::string& feature, - std::string& lang, - std::string* error) const -{ - assert(cmGeneratorExpression::Find(feature) == std::string::npos); - - bool isCFeature = - std::find_if(cm::cbegin(C_FEATURES) + 1, cm::cend(C_FEATURES), - cmStrCmp(feature)) != cm::cend(C_FEATURES); - if (isCFeature) { - lang = "C"; - return true; - } - bool isCxxFeature = - std::find_if(cm::cbegin(CXX_FEATURES) + 1, cm::cend(CXX_FEATURES), - cmStrCmp(feature)) != cm::cend(CXX_FEATURES); - if (isCxxFeature) { - lang = "CXX"; - return true; - } - bool isCudaFeature = - std::find_if(cm::cbegin(CUDA_FEATURES) + 1, cm::cend(CUDA_FEATURES), - cmStrCmp(feature)) != cm::cend(CUDA_FEATURES); - if (isCudaFeature) { - lang = "CUDA"; - return true; - } - std::ostringstream e; - if (error) { - e << "specified"; - } else { - e << "Specified"; - } - e << " unknown feature \"" << feature - << "\" for " - "target \"" - << target->GetName() << "\"."; - if (error) { - *error = e.str(); - } else { - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(), - this->Backtrace); - } - return false; -} - -const char* cmMakefile::CompileFeaturesAvailable(const std::string& lang, - std::string* error) const -{ - if (!this->GlobalGenerator->GetLanguageEnabled(lang)) { - std::ostringstream e; - if (error) { - e << "cannot"; - } else { - e << "Cannot"; - } - e << " use features from non-enabled language " << lang; - if (error) { - *error = e.str(); - } else { - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(), - this->Backtrace); - } - return nullptr; - } - - const char* featuresKnown = - this->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES"); - - if (!featuresKnown || !*featuresKnown) { - std::ostringstream e; - if (error) { - e << "no"; - } else { - e << "No"; - } - e << " known features for " << lang << " compiler\n\"" - << this->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID") - << "\"\nversion " - << this->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_VERSION") << "."; - if (error) { - *error = e.str(); - } else { - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str(), - this->Backtrace); - } - return nullptr; - } - return featuresKnown; -} - -bool cmMakefile::HaveStandardAvailable(cmTarget const* target, - std::string const& lang, - const std::string& feature) const -{ - if (lang == "C" || lang == "OBJC") { - return this->HaveCStandardAvailable(target, feature, lang); - } - if (lang == "CUDA") { - return this->HaveCudaStandardAvailable(target, feature, lang); - } - return this->HaveCxxStandardAvailable(target, feature, lang); -} - -bool cmMakefile::HaveCStandardAvailable(cmTarget const* target, - const std::string& feature, - std::string const& lang) const -{ - cmProp defaultCStandard = - this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); - if (!defaultCStandard) { - this->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("CMAKE_", lang, - "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support " - "not fully configured for this compiler.")); - // Return true so the caller does not try to lookup the default standard. - return true; - } - if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), - cmStrCmp(*defaultCStandard)) == cm::cend(C_STANDARDS)) { - const std::string e = cmStrCat("The CMAKE_", lang, - "_STANDARD_DEFAULT variable contains an " - "invalid value: \"", - *defaultCStandard, "\"."); - this->IssueMessage(MessageType::INTERNAL_ERROR, e); - return false; - } - - bool needC90 = false; - bool needC99 = false; - bool needC11 = false; - - this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11); - - cmProp existingCStandard = target->GetProperty(cmStrCat(lang, "_STANDARD")); - if (!existingCStandard) { - existingCStandard = defaultCStandard; - } - - if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), - cmStrCmp(*existingCStandard)) == cm::cend(C_STANDARDS)) { - const std::string e = cmStrCat( - "The ", lang, "_STANDARD property on target \"", target->GetName(), - "\" contained an invalid value: \"", *existingCStandard, "\"."); - this->IssueMessage(MessageType::FATAL_ERROR, e); - return false; - } - - const char* const* existingCIt = existingCStandard - ? std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), - cmStrCmp(*existingCStandard)) - : cm::cend(C_STANDARDS); - - if (needC11 && existingCStandard && - existingCIt < std::find_if(cm::cbegin(C_STANDARDS), - cm::cend(C_STANDARDS), cmStrCmp("11"))) { - return false; - } - if (needC99 && existingCStandard && - existingCIt < std::find_if(cm::cbegin(C_STANDARDS), - cm::cend(C_STANDARDS), cmStrCmp("99"))) { - return false; - } - if (needC90 && existingCStandard && - existingCIt < std::find_if(cm::cbegin(C_STANDARDS), - cm::cend(C_STANDARDS), cmStrCmp("90"))) { - return false; - } - return true; -} - -bool cmMakefile::IsLaterStandard(std::string const& lang, - std::string const& lhs, - std::string const& rhs) -{ - if (lang == "C" || lang == "OBJC") { - const char* const* rhsIt = std::find_if( - cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(rhs)); - - return std::find_if(rhsIt, cm::cend(C_STANDARDS), cmStrCmp(lhs)) != - cm::cend(C_STANDARDS); - } - if (lang == "CUDA") { - const char* const* rhsIt = std::find_if( - cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS), cmStrCmp(rhs)); - - return std::find_if(rhsIt, cm::cend(CUDA_STANDARDS), cmStrCmp(lhs)) != - cm::cend(CUDA_STANDARDS); - } - - const char* const* rhsIt = std::find_if( - cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), cmStrCmp(rhs)); - - return std::find_if(rhsIt, cm::cend(CXX_STANDARDS), cmStrCmp(lhs)) != - cm::cend(CXX_STANDARDS); -} - -bool cmMakefile::HaveCxxStandardAvailable(cmTarget const* target, - const std::string& feature, - std::string const& lang) const -{ - cmProp defaultCxxStandard = - this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); - if (!defaultCxxStandard) { - this->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("CMAKE_", lang, - "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support " - "not fully configured for this compiler.")); - // Return true so the caller does not try to lookup the default standard. - return true; - } - if (std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), - cmStrCmp(*defaultCxxStandard)) == cm::cend(CXX_STANDARDS)) { - const std::string e = - cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ", - "invalid value: \"", *defaultCxxStandard, "\"."); - this->IssueMessage(MessageType::INTERNAL_ERROR, e); - return false; - } - - bool needCxx98 = false; - bool needCxx11 = false; - bool needCxx14 = false; - bool needCxx17 = false; - bool needCxx20 = false; - this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14, - needCxx17, needCxx20); - - cmProp existingCxxStandard = - target->GetProperty(cmStrCat(lang, "_STANDARD")); - if (!existingCxxStandard) { - existingCxxStandard = defaultCxxStandard; - } - - const char* const* existingCxxLevel = - std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), - cmStrCmp(*existingCxxStandard)); - if (existingCxxLevel == cm::cend(CXX_STANDARDS)) { - const std::string e = cmStrCat( - "The ", lang, "_STANDARD property on target \"", target->GetName(), - "\" contained an invalid value: \"", *existingCxxStandard, "\"."); - this->IssueMessage(MessageType::FATAL_ERROR, e); - return false; - } - - /* clang-format off */ - const char* const* needCxxLevel = - needCxx20 ? &CXX_STANDARDS[4] - : needCxx17 ? &CXX_STANDARDS[3] - : needCxx14 ? &CXX_STANDARDS[2] - : needCxx11 ? &CXX_STANDARDS[1] - : needCxx98 ? &CXX_STANDARDS[0] - : nullptr; - /* clang-format on */ - - return !needCxxLevel || needCxxLevel <= existingCxxLevel; -} - -void cmMakefile::CheckNeededCxxLanguage(const std::string& feature, - std::string const& lang, - bool& needCxx98, bool& needCxx11, - bool& needCxx14, bool& needCxx17, - bool& needCxx20) const -{ - if (const char* propCxx98 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "98_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCxx98); - needCxx98 = cm::contains(props, feature); - } - if (const char* propCxx11 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCxx11); - needCxx11 = cm::contains(props, feature); - } - if (const char* propCxx14 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCxx14); - needCxx14 = cm::contains(props, feature); - } - if (const char* propCxx17 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCxx17); - needCxx17 = cm::contains(props, feature); - } - if (const char* propCxx20 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCxx20); - needCxx20 = cm::contains(props, feature); - } -} - -bool cmMakefile::AddRequiredTargetCxxFeature(cmTarget* target, - const std::string& feature, - std::string const& lang, - std::string* error) const -{ - bool needCxx98 = false; - bool needCxx11 = false; - bool needCxx14 = false; - bool needCxx17 = false; - bool needCxx20 = false; - - this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14, - needCxx17, needCxx20); - - cmProp existingCxxStandard = - target->GetProperty(cmStrCat(lang, "_STANDARD")); - if (existingCxxStandard == nullptr) { - cmProp defaultCxxStandard = - this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); - if (defaultCxxStandard && !defaultCxxStandard->empty()) { - existingCxxStandard = defaultCxxStandard; - } - } - const char* const* existingCxxLevel = nullptr; - if (existingCxxStandard) { - existingCxxLevel = - std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), - cmStrCmp(*existingCxxStandard)); - if (existingCxxLevel == cm::cend(CXX_STANDARDS)) { - const std::string e = cmStrCat( - "The ", lang, "_STANDARD property on target \"", target->GetName(), - "\" contained an invalid value: \"", *existingCxxStandard, "\"."); - if (error) { - *error = e; - } else { - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, - this->Backtrace); - } - return false; - } - } - - /* clang-format off */ - const char* const* needCxxLevel = - needCxx20 ? &CXX_STANDARDS[4] - : needCxx17 ? &CXX_STANDARDS[3] - : needCxx14 ? &CXX_STANDARDS[2] - : needCxx11 ? &CXX_STANDARDS[1] - : needCxx98 ? &CXX_STANDARDS[0] - : nullptr; - /* clang-format on */ - - if (needCxxLevel) { - // Ensure the C++ language level is high enough to support - // the needed C++ features. - if (!existingCxxLevel || existingCxxLevel < needCxxLevel) { - target->SetProperty(cmStrCat(lang, "_STANDARD"), *needCxxLevel); - } - } - - return true; -} - -bool cmMakefile::HaveCudaStandardAvailable(cmTarget const* target, - const std::string& feature, - std::string const& lang) const -{ - cmProp defaultCudaStandard = - this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); - if (!defaultCudaStandard) { - this->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("CMAKE_", lang, - "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support " - "not fully configured for this compiler.")); - // Return true so the caller does not try to lookup the default standard. - return true; - } - if (std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS), - cmStrCmp(*defaultCudaStandard)) == - cm::cend(CUDA_STANDARDS)) { - const std::string e = - cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ", - "invalid value: \"", *defaultCudaStandard, "\"."); - this->IssueMessage(MessageType::INTERNAL_ERROR, e); - return false; - } - - bool needCuda03 = false; - bool needCuda11 = false; - bool needCuda14 = false; - bool needCuda17 = false; - bool needCuda20 = false; - this->CheckNeededCudaLanguage(feature, lang, needCuda03, needCuda11, - needCuda14, needCuda17, needCuda20); - - cmProp existingCudaStandard = - target->GetProperty(cmStrCat(lang, "_STANDARD")); - if (!existingCudaStandard) { - existingCudaStandard = defaultCudaStandard; - } - - const char* const* existingCudaLevel = - std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS), - cmStrCmp(*existingCudaStandard)); - if (existingCudaLevel == cm::cend(CUDA_STANDARDS)) { - const std::string e = cmStrCat( - "The ", lang, "_STANDARD property on target \"", target->GetName(), - "\" contained an invalid value: \"", *existingCudaStandard, "\"."); - this->IssueMessage(MessageType::FATAL_ERROR, e); - return false; - } - - /* clang-format off */ - const char* const* needCudaLevel = - needCuda20 ? &CUDA_STANDARDS[4] - : needCuda17 ? &CUDA_STANDARDS[3] - : needCuda14 ? &CUDA_STANDARDS[2] - : needCuda11 ? &CUDA_STANDARDS[1] - : needCuda03 ? &CUDA_STANDARDS[0] - : nullptr; - /* clang-format on */ - - return !needCudaLevel || needCudaLevel <= existingCudaLevel; -} - -void cmMakefile::CheckNeededCudaLanguage(const std::string& feature, - std::string const& lang, - bool& needCuda03, bool& needCuda11, - bool& needCuda14, bool& needCuda17, - bool& needCuda20) const -{ - if (const char* propCuda03 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "03_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCuda03); - needCuda03 = cm::contains(props, feature); - } - if (const char* propCuda11 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCuda11); - needCuda11 = cm::contains(props, feature); - } - if (const char* propCuda14 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCuda14); - needCuda14 = cm::contains(props, feature); - } - if (const char* propCuda17 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCuda17); - needCuda17 = cm::contains(props, feature); - } - if (const char* propCuda20 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propCuda20); - needCuda20 = cm::contains(props, feature); - } -} - -bool cmMakefile::AddRequiredTargetCudaFeature(cmTarget* target, - const std::string& feature, - std::string const& lang, - std::string* error) const -{ - bool needCuda03 = false; - bool needCuda11 = false; - bool needCuda14 = false; - bool needCuda17 = false; - bool needCuda20 = false; - - this->CheckNeededCudaLanguage(feature, lang, needCuda03, needCuda11, - needCuda14, needCuda17, needCuda20); - - cmProp existingCudaStandard = - target->GetProperty(cmStrCat(lang, "_STANDARD")); - if (existingCudaStandard == nullptr) { - cmProp defaultCudaStandard = - this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); - if (defaultCudaStandard && !defaultCudaStandard->empty()) { - existingCudaStandard = defaultCudaStandard; - } - } - const char* const* existingCudaLevel = nullptr; - if (existingCudaStandard) { - existingCudaLevel = - std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS), - cmStrCmp(*existingCudaStandard)); - if (existingCudaLevel == cm::cend(CUDA_STANDARDS)) { - const std::string e = cmStrCat( - "The ", lang, "_STANDARD property on target \"", target->GetName(), - "\" contained an invalid value: \"", *existingCudaStandard, "\"."); - if (error) { - *error = e; - } else { - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, - this->Backtrace); - } - return false; - } - } - - /* clang-format off */ - const char* const* needCudaLevel = - needCuda20 ? &CUDA_STANDARDS[4] - : needCuda17 ? &CUDA_STANDARDS[3] - : needCuda14 ? &CUDA_STANDARDS[2] - : needCuda11 ? &CUDA_STANDARDS[1] - : needCuda03 ? &CUDA_STANDARDS[0] - : nullptr; - /* clang-format on */ - - if (needCudaLevel) { - // Ensure the CUDA language level is high enough to support - // the needed CUDA features. - if (!existingCudaLevel || existingCudaLevel < needCudaLevel) { - target->SetProperty("CUDA_STANDARD", *needCudaLevel); - } - } - - return true; -} - -void cmMakefile::CheckNeededCLanguage(const std::string& feature, - std::string const& lang, bool& needC90, - bool& needC99, bool& needC11) const -{ - if (const char* propC90 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "90_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propC90); - needC90 = cm::contains(props, feature); - } - if (const char* propC99 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "99_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propC99); - needC99 = cm::contains(props, feature); - } - if (const char* propC11 = - this->GetDefinition(cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) { - std::vector<std::string> props = cmExpandedList(propC11); - needC11 = cm::contains(props, feature); - } -} - -bool cmMakefile::AddRequiredTargetCFeature(cmTarget* target, - const std::string& feature, - std::string const& lang, - std::string* error) const -{ - bool needC90 = false; - bool needC99 = false; - bool needC11 = false; - - this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11); - - cmProp existingCStandard = target->GetProperty(cmStrCat(lang, "_STANDARD")); - if (existingCStandard == nullptr) { - cmProp defaultCStandard = - this->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT")); - if (defaultCStandard && !defaultCStandard->empty()) { - existingCStandard = defaultCStandard; - } - } - if (existingCStandard) { - if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), - cmStrCmp(*existingCStandard)) == cm::cend(C_STANDARDS)) { - const std::string e = cmStrCat( - "The ", lang, "_STANDARD property on target \"", target->GetName(), - "\" contained an invalid value: \"", *existingCStandard, "\"."); - if (error) { - *error = e; - } else { - this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, - this->Backtrace); - } - return false; - } - } - const char* const* existingCIt = existingCStandard - ? std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), - cmStrCmp(*existingCStandard)) - : cm::cend(C_STANDARDS); - - bool setC90 = needC90 && !existingCStandard; - bool setC99 = needC99 && !existingCStandard; - bool setC11 = needC11 && !existingCStandard; - - if (needC11 && existingCStandard && - existingCIt < std::find_if(cm::cbegin(C_STANDARDS), - cm::cend(C_STANDARDS), cmStrCmp("11"))) { - setC11 = true; - } else if (needC99 && existingCStandard && - existingCIt < std::find_if(cm::cbegin(C_STANDARDS), - cm::cend(C_STANDARDS), - cmStrCmp("99"))) { - setC99 = true; - } else if (needC90 && existingCStandard && - existingCIt < std::find_if(cm::cbegin(C_STANDARDS), - cm::cend(C_STANDARDS), - cmStrCmp("90"))) { - setC90 = true; - } - - if (setC11) { - target->SetProperty(cmStrCat(lang, "_STANDARD"), "11"); - } else if (setC99) { - target->SetProperty(cmStrCat(lang, "_STANDARD"), "99"); - } else if (setC90) { - target->SetProperty(cmStrCat(lang, "_STANDARD"), "90"); - } - return true; -} - cmMakefile::FunctionPushPop::FunctionPushPop(cmMakefile* mf, const std::string& fileName, cmPolicies::PolicyMap const& pm) diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 45d7109e10..c7940fb405 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMakefile_h -#define cmMakefile_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -16,6 +15,7 @@ #include <unordered_map> #include <vector> +#include <cm/optional> #include <cm/string_view> #include "cmsys/RegularExpression.hxx" @@ -342,12 +342,19 @@ public: */ void SetProjectName(std::string const& name); - /** Get the configurations to be generated. */ - std::string GetConfigurations(std::vector<std::string>& configs, - bool single = true) const; + /* Get the default configuration */ + std::string GetDefaultConfiguration() const; + + enum GeneratorConfigQuery + { + IncludeEmptyConfig, // Include "" aka noconfig + ExcludeEmptyConfig, // Exclude "" aka noconfig + OnlyMultiConfig, + }; /** Get the configurations for dependency checking. */ - std::vector<std::string> GetGeneratorConfigs() const; + std::vector<std::string> GetGeneratorConfigs( + GeneratorConfigQuery mode) const; /** * Set the name of the library. @@ -508,8 +515,7 @@ public: * If the variable is not found in this makefile instance, the * cache is then queried. */ - const char* GetDefinition(const std::string&) const; - const std::string* GetDef(const std::string&) const; + cmProp GetDefinition(const std::string&) const; const std::string& GetSafeDefinition(const std::string&) const; const std::string& GetRequiredDefinition(const std::string& name) const; bool IsDefinitionSet(const std::string&) const; @@ -635,8 +641,6 @@ public: * Get the current context backtrace. */ cmListFileBacktrace GetBacktrace() const; - cmListFileBacktrace GetBacktrace(cmCommandContext const& lfc) const; - cmListFileContext GetExecutionContext() const; /** * Get the vector of files created by this makefile @@ -686,12 +690,14 @@ public: */ int ConfigureFile(const std::string& infile, const std::string& outfile, bool copyonly, bool atOnly, bool escapeQuotes, + bool use_source_permissions, cmNewLineStyle = cmNewLineStyle()); /** * Print a command's invocation */ - void PrintCommandTrace(const cmListFileFunction& lff) const; + void PrintCommandTrace(cmListFileFunction const& lff, + cm::optional<std::string> const& deferId = {}) const; /** * Set a callback that is invoked whenever ExecuteCommand is called. @@ -702,8 +708,8 @@ public: * Execute a single CMake command. Returns true if the command * succeeded or false if it failed. */ - bool ExecuteCommand(const cmListFileFunction& lff, - cmExecutionStatus& status); + bool ExecuteCommand(const cmListFileFunction& lff, cmExecutionStatus& status, + cm::optional<std::string> deferId = {}); //! Enable support for named language, if nil then all languages are /// enabled. @@ -728,12 +734,9 @@ public: * variable replacement and list expansion. */ bool ExpandArguments(std::vector<cmListFileArgument> const& inArgs, - std::vector<std::string>& outArgs, - const char* filename = nullptr) const; - + std::vector<std::string>& outArgs) const; bool ExpandArguments(std::vector<cmListFileArgument> const& inArgs, - std::vector<cmExpandedCommandArgument>& outArgs, - const char* filename = nullptr) const; + std::vector<cmExpandedCommandArgument>& outArgs) const; /** * Get the instance @@ -925,21 +928,6 @@ public: bool PolicyOptionalWarningEnabled(std::string const& var); - bool AddRequiredTargetFeature(cmTarget* target, const std::string& feature, - std::string* error = nullptr) const; - - bool CompileFeatureKnown(cmTarget const* target, const std::string& feature, - std::string& lang, std::string* error) const; - - const char* CompileFeaturesAvailable(const std::string& lang, - std::string* error) const; - - bool HaveStandardAvailable(cmTarget const* target, std::string const& lang, - const std::string& feature) const; - - bool IsLaterStandard(std::string const& lang, std::string const& lhs, - std::string const& rhs); - void PushLoopBlock(); void PopLoopBlock(); bool IsLoopBlock() const; @@ -951,12 +939,10 @@ public: const char* GetDefineFlagsCMP0059() const; - std::string GetExecutionFilePath() const; - void EnforceDirectoryLevelRules() const; void AddEvaluationFile( - const std::string& inputFile, + const std::string& inputFile, const std::string& targetName, std::unique_ptr<cmCompiledGeneratorExpression> outputName, std::unique_ptr<cmCompiledGeneratorExpression> condition, bool inputIsContent); @@ -981,13 +967,16 @@ public: int GetRecursionDepth() const; void SetRecursionDepth(int recursionDepth); + std::string NewDeferId(); + bool DeferCall(std::string id, std::string fileName, cmListFileFunction lff); + bool DeferCancelCall(std::string const& id); + cm::optional<std::string> DeferGetCallIds() const; + cm::optional<std::string> DeferGetCall(std::string const& id) const; + protected: // add link libraries and directories to the target void AddGlobalLinkInformation(cmTarget& target); - // Check for a an unused variable - void LogUnused(const char* reason, const std::string& name) const; - mutable std::set<cmListFileContext> CMP0054ReportedIds; // libraries, classes, and executables @@ -1045,10 +1034,25 @@ private: cmListFileBacktrace Backtrace; int RecursionDepth; + struct DeferCommand + { + // Id is empty for an already-executed or cancelled operation. + std::string Id; + std::string FilePath; + cmListFileFunction Command; + }; + struct DeferCommands + { + std::vector<DeferCommand> Commands; + }; + std::unique_ptr<DeferCommands> Defer; + bool DeferRunning = false; + void DoGenerate(cmLocalGenerator& lg); - void ReadListFile(cmListFile const& listFile, - const std::string& filenametoread); + void RunListFile(cmListFile const& listFile, + const std::string& filenametoread, + DeferCommands* defer = nullptr); bool ParseDefineFlag(std::string const& definition, bool remove); @@ -1099,6 +1103,12 @@ private: class ListFileScope; friend class ListFileScope; + class DeferScope; + friend class DeferScope; + + class DeferCallScope; + friend class DeferCallScope; + class BuildsystemFileScope; friend class BuildsystemFileScope; @@ -1162,49 +1172,9 @@ private: */ bool MightHaveCustomCommand(const std::string& name) const; - bool AddRequiredTargetCFeature(cmTarget* target, const std::string& feature, - std::string const& lang, - std::string* error = nullptr) const; - bool AddRequiredTargetCxxFeature(cmTarget* target, - const std::string& feature, - std::string const& lang, - std::string* error = nullptr) const; - bool AddRequiredTargetCudaFeature(cmTarget* target, - const std::string& feature, - std::string const& lang, - std::string* error = nullptr) const; - - void CheckNeededCLanguage(const std::string& feature, - std::string const& lang, bool& needC90, - bool& needC99, bool& needC11) const; - void CheckNeededCxxLanguage(const std::string& feature, - std::string const& lang, bool& needCxx98, - bool& needCxx11, bool& needCxx14, - bool& needCxx17, bool& needCxx20) const; - void CheckNeededCudaLanguage(const std::string& feature, - std::string const& lang, bool& needCuda03, - bool& needCuda11, bool& needCuda14, - bool& needCuda17, bool& needCuda20) const; - - bool HaveCStandardAvailable(cmTarget const* target, - const std::string& feature, - std::string const& lang) const; - bool HaveCxxStandardAvailable(cmTarget const* target, - const std::string& feature, - std::string const& lang) const; - bool HaveCudaStandardAvailable(cmTarget const* target, - const std::string& feature, - std::string const& lang) const; - - void CheckForUnusedVariables() const; - - // Unused variable flags - bool WarnUnused; bool CheckSystemVars; bool CheckCMP0000; std::set<std::string> WarnedCMP0074; bool IsSourceFileTryCompile; mutable bool SuppressSideEffects; }; - -#endif diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 446f225bda..871878c0b0 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -91,19 +91,12 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule( std::vector<std::string> commands; - // Get the language to use for linking this library. - std::string linkLanguage = "CUDA"; + // Get the name of the device object to generate. std::string const& objExt = this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION"); - - // Build list of dependencies. - std::vector<std::string> depends; - this->AppendLinkDepends(depends, linkLanguage); - - // Get the name of the device object to generate. - std::string const targetOutputReal = + std::string const targetOutput = this->GeneratorTarget->ObjectDirectory + "cmake_device_link" + objExt; - this->DeviceLinkObject = targetOutputReal; + this->DeviceLinkObject = targetOutput; this->NumberOfProgressActions++; if (!this->NoRuleMessages) { @@ -111,7 +104,7 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule( this->MakeEchoProgress(progress); // Add the link message. std::string buildEcho = - cmStrCat("Linking ", linkLanguage, " device code ", + cmStrCat("Linking CUDA device code ", this->LocalGenerator->ConvertToOutputFormat( this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), @@ -121,6 +114,29 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule( commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress); } + if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") { + this->WriteDeviceLinkRule(commands, targetOutput); + } else { + this->WriteNvidiaDeviceExecutableRule(relink, commands, targetOutput); + } + + // Write the main driver rule to build everything in this target. + this->WriteTargetDriverRule(targetOutput, relink); +#else + static_cast<void>(relink); +#endif +} + +void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule( + bool relink, std::vector<std::string>& commands, + const std::string& targetOutput) +{ + const std::string linkLanguage = "CUDA"; + + // Build list of dependencies. + std::vector<std::string> depends; + this->AppendLinkDepends(depends, linkLanguage); + // Build a list of compiler flags and linker flags. std::string langFlags; std::string linkFlags; @@ -136,7 +152,7 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule( // may need to be cleaned. std::vector<std::string> exeCleanFiles; exeCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath( - this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutputReal)); + this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutput)); // Determine whether a link script will be used. bool useLinkScript = this->GlobalGenerator->GetUseLinkScript(); @@ -195,7 +211,7 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule( : cmOutputConverter::SHELL; std::string target = this->LocalGenerator->ConvertToOutputFormat( this->LocalGenerator->MaybeConvertToRelativePath( - this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutputReal), + this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutput), output); std::string targetFullPathCompilePDB = @@ -218,7 +234,7 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule( const char* val = this->LocalGenerator->GetRuleLauncher( this->GeneratorTarget, "RULE_LAUNCH_LINK"); - if (val && *val) { + if (cmNonempty(val)) { launcher = cmStrCat(val, ' '); } @@ -226,7 +242,7 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule( this->LocalGenerator->CreateRulePlaceholderExpander()); // Expand placeholders in the commands. - rulePlaceholderExpander->SetTargetImpLib(targetOutputReal); + rulePlaceholderExpander->SetTargetImpLib(targetOutput); for (std::string& real_link_command : real_link_commands) { real_link_command = cmStrCat(launcher, real_link_command); rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, @@ -255,17 +271,10 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule( // Write the build rule. this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, - targetOutputReal, depends, commands, - false); - - // Write the main driver rule to build everything in this target. - this->WriteTargetDriverRule(targetOutputReal, relink); + targetOutput, depends, commands, false); // Clean all the possible executable names and symlinks. this->CleanFiles.insert(exeCleanFiles.begin(), exeCleanFiles.end()); -#else - static_cast<void>(relink); -#endif } void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) @@ -371,7 +380,8 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) this->LocalGenerator->AddConfigVariableFlags( linkFlags, "CMAKE_EXE_LINKER_FLAGS", this->GetConfigName()); - if (this->GeneratorTarget->GetPropertyAsBool("WIN32_EXECUTABLE")) { + if (this->GeneratorTarget->IsWin32Executable( + this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"))) { this->LocalGenerator->AppendFlags( linkFlags, this->Makefile->GetSafeDefinition("CMAKE_CREATE_WIN32_EXE")); } else { @@ -583,7 +593,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) const char* val = this->LocalGenerator->GetRuleLauncher( this->GeneratorTarget, "RULE_LAUNCH_LINK"); - if (val && *val) { + if (cmNonempty(val)) { launcher = cmStrCat(val, ' '); } diff --git a/Source/cmMakefileExecutableTargetGenerator.h b/Source/cmMakefileExecutableTargetGenerator.h index b9bbe86679..520f57725e 100644 --- a/Source/cmMakefileExecutableTargetGenerator.h +++ b/Source/cmMakefileExecutableTargetGenerator.h @@ -1,11 +1,11 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMakefileExecutableTargetGenerator_h -#define cmMakefileExecutableTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep #include <string> +#include <vector> #include "cmMakefileTargetGenerator.h" @@ -24,9 +24,10 @@ public: protected: virtual void WriteExecutableRule(bool relink); virtual void WriteDeviceExecutableRule(bool relink); + virtual void WriteNvidiaDeviceExecutableRule( + bool relink, std::vector<std::string>& commands, + const std::string& targetOutput); private: std::string DeviceLinkObject; }; - -#endif diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 5809b4a5cc..b32ea6a24b 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -129,8 +129,7 @@ void cmMakefileLibraryTargetGenerator::WriteStaticLibraryRules() const bool requiresDeviceLinking = requireDeviceLinking( *this->GeneratorTarget, *this->LocalGenerator, this->GetConfigName()); if (requiresDeviceLinking) { - std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY"; - this->WriteDeviceLibraryRules(linkRuleVar, false); + this->WriteDeviceLibraryRules("CMAKE_CUDA_DEVICE_LINK_LIBRARY", false); } std::string linkLanguage = @@ -156,8 +155,7 @@ void cmMakefileLibraryTargetGenerator::WriteSharedLibraryRules(bool relink) const bool requiresDeviceLinking = requireDeviceLinking( *this->GeneratorTarget, *this->LocalGenerator, this->GetConfigName()); if (requiresDeviceLinking) { - std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY"; - this->WriteDeviceLibraryRules(linkRuleVar, relink); + this->WriteDeviceLibraryRules("CMAKE_CUDA_DEVICE_LINK_LIBRARY", relink); } } @@ -191,8 +189,7 @@ void cmMakefileLibraryTargetGenerator::WriteModuleLibraryRules(bool relink) const bool requiresDeviceLinking = requireDeviceLinking( *this->GeneratorTarget, *this->LocalGenerator, this->GetConfigName()); if (requiresDeviceLinking) { - std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY"; - this->WriteDeviceLibraryRules(linkRuleVar, relink); + this->WriteDeviceLibraryRules("CMAKE_CUDA_DEVICE_LINK_LIBRARY", relink); } } @@ -239,29 +236,13 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules( // TODO: Merge the methods that call this method to avoid // code duplication. std::vector<std::string> commands; - - // Get the language to use for linking this library. - std::string linkLanguage = "CUDA"; std::string const objExt = this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION"); - // Build list of dependencies. - std::vector<std::string> depends; - this->AppendLinkDepends(depends, linkLanguage); - - // Add language-specific flags. - std::string langFlags; - this->LocalGenerator->AddLanguageFlagsForLinking( - langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName()); - - // Create set of linking flags. - std::string linkFlags; - this->GetDeviceLinkFlags(linkFlags, linkLanguage); - // Get the name of the device object to generate. - std::string const targetOutputReal = + std::string const targetOutput = this->GeneratorTarget->ObjectDirectory + "cmake_device_link" + objExt; - this->DeviceLinkObject = targetOutputReal; + this->DeviceLinkObject = targetOutput; this->NumberOfProgressActions++; if (!this->NoRuleMessages) { @@ -269,7 +250,7 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules( this->MakeEchoProgress(progress); // Add the link message. std::string buildEcho = - cmStrCat("Linking ", linkLanguage, " device code ", + cmStrCat("Linking CUDA device code ", this->LocalGenerator->ConvertToOutputFormat( this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), @@ -278,10 +259,41 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules( this->LocalGenerator->AppendEcho( commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress); } + + if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") { + this->WriteDeviceLinkRule(commands, targetOutput); + } else { + this->WriteNvidiaDeviceLibraryRules(linkRuleVar, relink, commands, + targetOutput); + } + + // Write the main driver rule to build everything in this target. + this->WriteTargetDriverRule(targetOutput, relink); +} + +void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules( + const std::string& linkRuleVar, bool relink, + std::vector<std::string>& commands, const std::string& targetOutput) +{ + std::string linkLanguage = "CUDA"; + + // Build list of dependencies. + std::vector<std::string> depends; + this->AppendLinkDepends(depends, linkLanguage); + + // Add language-specific flags. + std::string langFlags; + this->LocalGenerator->AddLanguageFlagsForLinking( + langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName()); + + // Create set of linking flags. + std::string linkFlags; + this->GetDeviceLinkFlags(linkFlags, linkLanguage); + // Clean files associated with this library. std::set<std::string> libCleanFiles; libCleanFiles.insert(this->LocalGenerator->MaybeConvertToRelativePath( - this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutputReal)); + this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutput)); // Determine whether a link script will be used. bool useLinkScript = this->GlobalGenerator->GetUseLinkScript(); @@ -335,7 +347,7 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules( std::string target = this->LocalGenerator->ConvertToOutputFormat( this->LocalGenerator->MaybeConvertToRelativePath( - this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutputReal), + this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutput), output); std::string targetFullPathCompilePDB = @@ -356,7 +368,7 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules( std::string launcher; const char* val = this->LocalGenerator->GetRuleLauncher( this->GeneratorTarget, "RULE_LAUNCH_LINK"); - if (val && *val) { + if (cmNonempty(val)) { launcher = cmStrCat(val, ' '); } @@ -364,7 +376,7 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules( this->LocalGenerator->CreateRulePlaceholderExpander()); // Construct the main link rule and expand placeholders. - rulePlaceholderExpander->SetTargetImpLib(targetOutputReal); + rulePlaceholderExpander->SetTargetImpLib(targetOutput); std::string linkRule = this->GetLinkRule(linkRuleVar); cmExpandList(linkRule, real_link_commands); @@ -399,14 +411,11 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules( commands1.clear(); // Compute the list of outputs. - std::vector<std::string> outputs(1, targetOutputReal); + std::vector<std::string> outputs(1, targetOutput); // Write the build rule. this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends, commands, false); - - // Write the main driver rule to build everything in this target. - this->WriteTargetDriverRule(targetOutputReal, relink); #else static_cast<void>(linkRuleVar); static_cast<void>(relink); @@ -809,7 +818,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules( std::string launcher; const char* val = this->LocalGenerator->GetRuleLauncher( this->GeneratorTarget, "RULE_LAUNCH_LINK"); - if (val && *val) { + if (cmNonempty(val)) { launcher = cmStrCat(val, ' '); } diff --git a/Source/cmMakefileLibraryTargetGenerator.h b/Source/cmMakefileLibraryTargetGenerator.h index ca22b099d5..cc989e7712 100644 --- a/Source/cmMakefileLibraryTargetGenerator.h +++ b/Source/cmMakefileLibraryTargetGenerator.h @@ -1,11 +1,11 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMakefileLibraryTargetGenerator_h -#define cmMakefileLibraryTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep #include <string> +#include <vector> #include "cmMakefileTargetGenerator.h" @@ -28,6 +28,10 @@ protected: void WriteModuleLibraryRules(bool relink); void WriteDeviceLibraryRules(const std::string& linkRule, bool relink); + void WriteNvidiaDeviceLibraryRules(const std::string& linkRuleVar, + bool relink, + std::vector<std::string>& commands, + const std::string& targetOutput); void WriteLibraryRules(const std::string& linkRule, const std::string& extraFlags, bool relink); // MacOSX Framework support methods @@ -39,5 +43,3 @@ protected: private: std::string DeviceLinkObject; }; - -#endif diff --git a/Source/cmMakefileProfilingData.cxx b/Source/cmMakefileProfilingData.cxx index 29fd440514..86188db4cd 100644 --- a/Source/cmMakefileProfilingData.cxx +++ b/Source/cmMakefileProfilingData.cxx @@ -58,7 +58,7 @@ void cmMakefileProfilingData::StartEntry(const cmListFileFunction& lff, cmsys::SystemInformation info; Json::Value v; v["ph"] = "B"; - v["name"] = lff.Name.Lower; + v["name"] = lff.LowerCaseName(); v["cat"] = "cmake"; v["ts"] = Json::Value::UInt64( std::chrono::duration_cast<std::chrono::microseconds>( @@ -67,9 +67,9 @@ void cmMakefileProfilingData::StartEntry(const cmListFileFunction& lff, v["pid"] = static_cast<int>(info.GetProcessId()); v["tid"] = 0; Json::Value argsValue; - if (!lff.Arguments.empty()) { + if (!lff.Arguments().empty()) { std::string args; - for (const auto& a : lff.Arguments) { + for (auto const& a : lff.Arguments()) { args += (args.empty() ? "" : " ") + a.Value; } argsValue["functionArgs"] = args; diff --git a/Source/cmMakefileProfilingData.h b/Source/cmMakefileProfilingData.h index 1babd97eb7..a86764a707 100644 --- a/Source/cmMakefileProfilingData.h +++ b/Source/cmMakefileProfilingData.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMakefileProfilingData_h -#define cmMakefileProfilingData_h +#pragma once #include <memory> #include <string> @@ -12,7 +11,7 @@ class StreamWriter; } class cmListFileContext; -struct cmListFileFunction; +class cmListFileFunction; class cmMakefileProfilingData { @@ -26,4 +25,3 @@ private: cmsys::ofstream ProfileStream; std::unique_ptr<Json::StreamWriter> JsonWriter; }; -#endif diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 8396fa326c..c6d6c9946f 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -2,10 +2,13 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmMakefileTargetGenerator.h" +#include <algorithm> #include <cassert> #include <cstdio> +#include <iterator> #include <sstream> #include <unordered_map> +#include <unordered_set> #include <utility> #include <cm/memory> @@ -25,11 +28,14 @@ #include "cmMakefileExecutableTargetGenerator.h" #include "cmMakefileLibraryTargetGenerator.h" #include "cmMakefileUtilityTargetGenerator.h" +#include "cmMessageType.h" #include "cmOutputConverter.h" +#include "cmPolicies.h" #include "cmProperty.h" #include "cmRange.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" +#include "cmSourceFileLocationKind.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" @@ -51,6 +57,17 @@ cmMakefileTargetGenerator::cmMakefileTargetGenerator(cmGeneratorTarget* target) if (cmProp ruleStatus = cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) { this->NoRuleMessages = cmIsOff(*ruleStatus); } + switch (this->GeneratorTarget->GetPolicyStatusCMP0113()) { + case cmPolicies::WARN: + case cmPolicies::OLD: + this->CMP0113New = false; + break; + case cmPolicies::NEW: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + this->CMP0113New = true; + break; + } MacOSXContentGenerator = cm::make_unique<MacOSXContentGeneratorType>(this); } @@ -71,6 +88,7 @@ std::unique_ptr<cmMakefileTargetGenerator> cmMakefileTargetGenerator::New( case cmStateEnums::OBJECT_LIBRARY: result = cm::make_unique<cmMakefileLibraryTargetGenerator>(tgt); break; + case cmStateEnums::INTERFACE_LIBRARY: case cmStateEnums::UTILITY: result = cm::make_unique<cmMakefileUtilityTargetGenerator>(tgt); break; @@ -196,18 +214,32 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() } } + std::string currentBinDir = + this->LocalGenerator->GetCurrentBinaryDirectory(); + + // Look for ISPC extra object files generated by this target + auto ispcAdditionalObjs = + this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName()); + for (std::string const& ispcObj : ispcAdditionalObjs) { + this->CleanFiles.insert(this->LocalGenerator->MaybeConvertToRelativePath( + currentBinDir, ispcObj)); + } + // add custom commands to the clean rules? - cmProp clean_no_custom = this->Makefile->GetProperty("CLEAN_NO_CUSTOM"); - bool clean = clean_no_custom ? cmIsOff(*clean_no_custom) : true; + bool clean = cmIsOff(this->Makefile->GetProperty("CLEAN_NO_CUSTOM")); // First generate the object rule files. Save a list of all object // files for this target. std::vector<cmSourceFile const*> customCommands; this->GeneratorTarget->GetCustomCommands(customCommands, this->GetConfigName()); - std::string currentBinDir = - this->LocalGenerator->GetCurrentBinaryDirectory(); for (cmSourceFile const* sf : customCommands) { + if (this->CMP0113New && + !this->LocalGenerator->GetCommandsVisited(this->GeneratorTarget) + .insert(sf) + .second) { + continue; + } cmCustomCommandGenerator ccg(*sf->GetCustomCommand(), this->GetConfigName(), this->LocalGenerator); this->GenerateCustomRuleFile(ccg); @@ -257,17 +289,18 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() this->GeneratorTarget->GetExtraSources(extraSources, this->GetConfigName()); this->OSXBundleGenerator->GenerateMacOSXContentStatements( extraSources, this->MacOSXContentGenerator.get(), this->GetConfigName()); - const char* pchExtension = - this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); + cmProp pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); std::vector<cmSourceFile const*> externalObjects; this->GeneratorTarget->GetExternalObjects(externalObjects, this->GetConfigName()); for (cmSourceFile const* sf : externalObjects) { auto const& objectFileName = sf->GetFullPath(); - if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) { + if (!cmSystemTools::StringEndsWith(objectFileName, + cmToCStr(pchExtension))) { this->ExternalObjects.push_back(objectFileName); } } + std::vector<cmSourceFile const*> objectSources; this->GeneratorTarget->GetObjectSources(objectSources, this->GetConfigName()); @@ -525,6 +558,14 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( } } + if (lang != "ISPC") { + auto const& headers = + this->GeneratorTarget->GetGeneratedISPCHeaders(config); + if (!headers.empty()) { + depends.insert(depends.end(), headers.begin(), headers.end()); + } + } + std::string relativeObj = cmStrCat(this->LocalGenerator->GetHomeRelativeOutputPath(), obj); // Write the build rule. @@ -545,6 +586,28 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( this->AppendFortranPreprocessFlags(flags, source); } + std::string ispcHeaderRelative; + std::string ispcHeaderForShell; + if (lang == "ISPC") { + std::string ispcSource = + cmSystemTools::GetFilenameWithoutLastExtension(objectName); + ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource); + + cmProp ispcSuffixProp = + this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX"); + assert(ispcSuffixProp != nullptr); + + std::string directory = this->GeneratorTarget->GetObjectDirectory(config); + if (cmProp prop = + this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) { + directory = + cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop); + } + ispcHeaderRelative = cmStrCat(directory, '/', ispcSource, *ispcSuffixProp); + ispcHeaderForShell = this->LocalGenerator->ConvertToOutputFormat( + ispcHeaderRelative, cmOutputConverter::SHELL); + } + // Add flags from source file properties. const std::string COMPILE_FLAGS("COMPILE_FLAGS"); if (cmProp cflags = source.GetProperty(COMPILE_FLAGS)) { @@ -710,6 +773,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( cmOutputConverter::SHELL); vars.ObjectFileDir = objectFileDir.c_str(); vars.Flags = flags.c_str(); + vars.ISPCHeader = ispcHeaderForShell.c_str(); std::string definesString = cmStrCat("$(", lang, "_DEFINES)"); @@ -728,7 +792,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( // ability to export compile commands bool lang_has_preprocessor = ((lang == "C") || (lang == "CXX") || (lang == "OBJC") || - (lang == "OBJCXX") || (lang == "Fortran") || (lang == "CUDA")); + (lang == "OBJCXX") || (lang == "Fortran") || (lang == "CUDA") || + lang == "ISPC"); bool const lang_has_assembly = lang_has_preprocessor; bool const lang_can_export_cmds = lang_has_preprocessor; @@ -771,21 +836,25 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( compileCommand.replace(compileCommand.find(langFlags), langFlags.size(), this->GetFlags(lang, this->GetConfigName())); std::string langDefines = std::string("$(") + lang + "_DEFINES)"; - compileCommand.replace(compileCommand.find(langDefines), - langDefines.size(), - this->GetDefines(lang, this->GetConfigName())); + std::string::size_type ldPos = compileCommand.find(langDefines); + if (ldPos != std::string::npos) { + compileCommand.replace(ldPos, langDefines.size(), + this->GetDefines(lang, this->GetConfigName())); + } std::string langIncludes = std::string("$(") + lang + "_INCLUDES)"; - compileCommand.replace(compileCommand.find(langIncludes), - langIncludes.size(), - this->GetIncludes(lang, this->GetConfigName())); + std::string::size_type liPos = compileCommand.find(langIncludes); + if (liPos != std::string::npos) { + compileCommand.replace(liPos, langIncludes.size(), + this->GetIncludes(lang, this->GetConfigName())); + } - const char* eliminate[] = { + cmProp eliminate[] = { this->Makefile->GetDefinition("CMAKE_START_TEMP_FILE"), this->Makefile->GetDefinition("CMAKE_END_TEMP_FILE") }; - for (const char* el : eliminate) { + for (cmProp el : eliminate) { if (el) { - cmSystemTools::ReplaceString(compileCommand, el, ""); + cmSystemTools::ReplaceString(compileCommand, *el, ""); } } @@ -797,10 +866,10 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( std::string compilerLauncher; if (!compileCommands.empty() && (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" || - lang == "OBJC" || lang == "OBJCXX")) { + lang == "ISPC" || lang == "OBJC" || lang == "OBJCXX")) { std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER"; cmProp clauncher = this->GeneratorTarget->GetProperty(clauncher_prop); - if (clauncher && !clauncher->empty()) { + if (cmNonempty(clauncher)) { compilerLauncher = *clauncher; } } @@ -815,8 +884,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( cmProp cpplint = this->GeneratorTarget->GetProperty(cpplint_prop); std::string const cppcheck_prop = lang + "_CPPCHECK"; cmProp cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop); - if ((iwyu && !iwyu->empty()) || (tidy && !tidy->empty()) || - (cpplint && !cpplint->empty()) || (cppcheck && !cppcheck->empty())) { + if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) || + cmNonempty(cppcheck)) { std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_co_compile"; if (!compilerLauncher.empty()) { // In __run_co_compile case the launcher command is supplied @@ -825,30 +894,33 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( run_iwyu += this->LocalGenerator->EscapeForShell(compilerLauncher); compilerLauncher.clear(); } - if (iwyu && !iwyu->empty()) { + if (cmNonempty(iwyu)) { run_iwyu += " --iwyu="; run_iwyu += this->LocalGenerator->EscapeForShell(*iwyu); } - if (tidy && !tidy->empty()) { + if (cmNonempty(tidy)) { run_iwyu += " --tidy="; - const char* driverMode = this->Makefile->GetDefinition( - "CMAKE_" + lang + "_CLANG_TIDY_DRIVER_MODE"); - if (!(driverMode && *driverMode)) { + cmProp p = this->Makefile->GetDefinition("CMAKE_" + lang + + "_CLANG_TIDY_DRIVER_MODE"); + std::string driverMode; + if (cmNonempty(p)) { + driverMode = *p; + } else { driverMode = lang == "C" ? "gcc" : "g++"; } run_iwyu += this->LocalGenerator->EscapeForShell( cmStrCat(*tidy, ";--extra-arg-before=--driver-mode=", driverMode)); } - if (cpplint && !cpplint->empty()) { + if (cmNonempty(cpplint)) { run_iwyu += " --cpplint="; run_iwyu += this->LocalGenerator->EscapeForShell(*cpplint); } - if (cppcheck && !cppcheck->empty()) { + if (cmNonempty(cppcheck)) { run_iwyu += " --cppcheck="; run_iwyu += this->LocalGenerator->EscapeForShell(*cppcheck); } - if ((tidy && !tidy->empty()) || (cpplint && !cpplint->empty()) || - (cppcheck && !cppcheck->empty())) { + if (cmNonempty(tidy) || (cmNonempty(cpplint)) || + (cmNonempty(cppcheck))) { run_iwyu += " --source="; run_iwyu += sourceFile; } @@ -875,7 +947,7 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( { const char* val = this->LocalGenerator->GetRuleLauncher( this->GeneratorTarget, "RULE_LAUNCH_COMPILE"); - if (val && *val) { + if (cmNonempty(val)) { launcher = cmStrCat(val, ' '); } } @@ -903,9 +975,16 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( if (!evaluated_outputs.empty()) { // Register these as extra files to clean. cmExpandList(evaluated_outputs, outputs); - this->CleanFiles.insert(outputs.begin() + 1, outputs.end()); } } + if (!ispcHeaderRelative + .empty()) { // can't move ispcHeader as vars is using it + outputs.emplace_back(ispcHeaderRelative); + } + + if (outputs.size() > 1) { + this->CleanFiles.insert(outputs.begin() + 1, outputs.end()); + } // Write the rule. this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends, @@ -935,10 +1014,10 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( std::string preprocessRuleVar = cmStrCat("CMAKE_", lang, "_CREATE_PREPROCESSED_SOURCE"); - if (const char* preprocessRule = + if (cmProp preprocessRule = this->Makefile->GetDefinition(preprocessRuleVar)) { std::vector<std::string> preprocessCommands = - cmExpandedList(preprocessRule); + cmExpandedList(*preprocessRule); std::string shellObjI = this->LocalGenerator->ConvertToOutputFormat( objI, cmOutputConverter::SHELL); @@ -980,10 +1059,10 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( std::string assemblyRuleVar = cmStrCat("CMAKE_", lang, "_CREATE_ASSEMBLY_SOURCE"); - if (const char* assemblyRule = + if (cmProp assemblyRule = this->Makefile->GetDefinition(assemblyRuleVar)) { std::vector<std::string> assemblyCommands = - cmExpandedList(assemblyRule); + cmExpandedList(*assemblyRule); std::string shellObjS = this->LocalGenerator->ConvertToOutputFormat( objS, cmOutputConverter::SHELL); @@ -1236,16 +1315,7 @@ void cmMakefileTargetGenerator::DriveCustomCommands( std::vector<std::string>& depends) { // Depend on all custom command outputs. - std::vector<cmSourceFile*> sources; - this->GeneratorTarget->GetSourceFiles( - sources, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); - for (cmSourceFile* source : sources) { - if (cmCustomCommand* cc = source->GetCustomCommand()) { - cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), - this->LocalGenerator); - cm::append(depends, ccg.GetOutputs()); - } - } + cm::append(depends, this->CustomCommandOutputs); } void cmMakefileTargetGenerator::WriteObjectDependRules( @@ -1259,6 +1329,130 @@ void cmMakefileTargetGenerator::WriteObjectDependRules( } } +void cmMakefileTargetGenerator::WriteDeviceLinkRule( + std::vector<std::string>& commands, const std::string& output) +{ + std::string architecturesStr = + this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES"); + + if (cmIsOff(architecturesStr)) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "CUDA_SEPARABLE_COMPILATION on Clang " + "requires CUDA_ARCHITECTURES to be set."); + return; + } + + std::vector<std::string> architectures = cmExpandedList(architecturesStr); + + // Ensure there are no duplicates. + const std::vector<std::string> linkDeps = [&]() -> std::vector<std::string> { + std::vector<std::string> deps; + this->AppendTargetDepends(deps, true); + this->GeneratorTarget->GetLinkDepends(deps, this->GetConfigName(), "CUDA"); + std::copy(this->Objects.begin(), this->Objects.end(), + std::back_inserter(deps)); + + std::unordered_set<std::string> depsSet(deps.begin(), deps.end()); + deps.clear(); + std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps)); + return deps; + }(); + + const std::string objectDir = this->GeneratorTarget->ObjectDirectory; + const std::string relObjectDir = + this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetCurrentBinaryDirectory(), objectDir); + + // Construct a list of files associated with this executable that + // may need to be cleaned. + std::vector<std::string> cleanFiles; + cleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetCurrentBinaryDirectory(), output)); + + std::string profiles; + std::vector<std::string> fatbinaryDepends; + std::string registerFile = cmStrCat(objectDir, "cmake_cuda_register.h"); + + // Link device code for each architecture. + for (const std::string& architectureKind : architectures) { + // Clang always generates real code, so strip the specifier. + const std::string architecture = + architectureKind.substr(0, architectureKind.find('-')); + const std::string cubin = + cmStrCat(relObjectDir, "sm_", architecture, ".cubin"); + + profiles += cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin); + fatbinaryDepends.emplace_back(cubin); + + std::string registerFileCmd; + + // The generated register file contains macros that when expanded register + // the device routines. Because the routines are the same for all + // architectures the register file will be the same too. Thus generate it + // only on the first invocation to reduce overhead. + if (fatbinaryDepends.size() == 1) { + std::string registerFileRel = + this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetCurrentBinaryDirectory(), registerFile); + registerFileCmd = + cmStrCat(" --register-link-binaries=", registerFileRel); + cleanFiles.push_back(registerFileRel); + } + + std::string command = cmStrCat( + this->Makefile->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"), + " -arch=sm_", architecture, registerFileCmd, " -o=$@ ", + cmJoin(linkDeps, " ")); + + this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, cubin, + linkDeps, { command }, false); + } + + // Combine all architectures into a single fatbinary. + const std::string fatbinaryCommand = + cmStrCat(this->Makefile->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"), + " -64 -cmdline=--compile-only -compress-all -link " + "--embedded-fatbin=$@", + profiles); + const std::string fatbinaryOutput = + cmStrCat(objectDir, "cmake_cuda_fatbin.h"); + const std::string fatbinaryOutputRel = + this->LocalGenerator->MaybeConvertToRelativePath( + this->LocalGenerator->GetCurrentBinaryDirectory(), fatbinaryOutput); + + this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, + fatbinaryOutputRel, fatbinaryDepends, + { fatbinaryCommand }, false); + + // Compile the stub that registers the kernels and contains the fatbinaries. + cmRulePlaceholderExpander::RuleVariables vars; + vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); + vars.CMTargetType = + cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str(); + + vars.Language = "CUDA"; + vars.Object = output.c_str(); + vars.Fatbinary = fatbinaryOutput.c_str(); + vars.RegisterFile = registerFile.c_str(); + + std::string flags = this->GetFlags("CUDA", this->GetConfigName()); + vars.Flags = flags.c_str(); + + std::string compileCmd = this->GetLinkRule("CMAKE_CUDA_DEVICE_LINK_COMPILE"); + std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( + this->LocalGenerator->CreateRulePlaceholderExpander()); + rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, + compileCmd, vars); + + commands.emplace_back(compileCmd); + this->LocalGenerator->WriteMakeRule( + *this->BuildFileStream, nullptr, output, + { cmStrCat(relObjectDir, "cmake_cuda_fatbin.h") }, commands, false); + + // Clean all the possible executable names and symlinks. + this->CleanFiles.insert(cleanFiles.begin(), cleanFiles.end()); +} + void cmMakefileTargetGenerator::GenerateCustomRuleFile( cmCustomCommandGenerator const& ccg) { @@ -1292,6 +1486,22 @@ void cmMakefileTargetGenerator::GenerateCustomRuleFile( bool symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends, commands); + // Symbolic inputs are not expected to exist, so add dummy rules. + if (this->CMP0113New && !depends.empty()) { + std::vector<std::string> no_depends; + std::vector<std::string> no_commands; + for (std::string const& dep : depends) { + if (cmSourceFile* dsf = + this->Makefile->GetSource(dep, cmSourceFileLocationKind::Known)) { + if (dsf->GetPropertyAsBool("SYMBOLIC")) { + this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, + dep, no_depends, no_commands, + true); + } + } + } + } + // If the rule has changed make sure the output is rebuilt. if (!symbolic) { this->GlobalGenerator->AddRuleHash(ccg.GetOutputs(), content.str()); @@ -1306,6 +1516,8 @@ void cmMakefileTargetGenerator::GenerateCustomRuleFile( this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, idi.first, objFullPath, srcFullPath); } + + this->CustomCommandOutputs.insert(outputs.begin(), outputs.end()); } void cmMakefileTargetGenerator::MakeEchoProgress( @@ -1330,17 +1542,17 @@ void cmMakefileTargetGenerator::WriteObjectsVariable( << this->GeneratorTarget->GetName() << "\n" << variableName << " ="; std::string object; - const char* lineContinue = - this->Makefile->GetDefinition("CMAKE_MAKE_LINE_CONTINUE"); - if (!lineContinue) { + std::string lineContinue; + if (cmProp p = this->Makefile->GetDefinition("CMAKE_MAKE_LINE_CONTINUE")) { + lineContinue = *p; + } else { lineContinue = "\\"; } - const char* pchExtension = - this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); + cmProp pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); for (std::string const& obj : this->Objects) { - if (cmSystemTools::StringEndsWith(obj, pchExtension)) { + if (cmSystemTools::StringEndsWith(obj, cmToCStr(pchExtension))) { continue; } *this->BuildFileStream << " " << lineContinue << "\n"; @@ -1435,14 +1647,13 @@ private: void cmMakefileTargetGenerator::WriteObjectsStrings( std::vector<std::string>& objStrings, std::string::size_type limit) { - const char* pchExtension = - this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); + cmProp pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); cmMakefileTargetGeneratorObjectStrings helper( objStrings, this->LocalGenerator, this->LocalGenerator->GetStateSnapshot().GetDirectory(), limit); for (std::string const& obj : this->Objects) { - if (cmSystemTools::StringEndsWith(obj, pchExtension)) { + if (cmSystemTools::StringEndsWith(obj, cmToCStr(pchExtension))) { continue; } helper.Feed(obj); @@ -1450,6 +1661,11 @@ void cmMakefileTargetGenerator::WriteObjectsStrings( for (std::string const& obj : this->ExternalObjects) { helper.Feed(obj); } + auto ispcAdditionalObjs = + this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName()); + for (std::string const& obj : ispcAdditionalObjs) { + helper.Feed(obj); + } helper.Done(); } @@ -1493,10 +1709,11 @@ void cmMakefileTargetGenerator::WriteTargetDriverRule( } void cmMakefileTargetGenerator::AppendTargetDepends( - std::vector<std::string>& depends) + std::vector<std::string>& depends, bool ignoreType) { // Static libraries never depend on anything for linking. - if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) { + if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY && + !ignoreType) { return; } @@ -1565,8 +1782,8 @@ std::string cmMakefileTargetGenerator::GetLinkRule( cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), "_GNUtoMS_RULE"); - if (const char* rule = this->Makefile->GetDefinition(ruleVar)) { - linkRule += rule; + if (cmProp rule = this->Makefile->GetDefinition(ruleVar)) { + linkRule += *rule; } } return linkRule; @@ -1615,8 +1832,8 @@ bool cmMakefileTargetGenerator::CheckUseResponseFileForObjects( // Check for an explicit setting one way or the other. std::string const responseVar = "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_OBJECTS"; - if (const char* val = this->Makefile->GetDefinition(responseVar)) { - if (*val) { + if (cmProp val = this->Makefile->GetDefinition(responseVar)) { + if (!val->empty()) { return cmIsOn(val); } } @@ -1654,8 +1871,8 @@ bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries( // Check for an explicit setting one way or the other. std::string const responseVar = "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_LIBRARIES"; - if (const char* val = this->Makefile->GetDefinition(responseVar)) { - if (*val) { + if (cmProp val = this->Makefile->GetDefinition(responseVar)) { + if (!val->empty()) { return cmIsOn(val); } } @@ -1716,8 +1933,10 @@ void cmMakefileTargetGenerator::CreateLinkLibs( cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), "_RESPONSE_FILE_LINK_FLAG"); - const char* responseFlag = this->Makefile->GetDefinition(responseFlagVar); - if (!responseFlag) { + std::string responseFlag; + if (cmProp p = this->Makefile->GetDefinition(responseFlagVar)) { + responseFlag = *p; + } else { responseFlag = "@"; } @@ -1754,8 +1973,10 @@ void cmMakefileTargetGenerator::CreateObjectLists( cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), "_RESPONSE_FILE_LINK_FLAG"); - const char* responseFlag = this->Makefile->GetDefinition(responseFlagVar); - if (!responseFlag) { + std::string responseFlag; + if (cmProp p = this->Makefile->GetDefinition(responseFlagVar)) { + responseFlag = *p; + } else { responseFlag = "@"; } @@ -1850,11 +2071,11 @@ void cmMakefileTargetGenerator::GenDefFile( this->LocalGenerator->MaybeConvertToRelativePath( this->LocalGenerator->GetCurrentBinaryDirectory(), objlist_file), cmOutputConverter::SHELL); - const char* nm_executable = this->Makefile->GetDefinition("CMAKE_NM"); - if (nm_executable && *nm_executable) { + cmProp nm_executable = this->Makefile->GetDefinition("CMAKE_NM"); + if (cmNonempty(nm_executable)) { cmd += " --nm="; cmd += this->LocalCommonGenerator->ConvertToOutputFormat( - nm_executable, cmOutputConverter::SHELL); + *nm_executable, cmOutputConverter::SHELL); } real_link_commands.insert(real_link_commands.begin(), cmd); // create a list of obj files for the -E __create_def to read diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index f38f86237d..cb804e06ae 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMakefileTargetGenerator_h -#define cmMakefileTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -105,6 +104,10 @@ protected: void WriteObjectDependRules(cmSourceFile const& source, std::vector<std::string>& depends); + // CUDA device linking. + void WriteDeviceLinkRule(std::vector<std::string>& commands, + const std::string& output); + // write the build rule for a custom command void GenerateCustomRuleFile(cmCustomCommandGenerator const& ccg); @@ -128,7 +131,8 @@ protected: void DriveCustomCommands(std::vector<std::string>& depends); // append intertarget dependencies - void AppendTargetDepends(std::vector<std::string>& depends); + void AppendTargetDepends(std::vector<std::string>& depends, + bool ignoreType = false); // Append object file dependencies. void AppendObjectDepends(std::vector<std::string>& depends); @@ -197,6 +201,8 @@ protected: unsigned long NumberOfProgressActions; bool NoRuleMessages; + bool CMP0113New = false; + // the path to the directory the build file is in std::string TargetBuildDirectory; std::string TargetBuildDirectoryFull; @@ -229,6 +235,9 @@ protected: // Set of extra output files to be driven by the build. std::set<std::string> ExtraFiles; + // Set of custom command output files to be driven by the build. + std::set<std::string> CustomCommandOutputs; + using MultipleOutputPairsType = std::map<std::string, std::string>; MultipleOutputPairsType MultipleOutputPairs; bool WriteMakeRule(std::ostream& os, const char* comment, @@ -245,5 +254,3 @@ protected: std::unique_ptr<cmOSXBundleGenerator> OSXBundleGenerator; std::unique_ptr<MacOSXContentGeneratorType> MacOSXContentGenerator; }; - -#endif diff --git a/Source/cmMakefileUtilityTargetGenerator.h b/Source/cmMakefileUtilityTargetGenerator.h index be243a7f53..d2b4ba5a9e 100644 --- a/Source/cmMakefileUtilityTargetGenerator.h +++ b/Source/cmMakefileUtilityTargetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMakefileUtilityTargetGenerator_h -#define cmMakefileUtilityTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -21,5 +20,3 @@ public: protected: }; - -#endif diff --git a/Source/cmMarkAsAdvancedCommand.h b/Source/cmMarkAsAdvancedCommand.h index de7bf08b43..e420e64fa2 100644 --- a/Source/cmMarkAsAdvancedCommand.h +++ b/Source/cmMarkAsAdvancedCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMarkAsAdvancedCommand_h -#define cmMarkAsAdvancedCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ class cmExecutionStatus; */ bool cmMarkAsAdvancedCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmMathCommand.h b/Source/cmMathCommand.h index ac1957c24e..e6b347b85c 100644 --- a/Source/cmMathCommand.h +++ b/Source/cmMathCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMathCommand_h -#define cmMathCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -13,5 +12,3 @@ class cmExecutionStatus; /// Mathematical expressions: math(EXPR ...) command. bool cmMathCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmMessageCommand.h b/Source/cmMessageCommand.h index 7d544c408d..c37098c75e 100644 --- a/Source/cmMessageCommand.h +++ b/Source/cmMessageCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMessageCommand_h -#define cmMessageCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -16,5 +15,3 @@ class cmExecutionStatus; */ bool cmMessageCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmMessageType.h b/Source/cmMessageType.h index b57b86bee5..44de42932e 100644 --- a/Source/cmMessageType.h +++ b/Source/cmMessageType.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMessageType_h -#define cmMessageType_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ enum class MessageType DEPRECATION_ERROR, DEPRECATION_WARNING }; - -#endif diff --git a/Source/cmMessenger.h b/Source/cmMessenger.h index 8c097827eb..b6f5712cc4 100644 --- a/Source/cmMessenger.h +++ b/Source/cmMessenger.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmMessenger_h -#define cmMessenger_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -57,5 +56,3 @@ private: bool DevWarningsAsErrors = false; bool DeprecatedWarningsAsErrors = false; }; - -#endif diff --git a/Source/cmNewLineStyle.h b/Source/cmNewLineStyle.h index ab9002e4d2..a2b985bde0 100644 --- a/Source/cmNewLineStyle.h +++ b/Source/cmNewLineStyle.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmNewLineStyle_h -#define cmNewLineStyle_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -35,5 +34,3 @@ public: private: Style NewLineStyle = Invalid; }; - -#endif diff --git a/Source/cmNinjaLinkLineComputer.h b/Source/cmNinjaLinkLineComputer.h index b2b2e84e1c..5d22f3e13f 100644 --- a/Source/cmNinjaLinkLineComputer.h +++ b/Source/cmNinjaLinkLineComputer.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmNinjaLinkLineComputer_h -#define cmNinjaLinkLineComputer_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -29,5 +28,3 @@ public: private: cmGlobalNinjaGenerator const* GG; }; - -#endif diff --git a/Source/cmNinjaLinkLineDeviceComputer.h b/Source/cmNinjaLinkLineDeviceComputer.h index 84ced5b352..457f036e46 100644 --- a/Source/cmNinjaLinkLineDeviceComputer.h +++ b/Source/cmNinjaLinkLineDeviceComputer.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmNinjaLinkLineDeviceComputer_h -#define cmNinjaLinkLineDeviceComputer_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -30,5 +29,3 @@ public: private: cmGlobalNinjaGenerator const* GG; }; - -#endif diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index 32ab9f80ec..ccb959b013 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -8,6 +8,7 @@ #include <map> #include <set> #include <sstream> +#include <unordered_set> #include <utility> #include <cm/memory> @@ -25,6 +26,7 @@ #include "cmLocalGenerator.h" #include "cmLocalNinjaGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmNinjaLinkLineDeviceComputer.h" #include "cmNinjaTypes.h" #include "cmOSXBundleGenerator.h" @@ -178,6 +180,33 @@ std::string cmNinjaNormalTargetGenerator::LanguageLinkerDeviceRule( "_", config); } +std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceRule( + const std::string& config) const +{ + return cmStrCat( + this->TargetLinkLanguage(config), "_DEVICE_LINK__", + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), + '_', config); +} + +std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceCompileRule( + const std::string& config) const +{ + return cmStrCat( + this->TargetLinkLanguage(config), "_DEVICE_LINK_COMPILE__", + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), + '_', config); +} + +std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule( + const std::string& config) const +{ + return cmStrCat( + this->TargetLinkLanguage(config), "_FATBINARY__", + cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()), + '_', config); +} + struct cmNinjaRemoveNoOpCommands { bool operator()(std::string const& cmd) @@ -186,7 +215,7 @@ struct cmNinjaRemoveNoOpCommands } }; -void cmNinjaNormalTargetGenerator::WriteDeviceLinkRule( +void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule( bool useResponseFile, const std::string& config) { cmNinjaRule rule(this->LanguageLinkerDeviceRule(config)); @@ -239,7 +268,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRule( std::string launcher; const char* val = this->GetLocalGenerator()->GetRuleLauncher( this->GetGeneratorTarget(), "RULE_LAUNCH_LINK"); - if (val && *val) { + if (cmNonempty(val)) { launcher = cmStrCat(val, ' '); } @@ -272,6 +301,55 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkRule( } } +void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules( + const std::string& config) +{ + const cmMakefile* mf = this->GetMakefile(); + + cmNinjaRule rule(LanguageLinkerCudaDeviceRule(config)); + rule.Command = this->GetLocalGenerator()->BuildCommandLine( + { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"), + " -arch=$ARCH $REGISTER -o=$out $in") }); + rule.Comment = "Rule for CUDA device linking."; + rule.Description = "Linking CUDA $out"; + this->GetGlobalGenerator()->AddRule(rule); + + cmRulePlaceholderExpander::RuleVariables vars; + vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); + vars.CMTargetType = + cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str(); + + vars.Language = "CUDA"; + vars.Object = "$out"; + vars.Fatbinary = "$FATBIN"; + vars.RegisterFile = "$REGISTER"; + + std::string flags = this->GetFlags("CUDA", config); + vars.Flags = flags.c_str(); + + std::string compileCmd = this->GetMakefile()->GetRequiredDefinition( + "CMAKE_CUDA_DEVICE_LINK_COMPILE"); + std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander( + this->GetLocalGenerator()->CreateRulePlaceholderExpander()); + rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), + compileCmd, vars); + + rule.Name = LanguageLinkerCudaDeviceCompileRule(config); + rule.Command = this->GetLocalGenerator()->BuildCommandLine({ compileCmd }); + rule.Comment = "Rule for compiling CUDA device stubs."; + rule.Description = "Compiling CUDA device stub $out"; + this->GetGlobalGenerator()->AddRule(rule); + + rule.Name = LanguageLinkerCudaFatbinaryRule(config); + rule.Command = this->GetLocalGenerator()->BuildCommandLine( + { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"), + " -64 -cmdline=--compile-only -compress-all -link " + "--embedded-fatbin=$out $PROFILES") }); + rule.Comment = "Rule for CUDA fatbinaries."; + rule.Description = "Creating fatbinary $out"; + this->GetGlobalGenerator()->AddRule(rule); +} + void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile, const std::string& config) { @@ -307,10 +385,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile, // build response file name std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; - const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); + cmProp flag = GetMakefile()->GetDefinition(cmakeLinkVar); if (flag) { - responseFlag = flag; + responseFlag = *flag; } else { responseFlag = "@"; } @@ -376,7 +454,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile, std::string launcher; const char* val = this->GetLocalGenerator()->GetRuleLauncher( this->GetGeneratorTarget(), "RULE_LAUNCH_LINK"); - if (val && *val) { + if (cmNonempty(val)) { launcher = cmStrCat(val, ' '); } @@ -478,15 +556,15 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd( // CMAKE_<lang>_CREATE_STATIC_LIBRARY_IPO define instead. std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable( this->TargetLinkLanguage(config), config); - const char* linkCmd = mf->GetDefinition(linkCmdVar); + cmProp linkCmd = mf->GetDefinition(linkCmdVar); if (linkCmd) { - std::string linkCmdStr = linkCmd; + std::string linkCmdStr = *linkCmd; if (this->GetGeneratorTarget()->HasImplibGNUtoMS(config)) { std::string ruleVar = cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(config), "_GNUtoMS_RULE"); - if (const char* rule = this->Makefile->GetDefinition(ruleVar)) { - linkCmdStr += rule; + if (cmProp rule = this->Makefile->GetDefinition(ruleVar)) { + linkCmdStr += *rule; } } cmExpandList(linkCmdStr, linkCmds); @@ -586,7 +664,6 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( // First and very important step is to make sure while inside this // step our link language is set to CUDA - std::string cudaLinkLanguage = "CUDA"; std::string const& objExt = this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION"); @@ -598,6 +675,118 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( std::string targetOutputReal = ConvertToNinjaPath(targetOutputDir + "cmake_device_link" + objExt); + if (firstForConfig) { + globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal); + } + this->DeviceLinkObject = targetOutputReal; + + // Write comments. + cmGlobalNinjaGenerator::WriteDivider(this->GetCommonFileStream()); + this->GetCommonFileStream() + << "# Device Link build statements for " + << cmState::GetTargetTypeName(genTarget->GetType()) << " target " + << this->GetTargetName() << "\n\n"; + + if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") { + std::string architecturesStr = + this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES"); + + if (cmIsOff(architecturesStr)) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "CUDA_SEPARABLE_COMPILATION on Clang " + "requires CUDA_ARCHITECTURES to be set."); + return; + } + + this->WriteDeviceLinkRules(config); + this->WriteDeviceLinkStatements(config, cmExpandedList(architecturesStr), + targetOutputReal); + } else { + this->WriteNvidiaDeviceLinkStatement(config, fileConfig, targetOutputDir, + targetOutputReal); + } +} + +void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatements( + const std::string& config, const std::vector<std::string>& architectures, + const std::string& output) +{ + // Ensure there are no duplicates. + const cmNinjaDeps explicitDeps = [&]() -> std::vector<std::string> { + std::unordered_set<std::string> depsSet; + const cmNinjaDeps linkDeps = + this->ComputeLinkDeps(this->TargetLinkLanguage(config), config, true); + const cmNinjaDeps objects = this->GetObjects(config); + depsSet.insert(linkDeps.begin(), linkDeps.end()); + depsSet.insert(objects.begin(), objects.end()); + + std::vector<std::string> deps; + std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps)); + return deps; + }(); + + const std::string objectDir = + cmStrCat(this->GeneratorTarget->GetSupportDirectory(), + this->GetGlobalGenerator()->ConfigDirectory(config)); + const std::string ninjaOutputDir = this->ConvertToNinjaPath(objectDir); + + cmNinjaBuild fatbinary(LanguageLinkerCudaFatbinaryRule(config)); + + // Link device code for each architecture. + for (const std::string& architectureKind : architectures) { + // Clang always generates real code, so strip the specifier. + const std::string architecture = + architectureKind.substr(0, architectureKind.find('-')); + const std::string cubin = + cmStrCat(ninjaOutputDir, "/sm_", architecture, ".cubin"); + + fatbinary.Variables["PROFILES"] += + cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin); + fatbinary.ExplicitDeps.emplace_back(cubin); + + cmNinjaBuild dlink(LanguageLinkerCudaDeviceRule(config)); + dlink.ExplicitDeps = explicitDeps; + dlink.Outputs = { cubin }; + dlink.Variables["ARCH"] = cmStrCat("sm_", architecture); + + // The generated register file contains macros that when expanded register + // the device routines. Because the routines are the same for all + // architectures the register file will be the same too. Thus generate it + // only on the first invocation to reduce overhead. + if (fatbinary.ExplicitDeps.size() == 1) { + dlink.Variables["REGISTER"] = cmStrCat( + "--register-link-binaries=", ninjaOutputDir, "/cmake_cuda_register.h"); + } + + this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), dlink); + } + + // Combine all architectures into a single fatbinary. + fatbinary.Outputs = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") }; + this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), + fatbinary); + + // Compile the stub that registers the kernels and contains the fatbinaries. + cmNinjaBuild dcompile(LanguageLinkerCudaDeviceCompileRule(config)); + dcompile.Outputs = { output }; + dcompile.ExplicitDeps = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") }; + dcompile.Variables["FATBIN"] = + this->GetLocalGenerator()->ConvertToOutputFormat( + cmStrCat(objectDir, "/cmake_cuda_fatbin.h"), cmOutputConverter::SHELL); + dcompile.Variables["REGISTER"] = + this->GetLocalGenerator()->ConvertToOutputFormat( + cmStrCat(objectDir, "/cmake_cuda_register.h"), cmOutputConverter::SHELL); + this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), + dcompile); +} + +void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkStatement( + const std::string& config, const std::string& fileConfig, + const std::string& outputDir, const std::string& output) +{ + cmGeneratorTarget* genTarget = this->GetGeneratorTarget(); + cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator(); + std::string targetOutputImplib = ConvertToNinjaPath( genTarget->GetFullPath(config, cmStateEnums::ImportLibraryArtifact)); @@ -606,8 +795,8 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( cmStrCat(this->GetLocalGenerator()->GetTargetDirectory(genTarget), globalGen->ConfigDirectory(fileConfig), "/"); targetOutputFileConfigDir = - globalGen->ExpandCFGIntDir(targetOutputDir, fileConfig); - if (targetOutputDir == targetOutputFileConfigDir) { + globalGen->ExpandCFGIntDir(outputDir, fileConfig); + if (outputDir == targetOutputFileConfigDir) { return; } @@ -623,27 +812,15 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( } } - if (firstForConfig) { - globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal); - } - this->DeviceLinkObject = targetOutputReal; - - // Write comments. - cmGlobalNinjaGenerator::WriteDivider(this->GetCommonFileStream()); - const cmStateEnums::TargetType targetType = genTarget->GetType(); - this->GetCommonFileStream() << "# Device Link build statements for " - << cmState::GetTargetTypeName(targetType) - << " target " << this->GetTargetName() << "\n\n"; - // Compute the comment. cmNinjaBuild build(this->LanguageLinkerDeviceRule(config)); build.Comment = - cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal); + cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', output); cmNinjaVars& vars = build.Variables; // Compute outputs. - build.Outputs.push_back(targetOutputReal); + build.Outputs.push_back(output); // Compute specific libraries to link with. build.ExplicitDeps = this->GetObjects(config); build.ImplicitDeps = @@ -659,7 +836,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator(); vars["TARGET_FILE"] = - localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL); + localGen.ConvertToOutputFormat(output, cmOutputConverter::SHELL); std::unique_ptr<cmLinkLineComputer> linkLineComputer( new cmNinjaLinkLineDeviceComputer( @@ -683,8 +860,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( // Compute language specific link flags. std::string langFlags; - localGen.AddLanguageFlagsForLinking(langFlags, genTarget, cudaLinkLanguage, - config); + localGen.AddLanguageFlagsForLinking(langFlags, genTarget, "CUDA", config); vars["LANGUAGE_COMPILE_FLAGS"] = langFlags; auto const tgtNames = this->TargetNames(config); @@ -692,7 +868,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( vars["SONAME_FLAG"] = this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage(config)); vars["SONAME"] = tgtNames.SharedObject; - if (targetType == cmStateEnums::SHARED_LIBRARY) { + if (genTarget->GetType() == cmStateEnums::SHARED_LIBRARY) { std::string install_dir = this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(config); if (!install_dir.empty()) { @@ -731,7 +907,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( // do not check if the user has explicitly forced a response file. int const commandLineLengthLimit = static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) - - globalGen->GetRuleCmdLength(this->LanguageLinkerDeviceRule(config)); + globalGen->GetRuleCmdLength(build.Rule); build.RspFile = this->ConvertToNinjaPath( cmStrCat("CMakeFiles/", genTarget->GetName(), @@ -739,13 +915,14 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement( // Gather order-only dependencies. this->GetLocalGenerator()->AppendTargetDepends( - this->GetGeneratorTarget(), build.OrderOnlyDeps, config, config); + this->GetGeneratorTarget(), build.OrderOnlyDeps, config, config, + DependOnTargetArtifact); // Write the build statement for this target. bool usedResponseFile = false; globalGen->WriteBuild(this->GetCommonFileStream(), build, commandLineLengthLimit, &usedResponseFile); - this->WriteDeviceLinkRule(usedResponseFile, config); + this->WriteNvidiaDeviceLinkRule(usedResponseFile, config); } void cmNinjaNormalTargetGenerator::WriteLinkStatement( @@ -910,11 +1087,16 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( linkBuild.ExplicitDeps.push_back( this->ConvertToNinjaPath(this->GetSourceFilePath(source))); } - linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]); } else { linkBuild.ExplicitDeps = this->GetObjects(config); } + + std::vector<std::string> extraISPCObjects = + this->GetGeneratorTarget()->GetGeneratedISPCObjects(config); + std::transform(extraISPCObjects.begin(), extraISPCObjects.end(), + std::back_inserter(linkBuild.ExplicitDeps), MapToNinjaPath()); + linkBuild.ImplicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage(config), config); @@ -1026,8 +1208,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( gt->GetFullNameComponents(prefix, base, suffix, config); std::string dbg_suffix = ".dbg"; // TODO: Where to document? - if (auto d = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) { - dbg_suffix = d; + if (cmProp d = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) { + dbg_suffix = *d; } vars["TARGET_PDB"] = base + suffix + dbg_suffix; } @@ -1090,11 +1272,11 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( cmd += this->GetLocalGenerator()->ConvertToOutputFormat( obj_list_file, cmOutputConverter::SHELL); - const char* nm_executable = GetMakefile()->GetDefinition("CMAKE_NM"); - if (nm_executable && *nm_executable) { + cmProp nm_executable = GetMakefile()->GetDefinition("CMAKE_NM"); + if (cmNonempty(nm_executable)) { cmd += " --nm="; cmd += this->LocalCommonGenerator->ConvertToOutputFormat( - nm_executable, cmOutputConverter::SHELL); + *nm_executable, cmOutputConverter::SHELL); } preLinkCmdLines.push_back(std::move(cmd)); @@ -1143,7 +1325,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( // build response file name std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG"; - const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); + cmProp flag = GetMakefile()->GetDefinition(cmakeLinkVar); bool const lang_supports_response = !(this->TargetLinkLanguage(config) == "RC" || @@ -1160,8 +1342,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp")); // Gather order-only dependencies. - this->GetLocalGenerator()->AppendTargetDepends(gt, linkBuild.OrderOnlyDeps, - config, fileConfig); + this->GetLocalGenerator()->AppendTargetDepends( + gt, linkBuild.OrderOnlyDeps, config, fileConfig, DependOnTargetArtifact); // Add order-only dependencies on versioning symlinks of shared libs we link. if (!this->GeneratorTarget->IsDLLPlatform()) { diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h index 9de99b9d5f..ffc405c702 100644 --- a/Source/cmNinjaNormalTargetGenerator.h +++ b/Source/cmNinjaNormalTargetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmNinjaNormalTargetGenerator_h -#define cmNinjaNormalTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -22,18 +21,31 @@ public: private: std::string LanguageLinkerRule(const std::string& config) const; std::string LanguageLinkerDeviceRule(const std::string& config) const; + std::string LanguageLinkerCudaDeviceRule(const std::string& config) const; + std::string LanguageLinkerCudaDeviceCompileRule( + const std::string& config) const; + std::string LanguageLinkerCudaFatbinaryRule(const std::string& config) const; const char* GetVisibleTypeName() const; void WriteLanguagesRules(const std::string& config); void WriteLinkRule(bool useResponseFile, const std::string& config); - void WriteDeviceLinkRule(bool useResponseFile, const std::string& config); + void WriteDeviceLinkRules(const std::string& config); + void WriteNvidiaDeviceLinkRule(bool useResponseFile, + const std::string& config); void WriteLinkStatement(const std::string& config, const std::string& fileConfig, bool firstForConfig); void WriteDeviceLinkStatement(const std::string& config, const std::string& fileConfig, bool firstForConfig); + void WriteDeviceLinkStatements(const std::string& config, + const std::vector<std::string>& architectures, + const std::string& output); + void WriteNvidiaDeviceLinkStatement(const std::string& config, + const std::string& fileConfig, + const std::string& outputDir, + const std::string& output); void WriteObjectLibStatement(const std::string& config); @@ -46,5 +58,3 @@ private: std::string TargetLinkLanguage(const std::string& config) const; std::string DeviceLinkObject; }; - -#endif // ! cmNinjaNormalTargetGenerator_h diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index d406c9951a..d41cbd286a 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -51,6 +51,7 @@ std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New( return cm::make_unique<cmNinjaNormalTargetGenerator>(target); case cmStateEnums::UTILITY: + case cmStateEnums::INTERFACE_LIBRARY: case cmStateEnums::GLOBAL_TARGET: return cm::make_unique<cmNinjaUtilityTargetGenerator>(target); @@ -65,7 +66,8 @@ cmNinjaTargetGenerator::cmNinjaTargetGenerator(cmGeneratorTarget* target) , LocalGenerator( static_cast<cmLocalNinjaGenerator*>(target->GetLocalGenerator())) { - for (auto const& fileConfig : target->Makefile->GetGeneratorConfigs()) { + for (auto const& fileConfig : + target->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { this->Configs[fileConfig].MacOSXContentGenerator = cm::make_unique<MacOSXContentGeneratorType>(this, fileConfig); } @@ -335,11 +337,13 @@ std::string cmNinjaTargetGenerator::ComputeIncludes( } cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps( - const std::string& linkLanguage, const std::string& config) const + const std::string& linkLanguage, const std::string& config, + bool ignoreType) const { // Static libraries never depend on other targets for linking. - if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY || - this->GeneratorTarget->GetType() == cmStateEnums::OBJECT_LIBRARY) { + if (!ignoreType && + (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY || + this->GeneratorTarget->GetType() == cmStateEnums::OBJECT_LIBRARY)) { return cmNinjaDeps(); } @@ -620,6 +624,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, vars.TargetCompilePDB = "$TARGET_COMPILE_PDB"; vars.ObjectDir = "$OBJECT_DIR"; vars.ObjectFileDir = "$OBJECT_FILE_DIR"; + vars.ISPCHeader = "$ISPC_HEADER_FILE"; cmMakefile* mf = this->GetMakefile(); @@ -652,7 +657,7 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, std::string launcher; const char* val = this->GetLocalGenerator()->GetRuleLauncher( this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE"); - if (val && *val) { + if (cmNonempty(val)) { launcher = cmStrCat(val, ' '); } @@ -750,9 +755,9 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, if (!mf->GetIsSourceFileTryCompile()) { rule.DepType = "gcc"; rule.DepFile = "$DEP_FILE"; - auto d = mf->GetDefinition("CMAKE_C_COMPILER"); + cmProp d = mf->GetDefinition("CMAKE_C_COMPILER"); const std::string cl = - d ? d : mf->GetSafeDefinition("CMAKE_CXX_COMPILER"); + d ? *d : mf->GetSafeDefinition("CMAKE_CXX_COMPILER"); cldeps = cmStrCat('"', cmSystemTools::GetCMClDepsCommand(), "\" ", lang, ' ', vars.Source, " $DEP_FILE $out \"", mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"), @@ -766,8 +771,9 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, if (!depfileFlags.empty()) { cmSystemTools::ReplaceString(depfileFlags, "<DEPFILE>", "$DEP_FILE"); cmSystemTools::ReplaceString(depfileFlags, "<OBJECT>", "$out"); - cmSystemTools::ReplaceString(depfileFlags, "<CMAKE_C_COMPILER>", - mf->GetDefinition("CMAKE_C_COMPILER")); + cmSystemTools::ReplaceString( + depfileFlags, "<CMAKE_C_COMPILER>", + cmToCStr(mf->GetDefinition("CMAKE_C_COMPILER"))); flags += cmStrCat(' ', depfileFlags); } } @@ -800,10 +806,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, std::string compilerLauncher; if (!compileCmds.empty() && (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" || - lang == "OBJC" || lang == "OBJCXX")) { + lang == "ISPC" || lang == "OBJC" || lang == "OBJCXX")) { std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER"); cmProp clauncher = this->GeneratorTarget->GetProperty(clauncher_prop); - if (clauncher && !clauncher->empty()) { + if (cmNonempty(clauncher)) { compilerLauncher = *clauncher; } } @@ -818,8 +824,8 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, cmProp cpplint = this->GeneratorTarget->GetProperty(cpplint_prop); std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK"); cmProp cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop); - if ((iwyu && !iwyu->empty()) || (tidy && !tidy->empty()) || - (cpplint && !cpplint->empty()) || (cppcheck && !cppcheck->empty())) { + if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) || + cmNonempty(cppcheck)) { std::string run_iwyu = cmStrCat(cmakeCmd, " -E __run_co_compile"); if (!compilerLauncher.empty()) { // In __run_co_compile case the launcher command is supplied @@ -829,31 +835,33 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, this->LocalGenerator->EscapeForShell(compilerLauncher)); compilerLauncher.clear(); } - if (iwyu && !iwyu->empty()) { + if (cmNonempty(iwyu)) { run_iwyu += cmStrCat(" --iwyu=", this->GetLocalGenerator()->EscapeForShell(*iwyu)); } - if (tidy && !tidy->empty()) { + if (cmNonempty(tidy)) { run_iwyu += " --tidy="; - const char* driverMode = this->Makefile->GetDefinition( + cmProp p = this->Makefile->GetDefinition( cmStrCat("CMAKE_", lang, "_CLANG_TIDY_DRIVER_MODE")); - if (!(driverMode && *driverMode)) { + std::string driverMode; + if (cmNonempty(p)) { + driverMode = *p; + } else { driverMode = lang == "C" ? "gcc" : "g++"; } run_iwyu += this->GetLocalGenerator()->EscapeForShell( cmStrCat(*tidy, ";--extra-arg-before=--driver-mode=", driverMode)); } - if (cpplint && !cpplint->empty()) { + if (cmNonempty(cpplint)) { run_iwyu += cmStrCat( " --cpplint=", this->GetLocalGenerator()->EscapeForShell(*cpplint)); } - if (cppcheck && !cppcheck->empty()) { + if (cmNonempty(cppcheck)) { run_iwyu += cmStrCat(" --cppcheck=", this->GetLocalGenerator()->EscapeForShell(*cppcheck)); } - if ((tidy && !tidy->empty()) || (cpplint && !cpplint->empty()) || - (cppcheck && !cppcheck->empty())) { + if (cmNonempty(tidy) || cmNonempty(cpplint) || cmNonempty(cppcheck)) { run_iwyu += " --source=$in"; } run_iwyu += " -- "; @@ -931,15 +939,15 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( config); } if (firstForConfig) { - const char* pchExtension = - GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION"); + cmProp pchExtension = GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION"); std::vector<cmSourceFile const*> externalObjects; this->GeneratorTarget->GetExternalObjects(externalObjects, config); for (cmSourceFile const* sf : externalObjects) { auto objectFileName = this->GetGlobalGenerator()->ExpandCFGIntDir( this->GetSourceFilePath(sf), config); - if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) { + if (!cmSystemTools::StringEndsWith(objectFileName, + cmToCStr(pchExtension))) { this->Configs[config].Objects.push_back(objectFileName); } } @@ -994,6 +1002,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( { std::vector<cmSourceFile const*> objectSources; this->GeneratorTarget->GetObjectSources(objectSources, config); + for (cmSourceFile const* sf : objectSources) { this->WriteObjectBuildStatement(sf, config, fileConfig, firstForConfig); } @@ -1148,7 +1157,7 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( // build response file name std::string cmakeLinkVar = cmStrCat(cmakeVarLang, "_RESPONSE_FILE_FLAG"); - const char* flag = GetMakefile()->GetDefinition(cmakeLinkVar); + cmProp flag = GetMakefile()->GetDefinition(cmakeLinkVar); bool const lang_supports_response = !(language == "RC" || (language == "CUDA" && !flag)); @@ -1189,9 +1198,10 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( objBuild.Outputs.push_back(objectFileName); if (firstForConfig) { - const char* pchExtension = + cmProp pchExtension = this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION"); - if (!cmSystemTools::StringEndsWith(objectFileName, pchExtension)) { + if (!cmSystemTools::StringEndsWith(objectFileName, + cmToCStr(pchExtension))) { // Add this object to the list of object files. this->Configs[config].Objects.push_back(objectFileName); } @@ -1359,6 +1369,63 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( objBuild.RspFile = cmStrCat(objectFileName, ".rsp"); + if (language == "ISPC") { + std::string const& objectName = + this->GeneratorTarget->GetObjectName(source); + std::string ispcSource = + cmSystemTools::GetFilenameWithoutLastExtension(objectName); + ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource); + + cmProp ispcSuffixProp = + this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX"); + assert(ispcSuffixProp != nullptr); + + std::string ispcHeaderDirectory = + this->GeneratorTarget->GetObjectDirectory(config); + if (cmProp prop = + this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) { + ispcHeaderDirectory = + cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop); + } + + std::string ispcHeader = + cmStrCat(ispcHeaderDirectory, '/', ispcSource, *ispcSuffixProp); + ispcHeader = this->ConvertToNinjaPath(ispcHeader); + + // Make sure ninja knows what command generates the header + objBuild.ImplicitOuts.push_back(ispcHeader); + + // Make sure ninja knows how to clean the generated header + this->GetGlobalGenerator()->AddAdditionalCleanFile(ispcHeader, config); + + auto ispcSuffixes = + detail::ComputeISPCObjectSuffixes(this->GeneratorTarget); + if (ispcSuffixes.size() > 1) { + std::string rootObjectDir = + this->GeneratorTarget->GetObjectDirectory(config); + auto ispcSideEfffectObjects = detail::ComputeISPCExtraObjects( + objectName, rootObjectDir, ispcSuffixes); + + for (auto sideEffect : ispcSideEfffectObjects) { + sideEffect = this->ConvertToNinjaPath(sideEffect); + objBuild.ImplicitOuts.emplace_back(sideEffect); + this->GetGlobalGenerator()->AddAdditionalCleanFile(sideEffect, config); + } + } + + vars["ISPC_HEADER_FILE"] = + this->GetLocalGenerator()->ConvertToOutputFormat( + ispcHeader, cmOutputConverter::SHELL); + } else { + auto headers = this->GeneratorTarget->GetGeneratedISPCHeaders(config); + if (!headers.empty()) { + std::transform(headers.begin(), headers.end(), headers.begin(), + MapToNinjaPath()); + objBuild.OrderOnlyDeps.insert(objBuild.OrderOnlyDeps.end(), + headers.begin(), headers.end()); + } + } + if (language == "Swift") { this->EmitSwiftDependencyInfo(source, config); } else { diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index 8d4372edc6..a27c9b4b78 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmNinjaTargetGenerator_h -#define cmNinjaTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -114,7 +113,8 @@ protected: /// @return the list of link dependency for the given target @a target. cmNinjaDeps ComputeLinkDeps(const std::string& linkLanguage, - const std::string& config) const; + const std::string& config, + bool ignoreType = false) const; /// @return the source file path for the given @a source. std::string GetSourceFilePath(cmSourceFile const* source) const; @@ -219,5 +219,3 @@ private: std::map<std::string, ByConfig> Configs; }; - -#endif // ! cmNinjaTargetGenerator_h diff --git a/Source/cmNinjaTypes.h b/Source/cmNinjaTypes.h index bd0e83fa18..320f41bb32 100644 --- a/Source/cmNinjaTypes.h +++ b/Source/cmNinjaTypes.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmNinjaTypes_h -#define cmNinjaTypes_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -60,5 +59,3 @@ public: cmNinjaVars Variables; std::string RspFile; }; - -#endif // ! cmNinjaTypes_h diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx index c964bc14fd..ad1d5f1cdb 100644 --- a/Source/cmNinjaUtilityTargetGenerator.cxx +++ b/Source/cmNinjaUtilityTargetGenerator.cxx @@ -103,7 +103,8 @@ void cmNinjaUtilityTargetGenerator::Generate(const std::string& config) std::copy(util_outputs.begin(), util_outputs.end(), std::back_inserter(gg->GetByproductsForCleanTarget())); } - lg->AppendTargetDepends(genTarget, deps, config, config); + lg->AppendTargetDepends(genTarget, deps, config, config, + DependOnTargetArtifact); if (commands.empty()) { phonyBuild.Comment = "Utility command for " + this->GetTargetName(); diff --git a/Source/cmNinjaUtilityTargetGenerator.h b/Source/cmNinjaUtilityTargetGenerator.h index ca3f0a4648..24b47f806d 100644 --- a/Source/cmNinjaUtilityTargetGenerator.h +++ b/Source/cmNinjaUtilityTargetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmNinjaUtilityTargetGenerator_h -#define cmNinjaUtilityTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -19,5 +18,3 @@ public: void Generate(const std::string& config) override; }; - -#endif // ! cmNinjaUtilityTargetGenerator_h diff --git a/Source/cmOSXBundleGenerator.h b/Source/cmOSXBundleGenerator.h index 5bf1d9859b..4c33fcc957 100644 --- a/Source/cmOSXBundleGenerator.h +++ b/Source/cmOSXBundleGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmOSXBundleGenerator_h -#define cmOSXBundleGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -69,5 +68,3 @@ private: cmLocalGenerator* LocalGenerator; std::set<std::string>* MacContentFolders; }; - -#endif diff --git a/Source/cmOptionCommand.h b/Source/cmOptionCommand.h index cbd1cb8643..912e0eeeff 100644 --- a/Source/cmOptionCommand.h +++ b/Source/cmOptionCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmOptionCommand_h -#define cmOptionCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,4 +16,3 @@ class cmExecutionStatus; */ bool cmOptionCommand(std::vector<std::string> const& args, cmExecutionStatus& status); -#endif diff --git a/Source/cmOrderDirectories.h b/Source/cmOrderDirectories.h index 8ce53e075c..7788ea8367 100644 --- a/Source/cmOrderDirectories.h +++ b/Source/cmOrderDirectories.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmOrderDirectories_h -#define cmOrderDirectories_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -95,5 +94,3 @@ private: friend class cmOrderDirectoriesConstraint; friend class cmOrderDirectoriesConstraintLibrary; }; - -#endif diff --git a/Source/cmOutputConverter.h b/Source/cmOutputConverter.h index a8b4528421..655bc87032 100644 --- a/Source/cmOutputConverter.h +++ b/Source/cmOutputConverter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmOutputConverter_h -#define cmOutputConverter_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -117,5 +116,3 @@ private: bool LinkScriptShell; }; - -#endif diff --git a/Source/cmOutputRequiredFilesCommand.h b/Source/cmOutputRequiredFilesCommand.h index 4c11894571..9daba8f859 100644 --- a/Source/cmOutputRequiredFilesCommand.h +++ b/Source/cmOutputRequiredFilesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmOutputRequiredFilesCommand_h -#define cmOutputRequiredFilesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmOutputRequiredFilesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx index d712edb812..246506978f 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx @@ -13,6 +13,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -195,7 +196,7 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args, for (unsigned long i = argvStart; i < count; ++i) { std::ostringstream argName; argName << "ARGV" << i; - const char* arg = status.GetMakefile().GetDefinition(argName.str()); + cmProp arg = status.GetMakefile().GetDefinition(argName.str()); if (!arg) { status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, "PARSE_ARGV called with " + @@ -203,7 +204,7 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args, cmSystemTools::SetFatalErrorOccured(); return true; } - list.emplace_back(arg); + list.emplace_back(*arg); } } diff --git a/Source/cmParseArgumentsCommand.h b/Source/cmParseArgumentsCommand.h index b2e436d89c..008977ba53 100644 --- a/Source/cmParseArgumentsCommand.h +++ b/Source/cmParseArgumentsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmParseArgumentsCommand_h -#define cmParseArgumentsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmParseArgumentsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmPathLabel.h b/Source/cmPathLabel.h index 55dffabe03..d19d2be24a 100644 --- a/Source/cmPathLabel.h +++ b/Source/cmPathLabel.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmPathLabel_h -#define cmPathLabel_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -33,5 +32,3 @@ protected: std::string Label; unsigned int Hash; }; - -#endif diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx index dea3f8a356..01e8c047a0 100644 --- a/Source/cmPolicies.cxx +++ b/Source/cmPolicies.cxx @@ -7,9 +7,11 @@ #include <sstream> #include <vector> +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmState.h" +#include "cmStateSnapshot.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -157,7 +159,8 @@ static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy, bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, std::string const& version_min, - std::string const& version_max) + std::string const& version_max, + WarnCompat warnCompat) { // Parse components of the minimum version. unsigned int minMajor = 2; @@ -244,13 +247,34 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, polPatch = maxPatch; } - return cmPolicies::ApplyPolicyVersion(mf, polMajor, polMinor, polPatch); + return cmPolicies::ApplyPolicyVersion(mf, polMajor, polMinor, polPatch, + warnCompat); } bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer, unsigned int minorVer, - unsigned int patchVer) + unsigned int patchVer, + WarnCompat warnCompat) { + // Warn about policy versions for which support will be removed. + if (warnCompat == WarnCompat::On && + (majorVer < 2 || (majorVer == 2 && minorVer < 8) || + (majorVer == 2 && minorVer == 8 && patchVer < 12)) && + // Avoid warning on calls generated by install(EXPORT) + // in CMake versions prior to 3.18. + !(majorVer == 2 && minorVer == 6 && patchVer == 0 && + mf->GetStateSnapshot().CanPopPolicyScope() && + cmSystemTools::Strucmp(mf->GetBacktrace().Top().Name.c_str(), + "cmake_policy") == 0)) { + mf->IssueMessage( + MessageType::DEPRECATION_WARNING, + "Compatibility with CMake < 2.8.12 will be removed from " + "a future version of CMake.\n" + "Update the VERSION argument <min> value or use a ...<max> suffix " + "to tell CMake that the project does not need compatibility with " + "older versions."); + } + // now loop over all the policies and set them as appropriate std::vector<cmPolicies::PolicyID> ancientPolicies; for (PolicyID pid = cmPolicies::CMP0000; pid != cmPolicies::CMPCOUNT; diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index a82f4211e2..18ce9c3f21 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmPolicies_h -#define cmPolicies_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -320,7 +319,28 @@ class cmMakefile; SELECT(POLICY, CMP0107, "An ALIAS target cannot overwrite another target.", \ 3, 18, 0, cmPolicies::WARN) \ SELECT(POLICY, CMP0108, "A target cannot link to itself through an alias.", \ - 3, 18, 0, cmPolicies::WARN) + 3, 18, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0109, \ + "find_program() requires permission to execute but not to read.", 3, \ + 19, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0110, \ + "add_test() supports arbitrary characters in test names.", 3, 19, 0, \ + cmPolicies::WARN) \ + SELECT(POLICY, CMP0111, \ + "An imported target missing its location property fails during " \ + "generation.", \ + 3, 19, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0112, \ + "Target file component generator expressions do not add target " \ + "dependencies.", \ + 3, 19, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0113, \ + "Makefile generators do not repeat custom commands from target " \ + "dependencies.", \ + 3, 19, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0114, \ + "ExternalProject step targets fully adopt their steps.", 3, 19, 0, \ + cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -353,7 +373,9 @@ class cmMakefile; F(CMP0099) \ F(CMP0104) \ F(CMP0105) \ - F(CMP0108) + F(CMP0108) \ + F(CMP0112) \ + F(CMP0113) /** \class cmPolicies * \brief Handles changes in CMake behavior and policies @@ -396,12 +418,20 @@ public: //! Get the default status for a policy static cmPolicies::PolicyStatus GetPolicyStatus(cmPolicies::PolicyID id); + enum class WarnCompat + { + Off, + On + }; + //! Set a policy level for this listfile static bool ApplyPolicyVersion(cmMakefile* mf, std::string const& version_min, - std::string const& version_max); + std::string const& version_max, + WarnCompat warnCompat); static bool ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer, - unsigned int minorVer, unsigned int patchVer); + unsigned int minorVer, unsigned int patchVer, + WarnCompat warnCompat); //! return a warning string for a given policy static std::string GetPolicyWarning(cmPolicies::PolicyID id); @@ -426,5 +456,3 @@ public: std::bitset<cmPolicies::CMPCOUNT * POLICY_STATUS_COUNT> Status; }; }; - -#endif diff --git a/Source/cmProcessOutput.h b/Source/cmProcessOutput.h index 3db47a4b49..a1f73bd0e2 100644 --- a/Source/cmProcessOutput.h +++ b/Source/cmProcessOutput.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmProcessOutput_h -#define cmProcessOutput_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -84,5 +83,3 @@ private: bool DoDecodeText(std::string raw, std::string& decoded, wchar_t* lastChar); #endif }; - -#endif diff --git a/Source/cmProcessTools.h b/Source/cmProcessTools.h index 21d59c463d..74ec5e0b15 100644 --- a/Source/cmProcessTools.h +++ b/Source/cmProcessTools.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmProcessTools_h -#define cmProcessTools_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -86,5 +85,3 @@ public: OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto); }; - -#endif diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 2ec66d9f14..0cfba63534 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -15,6 +15,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmProperty.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -302,8 +303,8 @@ bool cmProjectCommand(std::vector<std::string> const& args, } std::string vw; for (std::string const& i : vv) { - const char* const v = mf.GetDefinition(i); - if (v && *v) { + cmProp v = mf.GetDefinition(i); + if (cmNonempty(v)) { if (cmp0048 == cmPolicies::WARN) { if (!injectedProjectCommand) { vw += "\n "; @@ -352,12 +353,12 @@ static bool IncludeByVariable(cmExecutionStatus& status, const std::string& variable) { cmMakefile& mf = status.GetMakefile(); - const char* const include = mf.GetDefinition(variable); + cmProp include = mf.GetDefinition(variable); if (!include) { return true; } - const bool readit = mf.ReadDependentFile(include); + const bool readit = mf.ReadDependentFile(*include); if (readit) { return true; } @@ -366,7 +367,7 @@ static bool IncludeByVariable(cmExecutionStatus& status, return true; } - status.SetError(cmStrCat("could not find file:\n ", include)); + status.SetError(cmStrCat("could not find file:\n ", *include)); return false; } diff --git a/Source/cmProjectCommand.h b/Source/cmProjectCommand.h index c06b4594ce..33f095523b 100644 --- a/Source/cmProjectCommand.h +++ b/Source/cmProjectCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmProjectCommand_h -#define cmProjectCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmProjectCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmProperty.h b/Source/cmProperty.h index b0fcce7f53..1e03c3f0f5 100644 --- a/Source/cmProperty.h +++ b/Source/cmProperty.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmProperty_h -#define cmProperty_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -26,4 +25,12 @@ public: using cmProp = const std::string*; -#endif +inline const char* cmToCStr(cmProp p) +{ + return p ? p->c_str() : nullptr; +} + +inline const char* cmToCStrSafe(cmProp p) +{ + return p ? p->c_str() : ""; +} diff --git a/Source/cmPropertyDefinition.h b/Source/cmPropertyDefinition.h index f83bc4f742..fca936e29a 100644 --- a/Source/cmPropertyDefinition.h +++ b/Source/cmPropertyDefinition.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmPropertyDefinition_h -#define cmPropertyDefinition_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -65,5 +64,3 @@ private: using key_type = std::pair<std::string, cmProperty::ScopeType>; std::map<key_type, cmPropertyDefinition> Map_; }; - -#endif diff --git a/Source/cmPropertyMap.h b/Source/cmPropertyMap.h index 5fc46a2b4d..cda585a89f 100644 --- a/Source/cmPropertyMap.h +++ b/Source/cmPropertyMap.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmPropertyMap_h -#define cmPropertyMap_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -49,5 +48,3 @@ public: private: std::unordered_map<std::string, std::string> Map_; }; - -#endif diff --git a/Source/cmQTWrapCPPCommand.cxx b/Source/cmQTWrapCPPCommand.cxx index 795c2ee5a9..de462db350 100644 --- a/Source/cmQTWrapCPPCommand.cxx +++ b/Source/cmQTWrapCPPCommand.cxx @@ -41,7 +41,7 @@ bool cmQTWrapCPPCommand(std::vector<std::string> const& args, cmSourceFile* sf = mf.GetOrCreateSource(newName, true); if (curr) { cmProp p = curr->GetProperty("ABSTRACT"); - sf->SetProperty("ABSTRACT", p ? p->c_str() : nullptr); + sf->SetProperty("ABSTRACT", cmToCStr(p)); } // Compute the name of the header from which to generate the file. diff --git a/Source/cmQTWrapCPPCommand.h b/Source/cmQTWrapCPPCommand.h index 75fa180280..28ceb3a47b 100644 --- a/Source/cmQTWrapCPPCommand.h +++ b/Source/cmQTWrapCPPCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQTWrapCPPCommand_h -#define cmQTWrapCPPCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmQTWrapCPPCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmQTWrapUICommand.h b/Source/cmQTWrapUICommand.h index a17ef547db..3f92ea9185 100644 --- a/Source/cmQTWrapUICommand.h +++ b/Source/cmQTWrapUICommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQTWrapUICommand_h -#define cmQTWrapUICommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmQTWrapUICommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index a740ba30a8..cf90417f29 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGen_h -#define cmQtAutoGen_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -141,5 +140,3 @@ public: std::vector<std::string> ListOptions_; }; }; - -#endif diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx index 3d4f5d7227..fac2bbf2dc 100644 --- a/Source/cmQtAutoGenGlobalInitializer.cxx +++ b/Source/cmQtAutoGenGlobalInitializer.cxx @@ -50,7 +50,7 @@ cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer( { cmMakefile* makefile = localGen->GetMakefile(); // Detect global autogen target name - if (cmIsOn(makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTOGEN_TARGET"))) { + if (makefile->IsOn("CMAKE_GLOBAL_AUTOGEN_TARGET")) { std::string targetName = makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTOGEN_TARGET_NAME"); if (targetName.empty()) { @@ -61,7 +61,7 @@ cmQtAutoGenGlobalInitializer::cmQtAutoGenGlobalInitializer( } // Detect global autorcc target name - if (cmIsOn(makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTORCC_TARGET"))) { + if (makefile->IsOn("CMAKE_GLOBAL_AUTORCC_TARGET")) { std::string targetName = makefile->GetSafeDefinition("CMAKE_GLOBAL_AUTORCC_TARGET_NAME"); if (targetName.empty()) { diff --git a/Source/cmQtAutoGenGlobalInitializer.h b/Source/cmQtAutoGenGlobalInitializer.h index 2f6e581852..cdae137510 100644 --- a/Source/cmQtAutoGenGlobalInitializer.h +++ b/Source/cmQtAutoGenGlobalInitializer.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGenGlobalInitializer_h -#define cmQtAutoGenGlobalInitializer_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -82,5 +81,3 @@ private: CompilerFeatures_; Keywords const Keywords_; }; - -#endif diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index bd0d6bf5df..3b62e9cf8b 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -315,10 +315,9 @@ bool cmQtAutoGenInitializer::InitCustomTargets() { // Configurations this->MultiConfig = this->GlobalGen->IsMultiConfig(); - this->ConfigDefault = this->Makefile->GetConfigurations(this->ConfigsList); - if (this->ConfigsList.empty()) { - this->ConfigsList.push_back(this->ConfigDefault); - } + this->ConfigDefault = this->Makefile->GetDefaultConfiguration(); + this->ConfigsList = + this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); // Verbosity { @@ -490,7 +489,7 @@ bool cmQtAutoGenInitializer::InitCustomTargets() if (this->Moc.Enabled) { // Path prefix - if (cmIsOn(this->GenTarget->GetSafeProperty("AUTOMOC_PATH_PREFIX"))) { + if (cmIsOn(this->GenTarget->GetProperty("AUTOMOC_PATH_PREFIX"))) { this->Moc.PathPrefix = true; } @@ -789,9 +788,9 @@ bool cmQtAutoGenInitializer::InitScanFiles() // Register files that will be scanned by moc or uic if (this->MocOrUicEnabled()) { - if (cm->IsHeaderExtension(extLower)) { + if (cm->IsAHeaderExtension(extLower)) { addMUHeader(makeMUFile(sf, fullPath, true), extLower); - } else if (cm->IsSourceExtension(extLower)) { + } else if (cm->IsACLikeSourceExtension(extLower)) { addMUSource(makeMUFile(sf, fullPath, true)); } } @@ -863,7 +862,7 @@ bool cmQtAutoGenInitializer::InitScanFiles() if (sf != nullptr) { auto eMuf = makeMUFile(sf, fullPath, true); - // Ony process moc/uic when the parent is processed as well + // Only process moc/uic when the parent is processed as well if (!muf.MocIt) { eMuf->MocIt = false; } @@ -895,14 +894,14 @@ bool cmQtAutoGenInitializer::InitScanFiles() std::string const& extLower = cmSystemTools::LowerCase(sf->GetExtension()); - if (cm->IsHeaderExtension(extLower)) { + if (cm->IsAHeaderExtension(extLower)) { if (!cm::contains(this->AutogenTarget.Headers, sf.get())) { auto muf = makeMUFile(sf.get(), fullPath, false); if (muf->SkipMoc || muf->SkipUic) { addMUHeader(std::move(muf), extLower); } } - } else if (cm->IsSourceExtension(extLower)) { + } else if (cm->IsACLikeSourceExtension(extLower)) { if (!cm::contains(this->AutogenTarget.Sources, sf.get())) { auto muf = makeMUFile(sf.get(), fullPath, false); if (muf->SkipMoc || muf->SkipUic) { @@ -1659,7 +1658,7 @@ void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName, }; for (std::string const& prop : props) { cmProp propName = this->Makefile->GetState()->GetGlobalProperty(prop); - if (propName && !propName->empty()) { + if (cmNonempty(propName)) { groupName = *propName; property = prop; break; @@ -1759,8 +1758,8 @@ cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target) // Read versions from variables for (auto const& keyPair : keys) { - addVersion(target->Makefile->GetDef(std::string(keyPair.first)), - target->Makefile->GetDef(std::string(keyPair.second))); + addVersion(target->Makefile->GetDefinition(std::string(keyPair.first)), + target->Makefile->GetDefinition(std::string(keyPair.second))); } // Read versions from directory properties diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index 48ec1a0177..3ab303ac1b 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGenInitializer_h -#define cmQtAutoGenInitializer_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -249,5 +248,3 @@ private: std::vector<Qrc> Qrcs; } Rcc; }; - -#endif diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index 83fb3ed883..b4f057d066 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGenerator_h -#define cmQtAutoGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -177,5 +176,3 @@ private: // -- Directories ProjectDirsT ProjectDirs_; }; - -#endif diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h index ffcc2db705..20f9d6eeb8 100644 --- a/Source/cmQtAutoMocUic.h +++ b/Source/cmQtAutoMocUic.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoMocUic_h -#define cmQtAutoMocUic_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ * @return true on success */ bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config); - -#endif diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h index a74b33ae4e..d525efae96 100644 --- a/Source/cmQtAutoRcc.h +++ b/Source/cmQtAutoRcc.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoRcc_h -#define cmQtAutoRcc_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ * @return true on success */ bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config); - -#endif diff --git a/Source/cmRST.h b/Source/cmRST.h index 6b5d416d86..17cdfe896e 100644 --- a/Source/cmRST.h +++ b/Source/cmRST.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef _cmRST_h -#define _cmRST_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -97,5 +96,3 @@ private: std::set<std::string> Replaced; std::string ReplaceName; }; - -#endif diff --git a/Source/cmRange.h b/Source/cmRange.h index 3be5193e79..30af7d2718 100644 --- a/Source/cmRange.h +++ b/Source/cmRange.h @@ -1,7 +1,6 @@ /* 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 +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -235,5 +234,3 @@ auto cmReverseRange(Range const& range) -> cmRange<decltype(range.rbegin())> { return { range.rbegin(), range.rend() }; } - -#endif diff --git a/Source/cmRemoveCommand.cxx b/Source/cmRemoveCommand.cxx index 457b708489..13455886b2 100644 --- a/Source/cmRemoveCommand.cxx +++ b/Source/cmRemoveCommand.cxx @@ -4,6 +4,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" // cmRemoveCommand @@ -16,7 +17,7 @@ bool cmRemoveCommand(std::vector<std::string> const& args, std::string const& variable = args[0]; // VAR is always first // get the old value - const char* cacheValue = status.GetMakefile().GetDefinition(variable); + cmProp cacheValue = status.GetMakefile().GetDefinition(variable); // if there is no old value then return if (!cacheValue) { @@ -24,7 +25,7 @@ bool cmRemoveCommand(std::vector<std::string> const& args, } // expand the variable - std::vector<std::string> const varArgsExpanded = cmExpandedList(cacheValue); + std::vector<std::string> const varArgsExpanded = cmExpandedList(*cacheValue); // expand the args // check for REMOVE(VAR v1 v2 ... vn) diff --git a/Source/cmRemoveCommand.h b/Source/cmRemoveCommand.h index fb72ab5f76..37bfd8c93a 100644 --- a/Source/cmRemoveCommand.h +++ b/Source/cmRemoveCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmRemoveCommand_h -#define cmRemoveCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ class cmExecutionStatus; */ bool cmRemoveCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmRemoveDefinitionsCommand.h b/Source/cmRemoveDefinitionsCommand.h index 868416bc1f..8d0fe18b3f 100644 --- a/Source/cmRemoveDefinitionsCommand.h +++ b/Source/cmRemoveDefinitionsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmRemoveDefinitionsCommand_h -#define cmRemoveDefinitionsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmRemoveDefinitionsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmReturnCommand.h b/Source/cmReturnCommand.h index 2404a363db..abae1a4b96 100644 --- a/Source/cmReturnCommand.h +++ b/Source/cmReturnCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmReturnCommand_h -#define cmReturnCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -13,5 +12,3 @@ class cmExecutionStatus; /// Return from a directory or function bool cmReturnCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx index 254131b4d7..f5f9c67140 100644 --- a/Source/cmRulePlaceholderExpander.cxx +++ b/Source/cmRulePlaceholderExpander.cxx @@ -90,6 +90,11 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( return replaceValues.AIXExports; } } + if (replaceValues.ISPCHeader) { + if (variable == "ISPC_HEADER") { + return replaceValues.ISPCHeader; + } + } if (replaceValues.Defines && variable == "DEFINES") { return replaceValues.Defines; } @@ -136,6 +141,16 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( return replaceValues.DependencyFile; } } + if (replaceValues.Fatbinary) { + if (variable == "FATBINARY") { + return replaceValues.Fatbinary; + } + } + if (replaceValues.RegisterFile) { + if (variable == "REGISTER_FILE") { + return replaceValues.RegisterFile; + } + } if (replaceValues.Target) { if (variable == "TARGET_QUOTED") { @@ -261,7 +276,7 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable( this->VariableMappings["CMAKE_" + compIt->second + "_COMPILE_OPTIONS_SYSROOT"]; - // if there is a required first argument to the compiler add it + // if there are required arguments to the compiler add it // to the compiler string if (!compilerArg1.empty()) { ret += " "; diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h index 09e8a3b1c0..c8d107d4d1 100644 --- a/Source/cmRulePlaceholderExpander.h +++ b/Source/cmRulePlaceholderExpander.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmRulePlaceholderExpander_h -#define cmRulePlaceholderExpander_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -64,6 +63,9 @@ public: const char* SwiftModuleName; const char* SwiftOutputFileMap; const char* SwiftSources; + const char* ISPCHeader; + const char* Fatbinary; + const char* RegisterFile; }; // Expand rule variables in CMake of the type found in language rules @@ -84,5 +86,3 @@ private: std::string CompilerSysroot; std::string LinkerSysroot; }; - -#endif diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h index 9e2dfb6c21..7f3b8e9bd9 100644 --- a/Source/cmRuntimeDependencyArchive.h +++ b/Source/cmRuntimeDependencyArchive.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmRuntimeDependencyArchive_h -#define cmRuntimeDependencyArchive_h +#pragma once #include <map> #include <memory> @@ -66,5 +65,3 @@ private: std::map<std::string, std::set<std::string>> ResolvedPaths; std::set<std::string> UnresolvedPaths; }; - -#endif // cmRuntimeDependencyArchive_h diff --git a/Source/cmScriptGenerator.h b/Source/cmScriptGenerator.h index 7d676c9c33..46d794c49a 100644 --- a/Source/cmScriptGenerator.h +++ b/Source/cmScriptGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmScriptGenerator_h -#define cmScriptGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -90,5 +89,3 @@ private: void GenerateScriptActionsOnce(std::ostream& os, Indent indent); void GenerateScriptActionsPerConfig(std::ostream& os, Indent indent); }; - -#endif diff --git a/Source/cmSearchPath.cxx b/Source/cmSearchPath.cxx index 766d3478b3..a58be621c5 100644 --- a/Source/cmSearchPath.cxx +++ b/Source/cmSearchPath.cxx @@ -8,6 +8,7 @@ #include "cmFindCommon.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -77,8 +78,8 @@ void cmSearchPath::AddCMakePath(const std::string& variable) assert(this->FC != nullptr); // Get a path from a CMake variable. - if (const char* value = this->FC->Makefile->GetDefinition(variable)) { - std::vector<std::string> expanded = cmExpandedList(value); + if (cmProp value = this->FC->Makefile->GetDefinition(variable)) { + std::vector<std::string> expanded = cmExpandedList(*value); for (std::string const& p : expanded) { this->AddPathInternal( @@ -101,8 +102,8 @@ void cmSearchPath::AddCMakePrefixPath(const std::string& variable) assert(this->FC != nullptr); // Get a path from a CMake variable. - if (const char* value = this->FC->Makefile->GetDefinition(variable)) { - std::vector<std::string> expanded = cmExpandedList(value); + if (cmProp value = this->FC->Makefile->GetDefinition(variable)) { + std::vector<std::string> expanded = cmExpandedList(*value); this->AddPrefixPaths( expanded, this->FC->Makefile->GetCurrentSourceDirectory().c_str()); @@ -178,15 +179,15 @@ void cmSearchPath::AddPrefixPaths(const std::vector<std::string>& paths, dir += "/"; } if (subdir == "include" || subdir == "lib") { - const char* arch = + cmProp arch = this->FC->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE"); - if (arch && *arch) { + if (cmNonempty(arch)) { if (this->FC->Makefile->IsDefinitionSet("CMAKE_SYSROOT") && this->FC->Makefile->IsDefinitionSet( "CMAKE_PREFIX_LIBRARY_ARCHITECTURE")) { - this->AddPathInternal(cmStrCat('/', arch, dir, subdir), base); + this->AddPathInternal(cmStrCat('/', *arch, dir, subdir), base); } else { - this->AddPathInternal(cmStrCat(dir, subdir, '/', arch), base); + this->AddPathInternal(cmStrCat(dir, subdir, '/', *arch), base); } } } diff --git a/Source/cmSearchPath.h b/Source/cmSearchPath.h index 3ecc73bca2..c15cb97b7b 100644 --- a/Source/cmSearchPath.h +++ b/Source/cmSearchPath.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSearchPath_h -#define cmSearchPath_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -50,5 +49,3 @@ protected: cmFindCommon* FC; std::vector<std::string> Paths; }; - -#endif diff --git a/Source/cmSeparateArgumentsCommand.cxx b/Source/cmSeparateArgumentsCommand.cxx index cfe308755b..52b1a4431b 100644 --- a/Source/cmSeparateArgumentsCommand.cxx +++ b/Source/cmSeparateArgumentsCommand.cxx @@ -4,8 +4,13 @@ #include <algorithm> +#include <cmext/string_view> + +#include "cmArgumentParser.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmProperty.h" +#include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -18,86 +23,143 @@ bool cmSeparateArgumentsCommand(std::vector<std::string> const& args, return false; } - std::string var; - std::string command; - enum Mode - { - ModeOld, - ModeUnix, - ModeWindows - }; - Mode mode = ModeOld; - enum Doing - { - DoingNone, - DoingVariable, - DoingMode, - DoingCommand - }; - Doing doing = DoingVariable; - for (std::string const& arg : args) { - if (doing == DoingVariable) { - var = arg; - doing = DoingMode; - // This will always clone one of the other blocks. - // NOLINTNEXTLINE(bugprone-branch-clone) - } else if (doing == DoingMode && arg == "NATIVE_COMMAND") { -#ifdef _WIN32 - mode = ModeWindows; -#else - mode = ModeUnix; -#endif - doing = DoingCommand; - } else if (doing == DoingMode && arg == "UNIX_COMMAND") { - mode = ModeUnix; - doing = DoingCommand; - } else if (doing == DoingMode && arg == "WINDOWS_COMMAND") { - mode = ModeWindows; - doing = DoingCommand; - } else if (doing == DoingCommand) { - command = arg; - doing = DoingNone; - } else { - status.SetError(cmStrCat("given unknown argument ", arg)); - return false; - } - } + std::string const& var = args.front(); - if (mode == ModeOld) { + if (args.size() == 1) { // Original space-replacement version of command. - if (const char* def = status.GetMakefile().GetDefinition(var)) { - std::string value = def; + if (cmProp def = status.GetMakefile().GetDefinition(var)) { + std::string value = *def; std::replace(value.begin(), value.end(), ' ', ';'); status.GetMakefile().AddDefinition(var, value); } - } else { - // Parse the command line. - std::vector<std::string> vec; - if (mode == ModeUnix) { - cmSystemTools::ParseUnixCommandLine(command.c_str(), vec); - } else // if(mode == ModeWindows) - { - cmSystemTools::ParseWindowsCommandLine(command.c_str(), vec); + + return true; + } + + struct Arguments + { + bool UnixCommand = false; + bool WindowsCommand = false; + bool NativeCommand = false; + bool Program = false; + bool SeparateArgs = false; + }; + + static auto const parser = + cmArgumentParser<Arguments>{} + .Bind("UNIX_COMMAND"_s, &Arguments::UnixCommand) + .Bind("WINDOWS_COMMAND"_s, &Arguments::WindowsCommand) + .Bind("NATIVE_COMMAND"_s, &Arguments::NativeCommand) + .Bind("PROGRAM"_s, &Arguments::Program) + .Bind("SEPARATE_ARGS"_s, &Arguments::SeparateArgs); + + std::vector<std::string> unparsedArguments; + Arguments arguments = + parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments); + + if (!arguments.UnixCommand && !arguments.WindowsCommand && + !arguments.NativeCommand) { + status.SetError("missing required option: 'UNIX_COMMAND' or " + "'WINDOWS_COMMAND' or 'NATIVE_COMMAND'"); + return false; + } + if ((arguments.UnixCommand && arguments.WindowsCommand) || + (arguments.UnixCommand && arguments.NativeCommand) || + (arguments.WindowsCommand && arguments.NativeCommand)) { + status.SetError("'UNIX_COMMAND', 'WINDOWS_COMMAND' and 'NATIVE_COMMAND' " + "are mutually exclusive"); + return false; + } + if (arguments.SeparateArgs && !arguments.Program) { + status.SetError("`SEPARATE_ARGS` option requires `PROGRAM' option"); + return false; + } + + if (unparsedArguments.size() > 1) { + status.SetError("given unexpected argument(s)"); + return false; + } + + if (unparsedArguments.empty()) { + status.GetMakefile().AddDefinition(var, {}); + return true; + } + + std::string& command = unparsedArguments.front(); + + if (command.empty()) { + status.GetMakefile().AddDefinition(var, command); + return true; + } + + if (arguments.Program && !arguments.SeparateArgs) { + std::string program; + std::string programArgs; + + // First assume the path to the program was specified with no + // arguments and with no quoting or escaping for spaces. + // Only bother doing this if there is non-whitespace. + if (!cmTrimWhitespace(command).empty()) { + program = cmSystemTools::FindProgram(command); } - // Construct the result list value. - std::string value; - const char* sep = ""; - for (std::string const& vi : vec) { - // Separate from the previous argument. - value += sep; - sep = ";"; - - // Preserve semicolons. - for (char si : vi) { - if (si == ';') { - value += '\\'; + // If that failed then assume a command-line string was given + // and split the program part from the rest of the arguments. + if (program.empty()) { + if (cmSystemTools::SplitProgramFromArgs(command, program, programArgs)) { + if (!cmSystemTools::FileExists(program)) { + program = cmSystemTools::FindProgram(program); } - value += si; } } - status.GetMakefile().AddDefinition(var, value); + + if (!program.empty()) { + program += cmStrCat(';', programArgs); + } + + status.GetMakefile().AddDefinition(var, program); + return true; } + // split command given + std::vector<std::string> values; + + if (arguments.NativeCommand) { +#if defined(_WIN32) + arguments.WindowsCommand = true; +#else + arguments.UnixCommand = true; +#endif + } + + if (arguments.UnixCommand) { + cmSystemTools::ParseUnixCommandLine(command.c_str(), values); + } else { + cmSystemTools::ParseWindowsCommandLine(command.c_str(), values); + } + + if (arguments.Program) { + // check program exist + if (!cmSystemTools::FileExists(values.front())) { + auto result = cmSystemTools::FindProgram(values.front()); + if (result.empty()) { + values.clear(); + } else { + values.front() = result; + } + } + } + + // preserve semicolons in arguments + std::for_each(values.begin(), values.end(), [](std::string& value) { + std::string::size_type pos = 0; + while ((pos = value.find_first_of(';', pos)) != std::string::npos) { + value.insert(pos, 1, '\\'); + pos += 2; + } + }); + auto value = cmJoin(values, ";"); + status.GetMakefile().AddDefinition(var, value); + return true; } diff --git a/Source/cmSeparateArgumentsCommand.h b/Source/cmSeparateArgumentsCommand.h index e000c511d2..d284a40259 100644 --- a/Source/cmSeparateArgumentsCommand.h +++ b/Source/cmSeparateArgumentsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSeparateArgumentsCommand_h -#define cmSeparateArgumentsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ class cmExecutionStatus; */ bool cmSeparateArgumentsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index 4f7131fc62..e586fd9d8f 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -136,6 +136,7 @@ bool cmServerProtocol::Activate(cmServer* server, this->m_Server = server; this->m_CMakeInstance = cm::make_unique<cmake>(cmake::RoleProject, cmState::Project); + this->m_WarnUnused = false; const bool result = this->DoActivate(request, errorMessage); if (!result) { this->m_CMakeInstance = nullptr; @@ -636,7 +637,7 @@ cmServerResponse cmServerProtocol1::ProcessGlobalSettings( obj[kTRACE_KEY] = cm->GetTrace(); obj[kTRACE_EXPAND_KEY] = cm->GetTraceExpand(); obj[kWARN_UNINITIALIZED_KEY] = cm->GetWarnUninitialized(); - obj[kWARN_UNUSED_KEY] = cm->GetWarnUnused(); + obj[kWARN_UNUSED_KEY] = m_WarnUnused; obj[kWARN_UNUSED_CLI_KEY] = cm->GetWarnUnusedCli(); obj[kCHECK_SYSTEM_VARS_KEY] = cm->GetCheckSystemVars(); @@ -682,7 +683,7 @@ cmServerResponse cmServerProtocol1::ProcessSetGlobalSettings( setBool(request, kTRACE_EXPAND_KEY, [cm](bool e) { cm->SetTraceExpand(e); }); setBool(request, kWARN_UNINITIALIZED_KEY, [cm](bool e) { cm->SetWarnUninitialized(e); }); - setBool(request, kWARN_UNUSED_KEY, [cm](bool e) { cm->SetWarnUnused(e); }); + setBool(request, kWARN_UNUSED_KEY, [this](bool e) { m_WarnUnused = e; }); setBool(request, kWARN_UNUSED_CLI_KEY, [cm](bool e) { cm->SetWarnUnusedCli(e); }); setBool(request, kCHECK_SYSTEM_VARS_KEY, diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h index c71b7bf606..6009e23bf8 100644 --- a/Source/cmServerProtocol.h +++ b/Source/cmServerProtocol.h @@ -94,6 +94,7 @@ protected: // Implement protocol specific activation tasks here. Called from Activate(). virtual bool DoActivate(const cmServerRequest& request, std::string* errorMessage); + bool m_WarnUnused = false; // storage for legacy option private: std::unique_ptr<cmake> m_CMakeInstance; diff --git a/Source/cmSetCommand.h b/Source/cmSetCommand.h index 0973d33aa7..695b185259 100644 --- a/Source/cmSetCommand.h +++ b/Source/cmSetCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSetCommand_h -#define cmSetCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ class cmExecutionStatus; */ bool cmSetCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmSetDirectoryPropertiesCommand.h b/Source/cmSetDirectoryPropertiesCommand.h index c243dd70f7..f5b6406491 100644 --- a/Source/cmSetDirectoryPropertiesCommand.h +++ b/Source/cmSetDirectoryPropertiesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSetDirectoryPropertiesCommand_h -#define cmSetDirectoryPropertiesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmSetDirectoryPropertiesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx index 6ca763b0ba..df6a38a31d 100644 --- a/Source/cmSetPropertyCommand.cxx +++ b/Source/cmSetPropertyCommand.cxx @@ -113,7 +113,7 @@ bool HandleSourceFileDirectoryScopes( "given non-existent target for TARGET_DIRECTORY ", target_name)); return false; } - cmProp target_source_dir = target->GetProperty("SOURCE_DIR"); + cmProp target_source_dir = target->GetProperty("BINARY_DIR"); cmMakefile* target_dir_mf = status.GetMakefile().GetGlobalGenerator()->FindMakefile( *target_source_dir); diff --git a/Source/cmSetPropertyCommand.h b/Source/cmSetPropertyCommand.h index af566a3a5c..89fdd9af1e 100644 --- a/Source/cmSetPropertyCommand.h +++ b/Source/cmSetPropertyCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSetsPropertiesCommand_h -#define cmSetsPropertiesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -41,5 +40,3 @@ void MakeSourceFilePathsAbsoluteIfNeeded( std::vector<std::string>::const_iterator files_it_begin, std::vector<std::string>::const_iterator files_it_end, bool needed); } - -#endif diff --git a/Source/cmSetSourceFilesPropertiesCommand.h b/Source/cmSetSourceFilesPropertiesCommand.h index 5eef7859aa..8f88522e56 100644 --- a/Source/cmSetSourceFilesPropertiesCommand.h +++ b/Source/cmSetSourceFilesPropertiesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSetSourceFilesPropertiesCommand_h -#define cmSetSourceFilesPropertiesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmSetSourceFilesPropertiesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmSetTargetPropertiesCommand.h b/Source/cmSetTargetPropertiesCommand.h index 9d40c7498c..0c04f31936 100644 --- a/Source/cmSetTargetPropertiesCommand.h +++ b/Source/cmSetTargetPropertiesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSetTargetsPropertiesCommand_h -#define cmSetTargetsPropertiesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmSetTargetPropertiesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmSetTestsPropertiesCommand.h b/Source/cmSetTestsPropertiesCommand.h index 4b754641b7..b4f16411f6 100644 --- a/Source/cmSetTestsPropertiesCommand.h +++ b/Source/cmSetTestsPropertiesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSetTestsPropertiesCommand_h -#define cmSetTestsPropertiesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmSetTestsPropertiesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmSiteNameCommand.cxx b/Source/cmSiteNameCommand.cxx index b2d905e5b9..58af8f0b0f 100644 --- a/Source/cmSiteNameCommand.cxx +++ b/Source/cmSiteNameCommand.cxx @@ -6,6 +6,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -26,15 +27,15 @@ bool cmSiteNameCommand(std::vector<std::string> const& args, paths.emplace_back("/sbin"); paths.emplace_back("/usr/local/bin"); - const char* cacheValue = status.GetMakefile().GetDefinition(args[0]); + cmProp cacheValue = status.GetMakefile().GetDefinition(args[0]); if (cacheValue) { return true; } - const char* temp = status.GetMakefile().GetDefinition("HOSTNAME"); + cmProp temp = status.GetMakefile().GetDefinition("HOSTNAME"); std::string hostname_cmd; if (temp) { - hostname_cmd = temp; + hostname_cmd = *temp; } else { hostname_cmd = cmSystemTools::FindProgram("hostname", paths); } diff --git a/Source/cmSiteNameCommand.h b/Source/cmSiteNameCommand.h index e8fc608077..432ba37486 100644 --- a/Source/cmSiteNameCommand.h +++ b/Source/cmSiteNameCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSiteNameCommand_h -#define cmSiteNameCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ class cmExecutionStatus; */ bool cmSiteNameCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx index f5254392db..ef44a578db 100644 --- a/Source/cmSourceFile.cxx +++ b/Source/cmSourceFile.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmSourceFile.h" -#include <array> #include <utility> #include "cmGlobalGenerator.h" @@ -130,13 +129,11 @@ bool cmSourceFile::FindFullPath(std::string* error) // Location path std::string const& lPath = this->Location.GetFullPath(); // List of extension lists - std::array<std::vector<std::string> const*, 2> const extsLists = { - { &makefile->GetCMakeInstance()->GetSourceExtensions(), - &makefile->GetCMakeInstance()->GetHeaderExtensions() } - }; + std::vector<std::string> exts = + makefile->GetCMakeInstance()->GetAllExtensions(); // Tries to find the file in a given directory - auto findInDir = [this, &extsLists, &lPath](std::string const& dir) -> bool { + auto findInDir = [this, &exts, &lPath](std::string const& dir) -> bool { // Compute full path std::string const fullPath = cmSystemTools::CollapseFullPath(lPath, dir); // Try full path @@ -145,14 +142,12 @@ bool cmSourceFile::FindFullPath(std::string* error) return true; } // Try full path with extension - for (auto& exts : extsLists) { - for (std::string const& ext : *exts) { - if (!ext.empty()) { - std::string extPath = cmStrCat(fullPath, '.', ext); - if (cmSystemTools::FileExists(extPath)) { - this->FullPath = extPath; - return true; - } + for (std::string const& ext : exts) { + if (!ext.empty()) { + std::string extPath = cmStrCat(fullPath, '.', ext); + if (cmSystemTools::FileExists(extPath)) { + this->FullPath = extPath; + return true; } } } @@ -175,11 +170,9 @@ bool cmSourceFile::FindFullPath(std::string* error) // Compose error std::string err = cmStrCat("Cannot find source file:\n ", lPath, "\nTried extensions"); - for (auto exts : extsLists) { - for (std::string const& ext : *exts) { - err += " ."; - err += ext; - } + for (std::string const& ext : exts) { + err += " ."; + err += ext; } if (error != nullptr) { *error = std::move(err); @@ -387,8 +380,7 @@ const char* cmSourceFile::GetSafeProperty(const std::string& prop) const bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const { - cmProp p = this->GetProperty(prop); - return p && cmIsOn(*p); + return cmIsOn(this->GetProperty(prop)); } void cmSourceFile::SetProperties(cmPropertyMap properties) diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h index e6690152e4..39ea8e33f4 100644 --- a/Source/cmSourceFile.h +++ b/Source/cmSourceFile.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSourceFile_h -#define cmSourceFile_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -161,5 +160,3 @@ private: #define CM_PCH_REGEX "cmake_pch(_[^.]+)?\\.(h|hxx)$" #define CM_RESOURCE_REGEX "\\.(pdf|plist|png|jpeg|jpg|storyboard|xcassets)$" - -#endif diff --git a/Source/cmSourceFileLocation.cxx b/Source/cmSourceFileLocation.cxx index e852c05bab..222bafac88 100644 --- a/Source/cmSourceFileLocation.cxx +++ b/Source/cmSourceFileLocation.cxx @@ -101,7 +101,7 @@ void cmSourceFileLocation::UpdateExtension(const std::string& name) cmMakefile const* mf = this->Makefile; auto cm = mf->GetCMakeInstance(); if (!gg->GetLanguageFromExtension(ext.c_str()).empty() || - cm->IsSourceExtension(ext) || cm->IsHeaderExtension(ext)) { + cm->IsAKnownExtension(ext)) { // This is a known extension. Use the given filename with extension. this->Name = cmSystemTools::GetFilenameName(name); this->AmbiguousExtension = false; @@ -157,7 +157,7 @@ bool cmSourceFileLocation::MatchesAmbiguousExtension( auto ext = cm::string_view(this->Name).substr(loc.Name.size() + 1); cmMakefile const* mf = this->Makefile; auto cm = mf->GetCMakeInstance(); - return cm->IsSourceExtension(ext) || cm->IsHeaderExtension(ext); + return cm->IsAKnownExtension(ext); } bool cmSourceFileLocation::Matches(cmSourceFileLocation const& loc) diff --git a/Source/cmSourceFileLocation.h b/Source/cmSourceFileLocation.h index 87040b882d..b373d3d782 100644 --- a/Source/cmSourceFileLocation.h +++ b/Source/cmSourceFileLocation.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSourceFileLocation_h -#define cmSourceFileLocation_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -102,5 +101,3 @@ private: void Update(cmSourceFileLocation const& loc); void UpdateExtension(const std::string& name); }; - -#endif diff --git a/Source/cmSourceFileLocationKind.h b/Source/cmSourceFileLocationKind.h index dd4c6dd4fe..73108f10b6 100644 --- a/Source/cmSourceFileLocationKind.h +++ b/Source/cmSourceFileLocationKind.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSourceFileLocationKind_h -#define cmSourceFileLocationKind_h +#pragma once enum class cmSourceFileLocationKind { @@ -11,5 +10,3 @@ enum class cmSourceFileLocationKind // extensions or absolute path. Known }; - -#endif diff --git a/Source/cmSourceGroup.h b/Source/cmSourceGroup.h index 623cded234..295240d773 100644 --- a/Source/cmSourceGroup.h +++ b/Source/cmSourceGroup.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSourceGroup_h -#define cmSourceGroup_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -125,5 +124,3 @@ private: std::unique_ptr<cmSourceGroupInternals> Internal; }; - -#endif diff --git a/Source/cmSourceGroupCommand.h b/Source/cmSourceGroupCommand.h index ad39701579..44e1f8e52f 100644 --- a/Source/cmSourceGroupCommand.h +++ b/Source/cmSourceGroupCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSourceGroupCommand_h -#define cmSourceGroupCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmSourceGroupCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx new file mode 100644 index 0000000000..8672f614bd --- /dev/null +++ b/Source/cmStandardLevelResolver.cxx @@ -0,0 +1,538 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmStandardLevelResolver.h" + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <sstream> +#include <stdexcept> +#include <unordered_map> +#include <utility> +#include <vector> + +#include <cm/iterator> +#include <cmext/algorithm> + +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmProperty.h" +#include "cmStringAlgorithms.h" +#include "cmTarget.h" +#include "cmake.h" + +namespace { + +#define FEATURE_STRING(F) , #F +const char* const C_FEATURES[] = { nullptr FOR_EACH_C_FEATURE( + FEATURE_STRING) }; + +const char* const CXX_FEATURES[] = { nullptr FOR_EACH_CXX_FEATURE( + FEATURE_STRING) }; + +const char* const CUDA_FEATURES[] = { nullptr FOR_EACH_CUDA_FEATURE( + FEATURE_STRING) }; +#undef FEATURE_STRING + +struct StandardNeeded +{ + int index; + int value; +}; + +struct StanardLevelComputer +{ + explicit StanardLevelComputer(std::string lang, std::vector<int> levels, + std::vector<std::string> levelsStr) + : Language(std::move(lang)) + , Levels(std::move(levels)) + , LevelsAsStrings(std::move(levelsStr)) + { + assert(levels.size() == levelsStr.size()); + } + + std::string GetCompileOptionDef(cmMakefile* makefile, + cmGeneratorTarget const* target, + std::string const& config) const + { + + const auto& stds = this->Levels; + const auto& stdsStrings = this->LevelsAsStrings; + + cmProp defaultStd = makefile->GetDefinition( + cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT")); + if (!cmNonempty(defaultStd)) { + // this compiler has no notion of language standard levels + return std::string{}; + } + + bool ext = true; + if (cmProp extPropValue = target->GetLanguageExtensions(this->Language)) { + if (cmIsOff(*extPropValue)) { + ext = false; + } + } + + cmProp standardProp = target->GetLanguageStandard(this->Language, config); + if (!standardProp) { + if (ext) { + // No language standard is specified and extensions are not disabled. + // Check if this compiler needs a flag to enable extensions. + return cmStrCat("CMAKE_", this->Language, "_EXTENSION_COMPILE_OPTION"); + } + return std::string{}; + } + + std::string const type = ext ? "EXTENSION" : "STANDARD"; + + if (target->GetLanguageStandardRequired(this->Language)) { + std::string option_flag = cmStrCat( + "CMAKE_", this->Language, *standardProp, "_", type, "_COMPILE_OPTION"); + + cmProp opt = target->Target->GetMakefile()->GetDefinition(option_flag); + if (!opt) { + std::ostringstream e; + e << "Target \"" << target->GetName() + << "\" requires the language " + "dialect \"" + << this->Language << *standardProp << "\" " + << (ext ? "(with compiler extensions)" : "") + << ", but CMake " + "does not know the compile flags to use to enable it."; + makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } + return option_flag; + } + + std::string standardStr(*standardProp); + if (this->Language == "CUDA" && standardStr == "98") { + standardStr = "03"; + } + + int standardValue = -1; + int defaultValue = -1; + try { + standardValue = std::stoi(standardStr); + defaultValue = std::stoi(*defaultStd); + } catch (std::invalid_argument&) { + // fall through as we want an error + // when we can't find the bad value in the `stds` vector + } + + auto stdIt = std::find(cm::cbegin(stds), cm::cend(stds), standardValue); + if (stdIt == cm::cend(stds)) { + std::string e = + cmStrCat(this->Language, "_STANDARD is set to invalid value '", + standardStr, "'"); + makefile->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, + target->GetBacktrace()); + return std::string{}; + } + + auto defaultStdIt = + std::find(cm::cbegin(stds), cm::cend(stds), defaultValue); + if (defaultStdIt == cm::cend(stds)) { + std::string e = cmStrCat("CMAKE_", this->Language, + "_STANDARD_DEFAULT is set to invalid value '", + *defaultStd, "'"); + makefile->IssueMessage(MessageType::INTERNAL_ERROR, e); + return std::string{}; + } + + // If the standard requested is older than the compiler's default + // then we need to use a flag to change it. + if (stdIt <= defaultStdIt) { + auto offset = std::distance(cm::cbegin(stds), stdIt); + return cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type, + "_COMPILE_OPTION"); + } + + // The standard requested is at least as new as the compiler's default, + // and the standard request is not required. Decay to the newest standard + // for which a flag is defined. + for (; defaultStdIt < stdIt; --stdIt) { + auto offset = std::distance(cm::cbegin(stds), stdIt); + std::string option_flag = + cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type, + "_COMPILE_OPTION"); + if (target->Target->GetMakefile()->GetDefinition(option_flag)) { + return option_flag; + } + } + + return std::string{}; + } + + bool GetNewRequiredStandard(cmMakefile* makefile, + std::string const& targetName, + const std::string& feature, + cmProp currentLangStandardValue, + std::string& newRequiredStandard, + std::string* error) const + { + if (currentLangStandardValue) { + newRequiredStandard = *currentLangStandardValue; + } else { + newRequiredStandard.clear(); + } + + auto needed = this->HighestStandardNeeded(makefile, feature); + + cmProp existingStandard = currentLangStandardValue; + if (existingStandard == nullptr) { + cmProp defaultStandard = makefile->GetDefinition( + cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT")); + if (cmNonempty(defaultStandard)) { + existingStandard = defaultStandard; + } + } + + auto existingLevelIter = cm::cend(this->Levels); + if (existingStandard) { + existingLevelIter = + std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), + std::stoi(*existingStandard)); + if (existingLevelIter == cm::cend(this->Levels)) { + const std::string e = + cmStrCat("The ", this->Language, "_STANDARD property on target \"", + targetName, "\" contained an invalid value: \"", + *existingStandard, "\"."); + if (error) { + *error = e; + } else { + makefile->IssueMessage(MessageType::FATAL_ERROR, e); + } + return false; + } + } + + if (needed.index != -1) { + // Ensure the C++ language level is high enough to support + // the needed C++ features. + if (existingLevelIter == cm::cend(this->Levels) || + existingLevelIter < this->Levels.begin() + needed.index) { + newRequiredStandard = this->LevelsAsStrings[needed.index]; + } + } + + return true; + } + + bool HaveStandardAvailable(cmMakefile* makefile, + cmGeneratorTarget const* target, + std::string const& config, + std::string const& feature) const + { + cmProp defaultStandard = makefile->GetDefinition( + cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT")); + if (!defaultStandard) { + makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("CMAKE_", this->Language, + "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support " + "not fully configured for this compiler.")); + // Return true so the caller does not try to lookup the default standard. + return true; + } + // convert defaultStandard to an integer + if (std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), + std::stoi(*defaultStandard)) == cm::cend(this->Levels)) { + const std::string e = cmStrCat("The CMAKE_", this->Language, + "_STANDARD_DEFAULT variable contains an " + "invalid value: \"", + *defaultStandard, "\"."); + makefile->IssueMessage(MessageType::INTERNAL_ERROR, e); + return false; + } + + cmProp existingStandard = + target->GetLanguageStandard(this->Language, config); + if (!existingStandard) { + existingStandard = defaultStandard; + } + + auto existingLevelIter = + std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), + std::stoi(*existingStandard)); + if (existingLevelIter == cm::cend(this->Levels)) { + const std::string e = + cmStrCat("The ", this->Language, "_STANDARD property on target \"", + target->GetName(), "\" contained an invalid value: \"", + *existingStandard, "\"."); + makefile->IssueMessage(MessageType::FATAL_ERROR, e); + return false; + } + + auto needed = this->HighestStandardNeeded(makefile, feature); + + return (needed.index == -1) || + (this->Levels.begin() + needed.index) <= existingLevelIter; + } + + StandardNeeded HighestStandardNeeded(cmMakefile* makefile, + std::string const& feature) const + { + std::string prefix = cmStrCat("CMAKE_", this->Language); + StandardNeeded maxLevel = { -1, -1 }; + for (size_t i = 0; i < this->Levels.size(); ++i) { + if (cmProp prop = makefile->GetDefinition( + cmStrCat(prefix, this->LevelsAsStrings[i], "_COMPILE_FEATURES"))) { + std::vector<std::string> props = cmExpandedList(*prop); + if (cm::contains(props, feature)) { + maxLevel = { static_cast<int>(i), this->Levels[i] }; + } + } + } + return maxLevel; + } + + bool IsLaterStandard(int lhs, int rhs) const + { + auto rhsIt = + std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), rhs); + + return std::find(rhsIt, cm::cend(this->Levels), lhs) != + cm::cend(this->Levels); + } + + std::string Language; + std::vector<int> Levels; + std::vector<std::string> LevelsAsStrings; +}; + +std::unordered_map<std::string, StanardLevelComputer> StandardComputerMapping = + { + { "C", + StanardLevelComputer{ "C", std::vector<int>{ 90, 99, 11 }, + std::vector<std::string>{ "90", "99", "11" } } }, + { "CXX", + StanardLevelComputer{ + "CXX", std::vector<int>{ 98, 11, 14, 17, 20 }, + std::vector<std::string>{ "98", "11", "14", "17", "20" } } }, + { "CUDA", + StanardLevelComputer{ + "CUDA", std::vector<int>{ 03, 11, 14, 17, 20 }, + std::vector<std::string>{ "03", "11", "14", "17", "20" } } }, + { "OBJC", + StanardLevelComputer{ "OBJC", std::vector<int>{ 90, 99, 11 }, + std::vector<std::string>{ "90", "99", "11" } } }, + { "OBJCXX", + StanardLevelComputer{ + "OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20 }, + std::vector<std::string>{ "98", "11", "14", "17", "20" } } }, + }; +} + +std::string cmStandardLevelResolver::GetCompileOptionDef( + cmGeneratorTarget const* target, std::string const& lang, + std::string const& config) const +{ + const auto& mapping = StandardComputerMapping.find(lang); + if (mapping == cm::cend(StandardComputerMapping)) { + return std::string{}; + } + + return mapping->second.GetCompileOptionDef(this->Makefile, target, config); +} + +bool cmStandardLevelResolver::AddRequiredTargetFeature( + cmTarget* target, const std::string& feature, std::string* error) const +{ + if (cmGeneratorExpression::Find(feature) != std::string::npos) { + target->AppendProperty("COMPILE_FEATURES", feature); + return true; + } + + std::string lang; + if (!this->CheckCompileFeaturesAvailable(target->GetName(), feature, lang, + error)) { + return false; + } + + target->AppendProperty("COMPILE_FEATURES", feature); + + // FIXME: Add a policy to avoid updating the <LANG>_STANDARD target + // property due to COMPILE_FEATURES. The language standard selection + // should be done purely at generate time based on whatever the project + // code put in these properties explicitly. That is mostly true now, + // but for compatibility we need to continue updating the property here. + std::string newRequiredStandard; + bool newRequired = this->GetNewRequiredStandard( + target->GetName(), feature, + target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard, + error); + if (!newRequiredStandard.empty()) { + target->SetProperty(cmStrCat(lang, "_STANDARD"), newRequiredStandard); + } + return newRequired; +} + +bool cmStandardLevelResolver::CheckCompileFeaturesAvailable( + const std::string& targetName, const std::string& feature, std::string& lang, + std::string* error) const +{ + if (!this->CompileFeatureKnown(targetName, feature, lang, error)) { + return false; + } + + const char* features = this->CompileFeaturesAvailable(lang, error); + if (!features) { + return false; + } + + std::vector<std::string> availableFeatures = cmExpandedList(features); + if (!cm::contains(availableFeatures, feature)) { + std::ostringstream e; + e << "The compiler feature \"" << feature << "\" is not known to " << lang + << " compiler\n\"" + << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID") + << "\"\nversion " + << this->Makefile->GetSafeDefinition("CMAKE_" + lang + + "_COMPILER_VERSION") + << "."; + if (error) { + *error = e.str(); + } else { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } + return false; + } + + return true; +} + +bool cmStandardLevelResolver::CompileFeatureKnown( + const std::string& targetName, const std::string& feature, std::string& lang, + std::string* error) const +{ + assert(cmGeneratorExpression::Find(feature) == std::string::npos); + + bool isCFeature = + std::find_if(cm::cbegin(C_FEATURES) + 1, cm::cend(C_FEATURES), + cmStrCmp(feature)) != cm::cend(C_FEATURES); + if (isCFeature) { + lang = "C"; + return true; + } + bool isCxxFeature = + std::find_if(cm::cbegin(CXX_FEATURES) + 1, cm::cend(CXX_FEATURES), + cmStrCmp(feature)) != cm::cend(CXX_FEATURES); + if (isCxxFeature) { + lang = "CXX"; + return true; + } + bool isCudaFeature = + std::find_if(cm::cbegin(CUDA_FEATURES) + 1, cm::cend(CUDA_FEATURES), + cmStrCmp(feature)) != cm::cend(CUDA_FEATURES); + if (isCudaFeature) { + lang = "CUDA"; + return true; + } + std::ostringstream e; + if (error) { + e << "specified"; + } else { + e << "Specified"; + } + e << " unknown feature \"" << feature + << "\" for " + "target \"" + << targetName << "\"."; + if (error) { + *error = e.str(); + } else { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } + return false; +} + +const char* cmStandardLevelResolver::CompileFeaturesAvailable( + const std::string& lang, std::string* error) const +{ + if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) { + std::ostringstream e; + if (error) { + e << "cannot"; + } else { + e << "Cannot"; + } + e << " use features from non-enabled language " << lang; + if (error) { + *error = e.str(); + } else { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } + return nullptr; + } + + cmProp featuresKnown = + this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES"); + + if (!cmNonempty(featuresKnown)) { + std::ostringstream e; + if (error) { + e << "no"; + } else { + e << "No"; + } + e << " known features for " << lang << " compiler\n\"" + << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID") + << "\"\nversion " + << this->Makefile->GetSafeDefinition("CMAKE_" + lang + + "_COMPILER_VERSION") + << "."; + if (error) { + *error = e.str(); + } else { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + } + return nullptr; + } + return cmToCStr(featuresKnown); +} + +bool cmStandardLevelResolver::GetNewRequiredStandard( + const std::string& targetName, const std::string& feature, + cmProp currentLangStandardValue, std::string& newRequiredStandard, + std::string* error) const +{ + std::string lang; + if (!this->CheckCompileFeaturesAvailable(targetName, feature, lang, error)) { + return false; + } + + auto mapping = StandardComputerMapping.find(lang); + if (mapping != cm::cend(StandardComputerMapping)) { + return mapping->second.GetNewRequiredStandard( + this->Makefile, targetName, feature, currentLangStandardValue, + newRequiredStandard, error); + } + return false; +} + +bool cmStandardLevelResolver::HaveStandardAvailable( + cmGeneratorTarget const* target, std::string const& lang, + std::string const& config, const std::string& feature) const +{ + auto mapping = StandardComputerMapping.find(lang); + if (mapping != cm::cend(StandardComputerMapping)) { + return mapping->second.HaveStandardAvailable(this->Makefile, target, + config, feature); + } + return false; +} + +bool cmStandardLevelResolver::IsLaterStandard(std::string const& lang, + std::string const& lhs, + std::string const& rhs) const +{ + auto mapping = StandardComputerMapping.find(lang); + if (mapping != cm::cend(StandardComputerMapping)) { + return mapping->second.IsLaterStandard(std::stoi(lhs), std::stoi(rhs)); + } + return false; +} diff --git a/Source/cmStandardLevelResolver.h b/Source/cmStandardLevelResolver.h new file mode 100644 index 0000000000..d84fbcbe19 --- /dev/null +++ b/Source/cmStandardLevelResolver.h @@ -0,0 +1,57 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <string> + +#include "cmProperty.h" + +class cmMakefile; +class cmGeneratorTarget; +class cmTarget; + +class cmStandardLevelResolver +{ + +public: + explicit cmStandardLevelResolver(cmMakefile* makefile) + : Makefile(makefile) + { + } + + std::string GetCompileOptionDef(cmGeneratorTarget const* target, + std::string const& lang, + std::string const& config) const; + + bool AddRequiredTargetFeature(cmTarget* target, const std::string& feature, + std::string* error = nullptr) const; + + bool CompileFeatureKnown(const std::string& targetName, + const std::string& feature, std::string& lang, + std::string* error) const; + + const char* CompileFeaturesAvailable(const std::string& lang, + std::string* error) const; + + bool GetNewRequiredStandard(const std::string& targetName, + const std::string& feature, + cmProp currentLangStandardValue, + std::string& newRequiredStandard, + std::string* error = nullptr) const; + + bool HaveStandardAvailable(cmGeneratorTarget const* target, + std::string const& lang, + std::string const& config, + const std::string& feature) const; + + bool IsLaterStandard(std::string const& lang, std::string const& lhs, + std::string const& rhs) const; + +private: + bool CheckCompileFeaturesAvailable(const std::string& targetName, + const std::string& feature, + std::string& lang, + std::string* error) const; + + cmMakefile* Makefile; +}; diff --git a/Source/cmStandardLexer.h b/Source/cmStandardLexer.h index e0b21165ef..0203779891 100644 --- a/Source/cmStandardLexer.h +++ b/Source/cmStandardLexer.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmStandardLexer_h -#define cmStandardLexer_h +#pragma once #if defined(__linux) /* Needed for glibc < 2.12 */ @@ -74,5 +73,3 @@ typedef KWIML_INT_int16_t flex_int16_t; typedef KWIML_INT_uint16_t flex_uint16_t; typedef KWIML_INT_int32_t flex_int32_t; typedef KWIML_INT_uint32_t flex_uint32_t; - -#endif diff --git a/Source/cmState.cxx b/Source/cmState.cxx index 0b6b40f4d5..d268e62337 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -135,6 +135,11 @@ bool cmState::DeleteCache(const std::string& path) return this->CacheManager->DeleteCache(path); } +bool cmState::IsCacheLoaded() const +{ + return this->CacheManager->IsCacheLoaded(); +} + std::vector<std::string> cmState::GetCacheEntryKeys() const { return this->CacheManager->GetCacheEntryKeys(); @@ -469,9 +474,10 @@ void cmState::AddUnexpectedCommand(std::string const& name, const char* error) name, [name, error](std::vector<cmListFileArgument> const&, cmExecutionStatus& status) -> bool { - const char* versionValue = + cmProp versionValue = status.GetMakefile().GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); - if (name == "endif" && (!versionValue || atof(versionValue) <= 1.4)) { + if (name == "endif" && + (!versionValue || atof(versionValue->c_str()) <= 1.4)) { return true; } status.SetError(error); @@ -623,8 +629,7 @@ cmProp cmState::GetGlobalProperty(const std::string& prop) bool cmState::GetGlobalPropertyAsBool(const std::string& prop) { - cmProp p = this->GetGlobalProperty(prop); - return p && cmIsOn(*p); + return cmIsOn(this->GetGlobalProperty(prop)); } void cmState::SetSourceDirectory(std::string const& sourceDirectory) @@ -837,6 +842,21 @@ cmStateSnapshot cmState::CreateBuildsystemDirectorySnapshot( return snapshot; } +cmStateSnapshot cmState::CreateDeferCallSnapshot( + cmStateSnapshot const& originSnapshot, std::string const& fileName) +{ + cmStateDetail::PositionType pos = + this->SnapshotData.Push(originSnapshot.Position, *originSnapshot.Position); + pos->SnapshotType = cmStateEnums::DeferCallType; + pos->Keep = false; + pos->ExecutionListFile = this->ExecutionListFiles.Push( + originSnapshot.Position->ExecutionListFile, fileName); + assert(originSnapshot.Position->Vars.IsValid()); + pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->PolicyScope = originSnapshot.Position->Policies; + return { this, pos }; +} + cmStateSnapshot cmState::CreateFunctionCallSnapshot( cmStateSnapshot const& originSnapshot, std::string const& fileName) { diff --git a/Source/cmState.h b/Source/cmState.h index 885496a92a..e4c9eb5249 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -1,15 +1,14 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmState_h -#define cmState_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep #include <functional> -#include <map> #include <memory> #include <set> #include <string> +#include <unordered_map> #include <vector> #include "cmDefinitions.h" @@ -56,6 +55,8 @@ public: cmStateSnapshot CreateBaseSnapshot(); cmStateSnapshot CreateBuildsystemDirectorySnapshot( cmStateSnapshot const& originSnapshot); + cmStateSnapshot CreateDeferCallSnapshot( + cmStateSnapshot const& originSnapshot, std::string const& fileName); cmStateSnapshot CreateFunctionCallSnapshot( cmStateSnapshot const& originSnapshot, std::string const& fileName); cmStateSnapshot CreateMacroCallSnapshot( @@ -86,6 +87,8 @@ public: bool DeleteCache(const std::string& path); + bool IsCacheLoaded() const; + std::vector<std::string> GetCacheEntryKeys() const; cmProp GetCacheEntryValue(std::string const& key) const; std::string GetSafeCacheEntryValue(std::string const& key) const; @@ -220,8 +223,8 @@ private: cmPropertyDefinitionMap PropertyDefinitions; std::vector<std::string> EnabledLanguages; - std::map<std::string, Command> BuiltinCommands; - std::map<std::string, Command> ScriptedCommands; + std::unordered_map<std::string, Command> BuiltinCommands; + std::unordered_map<std::string, Command> ScriptedCommands; cmPropertyMap GlobalProperties; std::unique_ptr<cmCacheManager> CacheManager; std::unique_ptr<cmGlobVerificationManager> GlobVerificationManager; @@ -249,5 +252,3 @@ private: bool NinjaMulti = false; Mode CurrentMode = Unknown; }; - -#endif diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx index a4fe663044..796bb1f754 100644 --- a/Source/cmStateDirectory.cxx +++ b/Source/cmStateDirectory.cxx @@ -648,8 +648,7 @@ cmProp cmStateDirectory::GetProperty(const std::string& prop, bool chain) const bool cmStateDirectory::GetPropertyAsBool(const std::string& prop) const { - cmProp p = this->GetProperty(prop); - return p && cmIsOn(*p); + return cmIsOn(this->GetProperty(prop)); } std::vector<std::string> cmStateDirectory::GetPropertyKeys() const diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h index 765af6f4e3..56a262d87b 100644 --- a/Source/cmStateDirectory.h +++ b/Source/cmStateDirectory.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmStateDirectory_h -#define cmStateDirectory_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -104,5 +103,3 @@ private: cmStateSnapshot Snapshot_; friend class cmStateSnapshot; }; - -#endif diff --git a/Source/cmStatePrivate.h b/Source/cmStatePrivate.h index 4efaf97ebe..489264468d 100644 --- a/Source/cmStatePrivate.h +++ b/Source/cmStatePrivate.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmStatePrivate_h -#define cmStatePrivate_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -99,5 +98,3 @@ struct cmStateDetail::BuildsystemDirectoryStateType std::vector<cmStateSnapshot> Children; }; - -#endif diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index c223431854..1e20abb257 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx @@ -53,7 +53,7 @@ void cmStateSnapshot::SetListFile(const std::string& listfile) *this->Position->ExecutionListFile = listfile; } -std::string cmStateSnapshot::GetExecutionListFile() const +std::string const& cmStateSnapshot::GetExecutionListFile() const { return *this->Position->ExecutionListFile; } @@ -148,7 +148,7 @@ bool cmStateSnapshot::PopPolicy() bool cmStateSnapshot::CanPopPolicyScope() { - return this->Position->Policies == this->Position->PolicyScope; + return this->Position->Policies != this->Position->PolicyScope; } void cmStateSnapshot::SetPolicy(cmPolicies::PolicyID id, @@ -232,11 +232,6 @@ void cmStateSnapshot::RemoveDefinition(std::string const& name) this->Position->Vars->Unset(name); } -std::vector<std::string> cmStateSnapshot::UnusedKeys() const -{ - return this->Position->Vars->UnusedKeys(); -} - std::vector<std::string> cmStateSnapshot::ClosureKeys() const { return cmDefinitions::ClosureKeys(this->Position->Vars, @@ -328,7 +323,7 @@ void cmStateSnapshot::SetDefaultDefinitions() #if defined(__CYGWIN__) std::string legacy; if (cmSystemTools::GetEnv("CMAKE_LEGACY_CYGWIN_WIN32", legacy) && - cmIsOn(legacy.c_str())) { + cmIsOn(legacy)) { this->SetDefinition("WIN32", "1"); this->SetDefinition("CMAKE_HOST_WIN32", "1"); } @@ -416,8 +411,7 @@ void cmStateSnapshot::InitializeFromParent() parent->BuildSystemDirectory->Properties.GetPropertyValue( "INCLUDE_REGULAR_EXPRESSION"); this->Position->BuildSystemDirectory->Properties.SetProperty( - "INCLUDE_REGULAR_EXPRESSION", - include_regex ? include_regex->c_str() : nullptr); + "INCLUDE_REGULAR_EXPRESSION", cmToCStr(include_regex)); } cmState* cmStateSnapshot::GetState() const diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h index 021fd53c13..d06cba37a8 100644 --- a/Source/cmStateSnapshot.h +++ b/Source/cmStateSnapshot.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmStateSnapshot_h -#define cmStateSnapshot_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -28,13 +27,12 @@ public: bool IsInitialized(std::string const& name) const; void SetDefinition(std::string const& name, cm::string_view value); void RemoveDefinition(std::string const& name); - std::vector<std::string> UnusedKeys() const; std::vector<std::string> ClosureKeys() const; bool RaiseScope(std::string const& var, const char* varDef); void SetListFile(std::string const& listfile); - std::string GetExecutionListFile() const; + std::string const& GetExecutionListFile() const; std::vector<cmStateSnapshot> GetChildren(); @@ -88,5 +86,3 @@ private: bool operator==(const cmStateSnapshot& lhs, const cmStateSnapshot& rhs); bool operator!=(const cmStateSnapshot& lhs, const cmStateSnapshot& rhs); - -#endif diff --git a/Source/cmStateTypes.h b/Source/cmStateTypes.h index d089ea73d5..010d7e3e91 100644 --- a/Source/cmStateTypes.h +++ b/Source/cmStateTypes.h @@ -1,8 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmStateTypes_h -#define cmStateTypes_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -19,6 +18,7 @@ enum SnapshotType { BaseType, BuildsystemDirectoryType, + DeferCallType, FunctionCallType, MacroCallType, IncludeFileType, @@ -60,5 +60,3 @@ enum ArtifactType ImportLibraryArtifact }; } - -#endif diff --git a/Source/cmString.cxx b/Source/cmString.cxx index 2a0c125e63..898b8286e5 100644 --- a/Source/cmString.cxx +++ b/Source/cmString.cxx @@ -17,7 +17,7 @@ static std::string const empty_string_; void String::internally_mutate_to_stable_string() { // We assume that only one thread mutates this instance at - // a time even if we point to a shared string buffer refernced + // a time even if we point to a shared string buffer referenced // by other threads. *this = String(data(), size()); } diff --git a/Source/cmString.hxx b/Source/cmString.hxx index 87bfdffcfd..b41b9605b8 100644 --- a/Source/cmString.hxx +++ b/Source/cmString.hxx @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmString_hxx -#define cmString_hxx +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -383,7 +382,7 @@ public: instance is mutated or destroyed. */ std::string const* str_if_stable() const; - /** Get a refernce to a normal std::string. The reference + /** Get a reference to a normal std::string. The reference is valid until this instance is mutated or destroyed. */ std::string const& str(); @@ -928,5 +927,3 @@ struct hash<cm::String> } }; } - -#endif diff --git a/Source/cmStringAlgorithms.cxx b/Source/cmStringAlgorithms.cxx index 71d28a49df..e0af28185a 100644 --- a/Source/cmStringAlgorithms.cxx +++ b/Source/cmStringAlgorithms.cxx @@ -7,6 +7,7 @@ #include <cstddef> // IWYU pragma: keep #include <cstdio> #include <cstdlib> +#include <iterator> std::string cmTrimWhitespace(cm::string_view str) { @@ -323,3 +324,52 @@ bool cmStrToULong(std::string const& str, unsigned long* value) { return cmStrToULong(str.c_str(), value); } + +template <typename Range> +std::size_t getJoinedLength(Range const& rng, cm::string_view separator) +{ + std::size_t rangeLength{}; + for (auto const& item : rng) { + rangeLength += item.size(); + } + + auto const separatorsLength = (rng.size() - 1) * separator.size(); + + return rangeLength + separatorsLength; +} + +template <typename Range> +std::string cmJoinImpl(Range const& rng, cm::string_view separator, + cm::string_view initial) +{ + if (rng.empty()) { + return { std::begin(initial), std::end(initial) }; + } + + std::string result; + result.reserve(initial.size() + getJoinedLength(rng, separator)); + result.append(std::begin(initial), std::end(initial)); + + auto begin = std::begin(rng); + auto end = std::end(rng); + result += *begin; + + for (++begin; begin != end; ++begin) { + result.append(std::begin(separator), std::end(separator)); + result += *begin; + } + + return result; +} + +std::string cmJoin(std::vector<std::string> const& rng, + cm::string_view separator, cm::string_view initial) +{ + return cmJoinImpl(rng, separator, initial); +} + +std::string cmJoin(cmStringRange const& rng, cm::string_view separator, + cm::string_view initial) +{ + return cmJoinImpl(rng, separator, initial); +} diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h index a5ecca758d..01e3d940ec 100644 --- a/Source/cmStringAlgorithms.h +++ b/Source/cmStringAlgorithms.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmStringAlgorithms_h -#define cmStringAlgorithms_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -20,6 +19,27 @@ /** String range type. */ using cmStringRange = cmRange<std::vector<std::string>::const_iterator>; +/** Check for non-empty string. */ +inline bool cmNonempty(const char* str) +{ + return str && *str; +} +inline bool cmNonempty(cm::string_view str) +{ + return !str.empty(); +} +inline bool cmNonempty(std::string const* str) +{ + return str && !str->empty(); +} + +/** Returns length of a literal string. */ +template <size_t N> +constexpr size_t cmStrLen(const char (&/*str*/)[N]) +{ + return N - 1; +} + /** Callable string comparison struct. */ struct cmStrCmp { @@ -67,6 +87,17 @@ std::string cmJoin(Range const& rng, cm::string_view separator) return os.str(); } +/** + * Faster overloads for std::string ranges. + * If @a initial is provided, it prepends the resulted string without + * @a separator between them. + */ +std::string cmJoin(std::vector<std::string> const& rng, + cm::string_view separator, cm::string_view initial = {}); + +std::string cmJoin(cmStringRange const& rng, cm::string_view separator, + cm::string_view initial = {}); + /** Extract tokens that are separated by any of the characters in @a sep. */ std::vector<std::string> cmTokenize(cm::string_view str, cm::string_view sep); @@ -205,10 +236,11 @@ bool cmIsNOTFOUND(cm::string_view val); bool cmIsOn(cm::string_view val); inline bool cmIsOn(const char* val) { - if (!val) { - return false; - } - return cmIsOn(cm::string_view(val)); + return val && cmIsOn(cm::string_view(val)); +} +inline bool cmIsOn(std::string const* val) +{ + return val && cmIsOn(*val); } /** @@ -221,10 +253,11 @@ inline bool cmIsOn(const char* val) bool cmIsOff(cm::string_view val); inline bool cmIsOff(const char* val) { - if (!val) { - return true; - } - return cmIsOff(cm::string_view(val)); + return !val || cmIsOff(cm::string_view(val)); +} +inline bool cmIsOff(std::string const* val) +{ + return !val || cmIsOff(*val); } /** Returns true if string @a str starts with the character @a prefix. */ @@ -290,5 +323,3 @@ bool cmStrToLong(std::string const& str, long* value); * integer */ bool cmStrToULong(const char* str, unsigned long* value); bool cmStrToULong(std::string const& str, unsigned long* value); - -#endif diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index a7c21ccf01..b28fca954c 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx @@ -8,11 +8,21 @@ #include <cctype> #include <cstdio> #include <cstdlib> +#include <initializer_list> +#include <limits> #include <memory> +#include <stdexcept> +#include <utility> #include <cm/iterator> +#include <cm/optional> +#include <cm/string_view> #include <cmext/string_view> +#include <cm3p/json/reader.h> +#include <cm3p/json/value.h> +#include <cm3p/json/writer.h> + #include "cmsys/RegularExpression.hxx" #include "cmCryptoHash.h" @@ -20,6 +30,7 @@ #include "cmGeneratorExpression.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmRange.h" #include "cmStringAlgorithms.h" #include "cmStringReplaceHelper.h" @@ -533,15 +544,14 @@ bool HandleAppendCommand(std::vector<std::string> const& args, return true; } - const std::string& variable = args[1]; + auto const& variableName = args[1]; + + cm::string_view oldView{ status.GetMakefile().GetSafeDefinition( + variableName) }; + + auto const newValue = cmJoin(cmMakeRange(args).advance(2), {}, oldView); + status.GetMakefile().AddDefinition(variableName, newValue); - std::string value; - const char* oldValue = status.GetMakefile().GetDefinition(variable); - if (oldValue) { - value = oldValue; - } - value += cmJoin(cmMakeRange(args).advance(2), std::string()); - status.GetMakefile().AddDefinition(variable, value); return true; } @@ -561,9 +571,9 @@ bool HandlePrependCommand(std::vector<std::string> const& args, const std::string& variable = args[1]; std::string value = cmJoin(cmMakeRange(args).advance(2), std::string()); - const char* oldValue = status.GetMakefile().GetDefinition(variable); + cmProp oldValue = status.GetMakefile().GetDefinition(variable); if (oldValue) { - value += oldValue; + value += *oldValue; } status.GetMakefile().AddDefinition(variable, value); return true; @@ -929,6 +939,296 @@ bool HandleUuidCommand(std::vector<std::string> const& args, #endif } +#if !defined(CMAKE_BOOTSTRAP) + +// Helpers for string(JSON ...) +struct Args : cmRange<typename std::vector<std::string>::const_iterator> +{ + using cmRange<typename std::vector<std::string>::const_iterator>::cmRange; + + auto PopFront(cm::string_view error) -> const std::string&; + auto PopBack(cm::string_view error) -> const std::string&; +}; + +class json_error : public std::runtime_error +{ +public: + json_error(std::initializer_list<cm::string_view> message, + cm::optional<Args> errorPath = cm::nullopt) + : std::runtime_error(cmCatViews(message)) + , ErrorPath{ + std::move(errorPath) // NOLINT(performance-move-const-arg) + } + { + } + cm::optional<Args> ErrorPath; +}; + +const std::string& Args::PopFront(cm::string_view error) +{ + if (empty()) { + throw json_error({ error }); + } + const std::string& res = *begin(); + advance(1); + return res; +} + +const std::string& Args::PopBack(cm::string_view error) +{ + if (empty()) { + throw json_error({ error }); + } + const std::string& res = *(end() - 1); + retreat(1); + return res; +} + +cm::string_view JsonTypeToString(Json::ValueType type) +{ + switch (type) { + case Json::ValueType::nullValue: + return "NULL"_s; + case Json::ValueType::intValue: + case Json::ValueType::uintValue: + case Json::ValueType::realValue: + return "NUMBER"_s; + case Json::ValueType::stringValue: + return "STRING"_s; + case Json::ValueType::booleanValue: + return "BOOLEAN"_s; + case Json::ValueType::arrayValue: + return "ARRAY"_s; + case Json::ValueType::objectValue: + return "OBJECT"_s; + } + throw json_error({ "invalid JSON type found"_s }); +} + +int ParseIndex( + const std::string& str, cm::optional<Args> const& progress = cm::nullopt, + Json::ArrayIndex max = std::numeric_limits<Json::ArrayIndex>::max()) +{ + unsigned long lindex; + if (!cmStrToULong(str, &lindex)) { + throw json_error({ "expected an array index, got: '"_s, str, "'"_s }, + progress); + } + Json::ArrayIndex index = static_cast<Json::ArrayIndex>(lindex); + if (index >= max) { + cmAlphaNum sizeStr{ max }; + throw json_error({ "expected an index less then "_s, sizeStr.View(), + " got '"_s, str, "'"_s }, + progress); + } + return index; +} + +Json::Value& ResolvePath(Json::Value& json, Args path) +{ + Json::Value* search = &json; + + for (auto curr = path.begin(); curr != path.end(); ++curr) { + const std::string& field = *curr; + Args progress{ path.begin(), curr + 1 }; + + if (search->isArray()) { + auto index = ParseIndex(field, progress, search->size()); + search = &(*search)[index]; + + } else if (search->isObject()) { + if (!search->isMember(field)) { + const auto progressStr = cmJoin(progress, " "_s); + throw json_error({ "member '"_s, progressStr, "' not found"_s }, + progress); + } + search = &(*search)[field]; + } else { + const auto progressStr = cmJoin(progress, " "_s); + throw json_error( + { "invalid path '"_s, progressStr, + "', need element of OBJECT or ARRAY type to lookup '"_s, field, + "' got "_s, JsonTypeToString(search->type()) }, + progress); + } + } + return *search; +}; + +Json::Value ReadJson(const std::string& jsonstr) +{ + Json::CharReaderBuilder builder; + builder["collectComments"] = false; + auto jsonReader = std::unique_ptr<Json::CharReader>(builder.newCharReader()); + Json::Value json; + std::string error; + if (!jsonReader->parse(jsonstr.data(), jsonstr.data() + jsonstr.size(), + &json, &error)) { + throw json_error({ "failed parsing json string: "_s, error }); + } + return json; +} +std::string WriteJson(const Json::Value& value) +{ + Json::StreamWriterBuilder writer; + writer["indentation"] = " "; + writer["commentStyle"] = "None"; + return Json::writeString(writer, value); +} + +#endif + +bool HandleJSONCommand(std::vector<std::string> const& arguments, + cmExecutionStatus& status) +{ +#if !defined(CMAKE_BOOTSTRAP) + + auto& makefile = status.GetMakefile(); + Args args{ arguments.begin() + 1, arguments.end() }; + + const std::string* errorVariable = nullptr; + const std::string* outputVariable = nullptr; + bool success = true; + + try { + outputVariable = &args.PopFront("missing out-var argument"_s); + + if (!args.empty() && *args.begin() == "ERROR_VARIABLE"_s) { + args.PopFront(""); + errorVariable = &args.PopFront("missing error-var argument"_s); + makefile.AddDefinition(*errorVariable, "NOTFOUND"_s); + } + + const auto& mode = args.PopFront("missing mode argument"_s); + if (mode != "GET"_s && mode != "TYPE"_s && mode != "MEMBER"_s && + mode != "LENGTH"_s && mode != "REMOVE"_s && mode != "SET"_s && + mode != "EQUAL"_s) { + throw json_error( + { "got an invalid mode '"_s, mode, + "', expected one of GET, GET_ARRAY, TYPE, MEMBER, MEMBERS," + " LENGTH, REMOVE, SET, EQUAL"_s }); + } + + const auto& jsonstr = args.PopFront("missing json string argument"_s); + Json::Value json = ReadJson(jsonstr); + + if (mode == "GET"_s) { + const auto& value = ResolvePath(json, args); + if (value.isObject() || value.isArray()) { + makefile.AddDefinition(*outputVariable, WriteJson(value)); + } else if (value.isBool()) { + makefile.AddDefinitionBool(*outputVariable, value.asBool()); + } else { + makefile.AddDefinition(*outputVariable, value.asString()); + } + + } else if (mode == "TYPE"_s) { + const auto& value = ResolvePath(json, args); + makefile.AddDefinition(*outputVariable, JsonTypeToString(value.type())); + + } else if (mode == "MEMBER"_s) { + const auto& indexStr = args.PopBack("missing member index"_s); + const auto& value = ResolvePath(json, args); + if (!value.isObject()) { + throw json_error({ "MEMBER needs to be called with an element of " + "type OBJECT, got "_s, + JsonTypeToString(value.type()) }, + args); + } + const auto index = ParseIndex( + indexStr, Args{ args.begin(), args.end() + 1 }, value.size()); + const auto memIt = std::next(value.begin(), index); + makefile.AddDefinition(*outputVariable, memIt.name()); + + } else if (mode == "LENGTH"_s) { + const auto& value = ResolvePath(json, args); + if (!value.isArray() && !value.isObject()) { + throw json_error({ "LENGTH needs to be called with an " + "element of type ARRAY or OBJECT, got "_s, + JsonTypeToString(value.type()) }, + args); + } + + cmAlphaNum sizeStr{ value.size() }; + makefile.AddDefinition(*outputVariable, sizeStr.View()); + + } else if (mode == "REMOVE"_s) { + const auto& toRemove = + args.PopBack("missing member or index to remove"_s); + auto& value = ResolvePath(json, args); + + if (value.isArray()) { + const auto index = ParseIndex( + toRemove, Args{ args.begin(), args.end() + 1 }, value.size()); + Json::Value removed; + value.removeIndex(index, &removed); + + } else if (value.isObject()) { + Json::Value removed; + value.removeMember(toRemove, &removed); + + } else { + throw json_error({ "REMOVE needs to be called with an " + "element of type ARRAY or OBJECT, got "_s, + JsonTypeToString(value.type()) }, + args); + } + makefile.AddDefinition(*outputVariable, WriteJson(json)); + + } else if (mode == "SET"_s) { + const auto& newValueStr = args.PopBack("missing new value remove"_s); + const auto& toAdd = args.PopBack("missing member name to add"_s); + auto& value = ResolvePath(json, args); + + Json::Value newValue = ReadJson(newValueStr); + if (value.isObject()) { + value[toAdd] = newValue; + } else if (value.isArray()) { + const auto index = + ParseIndex(toAdd, Args{ args.begin(), args.end() + 1 }); + if (value.isValidIndex(index)) { + value[static_cast<int>(index)] = newValue; + } else { + value.append(newValue); + } + } else { + throw json_error({ "SET needs to be called with an " + "element of type OBJECT or ARRAY, got "_s, + JsonTypeToString(value.type()) }); + } + + makefile.AddDefinition(*outputVariable, WriteJson(json)); + + } else if (mode == "EQUAL"_s) { + const auto& jsonstr2 = + args.PopFront("missing second json string argument"_s); + Json::Value json2 = ReadJson(jsonstr2); + makefile.AddDefinitionBool(*outputVariable, json == json2); + } + + } catch (const json_error& e) { + if (outputVariable && e.ErrorPath) { + const auto errorPath = cmJoin(*e.ErrorPath, "-"); + makefile.AddDefinition(*outputVariable, + cmCatViews({ errorPath, "-NOTFOUND"_s })); + } else if (outputVariable) { + makefile.AddDefinition(*outputVariable, "NOTFOUND"_s); + } + + if (errorVariable) { + makefile.AddDefinition(*errorVariable, e.what()); + } else { + status.SetError(cmCatViews({ "sub-command JSON "_s, e.what(), "."_s })); + success = false; + } + } + return success; +#else + status.SetError(cmStrCat(arguments[0], " not available during bootstrap"_s)); + return false; +#endif +} + } // namespace bool cmStringCommand(std::vector<std::string> const& args, @@ -972,6 +1272,7 @@ bool cmStringCommand(std::vector<std::string> const& args, { "MAKE_C_IDENTIFIER"_s, HandleMakeCIdentifierCommand }, { "GENEX_STRIP"_s, HandleGenexStripCommand }, { "UUID"_s, HandleUuidCommand }, + { "JSON"_s, HandleJSONCommand }, }; return subcommand(args[0], args, status); diff --git a/Source/cmStringCommand.h b/Source/cmStringCommand.h index bd71ba2930..5320ea5975 100644 --- a/Source/cmStringCommand.h +++ b/Source/cmStringCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmStringCommand_h -#define cmStringCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -16,5 +15,3 @@ class cmExecutionStatus; */ bool cmStringCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmStringReplaceHelper.h b/Source/cmStringReplaceHelper.h index 74d481d70d..fd59d17243 100644 --- a/Source/cmStringReplaceHelper.h +++ b/Source/cmStringReplaceHelper.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmStringReplaceHelper_h -#define cmStringReplaceHelper_h +#pragma once #include <string> #include <utility> @@ -64,5 +63,3 @@ private: std::vector<RegexReplacement> Replacements; cmMakefile* Makefile = nullptr; }; - -#endif diff --git a/Source/cmSubcommandTable.h b/Source/cmSubcommandTable.h index 7deaaed697..80d8c6df45 100644 --- a/Source/cmSubcommandTable.h +++ b/Source/cmSubcommandTable.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSubcommandTable_h -#define cmSubcommandTable_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -32,5 +31,3 @@ public: private: std::vector<Elem> Impl; }; - -#endif diff --git a/Source/cmSubdirCommand.h b/Source/cmSubdirCommand.h index 3254e842ee..6770874cf6 100644 --- a/Source/cmSubdirCommand.h +++ b/Source/cmSubdirCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSubdirCommand_h -#define cmSubdirCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmSubdirCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmSubdirDependsCommand.h b/Source/cmSubdirDependsCommand.h index bf99bd17d8..133d702808 100644 --- a/Source/cmSubdirDependsCommand.h +++ b/Source/cmSubdirDependsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSubdirDependsCommand_h -#define cmSubdirDependsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmSubdirDependsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 1e78d36938..6a705f480d 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -5,7 +5,8 @@ // POSIX APIs are needed # define _POSIX_C_SOURCE 200809L #endif -#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) +#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__QNX__) // For isascii # define _XOPEN_SOURCE 700 #endif @@ -749,33 +750,140 @@ std::string cmSystemTools::FileExistsInParentDirectories( } #ifdef _WIN32 -cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry() +namespace { + +/* Helper class to save and restore the specified file (or directory) + attribute bits. Instantiate this class as an automatic variable on the + stack. Its constructor saves a copy of the file attributes, and then its + destructor restores the original attribute settings. */ +class SaveRestoreFileAttributes { - static WindowsFileRetry retry = { 0, 0 }; - if (!retry.Count) { - unsigned int data[2] = { 0, 0 }; - HKEY const keys[2] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }; - wchar_t const* const values[2] = { L"FilesystemRetryCount", - L"FilesystemRetryDelay" }; - for (int k = 0; k < 2; ++k) { - HKEY hKey; - if (RegOpenKeyExW(keys[k], L"Software\\Kitware\\CMake\\Config", 0, - KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) { - for (int v = 0; v < 2; ++v) { - DWORD dwData, dwType, dwSize = 4; - if (!data[v] && - RegQueryValueExW(hKey, values[v], 0, &dwType, (BYTE*)&dwData, - &dwSize) == ERROR_SUCCESS && - dwType == REG_DWORD && dwSize == 4) { - data[v] = static_cast<unsigned int>(dwData); - } +public: + SaveRestoreFileAttributes(std::wstring const& path, + uint32_t file_attrs_to_set); + ~SaveRestoreFileAttributes(); + + SaveRestoreFileAttributes(SaveRestoreFileAttributes const&) = delete; + SaveRestoreFileAttributes& operator=(SaveRestoreFileAttributes const&) = + delete; + + void SetPath(std::wstring const& path) { path_ = path; } + +private: + std::wstring path_; + uint32_t original_attr_bits_; +}; + +SaveRestoreFileAttributes::SaveRestoreFileAttributes( + std::wstring const& path, uint32_t file_attrs_to_set) + : path_(path) + , original_attr_bits_(0) +{ + // Set the specified attributes for the source file/directory. + original_attr_bits_ = GetFileAttributesW(path_.c_str()); + if ((INVALID_FILE_ATTRIBUTES != original_attr_bits_) && + ((file_attrs_to_set & original_attr_bits_) != file_attrs_to_set)) { + SetFileAttributesW(path_.c_str(), original_attr_bits_ | file_attrs_to_set); + } +} + +// We set attribute bits. Now we need to restore their original state. +SaveRestoreFileAttributes::~SaveRestoreFileAttributes() +{ + DWORD last_error = GetLastError(); + // Verify or restore the original attributes. + const DWORD source_attr_bits = GetFileAttributesW(path_.c_str()); + if (INVALID_FILE_ATTRIBUTES != source_attr_bits) { + if (original_attr_bits_ != source_attr_bits) { + // The file still exists, and its attributes aren't our saved values. + // Time to restore them. + SetFileAttributesW(path_.c_str(), original_attr_bits_); + } + } + SetLastError(last_error); +} + +struct WindowsFileRetryInit +{ + cmSystemTools::WindowsFileRetry Retry; + bool Explicit; +}; + +WindowsFileRetryInit InitWindowsFileRetry(wchar_t const* const values[2], + unsigned int const defaults[2]) +{ + unsigned int data[2] = { 0, 0 }; + HKEY const keys[2] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }; + for (int k = 0; k < 2; ++k) { + HKEY hKey; + if (RegOpenKeyExW(keys[k], L"Software\\Kitware\\CMake\\Config", 0, + KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) { + for (int v = 0; v < 2; ++v) { + DWORD dwData, dwType, dwSize = 4; + if (!data[v] && + RegQueryValueExW(hKey, values[v], 0, &dwType, (BYTE*)&dwData, + &dwSize) == ERROR_SUCCESS && + dwType == REG_DWORD && dwSize == 4) { + data[v] = static_cast<unsigned int>(dwData); } - RegCloseKey(hKey); } + RegCloseKey(hKey); } - retry.Count = data[0] ? data[0] : 5; - retry.Delay = data[1] ? data[1] : 500; } + WindowsFileRetryInit init; + init.Explicit = data[0] || data[1]; + init.Retry.Count = data[0] ? data[0] : defaults[0]; + init.Retry.Delay = data[1] ? data[1] : defaults[1]; + return init; +} + +WindowsFileRetryInit InitWindowsFileRetry() +{ + static wchar_t const* const values[2] = { L"FilesystemRetryCount", + L"FilesystemRetryDelay" }; + static unsigned int const defaults[2] = { 5, 500 }; + return InitWindowsFileRetry(values, defaults); +} + +WindowsFileRetryInit InitWindowsDirectoryRetry() +{ + static wchar_t const* const values[2] = { L"FilesystemDirectoryRetryCount", + L"FilesystemDirectoryRetryDelay" }; + static unsigned int const defaults[2] = { 120, 500 }; + WindowsFileRetryInit dirInit = InitWindowsFileRetry(values, defaults); + if (dirInit.Explicit) { + return dirInit; + } + WindowsFileRetryInit fileInit = InitWindowsFileRetry(); + if (fileInit.Explicit) { + return fileInit; + } + return dirInit; +} + +cmSystemTools::WindowsFileRetry GetWindowsRetry(std::wstring const& path) +{ + // If we are performing a directory operation, then try and get the + // appropriate timing info. + DWORD const attrs = GetFileAttributesW(path.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) { + return cmSystemTools::GetWindowsDirectoryRetry(); + } + return cmSystemTools::GetWindowsFileRetry(); +} + +} // end of anonymous namespace + +cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry() +{ + static WindowsFileRetry retry = InitWindowsFileRetry().Retry; + return retry; +} + +cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsDirectoryRetry() +{ + static cmSystemTools::WindowsFileRetry retry = + InitWindowsDirectoryRetry().Retry; return retry; } #endif @@ -836,6 +944,20 @@ void cmSystemTools::InitializeLibUV() #endif } +#ifdef _WIN32 +namespace { +bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname) +{ + // Not only ignore any previous error, but clear any memory of it. + SetLastError(0); + + // Use MOVEFILE_REPLACE_EXISTING to replace an existing destination file. + return MoveFileExW(oldname.c_str(), newname.c_str(), + MOVEFILE_REPLACE_EXISTING); +} +} +#endif + bool cmSystemTools::RenameFile(const std::string& oldname, const std::string& newname) { @@ -843,35 +965,63 @@ bool cmSystemTools::RenameFile(const std::string& oldname, # ifndef INVALID_FILE_ATTRIBUTES # define INVALID_FILE_ATTRIBUTES ((DWORD)-1) # endif + std::wstring const oldname_wstr = + SystemTools::ConvertToWindowsExtendedPath(oldname); + std::wstring const newname_wstr = + SystemTools::ConvertToWindowsExtendedPath(newname); + /* Windows MoveFileEx may not replace read-only or in-use files. If it fails then remove the read-only attribute from any existing destination. Try multiple times since we may be racing against another process creating/opening the destination file just before our MoveFileEx. */ - WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry(); - while ( - !MoveFileExW(SystemTools::ConvertToWindowsExtendedPath(oldname).c_str(), - SystemTools::ConvertToWindowsExtendedPath(newname).c_str(), - MOVEFILE_REPLACE_EXISTING) && - --retry.Count) { - DWORD last_error = GetLastError(); + WindowsFileRetry retry = GetWindowsRetry(oldname_wstr); + + // Use RAII to set the attribute bit blocking Microsoft Search Indexing, + // and restore the previous value upon return. + SaveRestoreFileAttributes save_restore_file_attributes( + oldname_wstr, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); + + DWORD move_last_error = 0; + while (!cmMoveFile(oldname_wstr, newname_wstr) && --retry.Count) { + move_last_error = GetLastError(); + + // There was no error ==> the operation is not yet complete. + if (move_last_error == NO_ERROR) { + break; + } + // Try again only if failure was due to access/sharing permissions. - if (last_error != ERROR_ACCESS_DENIED && - last_error != ERROR_SHARING_VIOLATION) { + // Most often ERROR_ACCESS_DENIED (a.k.a. I/O error) for a directory, and + // ERROR_SHARING_VIOLATION for a file, are caused by one of the following: + // 1) Anti-Virus Software + // 2) Windows Search Indexer + // 3) Windows Explorer has an associated directory already opened. + if (move_last_error != ERROR_ACCESS_DENIED && + move_last_error != ERROR_SHARING_VIOLATION) { return false; } - DWORD attrs = GetFileAttributesW( - SystemTools::ConvertToWindowsExtendedPath(newname).c_str()); + + DWORD const attrs = GetFileAttributesW(newname_wstr.c_str()); if ((attrs != INVALID_FILE_ATTRIBUTES) && - (attrs & FILE_ATTRIBUTE_READONLY)) { + (attrs & FILE_ATTRIBUTE_READONLY) && + // FILE_ATTRIBUTE_READONLY is not honored on directories. + !(attrs & FILE_ATTRIBUTE_DIRECTORY)) { // Remove the read-only attribute from the destination file. - SetFileAttributesW( - SystemTools::ConvertToWindowsExtendedPath(newname).c_str(), - attrs & ~FILE_ATTRIBUTE_READONLY); + SetFileAttributesW(newname_wstr.c_str(), + attrs & ~FILE_ATTRIBUTE_READONLY); } else { // The file may be temporarily in use so wait a bit. cmSystemTools::Delay(retry.Delay); } } + + // If we were successful, then there was no error. + if (retry.Count > 0) { + move_last_error = 0; + // Restore the attributes on the new name. + save_restore_file_attributes.SetPath(newname_wstr); + } + SetLastError(move_last_error); return retry.Count > 0; #else /* On UNIX we have an OS-provided call to do this atomically. */ @@ -1213,7 +1363,7 @@ bool cmSystemTools::UnsetEnv(const char* value) { # if !defined(HAVE_UNSETENV) std::string var = cmStrCat(value, '='); - return cmSystemTools::PutEnv(var.c_str()); + return cmSystemTools::PutEnv(var); # else unsetenv(value); return true; @@ -1292,7 +1442,7 @@ bool cmSystemTools::CreateTar(const std::string& outFileName, const std::vector<std::string>& files, cmTarCompression compressType, bool verbose, std::string const& mtime, - std::string const& format) + std::string const& format, int compressionLevel) { #if !defined(CMAKE_BOOTSTRAP) std::string cwd = cmSystemTools::GetCurrentWorkingDirectory(); @@ -1322,7 +1472,8 @@ bool cmSystemTools::CreateTar(const std::string& outFileName, break; } - cmArchiveWrite a(fout, compress, format.empty() ? "paxr" : format); + cmArchiveWrite a(fout, compress, format.empty() ? "paxr" : format, + compressionLevel); a.Open(); a.SetMTime(mtime); @@ -1925,6 +2076,7 @@ static std::string cmSystemToolsCMakeCursesCommand; static std::string cmSystemToolsCMakeGUICommand; static std::string cmSystemToolsCMClDepsCommand; static std::string cmSystemToolsCMakeRoot; +static std::string cmSystemToolsHTMLDoc; void cmSystemTools::FindCMakeResources(const char* argv0) { std::string exe_dir; @@ -2014,18 +2166,23 @@ void cmSystemTools::FindCMakeResources(const char* argv0) // Install tree has // - "<prefix><CMAKE_BIN_DIR>/cmake" // - "<prefix><CMAKE_DATA_DIR>" - if (cmHasSuffix(exe_dir, CMAKE_BIN_DIR)) { + // - "<prefix><CMAKE_DOC_DIR>" + if (cmHasLiteralSuffix(exe_dir, CMAKE_BIN_DIR)) { std::string const prefix = - exe_dir.substr(0, exe_dir.size() - strlen(CMAKE_BIN_DIR)); - cmSystemToolsCMakeRoot = prefix + CMAKE_DATA_DIR; + exe_dir.substr(0, exe_dir.size() - cmStrLen(CMAKE_BIN_DIR)); + cmSystemToolsCMakeRoot = cmStrCat(prefix, CMAKE_DATA_DIR); + if (cmSystemTools::FileExists( + cmStrCat(prefix, CMAKE_DOC_DIR "/html/index.html"))) { + cmSystemToolsHTMLDoc = cmStrCat(prefix, CMAKE_DOC_DIR "/html"); + } } if (cmSystemToolsCMakeRoot.empty() || !cmSystemTools::FileExists( - (cmSystemToolsCMakeRoot + "/Modules/CMake.cmake"))) { + cmStrCat(cmSystemToolsCMakeRoot, "/Modules/CMake.cmake"))) { // Build tree has "<build>/bin[/<config>]/cmake" and // "<build>/CMakeFiles/CMakeSourceDir.txt". std::string dir = cmSystemTools::GetFilenamePath(exe_dir); - std::string src_dir_txt = dir + "/CMakeFiles/CMakeSourceDir.txt"; + std::string src_dir_txt = cmStrCat(dir, "/CMakeFiles/CMakeSourceDir.txt"); cmsys::ifstream fin(src_dir_txt.c_str()); std::string src_dir; if (fin && cmSystemTools::GetLineFromStream(fin, src_dir) && @@ -2033,13 +2190,18 @@ void cmSystemTools::FindCMakeResources(const char* argv0) cmSystemToolsCMakeRoot = src_dir; } else { dir = cmSystemTools::GetFilenamePath(dir); - src_dir_txt = dir + "/CMakeFiles/CMakeSourceDir.txt"; + src_dir_txt = cmStrCat(dir, "/CMakeFiles/CMakeSourceDir.txt"); cmsys::ifstream fin2(src_dir_txt.c_str()); if (fin2 && cmSystemTools::GetLineFromStream(fin2, src_dir) && cmSystemTools::FileIsDirectory(src_dir)) { cmSystemToolsCMakeRoot = src_dir; } } + if (!cmSystemToolsCMakeRoot.empty() && cmSystemToolsHTMLDoc.empty() && + cmSystemTools::FileExists( + cmStrCat(dir, "/Utilities/Sphinx/html/index.html"))) { + cmSystemToolsHTMLDoc = cmStrCat(dir, "/Utilities/Sphinx/html"); + } } #else // Bootstrap build knows its source. @@ -2082,6 +2244,11 @@ std::string const& cmSystemTools::GetCMakeRoot() return cmSystemToolsCMakeRoot; } +std::string const& cmSystemTools::GetHTMLDoc() +{ + return cmSystemToolsHTMLDoc; +} + std::string cmSystemTools::GetCurrentWorkingDirectory() { return cmSystemTools::CollapseFullPath( diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index b886c58ddd..1100f05bda 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmSystemTools_h -#define cmSystemTools_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -363,7 +362,8 @@ public: const std::vector<std::string>& files, cmTarCompression compressType, bool verbose, std::string const& mtime = std::string(), - std::string const& format = std::string()); + std::string const& format = std::string(), + int compressionLevel = 0); static bool ExtractTar(const std::string& inFileName, const std::vector<std::string>& files, bool verbose); // This should be called first thing in main @@ -389,6 +389,7 @@ public: static std::string const& GetCMakeCursesCommand(); static std::string const& GetCMClDepsCommand(); static std::string const& GetCMakeRoot(); + static std::string const& GetHTMLDoc(); /** Get the CWD mapped through the KWSys translation map. */ static std::string GetCurrentWorkingDirectory(); @@ -434,6 +435,7 @@ public: unsigned int Delay; }; static WindowsFileRetry GetWindowsFileRetry(); + static WindowsFileRetry GetWindowsDirectoryRetry(); #endif /** Get the real path for a given path, removing all symlinks. @@ -464,5 +466,3 @@ private: static bool s_FatalErrorOccured; static bool s_DisableRunCommandOutput; }; - -#endif diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 36e1ad5fd1..bda2b30b16 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -7,6 +7,7 @@ #include <cstring> #include <initializer_list> #include <iterator> +#include <map> #include <set> #include <sstream> #include <unordered_set> @@ -185,6 +186,7 @@ public: std::vector<cmInstallTargetGenerator*> InstallGenerators; std::set<std::string> SystemIncludeDirectories; cmTarget::LinkLibraryVectorType OriginalLinkLibraries; + std::map<std::string, BTs<std::string>> LanguageStandardProperties; std::vector<std::string> IncludeDirectoriesEntries; std::vector<cmListFileBacktrace> IncludeDirectoriesBacktraces; std::vector<std::string> CompileOptionsEntries; @@ -214,6 +216,15 @@ public: std::string ProcessSourceItemCMP0049(const std::string& s); }; +namespace { +#define SETUP_COMMON_LANGUAGE_PROPERTIES(lang) \ + initProp(#lang "_COMPILER_LAUNCHER"); \ + initProp(#lang "_STANDARD"); \ + initProp(#lang "_STANDARD_REQUIRED"); \ + initProp(#lang "_EXTENSIONS"); \ + initProp(#lang "_VISIBILITY_PRESET") +} + cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, Visibility vis, cmMakefile* mf, PerConfig perConfig) : impl(cm::make_unique<cmTargetInternals>()) @@ -254,24 +265,30 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, auto initProp = [this, mf, &defKey](const std::string& property) { // Replace everything after "CMAKE_" defKey.replace(defKey.begin() + 6, defKey.end(), property); - if (const char* value = mf->GetDefinition(defKey)) { - this->SetProperty(property, value); + if (cmProp value = mf->GetDefinition(defKey)) { + this->SetProperty(property, *value); } }; auto initPropValue = [this, mf, &defKey](const std::string& property, const char* default_value) { // Replace everything after "CMAKE_" defKey.replace(defKey.begin() + 6, defKey.end(), property); - if (const char* value = mf->GetDefinition(defKey)) { - this->SetProperty(property, value); + if (cmProp value = mf->GetDefinition(defKey)) { + 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) { + if (this->CanCompileSources()) { + + SETUP_COMMON_LANGUAGE_PROPERTIES(C); + SETUP_COMMON_LANGUAGE_PROPERTIES(OBJC); + SETUP_COMMON_LANGUAGE_PROPERTIES(CXX); + SETUP_COMMON_LANGUAGE_PROPERTIES(OBJCXX); + SETUP_COMMON_LANGUAGE_PROPERTIES(CUDA); + initProp("ANDROID_API"); initProp("ANDROID_API_MIN"); initProp("ANDROID_ARCH"); @@ -308,6 +325,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("Fortran_MODULE_DIRECTORY"); initProp("Fortran_COMPILER_LAUNCHER"); initProp("Fortran_PREPROCESS"); + initProp("Fortran_VISIBILITY_PRESET"); initProp("GNUtoMS"); initProp("OSX_ARCHITECTURES"); initProp("IOS_INSTALL_COMBINED"); @@ -333,38 +351,26 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("NO_SYSTEM_FROM_IMPORTED"); initProp("BUILD_WITH_INSTALL_NAME_DIR"); initProp("C_CLANG_TIDY"); - initProp("C_COMPILER_LAUNCHER"); initProp("C_CPPLINT"); initProp("C_CPPCHECK"); initProp("C_INCLUDE_WHAT_YOU_USE"); initProp("LINK_WHAT_YOU_USE"); - initProp("C_STANDARD"); - initProp("C_STANDARD_REQUIRED"); - initProp("C_EXTENSIONS"); - initProp("OBJC_COMPILER_LAUNCHER"); - initProp("OBJC_STANDARD"); - initProp("OBJC_STANDARD_REQUIRED"); - initProp("OBJC_EXTENSIONS"); initProp("CXX_CLANG_TIDY"); - initProp("CXX_COMPILER_LAUNCHER"); initProp("CXX_CPPLINT"); initProp("CXX_CPPCHECK"); initProp("CXX_INCLUDE_WHAT_YOU_USE"); - initProp("CXX_STANDARD"); - initProp("CXX_STANDARD_REQUIRED"); - initProp("CXX_EXTENSIONS"); - initProp("OBJCXX_COMPILER_LAUNCHER"); - initProp("OBJCXX_STANDARD"); - initProp("OBJCXX_STANDARD_REQUIRED"); - initProp("OBJCXX_EXTENSIONS"); - initProp("CUDA_STANDARD"); - initProp("CUDA_STANDARD_REQUIRED"); - initProp("CUDA_EXTENSIONS"); - initProp("CUDA_COMPILER_LAUNCHER"); initProp("CUDA_SEPARABLE_COMPILATION"); initProp("CUDA_RESOLVE_DEVICE_SYMBOLS"); initProp("CUDA_RUNTIME_LIBRARY"); initProp("CUDA_ARCHITECTURES"); + initProp("VISIBILITY_INLINES_HIDDEN"); + initProp("JOB_POOL_COMPILE"); + initProp("JOB_POOL_LINK"); + initProp("JOB_POOL_PRECOMPILE_HEADER"); + initProp("ISPC_COMPILER_LAUNCHER"); + initProp("ISPC_HEADER_DIRECTORY"); + initPropValue("ISPC_HEADER_SUFFIX", "_ispc.h"); + initProp("ISPC_INSTRUCTION_SETS"); initProp("LINK_SEARCH_START_STATIC"); initProp("LINK_SEARCH_END_STATIC"); initProp("Swift_LANGUAGE_VERSION"); @@ -372,9 +378,12 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("VS_JUST_MY_CODE_DEBUGGING"); initProp("DISABLE_PRECOMPILE_HEADERS"); initProp("UNITY_BUILD"); + initProp("OPTIMIZE_DEPENDENCIES"); initPropValue("UNITY_BUILD_BATCH_SIZE", "8"); initPropValue("UNITY_BUILD_MODE", "BATCH"); initPropValue("PCH_WARN_INVALID", "ON"); + initPropValue("PCH_INSTANTIATE_TEMPLATES", "ON"); + #ifdef __APPLE__ if (this->GetGlobalGenerator()->IsXcode()) { initProp("XCODE_SCHEME_ADDRESS_SANITIZER"); @@ -395,20 +404,20 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"); initProp("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS"); initProp("XCODE_SCHEME_ENVIRONMENT"); + initPropValue("XCODE_LINK_BUILD_PHASE_MODE", "NONE"); } #endif } - if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - initProp("FOLDER"); + initProp("FOLDER"); - if (this->GetGlobalGenerator()->IsXcode()) { - initProp("XCODE_GENERATE_SCHEME"); - } + if (this->GetGlobalGenerator()->IsXcode()) { + initProp("XCODE_GENERATE_SCHEME"); } // Setup per-configuration property default values. - if (this->GetType() != cmStateEnums::UTILITY) { + if (this->GetType() != cmStateEnums::UTILITY && + this->GetType() != cmStateEnums::GLOBAL_TARGET) { static const auto configProps = { /* clang-format needs this comment to break after the opening brace */ "ARCHIVE_OUTPUT_DIRECTORY_", "LIBRARY_OUTPUT_DIRECTORY_", @@ -417,8 +426,8 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, "INTERPROCEDURAL_OPTIMIZATION_" }; // Collect the set of configuration types. - std::vector<std::string> configNames; - mf->GetConfigurations(configNames); + std::vector<std::string> configNames = + mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); for (std::string const& configName : configNames) { std::string configUpper = cmSystemTools::UpperCase(configName); for (auto const& prop : configProps) { @@ -485,16 +494,6 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, impl->Makefile->GetLinkDirectoriesBacktraces()); } - if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && - this->GetType() != cmStateEnums::UTILITY) { - initProp("C_VISIBILITY_PRESET"); - initProp("CXX_VISIBILITY_PRESET"); - initProp("OBJC_VISIBILITY_PRESET"); - initProp("OBJCXX_VISIBILITY_PRESET"); - initProp("CUDA_VISIBILITY_PRESET"); - initProp("VISIBILITY_INLINES_HIDDEN"); - } - if (impl->TargetType == cmStateEnums::EXECUTABLE) { initProp("ANDROID_GUI"); initProp("CROSSCOMPILING_EMULATOR"); @@ -503,6 +502,8 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, if (impl->TargetType == cmStateEnums::SHARED_LIBRARY || impl->TargetType == cmStateEnums::MODULE_LIBRARY) { this->SetProperty("POSITION_INDEPENDENT_CODE", "True"); + } else if (this->CanCompileSources()) { + initProp("POSITION_INDEPENDENT_CODE"); } if (impl->TargetType == cmStateEnums::SHARED_LIBRARY || impl->TargetType == cmStateEnums::EXECUTABLE) { @@ -510,11 +511,6 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("WINDOWS_EXPORT_ALL_SYMBOLS"); } - if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && - this->GetType() != cmStateEnums::UTILITY) { - initProp("POSITION_INDEPENDENT_CODE"); - } - // Record current policies for later use. impl->Makefile->RecordPolicies(impl->PolicyMap); @@ -526,36 +522,26 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, impl->PolicyMap.Set(cmPolicies::CMP0022, cmPolicies::NEW); } - if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY && - this->GetType() != cmStateEnums::UTILITY) { - initProp("JOB_POOL_COMPILE"); - initProp("JOB_POOL_LINK"); - initProp("JOB_POOL_PRECOMPILE_HEADER"); - } - if (impl->TargetType <= cmStateEnums::GLOBAL_TARGET) { initProp("DOTNET_TARGET_FRAMEWORK"); initProp("DOTNET_TARGET_FRAMEWORK_VERSION"); } - if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { - - // check for "CMAKE_VS_GLOBALS" variable and set up target properties - // if any - const char* globals = mf->GetDefinition("CMAKE_VS_GLOBALS"); - if (globals) { - const std::string genName = mf->GetGlobalGenerator()->GetName(); - if (cmHasLiteralPrefix(genName, "Visual Studio")) { - std::vector<std::string> props = cmExpandedList(globals); - const std::string vsGlobal = "VS_GLOBAL_"; - for (const std::string& i : props) { - // split NAME=VALUE - const std::string::size_type assignment = i.find('='); - if (assignment != std::string::npos) { - const std::string propName = vsGlobal + i.substr(0, assignment); - const std::string propValue = i.substr(assignment + 1); - initPropValue(propName, propValue.c_str()); - } + // check for "CMAKE_VS_GLOBALS" variable and set up target properties + // if any + cmProp globals = mf->GetDefinition("CMAKE_VS_GLOBALS"); + if (globals) { + const std::string genName = mf->GetGlobalGenerator()->GetName(); + if (cmHasLiteralPrefix(genName, "Visual Studio")) { + std::vector<std::string> props = cmExpandedList(*globals); + const std::string vsGlobal = "VS_GLOBAL_"; + for (const std::string& i : props) { + // split NAME=VALUE + const std::string::size_type assignment = i.find('='); + if (assignment != std::string::npos) { + const std::string propName = vsGlobal + i.substr(0, assignment); + const std::string propValue = i.substr(assignment + 1); + initPropValue(propName, propValue.c_str()); } } } @@ -598,6 +584,40 @@ cmGlobalGenerator* cmTarget::GetGlobalGenerator() const return impl->Makefile->GetGlobalGenerator(); } +BTs<std::string> const* cmTarget::GetLanguageStandardProperty( + const std::string& propertyName) const +{ + auto entry = impl->LanguageStandardProperties.find(propertyName); + if (entry != impl->LanguageStandardProperties.end()) { + return &entry->second; + } + + return nullptr; +} + +void cmTarget::SetLanguageStandardProperty(std::string const& lang, + std::string const& value, + const std::string& feature) +{ + cmListFileBacktrace featureBacktrace; + for (size_t i = 0; i < impl->CompileFeaturesEntries.size(); i++) { + if (impl->CompileFeaturesEntries[i] == feature) { + if (i < impl->CompileFeaturesBacktraces.size()) { + featureBacktrace = impl->CompileFeaturesBacktraces[i]; + } + break; + } + } + + BTs<std::string>& languageStandardProperty = + impl->LanguageStandardProperties[cmStrCat(lang, "_STANDARD")]; + if (languageStandardProperty.Value != value) { + languageStandardProperty.Value = value; + languageStandardProperty.Backtraces.clear(); + } + languageStandardProperty.Backtraces.emplace_back(featureBacktrace); +} + void cmTarget::AddUtility(std::string const& name, bool cross, cmMakefile* mf) { impl->Utilities.insert(BT<std::pair<std::string, bool>>( @@ -636,6 +656,12 @@ bool cmTarget::IsAppBundleOnApple() const this->GetPropertyAsBool("MACOSX_BUNDLE")); } +bool cmTarget::IsAndroidGuiExecutable() const +{ + return (this->GetType() == cmStateEnums::EXECUTABLE && impl->IsAndroid && + this->GetPropertyAsBool("ANDROID_GUI")); +} + std::vector<cmCustomCommand> const& cmTarget::GetPreBuildCommands() const { return impl->PreBuildCommands; @@ -996,9 +1022,9 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib, this->GetPolicyStatusCMP0073() == cmPolicies::WARN)) { std::string targetEntry = cmStrCat(impl->Name, "_LIB_DEPENDS"); std::string dependencies; - const char* old_val = mf.GetDefinition(targetEntry); + cmProp old_val = mf.GetDefinition(targetEntry); if (old_val) { - dependencies += old_val; + dependencies += *old_val; } switch (llt) { case GENERAL_LibraryType: @@ -1121,12 +1147,12 @@ cmBacktraceRange cmTarget::GetLinkImplementationBacktraces() const void cmTarget::SetProperty(const std::string& prop, const char* value) { - if (!cmTargetPropertyComputer::PassesWhitelist( - this->GetType(), prop, impl->Makefile->GetMessenger(), - impl->Makefile->GetBacktrace())) { - return; - } #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP + MAKE_STATIC_PROP(C_STANDARD); + MAKE_STATIC_PROP(CXX_STANDARD); + MAKE_STATIC_PROP(CUDA_STANDARD); + MAKE_STATIC_PROP(OBJC_STANDARD); + MAKE_STATIC_PROP(OBJCXX_STANDARD); MAKE_STATIC_PROP(COMPILE_DEFINITIONS); MAKE_STATIC_PROP(COMPILE_FEATURES); MAKE_STATIC_PROP(COMPILE_OPTIONS); @@ -1308,8 +1334,17 @@ void cmTarget::SetProperty(const std::string& prop, const char* value) cmStrCat(reusedFrom, ".dir/")); cmProp tmp = reusedTarget->GetProperty("COMPILE_PDB_NAME"); - this->SetProperty("COMPILE_PDB_NAME", tmp ? tmp->c_str() : nullptr); + this->SetProperty("COMPILE_PDB_NAME", cmToCStr(tmp)); this->AddUtility(reusedFrom, false, impl->Makefile); + } else if (prop == propC_STANDARD || prop == propCXX_STANDARD || + prop == propCUDA_STANDARD || prop == propOBJC_STANDARD || + prop == propOBJCXX_STANDARD) { + if (value) { + impl->LanguageStandardProperties[prop] = + BTs<std::string>(value, impl->Makefile->GetBacktrace()); + } else { + impl->LanguageStandardProperties.erase(prop); + } } else { impl->Properties.SetProperty(prop, value); } @@ -1318,11 +1353,6 @@ void cmTarget::SetProperty(const std::string& prop, const char* value) void cmTarget::AppendProperty(const std::string& prop, const std::string& value, bool asString) { - if (!cmTargetPropertyComputer::PassesWhitelist( - this->GetType(), prop, impl->Makefile->GetMessenger(), - impl->Makefile->GetBacktrace())) { - return; - } if (prop == "NAME") { impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, "NAME property is read-only\n"); @@ -1413,6 +1443,11 @@ void cmTarget::AppendProperty(const std::string& prop, } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME")) { impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, prop + " property may not be APPENDed."); + } else if (prop == "C_STANDARD" || prop == "CXX_STANDARD" || + prop == "CUDA_STANDARD" || prop == "OBJC_STANDARD" || + prop == "OBJCXX_STANDARD") { + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, + prop + " property may not be appended."); } else { impl->Properties.AppendProperty(prop, value, asString); } @@ -1626,6 +1661,11 @@ cmProp cmTarget::GetComputedProperty(const std::string& prop, cmProp cmTarget::GetProperty(const std::string& prop) const { #define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP + MAKE_STATIC_PROP(C_STANDARD); + MAKE_STATIC_PROP(CXX_STANDARD); + MAKE_STATIC_PROP(CUDA_STANDARD); + MAKE_STATIC_PROP(OBJC_STANDARD); + MAKE_STATIC_PROP(OBJCXX_STANDARD); MAKE_STATIC_PROP(LINK_LIBRARIES); MAKE_STATIC_PROP(TYPE); MAKE_STATIC_PROP(INCLUDE_DIRECTORIES); @@ -1646,6 +1686,11 @@ cmProp cmTarget::GetProperty(const std::string& prop) const MAKE_STATIC_PROP(TRUE); #undef MAKE_STATIC_PROP static std::unordered_set<std::string> const specialProps{ + propC_STANDARD, + propCXX_STANDARD, + propCUDA_STANDARD, + propOBJC_STANDARD, + propOBJCXX_STANDARD, propLINK_LIBRARIES, propTYPE, propINCLUDE_DIRECTORIES, @@ -1664,6 +1709,15 @@ cmProp cmTarget::GetProperty(const std::string& prop) const propSOURCES }; if (specialProps.count(prop)) { + if (prop == propC_STANDARD || prop == propCXX_STANDARD || + prop == propCUDA_STANDARD || prop == propOBJC_STANDARD || + prop == propOBJCXX_STANDARD) { + auto propertyIter = impl->LanguageStandardProperties.find(prop); + if (propertyIter == impl->LanguageStandardProperties.end()) { + return nullptr; + } + return &(propertyIter->second.Value); + } if (prop == propLINK_LIBRARIES) { if (impl->LinkImplementationPropertyEntries.empty()) { return nullptr; @@ -1804,8 +1858,7 @@ std::string const& cmTarget::GetSafeProperty(std::string const& prop) const bool cmTarget::GetPropertyAsBool(const std::string& prop) const { - cmProp p = this->GetProperty(prop); - return p && cmIsOn(*p); + return cmIsOn(this->GetProperty(prop)); } cmPropertyMap const& cmTarget::GetProperties() const @@ -1838,6 +1891,27 @@ bool cmTarget::IsPerConfig() const return impl->PerConfig; } +bool cmTarget::CanCompileSources() const +{ + if (this->IsImported()) { + return false; + } + switch (this->GetType()) { + case cmStateEnums::EXECUTABLE: + case cmStateEnums::STATIC_LIBRARY: + case cmStateEnums::SHARED_LIBRARY: + case cmStateEnums::MODULE_LIBRARY: + case cmStateEnums::OBJECT_LIBRARY: + return true; + case cmStateEnums::UTILITY: + case cmStateEnums::INTERFACE_LIBRARY: + case cmStateEnums::GLOBAL_TARGET: + case cmStateEnums::UNKNOWN_LIBRARY: + break; + } + return false; +} + const char* cmTarget::GetSuffixVariableInternal( cmStateEnums::ArtifactType artifact) const { @@ -1865,7 +1939,7 @@ const char* cmTarget::GetSuffixVariableInternal( case cmStateEnums::RuntimeBinaryArtifact: // Android GUI application packages store the native // binary as a shared library. - return (impl->IsAndroid && this->GetPropertyAsBool("ANDROID_GUI") + return (this->IsAndroidGuiExecutable() ? "CMAKE_SHARED_LIBRARY_SUFFIX" : "CMAKE_EXECUTABLE_SUFFIX"); case cmStateEnums::ImportLibraryArtifact: @@ -1906,7 +1980,7 @@ const char* cmTarget::GetPrefixVariableInternal( case cmStateEnums::RuntimeBinaryArtifact: // Android GUI application packages store the native // binary as a shared library. - return (impl->IsAndroid && this->GetPropertyAsBool("ANDROID_GUI") + return (this->IsAndroidGuiExecutable() ? "CMAKE_SHARED_LIBRARY_PREFIX" : ""); case cmStateEnums::ImportLibraryArtifact: @@ -1972,6 +2046,39 @@ std::string cmTarget::ImportedGetFullPath( } if (result.empty()) { + if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { + auto message = [&]() -> std::string { + std::string unset; + std::string configuration; + + if (artifact == cmStateEnums::RuntimeBinaryArtifact) { + unset = "IMPORTED_LOCATION"; + } else if (artifact == cmStateEnums::ImportLibraryArtifact) { + unset = "IMPORTED_IMPLIB"; + } + + if (!config.empty()) { + configuration = cmStrCat(" configuration \"", config, "\""); + } + + return cmStrCat(unset, " not set for imported target \"", + this->GetName(), "\"", configuration, "."); + }; + + switch (this->GetPolicyStatus(cmPolicies::CMP0111)) { + case cmPolicies::WARN: + impl->Makefile->IssueMessage( + MessageType::AUTHOR_WARNING, + cmPolicies::GetPolicyWarning(cmPolicies::CMP0111) + "\n" + + message()); + CM_FALLTHROUGH; + case cmPolicies::OLD: + break; + default: + impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, message()); + } + } + result = cmStrCat(this->GetName(), "-NOTFOUND"); } return result; diff --git a/Source/cmTarget.h b/Source/cmTarget.h index f0ddb686a7..d8f66bcf13 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTarget_h -#define cmTarget_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -196,6 +195,7 @@ public: bool IsImported() const; bool IsImportedGloballyVisible() const; bool IsPerConfig() const; + bool CanCompileSources() const; bool GetMappedConfig(std::string const& desired_config, cmProp& loc, cmProp& imp, std::string& suffix) const; @@ -209,6 +209,9 @@ public: //! Return whether this target is an executable Bundle on Apple. bool IsAppBundleOnApple() const; + //! Return whether this target is a GUI executable on Android. + bool IsAndroidGuiExecutable() const; + //! Get a backtrace from the creation of the target. cmListFileBacktrace const& GetBacktrace() const; @@ -233,6 +236,13 @@ public: void AddSystemIncludeDirectories(std::set<std::string> const& incs); std::set<std::string> const& GetSystemIncludeDirectories() const; + BTs<std::string> const* GetLanguageStandardProperty( + const std::string& propertyName) const; + + void SetLanguageStandardProperty(std::string const& lang, + std::string const& value, + const std::string& feature); + cmStringRange GetIncludeDirectoriesEntries() const; cmBacktraceRange GetIncludeDirectoriesBacktraces() const; @@ -280,5 +290,3 @@ private: private: std::unique_ptr<cmTargetInternals> impl; }; - -#endif diff --git a/Source/cmTargetCompileDefinitionsCommand.h b/Source/cmTargetCompileDefinitionsCommand.h index 05ff092e87..54a20fde5c 100644 --- a/Source/cmTargetCompileDefinitionsCommand.h +++ b/Source/cmTargetCompileDefinitionsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetCompileDefinitionsCommand_h -#define cmTargetCompileDefinitionsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmTargetCompileDefinitionsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmTargetCompileFeaturesCommand.cxx b/Source/cmTargetCompileFeaturesCommand.cxx index 06be4f0ddd..aa1abdd64c 100644 --- a/Source/cmTargetCompileFeaturesCommand.cxx +++ b/Source/cmTargetCompileFeaturesCommand.cxx @@ -4,6 +4,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmStandardLevelResolver.h" #include "cmStringAlgorithms.h" #include "cmTargetPropCommandBase.h" @@ -29,9 +30,10 @@ private: const std::vector<std::string>& content, bool /*prepend*/, bool /*system*/) override { + cmStandardLevelResolver standardResolver(this->Makefile); for (std::string const& it : content) { std::string error; - if (!this->Makefile->AddRequiredTargetFeature(tgt, it, &error)) { + if (!standardResolver.AddRequiredTargetFeature(tgt, it, &error)) { this->SetError(error); return false; // Not (successfully) handled. } diff --git a/Source/cmTargetCompileFeaturesCommand.h b/Source/cmTargetCompileFeaturesCommand.h index db0c04b7e5..9dbf486492 100644 --- a/Source/cmTargetCompileFeaturesCommand.h +++ b/Source/cmTargetCompileFeaturesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetCompileFeaturesCommand_h -#define cmTargetCompileFeaturesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmTargetCompileFeaturesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmTargetCompileOptionsCommand.h b/Source/cmTargetCompileOptionsCommand.h index 3ab1a89c56..1f7c6845f5 100644 --- a/Source/cmTargetCompileOptionsCommand.h +++ b/Source/cmTargetCompileOptionsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetCompileOptionsCommand_h -#define cmTargetCompileOptionsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmTargetCompileOptionsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmTargetDepend.h b/Source/cmTargetDepend.h index 5452cc7fba..36702bdbf9 100644 --- a/Source/cmTargetDepend.h +++ b/Source/cmTargetDepend.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetDepend_h -#define cmTargetDepend_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -60,5 +59,3 @@ public: class cmTargetDependSet : public std::set<cmTargetDepend> { }; - -#endif diff --git a/Source/cmTargetExport.h b/Source/cmTargetExport.h index 9304eabbb3..1e38d84923 100644 --- a/Source/cmTargetExport.h +++ b/Source/cmTargetExport.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetExport_h -#define cmTargetExport_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -32,6 +31,6 @@ public: cmInstallFilesGenerator* HeaderGenerator; std::string InterfaceIncludeDirectories; ///@} -}; -#endif + bool NamelinkOnly = false; +}; diff --git a/Source/cmTargetIncludeDirectoriesCommand.h b/Source/cmTargetIncludeDirectoriesCommand.h index 9958f419a3..223da7d8ac 100644 --- a/Source/cmTargetIncludeDirectoriesCommand.h +++ b/Source/cmTargetIncludeDirectoriesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetIncludeDirectoriesCommand_h -#define cmTargetIncludeDirectoriesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmTargetIncludeDirectoriesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmTargetLinkDirectoriesCommand.h b/Source/cmTargetLinkDirectoriesCommand.h index 3724d6cf82..e30570903d 100644 --- a/Source/cmTargetLinkDirectoriesCommand.h +++ b/Source/cmTargetLinkDirectoriesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetLinkDirectoriesCommand_h -#define cmTargetLinkDirectoriesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmTargetLinkDirectoriesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index df751da080..aecc18e3ac 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTargetLinkLibrariesCommand.h" -#include <cstring> #include <memory> #include <sstream> #include <unordered_set> @@ -11,9 +10,11 @@ #include "cmExecutionStatus.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -277,12 +278,12 @@ bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args, // with old versions of CMake and new) llt = GENERAL_LibraryType; std::string linkType = cmStrCat(args[0], "_LINK_TYPE"); - const char* linkTypeString = mf.GetDefinition(linkType); + cmProp linkTypeString = mf.GetDefinition(linkType); if (linkTypeString) { - if (strcmp(linkTypeString, "debug") == 0) { + if (*linkTypeString == "debug") { llt = DEBUG_LibraryType; } - if (strcmp(linkTypeString, "optimized") == 0) { + if (*linkTypeString == "optimized") { llt = OPTIMIZED_LibraryType; } } @@ -386,7 +387,7 @@ bool TLL::HandleLibrary(ProcessingState currentProcessingState, ? cmTarget::KeywordTLLSignature : cmTarget::PlainTLLSignature; if (!this->Target->PushTLLCommandTrace( - sig, this->Makefile.GetExecutionContext())) { + sig, this->Makefile.GetBacktrace().Top())) { std::ostringstream e; const char* modal = nullptr; MessageType messageType = MessageType::AUTHOR_WARNING; diff --git a/Source/cmTargetLinkLibrariesCommand.h b/Source/cmTargetLinkLibrariesCommand.h index 4b2deabea4..bc76f3ef64 100644 --- a/Source/cmTargetLinkLibrariesCommand.h +++ b/Source/cmTargetLinkLibrariesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetLinkLibrariesCommand_h -#define cmTargetLinkLibrariesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmTargetLinkLibrariesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmTargetLinkLibraryType.h b/Source/cmTargetLinkLibraryType.h index 192c6da29a..16ac41a3ec 100644 --- a/Source/cmTargetLinkLibraryType.h +++ b/Source/cmTargetLinkLibraryType.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetLinkLibraryType_h -#define cmTargetLinkLibraryType_h +#pragma once enum cmTargetLinkLibraryType { @@ -9,5 +8,3 @@ enum cmTargetLinkLibraryType DEBUG_LibraryType, OPTIMIZED_LibraryType }; - -#endif diff --git a/Source/cmTargetLinkOptionsCommand.h b/Source/cmTargetLinkOptionsCommand.h index 13fb40cb69..fbe7d49b86 100644 --- a/Source/cmTargetLinkOptionsCommand.h +++ b/Source/cmTargetLinkOptionsCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetLinkOptionsCommand_h -#define cmTargetLinkOptionsCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmTargetLinkOptionsCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmTargetPrecompileHeadersCommand.h b/Source/cmTargetPrecompileHeadersCommand.h index 8b0ac97777..dd08a6d6d9 100644 --- a/Source/cmTargetPrecompileHeadersCommand.h +++ b/Source/cmTargetPrecompileHeadersCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetPrecompileHeadersCommand_h -#define cmTargetPrecompileHeadersCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmTargetPrecompileHeadersCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmTargetPropCommandBase.cxx b/Source/cmTargetPropCommandBase.cxx index e7147209b4..9e301360a8 100644 --- a/Source/cmTargetPropCommandBase.cxx +++ b/Source/cmTargetPropCommandBase.cxx @@ -123,7 +123,7 @@ bool cmTargetPropCommandBase::ProcessContentArgs( } if (!content.empty()) { if (this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY && - scope != "INTERFACE") { + scope != "INTERFACE" && this->Property != "SOURCES") { this->SetError("may only set INTERFACE properties on INTERFACE targets"); return false; } diff --git a/Source/cmTargetPropCommandBase.h b/Source/cmTargetPropCommandBase.h index 601ad01132..50ac1aaa34 100644 --- a/Source/cmTargetPropCommandBase.h +++ b/Source/cmTargetPropCommandBase.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetPropCommandBase_h -#define cmTargetPropCommandBase_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -58,5 +57,3 @@ private: cmExecutionStatus& Status; }; - -#endif diff --git a/Source/cmTargetPropertyComputer.cxx b/Source/cmTargetPropertyComputer.cxx index f37995c23d..b9c93659fb 100644 --- a/Source/cmTargetPropertyComputer.cxx +++ b/Source/cmTargetPropertyComputer.cxx @@ -3,15 +3,12 @@ #include "cmTargetPropertyComputer.h" -#include <cctype> #include <sstream> -#include <unordered_set> #include "cmMessageType.h" #include "cmMessenger.h" #include "cmPolicies.h" #include "cmStateSnapshot.h" -#include "cmStringAlgorithms.h" bool cmTargetPropertyComputer::HandleLocationPropertyPolicy( std::string const& tgtName, cmMessenger* messenger, @@ -44,69 +41,3 @@ bool cmTargetPropertyComputer::HandleLocationPropertyPolicy( return messageType != MessageType::FATAL_ERROR; } - -bool cmTargetPropertyComputer::WhiteListedInterfaceProperty( - const std::string& prop) -{ - if (cmHasLiteralPrefix(prop, "INTERFACE_")) { - return true; - } - if (cmHasLiteralPrefix(prop, "_")) { - return true; - } - if (std::islower(prop[0])) { - return true; - } - static std::unordered_set<std::string> const builtIns{ - "COMPATIBLE_INTERFACE_BOOL", - "COMPATIBLE_INTERFACE_NUMBER_MAX", - "COMPATIBLE_INTERFACE_NUMBER_MIN", - "COMPATIBLE_INTERFACE_STRING", - "DEPRECATION", - "EXPORT_NAME", - "EXPORT_PROPERTIES", - "IMPORTED", - "IMPORTED_GLOBAL", - "MANUALLY_ADDED_DEPENDENCIES", - "NAME", - "PRIVATE_HEADER", - "PUBLIC_HEADER", - "TYPE" - }; - - if (builtIns.count(prop)) { - return true; - } - - if (prop == "IMPORTED_CONFIGURATIONS" || prop == "IMPORTED_LIBNAME" || - cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME_") || - cmHasLiteralPrefix(prop, "MAP_IMPORTED_CONFIG_")) { - return true; - } - - // This property should not be allowed but was incorrectly added in - // CMake 3.8. We can't remove it from the whitelist without breaking - // projects that try to set it. One day we could warn about this, but - // for now silently accept it. - if (prop == "NO_SYSTEM_FROM_IMPORTED") { - return true; - } - - return false; -} - -bool cmTargetPropertyComputer::PassesWhitelist( - cmStateEnums::TargetType tgtType, std::string const& prop, - cmMessenger* messenger, cmListFileBacktrace const& context) -{ - if (tgtType == cmStateEnums::INTERFACE_LIBRARY && - !WhiteListedInterfaceProperty(prop)) { - std::ostringstream e; - e << "INTERFACE_LIBRARY targets may only have whitelisted properties. " - "The property \"" - << prop << "\" is not allowed."; - messenger->IssueMessage(MessageType::FATAL_ERROR, e.str(), context); - return false; - } - return true; -} diff --git a/Source/cmTargetPropertyComputer.h b/Source/cmTargetPropertyComputer.h index f87b7c2d28..f2be318b81 100644 --- a/Source/cmTargetPropertyComputer.h +++ b/Source/cmTargetPropertyComputer.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetPropertyComputer_h -#define cmTargetPropertyComputer_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -35,12 +34,6 @@ public: return nullptr; } - static bool WhiteListedInterfaceProperty(const std::string& prop); - - static bool PassesWhitelist(cmStateEnums::TargetType tgtType, - std::string const& prop, cmMessenger* messenger, - cmListFileBacktrace const& context); - private: static bool HandleLocationPropertyPolicy(std::string const& tgtName, cmMessenger* messenger, @@ -107,5 +100,3 @@ private: static cmProp GetSources(Target const* tgt, cmMessenger* messenger, cmListFileBacktrace const& context); }; - -#endif diff --git a/Source/cmTargetSourcesCommand.h b/Source/cmTargetSourcesCommand.h index 5eecf3454e..306226c9ea 100644 --- a/Source/cmTargetSourcesCommand.h +++ b/Source/cmTargetSourcesCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTargetSourcesCommand_h -#define cmTargetSourcesCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmTargetSourcesCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmTest.h b/Source/cmTest.h index 72d4ed9165..f33b7e286d 100644 --- a/Source/cmTest.h +++ b/Source/cmTest.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTest_h -#define cmTest_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -66,5 +65,3 @@ private: cmMakefile* Makefile; cmListFileBacktrace Backtrace; }; - -#endif diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx index af91177178..da4593f67f 100644 --- a/Source/cmTestGenerator.cxx +++ b/Source/cmTestGenerator.cxx @@ -2,8 +2,12 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTestGenerator.h" +#include <algorithm> +#include <cstddef> // IWYU pragma: keep +#include <iterator> #include <memory> #include <ostream> +#include <string> #include <utility> #include <vector> @@ -11,7 +15,10 @@ #include "cmGeneratorTarget.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" #include "cmOutputConverter.h" +#include "cmPolicies.h" #include "cmProperty.h" #include "cmPropertyMap.h" #include "cmRange.h" @@ -20,6 +27,52 @@ #include "cmSystemTools.h" #include "cmTest.h" +namespace /* anonymous */ +{ + +bool needToQuoteTestName(const cmMakefile& mf, const std::string& name) +{ + // Determine if policy CMP0110 is set to NEW. + switch (mf.GetPolicyStatus(cmPolicies::CMP0110)) { + case cmPolicies::WARN: + // Only warn if a forbidden character is used in the name. + if (name.find_first_of("$[] #;\t\n\"\\") != std::string::npos) { + mf.IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0110), + "\nThe following name given to add_test() is invalid if " + "CMP0110 is not set or set to OLD:\n `", + name, "´\n")); + } + CM_FALLTHROUGH; + case cmPolicies::OLD: + // OLD behavior is to not quote the test's name. + return false; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + default: + // NEW behavior is to quote the test's name. + return true; + } +} + +std::size_t countMaxConsecutiveEqualSigns(const std::string& name) +{ + std::size_t max = 0; + auto startIt = find(name.begin(), name.end(), '='); + auto endIt = startIt; + for (; startIt != name.end(); startIt = find(endIt, name.end(), '=')) { + endIt = + find_if_not(startIt + 1, name.end(), [](char c) { return c == '='; }); + max = + std::max(max, static_cast<std::size_t>(std::distance(startIt, endIt))); + } + return max; +} + +} // End: anonymous namespace + cmTestGenerator::cmTestGenerator( cmTest* test, std::vector<std::string> const& configurations) : cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations) @@ -76,8 +129,21 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, // Set up generator expression evaluation context. cmGeneratorExpression ge(this->Test->GetBacktrace()); + // Determine if policy CMP0110 is set to NEW. + const bool quote_test_name = + needToQuoteTestName(*this->Test->GetMakefile(), this->Test->GetName()); + // Determine the number of equal-signs needed for quoting test name with + // [==[...]==] syntax. + const std::string equalSigns( + 1 + countMaxConsecutiveEqualSigns(this->Test->GetName()), '='); + // Start the test command. - os << indent << "add_test(" << this->Test->GetName() << " "; + if (quote_test_name) { + os << indent << "add_test([" << equalSigns << "[" << this->Test->GetName() + << "]" << equalSigns << "] "; + } else { + os << indent << "add_test(" << this->Test->GetName() << " "; + } // Evaluate command line arguments std::vector<std::string> argv = @@ -102,7 +168,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, // Prepend with the emulator when cross compiling if required. cmProp emulator = target->GetProperty("CROSSCOMPILING_EMULATOR"); - if (emulator != nullptr && !emulator->empty()) { + if (cmNonempty(emulator)) { std::vector<std::string> emulatorWithArgs = cmExpandedList(*emulator); std::string emulatorExe(emulatorWithArgs[0]); cmSystemTools::ConvertToUnixSlashes(emulatorExe); @@ -127,8 +193,13 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, os << ")\n"; // Output properties for the test. - os << indent << "set_tests_properties(" << this->Test->GetName() - << " PROPERTIES "; + if (quote_test_name) { + os << indent << "set_tests_properties([" << equalSigns << "[" + << this->Test->GetName() << "]" << equalSigns << "] PROPERTIES "; + } else { + os << indent << "set_tests_properties(" << this->Test->GetName() + << " PROPERTIES "; + } for (auto const& i : this->Test->GetProperties().GetList()) { os << " " << i.first << " " << cmOutputConverter::EscapeForCMake( @@ -140,7 +211,21 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os, void cmTestGenerator::GenerateScriptNoConfig(std::ostream& os, Indent indent) { - os << indent << "add_test(" << this->Test->GetName() << " NOT_AVAILABLE)\n"; + // Determine if policy CMP0110 is set to NEW. + const bool quote_test_name = + needToQuoteTestName(*this->Test->GetMakefile(), this->Test->GetName()); + // Determine the number of equal-signs needed for quoting test name with + // [==[...]==] syntax. + const std::string equalSigns( + 1 + countMaxConsecutiveEqualSigns(this->Test->GetName()), '='); + + if (quote_test_name) { + os << indent << "add_test([" << equalSigns << "[" << this->Test->GetName() + << "]" << equalSigns << "] NOT_AVAILABLE)\n"; + } else { + os << indent << "add_test(" << this->Test->GetName() + << " NOT_AVAILABLE)\n"; + } } bool cmTestGenerator::NeedsScriptNoConfig() const @@ -155,14 +240,27 @@ void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent) { this->TestGenerated = true; + // Determine if policy CMP0110 is set to NEW. + const bool quote_test_name = + needToQuoteTestName(*this->Test->GetMakefile(), this->Test->GetName()); + // Determine the number of equal-signs needed for quoting test name with + // [==[...]==] syntax. + const std::string equalSigns( + 1 + countMaxConsecutiveEqualSigns(this->Test->GetName()), '='); + // Get the test command line to be executed. std::vector<std::string> const& command = this->Test->GetCommand(); std::string exe = command[0]; cmSystemTools::ConvertToUnixSlashes(exe); - fout << indent; - fout << "add_test("; - fout << this->Test->GetName() << " \"" << exe << "\""; + if (quote_test_name) { + fout << indent << "add_test([" << equalSigns << "[" + << this->Test->GetName() << "]" << equalSigns << "] \"" << exe + << "\""; + } else { + fout << indent << "add_test(" << this->Test->GetName() << " \"" << exe + << "\""; + } for (std::string const& arg : cmMakeRange(command).advance(1)) { // Just double-quote all arguments so they are re-parsed @@ -182,8 +280,13 @@ void cmTestGenerator::GenerateOldStyle(std::ostream& fout, Indent indent) fout << ")\n"; // Output properties for the test. - fout << indent << "set_tests_properties(" << this->Test->GetName() - << " PROPERTIES "; + if (quote_test_name) { + fout << indent << "set_tests_properties([" << equalSigns << "[" + << this->Test->GetName() << "]" << equalSigns << "] PROPERTIES "; + } else { + fout << indent << "set_tests_properties(" << this->Test->GetName() + << " PROPERTIES "; + } for (auto const& i : this->Test->GetProperties().GetList()) { fout << " " << i.first << " " << cmOutputConverter::EscapeForCMake(i.second); diff --git a/Source/cmTestGenerator.h b/Source/cmTestGenerator.h index e388c1643d..6903140ab1 100644 --- a/Source/cmTestGenerator.h +++ b/Source/cmTestGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTestGenerator_h -#define cmTestGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -56,5 +55,3 @@ protected: cmTest* Test; bool TestGenerated; }; - -#endif diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx index 13f73dc484..67f7e11461 100644 --- a/Source/cmTimestamp.cxx +++ b/Source/cmTimestamp.cxx @@ -5,7 +5,8 @@ // POSIX APIs are needed # define _POSIX_C_SOURCE 200809L #endif -#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) +#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__QNX__) // For isascii # define _XOPEN_SOURCE 700 #endif @@ -164,7 +165,7 @@ std::string cmTimestamp::AddTimestampComponent(char flag, break; case 's': // Seconds since UNIX epoch (midnight 1-jan-1970) { - // Build a time_t for UNIX epoch and substract from the input "timeT": + // Build a time_t for UNIX epoch and subtract from the input "timeT": struct tm tmUnixEpoch; memset(&tmUnixEpoch, 0, sizeof(tmUnixEpoch)); tmUnixEpoch.tm_mday = 1; diff --git a/Source/cmTimestamp.h b/Source/cmTimestamp.h index 40338f8b24..8941abe1c1 100644 --- a/Source/cmTimestamp.h +++ b/Source/cmTimestamp.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTimestamp_h -#define cmTimestamp_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -30,5 +29,3 @@ private: std::string AddTimestampComponent(char flag, struct tm& timeStruct, time_t timeT) const; }; - -#endif diff --git a/Source/cmTryCompileCommand.h b/Source/cmTryCompileCommand.h index e525e851fb..d8cc16e59a 100644 --- a/Source/cmTryCompileCommand.h +++ b/Source/cmTryCompileCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTryCompileCommand_h -#define cmTryCompileCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -38,5 +37,3 @@ public: bool InitialPass(std::vector<std::string> const& args, cmExecutionStatus& status) override; }; - -#endif diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index 64d71bcda0..3f89641e14 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -146,10 +146,10 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, if (!this->OutputVariable.empty()) { // if the TryCompileCore saved output in this outputVariable then // prepend that output to this output - const char* compileOutput = + cmProp compileOutput = this->Makefile->GetDefinition(this->OutputVariable); if (compileOutput) { - runOutputContents = compileOutput + runOutputContents; + runOutputContents = *compileOutput + runOutputContents; } this->Makefile->AddDefinition(this->OutputVariable, runOutputContents); } @@ -328,12 +328,12 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, file << comment << "\n\n"; file << "set( " << this->RunResultVariable << " \n \"" - << this->Makefile->GetDefinition(this->RunResultVariable) + << this->Makefile->GetSafeDefinition(this->RunResultVariable) << "\"\n CACHE STRING \"Result from TRY_RUN\" FORCE)\n\n"; if (out) { file << "set( " << internalRunOutputName << " \n \"" - << this->Makefile->GetDefinition(internalRunOutputName) + << this->Makefile->GetSafeDefinition(internalRunOutputName) << "\"\n CACHE STRING \"Output from TRY_RUN\" FORCE)\n\n"; } file.close(); @@ -354,6 +354,6 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, } if (out) { - (*out) = this->Makefile->GetDefinition(internalRunOutputName); + (*out) = *this->Makefile->GetDefinition(internalRunOutputName); } } diff --git a/Source/cmTryRunCommand.h b/Source/cmTryRunCommand.h index c53a69474d..070c63cc89 100644 --- a/Source/cmTryRunCommand.h +++ b/Source/cmTryRunCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmTryRunCommand_h -#define cmTryRunCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -51,5 +50,3 @@ private: std::string RunOutputVariable; std::string CompileOutputVariable; }; - -#endif diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h index b5ccb19f13..5e8e7e6a7a 100644 --- a/Source/cmUVProcessChain.h +++ b/Source/cmUVProcessChain.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmUVProcessChain_h -#define cmUVProcessChain_h +#pragma once #include <array> #include <cstddef> // IWYU pragma: keep @@ -96,5 +95,3 @@ private: struct InternalData; std::unique_ptr<InternalData> Data; }; - -#endif diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h index 50faede835..efe45de053 100644 --- a/Source/cmUVStreambuf.h +++ b/Source/cmUVStreambuf.h @@ -1,7 +1,6 @@ /* 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 +#pragma once #include <algorithm> #include <cstring> @@ -215,5 +214,3 @@ void cmBasicUVStreambuf<CharT, Traits>::StreamRead(ssize_t nread) } using cmUVStreambuf = cmBasicUVStreambuf<char>; - -#endif diff --git a/Source/cmUnsetCommand.h b/Source/cmUnsetCommand.h index be4c166e83..3faa0530a5 100644 --- a/Source/cmUnsetCommand.h +++ b/Source/cmUnsetCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmUnsetCommand_h -#define cmUnsetCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -17,5 +16,3 @@ class cmExecutionStatus; */ bool cmUnsetCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmUseMangledMesaCommand.h b/Source/cmUseMangledMesaCommand.h index 215e4a3cc9..5670c5dcec 100644 --- a/Source/cmUseMangledMesaCommand.h +++ b/Source/cmUseMangledMesaCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmUseMangledMesaCommand_h -#define cmUseMangledMesaCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmUseMangledMesaCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmUtilitySourceCommand.cxx b/Source/cmUtilitySourceCommand.cxx index 6de78ff674..d276c8a603 100644 --- a/Source/cmUtilitySourceCommand.cxx +++ b/Source/cmUtilitySourceCommand.cxx @@ -6,6 +6,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" @@ -24,7 +25,7 @@ bool cmUtilitySourceCommand(std::vector<std::string> const& args, // The first argument is the cache entry name. std::string const& cacheEntry = *arg++; - const char* cacheValue = status.GetMakefile().GetDefinition(cacheEntry); + cmProp cacheValue = status.GetMakefile().GetDefinition(cacheEntry); // If it exists already and appears up to date then we are done. If // the string contains "(IntDir)" but that is not the // CMAKE_CFG_INTDIR setting then the value is out of date. @@ -45,7 +46,7 @@ bool cmUtilitySourceCommand(std::vector<std::string> const& args, } else { cmState* state = status.GetMakefile().GetState(); haveCacheValue = (cacheValue && - (strstr(cacheValue, "(IntDir)") == nullptr || + (strstr(cacheValue->c_str(), "(IntDir)") == nullptr || (intDir == "$(IntDir)")) && (state->GetCacheMajorVersion() != 0 && state->GetCacheMinorVersion() != 0)); @@ -84,8 +85,9 @@ bool cmUtilitySourceCommand(std::vector<std::string> const& args, std::string utilityDirectory = status.GetMakefile().GetCurrentBinaryDirectory(); std::string exePath; - if (auto d = status.GetMakefile().GetDefinition("EXECUTABLE_OUTPUT_PATH")) { - exePath = d; + if (cmProp d = + status.GetMakefile().GetDefinition("EXECUTABLE_OUTPUT_PATH")) { + exePath = *d; } if (!exePath.empty()) { utilityDirectory = exePath; @@ -96,7 +98,7 @@ bool cmUtilitySourceCommand(std::vector<std::string> const& args, // Construct the cache entry for the executable's location. std::string utilityExecutable = utilityDirectory + "/" + cmakeCFGout + "/" + utilityName + - status.GetMakefile().GetDefinition("CMAKE_EXECUTABLE_SUFFIX"); + *status.GetMakefile().GetDefinition("CMAKE_EXECUTABLE_SUFFIX"); // make sure we remove any /./ in the name cmSystemTools::ReplaceString(utilityExecutable, "/./", "/"); diff --git a/Source/cmUtilitySourceCommand.h b/Source/cmUtilitySourceCommand.h index 934d53905d..8cf7e7f707 100644 --- a/Source/cmUtilitySourceCommand.h +++ b/Source/cmUtilitySourceCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmUtilitySourceCommand_h -#define cmUtilitySourceCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmUtilitySourceCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmUtils.hxx b/Source/cmUtils.hxx index a7a3e81f6d..733e72fdad 100644 --- a/Source/cmUtils.hxx +++ b/Source/cmUtils.hxx @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmUtils_hxx -#define cmUtils_hxx +#pragma once #include "cmsys/SystemTools.hxx" @@ -13,5 +12,3 @@ inline bool isCMakeVerbose() return (cmSystemTools::HasEnv("VERBOSE") && !cmSystemTools::HasEnv("CMAKE_NO_VERBOSE")); } - -#endif diff --git a/Source/cmUuid.h b/Source/cmUuid.h index 7de20dd295..f5f724d7e4 100644 --- a/Source/cmUuid.h +++ b/Source/cmUuid.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmUuid_h -#define cmUuid_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -41,5 +40,3 @@ private: bool IntFromHexDigit(char input, char& output) const; }; - -#endif diff --git a/Source/cmVSSetupHelper.h b/Source/cmVSSetupHelper.h index a926eee924..04ea46db11 100644 --- a/Source/cmVSSetupHelper.h +++ b/Source/cmVSSetupHelper.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVSSetupHelper_h -#define cmVSSetupHelper_h +#pragma once #ifndef NOMINMAX # define NOMINMAX // Undefine min and max defined by windows.h @@ -136,5 +135,3 @@ private: std::string SpecifiedVSInstallLocation; }; - -#endif diff --git a/Source/cmVariableRequiresCommand.cxx b/Source/cmVariableRequiresCommand.cxx index 6b93c636ab..1fe03ab26d 100644 --- a/Source/cmVariableRequiresCommand.cxx +++ b/Source/cmVariableRequiresCommand.cxx @@ -4,6 +4,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmProperty.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -37,11 +38,11 @@ bool cmVariableRequiresCommand(std::vector<std::string> const& args, } } } - const char* reqVar = status.GetMakefile().GetDefinition(resultVariable); + cmProp reqVar = status.GetMakefile().GetDefinition(resultVariable); // if reqVar is unset, then set it to requirementsMet // if reqVar is set to true, but requirementsMet is false , then // set reqVar to false. - if (!reqVar || (!requirementsMet && status.GetMakefile().IsOn(reqVar))) { + if (!reqVar || (!requirementsMet && status.GetMakefile().IsOn(*reqVar))) { status.GetMakefile().AddDefinitionBool(resultVariable, requirementsMet); } diff --git a/Source/cmVariableRequiresCommand.h b/Source/cmVariableRequiresCommand.h index fb0520ed74..c6bfe8a440 100644 --- a/Source/cmVariableRequiresCommand.h +++ b/Source/cmVariableRequiresCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVariableRequiresCommand_h -#define cmVariableRequiresCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -12,5 +11,3 @@ class cmExecutionStatus; bool cmVariableRequiresCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmVariableWatch.h b/Source/cmVariableWatch.h index 6c418ed0ab..349ce0ef49 100644 --- a/Source/cmVariableWatch.h +++ b/Source/cmVariableWatch.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVariableWatch_h -#define cmVariableWatch_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -81,5 +80,3 @@ protected: StringToVectorOfPairs WatchMap; }; - -#endif diff --git a/Source/cmVariableWatchCommand.cxx b/Source/cmVariableWatchCommand.cxx index ecae16daeb..7c7fbca5b9 100644 --- a/Source/cmVariableWatchCommand.cxx +++ b/Source/cmVariableWatchCommand.cxx @@ -10,6 +10,7 @@ #include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmProperty.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmVariableWatch.h" @@ -44,20 +45,21 @@ void cmVariableWatchCommandVariableAccessed(const std::string& variable, std::string stack = *mf->GetProperty("LISTFILE_STACK"); if (!data->Command.empty()) { - cmListFileFunction newLFF; - const char* const currentListFile = + cmProp const currentListFile = mf->GetDefinition("CMAKE_CURRENT_LIST_FILE"); const auto fakeLineNo = std::numeric_limits<decltype(cmListFileArgument::Line)>::max(); - newLFF.Arguments = { + + std::vector<cmListFileArgument> newLFFArgs{ { variable, cmListFileArgument::Quoted, fakeLineNo }, { accessString, cmListFileArgument::Quoted, fakeLineNo }, { newValue ? newValue : "", cmListFileArgument::Quoted, fakeLineNo }, - { currentListFile, cmListFileArgument::Quoted, fakeLineNo }, + { *currentListFile, cmListFileArgument::Quoted, fakeLineNo }, { stack, cmListFileArgument::Quoted, fakeLineNo } }; - newLFF.Name = data->Command; - newLFF.Line = fakeLineNo; + + cmListFileFunction newLFF{ data->Command, fakeLineNo, + std::move(newLFFArgs) }; cmExecutionStatus status(*makefile); if (!makefile->ExecuteCommand(newLFF, status)) { cmSystemTools::Error( diff --git a/Source/cmVariableWatchCommand.h b/Source/cmVariableWatchCommand.h index 3f9f2443a6..4477cb7d88 100644 --- a/Source/cmVariableWatchCommand.h +++ b/Source/cmVariableWatchCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVariableWatchCommand_h -#define cmVariableWatchCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -16,5 +15,3 @@ class cmExecutionStatus; */ bool cmVariableWatchCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmVersion.h b/Source/cmVersion.h index 932ef0467c..9072c9fb56 100644 --- a/Source/cmVersion.h +++ b/Source/cmVersion.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVersion_h -#define cmVersion_h +#pragma once #include <cm3p/kwiml/int.h> @@ -30,5 +29,3 @@ public: ((((major)*1000u) * CMake_VERSION_ENCODE__BASE) + \ (((minor) % 1000u) * CMake_VERSION_ENCODE__BASE) + \ (((patch) % CMake_VERSION_ENCODE__BASE))) - -#endif diff --git a/Source/cmVersionMacros.h b/Source/cmVersionMacros.h index e8ac4f862f..f33f0dfac3 100644 --- a/Source/cmVersionMacros.h +++ b/Source/cmVersionMacros.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVersionMacros_h -#define cmVersionMacros_h +#pragma once #include "cmVersionConfig.h" @@ -9,5 +8,3 @@ #if CMake_VERSION_PATCH_IS_RELEASE(CMake_VERSION_PATCH) # define CMake_VERSION_IS_RELEASE 1 #endif - -#endif diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 2ea28393e2..4eb3b7f8fc 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -232,15 +232,17 @@ cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator( , LocalGenerator( (cmLocalVisualStudio10Generator*)target->GetLocalGenerator()) { - this->Makefile->GetConfigurations(this->Configurations); + this->Configurations = + this->Makefile->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); this->NsightTegra = gg->IsNsightTegra(); + this->Android = gg->TargetsAndroid(); for (int i = 0; i < 4; ++i) { this->NsightTegraVersion[i] = 0; } sscanf(gg->GetNsightTegraVersion().c_str(), "%u.%u.%u.%u", &this->NsightTegraVersion[0], &this->NsightTegraVersion[1], &this->NsightTegraVersion[2], &this->NsightTegraVersion[3]); - this->MSTools = !this->NsightTegra; + this->MSTools = !this->NsightTegra && !this->Android; this->Managed = false; this->TargetCompileAsWinRT = false; this->IsMissingFiles = false; @@ -312,11 +314,6 @@ std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString( void cmVisualStudio10TargetGenerator::Generate() { - // do not generate external ms projects - if (this->GeneratorTarget->GetType() == cmStateEnums::INTERFACE_LIBRARY || - this->GeneratorTarget->GetProperty("EXTERNAL_MSPROJECT")) { - return; - } const std::string ProjectFileExtension = computeProjectFileExtension(this->GeneratorTarget); if (ProjectFileExtension == ".vcxproj") { @@ -333,6 +330,13 @@ void cmVisualStudio10TargetGenerator::Generate() this->ProjectType = csproj; this->Managed = true; } + + if (this->Android && + this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE && + !this->GeneratorTarget->Target->IsAndroidGuiExecutable()) { + this->GlobalGenerator->AddAndroidExecutableWarning(this->Name); + } + // Tell the global generator the name of the project file this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME", this->Name); @@ -427,8 +431,8 @@ void cmVisualStudio10TargetGenerator::Generate() e1.Attribute("Label", "Globals"); e1.Element("ProjectGuid", "{" + this->GUID + "}"); - if (this->MSTools && - this->GeneratorTarget->GetType() <= cmStateEnums::GLOBAL_TARGET) { + if ((this->MSTools || this->Android) && + this->GeneratorTarget->IsInBuildSystem()) { this->WriteApplicationTypeSettings(e1); this->VerifyNecessaryFiles(); } @@ -469,7 +473,11 @@ void cmVisualStudio10TargetGenerator::Generate() cmProp vsGlobalKeyword = this->GeneratorTarget->GetProperty("VS_GLOBAL_KEYWORD"); if (!vsGlobalKeyword) { - e1.Element("Keyword", "Win32Proj"); + if (this->GlobalGenerator->TargetsAndroid()) { + e1.Element("Keyword", "Android"); + } else { + e1.Element("Keyword", "Win32Proj"); + } } else { e1.Element("Keyword", *vsGlobalKeyword); } @@ -503,7 +511,7 @@ void cmVisualStudio10TargetGenerator::Generate() p = this->GeneratorTarget->GetProperty( "DOTNET_TARGET_FRAMEWORK_VERSION"); } - const char* targetFrameworkVersion = p ? p->c_str() : nullptr; + const char* targetFrameworkVersion = cmToCStr(p); if (!targetFrameworkVersion && this->ProjectType == csproj && this->GlobalGenerator->TargetsWindowsCE() && this->GlobalGenerator->GetVersion() == @@ -583,20 +591,30 @@ void cmVisualStudio10TargetGenerator::Generate() case cmStateEnums::MODULE_LIBRARY: outputType = "Module"; break; - case cmStateEnums::EXECUTABLE: - if (this->GeneratorTarget->Target->GetPropertyAsBool( - "WIN32_EXECUTABLE")) { + case cmStateEnums::EXECUTABLE: { + auto const win32 = + this->GeneratorTarget->GetSafeProperty("WIN32_EXECUTABLE"); + if (win32.find("$<") != std::string::npos) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Target \"", this->GeneratorTarget->GetName(), + "\" has a generator expression in its WIN32_EXECUTABLE " + "property. This is not supported on managed executables.")); + return; + } + if (cmIsOn(win32)) { outputType = "WinExe"; } else { outputType = "Exe"; } - break; + } break; case cmStateEnums::UTILITY: + case cmStateEnums::INTERFACE_LIBRARY: case cmStateEnums::GLOBAL_TARGET: outputType = "Utility"; break; case cmStateEnums::UNKNOWN_LIBRARY: - case cmStateEnums::INTERFACE_LIBRARY: break; } e1.Element("OutputType", outputType); @@ -658,7 +676,7 @@ void cmVisualStudio10TargetGenerator::Generate() cmStrCat(this->DefaultArtifactDir, "\\nasm.props"); ConvertToWindowsSlash(propsLocal); this->Makefile->ConfigureFile(propsTemplate, propsLocal, false, true, - true); + true, true); Elem(e1, "Import").Attribute("Project", propsLocal); } } @@ -1134,14 +1152,17 @@ void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues(Elem& e0) break; case cmStateEnums::EXECUTABLE: if (this->NsightTegra && - !this->GeneratorTarget->GetPropertyAsBool("ANDROID_GUI")) { + !this->GeneratorTarget->Target->IsAndroidGuiExecutable()) { // Android executables are .so too. configType = "DynamicLibrary"; + } else if (this->Android) { + configType = "DynamicLibrary"; } else { configType = "Application"; } break; case cmStateEnums::UTILITY: + case cmStateEnums::INTERFACE_LIBRARY: case cmStateEnums::GLOBAL_TARGET: if (this->NsightTegra) { // Tegra-Android platform does not understand "Utility". @@ -1151,7 +1172,6 @@ void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues(Elem& e0) } break; case cmStateEnums::UNKNOWN_LIBRARY: - case cmStateEnums::INTERFACE_LIBRARY: break; } } @@ -1166,6 +1186,8 @@ void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues(Elem& e0) } } else if (this->NsightTegra) { this->WriteNsightTegraConfigurationValues(e1, c); + } else if (this->Android) { + this->WriteAndroidConfigurationValues(e1, c); } } } @@ -1200,9 +1222,10 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues( Elem& e1, std::string const& config) { cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; - const char* mfcFlag = this->Makefile->GetDefinition("CMAKE_MFC_FLAG"); + cmProp mfcFlag = this->Makefile->GetDefinition("CMAKE_MFC_FLAG"); if (mfcFlag) { - std::string const mfcFlagValue = mfcFlag; + std::string const mfcFlagValue = + cmGeneratorExpression::Evaluate(*mfcFlag, this->LocalGenerator, config); std::string useOfMfcValue = "false"; if (this->GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY) { @@ -1324,6 +1347,24 @@ void cmVisualStudio10TargetGenerator::WriteNsightTegraConfigurationValues( } } +void cmVisualStudio10TargetGenerator::WriteAndroidConfigurationValues( + Elem& e1, std::string const&) +{ + cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; + if (cmProp projectToolsetOverride = + this->GeneratorTarget->GetProperty("VS_PLATFORM_TOOLSET")) { + e1.Element("PlatformToolset", *projectToolsetOverride); + } else if (const char* toolset = gg->GetPlatformToolset()) { + e1.Element("PlatformToolset", toolset); + } + if (cmProp stlType = + this->GeneratorTarget->GetProperty("ANDROID_STL_TYPE")) { + if (*stlType != "none") { + e1.Element("UseOfStl", *stlType); + } + } +} + void cmVisualStudio10TargetGenerator::WriteCustomCommands(Elem& e0) { this->CSharpCustomCommandNames.clear(); @@ -1941,7 +1982,7 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(Elem& e1, } cmProp toolOverride = sf->GetProperty("VS_TOOL_OVERRIDE"); - if (toolOverride && !toolOverride->empty()) { + if (cmNonempty(toolOverride)) { tool = toolOverride->c_str(); } @@ -1950,12 +1991,12 @@ void cmVisualStudio10TargetGenerator::WriteExtraSource(Elem& e1, if (this->GlobalGenerator->TargetsWindowsPhone() || this->GlobalGenerator->TargetsWindowsStore()) { cmProp content = sf->GetProperty("VS_DEPLOYMENT_CONTENT"); - if (content && !content->empty()) { + if (cmNonempty(content)) { toolHasSettings = true; deployContent = *content; cmProp location = sf->GetProperty("VS_DEPLOYMENT_LOCATION"); - if (location && !location->empty()) { + if (cmNonempty(location)) { deployLocation = *location; } } @@ -2117,7 +2158,7 @@ void cmVisualStudio10TargetGenerator::WriteSource(Elem& e2, void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0) { - if (this->GeneratorTarget->GetType() > cmStateEnums::UTILITY) { + if (this->GeneratorTarget->GetType() == cmStateEnums::GLOBAL_TARGET) { return; } @@ -2258,7 +2299,7 @@ void cmVisualStudio10TargetGenerator::WriteAllSources(Elem& e0) e2.Attribute("UnityFilesDirectory", unityDir); } else { // Visual Studio versions prior to 2017 15.8 do not know about unity - // builds, thus we exclude the files alredy part of unity sources. + // builds, thus we exclude the files already part of unity sources. if (!si.Source->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION")) { exclude_configs = si.Configs; } @@ -2559,42 +2600,42 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions( e1.WritePlatformConfigTag("IntDir", cond, intermediateDir); - if (const char* sdkExecutableDirectories = this->Makefile->GetDefinition( + if (cmProp sdkExecutableDirectories = this->Makefile->GetDefinition( "CMAKE_VS_SDK_EXECUTABLE_DIRECTORIES")) { e1.WritePlatformConfigTag("ExecutablePath", cond, - sdkExecutableDirectories); + *sdkExecutableDirectories); } - if (const char* sdkIncludeDirectories = this->Makefile->GetDefinition( + if (cmProp sdkIncludeDirectories = this->Makefile->GetDefinition( "CMAKE_VS_SDK_INCLUDE_DIRECTORIES")) { - e1.WritePlatformConfigTag("IncludePath", cond, sdkIncludeDirectories); + e1.WritePlatformConfigTag("IncludePath", cond, *sdkIncludeDirectories); } - if (const char* sdkReferenceDirectories = this->Makefile->GetDefinition( + if (cmProp sdkReferenceDirectories = this->Makefile->GetDefinition( "CMAKE_VS_SDK_REFERENCE_DIRECTORIES")) { e1.WritePlatformConfigTag("ReferencePath", cond, - sdkReferenceDirectories); + *sdkReferenceDirectories); } - if (const char* sdkLibraryDirectories = this->Makefile->GetDefinition( + if (cmProp sdkLibraryDirectories = this->Makefile->GetDefinition( "CMAKE_VS_SDK_LIBRARY_DIRECTORIES")) { - e1.WritePlatformConfigTag("LibraryPath", cond, sdkLibraryDirectories); + e1.WritePlatformConfigTag("LibraryPath", cond, *sdkLibraryDirectories); } - if (const char* sdkLibraryWDirectories = this->Makefile->GetDefinition( + if (cmProp sdkLibraryWDirectories = this->Makefile->GetDefinition( "CMAKE_VS_SDK_LIBRARY_WINRT_DIRECTORIES")) { e1.WritePlatformConfigTag("LibraryWPath", cond, - sdkLibraryWDirectories); + *sdkLibraryWDirectories); } - if (const char* sdkSourceDirectories = + if (cmProp sdkSourceDirectories = this->Makefile->GetDefinition("CMAKE_VS_SDK_SOURCE_DIRECTORIES")) { - e1.WritePlatformConfigTag("SourcePath", cond, sdkSourceDirectories); + e1.WritePlatformConfigTag("SourcePath", cond, *sdkSourceDirectories); } - if (const char* sdkExcludeDirectories = this->Makefile->GetDefinition( + if (cmProp sdkExcludeDirectories = this->Makefile->GetDefinition( "CMAKE_VS_SDK_EXCLUDE_DIRECTORIES")) { - e1.WritePlatformConfigTag("ExcludePath", cond, sdkExcludeDirectories); + e1.WritePlatformConfigTag("ExcludePath", cond, *sdkExcludeDirectories); } std::string name = @@ -2828,6 +2869,23 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE")); } + // Add C-specific flags expressible in a ClCompile meant for C++. + if (langForClCompile == "CXX") { + std::set<std::string> languages; + this->GeneratorTarget->GetLanguages(languages, configName); + if (languages.count("C")) { + std::string flagsC; + this->LocalGenerator->AddCompileOptions(flagsC, this->GeneratorTarget, + "C", configName); + Options optC(this->LocalGenerator, Options::Compiler, + gg->GetClFlagTable()); + optC.Parse(flagsC); + if (const char* stdC = optC.GetFlag("LanguageStandard_C")) { + clOptions.AddFlag("LanguageStandard_C", stdC); + } + } + } + // Add a definition for the configuration name. std::string configDefine = cmStrCat("CMAKE_INTDIR=\"", configName, '"'); clOptions.AddDefine(configDefine); @@ -2915,7 +2973,9 @@ void cmVisualStudio10TargetGenerator::WriteClOptions( } } - if (this->MSTools) { + if (this->Android) { + e2.Element("ObjectFileName", "$(IntDir)%(filename).o"); + } else if (this->MSTools) { cmsys::RegularExpression clangToolset("v[0-9]+_clang_.*"); const char* toolset = this->GlobalGenerator->GetPlatformToolset(); if (toolset && clangToolset.find(toolset)) { @@ -3697,7 +3757,7 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( } if (this->MSTools) { - if (this->GeneratorTarget->GetPropertyAsBool("WIN32_EXECUTABLE")) { + if (this->GeneratorTarget->IsWin32Executable(config)) { if (this->GlobalGenerator->TargetsWindowsCE()) { linkOptions.AddFlag("SubSystem", "WindowsCE"); if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) { @@ -3725,21 +3785,23 @@ bool cmVisualStudio10TargetGenerator::ComputeLinkOptions( }; } - if (const char* stackVal = this->Makefile->GetDefinition( + if (cmProp stackVal = this->Makefile->GetDefinition( "CMAKE_" + linkLanguage + "_STACK_SIZE")) { - linkOptions.AddFlag("StackReserveSize", stackVal); + linkOptions.AddFlag("StackReserveSize", *stackVal); } linkOptions.AddFlag("GenerateDebugInformation", "false"); std::string pdb = cmStrCat(this->GeneratorTarget->GetPDBDirectory(config), '/', targetNames.PDB); - std::string imLib = - cmStrCat(this->GeneratorTarget->GetDirectory( - config, cmStateEnums::ImportLibraryArtifact), - '/', targetNames.ImportLibrary); + if (!targetNames.ImportLibrary.empty()) { + std::string imLib = + cmStrCat(this->GeneratorTarget->GetDirectory( + config, cmStateEnums::ImportLibraryArtifact), + '/', targetNames.ImportLibrary); - linkOptions.AddFlag("ImportLibrary", imLib); + linkOptions.AddFlag("ImportLibrary", imLib); + } linkOptions.AddFlag("ProgramDataBaseFile", pdb); // A Windows Runtime component uses internal .NET metadata, @@ -3882,7 +3944,8 @@ void cmVisualStudio10TargetGenerator::AddLibraries( if (managedType != cmGeneratorTarget::ManagedType::Native && this->GeneratorTarget->GetManagedType(config) != cmGeneratorTarget::ManagedType::Native && - l.Target->IsImported()) { + l.Target->IsImported() && + l.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { auto location = l.Target->GetFullPath(config); if (!location.empty()) { ConvertToWindowsSlash(location); @@ -4023,8 +4086,7 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups(Elem& e0) // output manifest flags <Manifest></Manifest> this->WriteManifestOptions(e1, c); if (this->NsightTegra && - this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE && - this->GeneratorTarget->GetPropertyAsBool("ANDROID_GUI")) { + this->GeneratorTarget->Target->IsAndroidGuiExecutable()) { this->WriteAntBuildOptions(e1, c); } } @@ -4110,7 +4172,7 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) Elem e1(e0, "ItemGroup"); e1.SetHasElements(); for (cmGeneratorTarget const* dt : depends) { - if (dt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + if (!dt->IsInBuildSystem()) { continue; } // skip fortran targets as they can not be processed by MSBuild @@ -4139,8 +4201,9 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0) } // Don't reference targets that don't produce any output. - if (dt->GetManagedType(this->Configurations[0]) == - cmGeneratorTarget::ManagedType::Undefined) { + if (this->Configurations.empty() || + dt->GetManagedType(this->Configurations[0]) == + cmGeneratorTarget::ManagedType::Undefined) { e2.Element("ReferenceOutputAssembly", "false"); e2.Element("CopyToOutputDirectory", "Never"); } @@ -4356,6 +4419,7 @@ void cmVisualStudio10TargetGenerator::WriteApplicationTypeSettings(Elem& e1) bool isAppContainer = false; bool const isWindowsPhone = this->GlobalGenerator->TargetsWindowsPhone(); bool const isWindowsStore = this->GlobalGenerator->TargetsWindowsStore(); + bool const isAndroid = this->GlobalGenerator->TargetsAndroid(); std::string const& rev = this->GlobalGenerator->GetApplicationTypeRevision(); if (isWindowsPhone || isWindowsStore) { e1.Element("ApplicationType", @@ -4393,13 +4457,19 @@ void cmVisualStudio10TargetGenerator::WriteApplicationTypeSettings(Elem& e1) this->Name + "_$(Configuration)_$(Platform).xap"); } } + } else if (isAndroid) { + e1.Element("ApplicationType", "Android"); + e1.Element("ApplicationTypeRevision", + gg->GetAndroidApplicationTypeRevision()); } if (isAppContainer) { e1.Element("AppContainerApplication", "true"); - } else if (this->Platform == "ARM64") { - e1.Element("WindowsSDKDesktopARM64Support", "true"); - } else if (this->Platform == "ARM") { - e1.Element("WindowsSDKDesktopARMSupport", "true"); + } else if (!isAndroid) { + if (this->Platform == "ARM64") { + e1.Element("WindowsSDKDesktopARM64Support", "true"); + } else if (this->Platform == "ARM") { + e1.Element("WindowsSDKDesktopARMSupport", "true"); + } } std::string const& targetPlatformVersion = gg->GetWindowsTargetPlatformVersion(); diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 7c71de3dc5..35dbba8121 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVisualStudioTargetGenerator_h -#define cmVisualStudioTargetGenerator_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -70,6 +69,7 @@ private: void WriteExtraSource(Elem& e1, cmSourceFile const* sf); void WriteNsightTegraConfigurationValues(Elem& e1, std::string const& config); + void WriteAndroidConfigurationValues(Elem& e1, std::string const& config); void WriteSource(Elem& e2, cmSourceFile const* sf); void WriteExcludeFromBuild(Elem& e2, std::vector<size_t> const& exclude_configs); @@ -215,6 +215,7 @@ private: bool MSTools; bool Managed; bool NsightTegra; + bool Android; unsigned int NsightTegraVersion[4]; bool TargetCompileAsWinRT; std::set<std::string> IPOEnabledConfigurations; @@ -257,5 +258,3 @@ private: ConfigToSettings& toolSettings); std::string GetCMakeFilePath(const char* name) const; }; - -#endif diff --git a/Source/cmVisualStudio10ToolsetOptions.h b/Source/cmVisualStudio10ToolsetOptions.h index 875a35b248..85cc2b6b2d 100644 --- a/Source/cmVisualStudio10ToolsetOptions.h +++ b/Source/cmVisualStudio10ToolsetOptions.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVisualStudio10ToolsetOptions_h -#define cmVisualStudio10ToolsetOptions_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -30,4 +29,3 @@ public: std::string GetToolsetName(std::string const& name, std::string const& toolset) const; }; -#endif diff --git a/Source/cmVisualStudioGeneratorOptions.h b/Source/cmVisualStudioGeneratorOptions.h index f9b50a7a3c..b123019cb3 100644 --- a/Source/cmVisualStudioGeneratorOptions.h +++ b/Source/cmVisualStudioGeneratorOptions.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVisualStudioGeneratorOptions_h -#define cmVisualStudioGeneratorOptions_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -100,5 +99,3 @@ private: FlagValue TakeFlag(std::string const& key); }; - -#endif diff --git a/Source/cmVisualStudioSlnData.h b/Source/cmVisualStudioSlnData.h index 5ce7d74f89..b217bd8436 100644 --- a/Source/cmVisualStudioSlnData.h +++ b/Source/cmVisualStudioSlnData.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVisualStudioSlnData_h -#define cmVisualStudioSlnData_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -50,5 +49,3 @@ private: using ProjectStringIndex = std::map<std::string, ProjectStorage::iterator>; ProjectStringIndex ProjectNameIndex; }; - -#endif diff --git a/Source/cmVisualStudioSlnParser.h b/Source/cmVisualStudioSlnParser.h index 4557cdb10a..1c3375951a 100644 --- a/Source/cmVisualStudioSlnParser.h +++ b/Source/cmVisualStudioSlnParser.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVisualStudioSlnParser_h -#define cmVisualStudioSlnParser_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -103,5 +102,3 @@ protected: bool ParseValue(const std::string& value, ParsedLine& parsedLine); }; - -#endif diff --git a/Source/cmVisualStudioWCEPlatformParser.h b/Source/cmVisualStudioWCEPlatformParser.h index 60a66113ec..eb4e978a2c 100644 --- a/Source/cmVisualStudioWCEPlatformParser.h +++ b/Source/cmVisualStudioWCEPlatformParser.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmVisualStudioWCEPlatformParser_h -#define cmVisualStudioWCEPlatformParser_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -67,5 +66,3 @@ private: std::string VcInstallDir; std::string VsInstallDir; }; - -#endif diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx index 0d8e894f8b..327c1c7baf 100644 --- a/Source/cmWhileCommand.cxx +++ b/Source/cmWhileCommand.cxx @@ -17,6 +17,7 @@ #include "cmMakefile.h" #include "cmMessageType.h" #include "cmSystemTools.h" +#include "cmake.h" class cmWhileFunctionBlocker : public cmFunctionBlocker { @@ -53,7 +54,7 @@ cmWhileFunctionBlocker::~cmWhileFunctionBlocker() bool cmWhileFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff, cmMakefile&) const { - return lff.Arguments.empty() || lff.Arguments == this->Args; + return lff.Arguments().empty() || lff.Arguments() == this->Args; } bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, @@ -66,14 +67,9 @@ bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, mf.ExpandArguments(this->Args, expandedArguments); MessageType messageType; - cmListFileContext execContext = this->GetStartingContext(); - - cmCommandContext commandContext; - commandContext.Line = execContext.Line; - commandContext.Name = execContext.Name; - - cmConditionEvaluator conditionEvaluator(mf, this->GetStartingContext(), - mf.GetBacktrace(commandContext)); + cmListFileBacktrace whileBT = + mf.GetBacktrace().Push(this->GetStartingContext()); + cmConditionEvaluator conditionEvaluator(mf, whileBT); bool isTrue = conditionEvaluator.IsTrue(expandedArguments, errorString, messageType); @@ -90,7 +86,7 @@ bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, err += "("; err += errorString; err += ")."; - mf.IssueMessage(messageType, err); + mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT); if (messageType == MessageType::FATAL_ERROR) { cmSystemTools::SetFatalErrorOccured(); return true; diff --git a/Source/cmWhileCommand.h b/Source/cmWhileCommand.h index beca652c6a..5b8f0783cf 100644 --- a/Source/cmWhileCommand.h +++ b/Source/cmWhileCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWhileCommand_h -#define cmWhileCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -13,5 +12,3 @@ struct cmListFileArgument; /// \brief Starts a while loop bool cmWhileCommand(std::vector<cmListFileArgument> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmWorkerPool.h b/Source/cmWorkerPool.h index 91799223a0..0fb6707244 100644 --- a/Source/cmWorkerPool.h +++ b/Source/cmWorkerPool.h @@ -1,7 +1,6 @@ /* 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 +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -221,5 +220,3 @@ private: unsigned int ThreadCount_ = 1; std::unique_ptr<cmWorkerPoolInternal> Int_; }; - -#endif diff --git a/Source/cmWorkingDirectory.h b/Source/cmWorkingDirectory.h index 4c7576de5c..c8adea980f 100644 --- a/Source/cmWorkingDirectory.h +++ b/Source/cmWorkingDirectory.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWorkingDirectory_h -#define cmWorkingDirectory_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -43,5 +42,3 @@ private: std::string OldDir; int ResultCode; }; - -#endif diff --git a/Source/cmWriteFileCommand.h b/Source/cmWriteFileCommand.h index 3e0e043760..0225e4f58e 100644 --- a/Source/cmWriteFileCommand.h +++ b/Source/cmWriteFileCommand.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmWriteFileCommand_h -#define cmWriteFileCommand_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -16,5 +15,3 @@ class cmExecutionStatus; */ bool cmWriteFileCommand(std::vector<std::string> const& args, cmExecutionStatus& status); - -#endif diff --git a/Source/cmXCode21Object.cxx b/Source/cmXCode21Object.cxx index 6b133a9e89..9b0dc58b20 100644 --- a/Source/cmXCode21Object.cxx +++ b/Source/cmXCode21Object.cxx @@ -4,11 +4,12 @@ #include <ostream> #include <string> +#include <utility> #include "cmSystemTools.h" -cmXCode21Object::cmXCode21Object(PBXType ptype, Type type) - : cmXCodeObject(ptype, type) +cmXCode21Object::cmXCode21Object(PBXType ptype, Type type, std::string id) + : cmXCodeObject(ptype, type, std::move(id)) { this->Version = 21; } @@ -16,7 +17,7 @@ cmXCode21Object::cmXCode21Object(PBXType ptype, Type type) void cmXCode21Object::PrintComment(std::ostream& out) { if (this->Comment.empty()) { - cmXCodeObject* n = this->GetObject("name"); + cmXCodeObject* n = this->GetAttribute("name"); if (n) { this->Comment = n->GetString(); cmSystemTools::ReplaceString(this->Comment, "\"", ""); diff --git a/Source/cmXCode21Object.h b/Source/cmXCode21Object.h index 76fad23fd2..f3fc43898d 100644 --- a/Source/cmXCode21Object.h +++ b/Source/cmXCode21Object.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmXCode21Object_h -#define cmXCode21Object_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -14,11 +13,10 @@ class cmXCode21Object : public cmXCodeObject { public: - cmXCode21Object(PBXType ptype, Type type); + cmXCode21Object(PBXType ptype, Type type, std::string id); void PrintComment(std::ostream&) override; static void PrintList(std::vector<std::unique_ptr<cmXCodeObject>> const&, std::ostream& out, PBXType t); static void PrintList(std::vector<std::unique_ptr<cmXCodeObject>> const&, std::ostream& out); }; -#endif diff --git a/Source/cmXCodeObject.cxx b/Source/cmXCodeObject.cxx index b301ab1213..d5c52756b1 100644 --- a/Source/cmXCodeObject.cxx +++ b/Source/cmXCodeObject.cxx @@ -40,7 +40,7 @@ cmXCodeObject::~cmXCodeObject() this->Version = 15; } -cmXCodeObject::cmXCodeObject(PBXType ptype, Type type) +cmXCodeObject::cmXCodeObject(PBXType ptype, Type type, std::string id) { this->Version = 15; this->Target = nullptr; @@ -48,27 +48,7 @@ cmXCodeObject::cmXCodeObject(PBXType ptype, Type type) this->IsA = ptype; - if (type == OBJECT) { - // Set the Id of an Xcode object to a unique string for each instance. - // However the Xcode user file references certain Ids: for those cases, - // override the generated Id using SetId(). - // - char cUuid[40] = { 0 }; - CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); - CFStringRef s = CFUUIDCreateString(kCFAllocatorDefault, uuid); - CFStringGetCString(s, cUuid, sizeof(cUuid), kCFStringEncodingUTF8); - this->Id = cUuid; - CFRelease(s); - CFRelease(uuid); - } else { - this->Id = - "Temporary cmake object, should not be referred to in Xcode file"; - } - - cmSystemTools::ReplaceString(this->Id, "-", ""); - if (this->Id.size() > 24) { - this->Id = this->Id.substr(0, 24); - } + this->Id = std::move(id); this->TypeValue = type; if (this->TypeValue == OBJECT) { diff --git a/Source/cmXCodeObject.h b/Source/cmXCodeObject.h index 24ecaa22c6..ac5be3f9e3 100644 --- a/Source/cmXCodeObject.h +++ b/Source/cmXCodeObject.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmXCodeObject_h -#define cmXCodeObject_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -58,7 +57,7 @@ public: }; static const char* PBXTypeNames[]; virtual ~cmXCodeObject(); - cmXCodeObject(PBXType ptype, Type type); + cmXCodeObject(PBXType ptype, Type type, std::string id); Type GetType() const { return this->TypeValue; } PBXType GetIsA() const { return this->IsA; } @@ -82,6 +81,10 @@ public: void SetObject(cmXCodeObject* value) { this->Object = value; } cmXCodeObject* GetObject() { return this->Object; } void AddObject(cmXCodeObject* value) { this->List.push_back(value); } + void PrependObject(cmXCodeObject* value) + { + this->List.insert(this->List.begin(), value); + } bool HasObject(cmXCodeObject* o) const { return cm::contains(this->List, o); @@ -107,7 +110,7 @@ public: void SetTarget(cmGeneratorTarget* t) { this->Target = t; } const std::string& GetComment() const { return this->Comment; } bool HasComment() const { return (!this->Comment.empty()); } - cmXCodeObject* GetObject(const char* name) const + cmXCodeObject* GetAttribute(const char* name) const { auto const i = this->ObjectAttributes.find(name); if (i != this->ObjectAttributes.end()) { @@ -167,4 +170,3 @@ protected: std::map<std::string, StringVec> DependTargets; std::map<std::string, cmXCodeObject*> ObjectAttributes; }; -#endif diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx index f4c2f2d4c7..55e941d947 100644 --- a/Source/cmXCodeScheme.cxx +++ b/Source/cmXCodeScheme.cxx @@ -33,7 +33,7 @@ void cmXCodeScheme::WriteXCodeSharedScheme(const std::string& xcProjDir, // Create shared scheme sub-directory tree // std::string xcodeSchemeDir = cmStrCat(xcProjDir, "/xcshareddata/xcschemes"); - cmSystemTools::MakeDirectory(xcodeSchemeDir.c_str()); + cmSystemTools::MakeDirectory(xcodeSchemeDir); std::string xcodeSchemeFile = cmStrCat(xcodeSchemeDir, '/', this->TargetName, ".xcscheme"); @@ -324,8 +324,7 @@ bool cmXCodeScheme::WriteLaunchActionBooleanAttribute( bool defaultValue) { cmProp property = Target->GetTarget()->GetProperty(varName); - bool isOn = - (property == nullptr && defaultValue) || (property && cmIsOn(*property)); + bool isOn = (property == nullptr && defaultValue) || cmIsOn(property); if (isOn) { xout.Attribute(attrName.c_str(), "YES"); diff --git a/Source/cmXCodeScheme.h b/Source/cmXCodeScheme.h index da4085669f..11f043e93c 100644 --- a/Source/cmXCodeScheme.h +++ b/Source/cmXCodeScheme.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmXCodeScheme_h -#define cmXCodeScheme_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -74,5 +73,3 @@ private: static bool IsExecutable(const cmXCodeObject* target); }; - -#endif diff --git a/Source/cmXMLParser.h b/Source/cmXMLParser.h index 1bc8d64693..7e805d7899 100644 --- a/Source/cmXMLParser.h +++ b/Source/cmXMLParser.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmXMLParser_h -#define cmXMLParser_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -107,5 +106,3 @@ protected: friend void cmXMLParserEndElement(void*, const char*); friend void cmXMLParserCharacterDataHandler(void*, const char*, int); }; - -#endif diff --git a/Source/cmXMLSafe.h b/Source/cmXMLSafe.h index 9aaf2d1578..9b4c539e5b 100644 --- a/Source/cmXMLSafe.h +++ b/Source/cmXMLSafe.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmXMLSafe_h -#define cmXMLSafe_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -33,5 +32,3 @@ private: bool DoQuotes; friend std::ostream& operator<<(std::ostream&, cmXMLSafe const&); }; - -#endif diff --git a/Source/cmXMLWriter.h b/Source/cmXMLWriter.h index 00ea08cefe..a16c4c8717 100644 --- a/Source/cmXMLWriter.h +++ b/Source/cmXMLWriter.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmXMLWiter_h -#define cmXMLWiter_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -196,5 +195,3 @@ public: private: cmXMLWriter& xmlwr; }; - -#endif diff --git a/Source/cm_codecvt.hxx b/Source/cm_codecvt.hxx index b2cb9e6a35..1860211a15 100644 --- a/Source/cm_codecvt.hxx +++ b/Source/cm_codecvt.hxx @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cm_codecvt_hxx -#define cm_codecvt_hxx +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -62,5 +61,3 @@ private: #endif }; - -#endif diff --git a/Source/cm_get_date.h b/Source/cm_get_date.h index 38a690ee05..65722df3f5 100644 --- a/Source/cm_get_date.h +++ b/Source/cm_get_date.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cm_get_date_h -#define cm_get_date_h +#pragma once #include <time.h> /* NOLINT(modernize-deprecated-headers) */ @@ -15,5 +14,3 @@ time_t cm_get_date(time_t now, const char* str); #ifdef __cplusplus } /* extern "C" */ #endif - -#endif diff --git a/Source/cm_sys_stat.h b/Source/cm_sys_stat.h index 9194286340..c4e3d84b54 100644 --- a/Source/cm_sys_stat.h +++ b/Source/cm_sys_stat.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cm_sys_stat_h -#define cm_sys_stat_h +#pragma once #if defined(_MSC_VER) using mode_t = unsigned short; @@ -10,5 +9,3 @@ using mode_t = unsigned short; #include <sys/types.h> // include sys/stat.h after sys/types.h #include <sys/stat.h> // IWYU pragma: export - -#endif diff --git a/Source/cm_utf8.h b/Source/cm_utf8.h index 27dc5591f9..fa9ed3a043 100644 --- a/Source/cm_utf8.h +++ b/Source/cm_utf8.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cm_utf8_h -#define cm_utf8_h +#pragma once #ifdef __cplusplus extern "C" { @@ -20,5 +19,3 @@ int cm_utf8_is_valid(const char* s); #ifdef __cplusplus } /* extern "C" */ #endif - -#endif diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 162e807f14..e655634ef3 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -13,6 +13,7 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW) # include <cm/iterator> @@ -27,6 +28,7 @@ #include "cm_sys_stat.h" +#include "cmCMakePresetsFile.h" #include "cmCommands.h" #include "cmDocumentation.h" #include "cmDocumentationEntry.h" @@ -64,10 +66,6 @@ # include "cmVariableWatch.h" #endif -#if !defined(CMAKE_BOOTSTRAP) -# define CMAKE_USE_ECLIPSE -#endif - #if defined(__MINGW32__) && defined(CMAKE_BOOTSTRAP) # define CMAKE_BOOT_MINGW #endif @@ -97,20 +95,21 @@ #if defined(CMAKE_USE_WMAKE) # include "cmGlobalWatcomWMakeGenerator.h" #endif -#include "cmGlobalUnixMakefileGenerator3.h" #if !defined(CMAKE_BOOTSTRAP) # include "cmGlobalNinjaGenerator.h" +# include "cmGlobalUnixMakefileGenerator3.h" +#elif defined(CMAKE_BOOTSTRAP_MAKEFILES) +# include "cmGlobalUnixMakefileGenerator3.h" +#elif defined(CMAKE_BOOTSTRAP_NINJA) +# include "cmGlobalNinjaGenerator.h" #endif -#include "cmExtraCodeLiteGenerator.h" -#if !defined(CMAKE_BOOT_MINGW) +#if !defined(CMAKE_BOOTSTRAP) # include "cmExtraCodeBlocksGenerator.h" -#endif -#include "cmExtraKateGenerator.h" -#include "cmExtraSublimeTextGenerator.h" - -#ifdef CMAKE_USE_ECLIPSE +# include "cmExtraCodeLiteGenerator.h" # include "cmExtraEclipseCDT4Generator.h" +# include "cmExtraKateGenerator.h" +# include "cmExtraSublimeTextGenerator.h" #endif #if defined(__linux__) || defined(_WIN32) @@ -201,13 +200,14 @@ cmake::cmake(Role role, cmState::Mode mode) }; // The "c" extension MUST precede the "C" extension. - setupExts(this->SourceFileExtensions, + setupExts(this->CLikeSourceFileExtensions, { "c", "C", "c++", "cc", "cpp", "cxx", "cu", "m", "M", "mm" }); setupExts(this->HeaderFileExtensions, { "h", "hh", "h++", "hm", "hpp", "hxx", "in", "txx" }); setupExts(this->CudaFileExtensions, { "cu" }); setupExts(this->FortranFileExtensions, { "f", "F", "for", "f77", "f90", "f95", "f03" }); + setupExts(this->ISPCFileExtensions, { "ispc" }); } } @@ -286,8 +286,101 @@ void cmake::CleanupCommandsAndMacros() this->CurrentSnapshot = this->State->Reset(); this->State->RemoveUserDefinedCommands(); this->CurrentSnapshot.SetDefaultDefinitions(); + // FIXME: InstalledFiles probably belongs in the global generator. + this->InstalledFiles.clear(); +} + +#ifndef CMAKE_BOOTSTRAP +void cmake::SetWarningFromPreset(const std::string& name, + const cm::optional<bool>& warning, + const cm::optional<bool>& error) +{ + if (warning) { + if (*warning) { + this->DiagLevels[name] = std::max(this->DiagLevels[name], DIAG_WARN); + } else { + this->DiagLevels[name] = DIAG_IGNORE; + } + } + if (error) { + if (*error) { + this->DiagLevels[name] = DIAG_ERROR; + } else { + this->DiagLevels[name] = std::min(this->DiagLevels[name], DIAG_WARN); + } + } +} + +void cmake::ProcessPresetVariables() +{ + for (auto const& var : this->UnprocessedPresetVariables) { + if (!var.second) { + continue; + } + cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED; + if (!var.second->Type.empty()) { + type = cmState::StringToCacheEntryType(var.second->Type); + } + this->ProcessCacheArg(var.first, var.second->Value, type); + } +} + +void cmake::PrintPresetVariables() +{ + bool first = true; + for (auto const& var : this->UnprocessedPresetVariables) { + if (!var.second) { + continue; + } + cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED; + if (!var.second->Type.empty()) { + type = cmState::StringToCacheEntryType(var.second->Type); + } + if (first) { + std::cout << "Preset CMake variables:\n\n"; + first = false; + } + std::cout << " " << var.first; + if (type != cmStateEnums::UNINITIALIZED) { + std::cout << ':' << cmState::CacheEntryTypeToString(type); + } + std::cout << "=\"" << var.second->Value << "\"\n"; + } + if (!first) { + std::cout << '\n'; + } + this->UnprocessedPresetVariables.clear(); +} + +void cmake::ProcessPresetEnvironment() +{ + for (auto const& var : this->UnprocessedPresetEnvironment) { + if (var.second) { + cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second)); + } + } } +void cmake::PrintPresetEnvironment() +{ + bool first = true; + for (auto const& var : this->UnprocessedPresetEnvironment) { + if (!var.second) { + continue; + } + if (first) { + std::cout << "Preset environment variables:\n\n"; + first = false; + } + std::cout << " " << var.first << "=\"" << *var.second << "\"\n"; + } + if (!first) { + std::cout << '\n'; + } + this->UnprocessedPresetEnvironment.clear(); +} +#endif + // Parse the args bool cmake::SetCacheArgs(const std::vector<std::string>& args) { @@ -310,28 +403,10 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) std::string value; cmStateEnums::CacheEntryType type = cmStateEnums::UNINITIALIZED; if (cmState::ParseCacheEntry(entry, var, value, type)) { - // The value is transformed if it is a filepath for example, so - // we can't compare whether the value is already in the cache until - // after we call AddCacheEntry. - bool haveValue = false; - std::string cachedValue; - if (this->WarnUnusedCli) { - if (cmProp v = this->State->GetInitializedCacheValue(var)) { - haveValue = true; - cachedValue = *v; - } - } - - this->AddCacheEntry(var, value.c_str(), - "No help, variable specified on the command line.", - type); - - if (this->WarnUnusedCli) { - if (!haveValue || - cachedValue != *this->State->GetInitializedCacheValue(var)) { - this->WatchUnusedCli(var); - } - } +#ifndef CMAKE_BOOTSTRAP + this->UnprocessedPresetVariables.erase(var); +#endif + this->ProcessCacheArg(var, value, type); } else { cmSystemTools::Error("Parse error in command line argument: " + arg + "\n" + "Should be: VAR:type=value\n"); @@ -411,6 +486,9 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) // now remove them from the cache for (std::string const& currentEntry : entriesToDelete) { +#ifndef CMAKE_BOOTSTRAP + this->UnprocessedPresetVariables.erase(currentEntry); +#endif this->State->RemoveCacheEntry(currentEntry); } } else if (cmHasLiteralPrefix(arg, "-C")) { @@ -464,6 +542,33 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args) return true; } +void cmake::ProcessCacheArg(const std::string& var, const std::string& value, + cmStateEnums::CacheEntryType type) +{ + // The value is transformed if it is a filepath for example, so + // we can't compare whether the value is already in the cache until + // after we call AddCacheEntry. + bool haveValue = false; + std::string cachedValue; + if (this->WarnUnusedCli) { + if (cmProp v = this->State->GetInitializedCacheValue(var)) { + haveValue = true; + cachedValue = *v; + } + } + + this->AddCacheEntry(var, value.c_str(), + "No help, variable specified on the command line.", + type); + + if (this->WarnUnusedCli) { + if (!haveValue || + cachedValue != *this->State->GetInitializedCacheValue(var)) { + this->WatchUnusedCli(var); + } + } +} + void cmake::ReadListFile(const std::vector<std::string>& args, const std::string& path) { @@ -624,9 +729,12 @@ void cmake::SetArgs(const std::vector<std::string>& args) { bool haveToolset = false; bool havePlatform = false; + bool haveBArg = false; #if !defined(CMAKE_BOOTSTRAP) std::string profilingFormat; std::string profilingOutput; + std::string presetName; + bool listPresets = false; #endif for (unsigned int i = 1; i < args.size(); ++i) { std::string const& arg = args[i]; @@ -670,6 +778,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) path = cmSystemTools::CollapseFullPath(path); cmSystemTools::ConvertToUnixSlashes(path); this->SetHomeOutputDirectory(path); + haveBArg = true; } else if ((i < args.size() - 2) && cmHasLiteralPrefix(arg, "--check-build-system")) { this->CheckBuildSystemArgument = args[++i]; @@ -780,8 +889,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) std::cout << "Warn about uninitialized values.\n"; this->SetWarnUninitialized(true); } else if (cmHasLiteralPrefix(arg, "--warn-unused-vars")) { - std::cout << "Finding unused variables.\n"; - this->SetWarnUnused(true); + // Option was removed. } else if (cmHasLiteralPrefix(arg, "--no-warn-unused-cli")) { std::cout << "Not searching for unused variables given on the " << "command line.\n"; @@ -833,32 +941,29 @@ void cmake::SetArgs(const std::vector<std::string>& args) } value = args[i]; } - auto gen = this->CreateGlobalGenerator(value); - if (!gen) { - std::string kdevError; - if (value.find("KDevelop3", 0) != std::string::npos) { - kdevError = "\nThe KDevelop3 generator is not supported anymore."; - } - - cmSystemTools::Error( - cmStrCat("Could not create named generator ", value, kdevError)); - this->PrintGeneratorList(); + if (!this->CreateAndSetGlobalGenerator(value, true)) { return; } - this->SetGlobalGenerator(std::move(gen)); #if !defined(CMAKE_BOOTSTRAP) - } else if (cmHasLiteralPrefix(arg, "--profiling-format")) { + } else if (cmHasLiteralPrefix(arg, "--profiling-format=")) { profilingFormat = arg.substr(strlen("--profiling-format=")); if (profilingFormat.empty()) { cmSystemTools::Error("No format specified for --profiling-format"); } - } else if (cmHasLiteralPrefix(arg, "--profiling-output")) { + } else if (cmHasLiteralPrefix(arg, "--profiling-output=")) { profilingOutput = arg.substr(strlen("--profiling-output=")); profilingOutput = cmSystemTools::CollapseFullPath(profilingOutput); cmSystemTools::ConvertToUnixSlashes(profilingOutput); if (profilingOutput.empty()) { cmSystemTools::Error("No path specified for --profiling-output"); } + } else if (cmHasLiteralPrefix(arg, "--preset=")) { + presetName = arg.substr(strlen("--preset=")); + if (presetName.empty()) { + cmSystemTools::Error("No preset specified for --preset"); + } + } else if (cmHasLiteralPrefix(arg, "--list-presets")) { + listPresets = true; #endif } // no option assume it is the path to the source or an existing build @@ -902,9 +1007,15 @@ void cmake::SetArgs(const std::vector<std::string>& args) const bool haveSourceDir = !this->GetHomeDirectory().empty(); const bool haveBinaryDir = !this->GetHomeOutputDirectory().empty(); + const bool havePreset = +#ifdef CMAKE_BOOTSTRAP + false; +#else + !presetName.empty(); +#endif if (this->CurrentWorkingMode == cmake::NORMAL_MODE && !haveSourceDir && - !haveBinaryDir) { + !haveBinaryDir && !havePreset) { this->IssueMessage( MessageType::WARNING, "No source or binary directory provided. Both will be assumed to be " @@ -918,6 +1029,95 @@ void cmake::SetArgs(const std::vector<std::string>& args) if (!haveBinaryDir) { this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory()); } + +#if !defined(CMAKE_BOOTSTRAP) + if (listPresets || !presetName.empty()) { + cmCMakePresetsFile settingsFile; + auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory()); + if (result != cmCMakePresetsFile::ReadFileResult::READ_OK) { + cmSystemTools::Error( + cmStrCat("Could not read presets from ", this->GetHomeDirectory(), + ": ", cmCMakePresetsFile::ResultToString(result))); + return; + } + if (listPresets) { + this->PrintPresetList(settingsFile); + return; + } + auto preset = settingsFile.Presets.find(presetName); + if (preset == settingsFile.Presets.end()) { + cmSystemTools::Error(cmStrCat("No such preset in ", + this->GetHomeDirectory(), ": \"", + presetName, '"')); + this->PrintPresetList(settingsFile); + return; + } + if (preset->second.Unexpanded.Hidden) { + cmSystemTools::Error(cmStrCat("Cannot use hidden preset in ", + this->GetHomeDirectory(), ": \"", + presetName, '"')); + this->PrintPresetList(settingsFile); + return; + } + auto const& expandedPreset = preset->second.Expanded; + if (!expandedPreset) { + cmSystemTools::Error(cmStrCat("Could not evaluate preset \"", + preset->second.Unexpanded.Name, + "\": Invalid macro expansion")); + return; + } + + if (!this->State->IsCacheLoaded() && !haveBArg) { + this->SetHomeOutputDirectory(expandedPreset->BinaryDir); + } + if (!this->GlobalGenerator) { + if (!this->CreateAndSetGlobalGenerator(expandedPreset->Generator, + false)) { + return; + } + } + this->UnprocessedPresetVariables = expandedPreset->CacheVariables; + this->UnprocessedPresetEnvironment = expandedPreset->Environment; + + if (!expandedPreset->ArchitectureStrategy || + expandedPreset->ArchitectureStrategy == + cmCMakePresetsFile::ArchToolsetStrategy::Set) { + if (!this->GeneratorPlatformSet) { + this->SetGeneratorPlatform(expandedPreset->Architecture); + } + } + if (!expandedPreset->ToolsetStrategy || + expandedPreset->ToolsetStrategy == + cmCMakePresetsFile::ArchToolsetStrategy::Set) { + if (!this->GeneratorToolsetSet) { + this->SetGeneratorToolset(expandedPreset->Toolset); + } + } + + this->SetWarningFromPreset("dev", expandedPreset->WarnDev, + expandedPreset->ErrorDev); + this->SetWarningFromPreset("deprecated", expandedPreset->WarnDeprecated, + expandedPreset->ErrorDeprecated); + if (expandedPreset->WarnUninitialized == true) { + this->SetWarnUninitialized(true); + } + if (expandedPreset->WarnUnusedCli == false) { + this->SetWarnUnusedCli(false); + } + if (expandedPreset->WarnSystemVars == true) { + this->SetCheckSystemVars(true); + } + if (expandedPreset->DebugOutput == true) { + this->SetDebugOutputOn(true); + } + if (expandedPreset->DebugTryCompile == true) { + this->DebugTryCompileOn(); + } + if (expandedPreset->DebugFind == true) { + this->SetDebugFindOutputOn(true); + } + } +#endif } cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr) @@ -986,7 +1186,7 @@ void cmake::PrintTraceFormatVersion() Json::StreamWriterBuilder builder; builder["indentation"] = ""; version["major"] = 1; - version["minor"] = 0; + version["minor"] = 1; val["version"] = version; msg = Json::writeString(builder, val); #endif @@ -1137,13 +1337,9 @@ void cmake::AddDefaultExtraGenerators() #if !defined(CMAKE_BOOTSTRAP) this->ExtraGenerators.push_back(cmExtraCodeBlocksGenerator::GetFactory()); this->ExtraGenerators.push_back(cmExtraCodeLiteGenerator::GetFactory()); - this->ExtraGenerators.push_back(cmExtraSublimeTextGenerator::GetFactory()); - this->ExtraGenerators.push_back(cmExtraKateGenerator::GetFactory()); - -# ifdef CMAKE_USE_ECLIPSE this->ExtraGenerators.push_back(cmExtraEclipseCDT4Generator::GetFactory()); -# endif - + this->ExtraGenerators.push_back(cmExtraKateGenerator::GetFactory()); + this->ExtraGenerators.push_back(cmExtraSublimeTextGenerator::GetFactory()); #endif } @@ -1224,7 +1420,7 @@ createExtraGenerator( } std::unique_ptr<cmGlobalGenerator> cmake::CreateGlobalGenerator( - const std::string& gname) + const std::string& gname, bool allowArch) { std::pair<std::unique_ptr<cmExternalMakefileProjectGenerator>, std::string> extra = createExtraGenerator(this->ExtraGenerators, gname); @@ -1234,7 +1430,7 @@ std::unique_ptr<cmGlobalGenerator> cmake::CreateGlobalGenerator( std::unique_ptr<cmGlobalGenerator> generator; for (const auto& g : this->Generators) { - generator = g->CreateGlobalGenerator(name, this); + generator = g->CreateGlobalGenerator(name, allowArch, this); if (generator) { break; } @@ -1247,6 +1443,78 @@ std::unique_ptr<cmGlobalGenerator> cmake::CreateGlobalGenerator( return generator; } +bool cmake::CreateAndSetGlobalGenerator(const std::string& name, + bool allowArch) +{ + auto gen = this->CreateGlobalGenerator(name, allowArch); + if (!gen) { + std::string kdevError; + std::string vsError; + if (name.find("KDevelop3", 0) != std::string::npos) { + kdevError = "\nThe KDevelop3 generator is not supported anymore."; + } + if (!allowArch && cmHasLiteralPrefix(name, "Visual Studio ") && + name.length() >= cmStrLen("Visual Studio xx xxxx ")) { + vsError = "\nUsing platforms in Visual Studio generator names is not " + "supported in CMakePresets.json."; + } + + cmSystemTools::Error( + cmStrCat("Could not create named generator ", name, kdevError, vsError)); + this->PrintGeneratorList(); + return false; + } + + this->SetGlobalGenerator(std::move(gen)); + return true; +} + +#ifndef CMAKE_BOOTSTRAP +void cmake::PrintPresetList(const cmCMakePresetsFile& file) const +{ + std::vector<GeneratorInfo> generators; + this->GetRegisteredGenerators(generators, false); + + std::vector<cmCMakePresetsFile::UnexpandedPreset> presets; + for (auto const& p : file.PresetOrder) { + auto const& preset = file.Presets.at(p); + if (!preset.Unexpanded.Hidden && preset.Expanded && + std::find_if(generators.begin(), generators.end(), + [&preset](const GeneratorInfo& info) { + return info.name == preset.Unexpanded.Generator; + }) != generators.end()) { + presets.push_back(preset.Unexpanded); + } + } + + if (presets.empty()) { + return; + } + + std::cout << "Available presets:\n\n"; + + auto longestPresetName = + std::max_element(presets.begin(), presets.end(), + [](const cmCMakePresetsFile::UnexpandedPreset& a, + const cmCMakePresetsFile::UnexpandedPreset& b) { + return a.Name.length() < b.Name.length(); + }); + auto longestLength = longestPresetName->Name.length(); + + for (auto const& preset : presets) { + std::cout << " \"" << preset.Name << '"'; + auto const& description = preset.DisplayName; + if (!description.empty()) { + for (std::size_t i = 0; i < longestLength - preset.Name.length(); ++i) { + std::cout << ' '; + } + std::cout << " - " << description; + } + std::cout << '\n'; + } +} +#endif + void cmake::SetHomeDirectory(const std::string& dir) { this->State->SetSourceDirectory(dir); @@ -1383,7 +1651,7 @@ struct SaveCacheEntry int cmake::HandleDeleteCacheVariables(const std::string& var) { - std::vector<std::string> argsSplit = cmExpandedList(std::string(var), true); + std::vector<std::string> argsSplit = cmExpandedList(var, true); // erase the property to avoid infinite recursion this->State->SetGlobalProperty("__CMAKE_DELETE_CACHE_CHANGE_VARS_", ""); if (this->State->GetIsInTryCompile()) { @@ -1402,8 +1670,13 @@ int cmake::HandleDeleteCacheVariables(const std::string& var) save.key = *i; warning << *i << "= "; i++; - save.value = *i; - warning << *i << "\n"; + if (i != argsSplit.end()) { + save.value = *i; + warning << *i << "\n"; + } else { + warning << "\n"; + i -= 1; + } cmProp existingValue = this->State->GetCacheEntryValue(save.key); if (existingValue) { save.type = this->State->GetCacheEntryType(save.key); @@ -1411,6 +1684,8 @@ int cmake::HandleDeleteCacheVariables(const std::string& var) this->State->GetCacheEntryProperty(save.key, "HELPSTRING")) { save.help = *help; } + } else { + save.type = cmStateEnums::CacheEntryType::UNINITIALIZED; } saved.push_back(std::move(save)); } @@ -1499,10 +1774,10 @@ int cmake::Configure() this->Messenger->SetSuppressDeprecatedWarnings(value && cmIsOff(*value)); value = this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED"); - this->Messenger->SetDeprecatedWarningsAsErrors(value && cmIsOn(*value)); + this->Messenger->SetDeprecatedWarningsAsErrors(cmIsOn(value)); value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_WARNINGS"); - this->Messenger->SetSuppressDevWarnings(value && cmIsOn(*value)); + this->Messenger->SetSuppressDevWarnings(cmIsOn(value)); value = this->State->GetCacheEntryValue("CMAKE_SUPPRESS_DEVELOPER_ERRORS"); this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(*value)); @@ -1757,6 +2032,9 @@ std::unique_ptr<cmGlobalGenerator> cmake::EvaluateDefaultGlobalGenerator() gen = cm::make_unique<cmGlobalNMakeMakefileGenerator>(this); } return std::unique_ptr<cmGlobalGenerator>(std::move(gen)); +#elif defined(CMAKE_BOOTSTRAP_NINJA) + return std::unique_ptr<cmGlobalGenerator>( + cm::make_unique<cmGlobalNinjaGenerator>(this)); #else return std::unique_ptr<cmGlobalGenerator>( cm::make_unique<cmGlobalUnixMakefileGenerator3>(this)); @@ -1800,6 +2078,9 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure) if (cmSystemTools::GetErrorOccuredFlag()) { return -1; } + if (this->GetWorkingMode() == HELP_MODE) { + return 0; + } // Log the trace format version to the desired output if (this->GetTrace()) { @@ -1828,11 +2109,19 @@ int cmake::Run(const std::vector<std::string>& args, bool noconfigure) this->AddCMakePaths(); } +#ifndef CMAKE_BOOTSTRAP + this->ProcessPresetVariables(); + this->ProcessPresetEnvironment(); +#endif // Add any cache args if (!this->SetCacheArgs(args)) { cmSystemTools::Error("Problem processing arguments. Aborting.\n"); return -1; } +#ifndef CMAKE_BOOTSTRAP + this->PrintPresetVariables(); + this->PrintPresetEnvironment(); +#endif // In script mode we terminate after running the script. if (this->GetWorkingMode() != NORMAL_MODE) { @@ -1970,6 +2259,19 @@ void cmake::AddGlobCacheEntry(bool recurse, bool listDirectories, backtrace); } +std::vector<std::string> cmake::GetAllExtensions() const +{ + std::vector<std::string> allExt = this->CLikeSourceFileExtensions.ordered; + allExt.insert(allExt.end(), this->HeaderFileExtensions.ordered.begin(), + this->HeaderFileExtensions.ordered.end()); + // cuda extensions are also in SourceFileExtensions so we ignore it here + allExt.insert(allExt.end(), this->FortranFileExtensions.ordered.begin(), + this->FortranFileExtensions.ordered.end()); + allExt.insert(allExt.end(), this->ISPCFileExtensions.ordered.begin(), + this->ISPCFileExtensions.ordered.end()); + return allExt; +} + std::string cmake::StripExtension(const std::string& file) const { auto dotpos = file.rfind('.'); @@ -1979,17 +2281,16 @@ std::string cmake::StripExtension(const std::string& file) const #else auto ext = cm::string_view(file).substr(dotpos + 1); #endif - if (this->IsSourceExtension(ext) || this->IsHeaderExtension(ext)) { + if (this->IsAKnownExtension(ext)) { return file.substr(0, dotpos); } } return file; } -const char* cmake::GetCacheDefinition(const std::string& name) const +cmProp cmake::GetCacheDefinition(const std::string& name) const { - cmProp p = this->State->GetInitializedCacheValue(name); - return p ? p->c_str() : nullptr; + return this->State->GetInitializedCacheValue(name); } void cmake::AddScriptingCommands() @@ -2022,13 +2323,17 @@ void cmake::AddDefaultGenerators() this->Generators.push_back(cmGlobalMSYSMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalMinGWMakefileGenerator::NewFactory()); #endif - this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory()); #if !defined(CMAKE_BOOTSTRAP) # if defined(__linux__) || defined(_WIN32) this->Generators.push_back(cmGlobalGhsMultiGenerator::NewFactory()); # endif + this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory()); this->Generators.push_back(cmGlobalNinjaGenerator::NewFactory()); this->Generators.push_back(cmGlobalNinjaMultiGenerator::NewFactory()); +#elif defined(CMAKE_BOOTSTRAP_NINJA) + this->Generators.push_back(cmGlobalNinjaGenerator::NewFactory()); +#elif defined(CMAKE_BOOTSTRAP_MAKEFILES) + this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory()); #endif #if defined(CMAKE_USE_WMAKE) this->Generators.push_back(cmGlobalWatcomWMakeGenerator::NewFactory()); @@ -2268,8 +2573,9 @@ int cmake::CheckBuildSystem() if (this->ClearBuildSystem) { // Get the generator used for this build system. - const char* genName = mf.GetDefinition("CMAKE_DEPENDS_GENERATOR"); - if (!genName || genName[0] == '\0') { + const char* genName = + cmToCStr(mf.GetDefinition("CMAKE_DEPENDS_GENERATOR")); + if (!cmNonempty(genName)) { genName = "Unix Makefiles"; } @@ -2743,9 +3049,7 @@ int cmake::Build(int jobs, const std::string& dir, } projName = *cachedProjectName; - cmProp cachedVerbose = - this->State->GetCacheEntryValue("CMAKE_VERBOSE_MAKEFILE"); - if (cachedVerbose && cmIsOn(*cachedVerbose)) { + if (cmIsOn(this->State->GetCacheEntryValue("CMAKE_VERBOSE_MAKEFILE"))) { verbose = true; } diff --git a/Source/cmake.h b/Source/cmake.h index 086ec87658..1ecf2c283a 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmake_h -#define cmake_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -28,7 +27,11 @@ #include "cmStateTypes.h" #if !defined(CMAKE_BOOTSTRAP) +# include <cm/optional> + # include <cm3p/json/value.h> + +# include "cmCMakePresetsFile.h" #endif class cmExternalMakefileProjectGeneratorFactory; @@ -89,13 +92,22 @@ public: enum WorkingMode { NORMAL_MODE, ///< Cmake runs to create project files - /** \brief Script mode (started by using -P). - * - * In script mode there is no generator and no cache. Also, - * languages are not enabled, so add_executable and things do - * nothing. - */ + + /** \brief Script mode (started by using -P). + * + * In script mode there is no generator and no cache. Also, + * languages are not enabled, so add_executable and things do + * nothing. + */ SCRIPT_MODE, + + /** \brief Help mode + * + * Used to print help for things that can only be determined after finding + * the source directory, for example, the list of presets. + */ + HELP_MODE, + /** \brief A pkg-config like mode * * In this mode cmake just searches for a package and prints the results to @@ -220,7 +232,15 @@ public: //! Create a GlobalGenerator std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name); + const std::string& name, bool allowArch = true); + + //! Create a GlobalGenerator and set it as our own + bool CreateAndSetGlobalGenerator(const std::string& name, bool allowArch); + +#ifndef CMAKE_BOOTSTRAP + //! Print list of presets + void PrintPresetList(const cmCMakePresetsFile& file) const; +#endif //! Return the global generator assigned to this instance of cmake cmGlobalGenerator* GetGlobalGenerator() @@ -264,44 +284,34 @@ public: this->GeneratorToolsetSet = true; } - const std::vector<std::string>& GetSourceExtensions() const - { - return this->SourceFileExtensions.ordered; - } - - bool IsSourceExtension(cm::string_view ext) const - { - return this->SourceFileExtensions.Test(ext); - } - - const std::vector<std::string>& GetHeaderExtensions() const + bool IsAKnownSourceExtension(cm::string_view ext) const { - return this->HeaderFileExtensions.ordered; + return this->CLikeSourceFileExtensions.Test(ext) || + this->CudaFileExtensions.Test(ext) || + this->FortranFileExtensions.Test(ext) || + this->ISPCFileExtensions.Test(ext); } - bool IsHeaderExtension(cm::string_view ext) const + bool IsACLikeSourceExtension(cm::string_view ext) const { - return this->HeaderFileExtensions.Test(ext); + return this->CLikeSourceFileExtensions.Test(ext); } - const std::vector<std::string>& GetCudaExtensions() const + bool IsAKnownExtension(cm::string_view ext) const { - return this->CudaFileExtensions.ordered; + return this->IsAKnownSourceExtension(ext) || this->IsAHeaderExtension(ext); } - bool IsCudaExtension(cm::string_view ext) const - { - return this->CudaFileExtensions.Test(ext); - } + std::vector<std::string> GetAllExtensions() const; - const std::vector<std::string>& GetFortranExtensions() const + const std::vector<std::string>& GetHeaderExtensions() const { - return this->FortranFileExtensions.ordered; + return this->HeaderFileExtensions.ordered; } - bool IsFortranExtension(cm::string_view ext) const + bool IsAHeaderExtension(cm::string_view ext) const { - return this->FortranFileExtensions.Test(ext); + return this->HeaderFileExtensions.Test(ext); } // Strips the extension (if present and known) from a filename @@ -310,7 +320,7 @@ public: /** * Given a variable name, return its value (as a string). */ - const char* GetCacheDefinition(const std::string&) const; + cmProp GetCacheDefinition(const std::string&) const; //! Add an entry into the cache void AddCacheEntry(const std::string& key, const char* value, const char* helpString, int type); @@ -340,9 +350,22 @@ public: bool GetIsInTryCompile() const; void SetIsInTryCompile(bool b); +#ifndef CMAKE_BOOTSTRAP + void SetWarningFromPreset(const std::string& name, + const cm::optional<bool>& warning, + const cm::optional<bool>& error); + void ProcessPresetVariables(); + void PrintPresetVariables(); + void ProcessPresetEnvironment(); + void PrintPresetEnvironment(); +#endif + //! Parse command line arguments that might set cache values bool SetCacheArgs(const std::vector<std::string>&); + void ProcessCacheArg(const std::string& var, const std::string& value, + cmStateEnums::CacheEntryType type); + using ProgressCallbackType = std::function<void(const std::string&, float)>; /** * Set the function used by GUIs to receive progress updates @@ -461,8 +484,6 @@ public: bool GetWarnUninitialized() { return this->WarnUninitialized; } void SetWarnUninitialized(bool b) { this->WarnUninitialized = b; } - bool GetWarnUnused() { return this->WarnUnused; } - void SetWarnUnused(bool b) { this->WarnUnused = b; } bool GetWarnUnusedCli() { return this->WarnUnusedCli; } void SetWarnUnusedCli(bool b) { this->WarnUnusedCli = b; } bool GetCheckSystemVars() { return this->CheckSystemVars; } @@ -616,7 +637,6 @@ private: TraceFormat TraceFormatVar = TRACE_HUMAN; cmGeneratedFileStream TraceFile; bool WarnUninitialized = false; - bool WarnUnused = false; bool WarnUnusedCli = true; bool CheckSystemVars = false; std::map<std::string, bool> UsedCliVariables; @@ -628,9 +648,10 @@ private: std::string CheckStampList; std::string VSSolutionFile; std::string EnvironmentGenerator; - FileExtensions SourceFileExtensions; + FileExtensions CLikeSourceFileExtensions; FileExtensions HeaderFileExtensions; FileExtensions CudaFileExtensions; + FileExtensions ISPCFileExtensions; FileExtensions FortranFileExtensions; bool ClearBuildSystem = false; bool DebugTryCompile = false; @@ -638,6 +659,12 @@ private: std::unique_ptr<cmFileTimeCache> FileTimeCache; std::string GraphVizFile; InstalledFilesMap InstalledFiles; +#ifndef CMAKE_BOOTSTRAP + std::map<std::string, cm::optional<cmCMakePresetsFile::CacheVariable>> + UnprocessedPresetVariables; + std::map<std::string, cm::optional<std::string>> + UnprocessedPresetEnvironment; +#endif #if !defined(CMAKE_BOOTSTRAP) std::unique_ptr<cmVariableWatch> VariableWatch; @@ -794,5 +821,3 @@ private: F(cuda_std_14) \ F(cuda_std_17) \ F(cuda_std_20) - -#endif diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index 7e589c0205..f7734a6e9d 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -3,11 +3,13 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <algorithm> #include <cassert> #include <cctype> #include <climits> #include <cstring> #include <iostream> +#include <sstream> #include <string> #include <utility> #include <vector> @@ -62,6 +64,8 @@ const char* cmDocumentationUsageNote[][2] = { const char* cmDocumentationOptions[][2] = { CMAKE_STANDARD_OPTIONS_TABLE, + { "--preset=<preset>", "Specify a configure preset." }, + { "--list-presets", "List available presets." }, { "-E", "CMake command mode." }, { "-L[A][H]", "List non-advanced cached variables." }, { "--build <dir>", "Build a CMake-generated project binary tree." }, @@ -69,7 +73,7 @@ const char* cmDocumentationOptions[][2] = { { "--open <dir>", "Open generated project in the associated application." }, { "-N", "View mode only." }, { "-P <file>", "Process script mode." }, - { "--find-package", "Run in pkg-config like mode." }, + { "--find-package", "Legacy pkg-config like mode. Do not use." }, { "--graphviz=[file]", "Generate graphviz of dependencies, see " "CMakeGraphVizOptions.cmake for more." }, @@ -91,7 +95,6 @@ const char* cmDocumentationOptions[][2] = { { "--trace-redirect=<file>", "Redirect trace output to a file instead of stderr." }, { "--warn-uninitialized", "Warn about uninitialized values." }, - { "--warn-unused-vars", "Warn about unused variables." }, { "--no-warn-unused-cli", "Don't warn about command line options." }, { "--check-system-vars", "Find problems with variable usage in system " @@ -253,6 +256,9 @@ int do_cmake(int ac, char const* const* av) } else if (cmHasLiteralPrefix(av[i], "--find-package")) { workingMode = cmake::FIND_PACKAGE_MODE; args.emplace_back(av[i]); + } else if (strcmp(av[i], "--list-presets") == 0) { + workingMode = cmake::HELP_MODE; + args.emplace_back(av[i]); } else { args.emplace_back(av[i]); } @@ -269,6 +275,7 @@ int do_cmake(int ac, char const* const* av) cmState::Mode mode = cmState::Unknown; switch (workingMode) { case cmake::NORMAL_MODE: + case cmake::HELP_MODE: mode = cmState::Project; break; case cmake::SCRIPT_MODE: @@ -520,6 +527,121 @@ int do_build(int ac, char const* const* av) #endif } +bool parse_default_directory_permissions(const std::string& permissions, + std::string& parsedPermissionsVar) +{ + std::vector<std::string> parsedPermissions; + enum Doing + { + DoingNone, + DoingOwner, + DoingGroup, + DoingWorld, + DoingOwnerAssignment, + DoingGroupAssignment, + DoingWorldAssignment, + }; + Doing doing = DoingNone; + + auto uniquePushBack = [&parsedPermissions](const std::string& e) { + if (std::find(parsedPermissions.begin(), parsedPermissions.end(), e) == + parsedPermissions.end()) { + parsedPermissions.push_back(e); + } + }; + + for (auto const& e : permissions) { + switch (doing) { + case DoingNone: + if (e == 'u') { + doing = DoingOwner; + } else if (e == 'g') { + doing = DoingGroup; + } else if (e == 'o') { + doing = DoingWorld; + } else { + return false; + } + break; + case DoingOwner: + if (e == '=') { + doing = DoingOwnerAssignment; + } else { + return false; + } + break; + case DoingGroup: + if (e == '=') { + doing = DoingGroupAssignment; + } else { + return false; + } + break; + case DoingWorld: + if (e == '=') { + doing = DoingWorldAssignment; + } else { + return false; + } + break; + case DoingOwnerAssignment: + if (e == 'r') { + uniquePushBack("OWNER_READ"); + } else if (e == 'w') { + uniquePushBack("OWNER_WRITE"); + } else if (e == 'x') { + uniquePushBack("OWNER_EXECUTE"); + } else if (e == ',') { + doing = DoingNone; + } else { + return false; + } + break; + case DoingGroupAssignment: + if (e == 'r') { + uniquePushBack("GROUP_READ"); + } else if (e == 'w') { + uniquePushBack("GROUP_WRITE"); + } else if (e == 'x') { + uniquePushBack("GROUP_EXECUTE"); + } else if (e == ',') { + doing = DoingNone; + } else { + return false; + } + break; + case DoingWorldAssignment: + if (e == 'r') { + uniquePushBack("WORLD_READ"); + } else if (e == 'w') { + uniquePushBack("WORLD_WRITE"); + } else if (e == 'x') { + uniquePushBack("WORLD_EXECUTE"); + } else if (e == ',') { + doing = DoingNone; + } else { + return false; + } + break; + } + } + if (doing != DoingOwnerAssignment && doing != DoingGroupAssignment && + doing != DoingWorldAssignment) { + return false; + } + + std::ostringstream oss; + for (auto i = 0u; i < parsedPermissions.size(); i++) { + if (i != 0) { + oss << ";"; + } + oss << parsedPermissions[i]; + } + + parsedPermissionsVar = oss.str(); + return true; +} + int do_install(int ac, char const* const* av) { #ifdef CMAKE_BOOTSTRAP @@ -530,6 +652,7 @@ int do_install(int ac, char const* const* av) std::string config; std::string component; + std::string defaultDirectoryPermissions; std::string prefix; std::string dir; bool strip = false; @@ -542,6 +665,7 @@ int do_install(int ac, char const* const* av) DoingConfig, DoingComponent, DoingPrefix, + DoingDefaultDirectoryPermissions, }; Doing doing = DoingDir; @@ -560,6 +684,8 @@ int do_install(int ac, char const* const* av) (strcmp(av[i], "-v") == 0)) { verbose = true; doing = DoingNone; + } else if (strcmp(av[i], "--default-directory-permissions") == 0) { + doing = DoingDefaultDirectoryPermissions; } else { switch (doing) { case DoingDir: @@ -578,6 +704,10 @@ int do_install(int ac, char const* const* av) prefix = av[i]; doing = DoingNone; break; + case DoingDefaultDirectoryPermissions: + defaultDirectoryPermissions = av[i]; + doing = DoingNone; + break; default: std::cerr << "Unknown argument " << av[i] << std::endl; dir.clear(); @@ -594,6 +724,8 @@ int do_install(int ac, char const* const* av) " <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" + " --default-directory-permissions <permission> \n" + " Default install permission. Use default permission <permission>.\n" " --prefix <prefix> = The installation prefix CMAKE_INSTALL_PREFIX.\n" " --strip = Performing install/strip.\n" " -v --verbose = Enable verbose output.\n" @@ -634,6 +766,18 @@ int do_install(int ac, char const* const* av) args.emplace_back("-DCMAKE_INSTALL_CONFIG_NAME=" + config); } + if (!defaultDirectoryPermissions.empty()) { + std::string parsedPermissionsVar; + if (!parse_default_directory_permissions(defaultDirectoryPermissions, + parsedPermissionsVar)) { + std::cerr << "--default-directory-permissions is in incorrect format" + << std::endl; + return 1; + } + args.emplace_back("-DCMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS=" + + parsedPermissionsVar); + } + args.emplace_back("-P"); args.emplace_back(dir + "/cmake_install.cmake"); diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index a1fafcb9d3..e2ff8b7535 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -129,6 +129,7 @@ void CMakeCommandUsage(const char* program) << " 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" + << " create_hardlink old new - create a hard link new -> old\n" << " true - do nothing with an exit code of 0\n" << " false - do nothing with an exit code of 1\n" #if defined(_WIN32) && !defined(__CYGWIN__) @@ -589,12 +590,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, filesDiffer = cmsys::SystemTools::TextFilesDiffer(args[3], args[4]); } else { ::CMakeCommandUsage(args[0].c_str()); - return 1; + return 2; } if (filesDiffer) { - std::cerr << "Files \"" << args[args.size() - 2] << "\" to \"" - << args[args.size() - 1] << "\" are different.\n"; return 1; } return 0; @@ -609,8 +608,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } cmsys::ifstream fin(args[3].c_str(), std::ios::in | std::ios::binary); if (!fin) { - std::cerr << "could not open object list file: " << args[3].c_str() - << "\n"; + std::cerr << "could not open object list file: " << args[3] << "\n"; return 1; } std::vector<std::string> files; @@ -633,13 +631,12 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } FILE* fout = cmsys::SystemTools::Fopen(args[2], "w+"); if (!fout) { - std::cerr << "could not open output .def file: " << args[2].c_str() - << "\n"; + std::cerr << "could not open output .def file: " << args[2] << "\n"; return 1; } bindexplib deffile; if (args.size() >= 5) { - auto a = args[4]; + std::string const& a = args[4]; if (cmHasLiteralPrefix(a, "--nm=")) { deffile.SetNmPath(a.substr(5)); std::cerr << a.substr(5) << "\n"; @@ -647,7 +644,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, std::cerr << "unknown argument: " << a << "\n"; } } - for (auto const& file : files) { + for (std::string const& file : files) { std::string const& ext = cmSystemTools::GetFilenameLastExtension(file); if (cmSystemTools::LowerCase(ext) == ".def") { if (!deffile.AddDefinitionFile(file.c_str())) { @@ -1031,7 +1028,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, // Command to create a symbolic link. Fails on platforms not // supporting them. if (args[1] == "create_symlink" && args.size() == 4) { - const char* destinationFileName = args[3].c_str(); + std::string const& destinationFileName = args[3]; if ((cmSystemTools::FileExists(destinationFileName) || cmSystemTools::FileIsSymlink(destinationFileName)) && !cmSystemTools::RemoveFile(destinationFileName)) { @@ -1047,6 +1044,34 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, return 0; } + // Command to create a hard link. Fails on platforms not + // supporting them. + if (args[1] == "create_hardlink" && args.size() == 4) { + const char* SouceFileName = args[2].c_str(); + const char* destinationFileName = args[3].c_str(); + + if (!cmSystemTools::FileExists(SouceFileName)) { + std::cerr << "failed to create hard link because source path '" + << SouceFileName << "' does not exist \n"; + return 1; + } + + if ((cmSystemTools::FileExists(destinationFileName) || + cmSystemTools::FileIsSymlink(destinationFileName)) && + !cmSystemTools::RemoveFile(destinationFileName)) { + std::string emsg = cmSystemTools::GetLastSystemError(); + std::cerr << "failed to create hard link '" << destinationFileName + << "' because existing path cannot be removed: " << emsg + << "\n"; + return 1; + } + + if (!cmSystemTools::CreateLink(args[2], args[3])) { + return 1; + } + return 0; + } + // Command to do nothing with an exit code of 0. if (args[1] == "true") { return 0; @@ -1145,7 +1170,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, return cmcmd::ExecuteLinkScript(args); } -#ifndef CMAKE_BOOTSTRAP +#if !defined(CMAKE_BOOTSTRAP) || defined(CMAKE_BOOTSTRAP_NINJA) // Internal CMake ninja dependency scanning support. if (args[1] == "cmake_ninja_depends") { return cmcmd_cmake_ninja_depends(args.begin() + 2, args.end()); @@ -1388,15 +1413,12 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, #if defined(_WIN32) && !defined(__CYGWIN__) // Write registry value if (args[1] == "write_regv" && args.size() > 3) { - return cmSystemTools::WriteRegistryValue(args[2].c_str(), - args[3].c_str()) - ? 0 - : 1; + return cmSystemTools::WriteRegistryValue(args[2], args[3]) ? 0 : 1; } // Delete registry value if (args[1] == "delete_regv" && args.size() > 2) { - return cmSystemTools::DeleteRegistryValue(args[2].c_str()) ? 0 : 1; + return cmSystemTools::DeleteRegistryValue(args[2]) ? 0 : 1; } // Remove file @@ -1759,7 +1781,7 @@ int cmcmd::RunLLVMRC(std::vector<std::string> const& args) // args[2] == source_file_path // args[3] == intermediate_file // args[4..n] == preprocess+args - // args[n+1] == -- + // args[n+1] == ++ // args[n+2...] == llvm-rc+args if (args.size() < 3) { std::cerr << "Invalid cmake_llvm_rc arguments"; @@ -1771,7 +1793,11 @@ int cmcmd::RunLLVMRC(std::vector<std::string> const& args) std::vector<std::string> resource_compile; std::vector<std::string>* pArgTgt = &preprocess; for (std::string const& arg : cmMakeRange(args).advance(4)) { - if (arg == "--") { + // We use ++ as seperator between the preprocessing step definition and the + // rc compilation step becase we need to prepend a -- to seperate the + // source file properly from other options when using clang-cl for + // preprocessing. + if (arg == "++") { pArgTgt = &resource_compile; } else { if (arg.find("SOURCE_DIR") != std::string::npos) { diff --git a/Source/cmcmd.h b/Source/cmcmd.h index ffadd5a107..a2e0b1ea3d 100644 --- a/Source/cmcmd.h +++ b/Source/cmcmd.h @@ -1,7 +1,6 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmcmd_h -#define cmcmd_h +#pragma once #include "cmConfigure.h" // IWYU pragma: keep @@ -40,5 +39,3 @@ protected: static int RunLLVMRC(std::vector<std::string> const& args); static int VisualStudioLink(std::vector<std::string> const& args, int type); }; - -#endif diff --git a/Source/kwsys/CTestConfig.cmake b/Source/kwsys/CTestConfig.cmake index 33ea84c9d2..12347b6ef4 100644 --- a/Source/kwsys/CTestConfig.cmake +++ b/Source/kwsys/CTestConfig.cmake @@ -3,7 +3,9 @@ set(CTEST_PROJECT_NAME "KWSys") set(CTEST_NIGHTLY_START_TIME "21:00:00 EDT") -set(CTEST_DROP_METHOD "http") +if (NOT CTEST_DROP_METHOD STREQUAL "https") + set(CTEST_DROP_METHOD "http") +endif () set(CTEST_DROP_SITE "open.cdash.org") set(CTEST_DROP_LOCATION "/submit.php?project=PublicDashboard") set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Source/kwsys/Glob.cxx b/Source/kwsys/Glob.cxx index 5452f733b2..c6d4b1985d 100644 --- a/Source/kwsys/Glob.cxx +++ b/Source/kwsys/Glob.cxx @@ -27,7 +27,7 @@ #include <cstdio> #include <cstring> namespace KWSYS_NAMESPACE { -#if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__) +#if defined(_WIN32) || defined(__APPLE__) // On Windows and Apple, no difference between lower and upper case # define KWSYS_GLOB_CASE_INDEPENDENT #endif diff --git a/Source/kwsys/Glob.hxx.in b/Source/kwsys/Glob.hxx.in index b5a34d59a1..e8474e2001 100644 --- a/Source/kwsys/Glob.hxx.in +++ b/Source/kwsys/Glob.hxx.in @@ -126,8 +126,8 @@ protected: bool RecurseListDirs; private: - Glob(const Glob&); // Not implemented. - void operator=(const Glob&); // Not implemented. + Glob(const Glob&) = delete; + void operator=(const Glob&) = delete; }; } // namespace @KWSYS_NAMESPACE@ diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c index cc4552913d..e1e7721b41 100644 --- a/Source/kwsys/ProcessUNIX.c +++ b/Source/kwsys/ProcessUNIX.c @@ -2128,17 +2128,17 @@ static void kwsysProcessSetExitExceptionByIndex(kwsysProcess* cp, int sig, #endif #ifdef SIGABRT case SIGABRT: - KWSYSPE_CASE(Other, "Child aborted"); + KWSYSPE_CASE(Other, "Subprocess aborted"); break; #endif #ifdef SIGKILL case SIGKILL: - KWSYSPE_CASE(Other, "Child killed"); + KWSYSPE_CASE(Other, "Subprocess killed"); break; #endif #ifdef SIGTERM case SIGTERM: - KWSYSPE_CASE(Other, "Child terminated"); + KWSYSPE_CASE(Other, "Subprocess terminated"); break; #endif #ifdef SIGHUP diff --git a/Source/kwsys/RegularExpression.cxx b/Source/kwsys/RegularExpression.cxx index 4f74eba532..fb4e38029a 100644 --- a/Source/kwsys/RegularExpression.cxx +++ b/Source/kwsys/RegularExpression.cxx @@ -359,7 +359,7 @@ bool RegularExpression::compile(const char* exp) this->regmatch.clear(); // Small enough for pointer-storage convention? - if (comp.regsize >= 32767L) { // Probably could be 65535L. + if (comp.regsize >= 65535L) { // RAISE Error, SYM(RegularExpression), SYM(Expr_Too_Big), printf("RegularExpression::compile(): Expression too big.\n"); return false; diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx index ed1cdc0ab5..9c34a565af 100644 --- a/Source/kwsys/SystemInformation.cxx +++ b/Source/kwsys/SystemInformation.cxx @@ -863,7 +863,7 @@ void SystemInformation::RunMemoryCheck() // Hide implementation details in an anonymous namespace. namespace { // ***************************************************************************** -#if defined(__linux) || defined(__APPLE__) +#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) int LoadLines(FILE* file, std::vector<std::string>& lines) { // Load each line in the given file into a the vector. @@ -893,7 +893,7 @@ int LoadLines(FILE* file, std::vector<std::string>& lines) return nRead; } -# if defined(__linux) +# if defined(__linux) || defined(__CYGWIN__) // ***************************************************************************** int LoadLines(const char* fileName, std::vector<std::string>& lines) { @@ -926,7 +926,7 @@ int NameValue(std::vector<std::string> const& lines, std::string const& name, } #endif -#if defined(__linux) +#if defined(__linux) || defined(__CYGWIN__) // **************************************************************************** template <typename T> int GetFieldsFromFile(const char* fileName, const char** fieldNames, T* values) @@ -3393,7 +3393,7 @@ bool SystemInformationImplementation::RetreiveInformationFromCpuInfoFile() pos = buffer.find("processor\t", pos + 1); } -#ifdef __linux +#if defined(__linux) || defined(__CYGWIN__) // Count sockets. std::set<int> PhysicalIDs; std::string idc = this->ExtractValueFromCpuInfoFile(buffer, "physical id"); @@ -3414,8 +3414,8 @@ bool SystemInformationImplementation::RetreiveInformationFromCpuInfoFile() this->NumberOfPhysicalCPU = NumberOfCoresPerSocket * (unsigned int)NumberOfSockets; -#else // __CYGWIN__ - // does not have "physical id" entries, neither "cpu cores" +#else + // For systems which do not have "physical id" entries, neither "cpu cores" // this has to be fixed for hyper-threading. std::string cpucount = this->ExtractValueFromCpuInfoFile(buffer, "cpu count"); @@ -3597,7 +3597,7 @@ long long SystemInformationImplementation::GetHostMemoryTotal() GlobalMemoryStatusEx(&statex); return statex.ullTotalPhys / 1024; # endif -#elif defined(__linux) +#elif defined(__linux) || defined(__CYGWIN__) long long memTotal = 0; int ierr = GetFieldFromFile("/proc/meminfo", "MemTotal:", memTotal); if (ierr) { @@ -3712,6 +3712,16 @@ long long SystemInformationImplementation::GetHostMemoryUsed() GlobalMemoryStatusEx(&statex); return (statex.ullTotalPhys - statex.ullAvailPhys) / 1024; # endif +#elif defined(__CYGWIN__) + const char* names[3] = { "MemTotal:", "MemFree:", nullptr }; + long long values[2] = { 0 }; + int ierr = GetFieldsFromFile("/proc/meminfo", names, values); + if (ierr) { + return ierr; + } + long long& memTotal = values[0]; + long long& memFree = values[1]; + return memTotal - memFree; #elif defined(__linux) // First try to use MemAvailable, but it only works on newer kernels const char* names2[3] = { "MemTotal:", "MemAvailable:", nullptr }; @@ -3773,7 +3783,7 @@ long long SystemInformationImplementation::GetProcMemoryUsed() return -2; } return pmc.WorkingSetSize / 1024; -#elif defined(__linux) +#elif defined(__linux) || defined(__CYGWIN__) long long memUsed = 0; int ierr = GetFieldFromFile("/proc/self/status", "VmRSS:", memUsed); if (ierr) { @@ -3850,7 +3860,8 @@ long long SystemInformationImplementation::GetProcessId() #if defined(_WIN32) return GetCurrentProcessId(); #elif defined(__linux) || defined(__APPLE__) || defined(__OpenBSD__) || \ - defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) + defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \ + defined(__CYGWIN__) return getpid(); #else return -1; diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 25705ea287..6144d9c04d 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -96,19 +96,12 @@ # if defined(_MSC_VER) && _MSC_VER >= 1800 # define KWSYS_WINDOWS_DEPRECATED_GetVersionEx # endif -#elif defined(__CYGWIN__) -# include <windows.h> -# undef _WIN32 #endif #if !KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H extern char** environ; #endif -#ifdef __CYGWIN__ -# include <sys/cygwin.h> -#endif - // getpwnam doesn't exist on Windows and Cray Xt3/Catamount // same for TIOCGWINSZ #if defined(_WIN32) || defined(__LIBCATAMOUNT__) || \ @@ -1290,15 +1283,7 @@ bool SystemTools::PathExists(const std::string& path) if (path.empty()) { return false; } -#if defined(__CYGWIN__) - // Convert path to native windows path if possible. - char winpath[MAX_PATH]; - if (SystemTools::PathCygwinToWin32(path.c_str(), winpath)) { - return (GetFileAttributesA(winpath) != INVALID_FILE_ATTRIBUTES); - } - struct stat st; - return lstat(path.c_str(), &st) == 0; -#elif defined(_WIN32) +#if defined(_WIN32) return (GetFileAttributesW(Encoding::ToWindowsExtendedPath(path).c_str()) != INVALID_FILE_ATTRIBUTES); #else @@ -1320,14 +1305,7 @@ bool SystemTools::FileExists(const std::string& filename) if (filename.empty()) { return false; } -#if defined(__CYGWIN__) - // Convert filename to native windows path if possible. - char winpath[MAX_PATH]; - if (SystemTools::PathCygwinToWin32(filename.c_str(), winpath)) { - return (GetFileAttributesA(winpath) != INVALID_FILE_ATTRIBUTES); - } - return access(filename.c_str(), R_OK) == 0; -#elif defined(_WIN32) +#if defined(_WIN32) DWORD attr = GetFileAttributesW(Encoding::ToWindowsExtendedPath(filename).c_str()); if (attr == INVALID_FILE_ATTRIBUTES) { @@ -1433,27 +1411,9 @@ int SystemTools::Stat(const std::string& path, SystemTools::Stat_t* buf) #endif } -#ifdef __CYGWIN__ -bool SystemTools::PathCygwinToWin32(const char* path, char* win32_path) -{ - auto itr = SystemToolsStatics->Cyg2Win32Map.find(path); - if (itr != SystemToolsStatics->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; - } - SystemToolsStatics->Cyg2Win32Map.insert( - SystemToolsStatic::StringMap::value_type(path, win32_path)); - } - return win32_path[0] != 0; -} -#endif - bool SystemTools::Touch(const std::string& filename, bool create) { - if (!SystemTools::PathExists(filename)) { + if (!SystemTools::FileExists(filename)) { if (create) { FILE* file = Fopen(filename, "a+b"); if (file) { diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in index 5dbb726a08..74dc176514 100644 --- a/Source/kwsys/SystemTools.hxx.in +++ b/Source/kwsys/SystemTools.hxx.in @@ -331,15 +331,6 @@ public: static int Stat(const char* path, Stat_t* buf); static int Stat(const std::string& path, Stat_t* buf); -/** - * Converts Cygwin path to Win32 path. Uses dictionary container for - * caching and calls to cygwin_conv_to_win32_path from Cygwin dll - * for actual translation. Returns true on success, else false. - */ -#ifdef __CYGWIN__ - static bool PathCygwinToWin32(const char* path, char* win32_path); -#endif - /** * Return file length */ diff --git a/Source/kwsys/testProcess.c b/Source/kwsys/testProcess.c index 0c658f566e..eed770cd85 100644 --- a/Source/kwsys/testProcess.c +++ b/Source/kwsys/testProcess.c @@ -450,24 +450,25 @@ static int runChild2(kwsysProcess* kp, const char* cmd[], int state, printf("The process is still executing.\n"); break; case kwsysProcess_State_Expired: - printf("Child was killed when timeout expired.\n"); + printf("Subprocess was killed when timeout expired.\n"); break; case kwsysProcess_State_Exited: - printf("Child exited with value = %d\n", kwsysProcess_GetExitValue(kp)); + printf("Subprocess exited with value = %d\n", + kwsysProcess_GetExitValue(kp)); result = ((exception != kwsysProcess_GetExitException(kp)) || (value != kwsysProcess_GetExitValue(kp))); break; case kwsysProcess_State_Killed: - printf("Child was killed by parent.\n"); + printf("Subprocess was killed by parent.\n"); break; case kwsysProcess_State_Exception: - printf("Child terminated abnormally: %s\n", + printf("Subprocess terminated abnormally: %s\n", kwsysProcess_GetExceptionString(kp)); result = ((exception != kwsysProcess_GetExitException(kp)) || (value != kwsysProcess_GetExitValue(kp))); break; case kwsysProcess_State_Disowned: - printf("Child was disowned.\n"); + printf("Subprocess was disowned.\n"); break; case kwsysProcess_State_Error: printf("Error in administrating child process: [%s]\n", diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx index 1d3461443a..cfa420df49 100644 --- a/Source/kwsys/testSystemTools.cxx +++ b/Source/kwsys/testSystemTools.cxx @@ -422,6 +422,28 @@ static bool CheckFileOperations() res = false; } +#if !defined(_WIN32) + std::string const testBadSymlink(testNewDir + "/badSymlink.txt"); + std::string const testBadSymlinkTgt(testNewDir + "/missing/symlinkTgt.txt"); + if (!kwsys::SystemTools::CreateSymlink(testBadSymlinkTgt, testBadSymlink)) { + std::cerr << "Problem with CreateSymlink for: " << testBadSymlink << " -> " + << testBadSymlinkTgt << std::endl; + res = false; + } + + if (!kwsys::SystemTools::Touch(testBadSymlink, false)) { + std::cerr << "Problem with Touch (no create) for: " << testBadSymlink + << std::endl; + res = false; + } +#endif + + if (!kwsys::SystemTools::Touch(testNewDir, false)) { + std::cerr << "Problem with Touch (no create) for: " << testNewDir + << std::endl; + res = false; + } + kwsys::SystemTools::Touch(testNewFile, true); if (!kwsys::SystemTools::RemoveADirectory(testNewDir)) { std::cerr << "Problem with RemoveADirectory for: " << testNewDir |