diff options
-rw-r--r-- | .gitlab/ci/ctest_exclusions.cmake | 3 | ||||
-rw-r--r-- | Modules/CMakeMSYSFindMake.cmake | 11 | ||||
-rw-r--r-- | Modules/FindCUDAToolkit.cmake | 35 | ||||
-rw-r--r-- | Modules/FindMsys.cmake | 9 | ||||
-rw-r--r-- | Modules/Platform/Windows-OpenWatcom.cmake | 47 | ||||
-rw-r--r-- | Source/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Source/CMakeVersion.cmake | 2 | ||||
-rw-r--r-- | Source/cmDyndepCollation.cxx | 652 | ||||
-rw-r--r-- | Source/cmDyndepCollation.h | 52 | ||||
-rw-r--r-- | Source/cmGlobalNinjaGenerator.cxx | 379 | ||||
-rw-r--r-- | Source/cmGlobalNinjaGenerator.h | 4 | ||||
-rw-r--r-- | Source/cmNinjaTargetGenerator.cxx | 224 | ||||
-rw-r--r-- | Source/cmStringAlgorithms.cxx | 18 | ||||
-rw-r--r-- | Source/cmStringAlgorithms.h | 43 | ||||
-rw-r--r-- | Source/cmStringCommand.cxx | 64 | ||||
-rw-r--r-- | Tests/CMakeLib/testStringAlgorithms.cxx | 14 | ||||
-rw-r--r-- | Tests/Cuda/Toolkit/CMakeLists.txt | 5 | ||||
-rw-r--r-- | Tests/CudaOnly/Toolkit/CMakeLists.txt | 6 |
18 files changed, 918 insertions, 652 deletions
diff --git a/.gitlab/ci/ctest_exclusions.cmake b/.gitlab/ci/ctest_exclusions.cmake index b29e785..a2789c3 100644 --- a/.gitlab/ci/ctest_exclusions.cmake +++ b/.gitlab/ci/ctest_exclusions.cmake @@ -31,9 +31,6 @@ if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "nvhpc_") list(APPEND test_exclusions # FIXME(#24187): This test fails with NVHPC as the CUDA host compiler. "^CudaOnly.SeparateCompilationPTX$" - - # FIXME(#24188): FindCUDAToolkit breaks on some symlink layouts. - "^Cuda.Toolkit$" ) endif() diff --git a/Modules/CMakeMSYSFindMake.cmake b/Modules/CMakeMSYSFindMake.cmake index 33b02c9..96fdb37 100644 --- a/Modules/CMakeMSYSFindMake.cmake +++ b/Modules/CMakeMSYSFindMake.cmake @@ -3,8 +3,13 @@ find_program(CMAKE_MAKE_PROGRAM make + REGISTRY_VIEW 32 PATHS - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MSYS-1.0_is1;Inno Setup: App Path]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MinGW;InstallLocation]/bin" - c:/msys/1.0/bin /msys/1.0/bin) + # Typical install path for 32-bit MSYS2 (https://repo.msys2.org/distrib/msys2-i686-latest.sfx.exe) + "C:/msys32/usr" + # Typical install path for MINGW32 (https://sourceforge.net/projects/mingw) + "C:/mingw/msys" + # Git for Windows 32-bit (https://gitforwindows.org/) + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GitForWindows;InstallPath]/usr") + mark_as_advanced(CMAKE_MAKE_PROGRAM) diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake index a4cebe4..5df31da 100644 --- a/Modules/FindCUDAToolkit.cmake +++ b/Modules/FindCUDAToolkit.cmake @@ -894,8 +894,25 @@ mark_as_advanced(CUDA_CUDART if(CUDAToolkit_FOUND) set(CUDAToolkit_INCLUDE_DIRS ${CUDAToolkit_INCLUDE_DIR}) get_filename_component(CUDAToolkit_LIBRARY_DIR ${CUDA_CUDART} DIRECTORY ABSOLUTE) + + # Build search paths without any symlinks + file(REAL_PATH "${CUDAToolkit_LIBRARY_DIR}" _cmake_search_dir) + set(CUDAToolkit_LIBRARY_SEARCH_DIRS "${_cmake_search_dir}") + + # Detect we are in a splayed nvhpc toolkit layout and add extra + # search paths without symlinks + if(CUDAToolkit_LIBRARY_DIR MATCHES ".*/cuda/${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}/lib64$") + # Search location for math_libs/ + file(REAL_PATH "${CUDAToolkit_LIBRARY_DIR}/../../../" _cmake_search_dir) + list(APPEND CUDAToolkit_LIBRARY_SEARCH_DIRS "${_cmake_search_dir}") + + # Search location for extras like cupti + file(REAL_PATH "${CUDAToolkit_LIBRARY_DIR}/../" _cmake_search_dir) + list(APPEND CUDAToolkit_LIBRARY_SEARCH_DIRS "${_cmake_search_dir}") + endif() endif() + #----------------------------------------------------------------------------- # Construct import targets if(CUDAToolkit_FOUND) @@ -907,21 +924,21 @@ if(CUDAToolkit_FOUND) find_library(CUDA_${lib_name}_LIBRARY NAMES ${search_names} - HINTS ${CUDAToolkit_LIBRARY_DIR} + HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS} ENV CUDA_PATH PATH_SUFFIXES nvidia/current lib64 lib/x64 lib + # Support NVHPC splayed math library layout + math_libs/${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}/lib64 + math_libs/lib64 ${arg_EXTRA_PATH_SUFFIXES} ) # Don't try any stub directories until we have exhausted all other # search locations. find_library(CUDA_${lib_name}_LIBRARY NAMES ${search_names} - HINTS ${CUDAToolkit_LIBRARY_DIR} + HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS} ENV CUDA_PATH PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs - # Support NVHPC splayed math library layout - ../../math_libs/${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}/lib64 - ../../math_libs/lib64 ) mark_as_advanced(CUDA_${lib_name}_LIBRARY) @@ -1054,11 +1071,15 @@ if(CUDAToolkit_FOUND) if(CUDAToolkit_CUPTI_INCLUDE_DIR) _CUDAToolkit_find_and_add_import_lib(cupti - EXTRA_PATH_SUFFIXES ../extras/CUPTI/lib64/ + EXTRA_PATH_SUFFIXES extras/CUPTI/lib64/ + extras/CUPTI/lib/ + ../extras/CUPTI/lib64/ ../extras/CUPTI/lib/ EXTRA_INCLUDE_DIRS "${CUDAToolkit_CUPTI_INCLUDE_DIR}") _CUDAToolkit_find_and_add_import_lib(cupti_static - EXTRA_PATH_SUFFIXES ../extras/CUPTI/lib64/ + EXTRA_PATH_SUFFIXES extras/CUPTI/lib64/ + extras/CUPTI/lib/ + ../extras/CUPTI/lib64/ ../extras/CUPTI/lib/ EXTRA_INCLUDE_DIRS "${CUDAToolkit_CUPTI_INCLUDE_DIR}") endif() diff --git a/Modules/FindMsys.cmake b/Modules/FindMsys.cmake index b4796d2..86597c2 100644 --- a/Modules/FindMsys.cmake +++ b/Modules/FindMsys.cmake @@ -19,11 +19,12 @@ if (WIN32) find_program(MSYS_CMD NAMES msys2_shell.cmd PATHS - "C:/msys64" + # Typical install path for MSYS2 (https://repo.msys2.org/distrib/msys2-i686-latest.sfx.exe) "C:/msys32" - "C:/MSYS" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MSYS\\setup;rootdir]" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Cygnus Solutions\\MSYS\\mounts v2\\/;native]" + # Typical install path for MSYS2 (https://repo.msys2.org/distrib/msys2-x86_64-latest.sfx.exe) + "C:/msys64" + # Git for Windows (https://gitforwindows.org/) + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GitForWindows;InstallPath]" ) get_filename_component(MSYS_INSTALL_PATH "${MSYS_CMD}" DIRECTORY) mark_as_advanced(MSYS_CMD) diff --git a/Modules/Platform/Windows-OpenWatcom.cmake b/Modules/Platform/Windows-OpenWatcom.cmake index 657a923..4e73160 100644 --- a/Modules/Platform/Windows-OpenWatcom.cmake +++ b/Modules/Platform/Windows-OpenWatcom.cmake @@ -6,8 +6,13 @@ include_guard() set(CMAKE_BUILD_TYPE_INIT Debug) -string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " system nt_dll") -string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " system nt_dll") +if(DEFINED CMAKE_SYSTEM_PROCESSOR AND CMAKE_SYSTEM_PROCESSOR STREQUAL "I86") + string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " system windows") + string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " system windows") +else() + string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " system nt_dll") + string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " system nt_dll") +endif() set(CMAKE_C_COMPILE_OPTIONS_DLL "-bd") # Note: This variable is a ';' separated list set(CMAKE_SHARED_LIBRARY_C_FLAGS "-bd") # ... while this is a space separated string. @@ -23,22 +28,45 @@ else() set(_br_bm "-br -bm") endif() -string(APPEND CMAKE_C_FLAGS_INIT " -bt=nt -dWIN32 ${_br_bm}") -string(APPEND CMAKE_CXX_FLAGS_INIT " -bt=nt -xs -dWIN32 ${_br_bm}") +if(DEFINED CMAKE_SYSTEM_PROCESSOR AND CMAKE_SYSTEM_PROCESSOR STREQUAL "I86") + string(APPEND CMAKE_C_FLAGS_INIT " -bt=windows ") + string(APPEND CMAKE_CXX_FLAGS_INIT " -bt=windows ") +else() + string(APPEND CMAKE_C_FLAGS_INIT " -bt=nt -dWIN32 ${_br_bm}") + string(APPEND CMAKE_CXX_FLAGS_INIT " -bt=nt -xs -dWIN32 ${_br_bm}") +endif() unset(__WINDOWS_WATCOM_CMP0136) unset(_br_bm) if(CMAKE_CROSSCOMPILING) - if(NOT CMAKE_C_STANDARD_INCLUDE_DIRECTORIES) - set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES $ENV{WATCOM}/h $ENV{WATCOM}/h/nt) - endif() - if(NOT CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES) - set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES $ENV{WATCOM}/h $ENV{WATCOM}/h/nt) + if(DEFINED CMAKE_SYSTEM_PROCESSOR AND CMAKE_SYSTEM_PROCESSOR STREQUAL "I86") + if(NOT CMAKE_C_STANDARD_INCLUDE_DIRECTORIES) + set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES $ENV{WATCOM}/h $ENV{WATCOM}/h/win) + endif() + if(NOT CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES $ENV{WATCOM}/h $ENV{WATCOM}/h/win) + endif() + else() + if(NOT CMAKE_C_STANDARD_INCLUDE_DIRECTORIES) + set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES $ENV{WATCOM}/h $ENV{WATCOM}/h/nt) + endif() + if(NOT CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES $ENV{WATCOM}/h $ENV{WATCOM}/h/nt) + endif() endif() endif() macro(__windows_open_watcom lang) +if(DEFINED CMAKE_SYSTEM_PROCESSOR AND CMAKE_SYSTEM_PROCESSOR STREQUAL "I86") + set(CMAKE_${lang}_CREATE_WIN32_EXE "system windows") + set(CMAKE_${lang}_CREATE_CONSOLE_EXE "system windows") + + set(CMAKE_${lang}_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_SingleThreaded "") + set(CMAKE_${lang}_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_SingleThreadedDLL "") + set(CMAKE_${lang}_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_MultiThreaded "") + set(CMAKE_${lang}_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_MultiThreadedDLL "") +else() set(CMAKE_${lang}_CREATE_WIN32_EXE "system nt_win") set(CMAKE_${lang}_CREATE_CONSOLE_EXE "system nt") @@ -46,4 +74,5 @@ macro(__windows_open_watcom lang) set(CMAKE_${lang}_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_SingleThreadedDLL -br) set(CMAKE_${lang}_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_MultiThreaded -bm) set(CMAKE_${lang}_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_MultiThreadedDLL -bm -br) +endif() endmacro() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index c0709c6..41a901a 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -198,6 +198,8 @@ add_library( cmDocumentationFormatter.cxx cmDynamicLoader.cxx cmDynamicLoader.h + cmDyndepCollation.cxx + cmDyndepCollation.h cmELF.h cmELF.cxx cmExprParserHelper.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index d2236b1..c3c47ae 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 25) -set(CMake_VERSION_PATCH 20221201) +set(CMake_VERSION_PATCH 20221202) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx new file mode 100644 index 0000000..2827659 --- /dev/null +++ b/Source/cmDyndepCollation.cxx @@ -0,0 +1,652 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmDyndepCollation.h" + +#include <algorithm> +#include <map> +#include <ostream> +#include <set> +#include <utility> +#include <vector> + +#include <cm/memory> +#include <cm/string_view> +#include <cmext/string_view> + +#include <cm3p/json/value.h> + +#include "cmExportBuildFileGenerator.h" +#include "cmExportSet.h" +#include "cmFileSet.h" +#include "cmGeneratedFileStream.h" +#include "cmGeneratorExpression.h" // IWYU pragma: keep +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmInstallCxxModuleBmiGenerator.h" +#include "cmInstallExportGenerator.h" +#include "cmInstallFileSetGenerator.h" +#include "cmInstallGenerator.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmOutputConverter.h" +#include "cmScanDepFormat.h" +#include "cmSourceFile.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetExport.h" + +namespace { + +Json::Value CollationInformationCxxModules( + cmGeneratorTarget const* gt, std::string const& config, + cmDyndepGeneratorCallbacks const& cb) +{ + cmTarget const* tgt = gt->Target; + auto all_file_sets = tgt->GetAllFileSetNames(); + Json::Value tdi_cxx_module_info = Json::objectValue; + for (auto const& file_set_name : all_file_sets) { + auto const* file_set = tgt->GetFileSet(file_set_name); + if (!file_set) { + gt->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", + file_set_name, + "\", but it was not found.")); + continue; + } + auto fs_type = file_set->GetType(); + // We only care about C++ module sources here. + if (fs_type != "CXX_MODULES"_s) { + continue; + } + + auto fileEntries = file_set->CompileFileEntries(); + auto directoryEntries = file_set->CompileDirectoryEntries(); + + auto directories = file_set->EvaluateDirectoryEntries( + directoryEntries, gt->LocalGenerator, config, gt); + std::map<std::string, std::vector<std::string>> files_per_dirs; + for (auto const& entry : fileEntries) { + file_set->EvaluateFileEntry(directories, files_per_dirs, entry, + gt->LocalGenerator, config, gt); + } + + std::map<std::string, cmSourceFile const*> sf_map; + { + std::vector<cmSourceFile const*> objectSources; + gt->GetObjectSources(objectSources, config); + for (auto const* sf : objectSources) { + auto full_path = sf->GetFullPath(); + if (full_path.empty()) { + gt->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" has a full path-less source file.")); + continue; + } + sf_map[full_path] = sf; + } + } + + Json::Value fs_dest = Json::nullValue; + for (auto const& ig : gt->Makefile->GetInstallGenerators()) { + if (auto const* fsg = + dynamic_cast<cmInstallFileSetGenerator const*>(ig.get())) { + if (fsg->GetTarget() == gt && fsg->GetFileSet() == file_set) { + fs_dest = fsg->GetDestination(config); + continue; + } + } + } + + for (auto const& files_per_dir : files_per_dirs) { + for (auto const& file : files_per_dir.second) { + auto lookup = sf_map.find(file); + if (lookup == sf_map.end()) { + gt->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), "\" has source file \"", + file, + R"(" which is not in any of its "FILE_SET BASE_DIRS".)")); + continue; + } + + auto const* sf = lookup->second; + + if (!sf) { + gt->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), "\" has source file \"", + file, "\" which has not been tracked properly.")); + continue; + } + + auto obj_path = cb.ObjectFilePath(sf, config); + Json::Value& tdi_module_info = tdi_cxx_module_info[obj_path] = + Json::objectValue; + + tdi_module_info["source"] = file; + tdi_module_info["relative-directory"] = files_per_dir.first; + tdi_module_info["name"] = file_set->GetName(); + tdi_module_info["type"] = file_set->GetType(); + tdi_module_info["visibility"] = + std::string(cmFileSetVisibilityToName(file_set->GetVisibility())); + tdi_module_info["destination"] = fs_dest; + } + } + } + + return tdi_cxx_module_info; +} + +Json::Value CollationInformationBmiInstallation(cmGeneratorTarget const* gt, + std::string const& config) +{ + cmInstallCxxModuleBmiGenerator const* bmi_gen = nullptr; + for (auto const& ig : gt->Makefile->GetInstallGenerators()) { + if (auto const* bmig = + dynamic_cast<cmInstallCxxModuleBmiGenerator const*>(ig.get())) { + if (bmig->GetTarget() == gt) { + bmi_gen = bmig; + continue; + } + } + } + if (bmi_gen) { + Json::Value tdi_bmi_info = Json::objectValue; + + tdi_bmi_info["permissions"] = bmi_gen->GetFilePermissions(); + tdi_bmi_info["destination"] = bmi_gen->GetDestination(config); + const char* msg_level = ""; + switch (bmi_gen->GetMessageLevel()) { + case cmInstallGenerator::MessageDefault: + break; + case cmInstallGenerator::MessageAlways: + msg_level = "MESSAGE_ALWAYS"; + break; + case cmInstallGenerator::MessageLazy: + msg_level = "MESSAGE_LAZY"; + break; + case cmInstallGenerator::MessageNever: + msg_level = "MESSAGE_NEVER"; + break; + } + tdi_bmi_info["message-level"] = msg_level; + tdi_bmi_info["script-location"] = bmi_gen->GetScriptLocation(config); + + return tdi_bmi_info; + } + return Json::nullValue; +} + +Json::Value CollationInformationExports(cmGeneratorTarget const* gt) +{ + Json::Value tdi_exports = Json::arrayValue; + std::string export_name = gt->GetExportName(); + + auto const& all_install_exports = gt->GetGlobalGenerator()->GetExportSets(); + for (auto const& exp : all_install_exports) { + // Ignore exports sets which are not for this target. + auto const& targets = exp.second.GetTargetExports(); + auto tgt_export = + std::find_if(targets.begin(), targets.end(), + [gt](std::unique_ptr<cmTargetExport> const& te) { + return te->Target == gt; + }); + if (tgt_export == targets.end()) { + continue; + } + + auto const* installs = exp.second.GetInstallations(); + for (auto const* install : *installs) { + Json::Value tdi_export_info = Json::objectValue; + + auto const& ns = install->GetNamespace(); + auto const& dest = install->GetDestination(); + auto const& cxxm_dir = install->GetCxxModuleDirectory(); + auto const& export_prefix = install->GetTempDir(); + + tdi_export_info["namespace"] = ns; + tdi_export_info["export-name"] = export_name; + tdi_export_info["destination"] = dest; + tdi_export_info["cxx-module-info-dir"] = cxxm_dir; + tdi_export_info["export-prefix"] = export_prefix; + tdi_export_info["install"] = true; + + tdi_exports.append(tdi_export_info); + } + } + + auto const& all_build_exports = gt->Makefile->GetExportBuildFileGenerators(); + for (auto const& exp : all_build_exports) { + std::vector<std::string> targets; + exp->GetTargets(targets); + + // Ignore exports sets which are not for this target. + auto const& name = gt->GetName(); + bool has_current_target = + std::any_of(targets.begin(), targets.end(), + [name](std::string const& tname) { return tname == name; }); + if (!has_current_target) { + continue; + } + + Json::Value tdi_export_info = Json::objectValue; + + auto const& ns = exp->GetNamespace(); + auto const& main_fn = exp->GetMainExportFileName(); + auto const& cxxm_dir = exp->GetCxxModuleDirectory(); + auto dest = cmsys::SystemTools::GetParentDirectory(main_fn); + auto const& export_prefix = + cmSystemTools::GetFilenamePath(exp->GetMainExportFileName()); + + tdi_export_info["namespace"] = ns; + tdi_export_info["export-name"] = export_name; + tdi_export_info["destination"] = dest; + tdi_export_info["cxx-module-info-dir"] = cxxm_dir; + tdi_export_info["export-prefix"] = export_prefix; + tdi_export_info["install"] = false; + + tdi_exports.append(tdi_export_info); + } + + return tdi_exports; +} +} + +void cmDyndepCollation::AddCollationInformation( + Json::Value& tdi, cmGeneratorTarget const* gt, std::string const& config, + cmDyndepGeneratorCallbacks const& cb) +{ + tdi["cxx-modules"] = CollationInformationCxxModules(gt, config, cb); + tdi["bmi-installation"] = CollationInformationBmiInstallation(gt, config); + tdi["exports"] = CollationInformationExports(gt); + tdi["config"] = config; +} + +struct CxxModuleFileSet +{ + std::string Name; + std::string RelativeDirectory; + std::string SourcePath; + std::string Type; + cmFileSetVisibility Visibility; + cm::optional<std::string> Destination; +}; + +struct CxxModuleBmiInstall +{ + std::string Component; + std::string Destination; + bool ExcludeFromAll; + bool Optional; + std::string Permissions; + std::string MessageLevel; + std::string ScriptLocation; +}; + +struct CxxModuleExport +{ + std::string Name; + std::string Destination; + std::string Prefix; + std::string CxxModuleInfoDir; + std::string Namespace; + bool Install; +}; + +struct cmCxxModuleExportInfo +{ + std::map<std::string, CxxModuleFileSet> ObjectToFileSet; + cm::optional<CxxModuleBmiInstall> BmiInstallation; + std::vector<CxxModuleExport> Exports; + std::string Config; +}; + +void cmCxxModuleExportInfoDeleter::operator()(cmCxxModuleExportInfo* ei) const +{ + delete ei; +} + +std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter> +cmDyndepCollation::ParseExportInfo(Json::Value const& tdi) +{ + auto export_info = + std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter>( + new cmCxxModuleExportInfo); + + export_info->Config = tdi["config"].asString(); + if (export_info->Config.empty()) { + export_info->Config = "noconfig"; + } + Json::Value const& tdi_exports = tdi["exports"]; + if (tdi_exports.isArray()) { + for (auto const& tdi_export : tdi_exports) { + CxxModuleExport exp; + exp.Install = tdi_export["install"].asBool(); + exp.Name = tdi_export["export-name"].asString(); + exp.Destination = tdi_export["destination"].asString(); + exp.Prefix = tdi_export["export-prefix"].asString(); + exp.CxxModuleInfoDir = tdi_export["cxx-module-info-dir"].asString(); + exp.Namespace = tdi_export["namespace"].asString(); + + export_info->Exports.push_back(exp); + } + } + auto const& bmi_installation = tdi["bmi-installation"]; + if (bmi_installation.isObject()) { + CxxModuleBmiInstall bmi_install; + + bmi_install.Component = bmi_installation["component"].asString(); + bmi_install.Destination = bmi_installation["destination"].asString(); + bmi_install.ExcludeFromAll = bmi_installation["exclude-from-all"].asBool(); + bmi_install.Optional = bmi_installation["optional"].asBool(); + bmi_install.Permissions = bmi_installation["permissions"].asString(); + bmi_install.MessageLevel = bmi_installation["message-level"].asString(); + bmi_install.ScriptLocation = + bmi_installation["script-location"].asString(); + + export_info->BmiInstallation = bmi_install; + } + Json::Value const& tdi_cxx_modules = tdi["cxx-modules"]; + if (tdi_cxx_modules.isObject()) { + for (auto i = tdi_cxx_modules.begin(); i != tdi_cxx_modules.end(); ++i) { + CxxModuleFileSet& fsi = export_info->ObjectToFileSet[i.key().asString()]; + auto const& tdi_cxx_module_info = *i; + fsi.Name = tdi_cxx_module_info["name"].asString(); + fsi.RelativeDirectory = + tdi_cxx_module_info["relative-directory"].asString(); + fsi.SourcePath = tdi_cxx_module_info["source"].asString(); + fsi.Type = tdi_cxx_module_info["type"].asString(); + fsi.Visibility = cmFileSetVisibilityFromName( + tdi_cxx_module_info["visibility"].asString(), nullptr); + auto const& tdi_fs_dest = tdi_cxx_module_info["destination"]; + if (tdi_fs_dest.isString()) { + fsi.Destination = tdi_fs_dest.asString(); + } + } + } + + return export_info; +} + +bool cmDyndepCollation::WriteDyndepMetadata( + std::string const& lang, std::vector<cmScanDepInfo> const& objects, + cmCxxModuleExportInfo const& export_info, + cmDyndepMetadataCallbacks const& cb) +{ + // Only C++ supports any of the file-set or BMI installation considered + // below. + if (lang != "CXX"_s) { + return true; + } + + bool result = true; + + // Prepare the export information blocks. + std::string const config_upper = + cmSystemTools::UpperCase(export_info.Config); + std::vector< + std::pair<std::unique_ptr<cmGeneratedFileStream>, CxxModuleExport const*>> + exports; + for (auto const& exp : export_info.Exports) { + std::unique_ptr<cmGeneratedFileStream> properties; + + std::string const export_dir = + cmStrCat(exp.Prefix, '/', exp.CxxModuleInfoDir, '/'); + std::string const property_file_path = cmStrCat( + export_dir, "target-", exp.Name, '-', export_info.Config, ".cmake"); + properties = cm::make_unique<cmGeneratedFileStream>(property_file_path); + + // Set up the preamble. + *properties << "set_property(TARGET \"" << exp.Namespace << exp.Name + << "\"\n" + << " PROPERTY IMPORTED_CXX_MODULES_" << config_upper << '\n'; + + exports.emplace_back(std::move(properties), &exp); + } + + std::unique_ptr<cmGeneratedFileStream> bmi_install_script; + if (export_info.BmiInstallation) { + bmi_install_script = cm::make_unique<cmGeneratedFileStream>( + export_info.BmiInstallation->ScriptLocation); + } + + auto cmEscape = [](cm::string_view str) { + return cmOutputConverter::EscapeForCMake( + str, cmOutputConverter::WrapQuotes::NoWrap); + }; + auto install_destination = + [&cmEscape](std::string const& dest) -> std::pair<bool, std::string> { + if (cmSystemTools::FileIsFullPath(dest)) { + return std::make_pair(true, cmEscape(dest)); + } + return std::make_pair(false, + cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest))); + }; + + // public/private requirement tracking. + std::set<std::string> private_modules; + std::map<std::string, std::set<std::string>> public_source_requires; + + for (cmScanDepInfo const& object : objects) { + // Convert to forward slashes. + auto output_path = object.PrimaryOutput; +#ifdef _WIN32 + cmSystemTools::ConvertToUnixSlashes(output_path); +#endif + // Find the fileset for this object. + auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path); + bool const has_provides = !object.Provides.empty(); + if (fileset_info_itr == export_info.ObjectToFileSet.end()) { + // If it provides anything, it should have a `CXX_MODULES` or + // `CXX_MODULE_INTERNAL_PARTITIONS` type and be present. + if (has_provides) { + // Take the first module provided to provide context. + auto const& provides = object.Provides[0]; + char const* ok_types = "`CXX_MODULES`"; + if (provides.LogicalName.find(':') != std::string::npos) { + ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if " + "it is not `export`ed)"; + } + cmSystemTools::Error(cmStrCat( + "Output ", object.PrimaryOutput, " provides the `", + provides.LogicalName, + "` module but it is not found in a `FILE_SET` of type ", ok_types)); + result = false; + } + + // This object file does not provide anything, so nothing more needs to + // be done. + continue; + } + + auto const& file_set = fileset_info_itr->second; + + // Verify the fileset type for the object. + if (file_set.Type == "CXX_MODULES"_s) { + if (!has_provides) { + cmSystemTools::Error( + cmStrCat("Output ", object.PrimaryOutput, + " is of type `CXX_MODULES` but does not provide a module")); + result = false; + continue; + } + } else if (file_set.Type == "CXX_MODULE_INTERNAL_PARTITIONS"_s) { + if (!has_provides) { + cmSystemTools::Error( + cmStrCat("Source ", file_set.SourcePath, + " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not " + "provide a module")); + result = false; + continue; + } + auto const& provides = object.Provides[0]; + if (provides.LogicalName.find(':') == std::string::npos) { + cmSystemTools::Error( + cmStrCat("Source ", file_set.SourcePath, + " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not " + "provide a module partition")); + result = false; + continue; + } + } else if (file_set.Type == "CXX_MODULE_HEADERS"_s) { + // TODO. + } else { + if (has_provides) { + auto const& provides = object.Provides[0]; + char const* ok_types = "`CXX_MODULES`"; + if (provides.LogicalName.find(':') != std::string::npos) { + ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if " + "it is not `export`ed)"; + } + cmSystemTools::Error( + cmStrCat("Source ", file_set.SourcePath, " provides the `", + provides.LogicalName, "` C++ module but is of type `", + file_set.Type, "` module but must be of type ", ok_types)); + result = false; + } + + // Not a C++ module; ignore. + continue; + } + + if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) { + // Nothing needs to be conveyed about non-`PUBLIC` modules. + for (auto const& p : object.Provides) { + private_modules.insert(p.LogicalName); + } + continue; + } + + // The module is public. Record what it directly requires. + { + auto& reqs = public_source_requires[file_set.SourcePath]; + for (auto const& r : object.Requires) { + reqs.insert(r.LogicalName); + } + } + + // Write out properties and install rules for any exports. + for (auto const& p : object.Provides) { + bool bmi_dest_is_abs = false; + std::string bmi_destination; + if (export_info.BmiInstallation) { + auto dest = + install_destination(export_info.BmiInstallation->Destination); + bmi_dest_is_abs = dest.first; + bmi_destination = cmStrCat(dest.second, '/'); + } + + std::string install_bmi_path; + std::string build_bmi_path; + auto m = cb.ModuleFile(p.LogicalName); + if (m) { + install_bmi_path = cmStrCat( + bmi_destination, cmEscape(cmSystemTools::GetFilenameName(*m))); + build_bmi_path = cmEscape(*m); + } + + for (auto const& exp : exports) { + std::string iface_source; + if (exp.second->Install && file_set.Destination) { + auto dest = install_destination(*file_set.Destination); + iface_source = cmStrCat( + dest.second, '/', cmEscape(file_set.RelativeDirectory), + cmEscape(cmSystemTools::GetFilenameName(file_set.SourcePath))); + } else { + iface_source = cmEscape(file_set.SourcePath); + } + + std::string bmi_path; + if (exp.second->Install && export_info.BmiInstallation) { + bmi_path = install_bmi_path; + } else if (!exp.second->Install) { + bmi_path = build_bmi_path; + } + + if (iface_source.empty()) { + // No destination for the C++ module source; ignore this property + // value. + continue; + } + + *exp.first << " \"" << cmEscape(p.LogicalName) << '=' + << iface_source; + if (!bmi_path.empty()) { + *exp.first << ',' << bmi_path; + } + *exp.first << "\"\n"; + } + + if (bmi_install_script) { + auto const& bmi_install = *export_info.BmiInstallation; + + *bmi_install_script << "if (CMAKE_INSTALL_COMPONENT STREQUAL \"" + << cmEscape(bmi_install.Component) << '\"'; + if (!bmi_install.ExcludeFromAll) { + *bmi_install_script << " OR NOT CMAKE_INSTALL_COMPONENT"; + } + *bmi_install_script << ")\n"; + *bmi_install_script << " file(INSTALL\n" + " DESTINATION \""; + if (!bmi_dest_is_abs) { + *bmi_install_script << "${CMAKE_INSTALL_PREFIX}/"; + } + *bmi_install_script << cmEscape(bmi_install.Destination) + << "\"\n" + " TYPE FILE\n"; + if (bmi_install.Optional) { + *bmi_install_script << " OPTIONAL\n"; + } + if (!bmi_install.MessageLevel.empty()) { + *bmi_install_script << " " << bmi_install.MessageLevel << "\n"; + } + if (!bmi_install.Permissions.empty()) { + *bmi_install_script << " PERMISSIONS" << bmi_install.Permissions + << "\n"; + } + *bmi_install_script << " FILES \"" << *m << "\")\n"; + if (bmi_dest_is_abs) { + *bmi_install_script + << " list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n" + " \"" + << cmEscape(cmSystemTools::GetFilenameName(*m)) + << "\")\n" + " if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n" + " message(WARNING\n" + " \"ABSOLUTE path INSTALL DESTINATION : " + "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n" + " endif ()\n" + " if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n" + " message(FATAL_ERROR\n" + " \"ABSOLUTE path INSTALL DESTINATION forbidden (by " + "caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n" + " endif ()\n"; + } + *bmi_install_script << "endif ()\n"; + } + } + } + + // Add trailing parenthesis for the `set_property` call. + for (auto const& exp : exports) { + *exp.first << ")\n"; + } + + // Check that public sources only require public modules. + for (auto const& pub_reqs : public_source_requires) { + for (auto const& req : pub_reqs.second) { + if (private_modules.count(req)) { + cmSystemTools::Error(cmStrCat( + "Public C++ module source `", pub_reqs.first, "` requires the `", + req, "` C++ module which is provided by a private source")); + result = false; + } + } + } + + return result; +} diff --git a/Source/cmDyndepCollation.h b/Source/cmDyndepCollation.h new file mode 100644 index 0000000..e70ac09 --- /dev/null +++ b/Source/cmDyndepCollation.h @@ -0,0 +1,52 @@ +/* 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 <functional> +#include <memory> +#include <string> +#include <vector> + +#include <cm/optional> + +class cmGeneratorTarget; +struct cmScanDepInfo; +class cmSourceFile; + +namespace Json { +class Value; +} + +struct cmDyndepGeneratorCallbacks +{ + std::function<std::string(cmSourceFile const* sf, std::string const& config)> + ObjectFilePath; +}; + +struct cmDyndepMetadataCallbacks +{ + std::function<cm::optional<std::string>(std::string const& name)> ModuleFile; +}; + +struct cmCxxModuleExportInfo; +struct cmCxxModuleExportInfoDeleter +{ + void operator()(cmCxxModuleExportInfo* ei) const; +}; + +struct cmDyndepCollation +{ + static void AddCollationInformation(Json::Value& tdi, + cmGeneratorTarget const* gt, + std::string const& config, + cmDyndepGeneratorCallbacks const& cb); + + static std::unique_ptr<cmCxxModuleExportInfo, cmCxxModuleExportInfoDeleter> + ParseExportInfo(Json::Value const& tdi); + static bool WriteDyndepMetadata(std::string const& lang, + std::vector<cmScanDepInfo> const& objects, + cmCxxModuleExportInfo const& export_info, + cmDyndepMetadataCallbacks const& cb); +}; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 618dfb7..4500f33 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -25,7 +25,7 @@ #include "cmsys/FStream.hxx" #include "cmCxxModuleMapper.h" -#include "cmFileSet.h" +#include "cmDyndepCollation.h" #include "cmFortranParser.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpressionEvaluationFile.h" @@ -2468,45 +2468,6 @@ cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran( } } -struct CxxModuleFileSet -{ - std::string Name; - std::string RelativeDirectory; - std::string SourcePath; - std::string Type; - cmFileSetVisibility Visibility; - cm::optional<std::string> Destination; -}; - -struct CxxModuleBmiInstall -{ - std::string Component; - std::string Destination; - bool ExcludeFromAll; - bool Optional; - std::string Permissions; - std::string MessageLevel; - std::string ScriptLocation; -}; - -struct CxxModuleExport -{ - std::string Name; - std::string Destination; - std::string Prefix; - std::string CxxModuleInfoDir; - std::string Namespace; - bool Install; -}; - -struct cmGlobalNinjaGenerator::CxxModuleExportInfo -{ - std::map<std::string, CxxModuleFileSet> ObjectToFileSet; - cm::optional<CxxModuleBmiInstall> BmiInstallation; - std::vector<CxxModuleExport> Exports; - std::string Config; -}; - bool cmGlobalNinjaGenerator::WriteDyndepFile( std::string const& dir_top_src, std::string const& dir_top_bld, std::string const& dir_cur_src, std::string const& dir_cur_bld, @@ -2514,7 +2475,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, std::string const& arg_lang, std::string const& arg_modmapfmt, - CxxModuleExportInfo const& export_info) + cmCxxModuleExportInfo const& export_info) { // Setup path conversions. { @@ -2749,279 +2710,18 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( cmGeneratedFileStream tmf(target_mods_file); tmf << target_module_info; - bool result = true; - - // Fortran doesn't support any of the file-set or BMI installation considered - // below. - if (arg_lang != "Fortran"_s) { - // Prepare the export information blocks. - std::string const config_upper = - cmSystemTools::UpperCase(export_info.Config); - std::vector<std::pair<std::unique_ptr<cmGeneratedFileStream>, - CxxModuleExport const*>> - exports; - for (auto const& exp : export_info.Exports) { - std::unique_ptr<cmGeneratedFileStream> properties; - - std::string const export_dir = - cmStrCat(exp.Prefix, '/', exp.CxxModuleInfoDir, '/'); - std::string const property_file_path = cmStrCat( - export_dir, "target-", exp.Name, '-', export_info.Config, ".cmake"); - properties = cm::make_unique<cmGeneratedFileStream>(property_file_path); - - // Set up the preamble. - *properties << "set_property(TARGET \"" << exp.Namespace << exp.Name - << "\"\n" - << " PROPERTY IMPORTED_CXX_MODULES_" << config_upper - << '\n'; - - exports.emplace_back(std::move(properties), &exp); - } - - std::unique_ptr<cmGeneratedFileStream> bmi_install_script; - if (export_info.BmiInstallation) { - bmi_install_script = cm::make_unique<cmGeneratedFileStream>( - export_info.BmiInstallation->ScriptLocation); - } - - auto cmEscape = [](cm::string_view str) { - return cmOutputConverter::EscapeForCMake( - str, cmOutputConverter::WrapQuotes::NoWrap); - }; - auto install_destination = - [&cmEscape](std::string const& dest) -> std::pair<bool, std::string> { - if (cmSystemTools::FileIsFullPath(dest)) { - return std::make_pair(true, cmEscape(dest)); - } - return std::make_pair(false, - cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest))); - }; - - // public/private requirement tracking. - std::set<std::string> private_modules; - std::map<std::string, std::set<std::string>> public_source_requires; - - for (cmScanDepInfo const& object : objects) { - // Convert to forward slashes. - auto output_path = object.PrimaryOutput; -# ifdef _WIN32 - cmSystemTools::ConvertToUnixSlashes(output_path); -# endif - // Find the fileset for this object. - auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path); - bool const has_provides = !object.Provides.empty(); - if (fileset_info_itr == export_info.ObjectToFileSet.end()) { - // If it provides anything, it should have a `CXX_MODULES` or - // `CXX_MODULE_INTERNAL_PARTITIONS` type and be present. - if (has_provides) { - // Take the first module provided to provide context. - auto const& provides = object.Provides[0]; - char const* ok_types = "`CXX_MODULES`"; - if (provides.LogicalName.find(':') != std::string::npos) { - ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if " - "it is not `export`ed)"; - } - cmSystemTools::Error( - cmStrCat("Output ", object.PrimaryOutput, " provides the `", - provides.LogicalName, - "` module but it is not found in a `FILE_SET` of type ", - ok_types)); - result = false; - } - - // This object file does not provide anything, so nothing more needs to - // be done. - continue; - } - - auto const& file_set = fileset_info_itr->second; - - // Verify the fileset type for the object. - if (file_set.Type == "CXX_MODULES"_s) { - if (!has_provides) { - cmSystemTools::Error(cmStrCat( - "Output ", object.PrimaryOutput, - " is of type `CXX_MODULES` but does not provide a module")); - result = false; - continue; - } - } else if (file_set.Type == "CXX_MODULE_INTERNAL_PARTITIONS"_s) { - if (!has_provides) { - cmSystemTools::Error(cmStrCat( - "Source ", file_set.SourcePath, - " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not " - "provide a module")); - result = false; - continue; - } - auto const& provides = object.Provides[0]; - if (provides.LogicalName.find(':') == std::string::npos) { - cmSystemTools::Error(cmStrCat( - "Source ", file_set.SourcePath, - " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not " - "provide a module partition")); - result = false; - continue; - } - } else if (file_set.Type == "CXX_MODULE_HEADERS"_s) { - // TODO. - } else { - if (has_provides) { - auto const& provides = object.Provides[0]; - char const* ok_types = "`CXX_MODULES`"; - if (provides.LogicalName.find(':') != std::string::npos) { - ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if " - "it is not `export`ed)"; - } - cmSystemTools::Error(cmStrCat( - "Source ", file_set.SourcePath, " provides the `", - provides.LogicalName, "` C++ module but is of type `", - file_set.Type, "` module but must be of type ", ok_types)); - result = false; - } - - // Not a C++ module; ignore. - continue; - } - - if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) { - // Nothing needs to be conveyed about non-`PUBLIC` modules. - for (auto const& p : object.Provides) { - private_modules.insert(p.LogicalName); - } - continue; - } - - // The module is public. Record what it directly requires. - { - auto& reqs = public_source_requires[file_set.SourcePath]; - for (auto const& r : object.Requires) { - reqs.insert(r.LogicalName); - } - } - - // Write out properties and install rules for any exports. - for (auto const& p : object.Provides) { - bool bmi_dest_is_abs = false; - std::string bmi_destination; - if (export_info.BmiInstallation) { - auto dest = - install_destination(export_info.BmiInstallation->Destination); - bmi_dest_is_abs = dest.first; - bmi_destination = cmStrCat(dest.second, '/'); - } - - std::string install_bmi_path; - std::string build_bmi_path; - auto m = mod_files.find(p.LogicalName); - if (m != mod_files.end()) { - install_bmi_path = - cmStrCat(bmi_destination, - cmEscape(cmSystemTools::GetFilenameName(m->second))); - build_bmi_path = cmEscape(m->second); - } - - for (auto const& exp : exports) { - std::string iface_source; - if (exp.second->Install && file_set.Destination) { - auto dest = install_destination(*file_set.Destination); - iface_source = cmStrCat( - dest.second, '/', cmEscape(file_set.RelativeDirectory), - cmEscape(cmSystemTools::GetFilenameName(file_set.SourcePath))); - } else { - iface_source = cmEscape(file_set.SourcePath); - } - - std::string bmi_path; - if (exp.second->Install && export_info.BmiInstallation) { - bmi_path = install_bmi_path; - } else if (!exp.second->Install) { - bmi_path = build_bmi_path; - } - - if (iface_source.empty()) { - // No destination for the C++ module source; ignore this property - // value. - continue; - } - - *exp.first << " \"" << cmEscape(p.LogicalName) << '=' - << iface_source; - if (!bmi_path.empty()) { - *exp.first << ',' << bmi_path; - } - *exp.first << "\"\n"; - } - - if (bmi_install_script) { - auto const& bmi_install = *export_info.BmiInstallation; - - *bmi_install_script << "if (CMAKE_INSTALL_COMPONENT STREQUAL \"" - << cmEscape(bmi_install.Component) << '\"'; - if (!bmi_install.ExcludeFromAll) { - *bmi_install_script << " OR NOT CMAKE_INSTALL_COMPONENT"; - } - *bmi_install_script << ")\n"; - *bmi_install_script << " file(INSTALL\n" - " DESTINATION \""; - if (!bmi_dest_is_abs) { - *bmi_install_script << "${CMAKE_INSTALL_PREFIX}/"; - } - *bmi_install_script << cmEscape(bmi_install.Destination) - << "\"\n" - " TYPE FILE\n"; - if (bmi_install.Optional) { - *bmi_install_script << " OPTIONAL\n"; - } - if (!bmi_install.MessageLevel.empty()) { - *bmi_install_script << " " << bmi_install.MessageLevel << "\n"; - } - if (!bmi_install.Permissions.empty()) { - *bmi_install_script << " PERMISSIONS" << bmi_install.Permissions - << "\n"; - } - *bmi_install_script << " FILES \"" << m->second << "\")\n"; - if (bmi_dest_is_abs) { - *bmi_install_script - << " list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n" - " \"" - << cmEscape(cmSystemTools::GetFilenameName(m->second)) - << "\")\n" - " if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n" - " message(WARNING\n" - " \"ABSOLUTE path INSTALL DESTINATION : " - "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n" - " endif ()\n" - " if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n" - " message(FATAL_ERROR\n" - " \"ABSOLUTE path INSTALL DESTINATION forbidden (by " - "caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n" - " endif ()\n"; - } - *bmi_install_script << "endif ()\n"; - } - } - } - - // Add trailing parenthesis for the `set_property` call. - for (auto const& exp : exports) { - *exp.first << ")\n"; - } - - // Check that public sources only require public modules. - for (auto const& pub_reqs : public_source_requires) { - for (auto const& req : pub_reqs.second) { - if (private_modules.count(req)) { - cmSystemTools::Error(cmStrCat( - "Public C++ module source `", pub_reqs.first, "` requires the `", - req, "` C++ module which is provided by a private source")); - result = false; - } - } + cmDyndepMetadataCallbacks cb; + cb.ModuleFile = + [mod_files](std::string const& name) -> cm::optional<std::string> { + auto m = mod_files.find(name); + if (m != mod_files.end()) { + return m->second; } - } + return {}; + }; - return result; + return cmDyndepCollation::WriteDyndepMetadata(arg_lang, objects, export_info, + cb); } int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, @@ -3095,58 +2795,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, } } - cmGlobalNinjaGenerator::CxxModuleExportInfo export_info; - export_info.Config = tdi["config"].asString(); - if (export_info.Config.empty()) { - export_info.Config = "noconfig"; - } - Json::Value const& tdi_exports = tdi["exports"]; - if (tdi_exports.isArray()) { - for (auto const& tdi_export : tdi_exports) { - CxxModuleExport exp; - exp.Install = tdi_export["install"].asBool(); - exp.Name = tdi_export["export-name"].asString(); - exp.Destination = tdi_export["destination"].asString(); - exp.Prefix = tdi_export["export-prefix"].asString(); - exp.CxxModuleInfoDir = tdi_export["cxx-module-info-dir"].asString(); - exp.Namespace = tdi_export["namespace"].asString(); - - export_info.Exports.push_back(exp); - } - } - auto const& bmi_installation = tdi["bmi-installation"]; - if (bmi_installation.isObject()) { - CxxModuleBmiInstall bmi_install; - - bmi_install.Component = bmi_installation["component"].asString(); - bmi_install.Destination = bmi_installation["destination"].asString(); - bmi_install.ExcludeFromAll = bmi_installation["exclude-from-all"].asBool(); - bmi_install.Optional = bmi_installation["optional"].asBool(); - bmi_install.Permissions = bmi_installation["permissions"].asString(); - bmi_install.MessageLevel = bmi_installation["message-level"].asString(); - bmi_install.ScriptLocation = - bmi_installation["script-location"].asString(); - - export_info.BmiInstallation = bmi_install; - } - Json::Value const& tdi_cxx_modules = tdi["cxx-modules"]; - if (tdi_cxx_modules.isObject()) { - for (auto i = tdi_cxx_modules.begin(); i != tdi_cxx_modules.end(); ++i) { - CxxModuleFileSet& fsi = export_info.ObjectToFileSet[i.key().asString()]; - auto const& tdi_cxx_module_info = *i; - fsi.Name = tdi_cxx_module_info["name"].asString(); - fsi.RelativeDirectory = - tdi_cxx_module_info["relative-directory"].asString(); - fsi.SourcePath = tdi_cxx_module_info["source"].asString(); - fsi.Type = tdi_cxx_module_info["type"].asString(); - fsi.Visibility = cmFileSetVisibilityFromName( - tdi_cxx_module_info["visibility"].asString(), nullptr); - auto const& tdi_fs_dest = tdi_cxx_module_info["destination"]; - if (tdi_fs_dest.isString()) { - fsi.Destination = tdi_fs_dest.asString(); - } - } - } + auto export_info = cmDyndepCollation::ParseExportInfo(tdi); cmake cm(cmake::RoleInternal, cmState::Unknown); cm.SetHomeDirectory(dir_top_src); @@ -3156,7 +2805,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, !cm::static_reference_cast<cmGlobalNinjaGenerator>(ggd).WriteDyndepFile( dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis, module_dir, linked_target_dirs, arg_lang, arg_modmapfmt, - export_info)) { + *export_info)) { return 1; } return 0; diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 6f654f6..775e792 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -35,6 +35,7 @@ class cmMakefile; class cmOutputConverter; class cmStateDirectory; class cmake; +struct cmCxxModuleExportInfo; /** * \class cmGlobalNinjaGenerator @@ -417,7 +418,6 @@ public: bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); } void StripNinjaOutputPathPrefixAsSuffix(std::string& path); - struct CxxModuleExportInfo; bool WriteDyndepFile( std::string const& dir_top_src, std::string const& dir_top_bld, std::string const& dir_cur_src, std::string const& dir_cur_bld, @@ -425,7 +425,7 @@ public: std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, std::string const& arg_lang, std::string const& arg_modmapfmt, - CxxModuleExportInfo const& export_info); + cmCxxModuleExportInfo const& export_info); virtual std::string BuildAlias(const std::string& alias, const std::string& /*config*/) const diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 095bf40..85a6fc2 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -4,6 +4,7 @@ #include <algorithm> #include <cassert> +#include <functional> #include <iterator> #include <map> #include <ostream> @@ -21,17 +22,12 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommandGenerator.h" -#include "cmExportBuildFileGenerator.h" -#include "cmExportSet.h" +#include "cmDyndepCollation.h" #include "cmFileSet.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalNinjaGenerator.h" -#include "cmInstallCxxModuleBmiGenerator.h" -#include "cmInstallExportGenerator.h" -#include "cmInstallFileSetGenerator.h" -#include "cmInstallGenerator.h" #include "cmLocalGenerator.h" #include "cmLocalNinjaGenerator.h" #include "cmMakefile.h" @@ -47,7 +43,6 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" -#include "cmTargetExport.h" #include "cmValue.h" #include "cmake.h" @@ -1637,214 +1632,15 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, tdi_linked_target_dirs.append(l); } - cmTarget* tgt = this->GeneratorTarget->Target; - auto all_file_sets = tgt->GetAllFileSetNames(); - Json::Value& tdi_cxx_module_info = tdi["cxx-modules"] = Json::objectValue; - for (auto const& file_set_name : all_file_sets) { - auto* file_set = tgt->GetFileSet(file_set_name); - if (!file_set) { - this->GetMakefile()->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), - "\" is tracked to have file set \"", file_set_name, - "\", but it was not found.")); - continue; - } - auto fs_type = file_set->GetType(); - // We only care about C++ module sources here. - if (fs_type != "CXX_MODULES"_s) { - continue; - } - - auto fileEntries = file_set->CompileFileEntries(); - auto directoryEntries = file_set->CompileDirectoryEntries(); - - auto directories = file_set->EvaluateDirectoryEntries( - directoryEntries, this->GeneratorTarget->LocalGenerator, config, - this->GeneratorTarget); - std::map<std::string, std::vector<std::string>> files_per_dirs; - for (auto const& entry : fileEntries) { - file_set->EvaluateFileEntry(directories, files_per_dirs, entry, - this->GeneratorTarget->LocalGenerator, - config, this->GeneratorTarget); - } - - std::map<std::string, cmSourceFile const*> sf_map; - { - std::vector<cmSourceFile const*> objectSources; - this->GeneratorTarget->GetObjectSources(objectSources, config); - for (auto const* sf : objectSources) { - auto full_path = sf->GetFullPath(); - if (full_path.empty()) { - this->GetMakefile()->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), - "\" has a full path-less source file.")); - continue; - } - sf_map[full_path] = sf; - } - } - - Json::Value fs_dest = Json::nullValue; - for (auto const& ig : this->GetMakefile()->GetInstallGenerators()) { - if (auto const* fsg = - dynamic_cast<cmInstallFileSetGenerator const*>(ig.get())) { - if (fsg->GetTarget() == this->GeneratorTarget && - fsg->GetFileSet() == file_set) { - fs_dest = fsg->GetDestination(config); - continue; - } - } - } - - for (auto const& files_per_dir : files_per_dirs) { - for (auto const& file : files_per_dir.second) { - auto lookup = sf_map.find(file); - if (lookup == sf_map.end()) { - this->GetMakefile()->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), "\" has source file \"", - file, - R"(" which is not in any of its "FILE_SET BASE_DIRS".)")); - continue; - } - - auto const* sf = lookup->second; - - if (!sf) { - this->GetMakefile()->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target \"", tgt->GetName(), "\" has source file \"", - file, "\" which has not been tracked properly.")); - continue; - } - - auto obj_path = this->GetObjectFilePath(sf, config); - Json::Value& tdi_module_info = tdi_cxx_module_info[obj_path] = - Json::objectValue; - - tdi_module_info["source"] = file; - tdi_module_info["relative-directory"] = files_per_dir.first; - tdi_module_info["name"] = file_set->GetName(); - tdi_module_info["type"] = file_set->GetType(); - tdi_module_info["visibility"] = - std::string(cmFileSetVisibilityToName(file_set->GetVisibility())); - tdi_module_info["destination"] = fs_dest; - } - } - } - - tdi["config"] = config; + cmDyndepGeneratorCallbacks cb; + cb.ObjectFilePath = [this](cmSourceFile const* sf, std::string const& cnf) { + return this->GetObjectFilePath(sf, cnf); + }; - // Add information about the export sets that this target is a member of. - Json::Value& tdi_exports = tdi["exports"] = Json::arrayValue; - std::string export_name = this->GeneratorTarget->GetExportName(); - - cmInstallCxxModuleBmiGenerator const* bmi_gen = nullptr; - for (auto const& ig : this->GetMakefile()->GetInstallGenerators()) { - if (auto const* bmig = - dynamic_cast<cmInstallCxxModuleBmiGenerator const*>(ig.get())) { - if (bmig->GetTarget() == this->GeneratorTarget) { - bmi_gen = bmig; - continue; - } - } - } - if (bmi_gen) { - Json::Value tdi_bmi_info = Json::objectValue; - - tdi_bmi_info["permissions"] = bmi_gen->GetFilePermissions(); - tdi_bmi_info["destination"] = bmi_gen->GetDestination(config); - const char* msg_level = ""; - switch (bmi_gen->GetMessageLevel()) { - case cmInstallGenerator::MessageDefault: - break; - case cmInstallGenerator::MessageAlways: - msg_level = "MESSAGE_ALWAYS"; - break; - case cmInstallGenerator::MessageLazy: - msg_level = "MESSAGE_LAZY"; - break; - case cmInstallGenerator::MessageNever: - msg_level = "MESSAGE_NEVER"; - break; - } - tdi_bmi_info["message-level"] = msg_level; - tdi_bmi_info["script-location"] = bmi_gen->GetScriptLocation(config); - - tdi["bmi-installation"] = tdi_bmi_info; - } else { - tdi["bmi-installation"] = Json::nullValue; - } - - auto const& all_install_exports = - this->GetGlobalGenerator()->GetExportSets(); - for (auto const& exp : all_install_exports) { - // Ignore exports sets which are not for this target. - auto const& targets = exp.second.GetTargetExports(); - auto tgt_export = - std::find_if(targets.begin(), targets.end(), - [this](std::unique_ptr<cmTargetExport> const& te) { - return te->Target == this->GeneratorTarget; - }); - if (tgt_export == targets.end()) { - continue; - } - - auto const* installs = exp.second.GetInstallations(); - for (auto const* install : *installs) { - Json::Value tdi_export_info = Json::objectValue; - - auto const& ns = install->GetNamespace(); - auto const& dest = install->GetDestination(); - auto const& cxxm_dir = install->GetCxxModuleDirectory(); - auto const& export_prefix = install->GetTempDir(); - - tdi_export_info["namespace"] = ns; - tdi_export_info["export-name"] = export_name; - tdi_export_info["destination"] = dest; - tdi_export_info["cxx-module-info-dir"] = cxxm_dir; - tdi_export_info["export-prefix"] = export_prefix; - tdi_export_info["install"] = true; - - tdi_exports.append(tdi_export_info); - } - } - - auto const& all_build_exports = - this->GetMakefile()->GetExportBuildFileGenerators(); - for (auto const& exp : all_build_exports) { - std::vector<std::string> targets; - exp->GetTargets(targets); - - // Ignore exports sets which are not for this target. - auto const& name = this->GeneratorTarget->GetName(); - bool has_current_target = - std::any_of(targets.begin(), targets.end(), - [name](std::string const& tname) { return tname == name; }); - if (!has_current_target) { - continue; - } - - Json::Value tdi_export_info = Json::objectValue; - - auto const& ns = exp->GetNamespace(); - auto const& main_fn = exp->GetMainExportFileName(); - auto const& cxxm_dir = exp->GetCxxModuleDirectory(); - auto dest = cmsys::SystemTools::GetParentDirectory(main_fn); - auto const& export_prefix = - cmSystemTools::GetFilenamePath(exp->GetMainExportFileName()); - - tdi_export_info["namespace"] = ns; - tdi_export_info["export-name"] = export_name; - tdi_export_info["destination"] = dest; - tdi_export_info["cxx-module-info-dir"] = cxxm_dir; - tdi_export_info["export-prefix"] = export_prefix; - tdi_export_info["install"] = false; - - tdi_exports.append(tdi_export_info); - } +#if !defined(CMAKE_BOOTSTRAP) + cmDyndepCollation::AddCollationInformation(tdi, this->GeneratorTarget, + config, cb); +#endif std::string const tdin = this->GetTargetDependInfoPath(lang, config); cmGeneratedFileStream tdif(tdin); diff --git a/Source/cmStringAlgorithms.cxx b/Source/cmStringAlgorithms.cxx index f73c854..e559cfa 100644 --- a/Source/cmStringAlgorithms.cxx +++ b/Source/cmStringAlgorithms.cxx @@ -203,15 +203,23 @@ cmAlphaNum::cmAlphaNum(double val) MakeDigits(this->View_, this->Digits_, "%g", val); } -std::string cmCatViews(std::initializer_list<cm::string_view> views) +std::string cmCatViews(cm::optional<std::string>&& first, + std::initializer_list<cm::string_view> views) { - std::size_t total_size = 0; + std::size_t totalSize = 0; for (cm::string_view const& view : views) { - total_size += view.size(); + totalSize += view.size(); } - std::string result(total_size, '\0'); - std::string::iterator sit = result.begin(); + std::string result; + std::string::size_type initialLen = 0; + if (first) { + totalSize += first->length(); + initialLen = first->length(); + result = std::move(*first); + } + result.resize(totalSize); + std::string::iterator sit = result.begin() + initialLen; for (cm::string_view const& view : views) { sit = std::copy_n(view.data(), view.size(), sit); } diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h index 83938bc..bff2eda 100644 --- a/Source/cmStringAlgorithms.h +++ b/Source/cmStringAlgorithms.h @@ -12,6 +12,7 @@ #include <utility> #include <vector> +#include <cm/optional> #include <cm/string_view> #include "cmRange.h" @@ -146,7 +147,8 @@ std::vector<std::string> cmExpandedLists(InputIt first, InputIt last) } /** Concatenate string pieces into a single string. */ -std::string cmCatViews(std::initializer_list<cm::string_view> views); +std::string cmCatViews(cm::optional<std::string>&& first, + std::initializer_list<cm::string_view> views); /** Utility class for cmStrCat. */ class cmAlphaNum @@ -189,13 +191,38 @@ private: char Digits_[32]; }; +template <typename A, typename B, typename... AV> +class cmStrCatHelper +{ +public: + static std::string Compute(cmAlphaNum const& a, cmAlphaNum const& b, + AV const&... args) + { + return cmCatViews( + cm::nullopt, + { a.View(), b.View(), static_cast<cmAlphaNum const&>(args).View()... }); + } +}; + +template <typename B, typename... AV> +class cmStrCatHelper<std::string, B, AV...> +{ +public: + static std::string Compute(std::string&& a, cmAlphaNum const& b, + AV const&... args) + { + return cmCatViews( + std::move(a), + { b.View(), static_cast<cmAlphaNum const&>(args).View()... }); + } +}; + /** Concatenate string pieces and numbers into a single string. */ -template <typename... AV> -inline std::string cmStrCat(cmAlphaNum const& a, cmAlphaNum const& b, - AV const&... args) +template <typename A, typename B, typename... AV> +inline std::string cmStrCat(A&& a, B&& b, AV&&... args) { - return cmCatViews( - { a.View(), b.View(), static_cast<cmAlphaNum const&>(args).View()... }); + return cmStrCatHelper<A, B, AV...>::Compute( + std::forward<A>(a), std::forward<B>(b), std::forward<AV>(args)...); } /** Joins wrapped elements of a range with separator into a single string. */ @@ -207,7 +234,9 @@ std::string cmWrap(cm::string_view prefix, Range const& rng, return std::string(); } return cmCatViews( - { prefix, cmJoin(rng, cmCatViews({ suffix, sep, prefix })), suffix }); + cm::nullopt, + { prefix, cmJoin(rng, cmCatViews(cm::nullopt, { suffix, sep, prefix })), + suffix }); } /** Joins wrapped elements of a range with separator into a single string. */ diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index c12d1fe..5a64588 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx @@ -9,7 +9,6 @@ #include <cctype> #include <cstdio> #include <cstdlib> -#include <initializer_list> #include <limits> #include <memory> #include <stdexcept> @@ -950,9 +949,9 @@ struct Args : cmRange<typename std::vector<std::string>::const_iterator> class json_error : public std::runtime_error { public: - json_error(std::initializer_list<cm::string_view> message, + json_error(const std::string& message, cm::optional<Args> errorPath = cm::nullopt) - : std::runtime_error(cmCatViews(message)) + : std::runtime_error(message) , ErrorPath{ std::move(errorPath) // NOLINT(performance-move-const-arg) } @@ -964,7 +963,7 @@ public: const std::string& Args::PopFront(cm::string_view error) { if (this->empty()) { - throw json_error({ error }); + throw json_error(std::string(error)); } const std::string& res = *this->begin(); this->advance(1); @@ -974,7 +973,7 @@ const std::string& Args::PopFront(cm::string_view error) const std::string& Args::PopBack(cm::string_view error) { if (this->empty()) { - throw json_error({ error }); + throw json_error(std::string(error)); } const std::string& res = *(this->end() - 1); this->retreat(1); @@ -999,7 +998,7 @@ cm::string_view JsonTypeToString(Json::ValueType type) case Json::ValueType::objectValue: return "OBJECT"_s; } - throw json_error({ "invalid JSON type found"_s }); + throw json_error("invalid JSON type found"); } int ParseIndex( @@ -1008,14 +1007,14 @@ int ParseIndex( { unsigned long lindex; if (!cmStrToULong(str, &lindex)) { - throw json_error({ "expected an array index, got: '"_s, str, "'"_s }, + throw json_error(cmStrCat("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 than "_s, sizeStr.View(), - " got '"_s, str, "'"_s }, + throw json_error(cmStrCat("expected an index less than "_s, sizeStr.View(), + " got '"_s, str, "'"_s), progress); } return index; @@ -1036,16 +1035,16 @@ Json::Value& ResolvePath(Json::Value& json, Args path) } else if (search->isObject()) { if (!search->isMember(field)) { const auto progressStr = cmJoin(progress, " "_s); - throw json_error({ "member '"_s, progressStr, "' not found"_s }, + throw json_error(cmStrCat("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()) }, + cmStrCat("invalid path '"_s, progressStr, + "', need element of OBJECT or ARRAY type to lookup '"_s, + field, "' got "_s, JsonTypeToString(search->type())), progress); } } @@ -1061,7 +1060,7 @@ Json::Value ReadJson(const std::string& jsonstr) std::string error; if (!jsonReader->parse(jsonstr.data(), jsonstr.data() + jsonstr.size(), &json, &error)) { - throw json_error({ "failed parsing json string: "_s, error }); + throw json_error(cmStrCat("failed parsing json string: "_s, error)); } return json; } @@ -1101,9 +1100,9 @@ bool HandleJSONCommand(std::vector<std::string> const& arguments, 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, TYPE, MEMBER, LENGTH, REMOVE, SET, " - " EQUAL"_s }); + cmStrCat("got an invalid mode '"_s, mode, + "', expected one of GET, TYPE, MEMBER, LENGTH, REMOVE, SET, " + " EQUAL"_s)); } const auto& jsonstr = args.PopFront("missing json string argument"_s); @@ -1127,10 +1126,11 @@ bool HandleJSONCommand(std::vector<std::string> const& arguments, 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); + throw json_error( + cmStrCat("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()); @@ -1140,9 +1140,9 @@ bool HandleJSONCommand(std::vector<std::string> const& arguments, } 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()) }, + throw json_error(cmStrCat("LENGTH needs to be called with an " + "element of type ARRAY or OBJECT, got "_s, + JsonTypeToString(value.type())), args); } @@ -1165,9 +1165,9 @@ bool HandleJSONCommand(std::vector<std::string> const& arguments, 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()) }, + throw json_error(cmStrCat("REMOVE needs to be called with an " + "element of type ARRAY or OBJECT, got "_s, + JsonTypeToString(value.type())), args); } makefile.AddDefinition(*outputVariable, WriteJson(json)); @@ -1189,9 +1189,9 @@ bool HandleJSONCommand(std::vector<std::string> const& arguments, 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()) }); + throw json_error(cmStrCat("SET needs to be called with an " + "element of type OBJECT or ARRAY, got "_s, + JsonTypeToString(value.type()))); } makefile.AddDefinition(*outputVariable, WriteJson(json)); @@ -1207,7 +1207,7 @@ bool HandleJSONCommand(std::vector<std::string> const& arguments, if (outputVariable && e.ErrorPath) { const auto errorPath = cmJoin(*e.ErrorPath, "-"); makefile.AddDefinition(*outputVariable, - cmCatViews({ errorPath, "-NOTFOUND"_s })); + cmStrCat(errorPath, "-NOTFOUND"_s)); } else if (outputVariable) { makefile.AddDefinition(*outputVariable, "NOTFOUND"_s); } @@ -1215,7 +1215,7 @@ bool HandleJSONCommand(std::vector<std::string> const& arguments, if (errorVariable) { makefile.AddDefinition(*errorVariable, e.what()); } else { - status.SetError(cmCatViews({ "sub-command JSON "_s, e.what(), "."_s })); + status.SetError(cmStrCat("sub-command JSON "_s, e.what(), "."_s)); success = false; } } diff --git a/Tests/CMakeLib/testStringAlgorithms.cxx b/Tests/CMakeLib/testStringAlgorithms.cxx index 1e6b611..f73e62a 100644 --- a/Tests/CMakeLib/testStringAlgorithms.cxx +++ b/Tests/CMakeLib/testStringAlgorithms.cxx @@ -6,6 +6,8 @@ #include <iostream> #include <sstream> #include <string> +#include <type_traits> +#include <utility> #include <vector> #include <cm/string_view> @@ -144,6 +146,18 @@ int testStringAlgorithms(int /*unused*/, char* /*unused*/ []) d -= val; assert_ok((d < div) && (d > -div), "cmStrCat double"); } + { + std::string val; + std::string expect; + val.reserve(120 * cmStrLen("cmStrCat move")); + auto data = val.data(); + for (int i = 0; i < 100; i++) { + val = cmStrCat(std::move(val), "cmStrCat move"); + expect += "cmStrCat move"; + } + assert_ok((val.data() == data), "cmStrCat move"); + assert_string(val, expect, "cmStrCat move"); + } // ---------------------------------------------------------------------- // Test cmWrap diff --git a/Tests/Cuda/Toolkit/CMakeLists.txt b/Tests/Cuda/Toolkit/CMakeLists.txt index 4df29fa..e6b298d 100644 --- a/Tests/Cuda/Toolkit/CMakeLists.txt +++ b/Tests/Cuda/Toolkit/CMakeLists.txt @@ -30,6 +30,8 @@ endif() foreach (cuda_lib IN LISTS cuda_libs) if(NOT CUDA_${cuda_lib}_LIBRARY) message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY not found") + elseif(CUDA_${cuda_lib}_LIBRARY MATCHES [[\.\./]]) + message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY[\"${CUDA_${cuda_lib}_LIBRARY}\"] to not contain /..") endif() if(NOT TARGET CUDA::${cuda_lib}) message(FATAL_ERROR "expected CUDAToolkit target CUDA::${cuda_lib} not found") @@ -41,6 +43,9 @@ if(CUDAToolkit_VERSION_MAJOR VERSION_LESS 11) list(APPEND npp_libs nppicom) endif() foreach (cuda_lib IN LISTS npp_libs) + if(CUDA_${cuda_lib}_LIBRARY MATCHES [[\.\./]]) + message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY[\"${CUDA_${cuda_lib}_LIBRARY}\"] to not contain /..") + endif() if(NOT TARGET CUDA::${cuda_lib}) message(FATAL_ERROR "The CUDA::${cuda_lib} target was expected but couldn't be found") endif() diff --git a/Tests/CudaOnly/Toolkit/CMakeLists.txt b/Tests/CudaOnly/Toolkit/CMakeLists.txt index 1486c1a..ddf940b 100644 --- a/Tests/CudaOnly/Toolkit/CMakeLists.txt +++ b/Tests/CudaOnly/Toolkit/CMakeLists.txt @@ -28,6 +28,8 @@ endif() foreach (cuda_lib IN LISTS cuda_libs) if(NOT CUDA_${cuda_lib}_LIBRARY) message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY not found") + elseif(CUDA_${cuda_lib}_LIBRARY MATCHES [[\.\./]]) + message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY[\"${CUDA_${cuda_lib}_LIBRARY}\"] to not contain /..") endif() if(NOT TARGET CUDA::${cuda_lib}) message(FATAL_ERROR "expected CUDAToolkit target CUDA::${cuda_lib} not found") @@ -41,6 +43,8 @@ endif() foreach (cuda_lib ) if(NOT CUDA_${cuda_lib}_LIBRARY) message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY not found") + elseif(CUDA_${cuda_lib}_LIBRARY MATCHES [[\.\./]]) + message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY[\"${CUDA_${cuda_lib}_LIBRARY}\"] to not contain /..") endif() if(NOT TARGET CUDA::${cuda_lib}) message(FATAL_ERROR "expected CUDAToolkit target CUDA::${cuda_lib} not found") @@ -50,6 +54,8 @@ endforeach() foreach (cuda_lib nvrtc nvToolsExt OpenCL) if(NOT CUDA_${cuda_lib}_LIBRARY) message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY not found") + elseif(CUDA_${cuda_lib}_LIBRARY MATCHES [[\.\./]]) + message(FATAL_ERROR "expected CUDAToolkit variable CUDA_${cuda_lib}_LIBRARY[\"${CUDA_${cuda_lib}_LIBRARY}\"] to not contain /..") endif() if(NOT TARGET CUDA::${cuda_lib}) |