diff options
Diffstat (limited to 'Source')
178 files changed, 9245 insertions, 3955 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 95b07cb..c268a92 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -4,7 +4,7 @@ # To ensure maximum portability across various compilers and platforms # 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") +if(NOT CMAKE_SYSTEM_NAME STREQUAL "QNX") set(CMAKE_C_EXTENSIONS FALSE) set(CMAKE_CXX_EXTENSIONS FALSE) endif() @@ -31,70 +31,48 @@ if(CMAKE_SYSTEM_NAME STREQUAL "AIX") set(CMake_USE_XCOFF_PARSER 1) endif() +# Watcom support +if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(CMAKE_USE_WMAKE 1) +endif() + +set(CMake_STAT_HAS_ST_MTIM ${KWSYS_CXX_STAT_HAS_ST_MTIM_COMPILED}) +set(CMake_STAT_HAS_ST_MTIMESPEC ${KWSYS_CXX_STAT_HAS_ST_MTIMESPEC_COMPILED}) + set(EXECUTABLE_OUTPUT_PATH ${CMake_BIN_DIR}) if(WIN32) # ensure Unicode friendly APIs are used on Windows - add_definitions(-DUNICODE -D_UNICODE) + add_compile_definitions(UNICODE _UNICODE) # minimize windows.h content - add_definitions(-DWIN32_LEAN_AND_MEAN) + add_compile_definitions(WIN32_LEAN_AND_MEAN) endif() # configure the .dox.in file if(CMake_BUILD_DEVELOPER_REFERENCE) - configure_file( - "${CMake_SOURCE_DIR}/Source/dir.dox.in" - "${CMake_BINARY_DIR}/Source/dir.dox" - @ONLY - ) + configure_file(dir.dox.in dir.dox @ONLY) endif() # configure the .h file -configure_file( - "${CMake_SOURCE_DIR}/Source/cmConfigure.cmake.h.in" - "${CMake_BINARY_DIR}/Source/cmConfigure.h" - ) -configure_file( - "${CMake_SOURCE_DIR}/Source/cmVersionConfig.h.in" - "${CMake_BINARY_DIR}/Source/cmVersionConfig.h" - ) -configure_file( - "${CMake_SOURCE_DIR}/Source/CPack/cmCPackConfigure.h.in" - "${CMake_BINARY_DIR}/Source/CPack/cmCPackConfigure.h" - ) +configure_file(cmConfigure.cmake.h.in cmConfigure.h) +configure_file(cmVersionConfig.h.in cmVersionConfig.h) # Tell CMake executable in the build tree where to find the source tree. configure_file( - "${CMake_SOURCE_DIR}/Source/CMakeSourceDir.txt.in" - "${CMake_BINARY_DIR}/CMakeFiles/CMakeSourceDir.txt" @ONLY - ) - -# add the include path to find the .h -include_directories( - "${CMake_BINARY_DIR}/Source" - "${CMake_SOURCE_DIR}/Source" - "${CMake_SOURCE_DIR}/Source/LexerParser" - ${CMAKE_ZLIB_INCLUDES} - ${CMAKE_EXPAT_INCLUDES} - ${CMAKE_TAR_INCLUDES} - ${CMake_HAIKU_INCLUDE_DIRS} + CMakeSourceDir.txt.in + "${CMake_BINARY_DIR}/CMakeFiles/CMakeSourceDir.txt" + @ONLY ) -# Check if we can build the Mach-O parser. -if(CMake_USE_MACH_PARSER) - set(MACH_SRCS cmMachO.h cmMachO.cxx) -endif() - -# Check if we can build the XCOFF parser. -if(CMake_USE_XCOFF_PARSER) - set(XCOFF_SRCS cmXCOFF.h cmXCOFF.cxx) -endif() +# Add a dummy library and add sources later depends on condition +add_library(ManifestLib INTERFACE) # -# Sources for CMakeLib +# create a library used by the command line and the GUI # -set(SRCS +add_library( + CMakeLib # Lexers/Parsers LexerParser/cmCommandArgumentLexer.cxx LexerParser/cmCommandArgumentLexer.h @@ -168,7 +146,9 @@ set(SRCS cmCMakePresetsGraphReadJSON.cxx cmCMakePresetsGraphReadJSONBuildPresets.cxx cmCMakePresetsGraphReadJSONConfigurePresets.cxx + cmCMakePresetsGraphReadJSONPackagePresets.cxx cmCMakePresetsGraphReadJSONTestPresets.cxx + cmCMakePresetsGraphReadJSONWorkflowPresets.cxx cmCommandArgumentParserHelper.cxx cmCommonTargetGenerator.cxx cmCommonTargetGenerator.h @@ -197,6 +177,8 @@ set(SRCS cmCustomCommandLines.cxx cmCustomCommandLines.h cmCustomCommandTypes.h + cmCxxModuleMapper.cxx + cmCxxModuleMapper.h cmDefinitions.cxx cmDefinitions.h cmDependencyProvider.h @@ -367,7 +349,6 @@ set(SRCS cmRulePlaceholderExpander.h cmLocalUnixMakefileGenerator3.cxx cmLocale.h - ${MACH_SRCS} cmMakefile.cxx cmMakefile.h cmMakefileTargetGenerator.cxx @@ -467,7 +448,6 @@ set(SRCS cmWorkerPool.h cmWorkingDirectory.cxx cmWorkingDirectory.h - ${XCOFF_SRCS} cmXMLParser.cxx cmXMLParser.h cmXMLSafe.cxx @@ -543,6 +523,8 @@ set(SRCS cmExecuteProcessCommand.h cmExpandedCommandArgument.cxx cmExpandedCommandArgument.h + cmExperimental.cxx + cmExperimental.h cmExportCommand.cxx cmExportCommand.h cmExportLibraryDependenciesCommand.cxx @@ -567,6 +549,8 @@ set(SRCS cmFindProgramCommand.h cmForEachCommand.cxx cmForEachCommand.h + cmBlockCommand.cxx + cmBlockCommand.h cmFunctionBlocker.cxx cmFunctionBlocker.h cmFunctionCommand.cxx @@ -603,6 +587,8 @@ set(SRCS cmInstallCommand.h cmInstallCommandArguments.cxx cmInstallCommandArguments.h + cmInstallCxxModuleBmiGenerator.cxx + cmInstallCxxModuleBmiGenerator.h cmInstallFilesCommand.cxx cmInstallFilesCommand.h cmInstallProgramsCommand.cxx @@ -721,6 +707,23 @@ set(SRCS cmWhileCommand.h cmWriteFileCommand.cxx cmWriteFileCommand.h + # Ninja support + cmScanDepFormat.cxx + cmGlobalNinjaGenerator.cxx + cmGlobalNinjaGenerator.h + cmNinjaTypes.h + cmLocalNinjaGenerator.cxx + cmLocalNinjaGenerator.h + cmNinjaTargetGenerator.cxx + cmNinjaTargetGenerator.h + cmNinjaNormalTargetGenerator.cxx + cmNinjaNormalTargetGenerator.h + cmNinjaUtilityTargetGenerator.cxx + cmNinjaUtilityTargetGenerator.h + cmNinjaLinkLineComputer.cxx + cmNinjaLinkLineComputer.h + cmNinjaLinkLineDeviceComputer.cxx + cmNinjaLinkLineDeviceComputer.h cm_get_date.h cm_get_date.c @@ -734,102 +737,152 @@ set(SRCS bindexplib.cxx ) +target_include_directories( + CMakeLib + PUBLIC + # add the include path to find the .h + "${CMAKE_CURRENT_BINARY_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/LexerParser" + ${CMake_HAIKU_INCLUDE_DIRS} + ) +target_link_libraries( + CMakeLib + PUBLIC + cmstd + cmsys + CURL::libcurl + EXPAT::EXPAT + JsonCpp::JsonCpp + $<TARGET_NAME_IF_EXISTS:kwiml::kwiml> + LibArchive::LibArchive + LibRHash::LibRHash + LibUV::LibUV + Threads::Threads + ZLIB::ZLIB + ) -SET_PROPERTY(SOURCE cmProcessOutput.cxx cmWindowsRegistry.cxx APPEND PROPERTY COMPILE_DEFINITIONS - KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE}) +# Check if we can build the Mach-O parser. +if(CMake_USE_MACH_PARSER) + target_sources( + CMakeLib + PRIVATE + cmMachO.h + cmMachO.cxx + ) +endif() + +# Check if we can build the XCOFF parser. +if(CMake_USE_XCOFF_PARSER) + target_sources( + CMakeLib + PRIVATE + cmXCOFF.h + cmXCOFF.cxx + ) +endif() # Xcode only works on Apple if(APPLE) - set(SRCS ${SRCS} - cmXCodeObject.cxx - cmXCode21Object.cxx - cmXCodeScheme.cxx - cmGlobalXCodeGenerator.cxx - cmGlobalXCodeGenerator.h - cmLocalXCodeGenerator.cxx - cmLocalXCodeGenerator.h) + target_sources( + CMakeLib + PRIVATE + cmXCodeObject.cxx + cmXCode21Object.cxx + cmXCodeScheme.cxx + cmGlobalXCodeGenerator.cxx + cmGlobalXCodeGenerator.h + cmLocalXCodeGenerator.cxx + cmLocalXCodeGenerator.h + ) endif() - -if (WIN32) - set(SRCS ${SRCS} - cmCallVisualStudioMacro.cxx - cmCallVisualStudioMacro.h +if(WIN32) + target_sources( + CMakeLib + PRIVATE + cmCallVisualStudioMacro.cxx + cmCallVisualStudioMacro.h ) if(NOT UNIX) - set(SRCS ${SRCS} - cmGlobalBorlandMakefileGenerator.cxx - cmGlobalBorlandMakefileGenerator.h - cmGlobalMSYSMakefileGenerator.cxx - cmGlobalMinGWMakefileGenerator.cxx - cmGlobalNMakeMakefileGenerator.cxx - cmGlobalNMakeMakefileGenerator.h - cmGlobalJOMMakefileGenerator.cxx - cmGlobalJOMMakefileGenerator.h - cmGlobalVisualStudio71Generator.cxx - cmGlobalVisualStudio71Generator.h - cmGlobalVisualStudio7Generator.cxx - cmGlobalVisualStudio7Generator.h - cmGlobalVisualStudio8Generator.cxx - cmGlobalVisualStudio8Generator.h - cmGlobalVisualStudio9Generator.cxx - cmGlobalVisualStudio9Generator.h - cmVisualStudioGeneratorOptions.h - cmVisualStudioGeneratorOptions.cxx - cmVsProjectType.h - cmVisualStudio10TargetGenerator.h - cmVisualStudio10TargetGenerator.cxx - cmLocalVisualStudio10Generator.cxx - cmLocalVisualStudio10Generator.h - cmGlobalVisualStudio10Generator.h - cmGlobalVisualStudio10Generator.cxx - cmGlobalVisualStudio11Generator.h - cmGlobalVisualStudio11Generator.cxx - cmGlobalVisualStudio12Generator.h - cmGlobalVisualStudio12Generator.cxx - cmGlobalVisualStudio14Generator.h - cmGlobalVisualStudio14Generator.cxx - cmGlobalVisualStudioGenerator.cxx - cmGlobalVisualStudioGenerator.h - cmGlobalVisualStudioVersionedGenerator.h - cmGlobalVisualStudioVersionedGenerator.cxx - cmIDEFlagTable.h - cmIDEOptions.cxx - cmIDEOptions.h - cmLocalVisualStudio7Generator.cxx - cmLocalVisualStudio7Generator.h - cmLocalVisualStudioGenerator.cxx - cmLocalVisualStudioGenerator.h - cmVisualStudioSlnData.h - cmVisualStudioSlnData.cxx - cmVisualStudioSlnParser.h - cmVisualStudioSlnParser.cxx - cmVisualStudioWCEPlatformParser.h - cmVisualStudioWCEPlatformParser.cxx - cmVSSetupHelper.cxx - cmVSSetupHelper.h + target_sources( + CMakeLib + PRIVATE + cmGlobalBorlandMakefileGenerator.cxx + cmGlobalBorlandMakefileGenerator.h + cmGlobalMSYSMakefileGenerator.cxx + cmGlobalMinGWMakefileGenerator.cxx + cmGlobalNMakeMakefileGenerator.cxx + cmGlobalNMakeMakefileGenerator.h + cmGlobalJOMMakefileGenerator.cxx + cmGlobalJOMMakefileGenerator.h + cmGlobalVisualStudio71Generator.cxx + cmGlobalVisualStudio71Generator.h + cmGlobalVisualStudio7Generator.cxx + cmGlobalVisualStudio7Generator.h + cmGlobalVisualStudio8Generator.cxx + cmGlobalVisualStudio8Generator.h + cmGlobalVisualStudio9Generator.cxx + cmGlobalVisualStudio9Generator.h + cmVisualStudioGeneratorOptions.h + cmVisualStudioGeneratorOptions.cxx + cmVsProjectType.h + cmVisualStudio10TargetGenerator.h + cmVisualStudio10TargetGenerator.cxx + cmLocalVisualStudio10Generator.cxx + cmLocalVisualStudio10Generator.h + cmGlobalVisualStudio10Generator.h + cmGlobalVisualStudio10Generator.cxx + cmGlobalVisualStudio11Generator.h + cmGlobalVisualStudio11Generator.cxx + cmGlobalVisualStudio12Generator.h + cmGlobalVisualStudio12Generator.cxx + cmGlobalVisualStudio14Generator.h + cmGlobalVisualStudio14Generator.cxx + cmGlobalVisualStudioGenerator.cxx + cmGlobalVisualStudioGenerator.h + cmGlobalVisualStudioVersionedGenerator.h + cmGlobalVisualStudioVersionedGenerator.cxx + cmIDEFlagTable.h + cmIDEOptions.cxx + cmIDEOptions.h + cmLocalVisualStudio7Generator.cxx + cmLocalVisualStudio7Generator.h + cmLocalVisualStudioGenerator.cxx + cmLocalVisualStudioGenerator.h + cmVisualStudioSlnData.h + cmVisualStudioSlnData.cxx + cmVisualStudioSlnParser.h + cmVisualStudioSlnParser.cxx + cmVisualStudioWCEPlatformParser.h + cmVisualStudioWCEPlatformParser.cxx + cmVSSetupHelper.cxx + cmVSSetupHelper.h ) # Add a manifest file to executables on Windows to allow for # GetVersion to work properly on Windows 8 and above. - set(MANIFEST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake.version.manifest) + target_sources(ManifestLib INTERFACE cmake.version.manifest) endif() -endif () +endif() # Watcom support -if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin") - set_property(SOURCE cmake.cxx APPEND PROPERTY COMPILE_DEFINITIONS CMAKE_USE_WMAKE) - list(APPEND SRCS - cmGlobalWatcomWMakeGenerator.cxx - cmGlobalWatcomWMakeGenerator.h +if(CMAKE_USE_WMAKE) + target_sources( + CMakeLib + PRIVATE + cmGlobalWatcomWMakeGenerator.cxx + cmGlobalWatcomWMakeGenerator.h ) endif() # GHS support # Works only for windows and linux if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(SRCS ${SRCS} + target_sources( + CMakeLib + PRIVATE cmGlobalGhsMultiGenerator.cxx cmGlobalGhsMultiGenerator.h cmLocalGhsMultiGenerator.cxx @@ -841,104 +894,45 @@ if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux") ) endif() - -# Ninja support -set(SRCS ${SRCS} - cmScanDepFormat.cxx - cmGlobalNinjaGenerator.cxx - cmGlobalNinjaGenerator.h - cmNinjaTypes.h - cmLocalNinjaGenerator.cxx - cmLocalNinjaGenerator.h - cmNinjaTargetGenerator.cxx - cmNinjaTargetGenerator.h - cmNinjaNormalTargetGenerator.cxx - cmNinjaNormalTargetGenerator.h - cmNinjaUtilityTargetGenerator.cxx - cmNinjaUtilityTargetGenerator.h - cmNinjaLinkLineComputer.cxx - cmNinjaLinkLineComputer.h - cmNinjaLinkLineDeviceComputer.cxx - cmNinjaLinkLineDeviceComputer.h - ) - # Temporary variable for tools targets set(_tools) if(WIN32 AND NOT CYGWIN) set_source_files_properties(cmcldeps.cxx PROPERTIES COMPILE_DEFINITIONS _WIN32_WINNT=0x0501) - add_executable(cmcldeps cmcldeps.cxx ${MANIFEST_FILE}) + add_executable(cmcldeps cmcldeps.cxx) + target_link_libraries(cmcldeps PRIVATE CMakeLib ManifestLib) list(APPEND _tools cmcldeps) - target_link_libraries(cmcldeps CMakeLib) endif() -foreach(v CURL_CA_BUNDLE CURL_CA_PATH) - if(${v}) - set_property(SOURCE cmCurl.cxx APPEND PROPERTY COMPILE_DEFINITIONS ${v}="${${v}}") - endif() -endforeach() - -foreach(check - STAT_HAS_ST_MTIM - STAT_HAS_ST_MTIMESPEC - ) - if(KWSYS_CXX_${check}_COMPILED) # abuse KWSys check cache entry - set(CMake_${check} 1) - else() - set(CMake_${check} 0) - endif() - set_property(SOURCE cmFileTime.cxx APPEND PROPERTY - COMPILE_DEFINITIONS CMake_${check}=${CMake_${check}}) -endforeach() - -# create a library used by the command line and the GUI -add_library(CMakeLib ${SRCS}) -target_link_libraries(CMakeLib cmsys - ${CMAKE_STD_LIBRARY} - ${CMAKE_EXPAT_LIBRARIES} ${CMAKE_ZLIB_LIBRARIES} - ${CMAKE_TAR_LIBRARIES} - ${CMAKE_CURL_LIBRARIES} - ${CMAKE_JSONCPP_LIBRARIES} - ${CMAKE_LIBUV_LIBRARIES} - ${CMAKE_LIBRHASH_LIBRARIES} - ${CMake_KWIML_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} - ) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "sparc") # the atomic instructions are implemented using libatomic on some platforms, # so linking to that may be required check_library_exists(atomic __atomic_fetch_add_4 "" LIBATOMIC_NEEDED) if(LIBATOMIC_NEEDED) - target_link_libraries(CMakeLib atomic) + target_link_libraries(CMakeLib PUBLIC atomic) endif() endif() # On Apple we need CoreFoundation and CoreServices if(APPLE) - target_link_libraries(CMakeLib "-framework CoreFoundation") - target_link_libraries(CMakeLib "-framework CoreServices") + target_link_libraries(CMakeLib PUBLIC "-framework CoreFoundation") + target_link_libraries(CMakeLib PUBLIC "-framework CoreServices") endif() if(WIN32 AND NOT UNIX) # We need the rpcrt4 library on Windows. # We need the crypt32 library on Windows for crypto/cert APIs. - target_link_libraries(CMakeLib rpcrt4 crypt32) + target_link_libraries(CMakeLib PUBLIC rpcrt4 crypt32) endif() target_compile_definitions(CMakeLib PUBLIC ${CLANG_TIDY_DEFINITIONS}) # -# CTestLib -# -include_directories( - "${CMake_SOURCE_DIR}/Source/CTest" - ${CMAKE_CURL_INCLUDES} - ) -# -# Sources for CTestLib +# Build CTestLib # -set(CTEST_SRCS cmCTest.cxx +add_library( + CTestLib + cmCTest.cxx CTest/cmProcess.cxx CTest/cmCTestBinPacker.cxx CTest/cmCTestBuildAndTestHandler.cxx @@ -1005,21 +999,18 @@ set(CTEST_SRCS cmCTest.cxx LexerParser/cmCTestResourceGroupsLexer.h LexerParser/cmCTestResourceGroupsLexer.in.l ) - -# Build CTestLib -add_library(CTestLib ${CTEST_SRCS}) -target_link_libraries(CTestLib CMakeLib ${CMAKE_CURL_LIBRARIES}) - -# -# CPack -# -include_directories( - "${CMake_SOURCE_DIR}/Source/CPack" +target_include_directories( + CTestLib + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/CTest" ) +target_link_libraries(CTestLib PUBLIC CMakeLib) + # -# Sources for CPack +# Build CPackLib # -set(CPACK_SRCS +add_library( + CPackLib CPack/cmCPackArchiveGenerator.cxx CPack/cmCPackComponentGroup.cxx CPack/cmCPackDebGenerator.cxx @@ -1030,9 +1021,7 @@ set(CPACK_SRCS CPack/cmCPackNSISGenerator.cxx CPack/cmCPackNuGetGenerator.cxx CPack/cmCPackSTGZGenerator.cxx - ) -# CPack IFW generator -set(CPACK_SRCS ${CPACK_SRCS} + # CPack IFW generator CPack/IFW/cmCPackIFWCommon.cxx CPack/IFW/cmCPackIFWCommon.h CPack/IFW/cmCPackIFWGenerator.cxx @@ -1044,19 +1033,20 @@ set(CPACK_SRCS ${CPACK_SRCS} CPack/IFW/cmCPackIFWRepository.cxx CPack/IFW/cmCPackIFWRepository.h ) - -if(CYGWIN) - set(CPACK_SRCS ${CPACK_SRCS} - CPack/cmCPackCygwinBinaryGenerator.cxx - CPack/cmCPackCygwinSourceGenerator.cxx - ) -endif() +target_include_directories( + CPackLib + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/CPack" + "${CMAKE_CURRENT_BINARY_DIR}/CPack" + ) +target_link_libraries(CPackLib PUBLIC CMakeLib) option(CPACK_ENABLE_FREEBSD_PKG "Add FreeBSD pkg(8) generator to CPack." OFF) - if(UNIX) - set(CPACK_SRCS ${CPACK_SRCS} - CPack/cmCPackRPMGenerator.cxx + target_sources( + CPackLib + PRIVATE + CPack/cmCPackRPMGenerator.cxx ) # Optionally, try to use pkg(8) @@ -1072,13 +1062,14 @@ if(UNIX) pkg DOC "FreeBSD pkg(8) library") if(FREEBSD_PKG_LIBRARIES) - set(CPACK_SRCS ${CPACK_SRCS} - CPack/cmCPackFreeBSDGenerator.cxx - ) + set(ENABLE_BUILD_FREEBSD_PKG 1) + target_sources(CPackLib PRIVATE CPack/cmCPackFreeBSDGenerator.cxx) + target_include_directories(CPackLib PUBLIC ${FREEBSD_PKG_INCLUDE_DIRS}) + target_link_libraries(CPackLib PUBLIC ${FREEBSD_PKG_LIBRARIES}) endif() endif() - if (NOT FREEBSD_PKG_INCLUDE_DIRS OR NOT FREEBSD_PKG_LIBRARIES) + if(NOT FREEBSD_PKG_INCLUDE_DIRS OR NOT FREEBSD_PKG_LIBRARIES) message(FATAL_ERROR "CPack needs libpkg(3) to produce FreeBSD packages natively.") endif() else() @@ -1088,47 +1079,57 @@ if(UNIX) endif() if(CYGWIN) + target_sources( + CPackLib + PRIVATE + CPack/cmCPackCygwinBinaryGenerator.cxx + CPack/cmCPackCygwinSourceGenerator.cxx + ) find_package(LibUUID) endif() -if(WIN32 OR (CYGWIN AND LibUUID_FOUND)) - set(CPACK_SRCS ${CPACK_SRCS} - CPack/WiX/cmCMakeToWixPath.cxx - CPack/WiX/cmCMakeToWixPath.h - CPack/WiX/cmCPackWIXGenerator.cxx - CPack/WiX/cmCPackWIXGenerator.h - CPack/WiX/cmWIXAccessControlList.cxx - CPack/WiX/cmWIXAccessControlList.h - CPack/WiX/cmWIXDirectoriesSourceWriter.cxx - CPack/WiX/cmWIXDirectoriesSourceWriter.h - CPack/WiX/cmWIXFeaturesSourceWriter.cxx - CPack/WiX/cmWIXFeaturesSourceWriter.h - CPack/WiX/cmWIXFilesSourceWriter.cxx - CPack/WiX/cmWIXFilesSourceWriter.h - CPack/WiX/cmWIXPatch.cxx - CPack/WiX/cmWIXPatch.h - CPack/WiX/cmWIXPatchParser.cxx - CPack/WiX/cmWIXPatchParser.h - CPack/WiX/cmWIXRichTextFormatWriter.cxx - CPack/WiX/cmWIXRichTextFormatWriter.h - CPack/WiX/cmWIXShortcut.cxx - CPack/WiX/cmWIXShortcut.h - CPack/WiX/cmWIXSourceWriter.cxx - CPack/WiX/cmWIXSourceWriter.h + +if(WIN32 OR (CYGWIN AND TARGET LibUUID::LibUUID)) + set(ENABLE_BUILD_WIX_GENERATOR 1) + target_sources( + CPackLib + PRIVATE + CPack/WiX/cmCMakeToWixPath.cxx + CPack/WiX/cmCMakeToWixPath.h + CPack/WiX/cmCPackWIXGenerator.cxx + CPack/WiX/cmCPackWIXGenerator.h + CPack/WiX/cmWIXAccessControlList.cxx + CPack/WiX/cmWIXAccessControlList.h + CPack/WiX/cmWIXDirectoriesSourceWriter.cxx + CPack/WiX/cmWIXDirectoriesSourceWriter.h + CPack/WiX/cmWIXFeaturesSourceWriter.cxx + CPack/WiX/cmWIXFeaturesSourceWriter.h + CPack/WiX/cmWIXFilesSourceWriter.cxx + CPack/WiX/cmWIXFilesSourceWriter.h + CPack/WiX/cmWIXPatch.cxx + CPack/WiX/cmWIXPatch.h + CPack/WiX/cmWIXPatchParser.cxx + CPack/WiX/cmWIXPatchParser.h + CPack/WiX/cmWIXRichTextFormatWriter.cxx + CPack/WiX/cmWIXRichTextFormatWriter.h + CPack/WiX/cmWIXShortcut.cxx + CPack/WiX/cmWIXShortcut.h + CPack/WiX/cmWIXSourceWriter.cxx + CPack/WiX/cmWIXSourceWriter.h ) + target_link_libraries(CPackLib PUBLIC $<TARGET_NAME_IF_EXISTS:LibUUID::LibUUID>) endif() if(APPLE) - set(CPACK_SRCS ${CPACK_SRCS} - CPack/cmCPackBundleGenerator.cxx - CPack/cmCPackDragNDropGenerator.cxx - CPack/cmCPackPKGGenerator.cxx - CPack/cmCPackProductBuildGenerator.cxx + target_sources( + CPackLib + PRIVATE + CPack/cmCPackBundleGenerator.cxx + CPack/cmCPackDragNDropGenerator.cxx + CPack/cmCPackPKGGenerator.cxx + CPack/cmCPackProductBuildGenerator.cxx ) endif() -# Build CPackLib -add_library(CPackLib ${CPACK_SRCS}) -target_link_libraries(CPackLib CMakeLib) if(APPLE) # Some compilers produce errors in the CoreServices framework headers. # Ideally such errors should be fixed by either the compiler vendor @@ -1136,8 +1137,7 @@ if(APPLE) # If it does not work, build with reduced functionality and warn. check_include_file("CoreServices/CoreServices.h" HAVE_CoreServices) if(HAVE_CoreServices) - set_property(SOURCE CPack/cmCPackDragNDropGenerator.cxx PROPERTY COMPILE_DEFINITIONS HAVE_CoreServices) - target_link_libraries(CPackLib "-framework CoreServices") + target_link_libraries(CPackLib PUBLIC "-framework CoreServices") else() message(WARNING "This compiler does not appear to support\n" " #include <CoreServices/CoreServices.h>\n" @@ -1145,31 +1145,25 @@ if(APPLE) "See CMakeFiles/CMakeError.log for details of the failure.") endif() endif() -if(CYGWIN AND LibUUID_FOUND) - target_link_libraries(CPackLib ${LibUUID_LIBRARIES}) - include_directories(CPackLib ${LibUUID_INCLUDE_DIRS}) - set_property(SOURCE CPack/cmCPackGeneratorFactory.cxx PROPERTY COMPILE_DEFINITIONS HAVE_LIBUUID) -endif() -if(CPACK_ENABLE_FREEBSD_PKG AND FREEBSD_PKG_INCLUDE_DIRS AND FREEBSD_PKG_LIBRARIES) - target_link_libraries(CPackLib ${FREEBSD_PKG_LIBRARIES}) - include_directories(${FREEBSD_PKG_INCLUDE_DIRS}) - add_definitions(-DHAVE_FREEBSD_PKG) -endif() + +# Render config header file for CPackLib +configure_file(CPack/cmCPackConfigure.h.in CPack/cmCPackConfigure.h) + # Build CMake executable -add_executable(cmake cmakemain.cxx cmcmd.cxx cmcmd.h ${MANIFEST_FILE}) +add_executable(cmake cmakemain.cxx cmcmd.cxx cmcmd.h) +target_link_libraries(cmake PRIVATE CMakeLib ManifestLib) list(APPEND _tools cmake) -target_link_libraries(cmake CMakeLib) # Build CTest executable -add_executable(ctest ctest.cxx ${MANIFEST_FILE}) +add_executable(ctest ctest.cxx) +target_link_libraries(ctest PRIVATE CTestLib ManifestLib) list(APPEND _tools ctest) -target_link_libraries(ctest CTestLib) # Build CPack executable -add_executable(cpack CPack/cpack.cxx ${MANIFEST_FILE}) +add_executable(cpack CPack/cpack.cxx) +target_link_libraries(cpack PRIVATE CPackLib ManifestLib) list(APPEND _tools cpack) -target_link_libraries(cpack CPackLib) # Curses GUI if(BUILD_CursesDialog) @@ -1182,8 +1176,8 @@ if(BUILD_QtDialog) add_subdirectory(QtDialog) endif() -include (${CMake_BINARY_DIR}/Source/LocalUserOptions.cmake OPTIONAL) -include (${CMake_SOURCE_DIR}/Source/LocalUserOptions.cmake OPTIONAL) +include(${CMAKE_CURRENT_BINARY_DIR}/LocalUserOptions.cmake OPTIONAL) +include(${CMAKE_CURRENT_SOURCE_DIR}/LocalUserOptions.cmake OPTIONAL) if(WIN32) # Compute the binary version that appears in the RC file. Version @@ -1202,14 +1196,14 @@ if(WIN32) set(CMake_RCVERSION_STR ${CMake_VERSION}) # Add Windows executable version information. - configure_file("CMakeVersion.rc.in" "CMakeVersion.rc" @ONLY) + configure_file(CMakeVersion.rc.in CMakeVersion.rc @ONLY) # We use a separate object library for this to work around a limitation of # MinGW's windres tool with spaces in the path to the include directories. add_library(CMakeVersion OBJECT "${CMAKE_CURRENT_BINARY_DIR}/CMakeVersion.rc") set_property(TARGET CMakeVersion PROPERTY INCLUDE_DIRECTORIES "") - foreach(_tool ${_tools}) - target_sources(${_tool} PRIVATE $<TARGET_OBJECTS:CMakeVersion>) + foreach(_tool IN LISTS _tools) + target_link_libraries(${_tool} PRIVATE CMakeVersion) endforeach() endif() @@ -1220,7 +1214,7 @@ endif() # Install tools -foreach(_tool ${_tools}) +foreach(_tool IN LISTS _tools) CMake_OPTIONAL_COMPONENT(${_tool}) install(TARGETS ${_tool} DESTINATION ${CMAKE_BIN_DIR} ${COMPONENT}) endforeach() diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 889db6e..5b9df91 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 24) -set(CMake_VERSION_PATCH 3) +set(CMake_VERSION_MINOR 25) +set(CMake_VERSION_PATCH 1) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) @@ -9,7 +9,7 @@ set(CMake_VERSION_IS_DIRTY 0) set(CMake_VERSION "${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}.${CMake_VERSION_PATCH}") if(DEFINED CMake_VERSION_RC) - set(CMake_VERSION "${CMake_VERSION}-rc${CMake_VERSION_RC}") + string(APPEND CMake_VERSION "-rc${CMake_VERSION_RC}") endif() # Releases define a small patch level. @@ -53,7 +53,7 @@ if(NOT CMake_VERSION_NO_GIT) # If this is not the exact commit of a release, add dev info. if(NOT "${git_subject}" MATCHES "^[Cc][Mm]ake ${CMake_VERSION}$") - set(CMake_VERSION "${CMake_VERSION}-g${git_hash}") + string(APPEND CMake_VERSION "-g${git_hash}") endif() # If this is a work tree, check whether it is dirty. @@ -68,7 +68,7 @@ if(NOT CMake_VERSION_NO_GIT) # No commit information. if(NOT CMake_VERSION_IS_RELEASE) # Generic development version. - set(CMake_VERSION "${CMake_VERSION}-git") + string(APPEND CMake_VERSION "-git") endif() endif() endif() @@ -80,5 +80,5 @@ else() set(CMake_VERSION_SUFFIX "") endif() if(CMake_VERSION_IS_DIRTY) - set(CMake_VERSION ${CMake_VERSION}-dirty) + string(APPEND CMake_VERSION "-dirty") endif() diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx index 56e8463..894c24b 100644 --- a/Source/CPack/cmCPackArchiveGenerator.cxx +++ b/Source/CPack/cmCPackArchiveGenerator.cxx @@ -94,6 +94,18 @@ std::string cmCPackArchiveGenerator::GetArchiveComponentFileName( int cmCPackArchiveGenerator::InitializeInternal() { this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "1"); + cmValue newExtensionValue = this->GetOption("CPACK_ARCHIVE_FILE_EXTENSION"); + if (!newExtensionValue.IsEmpty()) { + std::string newExtension = *newExtensionValue; + if (!cmHasLiteralPrefix(newExtension, ".")) { + newExtension = cmStrCat('.', newExtension); + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Using user-provided file extension " + << newExtension << " instead of the default " + << this->OutputExtension << std::endl); + this->OutputExtension = std::move(newExtension); + } return this->Superclass::InitializeInternal(); } diff --git a/Source/CPack/cmCPackConfigure.h.in b/Source/CPack/cmCPackConfigure.h.in index 8ac1661..2c1302d 100644 --- a/Source/CPack/cmCPackConfigure.h.in +++ b/Source/CPack/cmCPackConfigure.h.in @@ -1,2 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#cmakedefine01 ENABLE_BUILD_WIX_GENERATOR +#cmakedefine01 ENABLE_BUILD_FREEBSD_PKG +#cmakedefine01 HAVE_CoreServices diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx index 0f7acfb..0579066 100644 --- a/Source/CPack/cmCPackDragNDropGenerator.cxx +++ b/Source/CPack/cmCPackDragNDropGenerator.cxx @@ -14,6 +14,7 @@ #include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" +#include "cmCPackConfigure.h" #include "cmCPackGenerator.h" #include "cmCPackLog.h" #include "cmDuration.h" @@ -23,7 +24,7 @@ #include "cmValue.h" #include "cmXMLWriter.h" -#ifdef HAVE_CoreServices +#if HAVE_CoreServices // For the old LocaleStringToLangAndRegionCodes() function, to convert // to the old Script Manager RegionCode values needed for the 'LPic' data // structure used for generating multi-lingual SLAs. @@ -590,7 +591,7 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, kCFStringEncodingMacRoman); LangCode lang = 0; RegionCode region = 0; -#ifdef HAVE_CoreServices +#if HAVE_CoreServices OSStatus err = LocaleStringToLangAndRegionCodes(iso_language_cstr, &lang, ®ion); if (err != noErr) @@ -601,7 +602,7 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, << iso_language_cstr << std::endl); return 0; } -#ifdef HAVE_CoreServices +#if HAVE_CoreServices header_data.push_back(region); header_data.push_back(i); header_data.push_back(0); diff --git a/Source/CPack/cmCPackFreeBSDGenerator.cxx b/Source/CPack/cmCPackFreeBSDGenerator.cxx index 607d797..162dfc7 100644 --- a/Source/CPack/cmCPackFreeBSDGenerator.cxx +++ b/Source/CPack/cmCPackFreeBSDGenerator.cxx @@ -23,8 +23,6 @@ // Suffix used to tell libpkg what compression to use static const char FreeBSDPackageCompression[] = "txz"; -// Resulting package file-suffix, for < 1.17 and >= 1.17 versions of libpkg -static const char FreeBSDPackageSuffix_10[] = ".txz"; static const char FreeBSDPackageSuffix_17[] = ".pkg"; cmCPackFreeBSDGenerator::cmCPackFreeBSDGenerator() @@ -83,6 +81,14 @@ public: { if (!isValid()) return false; + // The API in the FreeBSD sources (the header has no documentation), + // is as follows: + // + // int pkg_create(struct pkg_create *pc, const char *metadata, const char + // *plist, bool hash) + // + // We let the plist be determined from what is installed, and all + // the rest comes from the manifest data. int r = pkg_create(d, manifest.c_str(), nullptr, false); return r == 0; } @@ -402,7 +408,8 @@ int cmCPackFreeBSDGenerator::PackageFiles() return 0; } - std::string output_dir = cmSystemTools::CollapseFullPath("../", toplevel); + const std::string output_dir = + cmSystemTools::CollapseFullPath("../", toplevel); PkgCreate package(output_dir, toplevel, manifestname); if (package.isValid()) { if (!package.Create()) { @@ -416,40 +423,33 @@ int cmCPackFreeBSDGenerator::PackageFiles() return 0; } - // Specifically looking for packages suffixed with the TAG, either extension - std::string broken_suffix_10 = - cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_10); + // Specifically looking for packages suffixed with the TAG std::string broken_suffix_17 = cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_17); for (std::string& name : packageFileNames) { cmCPackLogger(cmCPackLog::LOG_DEBUG, "Packagefile " << name << std::endl); - if (cmHasSuffix(name, broken_suffix_10)) { - name.replace(name.size() - broken_suffix_10.size(), std::string::npos, - FreeBSDPackageSuffix_10); - break; - } if (cmHasSuffix(name, broken_suffix_17)) { name.replace(name.size() - broken_suffix_17.size(), std::string::npos, FreeBSDPackageSuffix_17); break; } } - // If the name uses a *new* style name, which doesn't exist, but there - // is an *old* style name, then use that instead. This indicates we used - // an older libpkg, which still creates .txz instead of .pkg files. - for (std::string& name : packageFileNames) { - if (cmHasSuffix(name, FreeBSDPackageSuffix_17) && - !cmSystemTools::FileExists(name)) { - const std::string badSuffix(FreeBSDPackageSuffix_17); - const std::string goodSuffix(FreeBSDPackageSuffix_10); - std::string repairedName(name); - repairedName.replace(repairedName.size() - badSuffix.size(), - std::string::npos, goodSuffix); - if (cmSystemTools::FileExists(repairedName)) { - name = repairedName; - cmCPackLogger(cmCPackLog::LOG_DEBUG, - "Repaired packagefile " << name << std::endl); - } + + const std::string packageFileName = + var_lookup("CPACK_PACKAGE_FILE_NAME") + FreeBSDPackageSuffix_17; + if (packageFileNames.size() == 1 && !packageFileName.empty() && + packageFileNames[0] != packageFileName) { + // Since libpkg always writes <name>-<version>.<suffix>, + // if there is a CPACK_PACKAGE_FILE_NAME set, we need to + // rename, and then re-set the name. + const std::string sourceFile = packageFileNames[0]; + const std::string packageSubDirectory = + cmSystemTools::GetParentDirectory(sourceFile); + const std::string targetFileName = + packageSubDirectory + '/' + packageFileName; + if (cmSystemTools::RenameFile(sourceFile, targetFileName)) { + this->packageFileNames.clear(); + this->packageFileNames.emplace_back(targetFileName); } } diff --git a/Source/CPack/cmCPackGeneratorFactory.cxx b/Source/CPack/cmCPackGeneratorFactory.cxx index 725ea8a..efb94b9 100644 --- a/Source/CPack/cmCPackGeneratorFactory.cxx +++ b/Source/CPack/cmCPackGeneratorFactory.cxx @@ -6,7 +6,7 @@ #include <utility> #include "IFW/cmCPackIFWGenerator.h" -#ifdef HAVE_FREEBSD_PKG +#if ENABLE_BUILD_FREEBSD_PKG # include "cmCPackFreeBSDGenerator.h" #endif #include "cmCPackArchiveGenerator.h" @@ -34,7 +34,7 @@ # include "cmCPackRPMGenerator.h" #endif -#if defined(_WIN32) || (defined(__CYGWIN__) && defined(HAVE_LIBUUID)) +#if ENABLE_BUILD_WIX_GENERATOR # include "WiX/cmCPackWIXGenerator.h" #endif @@ -80,7 +80,7 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory() cmCPackCygwinSourceGenerator::CreateGenerator); } #endif -#if defined(_WIN32) || (defined(__CYGWIN__) && defined(HAVE_LIBUUID)) +#if ENABLE_BUILD_WIX_GENERATOR if (cmCPackWIXGenerator::CanGenerate()) { this->RegisterGenerator("WIX", "MSI file format via WiX tools", cmCPackWIXGenerator::CreateGenerator); @@ -119,7 +119,7 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory() cmCPackRPMGenerator::CreateGenerator); } #endif -#ifdef HAVE_FREEBSD_PKG +#if ENABLE_BUILD_FREEBSD_PKG if (cmCPackFreeBSDGenerator::CanGenerate()) { this->RegisterGenerator("FREEBSD", "FreeBSD pkg(8) packages", cmCPackFreeBSDGenerator::CreateGenerator); diff --git a/Source/CPack/cmCPackGeneratorFactory.h b/Source/CPack/cmCPackGeneratorFactory.h index f3e25a6..52c1b5c 100644 --- a/Source/CPack/cmCPackGeneratorFactory.h +++ b/Source/CPack/cmCPackGeneratorFactory.h @@ -8,6 +8,8 @@ #include <memory> #include <string> +#include "cmCPackConfigure.h" // IWYU pragma: keep + class cmCPackGenerator; class cmCPackLog; diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx index 217f716..6ca5783 100644 --- a/Source/CPack/cmCPackNSISGenerator.cxx +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -242,6 +242,33 @@ int cmCPackNSISGenerator::PackageFiles() this->SetOptionIfNotSet("CPACK_NSIS_LICENSE_PAGE", licenceCode); } + std::string nsisPreArguments; + if (cmValue nsisArguments = + this->GetOption("CPACK_NSIS_EXECUTABLE_PRE_ARGUMENTS")) { + std::vector<std::string> expandedArguments; + cmExpandList(nsisArguments, expandedArguments); + + for (auto& arg : expandedArguments) { + if (!cmHasPrefix(arg, NSIS_OPT)) { + nsisPreArguments = cmStrCat(nsisPreArguments, NSIS_OPT); + } + nsisPreArguments = cmStrCat(nsisPreArguments, arg, ' '); + } + } + + std::string nsisPostArguments; + if (cmValue nsisArguments = + this->GetOption("CPACK_NSIS_EXECUTABLE_POST_ARGUMENTS")) { + std::vector<std::string> expandedArguments; + cmExpandList(nsisArguments, expandedArguments); + for (auto& arg : expandedArguments) { + if (!cmHasPrefix(arg, NSIS_OPT)) { + nsisPostArguments = cmStrCat(nsisPostArguments, NSIS_OPT); + } + nsisPostArguments = cmStrCat(nsisPostArguments, arg, ' '); + } + } + // Setup all of the component sections if (this->Components.empty()) { this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", ""); @@ -358,8 +385,11 @@ int cmCPackNSISGenerator::PackageFiles() this->ConfigureFile(nsisInInstallOptions, nsisInstallOptions); this->ConfigureFile(nsisInFileName, nsisFileName); std::string nsisCmd = - cmStrCat('"', this->GetOption("CPACK_INSTALLER_PROGRAM"), "\" \"", - nsisFileName, '"'); + cmStrCat('"', this->GetOption("CPACK_INSTALLER_PROGRAM"), "\" ", + nsisPreArguments, " \"", nsisFileName, '"'); + if (!nsisPostArguments.empty()) { + nsisCmd = cmStrCat(nsisCmd, " ", nsisPostArguments); + } cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd << std::endl); std::string output; int retVal = 1; diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index 1650368..f06946b 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx @@ -1,6 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ +#include <algorithm> #include <cstddef> #include <functional> #include <iostream> @@ -11,10 +12,12 @@ #include <utility> #include <vector> +#include <cm/optional> #include <cmext/algorithm> #include "cmsys/Encoding.hxx" +#include "cmCMakePresetsGraph.h" #include "cmCPackGenerator.h" #include "cmCPackGeneratorFactory.h" #include "cmCPackLog.h" @@ -50,7 +53,7 @@ const char* cmDocumentationOptions[][2] = { { "-C <Configuration>", "Specify the project configuration" }, { "-D <var>=<value>", "Set a CPack variable." }, { "--config <configFile>", "Specify the config file." }, - { "--verbose,-V", "Enable verbose output" }, + { "-V,--verbose", "Enable verbose output" }, { "--trace", "Put underlying cmake scripts in trace mode." }, { "--trace-expand", "Put underlying cmake scripts in expanded trace mode." }, { "--debug", "Enable debug output (for CPack developers)" }, @@ -58,6 +61,8 @@ const char* cmDocumentationOptions[][2] = { { "-R <packageVersion>", "Override/define CPACK_PACKAGE_VERSION" }, { "-B <packageDirectory>", "Override/define CPACK_PACKAGE_DIRECTORY" }, { "--vendor <vendorName>", "Override/define CPACK_PACKAGE_VENDOR" }, + { "--preset", "Read arguments from a package preset" }, + { "--list-presets", "List available package presets" }, { nullptr, nullptr } }; @@ -116,6 +121,9 @@ int main(int argc, char const* const* argv) std::string cpackProjectVendor; std::string cpackConfigFile; + std::string preset; + bool listPresets = false; + std::map<std::string, std::string> definitions; auto const verboseLambda = [&log](const std::string&, cmake*, @@ -182,6 +190,10 @@ int main(int argc, char const* const* argv) CommandArgument::setToValue(cpackProjectPatch) }, CommandArgument{ "--vendor", CommandArgument::Values::One, CommandArgument::setToValue(cpackProjectVendor) }, + CommandArgument{ "--preset", CommandArgument::Values::One, + CommandArgument::setToValue(preset) }, + CommandArgument{ "--list-presets", CommandArgument::Values::Zero, + CommandArgument::setToTrue(listPresets) }, CommandArgument{ "-D", CommandArgument::Values::One, [&log, &definitions](const std::string& arg, cmake*, @@ -228,6 +240,160 @@ int main(int argc, char const* const* argv) } } + cmCPackGeneratorFactory generators; + generators.SetLogger(&log); + + // Set up presets + if (!preset.empty() || listPresets) { + const auto workingDirectory = cmSystemTools::GetCurrentWorkingDirectory(); + + auto const presetGeneratorsPresent = + [&generators](const cmCMakePresetsGraph::PackagePreset& p) { + return std::all_of(p.Generators.begin(), p.Generators.end(), + [&generators](const std::string& gen) { + return generators.GetGeneratorsList().count( + gen) != 0; + }); + }; + + cmCMakePresetsGraph presetsGraph; + auto result = presetsGraph.ReadProjectPresets(workingDirectory); + if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Could not read presets from " + << workingDirectory << ": " + << cmCMakePresetsGraph::ResultToString(result) + << std::endl); + return 1; + } + + if (listPresets) { + presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); + return 0; + } + + auto presetPair = presetsGraph.PackagePresets.find(preset); + if (presetPair == presetsGraph.PackagePresets.end()) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "No such package preset in " << workingDirectory << ": \"" + << preset << '"' << std::endl); + presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); + return 1; + } + + if (presetPair->second.Unexpanded.Hidden) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Cannot use hidden package preset in " + << workingDirectory << ": \"" << preset << '"' + << std::endl); + presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); + return 1; + } + + auto const& expandedPreset = presetPair->second.Expanded; + if (!expandedPreset) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Could not evaluate package preset \"" + << preset << "\": Invalid macro expansion" << std::endl); + presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); + return 1; + } + + if (!expandedPreset->ConditionResult) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Cannot use disabled package preset in " + << workingDirectory << ": \"" << preset << '"' + << std::endl); + presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); + return 1; + } + + if (!presetGeneratorsPresent(presetPair->second.Unexpanded)) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, "Cannot use preset"); + presetsGraph.PrintPackagePresetList(presetGeneratorsPresent); + return 1; + } + + auto configurePresetPair = + presetsGraph.ConfigurePresets.find(expandedPreset->ConfigurePreset); + if (configurePresetPair == presetsGraph.ConfigurePresets.end()) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "No such configure preset in " + << workingDirectory << ": \"" + << expandedPreset->ConfigurePreset << '"' << std::endl); + presetsGraph.PrintConfigurePresetList(); + return 1; + } + + if (configurePresetPair->second.Unexpanded.Hidden) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Cannot use hidden configure preset in " + << workingDirectory << ": \"" + << expandedPreset->ConfigurePreset << '"' << std::endl); + presetsGraph.PrintConfigurePresetList(); + return 1; + } + + auto const& expandedConfigurePreset = configurePresetPair->second.Expanded; + if (!expandedConfigurePreset) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Could not evaluate configure preset \"" + << expandedPreset->ConfigurePreset + << "\": Invalid macro expansion" << std::endl); + return 1; + } + + cmSystemTools::ChangeDirectory(expandedConfigurePreset->BinaryDir); + + auto presetEnvironment = expandedPreset->Environment; + for (auto const& var : presetEnvironment) { + if (var.second) { + cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second)); + } + } + + if (!expandedPreset->ConfigFile.empty() && cpackConfigFile.empty()) { + cpackConfigFile = expandedPreset->ConfigFile; + } + + if (!expandedPreset->Generators.empty() && generator.empty()) { + generator = cmJoin(expandedPreset->Generators, ";"); + } + + if (!expandedPreset->Configurations.empty() && cpackBuildConfig.empty()) { + cpackBuildConfig = cmJoin(expandedPreset->Configurations, ";"); + } + + definitions.insert(expandedPreset->Variables.begin(), + expandedPreset->Variables.end()); + + if (expandedPreset->DebugOutput == true) { + debugLambda("", &cminst, &globalMF); + } + + if (expandedPreset->VerboseOutput == true) { + verboseLambda("", &cminst, &globalMF); + } + + if (!expandedPreset->PackageName.empty() && cpackProjectName.empty()) { + cpackProjectName = expandedPreset->PackageName; + } + + if (!expandedPreset->PackageVersion.empty() && + cpackProjectVersion.empty()) { + cpackProjectVersion = expandedPreset->PackageVersion; + } + + if (!expandedPreset->PackageDirectory.empty() && + cpackProjectDirectory.empty()) { + cpackProjectDirectory = expandedPreset->PackageDirectory; + } + + if (!expandedPreset->VendorName.empty() && cpackProjectVendor.empty()) { + cpackProjectVendor = expandedPreset->VendorName; + } + } + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, "Read CPack config file: " << cpackConfigFile << std::endl); @@ -238,9 +404,6 @@ int main(int argc, char const* const* argv) cpackConfigFileSpecified = false; } - cmCPackGeneratorFactory generators; - generators.SetLogger(&log); - cmDocumentation doc; doc.addCPackStandardDocSections(); /* Were we invoked to display doc or to do some work ? diff --git a/Source/CTest/cmCTestCoverageCommand.cxx b/Source/CTest/cmCTestCoverageCommand.cxx index 7432d08..97b0a89 100644 --- a/Source/CTest/cmCTestCoverageCommand.cxx +++ b/Source/CTest/cmCTestCoverageCommand.cxx @@ -4,7 +4,6 @@ #include <set> -#include <cmext/algorithm> #include <cmext/string_view> #include "cmCTest.h" @@ -18,13 +17,6 @@ void cmCTestCoverageCommand::BindArguments() this->Bind("LABELS"_s, this->Labels); } -void cmCTestCoverageCommand::CheckArguments( - std::vector<std::string> const& keywords) -{ - this->LabelsMentioned = - !this->Labels.empty() || cm::contains(keywords, "LABELS"); -} - cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler() { this->CTest->SetCTestConfigurationFromCMakeVariable( @@ -36,9 +28,9 @@ cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler() handler->Initialize(); // If a LABELS option was given, select only files with the labels. - if (this->LabelsMentioned) { + if (this->Labels) { handler->SetLabelFilter( - std::set<std::string>(this->Labels.begin(), this->Labels.end())); + std::set<std::string>(this->Labels->begin(), this->Labels->end())); } handler->SetQuiet(this->Quiet); diff --git a/Source/CTest/cmCTestCoverageCommand.h b/Source/CTest/cmCTestCoverageCommand.h index 9344852..55c68b2 100644 --- a/Source/CTest/cmCTestCoverageCommand.h +++ b/Source/CTest/cmCTestCoverageCommand.h @@ -9,7 +9,9 @@ #include <vector> #include <cm/memory> +#include <cm/optional> +#include "cmArgumentParserTypes.h" // IWYU pragma: keep #include "cmCTestHandlerCommand.h" #include "cmCommand.h" @@ -41,9 +43,7 @@ public: protected: void BindArguments() override; - void CheckArguments(std::vector<std::string> const& keywords) override; cmCTestGenericHandler* InitializeHandler() override; - bool LabelsMentioned; - std::vector<std::string> Labels; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Labels; }; diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index 5494d20..be952cd 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -7,6 +7,7 @@ #include <cstring> #include <sstream> +#include <cm/string_view> #include <cmext/string_view> #include "cmCTest.h" @@ -81,15 +82,13 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args, // Process input arguments. std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; - std::vector<std::string> parsedKeywords; - this->Parse(args, &unparsedArguments, &keywordsMissingValue, - &parsedKeywords); - this->CheckArguments(keywordsMissingValue); - - std::sort(parsedKeywords.begin(), parsedKeywords.end()); - auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end()); - if (it != parsedKeywords.end()) { + this->Parse(args, &unparsedArguments); + this->CheckArguments(); + + std::sort(this->ParsedKeywords.begin(), this->ParsedKeywords.end()); + auto it = std::adjacent_find(this->ParsedKeywords.begin(), + this->ParsedKeywords.end()); + if (it != this->ParsedKeywords.end()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, cmStrCat("Called with more than one value for ", *it)); @@ -233,6 +232,7 @@ void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*) void cmCTestHandlerCommand::BindArguments() { + this->BindParsedKeywords(this->ParsedKeywords); this->Bind("APPEND"_s, this->Append); this->Bind("QUIET"_s, this->Quiet); this->Bind("RETURN_VALUE"_s, this->ReturnValue); @@ -242,6 +242,6 @@ void cmCTestHandlerCommand::BindArguments() this->Bind("SUBMIT_INDEX"_s, this->SubmitIndex); } -void cmCTestHandlerCommand::CheckArguments(std::vector<std::string> const&) +void cmCTestHandlerCommand::CheckArguments() { } diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h index 756952d..ed6d9af 100644 --- a/Source/CTest/cmCTestHandlerCommand.h +++ b/Source/CTest/cmCTestHandlerCommand.h @@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <cm/string_view> + #include "cmArgumentParser.h" #include "cmCTestCommand.h" @@ -42,8 +44,9 @@ protected: // Command argument handling. virtual void BindArguments(); - virtual void CheckArguments(std::vector<std::string> const& keywords); + virtual void CheckArguments(); + std::vector<cm::string_view> ParsedKeywords; bool Append = false; bool Quiet = false; std::string CaptureCMakeError; diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx index 788845b..6f6a642 100644 --- a/Source/CTest/cmCTestMemCheckHandler.cxx +++ b/Source/CTest/cmCTestMemCheckHandler.cxx @@ -1177,6 +1177,13 @@ bool cmCTestMemCheckHandler::ProcessMemCheckCudaOutput( // generic error: ignore ERROR SUMMARY, CUDA-MEMCHECK and others "== ([A-Z][a-z].*)" }; + // matchers for messages that aren't defects, but caught by above matchers + std::vector<cmsys::RegularExpression> false_positive_matchers{ + "== Error: No attachable process found.*timed-out", + "== Default timeout can be adjusted with --launch-timeout", + "== Error: Target application terminated before first instrumented API", + "== Tracking kernels launched by child processes requires" + }; std::vector<std::string::size_type> nonMemcheckOutput; auto sttime = std::chrono::steady_clock::now(); @@ -1196,11 +1203,17 @@ bool cmCTestMemCheckHandler::ProcessMemCheckCudaOutput( if (leakExpr.find(line)) { failure = static_cast<int>(this->FindOrAddWarning("Memory leak")); } else { - for (auto& matcher : matchers) { - if (matcher.find(line)) { + auto match_predicate = + [&line](cmsys::RegularExpression& matcher) -> bool { + return matcher.find(line); + }; + auto const pos_matcher = + std::find_if(matchers.begin(), matchers.end(), match_predicate); + if (pos_matcher != matchers.end()) { + if (!std::any_of(false_positive_matchers.begin(), + false_positive_matchers.end(), match_predicate)) { failure = - static_cast<int>(this->FindOrAddWarning(matcher.match(1))); - break; + static_cast<int>(this->FindOrAddWarning(pos_matcher->match(1))); } } } diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 2a2cb1c..5efe69f 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -8,16 +8,12 @@ #include <cstdint> #include <cstdio> #include <cstring> -#include <functional> #include <iomanip> #include <ratio> #include <sstream> #include <utility> #include <cm/memory> -#include <cm/optional> -#include <cm/string_view> -#include <cmext/string_view> #include "cmsys/RegularExpression.hxx" @@ -787,149 +783,31 @@ bool cmCTestRunTest::ForkProcess( this->TestProcess->SetTimeout(timeout); -#ifndef CMAKE_BOOTSTRAP cmSystemTools::SaveRestoreEnvironment sre; -#endif - std::ostringstream envMeasurement; + + // We split processing ENVIRONMENT and ENVIRONMENT_MODIFICATION into two + // phases to ensure that MYVAR=reset: in the latter phase resets to the + // former phase's settings, rather than to the original environment. if (environment && !environment->empty()) { - // Environment modification works on the assumption that the environment is - // actually modified here. If another strategy is used, there will need to - // be updates below in `apply_diff`. - cmSystemTools::AppendEnv(*environment); - for (auto const& var : *environment) { - envMeasurement << var << std::endl; - } + cmSystemTools::EnvDiff diff; + diff.AppendEnv(*environment); + diff.ApplyToCurrentEnv(&envMeasurement); } if (environment_modification && !environment_modification->empty()) { - std::map<std::string, cm::optional<std::string>> env_application; - -#ifdef _WIN32 - char path_sep = ';'; -#else - char path_sep = ':'; -#endif - - auto apply_diff = - [&env_application](const std::string& name, - std::function<void(std::string&)> const& apply) { - cm::optional<std::string> old_value = env_application[name]; - std::string output; - if (old_value) { - output = *old_value; - } else { - // This only works because the environment is actually modified above - // (`AppendEnv`). If CTest ever just creates an environment block - // directly, that block will need to be queried for the subprocess' - // value instead. - const char* curval = cmSystemTools::GetEnv(name); - if (curval) { - output = curval; - } - } - apply(output); - env_application[name] = output; - }; - - bool err_occurred = false; + cmSystemTools::EnvDiff diff; + bool env_ok = true; for (auto const& envmod : *environment_modification) { - // Split on `=` - auto const eq_loc = envmod.find_first_of('='); - if (eq_loc == std::string::npos) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Error: Missing `=` after the variable name in: " - << envmod << std::endl); - err_occurred = true; - continue; - } - auto const name = envmod.substr(0, eq_loc); - - // Split value on `:` - auto const op_value_start = eq_loc + 1; - auto const colon_loc = envmod.find_first_of(':', op_value_start); - if (colon_loc == std::string::npos) { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Error: Missing `:` after the operation in: " << envmod - << std::endl); - err_occurred = true; - continue; - } - auto const op = - envmod.substr(op_value_start, colon_loc - op_value_start); - - auto const value_start = colon_loc + 1; - auto const value = envmod.substr(value_start); - - // Determine what to do with the operation. - if (op == "reset"_s) { - auto entry = env_application.find(name); - if (entry != env_application.end()) { - env_application.erase(entry); - } - } else if (op == "set"_s) { - env_application[name] = value; - } else if (op == "unset"_s) { - env_application[name] = {}; - } else if (op == "string_append"_s) { - apply_diff(name, [&value](std::string& output) { output += value; }); - } else if (op == "string_prepend"_s) { - apply_diff(name, - [&value](std::string& output) { output.insert(0, value); }); - } else if (op == "path_list_append"_s) { - apply_diff(name, [&value, path_sep](std::string& output) { - if (!output.empty()) { - output += path_sep; - } - output += value; - }); - } else if (op == "path_list_prepend"_s) { - apply_diff(name, [&value, path_sep](std::string& output) { - if (!output.empty()) { - output.insert(output.begin(), path_sep); - } - output.insert(0, value); - }); - } else if (op == "cmake_list_append"_s) { - apply_diff(name, [&value](std::string& output) { - if (!output.empty()) { - output += ';'; - } - output += value; - }); - } else if (op == "cmake_list_prepend"_s) { - apply_diff(name, [&value](std::string& output) { - if (!output.empty()) { - output.insert(output.begin(), ';'); - } - output.insert(0, value); - }); - } else { - cmCTestLog(this->CTest, ERROR_MESSAGE, - "Error: Unrecognized environment manipulation argument: " - << op << std::endl); - err_occurred = true; - continue; - } + env_ok &= diff.ParseOperation(envmod); } - if (err_occurred) { + if (!env_ok) { return false; } - for (auto const& env_apply : env_application) { - if (env_apply.second) { - auto const env_update = - cmStrCat(env_apply.first, '=', *env_apply.second); - cmSystemTools::PutEnv(env_update); - envMeasurement << env_update << std::endl; - } else { - cmSystemTools::UnsetEnv(env_apply.first.c_str()); - // Signify that this variable is being actively unset - envMeasurement << "#" << env_apply.first << "=" << std::endl; - } - } + diff.ApplyToCurrentEnv(&envMeasurement); } if (this->UseAllocatedResources) { diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx index a2dc615..a1933cc 100644 --- a/Source/CTest/cmCTestSubmitCommand.cxx +++ b/Source/CTest/cmCTestSubmitCommand.cxx @@ -8,7 +8,6 @@ #include <cm/memory> #include <cm/vector> -#include <cmext/algorithm> #include <cmext/string_view> #include "cmCTest.h" @@ -87,7 +86,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // If FILES are given, but not PARTS, only the FILES are submitted // and *no* PARTS are submitted. // (This is why we select the empty "noParts" set in the - // FilesMentioned block below...) + // if(this->Files) block below...) // // If PARTS are given, only the selected PARTS are submitted. // @@ -96,7 +95,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // If given explicit FILES to submit, pass them to the handler. // - if (this->FilesMentioned) { + if (this->Files) { // Intentionally select *no* PARTS. (Pass an empty set.) If PARTS // were also explicitly mentioned, they will be selected below... // But FILES with no PARTS mentioned should just submit the FILES @@ -104,14 +103,14 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler() // handler->SelectParts(std::set<cmCTest::Part>()); handler->SelectFiles( - std::set<std::string>(this->Files.begin(), this->Files.end())); + std::set<std::string>(this->Files->begin(), this->Files->end())); } // If a PARTS option was given, select only the named parts for submission. // - if (this->PartsMentioned) { + if (this->Parts) { auto parts = - cmMakeRange(this->Parts).transform([this](std::string const& arg) { + cmMakeRange(*(this->Parts)).transform([this](std::string const& arg) { return this->CTest->GetPartFromName(arg); }); handler->SelectParts(std::set<cmCTest::Part>(parts.begin(), parts.end())); @@ -172,33 +171,31 @@ void cmCTestSubmitCommand::BindArguments() this->cmCTestHandlerCommand::BindArguments(); } -void cmCTestSubmitCommand::CheckArguments( - std::vector<std::string> const& keywords) +void cmCTestSubmitCommand::CheckArguments() { - this->PartsMentioned = - !this->Parts.empty() || cm::contains(keywords, "PARTS"); - this->FilesMentioned = - !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); - if (p == cmCTest::PartCount) { - std::ostringstream e; - e << "Part name \"" << arg << "\" is invalid."; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return true; - } - return false; - }); - - cm::erase_if(this->Files, [this](std::string const& arg) -> bool { - if (!cmSystemTools::FileExists(arg)) { - std::ostringstream e; - e << "File \"" << arg << "\" does not exist. Cannot submit " - << "a non-existent file."; - this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return true; - } - return false; - }); + if (this->Parts) { + cm::erase_if(*(this->Parts), [this](std::string const& arg) -> bool { + cmCTest::Part p = this->CTest->GetPartFromName(arg); + if (p == cmCTest::PartCount) { + std::ostringstream e; + e << "Part name \"" << arg << "\" is invalid."; + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return true; + } + return false; + }); + } + + if (this->Files) { + cm::erase_if(*(this->Files), [this](std::string const& arg) -> bool { + if (!cmSystemTools::FileExists(arg)) { + std::ostringstream e; + e << "File \"" << arg << "\" does not exist. Cannot submit " + << "a non-existent file."; + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return true; + } + return false; + }); + } } diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h index c5d11df..b67f182 100644 --- a/Source/CTest/cmCTestSubmitCommand.h +++ b/Source/CTest/cmCTestSubmitCommand.h @@ -8,6 +8,9 @@ #include <string> #include <vector> +#include <cm/optional> + +#include "cmArgumentParserTypes.h" #include "cmCTestHandlerCommand.h" class cmCommand; @@ -35,13 +38,11 @@ public: protected: void BindArguments() override; - void CheckArguments(std::vector<std::string> const& keywords) override; + void CheckArguments() override; cmCTestGenericHandler* InitializeHandler() override; bool CDashUpload = false; - bool FilesMentioned = false; bool InternalTest = false; - bool PartsMentioned = false; std::string BuildID; std::string CDashUploadFile; @@ -50,7 +51,7 @@ protected: std::string RetryDelay; std::string SubmitURL; - std::vector<std::string> Files; - std::vector<std::string> HttpHeaders; - std::vector<std::string> Parts; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> HttpHeaders; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Parts; }; diff --git a/Source/CTest/cmCTestUploadCommand.cxx b/Source/CTest/cmCTestUploadCommand.cxx index f86ee0d..2ed671c 100644 --- a/Source/CTest/cmCTestUploadCommand.cxx +++ b/Source/CTest/cmCTestUploadCommand.cxx @@ -21,7 +21,7 @@ void cmCTestUploadCommand::BindArguments() this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError); } -void cmCTestUploadCommand::CheckArguments(std::vector<std::string> const&) +void cmCTestUploadCommand::CheckArguments() { cm::erase_if(this->Files, [this](std::string const& arg) -> bool { if (!cmSystemTools::FileExists(arg)) { diff --git a/Source/CTest/cmCTestUploadCommand.h b/Source/CTest/cmCTestUploadCommand.h index fe155f6..a9d1dd2 100644 --- a/Source/CTest/cmCTestUploadCommand.h +++ b/Source/CTest/cmCTestUploadCommand.h @@ -10,6 +10,7 @@ #include <cm/memory> +#include "cmArgumentParserTypes.h" #include "cmCTestHandlerCommand.h" #include "cmCommand.h" @@ -42,8 +43,8 @@ public: protected: void BindArguments() override; - void CheckArguments(std::vector<std::string> const&) override; + void CheckArguments() override; cmCTestGenericHandler* InitializeHandler() override; - std::vector<std::string> Files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Files; }; diff --git a/Source/CursesDialog/form/CMakeLists.txt b/Source/CursesDialog/form/CMakeLists.txt index 68d28c8..63214e3 100644 --- a/Source/CursesDialog/form/CMakeLists.txt +++ b/Source/CursesDialog/form/CMakeLists.txt @@ -11,7 +11,7 @@ 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") +configure_file(cmFormConfigure.h.in cmFormConfigure.h) add_library(cmForm fld_arg.c diff --git a/Source/CursesDialog/form/form.h b/Source/CursesDialog/form/form.h index 39ed75a..b590c97 100644 --- a/Source/CursesDialog/form/form.h +++ b/Source/CursesDialog/form/form.h @@ -54,6 +54,9 @@ # if defined(__hpux) && !defined(HAVE__XOPEN_SOURCE_EXTENDED) # undef _XOPEN_SOURCE_EXTENDED # endif + /* Some curses/term headers define lower-case macros that + conflict with our source code. Undefine them. */ +# undef newline # endif #include <eti.h> diff --git a/Source/LexerParser/cmFortranLexer.cxx b/Source/LexerParser/cmFortranLexer.cxx index c3d0000..5703de1 100644 --- a/Source/LexerParser/cmFortranLexer.cxx +++ b/Source/LexerParser/cmFortranLexer.cxx @@ -557,31 +557,32 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static const flex_int16_t yy_accept[210] = +static const flex_int16_t yy_accept[216] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 49, 51, 50, 53, 1, 49, 33, 2, 47, 48, 35, 37, 50, 39, 49, 46, 46, 46, 46, - 46, 46, 49, 46, 51, 49, 50, 49, 46, 9, - 8, 9, 4, 3, 49, 0, 10, 0, 0, 0, - 0, 0, 33, 33, 34, 36, 39, 49, 46, 46, - 46, 46, 46, 46, 0, 52, 46, 0, 0, 0, - 12, 0, 0, 0, 0, 0, 0, 49, 0, 11, - 46, 0, 0, 5, 0, 0, 0, 0, 29, 0, - 33, 33, 33, 33, 0, 0, 40, 46, 46, 46, - - 46, 45, 12, 12, 0, 0, 0, 23, 0, 0, - 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 46, 46, 46, 46, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 30, 31, 0, 0, 0, 0, 0, 46, 46, - 46, 46, 0, 24, 25, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 20, 32, 27, 0, 0, 0, - 46, 46, 43, 46, 0, 26, 21, 0, 0, 0, - 19, 0, 0, 18, 28, 0, 0, 41, 46, 46, - 17, 22, 0, 7, 38, 7, 15, 0, 46, 46, - - 14, 16, 42, 44, 0, 0, 0, 13, 0 + 46, 46, 49, 46, 51, 49, 50, 51, 49, 46, + 9, 8, 9, 9, 4, 3, 49, 0, 10, 0, + 0, 0, 0, 0, 33, 33, 34, 36, 39, 49, + 46, 46, 46, 46, 46, 46, 0, 52, 0, 46, + 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, + 0, 49, 0, 11, 46, 0, 0, 0, 5, 0, + 0, 0, 0, 0, 29, 0, 33, 33, 33, 33, + + 0, 0, 40, 46, 46, 46, 46, 45, 12, 12, + 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 46, 46, 46, 46, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 30, 31, 0, + 0, 0, 0, 0, 46, 46, 46, 46, 0, 24, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 20, 32, 27, 0, 0, 0, 46, 46, 43, 46, + 0, 26, 21, 0, 0, 0, 19, 0, 0, 18, + 28, 0, 0, 41, 46, 46, 17, 22, 0, 7, + + 38, 7, 15, 0, 46, 46, 14, 16, 42, 44, + 0, 0, 0, 13, 0 } ; static const YY_CHAR yy_ec[256] = @@ -618,189 +619,197 @@ static const YY_CHAR yy_ec[256] = static const YY_CHAR yy_meta[50] = { 0, - 1, 2, 2, 3, 4, 3, 3, 1, 1, 3, - 3, 3, 3, 1, 3, 5, 3, 3, 1, 3, + 1, 2, 2, 2, 3, 4, 4, 1, 1, 4, + 4, 4, 4, 1, 4, 5, 4, 4, 1, 4, 6, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7 } ; -static const flex_int16_t yy_base[219] = +static const flex_int16_t yy_base[225] = { 0, - 0, 48, 0, 49, 464, 56, 52, 57, 62, 68, - 466, 0, 468, 468, 462, 468, 74, 81, 468, 468, - 468, 468, 447, 468, 442, 440, 0, 19, 41, 427, - 47, 41, 90, 119, 97, 158, 455, 207, 247, 468, - 454, 101, 468, 468, 0, 455, 468, 105, 430, 423, - 62, 67, 119, 151, 468, 468, 468, 121, 0, 90, - 93, 110, 431, 112, 142, 468, 0, 160, 295, 0, - 162, 411, 123, 102, 408, 405, 446, 344, 447, 468, - 0, 444, 170, 174, 420, 421, 132, 404, 95, 404, - 180, 186, 192, 228, 297, 397, 0, 168, 144, 52, - - 411, 0, 204, 217, 397, 179, 390, 170, 389, 378, - 364, 390, 389, 230, 468, 363, 355, 337, 337, 334, - 335, 335, 330, 334, 187, 339, 267, 339, 327, 327, - 327, 324, 325, 325, 318, 319, 318, 354, 352, 323, - 327, 468, 468, 310, 307, 305, 297, 297, 275, 275, - 277, 279, 287, 468, 468, 286, 283, 273, 196, 307, - 200, 238, 234, 210, 468, 468, 468, 174, 171, 162, - 279, 182, 0, 269, 150, 468, 468, 137, 109, 323, - 468, 239, 0, 468, 468, 72, 71, 0, 283, 283, - 468, 468, 51, 468, 468, 468, 468, 37, 283, 288, - - 330, 468, 0, 0, 331, 0, 52, 468, 468, 384, - 391, 397, 400, 407, 414, 421, 428, 435 + 0, 48, 0, 49, 55, 58, 64, 66, 75, 83, + 491, 0, 492, 492, 487, 492, 86, 92, 492, 492, + 492, 492, 472, 492, 467, 465, 0, 56, 59, 452, + 66, 16, 105, 131, 109, 170, 480, 481, 219, 259, + 492, 478, 479, 116, 492, 492, 0, 478, 492, 111, + 453, 446, 34, 78, 155, 174, 492, 492, 492, 121, + 0, 29, 105, 101, 454, 101, 131, 492, 474, 0, + 180, 307, 0, 146, 433, 117, 94, 430, 427, 468, + 467, 356, 468, 492, 0, 465, 464, 187, 191, 465, + 439, 440, 149, 423, 126, 423, 200, 240, 311, 322, + + 206, 416, 0, 152, 180, 176, 430, 0, 216, 224, + 417, 186, 418, 127, 418, 411, 415, 451, 450, 247, + 492, 423, 416, 398, 393, 373, 364, 364, 359, 353, + 198, 358, 178, 358, 346, 346, 346, 343, 344, 344, + 338, 340, 339, 376, 374, 343, 346, 492, 492, 329, + 325, 324, 313, 315, 211, 211, 291, 293, 313, 492, + 492, 314, 304, 304, 261, 328, 212, 249, 243, 203, + 492, 492, 492, 173, 158, 150, 293, 172, 0, 273, + 144, 492, 492, 137, 125, 335, 492, 339, 0, 492, + 492, 112, 63, 0, 304, 300, 492, 492, 58, 492, + + 492, 492, 492, 30, 311, 312, 361, 492, 0, 0, + 366, 0, 44, 492, 492, 396, 403, 409, 412, 419, + 426, 433, 440, 447 } ; -static const flex_int16_t yy_def[219] = +static const flex_int16_t yy_def[225] = { 0, - 209, 1, 1, 1, 1, 1, 210, 210, 210, 210, - 209, 211, 209, 209, 212, 209, 211, 209, 209, 209, - 209, 209, 209, 209, 209, 211, 213, 213, 213, 213, - 213, 213, 211, 213, 209, 211, 209, 214, 209, 209, - 209, 209, 209, 209, 211, 212, 209, 209, 209, 209, - 209, 209, 209, 215, 209, 209, 209, 211, 213, 213, - 213, 213, 213, 213, 209, 209, 34, 209, 209, 69, - 211, 209, 209, 209, 209, 209, 209, 214, 214, 209, - 39, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 215, 215, 215, 215, 209, 209, 213, 213, 213, 213, - - 213, 213, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 213, 213, 213, 213, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 213, 213, - 213, 213, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 213, 213, 213, 213, 209, 209, 209, 209, 209, 209, - 209, 216, 217, 209, 209, 209, 209, 213, 213, 213, - 209, 209, 209, 209, 209, 209, 209, 209, 213, 213, - - 209, 209, 213, 213, 209, 218, 218, 209, 0, 209, - 209, 209, 209, 209, 209, 209, 209, 209 + 215, 1, 1, 1, 1, 1, 216, 216, 216, 216, + 215, 217, 215, 215, 218, 215, 217, 215, 215, 215, + 215, 215, 215, 215, 215, 217, 219, 219, 219, 219, + 219, 219, 217, 219, 215, 217, 215, 215, 220, 215, + 215, 215, 215, 215, 215, 215, 217, 218, 215, 215, + 215, 215, 215, 215, 215, 221, 215, 215, 215, 217, + 219, 219, 219, 219, 219, 219, 215, 215, 215, 34, + 215, 215, 72, 217, 215, 215, 215, 215, 215, 215, + 215, 220, 220, 215, 40, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 221, 221, 221, 221, + + 215, 215, 219, 219, 219, 219, 219, 219, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 219, 219, 219, 219, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 219, 219, 219, 219, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 219, 219, 219, 219, + 215, 215, 215, 215, 215, 215, 215, 222, 223, 215, + 215, 215, 215, 219, 219, 219, 215, 215, 215, 215, + + 215, 215, 215, 215, 219, 219, 215, 215, 219, 219, + 215, 224, 224, 215, 0, 215, 215, 215, 215, 215, + 215, 215, 215, 215 } ; -static const flex_int16_t yy_nxt[518] = +static const flex_int16_t yy_nxt[542] = { 0, 12, 13, 14, 13, 13, 15, 16, 12, 17, 18, 19, 20, 21, 12, 22, 12, 23, 24, 12, 25, 12, 26, 27, 27, 27, 27, 28, 27, 27, 29, 27, 30, 27, 27, 27, 31, 27, 32, 33, 34, 27, 27, 28, 27, 29, 27, 27, 31, 32, 35, - 35, 60, 35, 35, 41, 36, 36, 35, 37, 41, - 35, 42, 43, 36, 41, 60, 42, 43, 44, 38, - 41, 42, 208, 61, 44, 48, 64, 42, 48, 202, - 39, 39, 53, 53, 63, 53, 54, 61, 64, 127, - 55, 65, 66, 201, 65, 63, 39, 39, 68, 49, - - 127, 68, 83, 84, 69, 83, 48, 87, 88, 48, - 89, 50, 198, 90, 197, 97, 51, 98, 52, 45, - 53, 53, 95, 53, 54, 95, 45, 45, 55, 99, - 49, 97, 45, 98, 67, 100, 121, 45, 102, 45, - 45, 122, 50, 65, 66, 108, 65, 51, 109, 52, - 193, 100, 92, 53, 102, 92, 93, 45, 67, 70, - 94, 68, 70, 104, 68, 96, 104, 69, 106, 107, - 126, 83, 84, 71, 83, 114, 118, 71, 114, 119, - 192, 92, 53, 115, 92, 93, 126, 92, 53, 94, - 92, 93, 191, 92, 53, 94, 92, 93, 125, 72, - - 73, 94, 74, 75, 189, 104, 76, 78, 104, 80, - 187, 133, 186, 125, 78, 78, 134, 185, 104, 103, - 78, 104, 78, 130, 149, 78, 131, 78, 78, 92, - 53, 114, 92, 93, 114, 149, 184, 94, 183, 115, - 195, 195, 182, 181, 179, 78, 78, 79, 79, 80, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 81, 79, 79, 79, 79, 79, 79, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 79, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 70, 151, 95, 70, - - 171, 95, 172, 173, 174, 188, 190, 199, 180, 203, - 103, 180, 151, 200, 204, 178, 171, 190, 172, 173, - 174, 188, 103, 199, 180, 203, 177, 180, 200, 176, - 204, 205, 205, 175, 205, 205, 72, 73, 103, 74, - 75, 96, 170, 76, 78, 169, 80, 168, 206, 206, - 167, 78, 78, 166, 165, 164, 163, 78, 162, 78, - 161, 160, 78, 159, 78, 78, 158, 157, 156, 155, - 154, 153, 152, 150, 148, 147, 146, 145, 144, 143, - 142, 141, 78, 78, 40, 40, 40, 40, 40, 40, - 40, 45, 140, 139, 138, 45, 45, 46, 46, 46, - - 46, 46, 46, 46, 59, 137, 59, 79, 79, 79, - 79, 79, 79, 79, 91, 91, 91, 91, 91, 91, - 91, 194, 194, 194, 136, 194, 194, 194, 196, 135, - 196, 132, 196, 196, 196, 207, 207, 207, 207, 207, - 129, 207, 128, 124, 123, 120, 117, 116, 113, 80, - 112, 111, 110, 105, 101, 86, 85, 47, 82, 77, - 62, 58, 57, 56, 47, 209, 37, 11, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209 + 35, 66, 35, 35, 103, 36, 36, 37, 38, 35, + 37, 38, 35, 66, 214, 36, 42, 43, 42, 43, + 103, 39, 208, 44, 45, 44, 45, 42, 43, 93, + 94, 46, 40, 40, 44, 42, 43, 50, 62, 46, + 50, 63, 44, 55, 55, 55, 55, 56, 40, 40, + + 207, 57, 62, 65, 204, 63, 67, 68, 69, 67, + 71, 51, 50, 71, 65, 50, 72, 88, 89, 90, + 88, 95, 101, 52, 96, 101, 106, 108, 53, 104, + 54, 47, 67, 68, 69, 67, 51, 114, 47, 47, + 115, 105, 106, 108, 47, 104, 70, 110, 52, 47, + 110, 47, 47, 53, 203, 54, 55, 55, 55, 55, + 56, 74, 112, 113, 57, 102, 199, 127, 139, 47, + 70, 73, 128, 140, 73, 98, 55, 98, 98, 99, + 198, 71, 131, 100, 71, 74, 197, 72, 88, 89, + 90, 88, 120, 124, 195, 120, 125, 131, 193, 192, + + 121, 98, 55, 98, 98, 99, 132, 101, 157, 100, + 101, 75, 76, 133, 77, 78, 191, 110, 79, 82, + 110, 84, 132, 157, 133, 110, 82, 82, 110, 190, + 136, 109, 82, 137, 82, 155, 177, 82, 178, 82, + 82, 98, 55, 98, 98, 99, 155, 189, 120, 100, + 102, 120, 177, 188, 178, 187, 121, 82, 82, 83, + 83, 84, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 85, 83, 83, 83, 83, 83, + 83, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 83, 85, 85, + + 85, 85, 85, 85, 85, 85, 85, 85, 73, 185, + 196, 73, 98, 55, 98, 98, 99, 179, 180, 194, + 100, 196, 109, 98, 55, 98, 98, 99, 205, 186, + 206, 100, 186, 179, 180, 194, 186, 209, 210, 186, + 201, 201, 201, 109, 205, 206, 184, 183, 75, 76, + 109, 77, 78, 209, 210, 79, 82, 182, 84, 181, + 176, 175, 211, 82, 82, 211, 174, 211, 173, 82, + 211, 82, 172, 171, 82, 170, 82, 82, 169, 212, + 168, 167, 166, 165, 212, 164, 163, 162, 161, 160, + 159, 158, 156, 154, 82, 82, 41, 41, 41, 41, + + 41, 41, 41, 47, 153, 152, 151, 47, 47, 48, + 48, 48, 48, 48, 48, 48, 61, 150, 61, 83, + 83, 83, 83, 83, 83, 83, 97, 97, 97, 97, + 97, 97, 97, 200, 200, 149, 200, 200, 200, 200, + 202, 148, 147, 202, 202, 202, 202, 213, 213, 213, + 213, 213, 146, 213, 145, 144, 143, 142, 141, 138, + 135, 134, 130, 129, 126, 123, 122, 89, 86, 119, + 84, 80, 118, 117, 116, 111, 68, 107, 92, 91, + 49, 87, 86, 81, 80, 64, 60, 59, 58, 49, + 215, 11, 215, 215, 215, 215, 215, 215, 215, 215, + + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215 } ; -static const flex_int16_t yy_chk[518] = +static const flex_int16_t yy_chk[542] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, - 4, 28, 2, 4, 7, 2, 4, 6, 6, 8, - 6, 7, 7, 6, 9, 28, 8, 8, 9, 6, - 10, 9, 207, 29, 10, 17, 32, 10, 17, 198, - 6, 6, 18, 18, 31, 18, 18, 29, 32, 100, - 18, 33, 33, 193, 33, 31, 6, 6, 35, 17, - - 100, 35, 42, 42, 35, 42, 48, 51, 51, 48, - 52, 17, 187, 52, 186, 60, 17, 61, 17, 34, - 53, 53, 58, 53, 53, 58, 34, 34, 53, 61, - 48, 60, 34, 61, 34, 62, 89, 34, 64, 34, - 34, 89, 48, 65, 65, 74, 65, 48, 74, 48, - 179, 62, 54, 54, 64, 54, 54, 34, 34, 36, - 54, 68, 36, 71, 68, 58, 71, 68, 73, 73, - 99, 83, 83, 36, 83, 84, 87, 71, 84, 87, - 178, 91, 91, 84, 91, 91, 99, 92, 92, 91, - 92, 92, 175, 93, 93, 92, 93, 93, 98, 36, - - 36, 93, 36, 36, 172, 103, 36, 38, 103, 38, - 170, 108, 169, 98, 38, 38, 108, 168, 104, 103, - 38, 104, 38, 106, 125, 38, 106, 38, 38, 94, - 94, 114, 94, 94, 114, 125, 164, 94, 163, 114, - 182, 182, 162, 161, 159, 38, 38, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 69, 127, 95, 69, - - 149, 95, 150, 151, 152, 171, 174, 189, 160, 199, - 69, 160, 127, 190, 200, 158, 149, 174, 150, 151, - 152, 171, 160, 189, 180, 199, 157, 180, 190, 156, - 200, 201, 205, 153, 201, 205, 69, 69, 180, 69, - 69, 95, 148, 69, 78, 147, 78, 146, 201, 205, - 145, 78, 78, 144, 141, 140, 139, 78, 138, 78, - 137, 136, 78, 135, 78, 78, 134, 133, 132, 131, - 130, 129, 128, 126, 124, 123, 122, 121, 120, 119, - 118, 117, 78, 78, 210, 210, 210, 210, 210, 210, - 210, 211, 116, 113, 112, 211, 211, 212, 212, 212, - - 212, 212, 212, 212, 213, 111, 213, 214, 214, 214, - 214, 214, 214, 214, 215, 215, 215, 215, 215, 215, - 215, 216, 216, 216, 110, 216, 216, 216, 217, 109, - 217, 107, 217, 217, 217, 218, 218, 218, 218, 218, - 105, 218, 101, 96, 90, 88, 86, 85, 82, 79, - 77, 76, 75, 72, 63, 50, 49, 46, 41, 37, - 30, 26, 25, 23, 15, 11, 5, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209 + 4, 32, 2, 4, 62, 2, 4, 5, 5, 6, + 6, 6, 6, 32, 213, 6, 7, 7, 8, 8, + 62, 6, 204, 7, 7, 8, 8, 9, 9, 53, + 53, 9, 6, 6, 9, 10, 10, 17, 28, 10, + 17, 29, 10, 18, 18, 18, 18, 18, 6, 6, + + 199, 18, 28, 31, 193, 29, 33, 33, 33, 33, + 35, 17, 50, 35, 31, 50, 35, 44, 44, 44, + 44, 54, 60, 17, 54, 60, 64, 66, 17, 63, + 17, 34, 67, 67, 67, 67, 50, 77, 34, 34, + 77, 63, 64, 66, 34, 63, 34, 74, 50, 34, + 74, 34, 34, 50, 192, 50, 55, 55, 55, 55, + 55, 74, 76, 76, 55, 60, 185, 95, 114, 34, + 34, 36, 95, 114, 36, 56, 56, 56, 56, 56, + 184, 71, 104, 56, 71, 36, 181, 71, 88, 88, + 88, 88, 89, 93, 178, 89, 93, 104, 176, 175, + + 89, 97, 97, 97, 97, 97, 105, 101, 133, 97, + 101, 36, 36, 106, 36, 36, 174, 109, 36, 39, + 109, 39, 105, 133, 106, 110, 39, 39, 110, 170, + 112, 109, 39, 112, 39, 131, 155, 39, 156, 39, + 39, 98, 98, 98, 98, 98, 131, 169, 120, 98, + 101, 120, 155, 168, 156, 167, 120, 39, 39, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + + 40, 40, 40, 40, 40, 40, 40, 40, 72, 165, + 180, 72, 99, 99, 99, 99, 99, 157, 158, 177, + 99, 180, 72, 100, 100, 100, 100, 100, 195, 166, + 196, 100, 166, 157, 158, 177, 186, 205, 206, 186, + 188, 188, 188, 166, 195, 196, 164, 163, 72, 72, + 186, 72, 72, 205, 206, 72, 82, 162, 82, 159, + 154, 153, 207, 82, 82, 207, 152, 211, 151, 82, + 211, 82, 150, 147, 82, 146, 82, 82, 145, 207, + 144, 143, 142, 141, 211, 140, 139, 138, 137, 136, + 135, 134, 132, 130, 82, 82, 216, 216, 216, 216, + + 216, 216, 216, 217, 129, 128, 127, 217, 217, 218, + 218, 218, 218, 218, 218, 218, 219, 126, 219, 220, + 220, 220, 220, 220, 220, 220, 221, 221, 221, 221, + 221, 221, 221, 222, 222, 125, 222, 222, 222, 222, + 223, 124, 123, 223, 223, 223, 223, 224, 224, 224, + 224, 224, 122, 224, 119, 118, 117, 116, 115, 113, + 111, 107, 102, 96, 94, 92, 91, 90, 87, 86, + 83, 81, 80, 79, 78, 75, 69, 65, 52, 51, + 48, 43, 42, 38, 37, 30, 26, 25, 23, 15, + 11, 215, 215, 215, 215, 215, 215, 215, 215, 215, + + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, + 215 } ; /* The intent behind this definition is that it'll catch @@ -1139,13 +1148,13 @@ yy_match: while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 210 ) + if ( yy_current_state >= 216 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } - while ( yy_base[yy_current_state] != 468 ); + while ( yy_base[yy_current_state] != 492 ); yy_find_action: yy_act = yy_accept[yy_current_state]; @@ -1733,7 +1742,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 210 ) + if ( yy_current_state >= 216 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; @@ -1762,11 +1771,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 210 ) + if ( yy_current_state >= 216 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; - yy_is_jam = (yy_current_state == 209); + yy_is_jam = (yy_current_state == 215); (void)yyg; return yy_is_jam ? 0 : yy_current_state; diff --git a/Source/LexerParser/cmFortranLexer.in.l b/Source/LexerParser/cmFortranLexer.in.l index 05769a1..fac3181 100644 --- a/Source/LexerParser/cmFortranLexer.in.l +++ b/Source/LexerParser/cmFortranLexer.in.l @@ -75,10 +75,10 @@ Modify cmFortranLexer.cxx: return STRING; } -<str_dq,str_sq>&[ \t]*\n | -<str_dq,str_sq>&[ \t]*\n[ \t]*& /* Ignore (continued strings, free fmt) */ +<str_dq,str_sq>&[ \t]*\r?\n | +<str_dq,str_sq>&[ \t]*\r?\n[ \t]*& /* Ignore (continued strings, free fmt) */ -<fixed_fmt,str_dq,str_sq>\n[ ]{5}[^ \t\n] { +<fixed_fmt,str_dq,str_sq>\r?\n[ ]{5}[^ \t\r\n] { if (cmFortranParser_GetOldStartcond(yyextra) == fixed_fmt) ; /* Ignore (cont. strings, fixed fmt) */ else @@ -132,15 +132,15 @@ $[ \t]*else { return F90PPR_ELSE; } $[ \t]*endif { return F90PPR_ENDIF; } /* Line continuations, possible involving comments. */ -&([ \t\n]*|!.*)* -&([ \t\n]*|!.*)*& +&([ \t\r\n]*|!.*)* +&([ \t\r\n]*|!.*)*& , { return COMMA; } :: { return DCOLON; } : { return COLON; } -<fixed_fmt>\n[ ]{5}[^ ] { return GARBAGE; } +<fixed_fmt>\r?\n[ ]{5}[^ ] { return GARBAGE; } =|=> { return ASSIGNMENT_OP; } @@ -159,13 +159,13 @@ $[ \t]*endif { return F90PPR_ENDIF; } \( { return LPAREN; } \) { return RPAREN; } -[^ \t\n\r:;,!'"a-zA-Z=&()]+ { return GARBAGE; } +[^ \t\r\n:;,!'"a-zA-Z=&()]+ { return GARBAGE; } ;|\n { return EOSTMT; } [ \t\r,] /* Ignore */ -\\[ \t]*\n /* Ignore line-endings preceded by \ */ +\\[ \t]*\r?\n /* Ignore line-endings preceded by \ */ . { return *yytext; } diff --git a/Source/LexerParser/cmFortranParser.cxx b/Source/LexerParser/cmFortranParser.cxx index b177296..f25856a 100644 --- a/Source/LexerParser/cmFortranParser.cxx +++ b/Source/LexerParser/cmFortranParser.cxx @@ -548,16 +548,16 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 594 +#define YYLAST 433 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 41 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 14 /* YYNRULES -- Number of rules. */ -#define YYNRULES 64 +#define YYNRULES 65 /* YYNSTATES -- Number of states. */ -#define YYNSTATES 127 +#define YYNSTATES 123 /* YYMAXUTOK -- Last valid token kind. */ #define YYMAXUTOK 295 @@ -610,13 +610,13 @@ static const yytype_int8 yytranslate[] = /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { - 0, 106, 106, 106, 109, 113, 118, 127, 133, 140, - 145, 149, 154, 166, 171, 176, 181, 186, 191, 196, - 201, 206, 210, 214, 218, 222, 223, 228, 228, 228, - 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, - 234, 234, 235, 235, 236, 236, 237, 237, 240, 241, + 0, 106, 106, 106, 109, 113, 118, 123, 129, 136, + 141, 146, 150, 155, 167, 172, 177, 182, 187, 192, + 197, 202, 207, 211, 215, 219, 223, 224, 229, 229, + 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, + 234, 235, 235, 236, 236, 237, 237, 238, 238, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, - 252, 253, 254, 255, 256 + 252, 253, 254, 255, 256, 257 }; #endif @@ -666,19 +666,19 @@ yysymbol_name (yysymbol_kind_t yysymbol) STATE-NUM. */ static const yytype_int16 yypact[] = { - -39, 21, -39, 1, -39, -20, -39, -39, -39, -39, + -39, 21, -39, 5, -39, -23, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -24, -18, 20, -8, - -3, 40, -39, 15, 16, 17, 19, 34, -39, -39, - -39, -39, -39, -39, 59, -39, -39, -39, -39, -39, - 36, 37, 38, -39, -39, -39, -39, -39, -39, 77, - 115, 130, 168, 183, -39, -39, -39, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -25, -19, 20, -8, + -15, -22, -39, -6, 14, 15, 16, 17, -39, -39, + -39, -39, -39, -39, 59, 51, 48, -39, 63, 64, + 35, 36, 37, -39, -39, -39, -39, -39, -39, 75, + 113, 128, 166, 181, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, 221, 236, 274, 289, -21, 26, -39, - 327, 342, 380, 395, 433, 448, -39, -39, -39, -39, - -39, -39, -39, -39, -39, 39, 41, 42, 486, -39, - -39, -39, -39, -39, -39, 18, -39, -39, -39, 43, - 501, 539, -39, -39, -39, 554, -39 + -39, -39, -39, -39, 68, -39, -39, -39, -20, 44, + -39, 219, 234, 272, 287, 325, 340, -39, -39, -39, + -39, -39, -39, 40, 41, 42, 378, -39, -39, -39, + -39, -39, -39, 46, 79, -39, -39, 50, -39, 393, + 90, -39, -39 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. @@ -686,19 +686,19 @@ static const yytype_int16 yypact[] = means the default is an error. */ static const yytype_int8 yydefact[] = { - 2, 0, 1, 0, 25, 0, 27, 28, 29, 31, - 30, 33, 32, 34, 36, 38, 42, 40, 44, 35, - 37, 39, 43, 41, 45, 46, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, 0, 0, 46, 46, - 46, 46, 26, 46, 0, 46, 46, 4, 46, 46, - 0, 0, 0, 46, 46, 46, 46, 46, 46, 0, - 0, 0, 0, 0, 15, 57, 56, 64, 62, 58, - 59, 60, 61, 63, 55, 48, 49, 50, 51, 52, - 53, 54, 47, 0, 0, 0, 0, 0, 0, 46, - 0, 0, 0, 0, 0, 0, 21, 22, 23, 24, - 14, 10, 13, 9, 6, 0, 0, 0, 0, 5, - 16, 17, 18, 19, 20, 0, 46, 46, 11, 0, - 0, 0, 46, 7, 12, 0, 8 + 2, 0, 1, 0, 26, 0, 28, 29, 30, 32, + 31, 34, 33, 35, 37, 39, 43, 41, 45, 36, + 38, 40, 44, 42, 46, 47, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, 0, 0, 47, 47, + 47, 47, 27, 47, 0, 0, 0, 4, 0, 0, + 0, 0, 0, 47, 47, 47, 47, 47, 47, 0, + 0, 0, 0, 0, 16, 58, 57, 65, 63, 59, + 60, 61, 62, 64, 56, 49, 50, 51, 52, 53, + 54, 55, 48, 11, 0, 14, 9, 6, 0, 0, + 47, 0, 0, 0, 0, 0, 0, 22, 23, 24, + 25, 15, 10, 0, 0, 0, 0, 5, 17, 18, + 19, 20, 21, 0, 0, 47, 12, 0, 7, 0, + 0, 13, 8 }; /* YYPGOTO[NTERM-NUM]. */ @@ -720,113 +720,65 @@ static const yytype_int8 yydefgoto[] = number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int8 yytable[] = { - 59, 60, 61, 62, 42, 63, 105, 83, 84, 106, - 85, 86, 43, 45, 46, 90, 91, 92, 93, 94, - 95, 2, 3, 47, 4, 49, 50, 5, 6, 7, + 59, 60, 61, 62, 51, 63, 52, 103, 42, 43, + 104, 53, 45, 46, 50, 91, 92, 93, 94, 95, + 96, 2, 3, 47, 4, 49, 54, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 54, 119, 55, - 56, 108, 57, 48, 107, 25, 26, 27, 28, 29, - 30, 31, 64, 65, 66, 67, 51, 58, 52, 87, - 88, 89, 115, 53, 116, 117, 122, 0, 120, 121, - 96, 65, 66, 67, 125, 68, 69, 70, 71, 72, - 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, - 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, - 75, 0, 76, 77, 78, 79, 80, 81, 97, 65, - 66, 67, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 98, 65, 66, 67, 0, 0, 0, + 18, 19, 20, 21, 22, 23, 24, 55, 56, 57, + 58, 85, 106, 48, 83, 25, 26, 27, 28, 29, + 30, 31, 64, 65, 66, 67, 86, 87, 88, 89, + 90, 102, 105, 113, 114, 115, 117, 119, 97, 65, + 66, 67, 118, 120, 84, 68, 69, 70, 71, 72, + 73, 74, 75, 122, 76, 77, 78, 79, 80, 81, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, - 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, - 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, - 81, 99, 65, 66, 67, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 100, 65, 66, 67, - 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, - 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, - 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, - 78, 79, 80, 81, 101, 65, 66, 67, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 102, - 65, 66, 67, 0, 0, 0, 0, 68, 69, 70, - 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, - 80, 81, 68, 69, 70, 71, 72, 73, 74, 75, - 0, 76, 77, 78, 79, 80, 81, 103, 65, 66, - 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 104, 65, 66, 67, 0, 0, 0, 0, - 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, - 77, 78, 79, 80, 81, 68, 69, 70, 71, 72, - 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, - 109, 65, 66, 67, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 110, 65, 66, 67, 0, - 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, - 75, 0, 76, 77, 78, 79, 80, 81, 68, 69, - 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, - 79, 80, 81, 111, 65, 66, 67, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 112, 65, - 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, - 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, - 81, 68, 69, 70, 71, 72, 73, 74, 75, 0, - 76, 77, 78, 79, 80, 81, 113, 65, 66, 67, + 76, 77, 78, 79, 80, 81, 98, 65, 66, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 114, 65, 66, 67, 0, 0, 0, 0, 68, + 0, 99, 65, 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, 72, 73, - 74, 75, 0, 76, 77, 78, 79, 80, 81, 118, + 74, 75, 0, 76, 77, 78, 79, 80, 81, 100, 65, 66, 67, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 123, 65, 66, 67, 0, 0, + 0, 0, 0, 0, 101, 65, 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, - 80, 81, 124, 65, 66, 67, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 126, 65, 66, + 80, 81, 107, 65, 66, 67, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 108, 65, 66, 67, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, 72, 73, 74, 75, 0, 76, - 77, 78, 79, 80, 81 + 77, 78, 79, 80, 81, 109, 65, 66, 67, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 110, 65, 66, 67, 0, 0, 0, 0, 68, 69, + 70, 71, 72, 73, 74, 75, 0, 76, 77, 78, + 79, 80, 81, 68, 69, 70, 71, 72, 73, 74, + 75, 0, 76, 77, 78, 79, 80, 81, 111, 65, + 66, 67, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 112, 65, 66, 67, 0, 0, 0, + 0, 68, 69, 70, 71, 72, 73, 74, 75, 0, + 76, 77, 78, 79, 80, 81, 68, 69, 70, 71, + 72, 73, 74, 75, 0, 76, 77, 78, 79, 80, + 81, 116, 65, 66, 67, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 121, 65, 66, 67, + 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, + 74, 75, 0, 76, 77, 78, 79, 80, 81, 68, + 69, 70, 71, 72, 73, 74, 75, 0, 76, 77, + 78, 79, 80, 81 }; static const yytype_int8 yycheck[] = { - 38, 39, 40, 41, 3, 43, 27, 45, 46, 30, - 48, 49, 32, 37, 32, 53, 54, 55, 56, 57, - 58, 0, 1, 3, 3, 33, 29, 6, 7, 8, + 38, 39, 40, 41, 26, 43, 28, 27, 3, 32, + 30, 33, 37, 32, 29, 53, 54, 55, 56, 57, + 58, 0, 1, 3, 3, 33, 32, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 32, 30, 33, - 33, 89, 33, 33, 28, 34, 35, 36, 37, 38, - 39, 40, 3, 4, 5, 6, 26, 33, 28, 33, - 33, 33, 33, 33, 33, 33, 33, -1, 116, 117, - 3, 4, 5, 6, 122, 26, 27, 28, 29, 30, - 31, 32, 33, -1, 35, 36, 37, 38, 39, 40, - -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, - 33, -1, 35, 36, 37, 38, 39, 40, 3, 4, - 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 3, 4, 5, 6, -1, -1, -1, + 19, 20, 21, 22, 23, 24, 25, 33, 33, 33, + 33, 3, 90, 33, 3, 34, 35, 36, 37, 38, + 39, 40, 3, 4, 5, 6, 3, 3, 33, 33, + 33, 3, 28, 33, 33, 33, 30, 115, 3, 4, + 5, 6, 3, 33, 33, 26, 27, 28, 29, 30, + 31, 32, 33, 3, 35, 36, 37, 38, 39, 40, -1, 26, 27, 28, 29, 30, 31, 32, 33, -1, - 35, 36, 37, 38, 39, 40, 26, 27, 28, 29, - 30, 31, 32, 33, -1, 35, 36, 37, 38, 39, - 40, 3, 4, 5, 6, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, - -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, - 32, 33, -1, 35, 36, 37, 38, 39, 40, 26, - 27, 28, 29, 30, 31, 32, 33, -1, 35, 36, - 37, 38, 39, 40, 3, 4, 5, 6, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, - 4, 5, 6, -1, -1, -1, -1, 26, 27, 28, - 29, 30, 31, 32, 33, -1, 35, 36, 37, 38, - 39, 40, 26, 27, 28, 29, 30, 31, 32, 33, - -1, 35, 36, 37, 38, 39, 40, 3, 4, 5, - 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 3, 4, 5, 6, -1, -1, -1, -1, - 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, - 36, 37, 38, 39, 40, 26, 27, 28, 29, 30, - 31, 32, 33, -1, 35, 36, 37, 38, 39, 40, - 3, 4, 5, 6, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 3, 4, 5, 6, -1, - -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, - 33, -1, 35, 36, 37, 38, 39, 40, 26, 27, - 28, 29, 30, 31, 32, 33, -1, 35, 36, 37, - 38, 39, 40, 3, 4, 5, 6, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 3, 4, - 5, 6, -1, -1, -1, -1, 26, 27, 28, 29, - 30, 31, 32, 33, -1, 35, 36, 37, 38, 39, - 40, 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, 36, 37, 38, 39, 40, 3, 4, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, -1, -1, -1, -1, 26, @@ -843,7 +795,23 @@ static const yytype_int8 yycheck[] = 6, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, 36, 37, 38, 39, 40, 26, 27, 28, 29, 30, 31, 32, 33, -1, 35, - 36, 37, 38, 39, 40 + 36, 37, 38, 39, 40, 3, 4, 5, 6, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 3, 4, 5, 6, -1, -1, -1, -1, 26, 27, + 28, 29, 30, 31, 32, 33, -1, 35, 36, 37, + 38, 39, 40, 26, 27, 28, 29, 30, 31, 32, + 33, -1, 35, 36, 37, 38, 39, 40, 3, 4, + 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 3, 4, 5, 6, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, -1, + 35, 36, 37, 38, 39, 40, 26, 27, 28, 29, + 30, 31, 32, 33, -1, 35, 36, 37, 38, 39, + 40, 3, 4, 5, 6, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, + -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, + 32, 33, -1, 35, 36, 37, 38, 39, 40, 26, + 27, 28, 29, 30, 31, 32, 33, -1, 35, 36, + 37, 38, 39, 40 }; /* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of @@ -858,11 +826,11 @@ static const yytype_int8 yystos[] = 29, 26, 28, 33, 32, 33, 33, 33, 33, 53, 53, 53, 53, 53, 3, 4, 5, 6, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, - 39, 40, 54, 53, 53, 53, 53, 33, 33, 33, - 53, 53, 53, 53, 53, 53, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 27, 30, 28, 53, 3, - 3, 3, 3, 3, 3, 33, 33, 33, 3, 30, - 53, 53, 33, 3, 3, 53, 3 + 39, 40, 54, 3, 33, 3, 3, 3, 33, 33, + 33, 53, 53, 53, 53, 53, 53, 3, 3, 3, + 3, 3, 3, 27, 30, 28, 53, 3, 3, 3, + 3, 3, 3, 33, 33, 33, 3, 30, 3, 53, + 33, 3, 3 }; /* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ @@ -870,23 +838,23 @@ static const yytype_int8 yyr1[] = { 0, 41, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, - 45, 45, 46, 46, 47, 47, 48, 48, 49, 49, - 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, + 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, + 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, + 49, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 54, 54, 54 + 54, 54, 54, 54, 54, 54 }; /* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ static const yytype_int8 yyr2[] = { - 0, 2, 0, 2, 2, 4, 4, 7, 9, 4, - 4, 5, 7, 4, 4, 3, 4, 4, 4, 4, - 4, 3, 3, 3, 3, 1, 2, 1, 1, 1, + 0, 2, 0, 2, 2, 4, 3, 6, 8, 3, + 4, 3, 5, 7, 3, 4, 3, 4, 4, 4, + 4, 4, 3, 3, 3, 3, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 0, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1 + 1, 1, 1, 1, 1, 1 }; @@ -1361,19 +1329,19 @@ yydestruct (const char *yymsg, case YYSYMBOL_STRING: /* STRING */ #line 100 "cmFortranParser.y" { free(((*yyvaluep).string)); } -#line 1365 "cmFortranParser.cxx" +#line 1333 "cmFortranParser.cxx" break; case YYSYMBOL_WORD: /* WORD */ #line 100 "cmFortranParser.y" { free(((*yyvaluep).string)); } -#line 1371 "cmFortranParser.cxx" +#line 1339 "cmFortranParser.cxx" break; case YYSYMBOL_CPP_INCLUDE_ANGLE: /* CPP_INCLUDE_ANGLE */ #line 100 "cmFortranParser.y" { free(((*yyvaluep).string)); } -#line 1377 "cmFortranParser.cxx" +#line 1345 "cmFortranParser.cxx" break; default: @@ -1655,7 +1623,7 @@ yyreduce: cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, true); } -#line 1659 "cmFortranParser.cxx" +#line 1627 "cmFortranParser.cxx" break; case 5: /* stmt: USE WORD other EOSTMT */ @@ -1665,77 +1633,83 @@ yyreduce: cmFortranParser_RuleUse(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1669 "cmFortranParser.cxx" +#line 1637 "cmFortranParser.cxx" break; - case 6: /* stmt: MODULE WORD other EOSTMT */ + case 6: /* stmt: MODULE WORD EOSTMT */ #line 118 "cmFortranParser.y" - { + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - if (cmsysString_strcasecmp((yyvsp[-2].string), "function") != 0 && - cmsysString_strcasecmp((yyvsp[-2].string), "procedure") != 0 && - cmsysString_strcasecmp((yyvsp[-2].string), "subroutine") != 0) { - cmFortranParser_RuleModule(parser, (yyvsp[-2].string)); - } - free((yyvsp[-2].string)); + cmFortranParser_RuleModule(parser, (yyvsp[-1].string)); + free((yyvsp[-1].string)); } -#line 1683 "cmFortranParser.cxx" +#line 1647 "cmFortranParser.cxx" break; - case 7: /* stmt: SUBMODULE LPAREN WORD RPAREN WORD other EOSTMT */ -#line 127 "cmFortranParser.y" - { + case 7: /* stmt: SUBMODULE LPAREN WORD RPAREN WORD EOSTMT */ +#line 123 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleSubmodule(parser, (yyvsp[-4].string), (yyvsp[-2].string)); - free((yyvsp[-4].string)); - free((yyvsp[-2].string)); + cmFortranParser_RuleSubmodule(parser, (yyvsp[-3].string), (yyvsp[-1].string)); + free((yyvsp[-3].string)); + free((yyvsp[-1].string)); } -#line 1694 "cmFortranParser.cxx" +#line 1658 "cmFortranParser.cxx" break; - case 8: /* stmt: SUBMODULE LPAREN WORD COLON WORD RPAREN WORD other EOSTMT */ -#line 133 "cmFortranParser.y" - { + case 8: /* stmt: SUBMODULE LPAREN WORD COLON WORD RPAREN WORD EOSTMT */ +#line 129 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-6].string), (yyvsp[-4].string), (yyvsp[-2].string)); - free((yyvsp[-6].string)); - free((yyvsp[-4].string)); - free((yyvsp[-2].string)); + cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-5].string), (yyvsp[-3].string), (yyvsp[-1].string)); + free((yyvsp[-5].string)); + free((yyvsp[-3].string)); + free((yyvsp[-1].string)); } -#line 1706 "cmFortranParser.cxx" +#line 1670 "cmFortranParser.cxx" break; - case 9: /* stmt: INTERFACE WORD other EOSTMT */ -#line 140 "cmFortranParser.y" - { + case 9: /* stmt: INTERFACE WORD EOSTMT */ +#line 136 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, true); - free((yyvsp[-2].string)); + free((yyvsp[-1].string)); } -#line 1716 "cmFortranParser.cxx" +#line 1680 "cmFortranParser.cxx" break; - case 10: /* stmt: END INTERFACE other EOSTMT */ -#line 145 "cmFortranParser.y" - { + case 10: /* stmt: END INTERFACE WORD EOSTMT */ +#line 141 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, false); + free((yyvsp[-1].string)); } -#line 1725 "cmFortranParser.cxx" +#line 1690 "cmFortranParser.cxx" break; - case 11: /* stmt: USE DCOLON WORD other EOSTMT */ -#line 149 "cmFortranParser.y" + case 11: /* stmt: END INTERFACE EOSTMT */ +#line 146 "cmFortranParser.y" + { + cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); + cmFortranParser_SetInInterface(parser, false); + } +#line 1699 "cmFortranParser.cxx" + break; + + case 12: /* stmt: USE DCOLON WORD other EOSTMT */ +#line 150 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleUse(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1735 "cmFortranParser.cxx" +#line 1709 "cmFortranParser.cxx" break; - case 12: /* stmt: USE COMMA WORD DCOLON WORD other EOSTMT */ -#line 154 "cmFortranParser.y" + case 13: /* stmt: USE COMMA WORD DCOLON WORD other EOSTMT */ +#line 155 "cmFortranParser.y" { if (cmsysString_strcasecmp((yyvsp[-4].string), "non_intrinsic") == 0) { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); @@ -1748,139 +1722,139 @@ yyreduce: free((yyvsp[-4].string)); free((yyvsp[-2].string)); } -#line 1752 "cmFortranParser.cxx" +#line 1726 "cmFortranParser.cxx" break; - case 13: /* stmt: INCLUDE STRING other EOSTMT */ -#line 166 "cmFortranParser.y" - { + case 14: /* stmt: INCLUDE STRING EOSTMT */ +#line 167 "cmFortranParser.y" + { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - cmFortranParser_RuleInclude(parser, (yyvsp[-2].string)); - free((yyvsp[-2].string)); + cmFortranParser_RuleInclude(parser, (yyvsp[-1].string)); + free((yyvsp[-1].string)); } -#line 1762 "cmFortranParser.cxx" +#line 1736 "cmFortranParser.cxx" break; - case 14: /* stmt: CPP_LINE_DIRECTIVE STRING other EOSTMT */ -#line 171 "cmFortranParser.y" + case 15: /* stmt: CPP_LINE_DIRECTIVE STRING other EOSTMT */ +#line 172 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleLineDirective(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1772 "cmFortranParser.cxx" +#line 1746 "cmFortranParser.cxx" break; - case 15: /* stmt: CPP_INCLUDE_ANGLE other EOSTMT */ -#line 176 "cmFortranParser.y" + case 16: /* stmt: CPP_INCLUDE_ANGLE other EOSTMT */ +#line 177 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleInclude(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1782 "cmFortranParser.cxx" +#line 1756 "cmFortranParser.cxx" break; - case 16: /* stmt: include STRING other EOSTMT */ -#line 181 "cmFortranParser.y" + case 17: /* stmt: include STRING other EOSTMT */ +#line 182 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleInclude(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1792 "cmFortranParser.cxx" +#line 1766 "cmFortranParser.cxx" break; - case 17: /* stmt: define WORD other EOSTMT */ -#line 186 "cmFortranParser.y" + case 18: /* stmt: define WORD other EOSTMT */ +#line 187 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleDefine(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1802 "cmFortranParser.cxx" +#line 1776 "cmFortranParser.cxx" break; - case 18: /* stmt: undef WORD other EOSTMT */ -#line 191 "cmFortranParser.y" + case 19: /* stmt: undef WORD other EOSTMT */ +#line 192 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleUndef(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1812 "cmFortranParser.cxx" +#line 1786 "cmFortranParser.cxx" break; - case 19: /* stmt: ifdef WORD other EOSTMT */ -#line 196 "cmFortranParser.y" + case 20: /* stmt: ifdef WORD other EOSTMT */ +#line 197 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleIfdef(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1822 "cmFortranParser.cxx" +#line 1796 "cmFortranParser.cxx" break; - case 20: /* stmt: ifndef WORD other EOSTMT */ -#line 201 "cmFortranParser.y" + case 21: /* stmt: ifndef WORD other EOSTMT */ +#line 202 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleIfndef(parser, (yyvsp[-2].string)); free((yyvsp[-2].string)); } -#line 1832 "cmFortranParser.cxx" +#line 1806 "cmFortranParser.cxx" break; - case 21: /* stmt: if other EOSTMT */ -#line 206 "cmFortranParser.y" + case 22: /* stmt: if other EOSTMT */ +#line 207 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleIf(parser); } -#line 1841 "cmFortranParser.cxx" +#line 1815 "cmFortranParser.cxx" break; - case 22: /* stmt: elif other EOSTMT */ -#line 210 "cmFortranParser.y" + case 23: /* stmt: elif other EOSTMT */ +#line 211 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleElif(parser); } -#line 1850 "cmFortranParser.cxx" +#line 1824 "cmFortranParser.cxx" break; - case 23: /* stmt: else other EOSTMT */ -#line 214 "cmFortranParser.y" + case 24: /* stmt: else other EOSTMT */ +#line 215 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleElse(parser); } -#line 1859 "cmFortranParser.cxx" +#line 1833 "cmFortranParser.cxx" break; - case 24: /* stmt: endif other EOSTMT */ -#line 218 "cmFortranParser.y" + case 25: /* stmt: endif other EOSTMT */ +#line 219 "cmFortranParser.y" { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleEndif(parser); } -#line 1868 "cmFortranParser.cxx" +#line 1842 "cmFortranParser.cxx" break; - case 48: /* misc_code: WORD */ -#line 240 "cmFortranParser.y" + case 49: /* misc_code: WORD */ +#line 241 "cmFortranParser.y" { free ((yyvsp[0].string)); } -#line 1874 "cmFortranParser.cxx" +#line 1848 "cmFortranParser.cxx" break; - case 55: /* misc_code: STRING */ -#line 247 "cmFortranParser.y" + case 56: /* misc_code: STRING */ +#line 248 "cmFortranParser.y" { free ((yyvsp[0].string)); } -#line 1880 "cmFortranParser.cxx" +#line 1854 "cmFortranParser.cxx" break; -#line 1884 "cmFortranParser.cxx" +#line 1858 "cmFortranParser.cxx" default: break; } @@ -2104,6 +2078,6 @@ yyreturnlab: return yyresult; } -#line 259 "cmFortranParser.y" +#line 260 "cmFortranParser.y" /* End of grammar */ diff --git a/Source/LexerParser/cmFortranParser.y b/Source/LexerParser/cmFortranParser.y index 07ca630..0b27060 100644 --- a/Source/LexerParser/cmFortranParser.y +++ b/Source/LexerParser/cmFortranParser.y @@ -115,34 +115,35 @@ stmt: cmFortranParser_RuleUse(parser, $2); free($2); } -| MODULE WORD other EOSTMT { +| MODULE WORD EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); - if (cmsysString_strcasecmp($2, "function") != 0 && - cmsysString_strcasecmp($2, "procedure") != 0 && - cmsysString_strcasecmp($2, "subroutine") != 0) { - cmFortranParser_RuleModule(parser, $2); - } + cmFortranParser_RuleModule(parser, $2); free($2); } -| SUBMODULE LPAREN WORD RPAREN WORD other EOSTMT { +| SUBMODULE LPAREN WORD RPAREN WORD EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleSubmodule(parser, $3, $5); free($3); free($5); } -| SUBMODULE LPAREN WORD COLON WORD RPAREN WORD other EOSTMT { +| SUBMODULE LPAREN WORD COLON WORD RPAREN WORD EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleSubmoduleNested(parser, $3, $5, $7); free($3); free($5); free($7); } -| INTERFACE WORD other EOSTMT { +| INTERFACE WORD EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, true); free($2); } -| END INTERFACE other EOSTMT { +| END INTERFACE WORD EOSTMT { + cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); + cmFortranParser_SetInInterface(parser, false); + free($3); + } +| END INTERFACE EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_SetInInterface(parser, false); } @@ -163,7 +164,7 @@ stmt: free($3); free($5); } -| INCLUDE STRING other EOSTMT { +| INCLUDE STRING EOSTMT { cmFortranParser* parser = cmFortran_yyget_extra(yyscanner); cmFortranParser_RuleInclude(parser, $2); free($2); diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake new file mode 100644 index 0000000..5cfb0e7 --- /dev/null +++ b/Source/Modules/CMakeBuildUtilities.cmake @@ -0,0 +1,379 @@ +#----------------------------------------------------------------------- +# Build the utilities used by CMake +# +# Originally it was a macro in the root `CMakeLists.txt` with the comment +# "Simply to improve readability...". +# However, as part of the modernization refactoring it was moved into a +# separate file cuz adding library alises wasn't possible inside the +# macro. +#----------------------------------------------------------------------- + +# Suppress unnecessary checks in third-party code. +include(Utilities/cmThirdPartyChecks.cmake) + +#--------------------------------------------------------------------- +# Create the kwsys library for CMake. +set(KWSYS_NAMESPACE cmsys) +set(KWSYS_USE_SystemTools 1) +set(KWSYS_USE_Directory 1) +set(KWSYS_USE_RegularExpression 1) +set(KWSYS_USE_Base64 1) +set(KWSYS_USE_MD5 1) +set(KWSYS_USE_Process 1) +set(KWSYS_USE_CommandLineArguments 1) +set(KWSYS_USE_ConsoleBuf 1) +set(KWSYS_HEADER_ROOT ${CMake_BINARY_DIR}/Source) +set(KWSYS_INSTALL_DOC_DIR "${CMAKE_DOC_DIR}") +if(CMake_NO_CXX_STANDARD) + set(KWSYS_CXX_STANDARD "") +endif() +if(CMake_NO_SELF_BACKTRACE) + set(KWSYS_NO_EXECINFO 1) +endif() +if(WIN32) + # FIXME: Teach KWSys to hard-code these checks on Windows. + set(KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC_COMPILED 0) + set(KWSYS_C_HAS_PTRDIFF_T_COMPILED 1) + set(KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H_COMPILED 1) + set(KWSYS_CXX_HAS_RLIMIT64_COMPILED 0) + set(KWSYS_CXX_HAS_SETENV_COMPILED 0) + set(KWSYS_CXX_HAS_UNSETENV_COMPILED 0) + set(KWSYS_CXX_HAS_UTIMENSAT_COMPILED 0) + set(KWSYS_CXX_HAS_UTIMES_COMPILED 0) + set(KWSYS_CXX_STAT_HAS_ST_MTIM_COMPILED 0) + set(KWSYS_CXX_STAT_HAS_ST_MTIMESPEC_COMPILED 0) + set(KWSYS_STL_HAS_WSTRING_COMPILED 1) + set(KWSYS_SYS_HAS_IFADDRS_H 0) +endif() +add_subdirectory(Source/kwsys) +set(kwsys_folder "Utilities/KWSys") +CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE} "${kwsys_folder}") +CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}_c "${kwsys_folder}") +if(BUILD_TESTING) + CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestDynload "${kwsys_folder}") + CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestProcess "${kwsys_folder}") + CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestsC "${kwsys_folder}") + CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestsCxx "${kwsys_folder}") + CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestSharedForward "${kwsys_folder}") +endif() + +#--------------------------------------------------------------------- +# Setup third-party libraries. +# Everything in the tree should be able to include files from the +# Utilities directory. +if((CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # using -isystem option generate error "template with C linkage" + include_directories("${CMake_SOURCE_DIR}/Utilities/std") +else() + include_directories(SYSTEM "${CMake_SOURCE_DIR}/Utilities/std") +endif() + +include_directories("${CMake_BINARY_DIR}/Utilities") +if((CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # using -isystem option generate error "template with C linkage" + include_directories("${CMake_SOURCE_DIR}/Utilities") +else() + include_directories(SYSTEM "${CMake_SOURCE_DIR}/Utilities") +endif() + +#--------------------------------------------------------------------- +# Build CMake std library for CMake and CTest. +add_subdirectory(Utilities/std) +CMAKE_SET_TARGET_FOLDER(cmstd "Utilities/std") + +# check for the use of system libraries versus builtin ones +# (a macro defined in this file) +CMAKE_HANDLE_SYSTEM_LIBRARIES() + +if(CMAKE_USE_SYSTEM_KWIML) + find_package(KWIML 1.0) + if(NOT KWIML_FOUND) + message(FATAL_ERROR "CMAKE_USE_SYSTEM_KWIML is ON but KWIML is not found!") + endif() +else() + if(BUILD_TESTING) + set(KWIML_TEST_ENABLE 1) + endif() + add_subdirectory(Utilities/KWIML) +endif() + +if(CMAKE_USE_SYSTEM_LIBRHASH) + find_package(LibRHash) + if(NOT LibRHash_FOUND) + message(FATAL_ERROR + "CMAKE_USE_SYSTEM_LIBRHASH is ON but LibRHash is not found!") + endif() +else() + add_subdirectory(Utilities/cmlibrhash) + add_library(LibRHash::LibRHash ALIAS cmlibrhash) + CMAKE_SET_TARGET_FOLDER(cmlibrhash "Utilities/3rdParty") +endif() + +#--------------------------------------------------------------------- +# Build zlib library for Curl, CMake, and CTest. +if(CMAKE_USE_SYSTEM_ZLIB) + find_package(ZLIB) + if(NOT ZLIB_FOUND) + message(FATAL_ERROR + "CMAKE_USE_SYSTEM_ZLIB is ON but a zlib is not found!") + endif() +else() + if(NOT POLICY CMP0102) # CMake < 3.17 + # Store in cache to protect from mark_as_advanced. + set(ZLIB_INCLUDE_DIR ${CMake_SOURCE_DIR}/Utilities CACHE PATH "") + else() + set(ZLIB_INCLUDE_DIR ${CMake_SOURCE_DIR}/Utilities) + endif() + set(ZLIB_LIBRARY cmzlib) + set(WITHOUT_ZLIB_DLL "") + set(WITHOUT_ZLIB_DLL_WITH_LIB cmzlib) + set(ZLIB_DLL "") + set(ZLIB_DLL_WITH_LIB cmzlib) + set(ZLIB_WINAPI "") + set(ZLIB_WINAPI_COMPILED 0) + set(ZLIB_WINAPI_WITH_LIB cmzlib) + add_subdirectory(Utilities/cmzlib) + add_library(ZLIB::ZLIB ALIAS cmzlib) + CMAKE_SET_TARGET_FOLDER(cmzlib "Utilities/3rdParty") +endif() + +#--------------------------------------------------------------------- +# Build Curl library for CTest. +if(CMAKE_USE_SYSTEM_CURL) + find_package(CURL) + if(NOT CURL_FOUND) + message(FATAL_ERROR + "CMAKE_USE_SYSTEM_CURL is ON but a curl is not found!") + endif() +else() + if(CMAKE_TESTS_CDASH_SERVER) + set(CMAKE_CURL_TEST_URL "${CMAKE_TESTS_CDASH_SERVER}/user.php") + endif() + set(_CMAKE_USE_OPENSSL_DEFAULT OFF) + if(NOT DEFINED CMAKE_USE_OPENSSL AND NOT WIN32 AND NOT APPLE + AND CMAKE_SYSTEM_NAME MATCHES "(Linux|FreeBSD)") + set(_CMAKE_USE_OPENSSL_DEFAULT ON) + endif() + option(CMAKE_USE_OPENSSL "Use OpenSSL." ${_CMAKE_USE_OPENSSL_DEFAULT}) + mark_as_advanced(CMAKE_USE_OPENSSL) + if(CMAKE_USE_OPENSSL) + set(CURL_CA_BUNDLE "" CACHE FILEPATH "Path to SSL CA Certificate Bundle") + set(CURL_CA_PATH "" CACHE PATH "Path to SSL CA Certificate Directory") + mark_as_advanced(CURL_CA_BUNDLE CURL_CA_PATH) + endif() + if(NOT CMAKE_USE_SYSTEM_NGHTTP2) + # Tell curl's FindNGHTTP2 module to use our library. + set(NGHTTP2_LIBRARY cmnghttp2) + set(NGHTTP2_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmnghttp2/lib/includes) + endif() + add_subdirectory(Utilities/cmcurl) + add_library(CURL::libcurl ALIAS cmcurl) + CMAKE_SET_TARGET_FOLDER(cmcurl "Utilities/3rdParty") + CMAKE_SET_TARGET_FOLDER(LIBCURL "Utilities/3rdParty") + if(NOT CMAKE_USE_SYSTEM_NGHTTP2) + # Configure after curl to re-use some check results. + add_subdirectory(Utilities/cmnghttp2) + CMAKE_SET_TARGET_FOLDER(cmnghttp2 "Utilities/3rdParty") + endif() +endif() + +#--------------------------------------------------------------------- +# Build expat library for CMake, CTest, and libarchive. +if(CMAKE_USE_SYSTEM_EXPAT) + find_package(EXPAT) + if(NOT EXPAT_FOUND) + message(FATAL_ERROR + "CMAKE_USE_SYSTEM_EXPAT is ON but a expat is not found!") + endif() + set(CMAKE_EXPAT_INCLUDES ${EXPAT_INCLUDE_DIRS}) + set(CMAKE_EXPAT_LIBRARIES ${EXPAT_LIBRARIES}) +else() + set(CMAKE_EXPAT_INCLUDES) + set(CMAKE_EXPAT_LIBRARIES cmexpat) + add_subdirectory(Utilities/cmexpat) + add_library(EXPAT::EXPAT ALIAS cmexpat) + CMAKE_SET_TARGET_FOLDER(cmexpat "Utilities/3rdParty") +endif() + +#--------------------------------------------------------------------- +# Build or use system libbz2 for libarchive. +if(NOT CMAKE_USE_SYSTEM_LIBARCHIVE) + if(CMAKE_USE_SYSTEM_BZIP2) + find_package(BZip2) + else() + set(BZIP2_INCLUDE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmbzip2") + set(BZIP2_LIBRARIES cmbzip2) + set(BZIP2_NEED_PREFIX "") + set(USE_BZIP2_DLL "") + set(USE_BZIP2_DLL_WITH_LIB cmbzip2) + set(USE_BZIP2_STATIC "") + set(USE_BZIP2_STATIC_WITH_LIB cmbzip2) + add_subdirectory(Utilities/cmbzip2) + CMAKE_SET_TARGET_FOLDER(cmbzip2 "Utilities/3rdParty") + endif() +endif() + +#--------------------------------------------------------------------- +# Build or use system zstd for libarchive. +if(NOT CMAKE_USE_SYSTEM_LIBARCHIVE) + if(NOT CMAKE_USE_SYSTEM_ZSTD) + set(ZSTD_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmzstd") + set(ZSTD_LIBRARY cmzstd) + add_subdirectory(Utilities/cmzstd) + CMAKE_SET_TARGET_FOLDER(cmzstd "Utilities/3rdParty") + endif() +endif() + +#--------------------------------------------------------------------- +# Build or use system liblzma for libarchive. +if(NOT CMAKE_USE_SYSTEM_LIBARCHIVE) + if(CMAKE_USE_SYSTEM_LIBLZMA) + find_package(LibLZMA) + if(NOT LIBLZMA_FOUND) + message(FATAL_ERROR "CMAKE_USE_SYSTEM_LIBLZMA is ON but LibLZMA is not found!") + endif() + else() + add_subdirectory(Utilities/cmliblzma) + CMAKE_SET_TARGET_FOLDER(cmliblzma "Utilities/3rdParty") + set(LIBLZMA_HAS_AUTO_DECODER 1) + set(LIBLZMA_HAS_EASY_ENCODER 1) + set(LIBLZMA_HAS_LZMA_PRESET 1) + set(LIBLZMA_INCLUDE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmliblzma/liblzma/api") + set(LIBLZMA_LIBRARY cmliblzma) + set(HAVE_LZMA_STREAM_ENCODER_MT 1) + endif() +endif() + +#--------------------------------------------------------------------- +# Build or use system libarchive for CMake and CTest. +if(CMAKE_USE_SYSTEM_LIBARCHIVE) + find_package(LibArchive 3.3.3) + if(NOT LibArchive_FOUND) + message(FATAL_ERROR "CMAKE_USE_SYSTEM_LIBARCHIVE is ON but LibArchive is not found!") + endif() + # NOTE `FindLibArchive` got imported targets support since 3.17 + if (NOT TARGET LibArchive::LibArchive) + add_library(LibArchive::LibArchive UNKNOWN IMPORTED) + set_target_properties(LibArchive::LibArchive PROPERTIES + IMPORTED_LOCATION "${LibArchive_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${LibArchive_INCLUDE_DIRS}") + endif () +else() + set(EXPAT_INCLUDE_DIR ${CMAKE_EXPAT_INCLUDES}) + set(EXPAT_LIBRARY ${CMAKE_EXPAT_LIBRARIES}) + set(ENABLE_MBEDTLS OFF) + set(ENABLE_NETTLE OFF) + if(DEFINED CMAKE_USE_OPENSSL) + set(ENABLE_OPENSSL "${CMAKE_USE_OPENSSL}") + else() + set(ENABLE_OPENSSL OFF) + endif() + set(ENABLE_LIBB2 OFF) + set(ENABLE_LZ4 OFF) + set(ENABLE_LZO OFF) + set(ENABLE_LZMA ON) + set(ENABLE_ZSTD ON) + set(ENABLE_ZLIB ON) + set(ENABLE_BZip2 ON) + set(ENABLE_LIBXML2 OFF) + set(ENABLE_EXPAT OFF) + set(ENABLE_PCREPOSIX OFF) + set(ENABLE_LibGCC OFF) + set(ENABLE_CNG OFF) + set(ENABLE_TAR OFF) + set(ENABLE_TAR_SHARED OFF) + set(ENABLE_CPIO OFF) + set(ENABLE_CPIO_SHARED OFF) + set(ENABLE_CAT OFF) + set(ENABLE_CAT_SHARED OFF) + set(ENABLE_XATTR OFF) + set(ENABLE_ACL OFF) + set(ENABLE_ICONV OFF) + set(ENABLE_TEST OFF) + set(ENABLE_COVERAGE OFF) + set(ENABLE_INSTALL OFF) + set(POSIX_REGEX_LIB "" CACHE INTERNAL "libarchive: No POSIX regular expression support") + set(ENABLE_SAFESEH "" CACHE INTERNAL "libarchive: No /SAFESEH linker flag") + set(WINDOWS_VERSION "WIN7" CACHE INTERNAL "libarchive: Set Windows version to use (Windows only)") + add_subdirectory(Utilities/cmlibarchive) + add_library(LibArchive::LibArchive ALIAS cmlibarchive) + target_compile_definitions(cmlibarchive INTERFACE LIBARCHIVE_STATIC) + CMAKE_SET_TARGET_FOLDER(cmlibarchive "Utilities/3rdParty") +endif() + +#--------------------------------------------------------------------- +# Build jsoncpp library. +if(CMAKE_USE_SYSTEM_JSONCPP) + find_package(JsonCpp 1.6.0) + if(NOT JsonCpp_FOUND) + message(FATAL_ERROR + "CMAKE_USE_SYSTEM_JSONCPP is ON but a JsonCpp is not found!") + endif() + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC|Clang") + set_property(TARGET JsonCpp::JsonCpp APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS -Wno-deprecated-declarations) + endif() +else() + add_subdirectory(Utilities/cmjsoncpp) + add_library(JsonCpp::JsonCpp ALIAS cmjsoncpp) + CMAKE_SET_TARGET_FOLDER(cmjsoncpp "Utilities/3rdParty") +endif() + +#--------------------------------------------------------------------- +# Build libuv library. +if(CMAKE_USE_SYSTEM_LIBUV) + if(WIN32) + find_package(LibUV 1.38.0) + else() + find_package(LibUV 1.28.0) + endif() + if(NOT LIBUV_FOUND) + message(FATAL_ERROR + "CMAKE_USE_SYSTEM_LIBUV is ON but a libuv is not found!") + endif() +else() + add_subdirectory(Utilities/cmlibuv) + add_library(LibUV::LibUV ALIAS cmlibuv) + CMAKE_SET_TARGET_FOLDER(cmlibuv "Utilities/3rdParty") +endif() + +#--------------------------------------------------------------------- +# Use curses? +if(NOT DEFINED BUILD_CursesDialog) + if(UNIX) + include(${CMake_SOURCE_DIR}/Source/Checks/Curses.cmake) + set(BUILD_CursesDialog_DEFAULT "${CMakeCheckCurses_COMPILED}") + elseif(WIN32) + set(BUILD_CursesDialog_DEFAULT "OFF") + endif() + option(BUILD_CursesDialog "Build the CMake Curses Dialog ccmake" "${BUILD_CursesDialog_DEFAULT}") +endif() +if(BUILD_CursesDialog) + if(UNIX) + set(CURSES_NEED_NCURSES TRUE) + find_package(Curses) + if(NOT CURSES_FOUND) + message(WARNING + "'ccmake' will not be built because Curses was not found.\n" + "Turn off BUILD_CursesDialog to suppress this message." + ) + set(BUILD_CursesDialog 0) + endif() + elseif(WIN32) + # FIXME: Add support for system-provided pdcurses. + add_subdirectory(Utilities/cmpdcurses) + set(CURSES_LIBRARY cmpdcurses) + set(CURSES_INCLUDE_PATH "") # cmpdcurses has usage requirements + set(CMAKE_USE_SYSTEM_FORM 0) + set(HAVE_CURSES_USE_DEFAULT_COLORS 1) + endif() +endif() +if(BUILD_CursesDialog) + if(NOT CMAKE_USE_SYSTEM_FORM) + add_subdirectory(Source/CursesDialog/form) + elseif(NOT CURSES_FORM_LIBRARY) + message(FATAL_ERROR "CMAKE_USE_SYSTEM_FORM in ON but CURSES_FORM_LIBRARY is not set!") + endif() +endif() diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 0c263bb..c90b7ee 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -3,7 +3,7 @@ project(QtDialog) CMake_OPTIONAL_COMPONENT(cmake-gui) -set (QT_COMPONENTS +set(QT_COMPONENTS Core Widgets Gui @@ -41,8 +41,8 @@ set(CMake_QT_EXTRA_LIBRARIES) # Try to find the package WinExtras for the task bar progress if(WIN32) find_package(Qt${INSTALLED_QT_VERSION}WinExtras QUIET) - if (Qt${INSTALLED_QT_VERSION}WinExtras_FOUND) - add_definitions(-DQT_WINEXTRAS) + if(Qt${INSTALLED_QT_VERSION}WinExtras_FOUND) + add_compile_definitions(QT_WINEXTRAS) list(APPEND CMake_QT_EXTRA_LIBRARIES Qt${INSTALLED_QT_VERSION}::WinExtras) list(APPEND QT_COMPONENTS WinExtras) endif() @@ -100,14 +100,14 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) endif() endmacro() macro(install_qt_plugins _comps _plugins_var) - foreach(_qt_comp ${${_comps}}) - if (INSTALLED_QT_VERSION VERSION_LESS 6) + foreach(_qt_comp IN LISTS ${_comps}) + if(INSTALLED_QT_VERSION VERSION_LESS 6) set(_qt_module_plugins ${Qt${INSTALLED_QT_VERSION}${_qt_comp}_PLUGINS}) else() get_target_property(_qt_module_plugins Qt${INSTALLED_QT_VERSION}::${_qt_comp} QT_PLUGINS) endif() - foreach(_qt_plugin ${_qt_module_plugins}) - if (INSTALLED_QT_VERSION VERSION_GREATER_EQUAL 6) + foreach(_qt_plugin IN LISTS _qt_module_plugins) + if(INSTALLED_QT_VERSION VERSION_GREATER_EQUAL 6) # Qt6 provides the plugins as individual packages that need to be found. find_package(Qt${INSTALLED_QT_VERSION}${_qt_plugin} QUIET PATHS ${Qt${INSTALLED_QT_VERSION}${_qt_comp}_DIR}) @@ -117,7 +117,7 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) endforeach() endmacro() if(APPLE) - if (INSTALLED_QT_VERSION VERSION_EQUAL 5) + if(INSTALLED_QT_VERSION VERSION_EQUAL 5) install_qt_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS) if(TARGET Qt5::QMacStylePlugin) install_qt_plugin("Qt5::QMacStylePlugin" QT_PLUGINS) @@ -132,7 +132,7 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources" ${COMPONENT}) elseif(WIN32 AND NOT CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES) - if (INSTALLED_QT_VERSION VERSION_EQUAL 5) + if(INSTALLED_QT_VERSION VERSION_EQUAL 5) install_qt_plugin("Qt5::QWindowsIntegrationPlugin" QT_PLUGINS) else() # FIXME: Minimize plugins for Qt6. @@ -152,7 +152,10 @@ if(APPLE) get_filename_component(Qt_BIN_DIR "${Qt_BIN_DIR}" PATH) endif() -set(SRCS +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +add_library( + CMakeGUILib STATIC AddCacheEntry.cxx AddCacheEntry.h CMakeSetupDialog.cxx @@ -179,6 +182,16 @@ set(SRCS WarningMessagesDialog.cxx WarningMessagesDialog.h ) +# 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} + Qt${INSTALLED_QT_VERSION}::Core + Qt${INSTALLED_QT_VERSION}::Widgets + ) + set(UI_SRCS CMakeSetupDialog.ui Compilers.ui @@ -204,7 +217,7 @@ set(MOC_SRCS ) set(QRC_SRCS CMakeSetup.qrc) -if (INSTALLED_QT_VERSION VERSION_LESS 6) +if(INSTALLED_QT_VERSION VERSION_LESS 6) qt5_wrap_ui(UI_BUILT_SRCS ${UI_SRCS}) qt5_wrap_cpp(MOC_BUILT_SRCS ${MOC_SRCS}) qt5_add_resources(QRC_BUILT_SRCS ${QRC_SRCS}) @@ -215,15 +228,18 @@ else() endif() add_library(CMakeGUIQRCLib OBJECT ${QRC_BUILT_SRCS}) -if (FALSE) # CMake's bootstrap binary does not support automoc +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_BUILT_SRCS} - ${MOC_BUILT_SRCS}) -endif () +else() + target_sources( + CMakeGUILib + PRIVATE + ${UI_BUILT_SRCS} + ${MOC_BUILT_SRCS} + ) +endif() if(USE_LGPL) install(FILES ${CMake_SOURCE_DIR}/Licenses/LGPLv${USE_LGPL}.txt @@ -233,22 +249,25 @@ if(USE_LGPL) PROPERTY COMPILE_DEFINITIONS USE_LGPL="${USE_LGPL}") endif() -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -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} - Qt${INSTALLED_QT_VERSION}::Core Qt${INSTALLED_QT_VERSION}::Widgets) - add_library(CMakeGUIMainLib STATIC CMakeSetup.cxx) -target_link_libraries(CMakeGUIMainLib PUBLIC CMakeGUILib) +target_link_libraries( + CMakeGUIMainLib + PUBLIC + CMakeGUILib + ) -add_executable(cmake-gui WIN32 MACOSX_BUNDLE CMakeGUIExec.cxx ${MANIFEST_FILE}) -target_link_libraries(cmake-gui CMakeGUIMainLib Qt${INSTALLED_QT_VERSION}::Core) +add_executable(cmake-gui WIN32 MACOSX_BUNDLE CMakeGUIExec.cxx) +target_link_libraries(cmake-gui + PRIVATE + CMakeGUIMainLib + CMakeGUIQRCLib + $<TARGET_NAME_IF_EXISTS:CMakeVersion> + ManifestLib + Qt${INSTALLED_QT_VERSION}::Core + ) -target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeGUIQRCLib>) if(WIN32) - target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeVersion> CMakeSetup.rc) + target_sources(CMakeGUIMainLib INTERFACE CMakeSetup.rc) endif() if(APPLE) target_sources(CMakeGUIMainLib INTERFACE CMakeSetup.icns) @@ -300,21 +319,19 @@ if(APPLE) $<TARGET_FILE_DIR:cmake>/cmake-gui ) endif() -set(CMAKE_INSTALL_DESTINATION_ARGS - BUNDLE DESTINATION "${CMAKE_BUNDLE_LOCATION}" ${COMPONENT}) install(TARGETS cmake-gui RUNTIME DESTINATION bin ${COMPONENT} - ${CMAKE_INSTALL_DESTINATION_ARGS}) + BUNDLE DESTINATION "${CMAKE_BUNDLE_LOCATION}" ${COMPONENT}) if(UNIX AND NOT APPLE) - foreach (size IN ITEMS 32 128) + foreach(size IN ITEMS 32 128) install( FILES "${CMAKE_CURRENT_SOURCE_DIR}/CMakeSetup${size}.png" DESTINATION "${CMAKE_XDGDATA_DIR}/icons/hicolor/${size}x${size}/apps" ${COMPONENT} RENAME "CMakeSetup.png") - endforeach () + endforeach() # install a desktop file so CMake appears in the application start menu # with an icon @@ -348,5 +365,4 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) endif() set(CMAKE_PACKAGE_QTGUI TRUE) -configure_file("${QtDialog_SOURCE_DIR}/QtDialogCPack.cmake.in" - "${QtDialog_BINARY_DIR}/QtDialogCPack.cmake" @ONLY) +configure_file(QtDialogCPack.cmake.in QtDialogCPack.cmake @ONLY) diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx index f3c4a8b..707eaae 100644 --- a/Source/QtDialog/FirstConfigure.cxx +++ b/Source/QtDialog/FirstConfigure.cxx @@ -244,7 +244,8 @@ void StartCompilerSetup::onGeneratorChanged(int index) if (!DefaultGeneratorPlatform.isEmpty()) { int platform_index = platforms.indexOf(DefaultGeneratorPlatform); if (platform_index != -1) { - this->PlatformOptions->setCurrentIndex(platform_index); + // The index is off-by-one due to the first empty item added above. + this->PlatformOptions->setCurrentIndex(platform_index + 1); } } } else { diff --git a/Source/cmAddSubDirectoryCommand.cxx b/Source/cmAddSubDirectoryCommand.cxx index 83d6306..6a2ab0b 100644 --- a/Source/cmAddSubDirectoryCommand.cxx +++ b/Source/cmAddSubDirectoryCommand.cxx @@ -26,6 +26,7 @@ bool cmAddSubDirectoryCommand(std::vector<std::string> const& args, std::string binArg; bool excludeFromAll = false; + bool system = false; // process the rest of the arguments looking for optional args for (std::string const& arg : cmMakeRange(args).advance(1)) { @@ -33,6 +34,10 @@ bool cmAddSubDirectoryCommand(std::vector<std::string> const& args, excludeFromAll = true; continue; } + if (arg == "SYSTEM") { + system = true; + continue; + } if (binArg.empty()) { binArg = arg; } else { @@ -40,6 +45,11 @@ bool cmAddSubDirectoryCommand(std::vector<std::string> const& args, return false; } } + // "SYSTEM" directory property should also affects targets in nested + // subdirectories. + if (mf.GetPropertyAsBool("SYSTEM")) { + system = true; + } // Compute the full path to the specified source directory. // Interpret a relative path with respect to the current source directory. @@ -102,7 +112,7 @@ bool cmAddSubDirectoryCommand(std::vector<std::string> const& args, binPath = cmSystemTools::CollapseFullPath(binPath); // Add the subdirectory using the computed full paths. - mf.AddSubDirectory(srcPath, binPath, excludeFromAll, true); + mf.AddSubDirectory(srcPath, binPath, excludeFromAll, true, system); return true; } diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx index 4624f1c..ad57a88 100644 --- a/Source/cmArgumentParser.cxx +++ b/Source/cmArgumentParser.cxx @@ -4,9 +4,14 @@ #include <algorithm> +#include "cmArgumentParserTypes.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmStringAlgorithms.h" + namespace ArgumentParser { -auto ActionMap::Emplace(cm::string_view name, Action action) +auto KeywordActionMap::Emplace(cm::string_view name, KeywordAction action) -> std::pair<iterator, bool> { auto const it = @@ -19,7 +24,7 @@ auto ActionMap::Emplace(cm::string_view name, Action action) : std::make_pair(this->emplace(it, name, std::move(action)), true); } -auto ActionMap::Find(cm::string_view name) const -> const_iterator +auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator { auto const it = std::lower_bound(this->begin(), this->end(), name, @@ -29,68 +34,171 @@ auto ActionMap::Find(cm::string_view name) const -> const_iterator return (it != this->end() && it->first == name) ? it : this->end(); } +auto PositionActionMap::Emplace(std::size_t pos, PositionAction action) + -> std::pair<iterator, bool> +{ + auto const it = std::lower_bound( + this->begin(), this->end(), pos, + [](value_type const& elem, std::size_t k) { return elem.first < k; }); + return (it != this->end() && it->first == pos) + ? std::make_pair(it, false) + : std::make_pair(this->emplace(it, pos, std::move(action)), true); +} + +auto PositionActionMap::Find(std::size_t pos) const -> const_iterator +{ + auto const it = std::lower_bound( + this->begin(), this->end(), pos, + [](value_type const& elem, std::size_t k) { return elem.first < k; }); + return (it != this->end() && it->first == pos) ? it : this->end(); +} + +void Instance::Bind(std::function<Continue(cm::string_view)> f, + ExpectAtLeast expect) +{ + this->KeywordValueFunc = std::move(f); + this->KeywordValuesExpected = expect.Count; +} + void Instance::Bind(bool& val) { val = true; - this->CurrentString = nullptr; - this->CurrentList = nullptr; - this->ExpectValue = false; + this->Bind(nullptr, ExpectAtLeast{ 0 }); } void Instance::Bind(std::string& val) { - this->CurrentString = &val; - this->CurrentList = nullptr; - this->ExpectValue = true; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val = std::string(arg); + return Continue::No; + }, + ExpectAtLeast{ 1 }); +} + +void Instance::Bind(NonEmpty<std::string>& val) +{ + this->Bind( + [this, &val](cm::string_view arg) -> Continue { + if (arg.empty() && this->ParseResults) { + this->ParseResults->AddKeywordError(this->Keyword, + " empty string not allowed\n"); + } + val.assign(std::string(arg)); + return Continue::No; + }, + ExpectAtLeast{ 1 }); } -void Instance::Bind(StringList& val) +void Instance::Bind(Maybe<std::string>& val) { - this->CurrentString = nullptr; - this->CurrentList = &val; - this->ExpectValue = true; + this->Bind( + [&val](cm::string_view arg) -> Continue { + static_cast<std::string&>(val) = std::string(arg); + return Continue::No; + }, + ExpectAtLeast{ 0 }); } -void Instance::Bind(MultiStringList& val) +void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val) { - this->CurrentString = nullptr; - this->CurrentList = (static_cast<void>(val.emplace_back()), &val.back()); - this->ExpectValue = false; + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 0 }); } -void Instance::Consume(cm::string_view arg, void* result, - std::vector<std::string>* unparsedArguments, - std::vector<std::string>* keywordsMissingValue, - std::vector<std::string>* parsedKeywords) +void Instance::Bind(NonEmpty<std::vector<std::string>>& val) { - auto const it = this->Bindings.Find(arg); - if (it != this->Bindings.end()) { - if (parsedKeywords != nullptr) { - parsedKeywords->emplace_back(arg); + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 1 }); +} + +void Instance::Bind(std::vector<std::vector<std::string>>& multiVal) +{ + multiVal.emplace_back(); + std::vector<std::string>& val = multiVal.back(); + this->Bind( + [&val](cm::string_view arg) -> Continue { + val.emplace_back(arg); + return Continue::Yes; + }, + ExpectAtLeast{ 0 }); +} + +void Instance::Consume(std::size_t pos, cm::string_view arg) +{ + auto const it = this->Bindings.Keywords.Find(arg); + if (it != this->Bindings.Keywords.end()) { + this->FinishKeyword(); + this->Keyword = it->first; + this->KeywordValuesSeen = 0; + this->DoneWithPositional = true; + if (this->Bindings.ParsedKeyword) { + this->Bindings.ParsedKeyword(*this, it->first); } - it->second(*this, result); - if (this->ExpectValue && keywordsMissingValue != nullptr) { - keywordsMissingValue->emplace_back(arg); + it->second(*this); + return; + } + + if (this->KeywordValueFunc) { + switch (this->KeywordValueFunc(arg)) { + case Continue::Yes: + break; + case Continue::No: + this->KeywordValueFunc = nullptr; + break; } + ++this->KeywordValuesSeen; return; } - if (this->CurrentString != nullptr) { - this->CurrentString->assign(std::string(arg)); - this->CurrentString = nullptr; - this->CurrentList = nullptr; - } else if (this->CurrentList != nullptr) { - this->CurrentList->emplace_back(arg); - } else if (unparsedArguments != nullptr) { - unparsedArguments->emplace_back(arg); + if (!this->DoneWithPositional) { + auto const pit = this->Bindings.Positions.Find(pos); + if (pit != this->Bindings.Positions.end()) { + pit->second(*this, pos, arg); + return; + } + } + + if (this->UnparsedArguments != nullptr) { + this->UnparsedArguments->emplace_back(arg); } +} - if (this->ExpectValue) { - if (keywordsMissingValue != nullptr) { - keywordsMissingValue->pop_back(); +void Instance::FinishKeyword() +{ + if (this->Keyword.empty()) { + return; + } + if (this->KeywordValuesSeen < this->KeywordValuesExpected) { + if (this->ParseResults != nullptr) { + this->ParseResults->AddKeywordError(this->Keyword, + " missing required value\n"); } - this->ExpectValue = false; + if (this->Bindings.KeywordMissingValue) { + this->Bindings.KeywordMissingValue(*this, this->Keyword); + } + } +} + +bool ParseResult::MaybeReportError(cmMakefile& mf) const +{ + if (*this) { + return false; + } + std::string e; + for (auto const& ke : this->KeywordErrors) { + e = cmStrCat(e, "Error after keyword \"", ke.first, "\":\n", ke.second); } + mf.IssueMessage(MessageType::FATAL_ERROR, e); + return true; } } // namespace ArgumentParser diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h index 71ed844..fdf54fb 100644 --- a/Source/cmArgumentParser.h +++ b/Source/cmArgumentParser.h @@ -5,59 +5,220 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <cassert> +#include <cstddef> #include <functional> +#include <map> #include <string> #include <utility> #include <vector> +#include <cm/optional> #include <cm/string_view> +#include <cm/type_traits> #include <cmext/string_view> +#include "cmArgumentParserTypes.h" // IWYU pragma: keep + +template <typename Result> +class cmArgumentParser; // IWYU pragma: keep + +class cmMakefile; + namespace ArgumentParser { -using StringList = std::vector<std::string>; -using MultiStringList = std::vector<StringList>; +class ParseResult +{ + std::map<cm::string_view, std::string> KeywordErrors; + +public: + explicit operator bool() const { return this->KeywordErrors.empty(); } + + void AddKeywordError(cm::string_view key, cm::string_view text) + + { + this->KeywordErrors[key] += text; + } + + std::map<cm::string_view, std::string> const& GetKeywordErrors() const + { + return this->KeywordErrors; + } + + bool MaybeReportError(cmMakefile& mf) const; +}; + +template <typename Result> +typename std::enable_if<std::is_base_of<ParseResult, Result>::value, + ParseResult*>::type +AsParseResultPtr(Result& result) +{ + return &result; +} + +template <typename Result> +typename std::enable_if<!std::is_base_of<ParseResult, Result>::value, + ParseResult*>::type +AsParseResultPtr(Result&) +{ + return nullptr; +} + +enum class Continue +{ + No, + Yes, +}; + +struct ExpectAtLeast +{ + std::size_t Count = 0; + + ExpectAtLeast(std::size_t count) + : Count(count) + { + } +}; class Instance; -using Action = std::function<void(Instance&, void*)>; +using KeywordAction = std::function<void(Instance&)>; +using KeywordNameAction = std::function<void(Instance&, cm::string_view)>; +using PositionAction = + std::function<void(Instance&, std::size_t, cm::string_view)>; -// using ActionMap = cm::flat_map<cm::string_view, Action>; -class ActionMap : public std::vector<std::pair<cm::string_view, Action>> +// using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>; +class KeywordActionMap + : public std::vector<std::pair<cm::string_view, KeywordAction>> { public: - std::pair<iterator, bool> Emplace(cm::string_view name, Action action); + std::pair<iterator, bool> Emplace(cm::string_view name, + KeywordAction action); const_iterator Find(cm::string_view name) const; }; +// using PositionActionMap = cm::flat_map<cm::string_view, PositionAction>; +class PositionActionMap + : public std::vector<std::pair<std::size_t, PositionAction>> +{ +public: + std::pair<iterator, bool> Emplace(std::size_t pos, PositionAction action); + const_iterator Find(std::size_t pos) const; +}; + +class ActionMap +{ +public: + KeywordActionMap Keywords; + KeywordNameAction KeywordMissingValue; + KeywordNameAction ParsedKeyword; + PositionActionMap Positions; +}; + +class Base +{ +public: + using ExpectAtLeast = ArgumentParser::ExpectAtLeast; + using Continue = ArgumentParser::Continue; + using Instance = ArgumentParser::Instance; + using ParseResult = ArgumentParser::ParseResult; + + ArgumentParser::ActionMap Bindings; + + bool MaybeBind(cm::string_view name, KeywordAction action) + { + return this->Bindings.Keywords.Emplace(name, std::move(action)).second; + } + + void Bind(cm::string_view name, KeywordAction action) + { + bool const inserted = this->MaybeBind(name, std::move(action)); + assert(inserted); + static_cast<void>(inserted); + } + + void BindParsedKeyword(KeywordNameAction action) + { + assert(!this->Bindings.ParsedKeyword); + this->Bindings.ParsedKeyword = std::move(action); + } + + void BindKeywordMissingValue(KeywordNameAction action) + { + assert(!this->Bindings.KeywordMissingValue); + this->Bindings.KeywordMissingValue = std::move(action); + } + + void Bind(std::size_t pos, PositionAction action) + { + bool const inserted = + this->Bindings.Positions.Emplace(pos, std::move(action)).second; + assert(inserted); + static_cast<void>(inserted); + } +}; + class Instance { public: - Instance(ActionMap const& bindings) + Instance(ActionMap const& bindings, ParseResult* parseResult, + std::vector<std::string>* unparsedArguments, void* result = nullptr) : Bindings(bindings) + , ParseResults(parseResult) + , UnparsedArguments(unparsedArguments) + , Result(result) { } + void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect); void Bind(bool& val); void Bind(std::string& val); - void Bind(StringList& val); - void Bind(MultiStringList& val); + void Bind(NonEmpty<std::string>& val); + void Bind(Maybe<std::string>& val); + void Bind(MaybeEmpty<std::vector<std::string>>& val); + void Bind(NonEmpty<std::vector<std::string>>& val); + void Bind(std::vector<std::vector<std::string>>& val); + + // cm::optional<> records the presence the keyword to which it binds. + template <typename T> + void Bind(cm::optional<T>& optVal) + { + if (!optVal) { + optVal.emplace(); + } + this->Bind(*optVal); + } - void Consume(cm::string_view arg, void* result, - std::vector<std::string>* unparsedArguments, - std::vector<std::string>* keywordsMissingValue, - std::vector<std::string>* parsedKeywords); + template <typename Range> + void Parse(Range const& args, std::size_t pos = 0) + { + for (cm::string_view arg : args) { + this->Consume(pos++, arg); + } + this->FinishKeyword(); + } private: ActionMap const& Bindings; - std::string* CurrentString = nullptr; - StringList* CurrentList = nullptr; - bool ExpectValue = false; + ParseResult* ParseResults = nullptr; + std::vector<std::string>* UnparsedArguments = nullptr; + void* Result = nullptr; + + cm::string_view Keyword; + std::size_t KeywordValuesSeen = 0; + std::size_t KeywordValuesExpected = 0; + std::function<Continue(cm::string_view)> KeywordValueFunc; + bool DoneWithPositional = false; + + void Consume(std::size_t pos, cm::string_view arg); + void FinishKeyword(); + + template <typename Result> + friend class ::cmArgumentParser; }; } // namespace ArgumentParser template <typename Result> -class cmArgumentParser +class cmArgumentParser : private ArgumentParser::Base { public: // I *think* this function could be made `constexpr` when the code is @@ -65,83 +226,194 @@ public: template <typename T> cmArgumentParser& Bind(cm::static_string_view name, T Result::*member) { - bool const inserted = - this->Bindings - .Emplace(name, - [member](ArgumentParser::Instance& instance, void* result) { - instance.Bind(static_cast<Result*>(result)->*member); - }) - .second; - assert(inserted), (void)inserted; + this->Base::Bind(name, [member](Instance& instance) { + instance.Bind(static_cast<Result*>(instance.Result)->*member); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + Continue (Result::*member)(cm::string_view), + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [member, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + instance.Bind( + [result, member](cm::string_view arg) -> Continue { + return (result->*member)(arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + Continue (Result::*member)(cm::string_view, + cm::string_view), + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [member, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + cm::string_view keyword = instance.Keyword; + instance.Bind( + [result, member, keyword](cm::string_view arg) -> Continue { + return (result->*member)(keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + std::function<Continue(Result&, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + instance.Bind( + [result, &f](cm::string_view arg) -> Continue { + return f(*result, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind( + cm::static_string_view name, + std::function<Continue(Result&, cm::string_view, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + Result* result = static_cast<Result*>(instance.Result); + cm::string_view keyword = instance.Keyword; + instance.Bind( + [result, keyword, &f](cm::string_view arg) -> Continue { + return f(*result, keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(std::size_t position, + cm::optional<std::string> Result::*member) + { + this->Base::Bind( + position, + [member](Instance& instance, std::size_t, cm::string_view arg) { + Result* result = static_cast<Result*>(instance.Result); + result->*member = arg; + }); + return *this; + } + + cmArgumentParser& BindParsedKeywords( + std::vector<cm::string_view> Result::*member) + { + this->Base::BindParsedKeyword( + [member](Instance& instance, cm::string_view arg) { + (static_cast<Result*>(instance.Result)->*member).emplace_back(arg); + }); return *this; } template <typename Range> - void Parse(Result& result, Range const& args, - std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + bool Parse(Result& result, Range const& args, + std::vector<std::string>* unparsedArguments, + std::size_t pos = 0) const { - ArgumentParser::Instance instance(this->Bindings); - for (cm::string_view arg : args) { - instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue, - parsedKeywords); - } + using ArgumentParser::AsParseResultPtr; + ParseResult* parseResultPtr = AsParseResultPtr(result); + Instance instance(this->Bindings, parseResultPtr, unparsedArguments, + &result); + instance.Parse(args, pos); + return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true; } template <typename Range> - Result Parse(Range const& args, - std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + Result Parse(Range const& args, std::vector<std::string>* unparsedArguments, + std::size_t pos = 0) const { Result result; - this->Parse(result, args, unparsedArguments, keywordsMissingValue, - parsedKeywords); + this->Parse(result, args, unparsedArguments, pos); return result; } - -private: - ArgumentParser::ActionMap Bindings; }; template <> -class cmArgumentParser<void> +class cmArgumentParser<void> : private ArgumentParser::Base { public: template <typename T> cmArgumentParser& Bind(cm::static_string_view name, T& ref) { - bool const inserted = this->Bind(cm::string_view(name), ref); - assert(inserted), (void)inserted; + this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); }); + return *this; + } + + cmArgumentParser& Bind(cm::static_string_view name, + std::function<Continue(cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind( + cm::static_string_view name, + std::function<Continue(cm::string_view, cm::string_view)> f, + ExpectAtLeast expect = { 1 }) + { + this->Base::Bind(name, [f, expect](Instance& instance) { + cm::string_view keyword = instance.Keyword; + instance.Bind( + [keyword, &f](cm::string_view arg) -> Continue { + return f(keyword, arg); + }, + expect); + }); + return *this; + } + + cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref) + { + this->Base::Bind(position, + [&ref](Instance&, std::size_t, cm::string_view arg) { + ref = std::string(arg); + }); + return *this; + } + + cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref) + { + this->Base::BindParsedKeyword( + [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); }); return *this; } template <typename Range> - void Parse(Range const& args, - std::vector<std::string>* unparsedArguments = nullptr, - std::vector<std::string>* keywordsMissingValue = nullptr, - std::vector<std::string>* parsedKeywords = nullptr) const + ParseResult Parse(Range const& args, + std::vector<std::string>* unparsedArguments, + std::size_t pos = 0) const { - ArgumentParser::Instance instance(this->Bindings); - for (cm::string_view arg : args) { - instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue, - parsedKeywords); - } + ParseResult parseResult; + Instance instance(this->Bindings, &parseResult, unparsedArguments); + instance.Parse(args, pos); + return parseResult; } protected: + using Base::Instance; + using Base::BindKeywordMissingValue; + template <typename T> bool Bind(cm::string_view name, T& ref) { - return this->Bindings - .Emplace(name, - [&ref](ArgumentParser::Instance& instance, void*) { - instance.Bind(ref); - }) - .second; + return this->MaybeBind(name, + [&ref](Instance& instance) { instance.Bind(ref); }); } - -private: - ArgumentParser::ActionMap Bindings; }; diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h new file mode 100644 index 0000000..7daae09 --- /dev/null +++ b/Source/cmArgumentParserTypes.h @@ -0,0 +1,69 @@ +/* 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 + +#if defined(__SUNPRO_CC) + +# include <string> +# include <vector> + +namespace ArgumentParser { + +template <typename T> +struct Maybe; +template <> +struct Maybe<std::string> : public std::string +{ + using std::string::basic_string; +}; + +template <typename T> +struct MaybeEmpty; +template <typename T> +struct MaybeEmpty<std::vector<T>> : public std::vector<T> +{ + using std::vector<T>::vector; +}; + +template <typename T> +struct NonEmpty; +template <typename T> +struct NonEmpty<std::vector<T>> : public std::vector<T> +{ + using std::vector<T>::vector; +}; +template <> +struct NonEmpty<std::string> : public std::string +{ + using std::string::basic_string; +}; + +} // namespace ArgumentParser + +#else + +namespace ArgumentParser { + +template <typename T> +struct Maybe : public T +{ + using T::T; +}; + +template <typename T> +struct MaybeEmpty : public T +{ + using T::T; +}; + +template <typename T> +struct NonEmpty : public T +{ + using T::T; +}; + +} // namespace ArgumentParser + +#endif diff --git a/Source/cmBlockCommand.cxx b/Source/cmBlockCommand.cxx new file mode 100644 index 0000000..ec79149 --- /dev/null +++ b/Source/cmBlockCommand.cxx @@ -0,0 +1,197 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmBlockCommand.h" + +#include <cstdint> // IWYU pragma: keep +#include <utility> + +#include <cm/memory> +#include <cm/optional> +#include <cm/string_view> +#include <cmext/enum_set> +#include <cmext/string_view> + +#include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" +#include "cmExecutionStatus.h" +#include "cmFunctionBlocker.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +namespace { +enum class ScopeType : std::uint8_t +{ + VARIABLES, + POLICIES +}; +using ScopeSet = cm::enum_set<ScopeType>; + +class BlockScopePushPop +{ +public: + BlockScopePushPop(cmMakefile* m, const ScopeSet& scopes); + ~BlockScopePushPop() = default; + + BlockScopePushPop(const BlockScopePushPop&) = delete; + BlockScopePushPop& operator=(const BlockScopePushPop&) = delete; + +private: + std::unique_ptr<cmMakefile::PolicyPushPop> PolicyScope; + std::unique_ptr<cmMakefile::VariablePushPop> VariableScope; +}; + +BlockScopePushPop::BlockScopePushPop(cmMakefile* mf, const ScopeSet& scopes) +{ + if (scopes.contains(ScopeType::POLICIES)) { + this->PolicyScope = cm::make_unique<cmMakefile::PolicyPushPop>(mf); + } + if (scopes.contains(ScopeType::VARIABLES)) { + this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf); + } +} + +class cmBlockFunctionBlocker : public cmFunctionBlocker +{ +public: + cmBlockFunctionBlocker(cmMakefile* mf, const ScopeSet& scopes, + std::vector<std::string> variableNames); + ~cmBlockFunctionBlocker() override; + + cm::string_view StartCommandName() const override { return "block"_s; } + cm::string_view EndCommandName() const override { return "endblock"_s; } + + bool EndCommandSupportsArguments() const override { return false; } + + bool ArgumentsMatch(cmListFileFunction const& lff, + cmMakefile& mf) const override; + + bool Replay(std::vector<cmListFileFunction> functions, + cmExecutionStatus& inStatus) override; + +private: + cmMakefile* Makefile; + ScopeSet Scopes; + BlockScopePushPop BlockScope; + std::vector<std::string> VariableNames; +}; + +cmBlockFunctionBlocker::cmBlockFunctionBlocker( + cmMakefile* const mf, const ScopeSet& scopes, + std::vector<std::string> variableNames) + : Makefile{ mf } + , Scopes{ scopes } + , BlockScope{ mf, scopes } + , VariableNames{ std::move(variableNames) } +{ +} + +cmBlockFunctionBlocker::~cmBlockFunctionBlocker() +{ + if (this->Scopes.contains(ScopeType::VARIABLES)) { + this->Makefile->RaiseScope(this->VariableNames); + } +} + +bool cmBlockFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff, + cmMakefile&) const +{ + // no arguments expected for endblock() + // but this method should not be called because EndCommandHasArguments() + // returns false. + return lff.Arguments().empty(); +} + +bool cmBlockFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, + cmExecutionStatus& inStatus) +{ + auto& mf = inStatus.GetMakefile(); + + // Invoke all the functions that were collected in the block. + for (cmListFileFunction const& fn : functions) { + cmExecutionStatus status(mf); + mf.ExecuteCommand(fn, status); + if (status.GetReturnInvoked()) { + mf.RaiseScope(status.GetReturnVariables()); + inStatus.SetReturnInvoked(status.GetReturnVariables()); + return true; + } + if (status.GetBreakInvoked()) { + inStatus.SetBreakInvoked(); + return true; + } + if (status.GetContinueInvoked()) { + inStatus.SetContinueInvoked(); + return true; + } + if (cmSystemTools::GetFatalErrorOccurred()) { + return true; + } + } + return true; +} + +} // anonymous namespace + +bool cmBlockCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + struct Arguments : public ArgumentParser::ParseResult + { + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> ScopeFor; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Propagate; + }; + static auto const parser = cmArgumentParser<Arguments>{} + .Bind("SCOPE_FOR"_s, &Arguments::ScopeFor) + .Bind("PROPAGATE"_s, &Arguments::Propagate); + std::vector<std::string> unrecognizedArguments; + auto parsedArgs = parser.Parse(args, &unrecognizedArguments); + + if (!unrecognizedArguments.empty()) { + status.SetError(cmStrCat("called with unsupported argument \"", + unrecognizedArguments[0], '"')); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + if (parsedArgs.MaybeReportError(status.GetMakefile())) { + cmSystemTools::SetFatalErrorOccurred(); + return true; + } + + ScopeSet scopes; + + if (parsedArgs.ScopeFor) { + for (auto const& scope : *parsedArgs.ScopeFor) { + if (scope == "VARIABLES"_s) { + scopes.insert(ScopeType::VARIABLES); + continue; + } + if (scope == "POLICIES"_s) { + scopes.insert(ScopeType::POLICIES); + continue; + } + status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"')); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + } else { + scopes = { ScopeType::VARIABLES, ScopeType::POLICIES }; + } + if (!scopes.contains(ScopeType::VARIABLES) && + !parsedArgs.Propagate.empty()) { + status.SetError( + "PROPAGATE cannot be specified without a new scope for VARIABLES"); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + + // create a function blocker + auto fb = cm::make_unique<cmBlockFunctionBlocker>( + &status.GetMakefile(), scopes, parsedArgs.Propagate); + status.GetMakefile().AddFunctionBlocker(std::move(fb)); + + return true; +} diff --git a/Source/cmBlockCommand.h b/Source/cmBlockCommand.h new file mode 100644 index 0000000..5fd8f42 --- /dev/null +++ b/Source/cmBlockCommand.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; + +/// Starts block() ... endblock() block +bool cmBlockCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx index 0750eea..58129a0 100644 --- a/Source/cmCMakeHostSystemInformationCommand.cxx +++ b/Source/cmCMakeHostSystemInformationCommand.cxx @@ -474,7 +474,7 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, } std::string const& key = *args.begin(); - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string ValueName; bool ValueNames = false; @@ -491,19 +491,15 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status, .Bind("SEPARATOR"_s, &Arguments::Separator) .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable); std::vector<std::string> invalidArgs; - std::vector<std::string> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args.advance(1), &invalidArgs, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args.advance(1), &invalidArgs); if (!invalidArgs.empty()) { status.SetError(cmStrCat("given invalid argument(s) \"", cmJoin(invalidArgs, ", "_s), "\".")); return false; } - if (!keywordsMissingValue.empty()) { - status.SetError(cmStrCat("missing expected value for argument(s) \"", - cmJoin(keywordsMissingValue, ", "_s), "\".")); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if ((!arguments.ValueName.empty() && (arguments.ValueNames || arguments.SubKeys)) || diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx index a2aaa2a..68e658c 100644 --- a/Source/cmCMakeLanguageCommand.cxx +++ b/Source/cmCMakeLanguageCommand.cxx @@ -14,15 +14,18 @@ #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmDependencyProvider.h" #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmListFileCache.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmRange.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmake.h" namespace { @@ -33,13 +36,14 @@ bool FatalError(cmExecutionStatus& status, std::string const& error) return false; } -std::array<cm::static_string_view, 12> InvalidCommands{ +std::array<cm::static_string_view, 14> InvalidCommands{ { // clang-format off "function"_s, "endfunction"_s, "macro"_s, "endmacro"_s, "if"_s, "elseif"_s, "else"_s, "endif"_s, "while"_s, "endwhile"_s, - "foreach"_s, "endforeach"_s + "foreach"_s, "endforeach"_s, + "block"_s, "endblock"_s } // clang-format on }; @@ -235,7 +239,7 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER( struct SetProviderArgs { std::string Command; - std::vector<std::string> Methods; + ArgumentParser::NonEmpty<std::vector<std::string>> Methods; }; auto const ArgsParser = @@ -303,6 +307,27 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER( return true; } + +bool cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL( + std::vector<cmListFileArgument> const& args, cmExecutionStatus& status) +{ + cmMakefile& makefile = status.GetMakefile(); + std::vector<std::string> expandedArgs; + makefile.ExpandArguments(args, expandedArgs); + + if (args.size() < 2 || expandedArgs.size() > 2) { + return FatalError( + status, + "sub-command GET_MESSAGE_LOG_LEVEL expects exactly one argument"); + } + + Message::LogLevel logLevel = makefile.GetCurrentLogLevel(); + std::string outputValue = cmake::LogLevelToString(logLevel); + + const std::string& outputVariable = expandedArgs[1]; + makefile.AddDefinition(outputVariable, outputValue); + return true; +} } bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, @@ -451,5 +476,9 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args, return cmCMakeLanguageCommandEVAL(args, status); } + if (expArgs[expArg] == "GET_MESSAGE_LOG_LEVEL") { + return cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL(args, status); + } + return FatalError(status, "called with unknown meta-operation"); } diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx index bf94c2d..7755082 100644 --- a/Source/cmCMakePathCommand.cxx +++ b/Source/cmCMakePathCommand.cxx @@ -2,7 +2,6 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCMakePathCommand.h" -#include <algorithm> #include <functional> #include <iomanip> #include <map> @@ -11,10 +10,12 @@ #include <utility> #include <vector> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmCMakePath.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" @@ -43,15 +44,12 @@ public: } 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 + Result Parse(std::vector<std::string> const& args) const { this->Inputs.clear(); return this->cmArgumentParser<Result>::Parse( - cmMakeRange(args).advance(Advance), &this->Inputs, keywordsMissingValue, - parsedKeywords); + cmMakeRange(args).advance(Advance), &this->Inputs); } const std::vector<std::string>& GetInputs() const { return this->Inputs; } @@ -82,52 +80,14 @@ public: template <int Advance = 2> Result Parse(std::vector<std::string> const& args) const { - this->KeywordsMissingValue.clear(); - this->ParsedKeywords.clear(); - return this->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; + args); } - -private: - mutable std::vector<std::string> KeywordsMissingValue; - mutable std::vector<std::string> ParsedKeywords; }; -struct OutputVariable +struct OutputVariable : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; }; // Usable when OUTPUT_VARIABLE is the only option class OutputVariableParser @@ -297,8 +257,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1])); @@ -307,7 +267,7 @@ bool HandleAppendCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -319,8 +279,8 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } std::string inputPath; @@ -334,7 +294,7 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -346,8 +306,8 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -364,7 +324,7 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args, path.RemoveFileName(); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -376,8 +336,8 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (parser.GetInputs().size() > 1) { @@ -395,7 +355,7 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, parser.GetInputs().empty() ? "" : parser.GetInputs().front()); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -403,9 +363,9 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args, bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; bool LastOnly = false; }; @@ -415,8 +375,8 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, Arguments const arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -438,7 +398,7 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -446,9 +406,9 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args, bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; bool LastOnly = false; }; @@ -458,8 +418,8 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, Arguments const arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (parser.GetInputs().size() > 1) { @@ -483,7 +443,7 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args, } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -495,8 +455,8 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args, const auto arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -512,7 +472,7 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args, auto path = cmCMakePath(inputPath).Normal(); status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } @@ -523,10 +483,10 @@ bool HandleTransformPathCommand( const std::string& base)>& transform, bool normalizeOption = false) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; - std::string BaseDirectory; + cm::optional<ArgumentParser::NonEmpty<std::string>> Output; + cm::optional<std::string> BaseDirectory; bool Normalize = false; }; @@ -538,8 +498,8 @@ bool HandleTransformPathCommand( Arguments arguments = parser.Parse(args); - if (!parser.checkOutputVariable(arguments, status)) { - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!parser.GetInputs().empty()) { @@ -547,17 +507,11 @@ bool HandleTransformPathCommand( 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 baseDirectory; + if (arguments.BaseDirectory) { + baseDirectory = *arguments.BaseDirectory; + } else { + baseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); } std::string inputPath; @@ -565,13 +519,13 @@ bool HandleTransformPathCommand( return false; } - auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory); + auto path = transform(cmCMakePath(inputPath), baseDirectory); if (arguments.Normalize) { path = path.Normal(); } status.GetMakefile().AddDefinition( - arguments.Output.empty() ? args[1] : arguments.Output, path.String()); + arguments.Output ? *arguments.Output : args[1], path.String()); return true; } diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx index b737c1f..7325e44 100644 --- a/Source/cmCMakePresetsGraph.cxx +++ b/Source/cmCMakePresetsGraph.cxx @@ -43,6 +43,10 @@ using ReadFileResult = cmCMakePresetsGraph::ReadFileResult; using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset; using BuildPreset = cmCMakePresetsGraph::BuildPreset; using TestPreset = cmCMakePresetsGraph::TestPreset; +using PackagePreset = cmCMakePresetsGraph::PackagePreset; +using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset; +template <typename T> +using PresetPair = cmCMakePresetsGraph::PresetPair<T>; using ExpandMacroResult = cmCMakePresetsGraphInternal::ExpandMacroResult; using MacroExpander = cmCMakePresetsGraphInternal::MacroExpander; @@ -261,6 +265,8 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, const TestPreset& preset, if (out->Output) { CHECK_EXPAND(out, out->Output->OutputLogFile, macroExpanders, graph.GetVersion(preset)); + CHECK_EXPAND(out, out->Output->OutputJUnitFile, macroExpanders, + graph.GetVersion(preset)); } if (out->Filter) { @@ -301,6 +307,36 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, const TestPreset& preset, return true; } +bool ExpandMacros(const cmCMakePresetsGraph& graph, + const PackagePreset& preset, + cm::optional<PackagePreset>& out, + const std::vector<MacroExpander>& macroExpanders) +{ + for (auto& variable : out->Variables) { + CHECK_EXPAND(out, variable.second, macroExpanders, + graph.GetVersion(preset)); + } + + CHECK_EXPAND(out, out->ConfigFile, macroExpanders, graph.GetVersion(preset)); + CHECK_EXPAND(out, out->PackageName, macroExpanders, + graph.GetVersion(preset)); + CHECK_EXPAND(out, out->PackageVersion, macroExpanders, + graph.GetVersion(preset)); + CHECK_EXPAND(out, out->PackageDirectory, macroExpanders, + graph.GetVersion(preset)); + CHECK_EXPAND(out, out->VendorName, macroExpanders, graph.GetVersion(preset)); + + return true; +} + +bool ExpandMacros(const cmCMakePresetsGraph& /*graph*/, + const WorkflowPreset& /*preset*/, + cm::optional<WorkflowPreset>& /*out*/, + const std::vector<MacroExpander>& /*macroExpanders*/) +{ + return true; +} + template <class T> bool ExpandMacros(const cmCMakePresetsGraph& graph, const T& preset, cm::optional<T>& out) @@ -556,6 +592,42 @@ ExpandMacroResult ExpandMacro(std::string& out, return ExpandMacroResult::Error; } + +template <typename T> +ReadFileResult SetupWorkflowConfigurePreset( + const T& preset, const ConfigurePreset*& configurePreset) +{ + if (preset.ConfigurePreset != configurePreset->Name) { + return ReadFileResult::INVALID_WORKFLOW_STEPS; + } + return ReadFileResult::READ_OK; +} + +template <> +ReadFileResult SetupWorkflowConfigurePreset<ConfigurePreset>( + const ConfigurePreset& preset, const ConfigurePreset*& configurePreset) +{ + configurePreset = &preset; + return ReadFileResult::READ_OK; +} + +template <typename T> +ReadFileResult TryReachPresetFromWorkflow( + const WorkflowPreset& origin, + const std::map<std::string, PresetPair<T>>& presets, const std::string& name, + const ConfigurePreset*& configurePreset) +{ + auto it = presets.find(name); + if (it == presets.end()) { + return ReadFileResult::INVALID_WORKFLOW_STEPS; + } + if (!origin.OriginFile->ReachableFiles.count( + it->second.Unexpanded.OriginFile)) { + return ReadFileResult::WORKFLOW_STEP_UNREACHABLE_FROM_FILE; + } + return SetupWorkflowConfigurePreset<T>(it->second.Unexpanded, + configurePreset); +} } bool cmCMakePresetsGraphInternal::EqualsCondition::Evaluate( @@ -781,6 +853,7 @@ cmCMakePresetsGraph::TestPreset::VisitPresetInherit( parentOutput.OutputOnFailure); InheritOptionalValue(output.Quiet, parentOutput.Quiet); InheritString(output.OutputLogFile, parentOutput.OutputLogFile); + InheritString(output.OutputJUnitFile, parentOutput.OutputJUnitFile); InheritOptionalValue(output.LabelSummary, parentOutput.LabelSummary); InheritOptionalValue(output.SubprojectSummary, parentOutput.SubprojectSummary); @@ -868,6 +941,57 @@ cmCMakePresetsGraph::TestPreset::VisitPresetAfterInherit(int /* version */) return ReadFileResult::READ_OK; } +cmCMakePresetsGraph::ReadFileResult +cmCMakePresetsGraph::PackagePreset::VisitPresetInherit( + const cmCMakePresetsGraph::Preset& parentPreset) +{ + auto& preset = *this; + const PackagePreset& parent = + static_cast<const PackagePreset&>(parentPreset); + + InheritString(preset.ConfigurePreset, parent.ConfigurePreset); + InheritOptionalValue(preset.InheritConfigureEnvironment, + parent.InheritConfigureEnvironment); + InheritVector(preset.Generators, parent.Generators); + InheritVector(preset.Configurations, parent.Configurations); + + for (auto const& v : parent.Variables) { + preset.Variables.insert(v); + } + + InheritOptionalValue(preset.DebugOutput, parent.DebugOutput); + InheritOptionalValue(preset.VerboseOutput, parent.VerboseOutput); + InheritString(preset.PackageName, parent.PackageName); + InheritString(preset.PackageVersion, parent.PackageVersion); + InheritString(preset.PackageDirectory, parent.PackageDirectory); + InheritString(preset.VendorName, parent.VendorName); + + return ReadFileResult::READ_OK; +} + +cmCMakePresetsGraph::ReadFileResult +cmCMakePresetsGraph::PackagePreset::VisitPresetAfterInherit(int /* version */) +{ + auto& preset = *this; + if (!preset.Hidden && preset.ConfigurePreset.empty()) { + return ReadFileResult::INVALID_PRESET; + } + return ReadFileResult::READ_OK; +} + +cmCMakePresetsGraph::ReadFileResult +cmCMakePresetsGraph::WorkflowPreset::VisitPresetInherit( + const cmCMakePresetsGraph::Preset& /*parentPreset*/) +{ + return ReadFileResult::READ_OK; +} + +cmCMakePresetsGraph::ReadFileResult +cmCMakePresetsGraph::WorkflowPreset::VisitPresetAfterInherit(int /* version */) +{ + return ReadFileResult::READ_OK; +} + std::string cmCMakePresetsGraph::GetFilename(const std::string& sourceDir) { return cmStrCat(sourceDir, "/CMakePresets.json"); @@ -901,8 +1025,9 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles) std::string filename = GetUserFilename(this->SourceDir); std::vector<File*> inProgressFiles; if (cmSystemTools::FileExists(filename)) { - auto result = this->ReadJSONFile(filename, RootType::User, - ReadReason::Root, inProgressFiles, file); + auto result = + this->ReadJSONFile(filename, RootType::User, ReadReason::Root, + inProgressFiles, file, this->errors); if (result != ReadFileResult::READ_OK) { return result; } @@ -910,8 +1035,9 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles) } else { filename = GetFilename(this->SourceDir); if (cmSystemTools::FileExists(filename)) { - auto result = this->ReadJSONFile( - filename, RootType::Project, ReadReason::Root, inProgressFiles, file); + auto result = + this->ReadJSONFile(filename, RootType::Project, ReadReason::Root, + inProgressFiles, file, this->errors); if (result != ReadFileResult::READ_OK) { return result; } @@ -928,6 +1054,8 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles) CHECK_OK(ComputePresetInheritance(this->ConfigurePresets, *this)); CHECK_OK(ComputePresetInheritance(this->BuildPresets, *this)); CHECK_OK(ComputePresetInheritance(this->TestPresets, *this)); + CHECK_OK(ComputePresetInheritance(this->PackagePresets, *this)); + CHECK_OK(ComputePresetInheritance(this->WorkflowPresets, *this)); for (auto& it : this->ConfigurePresets) { if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { @@ -983,6 +1111,79 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles) } } + for (auto& it : this->PackagePresets) { + if (!it.second.Unexpanded.Hidden) { + const auto configurePreset = + this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset); + if (configurePreset == this->ConfigurePresets.end()) { + return ReadFileResult::INVALID_CONFIGURE_PRESET; + } + if (!it.second.Unexpanded.OriginFile->ReachableFiles.count( + configurePreset->second.Unexpanded.OriginFile)) { + return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE; + } + + if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) { + it.second.Unexpanded.Environment.insert( + configurePreset->second.Unexpanded.Environment.begin(), + configurePreset->second.Unexpanded.Environment.end()); + } + } + + if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + return ReadFileResult::INVALID_MACRO_EXPANSION; + } + } + + for (auto& it : this->WorkflowPresets) { + using Type = WorkflowPreset::WorkflowStep::Type; + + const ConfigurePreset* configurePreset = nullptr; + for (auto const& step : it.second.Unexpanded.Steps) { + if (configurePreset == nullptr && step.PresetType != Type::Configure) { + return ReadFileResult::INVALID_WORKFLOW_STEPS; + } + if (configurePreset != nullptr && step.PresetType == Type::Configure) { + return ReadFileResult::INVALID_WORKFLOW_STEPS; + } + + ReadFileResult result; + switch (step.PresetType) { + case Type::Configure: + result = TryReachPresetFromWorkflow( + it.second.Unexpanded, this->ConfigurePresets, step.PresetName, + configurePreset); + break; + case Type::Build: + result = TryReachPresetFromWorkflow( + it.second.Unexpanded, this->BuildPresets, step.PresetName, + configurePreset); + break; + case Type::Test: + result = + TryReachPresetFromWorkflow(it.second.Unexpanded, this->TestPresets, + step.PresetName, configurePreset); + break; + case Type::Package: + result = TryReachPresetFromWorkflow( + it.second.Unexpanded, this->PackagePresets, step.PresetName, + configurePreset); + break; + } + if (result != ReadFileResult::READ_OK) { + return result; + } + } + + if (configurePreset == nullptr) { + return ReadFileResult::INVALID_WORKFLOW_STEPS; + } + + if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + return ReadFileResult::INVALID_MACRO_EXPANSION; + } + } + return ReadFileResult::READ_OK; } @@ -1026,6 +1227,10 @@ const char* cmCMakePresetsGraph::ResultToString(ReadFileResult result) case ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED: return "File version must be 2 or higher for build and test preset " "support."; + case ReadFileResult::PACKAGE_PRESETS_UNSUPPORTED: + return "File version must be 6 or higher for package preset support"; + case ReadFileResult::WORKFLOW_PRESETS_UNSUPPORTED: + return "File version must be 6 or higher for workflow preset support"; case ReadFileResult::INCLUDE_UNSUPPORTED: return "File version must be 4 or higher for include support"; case ReadFileResult::INVALID_INCLUDE: @@ -1047,6 +1252,12 @@ const char* cmCMakePresetsGraph::ResultToString(ReadFileResult result) case ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED: return "File version must be 5 or higher for testOutputTruncation " "preset support."; + case ReadFileResult::INVALID_WORKFLOW_STEPS: + return "Invalid workflow steps"; + case ReadFileResult::WORKFLOW_STEP_UNREACHABLE_FROM_FILE: + return "Workflow step is unreachable from preset's file"; + case ReadFileResult::CTEST_JUNIT_UNSUPPORTED: + return "File version must be 6 or higher for CTest JUnit output support"; } return "Unknown error"; @@ -1057,14 +1268,28 @@ void cmCMakePresetsGraph::ClearPresets() this->ConfigurePresets.clear(); this->BuildPresets.clear(); this->TestPresets.clear(); + this->PackagePresets.clear(); + this->WorkflowPresets.clear(); this->ConfigurePresetOrder.clear(); this->BuildPresetOrder.clear(); this->TestPresetOrder.clear(); + this->PackagePresetOrder.clear(); + this->WorkflowPresetOrder.clear(); this->Files.clear(); } +void cmCMakePresetsGraph::printPrecedingNewline(PrintPrecedingNewline* newline) +{ + if (newline) { + if (*newline == PrintPrecedingNewline::True) { + std::cout << std::endl; + } + *newline = PrintPrecedingNewline::True; + } +} + void cmCMakePresetsGraph::PrintPresets( const std::vector<const cmCMakePresetsGraph::Preset*>& presets) { @@ -1093,13 +1318,16 @@ void cmCMakePresetsGraph::PrintPresets( } } -void cmCMakePresetsGraph::PrintConfigurePresetList() const +void cmCMakePresetsGraph::PrintConfigurePresetList( + PrintPrecedingNewline* newline) const { - PrintConfigurePresetList([](const ConfigurePreset&) { return true; }); + PrintConfigurePresetList([](const ConfigurePreset&) { return true; }, + newline); } void cmCMakePresetsGraph::PrintConfigurePresetList( - const std::function<bool(const ConfigurePreset&)>& filter) const + const std::function<bool(const ConfigurePreset&)>& filter, + PrintPrecedingNewline* newline) const { std::vector<const cmCMakePresetsGraph::Preset*> presets; for (auto const& p : this->ConfigurePresetOrder) { @@ -1112,12 +1340,14 @@ void cmCMakePresetsGraph::PrintConfigurePresetList( } if (!presets.empty()) { + printPrecedingNewline(newline); std::cout << "Available configure presets:\n\n"; cmCMakePresetsGraph::PrintPresets(presets); } } -void cmCMakePresetsGraph::PrintBuildPresetList() const +void cmCMakePresetsGraph::PrintBuildPresetList( + PrintPrecedingNewline* newline) const { std::vector<const cmCMakePresetsGraph::Preset*> presets; for (auto const& p : this->BuildPresetOrder) { @@ -1130,12 +1360,14 @@ void cmCMakePresetsGraph::PrintBuildPresetList() const } if (!presets.empty()) { + printPrecedingNewline(newline); std::cout << "Available build presets:\n\n"; cmCMakePresetsGraph::PrintPresets(presets); } } -void cmCMakePresetsGraph::PrintTestPresetList() const +void cmCMakePresetsGraph::PrintTestPresetList( + PrintPrecedingNewline* newline) const { std::vector<const cmCMakePresetsGraph::Preset*> presets; for (auto const& p : this->TestPresetOrder) { @@ -1148,16 +1380,66 @@ void cmCMakePresetsGraph::PrintTestPresetList() const } if (!presets.empty()) { + printPrecedingNewline(newline); std::cout << "Available test presets:\n\n"; cmCMakePresetsGraph::PrintPresets(presets); } } +void cmCMakePresetsGraph::PrintPackagePresetList( + PrintPrecedingNewline* newline) const +{ + this->PrintPackagePresetList([](const PackagePreset&) { return true; }, + newline); +} + +void cmCMakePresetsGraph::PrintPackagePresetList( + const std::function<bool(const PackagePreset&)>& filter, + PrintPrecedingNewline* newline) const +{ + std::vector<const cmCMakePresetsGraph::Preset*> presets; + for (auto const& p : this->PackagePresetOrder) { + auto const& preset = this->PackagePresets.at(p); + if (!preset.Unexpanded.Hidden && preset.Expanded && + preset.Expanded->ConditionResult && filter(preset.Unexpanded)) { + presets.push_back( + static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded)); + } + } + + if (!presets.empty()) { + printPrecedingNewline(newline); + std::cout << "Available package presets:\n\n"; + cmCMakePresetsGraph::PrintPresets(presets); + } +} + +void cmCMakePresetsGraph::PrintWorkflowPresetList( + PrintPrecedingNewline* newline) const +{ + std::vector<const cmCMakePresetsGraph::Preset*> presets; + for (auto const& p : this->WorkflowPresetOrder) { + auto const& preset = this->WorkflowPresets.at(p); + if (!preset.Unexpanded.Hidden && preset.Expanded && + preset.Expanded->ConditionResult) { + presets.push_back( + static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded)); + } + } + + if (!presets.empty()) { + printPrecedingNewline(newline); + std::cout << "Available workflow presets:\n\n"; + cmCMakePresetsGraph::PrintPresets(presets); + } +} + void cmCMakePresetsGraph::PrintAllPresets() const { - this->PrintConfigurePresetList(); - std::cout << std::endl; - this->PrintBuildPresetList(); - std::cout << std::endl; - this->PrintTestPresetList(); + PrintPrecedingNewline newline = PrintPrecedingNewline::False; + this->PrintConfigurePresetList(&newline); + this->PrintBuildPresetList(&newline); + this->PrintTestPresetList(&newline); + this->PrintPackagePresetList(&newline); + this->PrintWorkflowPresetList(&newline); } diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h index f1f8662..17c902b 100644 --- a/Source/cmCMakePresetsGraph.h +++ b/Source/cmCMakePresetsGraph.h @@ -41,6 +41,8 @@ public: CONFIGURE_PRESET_UNREACHABLE_FROM_FILE, INVALID_MACRO_EXPANSION, BUILD_TEST_PRESETS_UNSUPPORTED, + PACKAGE_PRESETS_UNSUPPORTED, + WORKFLOW_PRESETS_UNSUPPORTED, INCLUDE_UNSUPPORTED, INVALID_INCLUDE, INVALID_CONFIGURE_PRESET, @@ -50,8 +52,12 @@ public: TOOLCHAIN_FILE_UNSUPPORTED, CYCLIC_INCLUDE, TEST_OUTPUT_TRUNCATION_UNSUPPORTED, + INVALID_WORKFLOW_STEPS, + WORKFLOW_STEP_UNREACHABLE_FROM_FILE, + CTEST_JUNIT_UNSUPPORTED, }; + std::string errors; enum class ArchToolsetStrategy { Set, @@ -95,7 +101,7 @@ public: std::string Name; std::vector<std::string> Inherits; - bool Hidden; + bool Hidden = false; File* OriginFile; std::string DisplayName; std::string Description; @@ -225,6 +231,7 @@ public: cm::optional<bool> OutputOnFailure; cm::optional<bool> Quiet; std::string OutputLogFile; + std::string OutputJUnitFile; cm::optional<bool> LabelSummary; cm::optional<bool> SubprojectSummary; cm::optional<int> MaxPassedTestOutputSize; @@ -325,6 +332,79 @@ public: ReadFileResult VisitPresetAfterInherit(int /* version */) override; }; + class PackagePreset : public Preset + { + public: + PackagePreset() = default; + PackagePreset(PackagePreset&& /*other*/) = default; + PackagePreset(const PackagePreset& /*other*/) = default; + PackagePreset& operator=(const PackagePreset& /*other*/) = default; + ~PackagePreset() override = default; +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) + PackagePreset& operator=(PackagePreset&& /*other*/) = default; +#else + // 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. + PackagePreset& operator=(PackagePreset&& /*other*/) = delete; +#endif + + std::string ConfigurePreset; + cm::optional<bool> InheritConfigureEnvironment; + std::vector<std::string> Generators; + std::vector<std::string> Configurations; + std::map<std::string, std::string> Variables; + std::string ConfigFile; + + cm::optional<bool> DebugOutput; + cm::optional<bool> VerboseOutput; + + std::string PackageName; + std::string PackageVersion; + std::string PackageDirectory; + std::string VendorName; + + ReadFileResult VisitPresetInherit(const Preset& parent) override; + ReadFileResult VisitPresetAfterInherit(int /* version */) override; + }; + + class WorkflowPreset : public Preset + { + public: + WorkflowPreset() = default; + WorkflowPreset(WorkflowPreset&& /*other*/) = default; + WorkflowPreset(const WorkflowPreset& /*other*/) = default; + WorkflowPreset& operator=(const WorkflowPreset& /*other*/) = default; + ~WorkflowPreset() override = default; +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) + WorkflowPreset& operator=(WorkflowPreset&& /*other*/) = default; +#else + // 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. + WorkflowPreset& operator=(WorkflowPreset&& /*other*/) = delete; +#endif + + class WorkflowStep + { + public: + enum class Type + { + Configure, + Build, + Test, + Package, + }; + Type PresetType; + std::string PresetName; + }; + + std::vector<WorkflowStep> Steps; + + ReadFileResult VisitPresetInherit(const Preset& parent) override; + ReadFileResult VisitPresetAfterInherit(int /* version */) override; + }; + template <class T> class PresetPair { @@ -336,10 +416,14 @@ public: std::map<std::string, PresetPair<ConfigurePreset>> ConfigurePresets; std::map<std::string, PresetPair<BuildPreset>> BuildPresets; std::map<std::string, PresetPair<TestPreset>> TestPresets; + std::map<std::string, PresetPair<PackagePreset>> PackagePresets; + std::map<std::string, PresetPair<WorkflowPreset>> WorkflowPresets; std::vector<std::string> ConfigurePresetOrder; std::vector<std::string> BuildPresetOrder; std::vector<std::string> TestPresetOrder; + std::vector<std::string> PackagePresetOrder; + std::vector<std::string> WorkflowPresetOrder; std::string SourceDir; std::vector<std::unique_ptr<File>> Files; @@ -382,13 +466,27 @@ public: return ""; } + enum class PrintPrecedingNewline + { + False, + True, + }; + static void printPrecedingNewline(PrintPrecedingNewline* p); + static void PrintPresets( const std::vector<const cmCMakePresetsGraph::Preset*>& presets); - void PrintConfigurePresetList() const; void PrintConfigurePresetList( - const std::function<bool(const ConfigurePreset&)>& filter) const; - void PrintBuildPresetList() const; - void PrintTestPresetList() const; + PrintPrecedingNewline* newline = nullptr) const; + void PrintConfigurePresetList( + const std::function<bool(const ConfigurePreset&)>& filter, + PrintPrecedingNewline* newline = nullptr) const; + void PrintBuildPresetList(PrintPrecedingNewline* newline = nullptr) const; + void PrintTestPresetList(PrintPrecedingNewline* newline = nullptr) const; + void PrintPackagePresetList(PrintPrecedingNewline* newline = nullptr) const; + void PrintPackagePresetList( + const std::function<bool(const PackagePreset&)>& filter, + PrintPrecedingNewline* newline = nullptr) const; + void PrintWorkflowPresetList(PrintPrecedingNewline* newline = nullptr) const; void PrintAllPresets() const; private: @@ -407,7 +505,7 @@ private: ReadFileResult ReadProjectPresetsInternal(bool allowNoFiles); ReadFileResult ReadJSONFile(const std::string& filename, RootType rootType, ReadReason readReason, - std::vector<File*>& inProgressFiles, - File*& file); + std::vector<File*>& inProgressFiles, File*& file, + std::string& errMsg); void ClearPresets(); }; diff --git a/Source/cmCMakePresetsGraphInternal.h b/Source/cmCMakePresetsGraphInternal.h index f7c7349..9e47a69 100644 --- a/Source/cmCMakePresetsGraphInternal.h +++ b/Source/cmCMakePresetsGraphInternal.h @@ -147,6 +147,14 @@ cmCMakePresetsGraph::ReadFileResult BuildPresetsHelper( cmCMakePresetsGraph::ReadFileResult TestPresetsHelper( std::vector<cmCMakePresetsGraph::TestPreset>& out, const Json::Value* value); +cmCMakePresetsGraph::ReadFileResult PackagePresetsHelper( + std::vector<cmCMakePresetsGraph::PackagePreset>& out, + const Json::Value* value); + +cmCMakePresetsGraph::ReadFileResult WorkflowPresetsHelper( + std::vector<cmCMakePresetsGraph::WorkflowPreset>& out, + const Json::Value* value); + cmJSONHelper<std::nullptr_t, cmCMakePresetsGraph::ReadFileResult> VendorHelper( cmCMakePresetsGraph::ReadFileResult error); diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx index d11e839..eec53c1 100644 --- a/Source/cmCMakePresetsGraphReadJSON.cxx +++ b/Source/cmCMakePresetsGraphReadJSON.cxx @@ -30,11 +30,13 @@ using CacheVariable = cmCMakePresetsGraph::CacheVariable; using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset; using BuildPreset = cmCMakePresetsGraph::BuildPreset; using TestPreset = cmCMakePresetsGraph::TestPreset; +using PackagePreset = cmCMakePresetsGraph::PackagePreset; +using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset; using ArchToolsetStrategy = cmCMakePresetsGraph::ArchToolsetStrategy; using JSONHelperBuilder = cmJSONHelperBuilder<ReadFileResult>; constexpr int MIN_VERSION = 1; -constexpr int MAX_VERSION = 5; +constexpr int MAX_VERSION = 6; struct CMakeVersion { @@ -46,9 +48,11 @@ struct CMakeVersion struct RootPresets { CMakeVersion CMakeMinimumRequired; - std::vector<cmCMakePresetsGraph::ConfigurePreset> ConfigurePresets; - std::vector<cmCMakePresetsGraph::BuildPreset> BuildPresets; - std::vector<cmCMakePresetsGraph::TestPreset> TestPresets; + std::vector<ConfigurePreset> ConfigurePresets; + std::vector<BuildPreset> BuildPresets; + std::vector<TestPreset> TestPresets; + std::vector<PackagePreset> PackagePresets; + std::vector<WorkflowPreset> WorkflowPresets; std::vector<std::string> Include; }; @@ -281,6 +285,10 @@ auto const RootPresetsHelper = cmCMakePresetsGraphInternal::BuildPresetsHelper, false) .Bind("testPresets"_s, &RootPresets::TestPresets, cmCMakePresetsGraphInternal::TestPresetsHelper, false) + .Bind("packagePresets"_s, &RootPresets::PackagePresets, + cmCMakePresetsGraphInternal::PackagePresetsHelper, false) + .Bind("workflowPresets"_s, &RootPresets::WorkflowPresets, + cmCMakePresetsGraphInternal::WorkflowPresetsHelper, false) .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired, CMakeVersionHelper, false) .Bind("include"_s, &RootPresets::Include, IncludeVectorHelper, false) @@ -411,7 +419,7 @@ cmCMakePresetsGraph::ReadFileResult EnvironmentMapHelper( cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( const std::string& filename, RootType rootType, ReadReason readReason, - std::vector<File*>& inProgressFiles, File*& file) + std::vector<File*>& inProgressFiles, File*& file, std::string& errMsg) { ReadFileResult result; @@ -430,6 +438,7 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( cmsys::ifstream fin(filename.c_str()); if (!fin) { + errMsg = cmStrCat(filename, ": Failed to read file\n", errMsg); return ReadFileResult::FILE_NOT_FOUND; } // If there's a BOM, toss it. @@ -438,7 +447,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( Json::Value root; Json::CharReaderBuilder builder; Json::CharReaderBuilder::strictMode(&builder.settings_); - if (!Json::parseFromStream(builder, fin, &root, nullptr)) { + if (!Json::parseFromStream(builder, fin, &root, &errMsg)) { + errMsg = cmStrCat(filename, ":\n", errMsg); return ReadFileResult::JSON_PARSE_ERROR; } @@ -456,6 +466,16 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( return ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED; } + // Support for package presets added in version 6. + if (v < 6 && root.isMember("packagePresets")) { + return ReadFileResult::PACKAGE_PRESETS_UNSUPPORTED; + } + + // Support for workflow presets added in version 6. + if (v < 6 && root.isMember("workflowPresets")) { + return ReadFileResult::WORKFLOW_PRESETS_UNSUPPORTED; + } + // Support for include added in version 4. if (v < 4 && root.isMember("include")) { return ReadFileResult::INCLUDE_UNSUPPORTED; @@ -490,6 +510,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( for (auto& preset : presets.ConfigurePresets) { preset.OriginFile = file; if (preset.Name.empty()) { + errMsg += R"(\n\t)"; + errMsg += filename; return ReadFileResult::INVALID_PRESET; } @@ -523,6 +545,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( for (auto& preset : presets.BuildPresets) { preset.OriginFile = file; if (preset.Name.empty()) { + errMsg += R"(\n\t)"; + errMsg += filename; return ReadFileResult::INVALID_PRESET; } @@ -564,17 +588,61 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( return ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED; } + // Support for outputJUnitFile added in version 6. + if (v < 6 && preset.Output && !preset.Output->OutputJUnitFile.empty()) { + return ReadFileResult::CTEST_JUNIT_UNSUPPORTED; + } + this->TestPresetOrder.push_back(preset.Name); } + for (auto& preset : presets.PackagePresets) { + preset.OriginFile = file; + if (preset.Name.empty()) { + return ReadFileResult::INVALID_PRESET; + } + + PresetPair<PackagePreset> presetPair; + presetPair.Unexpanded = preset; + presetPair.Expanded = cm::nullopt; + if (!this->PackagePresets.emplace(preset.Name, presetPair).second) { + return ReadFileResult::DUPLICATE_PRESETS; + } + + // Support for conditions added in version 3, but this requires version 5 + // already, so no action needed. + + this->PackagePresetOrder.push_back(preset.Name); + } + + for (auto& preset : presets.WorkflowPresets) { + preset.OriginFile = file; + if (preset.Name.empty()) { + return ReadFileResult::INVALID_PRESET; + } + + PresetPair<WorkflowPreset> presetPair; + presetPair.Unexpanded = preset; + presetPair.Expanded = cm::nullopt; + if (!this->WorkflowPresets.emplace(preset.Name, presetPair).second) { + return ReadFileResult::DUPLICATE_PRESETS; + } + + // Support for conditions added in version 3, but this requires version 6 + // already, so no action needed. + + this->WorkflowPresetOrder.push_back(preset.Name); + } + auto const includeFile = [this, &inProgressFiles, file]( const std::string& include, RootType rootType2, - ReadReason readReason2) -> ReadFileResult { + ReadReason readReason2, + std::string& FailureMessage) -> ReadFileResult { ReadFileResult r; File* includedFile; if ((r = this->ReadJSONFile(include, rootType2, readReason2, - inProgressFiles, includedFile)) != - ReadFileResult::READ_OK) { + inProgressFiles, includedFile, + FailureMessage)) != ReadFileResult::READ_OK) { return r; } @@ -589,8 +657,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( include = cmStrCat(directory, '/', include); } - if ((result = includeFile(include, rootType, ReadReason::Included)) != - ReadFileResult::READ_OK) { + if ((result = includeFile(include, rootType, ReadReason::Included, + errMsg)) != ReadFileResult::READ_OK) { return result; } } @@ -599,7 +667,7 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile( auto cmakePresetsFilename = GetFilename(this->SourceDir); if (cmSystemTools::FileExists(cmakePresetsFilename)) { if ((result = includeFile(cmakePresetsFilename, RootType::Project, - ReadReason::Root)) != + ReadReason::Root, errMsg)) != ReadFileResult::READ_OK) { return result; } diff --git a/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx b/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx new file mode 100644 index 0000000..4ae51b1 --- /dev/null +++ b/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx @@ -0,0 +1,95 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include <cstddef> +#include <functional> +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include <cm/optional> +#include <cmext/string_view> + +#include <cm3p/json/value.h> + +#include "cmCMakePresetsGraph.h" +#include "cmCMakePresetsGraphInternal.h" +#include "cmJSONHelpers.h" + +namespace { +using ReadFileResult = cmCMakePresetsGraph::ReadFileResult; +using PackagePreset = cmCMakePresetsGraph::PackagePreset; + +auto const OutputHelper = + cmJSONHelperBuilder<ReadFileResult>::Object<PackagePreset>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("debug"_s, &PackagePreset::DebugOutput, + cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false) + .Bind("verbose"_s, &PackagePreset::VerboseOutput, + cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false); + +auto const VariableHelper = cmJSONHelperBuilder<ReadFileResult>::String( + ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE); + +auto const VariablesHelper = + cmJSONHelperBuilder<ReadFileResult>::Map<std::string>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper); + +auto const PackagePresetHelper = + cmJSONHelperBuilder<ReadFileResult>::Object<PackagePreset>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("name"_s, &PackagePreset::Name, + cmCMakePresetsGraphInternal::PresetStringHelper) + .Bind("inherits"_s, &PackagePreset::Inherits, + cmCMakePresetsGraphInternal::PresetVectorOneOrMoreStringHelper, + false) + .Bind("hidden"_s, &PackagePreset::Hidden, + cmCMakePresetsGraphInternal::PresetBoolHelper, false) + .Bind<std::nullptr_t>("vendor"_s, nullptr, + cmCMakePresetsGraphInternal::VendorHelper( + ReadFileResult::INVALID_PRESET), + false) + .Bind("displayName"_s, &PackagePreset::DisplayName, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("description"_s, &PackagePreset::Description, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("environment"_s, &PackagePreset::Environment, + cmCMakePresetsGraphInternal::EnvironmentMapHelper, false) + .Bind("configurePreset"_s, &PackagePreset::ConfigurePreset, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("inheritConfigureEnvironment"_s, + &PackagePreset::InheritConfigureEnvironment, + cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false) + .Bind("generators"_s, &PackagePreset::Generators, + cmCMakePresetsGraphInternal::PresetVectorStringHelper, false) + .Bind("configurations"_s, &PackagePreset::Configurations, + cmCMakePresetsGraphInternal::PresetVectorStringHelper, false) + .Bind("variables"_s, &PackagePreset::Variables, VariablesHelper, false) + .Bind("configFile"_s, &PackagePreset::ConfigFile, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("output"_s, OutputHelper, false) + .Bind("packageName"_s, &PackagePreset::PackageName, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("packageVersion"_s, &PackagePreset::PackageVersion, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("packageDirectory"_s, &PackagePreset::PackageDirectory, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("vendorName"_s, &PackagePreset::VendorName, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("condition"_s, &PackagePreset::ConditionEvaluator, + cmCMakePresetsGraphInternal::PresetConditionHelper, false); +} + +namespace cmCMakePresetsGraphInternal { +cmCMakePresetsGraph::ReadFileResult PackagePresetsHelper( + std::vector<cmCMakePresetsGraph::PackagePreset>& out, + const Json::Value* value) +{ + static auto const helper = + cmJSONHelperBuilder<ReadFileResult>::Vector<PackagePreset>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, + PackagePresetHelper); + + return helper(out, value); +} +} diff --git a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx index c07d380..3856f63 100644 --- a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx +++ b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx @@ -104,6 +104,8 @@ auto const TestPresetOptionalOutputHelper = cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false) .Bind("outputLogFile"_s, &TestPreset::OutputOptions::OutputLogFile, cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("outputJUnitFile"_s, &TestPreset::OutputOptions::OutputJUnitFile, + cmCMakePresetsGraphInternal::PresetStringHelper, false) .Bind("labelSummary"_s, &TestPreset::OutputOptions::LabelSummary, cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false) .Bind("subprojectSummary"_s, diff --git a/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx b/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx new file mode 100644 index 0000000..33680a1 --- /dev/null +++ b/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx @@ -0,0 +1,95 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include <cstddef> +#include <functional> +#include <string> +#include <vector> + +#include <cmext/string_view> + +#include <cm3p/json/value.h> + +#include "cmCMakePresetsGraph.h" +#include "cmCMakePresetsGraphInternal.h" +#include "cmJSONHelpers.h" + +namespace { +using ReadFileResult = cmCMakePresetsGraph::ReadFileResult; +using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset; + +ReadFileResult WorkflowStepTypeHelper(WorkflowPreset::WorkflowStep::Type& out, + const Json::Value* value) +{ + if (!value) { + return ReadFileResult::INVALID_PRESET; + } + + if (!value->isString()) { + return ReadFileResult::INVALID_PRESET; + } + + if (value->asString() == "configure") { + out = WorkflowPreset::WorkflowStep::Type::Configure; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "build") { + out = WorkflowPreset::WorkflowStep::Type::Build; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "test") { + out = WorkflowPreset::WorkflowStep::Type::Test; + return ReadFileResult::READ_OK; + } + + if (value->asString() == "package") { + out = WorkflowPreset::WorkflowStep::Type::Package; + return ReadFileResult::READ_OK; + } + + return ReadFileResult::INVALID_PRESET; +} + +auto const WorkflowStepHelper = + cmJSONHelperBuilder<ReadFileResult>::Object<WorkflowPreset::WorkflowStep>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("type"_s, &WorkflowPreset::WorkflowStep::PresetType, + WorkflowStepTypeHelper) + .Bind("name"_s, &WorkflowPreset::WorkflowStep::PresetName, + cmCMakePresetsGraphInternal::PresetStringHelper); + +auto const WorkflowStepsHelper = + cmJSONHelperBuilder<ReadFileResult>::Vector<WorkflowPreset::WorkflowStep>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, + WorkflowStepHelper); + +auto const WorkflowPresetHelper = + cmJSONHelperBuilder<ReadFileResult>::Object<WorkflowPreset>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false) + .Bind("name"_s, &WorkflowPreset::Name, + cmCMakePresetsGraphInternal::PresetStringHelper) + .Bind<std::nullptr_t>("vendor"_s, nullptr, + cmCMakePresetsGraphInternal::VendorHelper( + ReadFileResult::INVALID_PRESET), + false) + .Bind("displayName"_s, &WorkflowPreset::DisplayName, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("description"_s, &WorkflowPreset::Description, + cmCMakePresetsGraphInternal::PresetStringHelper, false) + .Bind("steps"_s, &WorkflowPreset::Steps, WorkflowStepsHelper); +} + +namespace cmCMakePresetsGraphInternal { +cmCMakePresetsGraph::ReadFileResult WorkflowPresetsHelper( + std::vector<cmCMakePresetsGraph::WorkflowPreset>& out, + const Json::Value* value) +{ + static auto const helper = + cmJSONHelperBuilder<ReadFileResult>::Vector<WorkflowPreset>( + ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, + WorkflowPresetHelper); + + return helper(out, value); +} +} diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 66507a7..f60a1e9 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -2116,11 +2116,7 @@ bool cmCTest::HandleCommandLineArguments(size_t& i, return false; } i++; - this->Impl->TestHandler.SetJUnitXMLFileName(std::string(args[i])); - // Turn test output compression off. - // This makes it easier to include test output in the resulting - // JUnit XML report. - this->Impl->CompressTestOutput = false; + this->SetOutputJUnitFileName(std::string(args[i])); } cm::string_view noTestsPrefix = "--no-tests="; @@ -2458,6 +2454,9 @@ bool cmCTest::SetArgsFromPreset(const std::string& presetName, if (!expandedPreset->Output->OutputLogFile.empty()) { this->SetOutputLogFileName(expandedPreset->Output->OutputLogFile); } + if (!expandedPreset->Output->OutputJUnitFile.empty()) { + this->SetOutputJUnitFileName(expandedPreset->Output->OutputJUnitFile); + } this->Impl->LabelSummary = expandedPreset->Output->LabelSummary.value_or(true); @@ -3541,6 +3540,15 @@ void cmCTest::SetOutputLogFileName(const std::string& name) } } +void cmCTest::SetOutputJUnitFileName(const std::string& name) +{ + this->Impl->TestHandler.SetJUnitXMLFileName(name); + // Turn test output compression off. + // This makes it easier to include test output in the resulting + // JUnit XML report. + this->Impl->CompressTestOutput = false; +} + static const char* cmCTestStringLogType[] = { "DEBUG", "OUTPUT", "HANDLER_OUTPUT", diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 551c116..0017b3e 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -359,6 +359,9 @@ public: /** Set the output log file name */ void SetOutputLogFileName(const std::string& name); + /** Set the output JUnit file name */ + void SetOutputJUnitFileName(const std::string& name); + /** Set the visual studio or Xcode config type */ void SetConfigType(const std::string& ct); diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index c6296f9..27f2156 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -14,13 +14,13 @@ #include "cmAddLibraryCommand.h" #include "cmAddSubDirectoryCommand.h" #include "cmAddTestCommand.h" +#include "cmBlockCommand.h" #include "cmBreakCommand.h" #include "cmBuildCommand.h" #include "cmCMakeLanguageCommand.h" #include "cmCMakeMinimumRequired.h" #include "cmCMakePathCommand.h" #include "cmCMakePolicyCommand.h" -#include "cmCommand.h" #include "cmConfigureFileCommand.h" #include "cmContinueCommand.h" #include "cmCreateTestSourceList.h" @@ -127,6 +127,7 @@ void GetScriptingCommands(cmState* state) state->AddFlowControlCommand("macro", cmMacroCommand); state->AddFlowControlCommand("return", cmReturnCommand); state->AddFlowControlCommand("while", cmWhileCommand); + state->AddFlowControlCommand("block", cmBlockCommand); state->AddBuiltinCommand("cmake_language", cmCMakeLanguageCommand); state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired); @@ -199,6 +200,10 @@ void GetScriptingCommands(cmState* state) "An ENDWHILE command was found outside of a proper " "WHILE ENDWHILE structure. Or its arguments did not " "match the opening WHILE command."); + state->AddUnexpectedFlowControlCommand( + "endblock", + "An ENDBLOCK command was found outside of a proper " + "BLOCK ENDBLOCK structure."); #if !defined(CMAKE_BOOTSTRAP) state->AddBuiltinCommand("cmake_host_system_information", @@ -220,6 +225,8 @@ void GetScriptingCommands(cmState* state) void GetProjectCommands(cmState* state) { + state->AddBuiltinCommand("add_compile_definitions", + cmAddCompileDefinitionsCommand); state->AddBuiltinCommand("add_custom_command", cmAddCustomCommandCommand); state->AddBuiltinCommand("add_custom_target", cmAddCustomTargetCommand); state->AddBuiltinCommand("add_definitions", cmAddDefinitionsCommand); @@ -264,15 +271,12 @@ void GetProjectCommands(cmState* state) cmTargetLinkLibrariesCommand); state->AddBuiltinCommand("target_link_options", cmTargetLinkOptionsCommand); state->AddBuiltinCommand("target_sources", cmTargetSourcesCommand); - state->AddBuiltinCommand("try_compile", - cm::make_unique<cmTryCompileCommand>()); - state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>()); + state->AddBuiltinCommand("try_compile", cmTryCompileCommand); + state->AddBuiltinCommand("try_run", cmTryRunCommand); state->AddBuiltinCommand("target_precompile_headers", cmTargetPrecompileHeadersCommand); #if !defined(CMAKE_BOOTSTRAP) - state->AddBuiltinCommand("add_compile_definitions", - cmAddCompileDefinitionsCommand); state->AddBuiltinCommand("add_compile_options", cmAddCompileOptionsCommand); state->AddBuiltinCommand("aux_source_directory", cmAuxSourceDirectoryCommand); diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx index ba95168..5fe6756 100644 --- a/Source/cmCommonTargetGenerator.cxx +++ b/Source/cmCommonTargetGenerator.cxx @@ -9,6 +9,7 @@ #include "cmComputeLinkInformation.h" #include "cmGeneratorTarget.h" #include "cmGlobalCommonGenerator.h" +#include "cmGlobalGenerator.h" #include "cmLocalCommonGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" @@ -175,6 +176,9 @@ std::vector<std::string> cmCommonTargetGenerator::GetLinkedTargetDirectories( cmLocalGenerator* lg = linkee->GetLocalGenerator(); std::string di = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', lg->GetTargetDirectory(linkee)); + if (lg->GetGlobalGenerator()->IsMultiConfig()) { + di = cmStrCat(di, '/', config); + } dirs.push_back(std::move(di)); } } diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index cda70fc..6cfdf62 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -368,7 +368,7 @@ cmComputeLinkInformation::cmComputeLinkInformation( LibraryFeatureDescriptor{ "__CMAKE_LINK_EXECUTABLE", cmStrCat(this->LoaderFlag, "<LIBRARY>") }); } - // To link framewortk using a full path + // To link framework using a full path this->LibraryFeatureDescriptors.emplace( "__CMAKE_LINK_FRAMEWORK", LibraryFeatureDescriptor{ "__CMAKE_LINK_FRAMEWORK", "<LIBRARY>" }); @@ -1566,8 +1566,9 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry) if (target->IsFrameworkOnApple() && !this->GlobalGenerator->IsXcode()) { // Add the framework directory and the framework item itself - auto fwItems = this->GlobalGenerator->SplitFrameworkPath(item.Value, true); - if (!fwItems) { + auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath( + item.Value, cmGlobalGenerator::FrameworkFormat::Extended); + if (!fwDescriptor) { this->CMakeInstance->IssueMessage( MessageType::FATAL_ERROR, cmStrCat("Could not parse framework path \"", item.Value, @@ -1575,12 +1576,13 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry) item.Backtrace); return; } - if (!fwItems->first.empty()) { + if (!fwDescriptor->Directory.empty()) { // Add the directory portion to the framework search path. - this->AddFrameworkPath(fwItems->first); + this->AddFrameworkPath(fwDescriptor->Directory); } if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) { - this->Items.emplace_back(fwItems->second, ItemIsPath::Yes, target, + this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes, + target, this->FindLibraryFeature(entry.Feature)); } else { this->Items.emplace_back( @@ -1851,9 +1853,11 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) std::string const& item = entry.Item.Value; // Try to separate the framework name and path. - auto fwItems = - this->GlobalGenerator->SplitFrameworkPath(item, entry.Feature != DEFAULT); - if (!fwItems) { + auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath( + item, + entry.Feature == DEFAULT ? cmGlobalGenerator::FrameworkFormat::Relaxed + : cmGlobalGenerator::FrameworkFormat::Extended); + if (!fwDescriptor) { std::ostringstream e; e << "Could not parse framework path \"" << item << "\" " << "linked by target " << this->Target->GetName() << "."; @@ -1861,18 +1865,14 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) return; } - std::string fw_path = std::move(fwItems->first); - std::string fw = std::move(fwItems->second); - std::string full_fw = cmStrCat(fw, ".framework/", fw); - + const std::string& fw_path = fwDescriptor->Directory; if (!fw_path.empty()) { - full_fw = cmStrCat(fw_path, '/', full_fw); // Add the directory portion to the framework search path. this->AddFrameworkPath(fw_path); } // add runtime information - this->AddLibraryRuntimeInfo(full_fw); + this->AddLibraryRuntimeInfo(fwDescriptor->GetFullPath()); if (entry.Feature == DEFAULT) { // ensure FRAMEWORK feature is loaded @@ -1887,9 +1887,9 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry) ? "FRAMEWORK" : entry.Feature)); } else { - this->Items.emplace_back(fw, ItemIsPath::Yes, nullptr, - this->FindLibraryFeature(entry.Feature == DEFAULT - ? "FRAMEWORK" + this->Items.emplace_back( + fwDescriptor->GetLinkName(), ItemIsPath::Yes, nullptr, + this->FindLibraryFeature(entry.Feature == DEFAULT ? "FRAMEWORK" : entry.Feature)); } } @@ -2252,15 +2252,11 @@ void cmComputeLinkInformation::AddLibraryRuntimeInfo( // It could be an Apple framework if (!is_shared_library) { - if (fullPath.find(".framework") != std::string::npos) { - static cmsys::RegularExpression splitFramework( - "^(.*)/(.*).framework/(.*)$"); - if (splitFramework.find(fullPath) && - (std::string::npos != - splitFramework.match(3).find(splitFramework.match(2)))) { - is_shared_library = true; - } - } + is_shared_library = + this->GlobalGenerator + ->SplitFrameworkPath(fullPath, + cmGlobalGenerator::FrameworkFormat::Strict) + .has_value(); } if (!is_shared_library) { diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in index 6a419f6..90f3de0 100644 --- a/Source/cmConfigure.cmake.h.in +++ b/Source/cmConfigure.cmake.h.in @@ -14,10 +14,15 @@ #pragma warning(disable : 1572) /* floating-point equality test */ #endif +#if defined(__LCC__) && defined(__EDG__) && (__LCC__ == 123) +#pragma diag_suppress 2910 /* excess -Wunused-function in 1.23.x */ +#endif + #cmakedefine HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE #cmakedefine HAVE_UNSETENV #cmakedefine CMake_USE_MACH_PARSER #cmakedefine CMake_USE_XCOFF_PARSER +#cmakedefine CMAKE_USE_WMAKE #define CMake_DEFAULT_RECURSION_LIMIT @CMake_DEFAULT_RECURSION_LIMIT@ #define CMAKE_BIN_DIR "/@CMAKE_BIN_DIR@" #define CMAKE_DATA_DIR "/@CMAKE_DATA_DIR@" @@ -28,3 +33,11 @@ #if defined(_WIN32) && !defined(NOMINMAX) # define NOMINMAX #endif + +#cmakedefine CURL_CA_BUNDLE "@CURL_CA_BUNDLE@" +#cmakedefine CURL_CA_PATH "@CURL_CA_PATH@" + +#cmakedefine01 CMake_STAT_HAS_ST_MTIM +#cmakedefine01 CMake_STAT_HAS_ST_MTIMESPEC + +#cmakedefine KWSYS_ENCODING_DEFAULT_CODEPAGE @KWSYS_ENCODING_DEFAULT_CODEPAGE@ diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx index b1d37a6..b44111d 100644 --- a/Source/cmCoreTryCompile.cxx +++ b/Source/cmCoreTryCompile.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCoreTryCompile.h" +#include <array> #include <cstdio> #include <cstring> #include <set> @@ -12,13 +13,16 @@ #include <cmext/string_view> #include "cmsys/Directory.hxx" +#include "cmsys/FStream.hxx" +#include "cmArgumentParser.h" #include "cmExportTryCompileFileGenerator.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" +#include "cmRange.h" #include "cmState.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -28,142 +32,7 @@ #include "cmake.h" namespace { -class LanguageStandardState -{ -public: - LanguageStandardState(std::string&& lang) - : 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.emplace_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 = false; - bool DidStandard = false; - bool DidStandardRequired = false; - bool DidExtensions = false; - - std::string StandardFlag; - std::string RequiredFlag; - std::string ExtensionFlag; - - std::string StandardValue; - std::string RequiredValue; - std::string ExtensionValue; -}; - +constexpr const char* unique_binary_directory = "CMAKE_BINARY_DIR_USE_MKDTEMP"; constexpr size_t lang_property_start = 0; constexpr size_t lang_property_size = 4; constexpr size_t pie_property_start = 4; @@ -226,6 +95,8 @@ std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES = std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED"; std::string const kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT = "CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT"; +std::string const kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT = + "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT"; /* GHS Multi platform variables */ std::set<std::string> const ghs_platform_vars{ @@ -233,114 +104,251 @@ std::set<std::string> const ghs_platform_vars{ "GHS_OS_ROOT", "GHS_OS_DIR", "GHS_BSP_NAME", "GHS_OS_DIR_OPTION" }; +using Arguments = cmCoreTryCompile::Arguments; + +ArgumentParser::Continue TryCompileLangProp(Arguments& args, + cm::string_view key, + cm::string_view val) +{ + args.LangProps[std::string(key)] = std::string(val); + return ArgumentParser::Continue::No; +} + +ArgumentParser::Continue TryCompileCompileDefs(Arguments& args, + cm::string_view val) +{ + cmExpandList(val, args.CompileDefs); + return ArgumentParser::Continue::Yes; } -int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, - bool isTryRun) +cmArgumentParser<Arguments> makeTryCompileParser( + const cmArgumentParser<Arguments>& base) +{ + return cmArgumentParser<Arguments>{ base }.Bind("OUTPUT_VARIABLE"_s, + &Arguments::OutputVariable); +} + +cmArgumentParser<Arguments> makeTryRunParser( + const cmArgumentParser<Arguments>& base) +{ + return cmArgumentParser<Arguments>{ base } + .Bind("COMPILE_OUTPUT_VARIABLE"_s, &Arguments::CompileOutputVariable) + .Bind("RUN_OUTPUT_VARIABLE"_s, &Arguments::RunOutputVariable) + .Bind("RUN_OUTPUT_STDOUT_VARIABLE"_s, &Arguments::RunOutputStdOutVariable) + .Bind("RUN_OUTPUT_STDERR_VARIABLE"_s, &Arguments::RunOutputStdErrVariable) + .Bind("WORKING_DIRECTORY"_s, &Arguments::RunWorkingDirectory) + .Bind("ARGS"_s, &Arguments::RunArgs) + /* keep semicolon on own line */; +} + +#define BIND_LANG_PROPS(lang) \ + Bind(#lang "_STANDARD"_s, TryCompileLangProp) \ + .Bind(#lang "_STANDARD_REQUIRED"_s, TryCompileLangProp) \ + .Bind(#lang "_EXTENSIONS"_s, TryCompileLangProp) + +auto const TryCompileBaseArgParser = + cmArgumentParser<Arguments>{} + .Bind(0, &Arguments::CompileResultVariable) + .Bind("NO_CACHE"_s, &Arguments::NoCache) + .Bind("CMAKE_FLAGS"_s, &Arguments::CMakeFlags) + .Bind("__CMAKE_INTERNAL"_s, &Arguments::CMakeInternal) + /* keep semicolon on own line */; + +auto const TryCompileBaseSourcesArgParser = + cmArgumentParser<Arguments>{ TryCompileBaseArgParser } + .Bind("SOURCES"_s, &Arguments::Sources) + .Bind("COMPILE_DEFINITIONS"_s, TryCompileCompileDefs, + ArgumentParser::ExpectAtLeast{ 0 }) + .Bind("LINK_LIBRARIES"_s, &Arguments::LinkLibraries) + .Bind("LINK_OPTIONS"_s, &Arguments::LinkOptions) + .Bind("COPY_FILE"_s, &Arguments::CopyFileTo) + .Bind("COPY_FILE_ERROR"_s, &Arguments::CopyFileError) + .BIND_LANG_PROPS(C) + .BIND_LANG_PROPS(CUDA) + .BIND_LANG_PROPS(CXX) + .BIND_LANG_PROPS(HIP) + .BIND_LANG_PROPS(OBJC) + .BIND_LANG_PROPS(OBJCXX) + /* keep semicolon on own line */; + +auto const TryCompileBaseNewSourcesArgParser = + cmArgumentParser<Arguments>{ TryCompileBaseSourcesArgParser } + .Bind("SOURCE_FROM_CONTENT"_s, &Arguments::SourceFromContent) + .Bind("SOURCE_FROM_VAR"_s, &Arguments::SourceFromVar) + .Bind("SOURCE_FROM_FILE"_s, &Arguments::SourceFromFile) + /* keep semicolon on own line */; + +auto const TryCompileBaseProjectArgParser = + cmArgumentParser<Arguments>{ TryCompileBaseArgParser } + .Bind("PROJECT"_s, &Arguments::ProjectName) + .Bind("SOURCE_DIR"_s, &Arguments::SourceDirectoryOrFile) + .Bind("BINARY_DIR"_s, &Arguments::BinaryDirectory) + .Bind("TARGET"_s, &Arguments::TargetName) + /* keep semicolon on own line */; + +auto const TryCompileProjectArgParser = + makeTryCompileParser(TryCompileBaseProjectArgParser); + +auto const TryCompileSourcesArgParser = + makeTryCompileParser(TryCompileBaseNewSourcesArgParser); + +auto const TryCompileOldArgParser = + makeTryCompileParser(TryCompileBaseSourcesArgParser) + .Bind(1, &Arguments::BinaryDirectory) + .Bind(2, &Arguments::SourceDirectoryOrFile) + .Bind(3, &Arguments::ProjectName) + .Bind(4, &Arguments::TargetName) + /* keep semicolon on own line */; + +auto const TryRunSourcesArgParser = + makeTryRunParser(TryCompileBaseNewSourcesArgParser); + +auto const TryRunOldArgParser = makeTryRunParser(TryCompileOldArgParser); + +#undef BIND_LANG_PROPS + +std::string const TryCompileDefaultConfig = "DEBUG"; +} + +Arguments cmCoreTryCompile::ParseArgs( + const cmRange<std::vector<std::string>::const_iterator>& args, + const cmArgumentParser<Arguments>& parser, + std::vector<std::string>& unparsedArguments) +{ + auto arguments = parser.Parse(args, &unparsedArguments, 0); + if (!arguments.MaybeReportError(*(this->Makefile)) && + !unparsedArguments.empty()) { + std::string m = "Unknown arguments:"; + for (const auto& i : unparsedArguments) { + m = cmStrCat(m, "\n \"", i, "\""); + } + this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m); + } + return arguments; +} + +Arguments cmCoreTryCompile::ParseArgs( + cmRange<std::vector<std::string>::const_iterator> args, bool isTryRun) +{ + std::vector<std::string> unparsedArguments; + const auto& second = *(++args.begin()); + + if (!isTryRun && second == "PROJECT") { + // New PROJECT signature (try_compile only). + auto arguments = + this->ParseArgs(args, TryCompileProjectArgParser, unparsedArguments); + if (!arguments.BinaryDirectory) { + arguments.BinaryDirectory = unique_binary_directory; + } + return arguments; + } + + if (cmHasLiteralPrefix(second, "SOURCE")) { + // New SOURCES signature. + auto arguments = this->ParseArgs( + args, isTryRun ? TryRunSourcesArgParser : TryCompileSourcesArgParser, + unparsedArguments); + arguments.BinaryDirectory = unique_binary_directory; + return arguments; + } + + // Old signature. + auto arguments = this->ParseArgs( + args, isTryRun ? TryRunOldArgParser : TryCompileOldArgParser, + unparsedArguments); + // For historical reasons, treat some empty-valued keyword + // arguments as if they were not specified at all. + if (arguments.OutputVariable && arguments.OutputVariable->empty()) { + arguments.OutputVariable = cm::nullopt; + } + if (isTryRun) { + if (arguments.CompileOutputVariable && + arguments.CompileOutputVariable->empty()) { + arguments.CompileOutputVariable = cm::nullopt; + } + if (arguments.RunOutputVariable && arguments.RunOutputVariable->empty()) { + arguments.RunOutputVariable = cm::nullopt; + } + if (arguments.RunOutputStdOutVariable && + arguments.RunOutputStdOutVariable->empty()) { + arguments.RunOutputStdOutVariable = cm::nullopt; + } + if (arguments.RunOutputStdErrVariable && + arguments.RunOutputStdErrVariable->empty()) { + arguments.RunOutputStdErrVariable = cm::nullopt; + } + if (arguments.RunWorkingDirectory && + arguments.RunWorkingDirectory->empty()) { + arguments.RunWorkingDirectory = cm::nullopt; + } + } + return arguments; +} + +bool cmCoreTryCompile::TryCompileCode(Arguments& arguments, + cmStateEnums::TargetType targetType) { - this->BinaryDirectory = argv[1]; this->OutputFile.clear(); // which signature were we called with ? this->SrcFileSignature = true; - cmStateEnums::TargetType targetType = cmStateEnums::EXECUTABLE; - cmValue 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 == - cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY)) { - targetType = cmStateEnums::STATIC_LIBRARY; - } else { + bool useUniqueBinaryDirectory = false; + std::string sourceDirectory; + std::string projectName; + std::string targetName; + if (arguments.ProjectName) { + this->SrcFileSignature = false; + if (!arguments.SourceDirectoryOrFile || + arguments.SourceDirectoryOrFile->empty()) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "No <srcdir> specified."); + return false; + } + sourceDirectory = *arguments.SourceDirectoryOrFile; + projectName = *arguments.ProjectName; + if (arguments.TargetName) { + targetName = *arguments.TargetName; + } + } else { + projectName = "CMAKE_TRY_COMPILE"; + /* Use a random file name to avoid rapid creation and deletion + of the same executable name (some filesystems fail on that). */ + char targetNameBuf[64]; + snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", + cmSystemTools::RandomSeed() & 0xFFFFF); + targetName = targetNameBuf; + } + + if (!arguments.BinaryDirectory || arguments.BinaryDirectory->empty()) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "No <bindir> specified."); + return false; + } + if (*arguments.BinaryDirectory == unique_binary_directory) { + // leave empty until we're ready to create it, so we don't try to remove + // a non-existing directory if we abort due to e.g. bad arguments + this->BinaryDirectory.clear(); + useUniqueBinaryDirectory = true; + } else { + if (!cmSystemTools::FileIsFullPath(*arguments.BinaryDirectory)) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - cmStrCat("Invalid value '", *tt, - "' for CMAKE_TRY_COMPILE_TARGET_TYPE. Only '", - cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE), - "' and '", - cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY), - "' are allowed.")); - return -1; + cmStrCat("<bindir> is not an absolute path:\n '", + *arguments.BinaryDirectory, "'")); + return false; + } + this->BinaryDirectory = *arguments.BinaryDirectory; + // compute the binary dir when TRY_COMPILE is called with a src file + // signature + if (this->SrcFileSignature) { + this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; } } - std::string sourceDirectory = argv[2]; - std::string projectName; - std::string targetName; - std::vector<std::string> cmakeFlags(1, "CMAKE_FLAGS"); // fake argv[0] - std::vector<std::string> compileDefs; - std::string cmakeInternal; - std::string outputVariable; - std::string copyFile; - std::string copyFileError; - LanguageStandardState cState("C"); - LanguageStandardState cudaState("CUDA"); - LanguageStandardState cxxState("CXX"); - LanguageStandardState hipState("HIP"); - LanguageStandardState objcState("OBJC"); - LanguageStandardState objcxxState("OBJCXX"); std::vector<std::string> targets; - std::vector<std::string> linkOptions; - std::string libsToLink = " "; - bool useOldLinkLibs = true; - char targetNameBuf[64]; - bool didOutputVariable = false; - bool didCopyFile = false; - bool didCopyFileError = false; - bool useSources = argv[2] == "SOURCES"; - std::vector<std::string> sources; - - enum Doing - { - DoingNone, - DoingCMakeFlags, - DoingCompileDefinitions, - DoingLinkOptions, - DoingLinkLibraries, - DoingOutputVariable, - DoingCopyFile, - DoingCopyFileError, - DoingSources, - DoingCMakeInternal - }; - Doing doing = useSources ? DoingSources : DoingNone; - for (size_t i = 3; i < argv.size(); ++i) { - if (argv[i] == "CMAKE_FLAGS") { - doing = DoingCMakeFlags; - } else if (argv[i] == "COMPILE_DEFINITIONS") { - doing = DoingCompileDefinitions; - } else if (argv[i] == "LINK_OPTIONS") { - doing = DoingLinkOptions; - } else if (argv[i] == "LINK_LIBRARIES") { - doing = DoingLinkLibraries; - useOldLinkLibs = false; - } else if (argv[i] == "OUTPUT_VARIABLE") { - doing = DoingOutputVariable; - didOutputVariable = true; - } else if (argv[i] == "COPY_FILE") { - doing = DoingCopyFile; - didCopyFile = true; - } else if (argv[i] == "COPY_FILE_ERROR") { - doing = DoingCopyFileError; - didCopyFileError = true; - } else if (cState.UpdateIfMatches(argv, i) || - cxxState.UpdateIfMatches(argv, i) || - cudaState.UpdateIfMatches(argv, i) || - hipState.UpdateIfMatches(argv, i) || - objcState.UpdateIfMatches(argv, i) || - objcxxState.UpdateIfMatches(argv, i)) { - continue; - } else if (argv[i] == "__CMAKE_INTERNAL") { - doing = DoingCMakeInternal; - } else if (doing == DoingCMakeFlags) { - cmakeFlags.emplace_back(argv[i]); - } else if (doing == DoingCompileDefinitions) { - cmExpandList(argv[i], compileDefs); - } else if (doing == DoingLinkOptions) { - linkOptions.emplace_back(argv[i]); - } else if (doing == DoingLinkLibraries) { - libsToLink += "\"" + cmTrimWhitespace(argv[i]) + "\" "; - if (cmTarget* tgt = this->Makefile->FindTargetToUse(argv[i])) { + if (arguments.LinkLibraries) { + for (std::string const& i : *arguments.LinkLibraries) { + if (cmTarget* tgt = this->Makefile->FindTargetToUse(i)) { switch (tgt->GetType()) { case cmStateEnums::SHARED_LIBRARY: case cmStateEnums::STATIC_LIBRARY: @@ -359,114 +367,94 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, "IMPORTED LINK_LIBRARIES. Got ", tgt->GetName(), " of type ", cmState::GetTargetTypeName(tgt->GetType()), ".")); - return -1; + return false; } if (tgt->IsImported()) { - targets.emplace_back(argv[i]); + targets.emplace_back(i); } } - } else if (doing == DoingOutputVariable) { - outputVariable = argv[i]; - doing = DoingNone; - } else if (doing == DoingCopyFile) { - copyFile = argv[i]; - doing = DoingNone; - } else if (doing == DoingCopyFileError) { - copyFileError = argv[i]; - doing = DoingNone; - } else if (doing == DoingSources) { - sources.emplace_back(argv[i]); - } else if (doing == DoingCMakeInternal) { - cmakeInternal = argv[i]; - doing = DoingNone; - } else if (i == 3) { - this->SrcFileSignature = false; - projectName = argv[i]; - } else if (i == 4 && !this->SrcFileSignature) { - targetName = argv[i]; - } else { - std::ostringstream m; - m << "try_compile given unknown argument \"" << argv[i] << "\"."; - this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m.str()); } } - if (didCopyFile && copyFile.empty()) { + if (arguments.CopyFileTo && arguments.CopyFileTo->empty()) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "COPY_FILE must be followed by a file path"); - return -1; + return false; } - if (didCopyFileError && copyFileError.empty()) { + if (arguments.CopyFileError && arguments.CopyFileError->empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE_ERROR must be followed by a variable name"); - return -1; + return false; } - if (didCopyFileError && !didCopyFile) { + if (arguments.CopyFileError && !arguments.CopyFileTo) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "COPY_FILE_ERROR may be used only with COPY_FILE"); - return -1; + return false; } - if (didOutputVariable && outputVariable.empty()) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "OUTPUT_VARIABLE must be followed by a variable name"); - return -1; - } - - if (useSources && sources.empty()) { + if (arguments.Sources && arguments.Sources->empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, "SOURCES must be followed by at least one source file"); - return -1; + return false; } - if (!this->SrcFileSignature) { - if (!cState.Validate(this->Makefile)) { - return -1; - } - if (!cudaState.Validate(this->Makefile)) { - return -1; - } - if (!hipState.Validate(this->Makefile)) { - return -1; - } - if (!cxxState.Validate(this->Makefile)) { - return -1; + if (this->SrcFileSignature) { + if (arguments.SourceFromContent && + arguments.SourceFromContent->size() % 2) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + "SOURCE_FROM_CONTENT requires exactly two arguments"); + return false; } - if (!objcState.Validate(this->Makefile)) { - return -1; + if (arguments.SourceFromVar && arguments.SourceFromVar->size() % 2) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + "SOURCE_FROM_VAR requires exactly two arguments"); + return false; } - if (!objcxxState.Validate(this->Makefile)) { - return -1; + if (arguments.SourceFromFile && arguments.SourceFromFile->size() % 2) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + "SOURCE_FROM_FILE requires exactly two arguments"); + return false; } - } - - // compute the binary dir when TRY_COMPILE is called with a src file - // signature - if (this->SrcFileSignature) { - this->BinaryDirectory += "/CMakeFiles/CMakeTmp"; } else { // only valid for srcfile signatures - if (!compileDefs.empty()) { + if (!arguments.LangProps.empty()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat(arguments.LangProps.begin()->first, + " allowed only in source file signature")); + return false; + } + if (!arguments.CompileDefs.empty()) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - "COMPILE_DEFINITIONS specified on a srcdir type TRY_COMPILE"); - return -1; + "COMPILE_DEFINITIONS allowed only in source file signature"); + return false; } - if (!copyFile.empty()) { + if (arguments.CopyFileTo) { this->Makefile->IssueMessage( MessageType::FATAL_ERROR, - "COPY_FILE specified on a srcdir type TRY_COMPILE"); - return -1; + "COPY_FILE allowed only in source file signature"); + return false; } } + // make sure the binary directory exists - cmSystemTools::MakeDirectory(this->BinaryDirectory); + if (useUniqueBinaryDirectory) { + this->BinaryDirectory = + cmStrCat(this->Makefile->GetHomeOutputDirectory(), + "/CMakeFiles/CMakeScratch/TryCompile-XXXXXX"); + cmSystemTools::MakeTempDirectory(this->BinaryDirectory); + } else { + cmSystemTools::MakeDirectory(this->BinaryDirectory); + } // do not allow recursive try Compiles if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory()) { @@ -474,7 +462,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, e << "Attempt at a recursive or nested TRY_COMPILE in directory\n" << " " << this->BinaryDirectory << "\n"; this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return -1; + return false; } std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt"; @@ -485,9 +473,63 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, cmSystemTools::RemoveFile(ccFile); // Choose sources. - if (!useSources) { - sources.emplace_back(argv[2]); + std::vector<std::string> sources; + if (arguments.Sources) { + sources = std::move(*arguments.Sources); + } else if (arguments.SourceDirectoryOrFile) { + sources.emplace_back(*arguments.SourceDirectoryOrFile); } + if (arguments.SourceFromContent) { + auto const k = arguments.SourceFromContent->size(); + for (auto i = decltype(k){ 0 }; i < k; i += 2) { + const auto& name = (*arguments.SourceFromContent)[i + 0]; + const auto& content = (*arguments.SourceFromContent)[i + 1]; + auto out = this->WriteSource(name, content, "SOURCE_FROM_CONTENT"); + if (out.empty()) { + return false; + } + sources.emplace_back(std::move(out)); + } + } + if (arguments.SourceFromVar) { + auto const k = arguments.SourceFromVar->size(); + for (auto i = decltype(k){ 0 }; i < k; i += 2) { + const auto& name = (*arguments.SourceFromVar)[i + 0]; + const auto& var = (*arguments.SourceFromVar)[i + 1]; + const auto& content = this->Makefile->GetDefinition(var); + auto out = this->WriteSource(name, content, "SOURCE_FROM_VAR"); + if (out.empty()) { + return false; + } + sources.emplace_back(std::move(out)); + } + } + if (arguments.SourceFromFile) { + auto const k = arguments.SourceFromFile->size(); + for (auto i = decltype(k){ 0 }; i < k; i += 2) { + const auto& dst = (*arguments.SourceFromFile)[i + 0]; + const auto& src = (*arguments.SourceFromFile)[i + 1]; + + if (!cmSystemTools::GetFilenamePath(dst).empty()) { + const auto& msg = + cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, "\""); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + + auto dstPath = cmStrCat(this->BinaryDirectory, "/", dst); + auto const result = cmSystemTools::CopyFileAlways(src, dstPath); + if (!result.IsSuccess()) { + const auto& msg = cmStrCat("SOURCE_FROM_FILE failed to copy \"", src, + "\": ", result.GetString()); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return false; + } + + sources.emplace_back(std::move(dstPath)); + } + } + // TODO: ensure sources is not empty // Detect languages to enable. cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator(); @@ -508,7 +550,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, err << cmJoin(langs, " "); err << "\nSee project() command to enable other languages."; this->Makefile->IssueMessage(MessageType::FATAL_ERROR, err.str()); - return -1; + return false; } } @@ -535,7 +577,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, << cmSystemTools::GetLastSystemError(); /* clang-format on */ this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str()); - return -1; + return false; } cmValue def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH"); @@ -575,6 +617,14 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, *cmp0123 == "NEW"_s ? "NEW" : "OLD"); } + /* Set MSVC debug information format policy to match our selection. */ + if (cmValue msvcDebugInformationFormatDefault = + this->Makefile->GetDefinition( + kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)) { + fprintf(fout, "cmake_policy(SET CMP0141 %s)\n", + !msvcDebugInformationFormatDefault->empty() ? "NEW" : "OLD"); + } + /* Set cache/normal variable policy to match outer project. It may affect toolchain files. */ if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) != @@ -604,13 +654,22 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str()); - if (cmakeInternal == "ABI") { + if (arguments.CMakeInternal == "ABI") { // This is the ABI detection step, also used for implicit includes. // Erase any include_directories() calls from the toolchain file so // that we do not see them as implicit. Our ABI detection source // does not include any system headers anyway. fprintf(fout, "set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES \"\")\n"); + + // The link and compile lines for ABI detection step need to not use + // response files so we can extract implicit includes given to + // the underlying host compiler + if (testLangs.find("CUDA") != testLangs.end()) { + fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n"); + fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n"); + fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n"); + } } fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n"); for (std::string const& li : testLangs) { @@ -649,9 +708,9 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, CM_FALLTHROUGH; case cmPolicies::NEW: { // NEW behavior is to pass config-specific compiler flags. - static std::string const cfgDefault = "DEBUG"; - std::string const cfg = - !tcConfig.empty() ? cmSystemTools::UpperCase(tcConfig) : cfgDefault; + std::string const cfg = !tcConfig.empty() + ? cmSystemTools::UpperCase(tcConfig) + : TryCompileDefaultConfig; for (std::string const& li : testLangs) { std::string const langFlagsCfg = cmStrCat("CMAKE_", li, "_FLAGS_", cfg); @@ -702,18 +761,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, fprintf(fout, "set(CMAKE_SUPPRESS_REGENERATION 1)\n"); fprintf(fout, "link_directories(${LINK_DIRECTORIES})\n"); // handle any compile flags we need to pass on - if (!compileDefs.empty()) { + if (!arguments.CompileDefs.empty()) { // Pass using bracket arguments to preserve content. fprintf(fout, "add_definitions([==[%s]==])\n", - cmJoin(compileDefs, "]==] [==[").c_str()); + cmJoin(arguments.CompileDefs, "]==] [==[").c_str()); } - /* Use a random file name to avoid rapid creation and deletion - of the same executable name (some filesystems fail on that). */ - snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x", - cmSystemTools::RandomSeed() & 0xFFFFF); - targetName = targetNameBuf; - if (!targets.empty()) { std::string fname = "/" + std::string(targetName) + "Targets.cmake"; cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile, @@ -725,7 +778,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, this->Makefile->IssueMessage(MessageType::FATAL_ERROR, "could not write export file."); fclose(fout); - return -1; + return false; } fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n", fname.c_str()); @@ -769,24 +822,27 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, fprintf(fout, " \"%s\"", si.c_str()); // Add dependencies on any non-temporary sources. - if (si.find("CMakeTmp") == std::string::npos) { + if (!IsTemporary(si)) { this->Makefile->AddCMakeDependFile(si); } } fprintf(fout, ")\n"); - cState.Enabled(testLangs.find("C") != testLangs.end()); - cxxState.Enabled(testLangs.find("CXX") != testLangs.end()); - cudaState.Enabled(testLangs.find("CUDA") != testLangs.end()); - hipState.Enabled(testLangs.find("HIP") != testLangs.end()); - objcState.Enabled(testLangs.find("OBJC") != testLangs.end()); - objcxxState.Enabled(testLangs.find("OBJCXX") != testLangs.end()); + /* Write out the output location of the target we are building */ + std::string perConfigGenex; + if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) { + perConfigGenex = "_$<UPPER_CASE:$<CONFIG>>"; + } + fprintf(fout, + "file(GENERATE OUTPUT " + "\"${CMAKE_BINARY_DIR}/%s%s_loc\"\n", + targetName.c_str(), perConfigGenex.c_str()); + fprintf(fout, " CONTENT $<TARGET_FILE:%s>)\n", targetName.c_str()); bool warnCMP0067 = false; bool honorStandard = true; - if (cState.DidNone() && cxxState.DidNone() && objcState.DidNone() && - objcxxState.DidNone() && cudaState.DidNone() && hipState.DidNone()) { + if (arguments.LangProps.empty()) { switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) { case cmPolicies::WARN: warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled( @@ -811,18 +867,33 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, std::vector<std::string> warnCMP0067Variables; - cState.LoadUnsetPropertyValues(this->Makefile, honorStandard, warnCMP0067, - warnCMP0067Variables); - cxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - cudaState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - hipState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - objcState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); - objcxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard, - warnCMP0067, warnCMP0067Variables); + if (honorStandard || warnCMP0067) { + static std::array<std::string, 6> const possibleLangs{ + { "C", "CXX", "CUDA", "HIP", "OBJC", "OBJCXX" } + }; + static std::array<cm::string_view, 3> const langPropSuffixes{ + { "_STANDARD"_s, "_STANDARD_REQUIRED"_s, "_EXTENSIONS"_s } + }; + for (std::string const& lang : possibleLangs) { + if (testLangs.find(lang) == testLangs.end()) { + continue; + } + for (cm::string_view propSuffix : langPropSuffixes) { + std::string langProp = cmStrCat(lang, propSuffix); + if (!arguments.LangProps.count(langProp)) { + std::string langPropVar = cmStrCat("CMAKE_"_s, langProp); + std::string value = this->Makefile->GetSafeDefinition(langPropVar); + if (warnCMP0067 && !value.empty()) { + value.clear(); + warnCMP0067Variables.emplace_back(langPropVar); + } + if (!value.empty()) { + arguments.LangProps[langProp] = value; + } + } + } + } + } if (!warnCMP0067Variables.empty()) { std::ostringstream w; @@ -838,17 +909,20 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); } - cState.WriteProperties(fout, targetName); - cxxState.WriteProperties(fout, targetName); - cudaState.WriteProperties(fout, targetName); - hipState.WriteProperties(fout, targetName); - objcState.WriteProperties(fout, targetName); - objcxxState.WriteProperties(fout, targetName); + for (auto const& p : arguments.LangProps) { + if (p.second.empty()) { + continue; + } + fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n", + targetName.c_str(), + cmOutputConverter::EscapeForCMake(p.first).c_str(), + cmOutputConverter::EscapeForCMake(p.second).c_str()); + } - if (!linkOptions.empty()) { + if (!arguments.LinkOptions.empty()) { std::vector<std::string> options; - options.reserve(linkOptions.size()); - for (const auto& option : linkOptions) { + options.reserve(arguments.LinkOptions.size()); + for (const auto& option : arguments.LinkOptions) { options.emplace_back(cmOutputConverter::EscapeForCMake(option)); } @@ -862,15 +936,18 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, } } - if (useOldLinkLibs) { - fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n", - targetName.c_str()); - } else { + if (arguments.LinkLibraries) { + std::string libsToLink = " "; + for (std::string const& i : *arguments.LinkLibraries) { + libsToLink += "\"" + cmTrimWhitespace(i) + "\" "; + } fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(), libsToLink.c_str()); + } else { + fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n", + targetName.c_str()); } fclose(fout); - projectName = "CMAKE_TRY_COMPILE"; } // Forward a set of variables to the inner project cache. @@ -917,6 +994,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, vars.insert(kCMAKE_WARN_DEPRECATED); vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s); vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s); + vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s); if (cmValue varListStr = this->Makefile->GetDefinition( kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) { @@ -959,13 +1037,13 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) { vars.erase(kCMAKE_OSX_ARCHITECTURES); std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs; - cmakeFlags.emplace_back(std::move(flag)); + arguments.CMakeFlags.emplace_back(std::move(flag)); } for (std::string const& var : vars) { if (cmValue val = this->Makefile->GetDefinition(var)) { std::string flag = "-D" + var + "=" + *val; - cmakeFlags.emplace_back(std::move(flag)); + arguments.CMakeFlags.emplace_back(std::move(flag)); } } } @@ -975,37 +1053,50 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, for (std::string const& var : ghs_platform_vars) { if (cmValue val = this->Makefile->GetDefinition(var)) { std::string flag = "-D" + var + "=" + "'" + *val + "'"; - cmakeFlags.emplace_back(std::move(flag)); + arguments.CMakeFlags.emplace_back(std::move(flag)); } } } + if (this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) { + auto msg = + cmStrCat("Executing try_compile (", *arguments.CompileResultVariable, + ") in:\n ", this->BinaryDirectory); + this->Makefile->IssueMessage(MessageType::LOG, msg); + } + bool erroroc = cmSystemTools::GetErrorOccurredFlag(); cmSystemTools::ResetErrorOccurredFlag(); std::string output; // actually do the try compile now that everything is setup int res = this->Makefile->TryCompile( sourceDirectory, this->BinaryDirectory, projectName, targetName, - this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL, &cmakeFlags, - output); + this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL, + &arguments.CMakeFlags, output); if (erroroc) { cmSystemTools::SetErrorOccurred(); } // set the result var to the return value to indicate success or failure - this->Makefile->AddCacheDefinition(argv[0], (res == 0 ? "TRUE" : "FALSE"), - "Result of TRY_COMPILE", - cmStateEnums::INTERNAL); + if (arguments.NoCache) { + this->Makefile->AddDefinition(*arguments.CompileResultVariable, + (res == 0 ? "TRUE" : "FALSE")); + } else { + this->Makefile->AddCacheDefinition( + *arguments.CompileResultVariable, (res == 0 ? "TRUE" : "FALSE"), + "Result of TRY_COMPILE", cmStateEnums::INTERNAL); + } - if (!outputVariable.empty()) { - this->Makefile->AddDefinition(outputVariable, output); + if (arguments.OutputVariable) { + this->Makefile->AddDefinition(*arguments.OutputVariable, output); } if (this->SrcFileSignature) { std::string copyFileErrorMessage; - this->FindOutputFile(targetName, targetType); + this->FindOutputFile(targetName); - if ((res == 0) && !copyFile.empty()) { + if ((res == 0) && arguments.CopyFileTo) { + std::string const& copyFile = *arguments.CopyFileTo; if (this->OutputFile.empty() || !cmSystemTools::CopyFileAlways(this->OutputFile, copyFile)) { std::ostringstream emsg; @@ -1018,19 +1109,26 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, if (!this->FindErrorMessage.empty()) { emsg << this->FindErrorMessage; } - if (copyFileError.empty()) { + if (!arguments.CopyFileError) { this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str()); - return -1; + return false; } copyFileErrorMessage = emsg.str(); } } - if (!copyFileError.empty()) { + if (arguments.CopyFileError) { + std::string const& copyFileError = *arguments.CopyFileError; this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage); } } - return res; + return res == 0; +} + +bool cmCoreTryCompile::IsTemporary(std::string const& path) +{ + return ((path.find("CMakeTmp") != std::string::npos) || + (path.find("CMakeScratch") != std::string::npos)); } void cmCoreTryCompile::CleanupFiles(std::string const& binDir) @@ -1039,11 +1137,11 @@ void cmCoreTryCompile::CleanupFiles(std::string const& binDir) return; } - if (binDir.find("CMakeTmp") == std::string::npos) { + if (!IsTemporary(binDir)) { cmSystemTools::Error( "TRY_COMPILE attempt to remove -rf directory that does not contain " - "CMakeTmp:" + - binDir); + "CMakeTmp or CMakeScratch: \"" + + binDir + "\""); return; } @@ -1089,63 +1187,79 @@ void cmCoreTryCompile::CleanupFiles(std::string const& binDir) } } } + + if (binDir.find("CMakeScratch") != std::string::npos) { + cmSystemTools::RemoveADirectory(binDir); + } } -void cmCoreTryCompile::FindOutputFile(const std::string& targetName, - cmStateEnums::TargetType targetType) +void cmCoreTryCompile::FindOutputFile(const std::string& targetName) { this->FindErrorMessage.clear(); this->OutputFile.clear(); std::string tmpOutputFile = "/"; - if (targetType == cmStateEnums::EXECUTABLE) { - tmpOutputFile += targetName; - tmpOutputFile += - this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX"); - } else // if (targetType == cmStateEnums::STATIC_LIBRARY) - { - tmpOutputFile += - this->Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_PREFIX"); - tmpOutputFile += targetName; - tmpOutputFile += - this->Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"); + tmpOutputFile += targetName; + + if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) { + std::string const tcConfig = + this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"); + std::string const cfg = !tcConfig.empty() + ? cmSystemTools::UpperCase(tcConfig) + : TryCompileDefaultConfig; + tmpOutputFile = cmStrCat(tmpOutputFile, '_', cfg); + } + tmpOutputFile += "_loc"; + + std::string command = cmStrCat(this->BinaryDirectory, tmpOutputFile); + if (!cmSystemTools::FileExists(command)) { + std::ostringstream emsg; + emsg << "Unable to find the recorded try_compile output location:\n"; + emsg << cmStrCat(" ", command, "\n"); + this->FindErrorMessage = emsg.str(); + return; + } + + std::string outputFileLocation; + cmsys::ifstream ifs(command.c_str()); + cmSystemTools::GetLineFromStream(ifs, outputFileLocation); + if (!cmSystemTools::FileExists(outputFileLocation)) { + std::ostringstream emsg; + emsg << "Recorded try_compile output location doesn't exist:\n"; + emsg << cmStrCat(" ", outputFileLocation, "\n"); + this->FindErrorMessage = emsg.str(); + return; } - // a list of directories where to search for the compilation result - // at first directly in the binary dir - std::vector<std::string> searchDirs; - searchDirs.emplace_back(); - - cmValue config = - this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"); - // if a config was specified try that first - if (cmNonempty(config)) { - std::string tmp = cmStrCat('/', *config); - searchDirs.emplace_back(std::move(tmp)); + this->OutputFile = cmSystemTools::CollapseFullPath(outputFileLocation); +} + +std::string cmCoreTryCompile::WriteSource(std::string const& filename, + std::string const& content, + char const* command) const +{ + if (!cmSystemTools::GetFilenamePath(filename).empty()) { + const auto& msg = + cmStrCat(command, " given invalid filename \"", filename, "\""); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return {}; } - searchDirs.emplace_back("/Debug"); -#if defined(__APPLE__) - std::string app = "/" + targetName + ".app"; - if (cmNonempty(config)) { - std::string tmp = cmStrCat('/', *config, app); - searchDirs.emplace_back(std::move(tmp)); + + auto filepath = cmStrCat(this->BinaryDirectory, "/", filename); + cmsys::ofstream file{ filepath.c_str(), std::ios::out }; + if (!file) { + const auto& msg = + cmStrCat(command, " failed to open \"", filename, "\" for writing"); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return {}; } - std::string tmp = "/Debug" + app; - searchDirs.emplace_back(std::move(tmp)); - searchDirs.emplace_back(std::move(app)); -#endif - searchDirs.emplace_back("/Development"); - for (std::string const& sdir : searchDirs) { - std::string command = cmStrCat(this->BinaryDirectory, sdir, tmpOutputFile); - if (cmSystemTools::FileExists(command)) { - this->OutputFile = cmSystemTools::CollapseFullPath(command); - return; - } + file << content; + if (!file) { + const auto& msg = cmStrCat(command, " failed to write \"", filename, "\""); + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg); + return {}; } - std::ostringstream emsg; - emsg << "Unable to find the executable at any of:\n"; - emsg << cmWrap(" " + this->BinaryDirectory, searchDirs, tmpOutputFile, "\n") - << "\n"; - this->FindErrorMessage = emsg.str(); + file.close(); + return filepath; } diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h index 594fd7f..3e1e12c 100644 --- a/Source/cmCoreTryCompile.h +++ b/Source/cmCoreTryCompile.h @@ -4,28 +4,90 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include <map> #include <string> #include <vector> -#include "cmCommand.h" +#include <cm/optional> + +#include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmStateTypes.h" +class cmMakefile; +template <typename Iter> +class cmRange; + /** \class cmCoreTryCompile * \brief Base class for cmTryCompileCommand and cmTryRunCommand * * cmCoreTryCompile implements the functionality to build a program. * It is the base class for cmTryCompileCommand and cmTryRunCommand. */ -class cmCoreTryCompile : public cmCommand +class cmCoreTryCompile { public: -protected: + cmCoreTryCompile(cmMakefile* mf) + : Makefile(mf) + { + } + + struct Arguments : public ArgumentParser::ParseResult + { + cm::optional<std::string> CompileResultVariable; + cm::optional<std::string> BinaryDirectory; + cm::optional<std::string> SourceDirectoryOrFile; + cm::optional<std::string> ProjectName; + cm::optional<std::string> TargetName; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> Sources; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + SourceFromContent; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + SourceFromVar; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + SourceFromFile; + ArgumentParser::MaybeEmpty<std::vector<std::string>> CMakeFlags{ + 1, "CMAKE_FLAGS" + }; // fake argv[0] + std::vector<std::string> CompileDefs; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> + LinkLibraries; + ArgumentParser::MaybeEmpty<std::vector<std::string>> LinkOptions; + std::map<std::string, std::string> LangProps; + std::string CMakeInternal; + cm::optional<std::string> OutputVariable; + cm::optional<std::string> CopyFileTo; + cm::optional<std::string> CopyFileError; + bool NoCache = false; + + // Argument for try_run only. + // Keep in sync with warnings in cmCoreTryCompile::ParseArgs. + cm::optional<std::string> CompileOutputVariable; + cm::optional<std::string> RunOutputVariable; + cm::optional<std::string> RunOutputStdOutVariable; + cm::optional<std::string> RunOutputStdErrVariable; + cm::optional<std::string> RunWorkingDirectory; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> RunArgs; + }; + + Arguments ParseArgs(cmRange<std::vector<std::string>::const_iterator> args, + bool isTryRun); + /** - * This is the core code for try compile. It is here so that other - * commands, such as TryRun can access the same logic without - * duplication. + * This is the core code for try compile. It is here so that other commands, + * such as TryRun can access the same logic without duplication. + * + * This function requires at least two \p arguments and will crash if given + * fewer. */ - int TryCompileCode(std::vector<std::string> const& argv, bool isTryRun); + bool TryCompileCode(Arguments& arguments, + cmStateEnums::TargetType targetType); + + /** + * Returns \c true if \p path resides within a CMake temporary directory, + * otherwise returns \c false. + */ + static bool IsTemporary(std::string const& path); /** * This deletes all the files created by TryCompileCode. @@ -39,11 +101,20 @@ protected: TryCompileCode. The result is stored in OutputFile. If nothing is found, the error message is stored in FindErrorMessage. */ - void FindOutputFile(const std::string& targetName, - cmStateEnums::TargetType targetType); + void FindOutputFile(const std::string& targetName); std::string BinaryDirectory; std::string OutputFile; std::string FindErrorMessage; bool SrcFileSignature = false; + cmMakefile* Makefile; + +private: + std::string WriteSource(std::string const& name, std::string const& content, + char const* command) const; + + Arguments ParseArgs( + const cmRange<std::vector<std::string>::const_iterator>& args, + const cmArgumentParser<Arguments>& parser, + std::vector<std::string>& unparsedArguments); }; diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx index 2a52d1a..75c25e3 100644 --- a/Source/cmCreateTestSourceList.cxx +++ b/Source/cmCreateTestSourceList.cxx @@ -87,9 +87,7 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args, func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i); } cmSystemTools::ConvertToUnixSlashes(func_name); - std::replace(func_name.begin(), func_name.end(), ' ', '_'); - std::replace(func_name.begin(), func_name.end(), '/', '_'); - std::replace(func_name.begin(), func_name.end(), ':', '_'); + func_name = cmSystemTools::MakeCidentifier(func_name); bool already_declared = std::find(tests_func_name.begin(), tests_func_name.end(), func_name) != tests_func_name.end(); diff --git a/Source/cmCurl.cxx b/Source/cmCurl.cxx index 28ee24d..fd6aee1 100644 --- a/Source/cmCurl.cxx +++ b/Source/cmCurl.cxx @@ -34,10 +34,21 @@ std::string cmCurlSetCAInfo(::CURL* curl, const std::string& cafile) { std::string e; + std::string env_ca; if (!cafile.empty()) { ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, cafile.c_str()); check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: "); } + /* Honor the user-configurable OpenSSL environment variables. */ + else if (cmSystemTools::GetEnv("SSL_CERT_FILE", env_ca) && + cmSystemTools::FileExists(env_ca, true)) { + ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, env_ca.c_str()); + check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: "); + } else if (cmSystemTools::GetEnv("SSL_CERT_DIR", env_ca) && + cmSystemTools::FileIsDirectory(env_ca)) { + ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAPATH, env_ca.c_str()); + check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: "); + } #ifdef CMAKE_FIND_CAFILE # define CMAKE_CAFILE_FEDORA "/etc/pki/tls/certs/ca-bundle.crt" else if (cmSystemTools::FileExists(CMAKE_CAFILE_FEDORA, true)) { diff --git a/Source/cmCxxModuleMapper.cxx b/Source/cmCxxModuleMapper.cxx new file mode 100644 index 0000000..84691c9 --- /dev/null +++ b/Source/cmCxxModuleMapper.cxx @@ -0,0 +1,308 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCxxModuleMapper.h" + +#include <cassert> +#include <cstddef> +#include <set> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +#include <cm/string_view> +#include <cmext/string_view> + +#include "cmScanDepFormat.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule( + std::string const& logical_name) const +{ + if (auto l = this->BmiLocationForModule(logical_name)) { + return this->PathForGenerator(*l); + } + return {}; +} + +namespace { + +std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc, + cmScanDepInfo const& obj) +{ + std::stringstream mm; + + // Documented in GCC's documentation. The format is a series of + // lines with a module name and the associated filename separated + // by spaces. The first line may use `$root` as the module name + // to specify a "repository root". That is used to anchor any + // relative paths present in the file (CMake should never + // generate any). + + // Write the root directory to use for module paths. + mm << "$root " << loc.RootDirectory << "\n"; + + for (auto const& p : obj.Provides) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) { + mm << p.LogicalName << ' ' << *bmi_loc << '\n'; + } + } + for (auto const& r : obj.Requires) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) { + mm << r.LogicalName << ' ' << *bmi_loc << '\n'; + } + } + + return mm.str(); +} + +std::string CxxModuleMapContentMsvc(CxxModuleLocations const& loc, + cmScanDepInfo const& obj, + CxxModuleUsage const& usages) +{ + std::stringstream mm; + + // A response file of `-reference NAME=PATH` arguments. + + // MSVC's command line only supports a single output. If more than one is + // expected, we cannot make a useful module map file. + if (obj.Provides.size() > 1) { + return {}; + } + + auto flag_for_method = [](LookupMethod method) -> cm::static_string_view { + switch (method) { + case LookupMethod::ByName: + return "-reference"_s; + case LookupMethod::IncludeAngle: + return "-headerUnit:angle"_s; + case LookupMethod::IncludeQuote: + return "-headerUnit:quote"_s; + } + assert(false && "unsupported lookup method"); + return ""_s; + }; + + for (auto const& p : obj.Provides) { + if (p.IsInterface) { + mm << "-interface\n"; + } else { + mm << "-internalPartition\n"; + } + + if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) { + mm << "-ifcOutput " << *bmi_loc << '\n'; + } + } + + std::set<std::string> transitive_usage_directs; + std::set<std::string> transitive_usage_names; + + for (auto const& r : obj.Requires) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) { + auto flag = flag_for_method(r.Method); + + mm << flag << ' ' << r.LogicalName << '=' << *bmi_loc << "\n"; + transitive_usage_directs.insert(r.LogicalName); + + // Insert transitive usages. + auto transitive_usages = usages.Usage.find(r.LogicalName); + if (transitive_usages != usages.Usage.end()) { + transitive_usage_names.insert(transitive_usages->second.begin(), + transitive_usages->second.end()); + } + } + } + + for (auto const& transitive_name : transitive_usage_names) { + if (transitive_usage_directs.count(transitive_name)) { + continue; + } + + auto module_ref = usages.Reference.find(transitive_name); + if (module_ref != usages.Reference.end()) { + auto flag = flag_for_method(module_ref->second.Method); + mm << flag << ' ' << transitive_name << '=' << module_ref->second.Path + << "\n"; + } + } + + return mm.str(); +} +} + +bool CxxModuleUsage::AddReference(std::string const& logical, + std::string const& loc, LookupMethod method) +{ + auto r = this->Reference.find(logical); + if (r != this->Reference.end()) { + auto& ref = r->second; + + if (ref.Path == loc && ref.Method == method) { + return true; + } + + auto method_name = [](LookupMethod m) -> cm::static_string_view { + switch (m) { + case LookupMethod::ByName: + return "by-name"_s; + case LookupMethod::IncludeAngle: + return "include-angle"_s; + case LookupMethod::IncludeQuote: + return "include-quote"_s; + } + assert(false && "unsupported lookup method"); + return ""_s; + }; + + cmSystemTools::Error(cmStrCat("Disagreement of the location of the '", + logical, + "' module. " + "Location A: '", + ref.Path, "' via ", method_name(ref.Method), + "; " + "Location B: '", + loc, "' via ", method_name(method), ".")); + return false; + } + + auto& ref = this->Reference[logical]; + ref.Path = loc; + ref.Method = method; + + return true; +} + +cm::static_string_view CxxModuleMapExtension( + cm::optional<CxxModuleMapFormat> format) +{ + if (format) { + switch (*format) { + case CxxModuleMapFormat::Gcc: + return ".gcm"_s; + case CxxModuleMapFormat::Msvc: + return ".ifc"_s; + } + } + + return ".bmi"_s; +} + +std::set<std::string> CxxModuleUsageSeed( + CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects, + CxxModuleUsage& usages) +{ + // Track inner usages to populate usages from internal bits. + // + // This is a map of modules that required some other module that was not + // found to those that were not found. + std::map<std::string, std::set<std::string>> internal_usages; + std::set<std::string> unresolved; + + for (cmScanDepInfo const& object : objects) { + // Add references for each of the provided modules. + for (auto const& p : object.Provides) { + if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) { + // XXX(cxx-modules): How to support header units? + usages.AddReference(p.LogicalName, loc.PathForGenerator(*bmi_loc), + LookupMethod::ByName); + } + } + + // For each requires, pull in what is required. + for (auto const& r : object.Requires) { + // Find transitive usages. + auto transitive_usages = usages.Usage.find(r.LogicalName); + // Find the required name in the current target. + auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName); + + for (auto const& p : object.Provides) { + auto& this_usages = usages.Usage[p.LogicalName]; + + // Add the direct usage. + this_usages.insert(r.LogicalName); + + // Add the transitive usage. + if (transitive_usages != usages.Usage.end()) { + this_usages.insert(transitive_usages->second.begin(), + transitive_usages->second.end()); + } else if (bmi_loc) { + // Mark that we need to update transitive usages later. + internal_usages[p.LogicalName].insert(r.LogicalName); + } + } + + if (bmi_loc) { + usages.AddReference(r.LogicalName, loc.PathForGenerator(*bmi_loc), + r.Method); + } + } + } + + // While we have internal usages to manage. + while (!internal_usages.empty()) { + size_t starting_size = internal_usages.size(); + + // For each internal usage. + for (auto usage = internal_usages.begin(); usage != internal_usages.end(); + /* see end of loop */) { + auto& this_usages = usages.Usage[usage->first]; + + for (auto use = usage->second.begin(); use != usage->second.end(); + /* see end of loop */) { + // Check if this required module uses other internal modules; defer + // if so. + if (internal_usages.count(*use)) { + // Advance the iterator. + ++use; + continue; + } + + auto transitive_usages = usages.Usage.find(*use); + if (transitive_usages != usages.Usage.end()) { + this_usages.insert(transitive_usages->second.begin(), + transitive_usages->second.end()); + } + + // Remove the entry and advance the iterator. + use = usage->second.erase(use); + } + + // Erase the entry if it doesn't have any remaining usages. + if (usage->second.empty()) { + usage = internal_usages.erase(usage); + } else { + ++usage; + } + } + + // Check that at least one usage was resolved. + if (starting_size == internal_usages.size()) { + // Nothing could be resolved this loop; we have a cycle, so record the + // cycle and exit. + for (auto const& usage : internal_usages) { + unresolved.insert(usage.first); + } + break; + } + } + + return unresolved; +} + +std::string CxxModuleMapContent(CxxModuleMapFormat format, + CxxModuleLocations const& loc, + cmScanDepInfo const& obj, + CxxModuleUsage const& usages) +{ + switch (format) { + case CxxModuleMapFormat::Gcc: + return CxxModuleMapContentGcc(loc, obj); + case CxxModuleMapFormat::Msvc: + return CxxModuleMapContentMsvc(loc, obj, usages); + } + + assert(false); + return {}; +} diff --git a/Source/cmCxxModuleMapper.h b/Source/cmCxxModuleMapper.h new file mode 100644 index 0000000..8526a07 --- /dev/null +++ b/Source/cmCxxModuleMapper.h @@ -0,0 +1,85 @@ +/* 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 <map> +#include <set> +#include <string> +#include <vector> + +#include <cm/optional> +#include <cmext/string_view> + +#include "cmScanDepFormat.h" + +enum class CxxModuleMapFormat +{ + Gcc, + Msvc, +}; + +struct CxxModuleLocations +{ + // The path from which all relative paths should be computed. If + // this is relative, it is relative to the compiler's working + // directory. + std::string RootDirectory; + + // A function to convert a full path to a path for the generator. + std::function<std::string(std::string const&)> PathForGenerator; + + // Lookup the BMI location of a logical module name. + std::function<cm::optional<std::string>(std::string const&)> + BmiLocationForModule; + + // Returns the generator path (if known) for the BMI given a + // logical module name. + cm::optional<std::string> BmiGeneratorPathForModule( + std::string const& logical_name) const; +}; + +struct CxxModuleReference +{ + // The path to the module file used. + std::string Path; + // How the module was looked up. + LookupMethod Method; +}; + +struct CxxModuleUsage +{ + // The usage requirements for this object. + std::map<std::string, std::set<std::string>> Usage; + + // The references for this object. + std::map<std::string, CxxModuleReference> Reference; + + // Add a reference to a module. + // + // Returns `true` if it matches how it was found previously, `false` if it + // conflicts. + bool AddReference(std::string const& logical, std::string const& loc, + LookupMethod method); +}; + +// Return the extension to use for a given modulemap format. +cm::static_string_view CxxModuleMapExtension( + cm::optional<CxxModuleMapFormat> format); + +// Fill in module usage information for internal usages. +// +// Returns the set of unresolved module usage requirements (these form an +// import cycle). +std::set<std::string> CxxModuleUsageSeed( + CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects, + CxxModuleUsage& usages); + +// Return the contents of the module map in the given format for the +// object file. +std::string CxxModuleMapContent(CxxModuleMapFormat format, + CxxModuleLocations const& loc, + cmScanDepInfo const& obj, + CxxModuleUsage const& usages); diff --git a/Source/cmDefinePropertyCommand.cxx b/Source/cmDefinePropertyCommand.cxx index faefcb8..31ee665 100644 --- a/Source/cmDefinePropertyCommand.cxx +++ b/Source/cmDefinePropertyCommand.cxx @@ -8,6 +8,7 @@ #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmProperty.h" @@ -51,8 +52,8 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args, // Parse remaining arguments. bool inherited = false; std::string PropertyName; - std::vector<std::string> BriefDocs; - std::vector<std::string> FullDocs; + ArgumentParser::NonEmpty<std::vector<std::string>> BriefDocs; + ArgumentParser::NonEmpty<std::vector<std::string>> FullDocs; std::string initializeFromVariable; cmArgumentParser<void> parser; diff --git a/Source/cmDocumentation.cxx b/Source/cmDocumentation.cxx index 3619ade..d466a12 100644 --- a/Source/cmDocumentation.cxx +++ b/Source/cmDocumentation.cxx @@ -17,30 +17,32 @@ #include "cmVersion.h" static const char* cmDocumentationStandardOptions[][2] = { - { "--help,-help,-usage,-h,-H,/?", "Print usage information and exit." }, - { "--version,-version,/V [<f>]", "Print version number and exit." }, - { "--help-full [<f>]", "Print all help manuals and exit." }, - { "--help-manual <man> [<f>]", "Print one help manual and exit." }, - { "--help-manual-list [<f>]", "List help manuals available and exit." }, - { "--help-command <cmd> [<f>]", "Print help for one command and exit." }, - { "--help-command-list [<f>]", + { "-h,-H,--help,-help,-usage,/?", "Print usage information and exit." }, + { "--version,-version,/V [<file>]", "Print version number and exit." }, + { "--help-full [<file>]", "Print all help manuals and exit." }, + { "--help-manual <man> [<file>]", "Print one help manual and exit." }, + { "--help-manual-list [<file>]", "List help manuals available and exit." }, + { "--help-command <cmd> [<file>]", "Print help for one command and exit." }, + { "--help-command-list [<file>]", "List commands with help available and exit." }, - { "--help-commands [<f>]", "Print cmake-commands manual and exit." }, - { "--help-module <mod> [<f>]", "Print help for one module and exit." }, - { "--help-module-list [<f>]", "List modules with help available and exit." }, - { "--help-modules [<f>]", "Print cmake-modules manual and exit." }, - { "--help-policy <cmp> [<f>]", "Print help for one policy and exit." }, - { "--help-policy-list [<f>]", + { "--help-commands [<file>]", "Print cmake-commands manual and exit." }, + { "--help-module <mod> [<file>]", "Print help for one module and exit." }, + { "--help-module-list [<file>]", + "List modules with help available and exit." }, + { "--help-modules [<file>]", "Print cmake-modules manual and exit." }, + { "--help-policy <cmp> [<file>]", "Print help for one policy and exit." }, + { "--help-policy-list [<file>]", "List policies with help available and exit." }, - { "--help-policies [<f>]", "Print cmake-policies manual and exit." }, - { "--help-property <prop> [<f>]", "Print help for one property and exit." }, - { "--help-property-list [<f>]", + { "--help-policies [<file>]", "Print cmake-policies manual and exit." }, + { "--help-property <prop> [<file>]", + "Print help for one property and exit." }, + { "--help-property-list [<file>]", "List properties with help available and exit." }, - { "--help-properties [<f>]", "Print cmake-properties manual and exit." }, - { "--help-variable var [<f>]", "Print help for one variable and exit." }, - { "--help-variable-list [<f>]", + { "--help-properties [<file>]", "Print cmake-properties manual and exit." }, + { "--help-variable var [<file>]", "Print help for one variable and exit." }, + { "--help-variable-list [<file>]", "List variables with help available and exit." }, - { "--help-variables [<f>]", "Print cmake-variables manual and exit." }, + { "--help-variables [<file>]", "Print cmake-variables manual and exit." }, { nullptr, nullptr } }; diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 222ea80..7fbd826 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -47,7 +47,7 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::vector<std::vector<std::string>> Commands; std::string OutputVariable; @@ -95,14 +95,10 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args, .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal); std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args, &unparsedArguments, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args, &unparsedArguments); - if (!keywordsMissingValue.empty()) { - status.SetError(" called with no value for " + - keywordsMissingValue.front() + "."); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!unparsedArguments.empty()) { status.SetError(" given unknown argument \"" + unparsedArguments.front() + diff --git a/Source/cmExecutionStatus.h b/Source/cmExecutionStatus.h index 0feaedf..ced3548 100644 --- a/Source/cmExecutionStatus.h +++ b/Source/cmExecutionStatus.h @@ -5,6 +5,7 @@ #include <cmConfigure.h> // IWYU pragma: keep #include <string> +#include <vector> class cmMakefile; @@ -27,8 +28,21 @@ public: void SetError(std::string const& e) { this->Error = e; } std::string const& GetError() const { return this->Error; } - void SetReturnInvoked() { this->ReturnInvoked = true; } + void SetReturnInvoked() + { + this->Variables.clear(); + this->ReturnInvoked = true; + } + void SetReturnInvoked(std::vector<std::string> variables) + { + this->Variables = std::move(variables); + this->ReturnInvoked = true; + } bool GetReturnInvoked() const { return this->ReturnInvoked; } + const std::vector<std::string>& GetReturnVariables() const + { + return this->Variables; + } void SetBreakInvoked() { this->BreakInvoked = true; } bool GetBreakInvoked() const { return this->BreakInvoked; } @@ -46,4 +60,5 @@ private: bool BreakInvoked = false; bool ContinueInvoked = false; bool NestedError = false; + std::vector<std::string> Variables; }; diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx new file mode 100644 index 0000000..922b53f --- /dev/null +++ b/Source/cmExperimental.cxx @@ -0,0 +1,63 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmExperimental.h" + +#include <cassert> +#include <cstddef> +#include <string> + +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmValue.h" + +namespace { + +/* + * The `Uuid` fields of these objects should change periodically. + * Search for other instances to keep the documentation and test suite + * up-to-date. + */ + +struct FeatureData +{ + std::string const Uuid; + std::string const Variable; + std::string const Description; + bool Warned; +} LookupTable[] = { + // CxxModuleCMakeApi + { "3c375311-a3c9-4396-a187-3227ef642046", + "CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API", + "CMake's C++ module support is experimental. It is meant only for " + "experimentation and feedback to CMake developers.", + false }, +}; +static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) == + static_cast<size_t>(cmExperimental::Feature::Sentinel), + "Experimental feature lookup table mismatch"); + +FeatureData& DataForFeature(cmExperimental::Feature f) +{ + assert(f != cmExperimental::Feature::Sentinel); + return LookupTable[static_cast<size_t>(f)]; +} +} + +bool cmExperimental::HasSupportEnabled(cmMakefile const& mf, Feature f) +{ + bool enabled = false; + auto& data = DataForFeature(f); + + auto value = mf.GetDefinition(data.Variable); + if (value == data.Uuid) { + enabled = true; + } + + if (enabled && !data.Warned) { + mf.IssueMessage(MessageType::AUTHOR_WARNING, data.Description); + data.Warned = true; + } + + return enabled; +} diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h new file mode 100644 index 0000000..26e0d17 --- /dev/null +++ b/Source/cmExperimental.h @@ -0,0 +1,21 @@ +/* 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 + +class cmMakefile; + +class cmExperimental +{ +public: + enum class Feature + { + CxxModuleCMakeApi, + + Sentinel, + }; + + static bool HasSupportEnabled(cmMakefile const& mf, Feature f); +}; diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx index 6ce0c98..ed199ea 100644 --- a/Source/cmExportBuildFileGenerator.cxx +++ b/Source/cmExportBuildFileGenerator.cxx @@ -9,10 +9,13 @@ #include <sstream> #include <utility> +#include <cm/string_view> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmExportSet.h" #include "cmFileSet.h" +#include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -23,6 +26,7 @@ #include "cmPolicies.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" +#include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetExport.h" #include "cmValue.h" @@ -139,11 +143,18 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) this->GenerateTargetFileSets(gte, os); } + this->GenerateCxxModuleInformation(os); + // Generate import file content for each configuration. for (std::string const& c : this->Configurations) { this->GenerateImportConfig(os, c); } + // Generate import file content for each configuration. + for (std::string const& c : this->Configurations) { + this->GenerateImportCxxModuleConfigTargetInclusion(c); + } + this->GenerateMissingTargetsCheckCode(os); return true; @@ -382,6 +393,21 @@ std::string cmExportBuildFileGenerator::GetFileSetDirectories( std::any_of(directoryEntries.begin(), directoryEntries.end(), EntryIsContextSensitive); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->LG->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base directory entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + for (auto const& directory : directories) { auto dest = cmOutputConverter::EscapeForCMake( directory, cmOutputConverter::WrapQuotes::NoWrap); @@ -427,6 +453,21 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte, std::any_of(fileEntries.begin(), fileEntries.end(), EntryIsContextSensitive); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->LG->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + for (auto const& it : files) { for (auto const& filename : it.second) { auto escapedFile = cmOutputConverter::EscapeForCMake( @@ -447,3 +488,60 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte, return cmJoin(resultVector, " "); } + +std::string cmExportBuildFileGenerator::GetCxxModulesDirectory() const +{ + return this->CxxModulesDirectory; +} + +void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation( + std::ostream& os) const +{ + const char* opt = ""; + if (this->Configurations.size() > 1) { + // With more than one configuration, each individual file is optional. + opt = " OPTIONAL"; + } + + // Generate import file content for each configuration. + for (std::string c : this->Configurations) { + if (c.empty()) { + c = "noconfig"; + } + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << c << ".cmake\"" + << opt << ")\n"; + } +} + +bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion( + std::string config) const +{ + auto cxx_modules_dirname = this->GetCxxModulesDirectory(); + if (cxx_modules_dirname.empty()) { + return true; + } + + if (config.empty()) { + config = "noconfig"; + } + + std::string fileName = cmStrCat(this->FileDir, '/', cxx_modules_dirname, + "/cxx-modules-", config, ".cmake"); + + cmGeneratedFileStream os(fileName, true); + if (!os) { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName << "\": " << se; + cmSystemTools::Error(e.str()); + return false; + } + os.SetCopyIfDifferent(true); + + for (auto const* tgt : this->ExportedTargets) { + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-" << tgt->GetExportName() + << '-' << config << ".cmake\")\n"; + } + + return true; +} diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h index 5681e8f..4636196 100644 --- a/Source/cmExportBuildFileGenerator.h +++ b/Source/cmExportBuildFileGenerator.h @@ -47,6 +47,16 @@ public: } void SetExportSet(cmExportSet*); + /** Set the name of the C++ module directory. */ + void SetCxxModuleDirectory(std::string cxx_module_dir) + { + this->CxxModulesDirectory = std::move(cxx_module_dir); + } + const std::string& GetCxxModuleDirectory() const + { + return this->CxxModulesDirectory; + } + /** Set whether to append generated code to the output file. */ void SetAppendMode(bool append) { this->AppendMode = append; } @@ -81,6 +91,10 @@ protected: std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) override; + std::string GetCxxModulesDirectory() const override; + void GenerateCxxModuleConfigInformation(std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string) const; + std::pair<std::vector<std::string>, std::string> FindBuildExportInfo( cmGlobalGenerator* gg, const std::string& name); @@ -88,4 +102,6 @@ protected: cmExportSet* ExportSet; std::vector<cmGeneratorTarget*> Exports; cmLocalGenerator* LG; + // The directory for C++ module information. + std::string CxxModulesDirectory; }; diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 63440a3..a58f2b7 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -7,13 +7,15 @@ #include <utility> #include <cm/memory> -#include <cmext/algorithm> +#include <cm/optional> #include <cmext/string_view> #include "cmsys/RegularExpression.hxx" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" +#include "cmExperimental.h" #include "cmExportBuildAndroidMKGenerator.h" #include "cmExportBuildFileGenerator.h" #include "cmExportSet.h" @@ -57,10 +59,11 @@ bool cmExportCommand(std::vector<std::string> const& args, struct Arguments { std::string ExportSetName; - std::vector<std::string> Targets; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets; std::string Namespace; std::string Filename; std::string AndroidMKFile; + std::string CxxModulesDirectory; bool Append = false; bool ExportOld = false; }; @@ -69,6 +72,12 @@ bool cmExportCommand(std::vector<std::string> const& args, .Bind("NAMESPACE"_s, &Arguments::Namespace) .Bind("FILE"_s, &Arguments::Filename); + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + status.GetMakefile(), cmExperimental::Feature::CxxModuleCMakeApi); + if (supportCxx20FileSetTypes) { + parser.Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory); + } + if (args[0] == "EXPORT") { parser.Bind("EXPORT"_s, &Arguments::ExportSetName); } else { @@ -79,9 +88,7 @@ bool cmExportCommand(std::vector<std::string> const& args, } std::vector<std::string> unknownArgs; - std::vector<std::string> keywordsMissingValue; - Arguments const arguments = - parser.Parse(args, &unknownArgs, &keywordsMissingValue); + Arguments const arguments = parser.Parse(args, &unknownArgs); if (!unknownArgs.empty()) { status.SetError("Unknown argument: \"" + unknownArgs.front() + "\"."); @@ -145,9 +152,8 @@ bool cmExportCommand(std::vector<std::string> const& args, return false; } exportSet = &it->second; - } else if (!arguments.Targets.empty() || - cm::contains(keywordsMissingValue, "TARGETS")) { - for (std::string const& currentTarget : arguments.Targets) { + } else if (arguments.Targets) { + for (std::string const& currentTarget : *arguments.Targets) { if (mf.IsAlias(currentTarget)) { std::ostringstream e; e << "given ALIAS target \"" << currentTarget @@ -214,6 +220,7 @@ bool cmExportCommand(std::vector<std::string> const& args, } ebfg->SetExportFile(fname.c_str()); ebfg->SetNamespace(arguments.Namespace); + ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory); ebfg->SetAppendMode(arguments.Append); if (exportSet != nullptr) { ebfg->SetExportSet(exportSet); diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx index 5a33349..50bc78c 100644 --- a/Source/cmExportFileGenerator.cxx +++ b/Source/cmExportFileGenerator.cxx @@ -939,13 +939,13 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os) // Isolate the file policy level. // Support CMake versions as far back as 2.6 but also support using NEW - // policy settings for up to CMake 3.22 (this upper limit may be reviewed + // policy settings for up to CMake 3.23 (this upper limit may be reviewed // and increased from time to time). This reduces the opportunity for CMake // warnings when an older export file is later used with newer CMake // versions. /* clang-format off */ os << "cmake_policy(PUSH)\n" - << "cmake_policy(VERSION 2.8.3...3.22)\n"; + << "cmake_policy(VERSION 2.8.3...3.23)\n"; /* clang-format on */ } @@ -1095,6 +1095,10 @@ void cmExportFileGenerator::GenerateImportTargetCode( << " PROPERTY IMPORTED_NO_SYSTEM 1)\n"; } + if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) { + os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n"; + } + os << "\n"; } @@ -1308,3 +1312,28 @@ void cmExportFileGenerator::GenerateTargetFileSets(cmGeneratorTarget* gte, os << " )\nendif()\n\n"; } } + +void cmExportFileGenerator::GenerateCxxModuleInformation(std::ostream& os) +{ + auto const cxx_module_dirname = this->GetCxxModulesDirectory(); + if (cxx_module_dirname.empty()) { + return; + } + + // Write the include. + os << "# Include C++ module properties\n" + << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << cxx_module_dirname + << "/cxx-modules.cmake\")\n\n"; + + // Get the path to the file we're going to write. + std::string path = this->MainImportFile; + path = cmSystemTools::GetFilenamePath(path); + auto trampoline_path = + cmStrCat(path, '/', cxx_module_dirname, "/cxx-modules.cmake"); + + // Include all configuration-specific include files. + cmGeneratedFileStream ap(trampoline_path, true); + ap.SetCopyIfDifferent(true); + + this->GenerateCxxModuleConfigInformation(ap); +} diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h index d27a555..fdda878 100644 --- a/Source/cmExportFileGenerator.h +++ b/Source/cmExportFileGenerator.h @@ -182,6 +182,8 @@ protected: void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os, cmTargetExport* te = nullptr); + void GenerateCxxModuleInformation(std::ostream& os); + virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) = 0; @@ -226,4 +228,7 @@ private: virtual std::string InstallNameDir(cmGeneratorTarget const* target, const std::string& config) = 0; + + virtual std::string GetCxxModulesDirectory() const = 0; + virtual void GenerateCxxModuleConfigInformation(std::ostream& os) const = 0; }; diff --git a/Source/cmExportInstallAndroidMKGenerator.cxx b/Source/cmExportInstallAndroidMKGenerator.cxx index 4e4f8a1..d53254d 100644 --- a/Source/cmExportInstallAndroidMKGenerator.cxx +++ b/Source/cmExportInstallAndroidMKGenerator.cxx @@ -35,8 +35,7 @@ void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode( for (size_t n = 0; n < numDotDot; n++) { path += "/.."; } - os << "_IMPORT_PREFIX := " - << "$(LOCAL_PATH)" << path << "\n\n"; + os << "_IMPORT_PREFIX := $(LOCAL_PATH)" << path << "\n\n"; for (std::unique_ptr<cmTargetExport> const& te : this->IEGen->GetExportSet()->GetTargetExports()) { // Collect import properties for this target. diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx index adccdfe..195737b 100644 --- a/Source/cmExportInstallFileGenerator.cxx +++ b/Source/cmExportInstallFileGenerator.cxx @@ -7,6 +7,9 @@ #include <sstream> #include <utility> +#include <cm/string_view> +#include <cmext/string_view> + #include "cmExportSet.h" #include "cmFileSet.h" #include "cmGeneratedFileStream.h" @@ -18,6 +21,7 @@ #include "cmInstallTargetGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmStateTypes.h" @@ -162,10 +166,20 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) this->LoadConfigFiles(os); + bool result = true; + + this->GenerateCxxModuleInformation(os); + if (requiresConfigFiles) { + for (std::string const& c : this->Configurations) { + if (!this->GenerateImportCxxModuleConfigTargetInclusion(c)) { + result = false; + } + } + } + this->CleanupTemporaryVariables(os); this->GenerateImportedFileCheckLoop(os); - bool result = true; // Generate an import file for each configuration. // Don't do this if we only export INTERFACE_LIBRARY targets. if (requiresConfigFiles) { @@ -422,7 +436,10 @@ void cmExportInstallFileGenerator::SetImportLocationProperty( // Append the installed file name. if (target->IsAppBundleOnApple()) { value += cmInstallTargetGenerator::GetInstallFilename(target, config); - value += ".app/Contents/MacOS/"; + value += ".app/"; + if (!target->Makefile->PlatformIsAppleEmbedded()) { + value += "Contents/MacOS/"; + } value += cmInstallTargetGenerator::GetInstallFilename(target, config); } else { value += cmInstallTargetGenerator::GetInstallFilename( @@ -562,6 +579,21 @@ std::string cmExportInstallFileGenerator::GetFileSetDirectories( cge->Evaluate(gte->LocalGenerator, config, gte), cmOutputConverter::WrapQuotes::NoWrap)); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (cge->GetHadContextSensitiveCondition() && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) { resultVector.push_back( cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\"")); @@ -610,6 +642,21 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles( std::any_of(fileEntries.begin(), fileEntries.end(), EntryIsContextSensitive); + auto const& type = fileSet->GetType(); + // C++ modules do not support interface file sets which are dependent upon + // the configuration. + if (contextSensitive && + (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) { + auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); + std::ostringstream e; + e << "The \"" << gte->GetName() << "\" target's interface file set \"" + << fileSet->GetName() << "\" of type \"" << type + << "\" contains context-sensitive base file entries which is not " + "supported."; + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + return std::string{}; + } + for (auto const& it : files) { auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/'); for (auto const& filename : it.second) { @@ -635,3 +682,65 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles( return cmJoin(resultVector, " "); } + +std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const +{ + return IEGen->GetCxxModuleDirectory(); +} + +void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation( + std::ostream& os) const +{ + // Now load per-configuration properties for them. + /* clang-format off */ + os << "# Load information for each installed configuration.\n" + "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-*.cmake\")\n" + "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n" + " include(\"${_cmake_cxx_module_include}\")\n" + "endforeach()\n" + "unset(_cmake_cxx_module_include)\n" + "unset(_cmake_cxx_module_includes)\n"; + /* clang-format on */ +} + +bool cmExportInstallFileGenerator:: + GenerateImportCxxModuleConfigTargetInclusion(std::string const& config) +{ + auto cxx_modules_dirname = this->GetCxxModulesDirectory(); + if (cxx_modules_dirname.empty()) { + return true; + } + + std::string filename_config = config; + if (filename_config.empty()) { + filename_config = "noconfig"; + } + + std::string const dest = + cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/'); + std::string fileName = + cmStrCat(dest, "cxx-modules-", filename_config, ".cmake"); + + cmGeneratedFileStream os(fileName, true); + if (!os) { + std::string se = cmSystemTools::GetLastSystemError(); + std::ostringstream e; + e << "cannot write to file \"" << fileName << "\": " << se; + cmSystemTools::Error(e.str()); + return false; + } + os.SetCopyIfDifferent(true); + + // Record this per-config import file. + this->ConfigCxxModuleFiles[config] = fileName; + + auto& prop_files = this->ConfigCxxModuleTargetFiles[config]; + for (auto const* tgt : this->ExportedTargets) { + auto prop_filename = cmStrCat("target-", tgt->GetExportName(), '-', + filename_config, ".cmake"); + prop_files.emplace_back(cmStrCat(dest, prop_filename)); + os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n"; + } + + return true; +} diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h index 86fb505..e073a31 100644 --- a/Source/cmExportInstallFileGenerator.h +++ b/Source/cmExportInstallFileGenerator.h @@ -50,6 +50,23 @@ public: return this->ConfigImportFiles; } + /** Get the per-config C++ module file generated for each configuration. + This maps from the configuration name to the file temporary location + for installation. */ + std::map<std::string, std::string> const& GetConfigCxxModuleFiles() + { + return this->ConfigCxxModuleFiles; + } + + /** Get the per-config C++ module file generated for each configuration. + This maps from the configuration name to the file temporary location + for installation for each target in the export set. */ + std::map<std::string, std::vector<std::string>> const& + GetConfigCxxModuleTargetFiles() + { + return this->ConfigCxxModuleTargetFiles; + } + /** Compute the globbing expression used to load per-config import files from the main file. */ std::string GetConfigImportFileGlob(); @@ -100,8 +117,16 @@ protected: std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* te) override; + std::string GetCxxModulesDirectory() const override; + void GenerateCxxModuleConfigInformation(std::ostream&) const override; + bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&); + cmInstallExportGenerator* IEGen; // The import file generated for each configuration. std::map<std::string, std::string> ConfigImportFiles; + // The C++ module property file generated for each configuration. + std::map<std::string, std::string> ConfigCxxModuleFiles; + // The C++ module property target files generated for each configuration. + std::map<std::string, std::vector<std::string>> ConfigCxxModuleTargetFiles; }; diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h index 1dd8a20..5c34fad 100644 --- a/Source/cmExportTryCompileFileGenerator.h +++ b/Source/cmExportTryCompileFileGenerator.h @@ -55,6 +55,9 @@ protected: std::string GetFileSetFiles(cmGeneratorTarget* target, cmFileSet* fileSet, cmTargetExport* te) override; + std::string GetCxxModulesDirectory() const override { return {}; } + void GenerateCxxModuleConfigInformation(std::ostream&) const override {} + private: std::string FindTargets(const std::string& prop, const cmGeneratorTarget* tgt, diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index dd0540c..0581802 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -27,6 +27,7 @@ #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" +#include "cmInstallCxxModuleBmiGenerator.h" #include "cmInstallDirectoryGenerator.h" #include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" @@ -1092,6 +1093,21 @@ Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen) if (installFileSet->GetOptional()) { installer["isOptional"] = true; } + } else if (auto* cxxModuleBmi = + dynamic_cast<cmInstallCxxModuleBmiGenerator*>(gen)) { + installer["type"] = "cxxModuleBmi"; + installer["destination"] = cxxModuleBmi->GetDestination(this->Config); + + auto const* target = cxxModuleBmi->GetTarget(); + installer["cxxModuleBmiTarget"] = Json::objectValue; + installer["cxxModuleBmiTarget"]["id"] = TargetId(target, this->TopBuild); + installer["cxxModuleBmiTarget"]["index"] = this->TargetIndexMap[target]; + + // FIXME: Parse FilePermissions. + // FIXME: Parse MessageLevel. + if (cxxModuleBmi->GetOptional()) { + installer["isOptional"] = true; + } } // Add fields common to all install generators. diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 3c234a6..fe38db5 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -28,8 +28,8 @@ #include "cm_sys_stat.h" -#include "cmAlgorithms.h" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmCMakePath.h" #include "cmCryptoHash.h" #include "cmELF.h" @@ -172,7 +172,8 @@ bool HandleReadCommand(std::vector<std::string> const& args, .Bind("LIMIT"_s, &Arguments::Limit) .Bind("HEX"_s, &Arguments::Hex); - Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3)); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3), + /*unparsedArguments=*/nullptr); std::string fileName = fileNameArg; if (!cmsys::SystemTools::FileIsFullPath(fileName)) { @@ -959,42 +960,34 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string oldRPath; - std::string newRPath; + cm::optional<std::string> oldRPath; + cm::optional<std::string> newRPath; bool removeEnvironmentRPath = false; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; - std::vector<std::string> parsedArgs; parser.Bind("FILE"_s, file) .Bind("OLD_RPATH"_s, oldRPath) .Bind("NEW_RPATH"_s, newRPath) .Bind("INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, removeEnvironmentRPath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_CHANGE given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_CHANGE \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_CHANGE not given FILE option."); return false; } - if (oldRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "OLD_RPATH") == - parsedArgs.end()) { + if (!oldRPath) { status.SetError("RPATH_CHANGE not given OLD_RPATH option."); return false; } - if (newRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == - parsedArgs.end()) { + if (!newRPath) { status.SetError("RPATH_CHANGE not given NEW_RPATH option."); return false; } @@ -1008,17 +1001,17 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, std::string emsg; bool changed; - if (!cmSystemTools::ChangeRPath(file, oldRPath, newRPath, + if (!cmSystemTools::ChangeRPath(file, *oldRPath, *newRPath, removeEnvironmentRPath, &emsg, &changed)) { status.SetError(cmStrCat("RPATH_CHANGE could not write new RPATH:\n ", - newRPath, "\nto the file:\n ", file, "\n", + *newRPath, "\nto the file:\n ", file, "\n", emsg)); success = false; } if (success) { if (changed) { std::string message = - cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"'); + cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"'); status.GetMakefile().DisplayStatus(message, -1); } ft.Store(file); @@ -1031,31 +1024,25 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string newRPath; + cm::optional<std::string> newRPath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; - std::vector<std::string> parsedArgs; parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError(cmStrCat("RPATH_SET given unrecognized argument \"", unknownArgs.front(), "\".")); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_SET \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_SET not given FILE option."); return false; } - if (newRPath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == - parsedArgs.end()) { + if (!newRPath) { status.SetError("RPATH_SET not given NEW_RPATH option."); return false; } @@ -1069,16 +1056,16 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args, std::string emsg; bool changed; - if (!cmSystemTools::SetRPath(file, newRPath, &emsg, &changed)) { + if (!cmSystemTools::SetRPath(file, *newRPath, &emsg, &changed)) { status.SetError(cmStrCat("RPATH_SET could not write new RPATH:\n ", - newRPath, "\nto the file:\n ", file, "\n", + *newRPath, "\nto the file:\n ", file, "\n", emsg)); success = false; } if (success) { if (changed) { std::string message = - cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"'); + cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"'); status.GetMakefile().DisplayStatus(message, -1); } ft.Store(file); @@ -1093,18 +1080,16 @@ bool HandleRPathRemoveCommand(std::vector<std::string> const& args, std::string file; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; parser.Bind("FILE"_s, file); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_REMOVE given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_REMOVE \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_REMOVE not given FILE option."); @@ -1141,31 +1126,25 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - std::string rpath; + cm::optional<std::string> rpath; cmArgumentParser<void> parser; std::vector<std::string> unknownArgs; - std::vector<std::string> missingArgs; - std::vector<std::string> parsedArgs; parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath); - parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, - &parsedArgs); + ArgumentParser::ParseResult parseResult = + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs); if (!unknownArgs.empty()) { status.SetError( cmStrCat("RPATH_CHECK given unknown argument ", unknownArgs.front())); return false; } - if (!missingArgs.empty()) { - status.SetError(cmStrCat("RPATH_CHECK \"", missingArgs.front(), - "\" argument not given value.")); - return false; + if (parseResult.MaybeReportError(status.GetMakefile())) { + return true; } if (file.empty()) { status.SetError("RPATH_CHECK not given FILE option."); return false; } - if (rpath.empty() && - std::find(parsedArgs.begin(), parsedArgs.end(), "RPATH") == - parsedArgs.end()) { + if (!rpath) { status.SetError("RPATH_CHECK not given RPATH option."); return false; } @@ -1174,7 +1153,7 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, // delete it. This is used during installation to re-install a file // if its RPath will change. if (cmSystemTools::FileExists(file, true) && - !cmSystemTools::CheckRPath(file, rpath)) { + !cmSystemTools::CheckRPath(file, *rpath)) { cmSystemTools::RemoveFile(file); } @@ -1203,7 +1182,8 @@ bool HandleReadElfCommand(std::vector<std::string> const& args, .Bind("RPATH"_s, &Arguments::RPath) .Bind("RUNPATH"_s, &Arguments::RunPath) .Bind("CAPTURE_ERROR"_s, &Arguments::Error); - Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2)); + Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2), + /*unparsedArguments=*/nullptr); if (!cmSystemTools::FileExists(fileNameArg, true)) { status.SetError(cmStrCat("READ_ELF given FILE \"", fileNameArg, @@ -1256,9 +1236,9 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string BaseDirectory; + cm::optional<std::string> BaseDirectory; bool ExpandTilde = false; }; static auto const parser = @@ -1267,22 +1247,18 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde); 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); + parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments); 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 (arguments.MaybeReportError(status.GetMakefile())) { + return true; } - if (parsedKeywords.empty()) { + if (!arguments.BaseDirectory) { arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); } @@ -1301,7 +1277,7 @@ bool HandleRealPathCommand(std::vector<std::string> const& args, } cmCMakePath path(input, cmCMakePath::auto_format); - path = path.Absolute(arguments.BaseDirectory).Normal(); + path = path.Absolute(*arguments.BaseDirectory).Normal(); auto realPath = cmSystemTools::GetRealPath(path.GenericString()); status.GetMakefile().AddDefinition(args[2], realPath); @@ -1944,7 +1920,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, std::string msg; std::string actualHash = hash->HashFile(file); if (actualHash == expectedHash) { - msg = cmStrCat("returning early; file already exists with expected ", + msg = cmStrCat("skipping download as file already exists with expected ", hashMatchMSG, '"'); if (!statusVar.empty()) { status.GetMakefile().AddDefinition(statusVar, cmStrCat(0, ";\"", msg)); @@ -2114,6 +2090,13 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, ::curl_global_cleanup(); + // Ensure requested curl logs are returned (especially in case of failure) + // + if (!logVar.empty()) { + chunkDebug.push_back(0); + status.GetMakefile().AddDefinition(logVar, chunkDebug.data()); + } + // Explicitly flush/close so we can measure the md5 accurately. // if (!file.empty()) { @@ -2156,11 +2139,6 @@ bool HandleDownloadCommand(std::vector<std::string> const& args, } } - if (!logVar.empty()) { - chunkDebug.push_back(0); - status.GetMakefile().AddDefinition(logVar, chunkDebug.data()); - } - return true; #else status.SetError("DOWNLOAD not supported by bootstrap cmake."); @@ -2501,17 +2479,18 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, return false; } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; - std::string Input; - std::string Content; - std::string Condition; - std::string Target; - std::string NewLineStyle; + cm::optional<std::string> Output; + cm::optional<std::string> Input; + cm::optional<std::string> Content; + cm::optional<std::string> Condition; + cm::optional<std::string> Target; + cm::optional<std::string> NewLineStyle; bool NoSourcePermissions = false; bool UseSourcePermissions = false; - std::vector<std::string> FilePermissions; + ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions; + std::vector<cm::string_view> ParsedKeywords; }; static auto const parser = @@ -2524,18 +2503,15 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, .Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions) .Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions) .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions) - .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle); + .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle) + .BindParsedKeywords(&Arguments::ParsedKeywords); std::vector<std::string> unparsedArguments; - std::vector<std::string> keywordsMissingValues; - std::vector<std::string> parsedKeywords; Arguments const arguments = - parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments, - &keywordsMissingValues, &parsedKeywords); + parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments); - if (!keywordsMissingValues.empty()) { - status.SetError("Incorrect arguments to GENERATE subcommand."); - return false; + if (arguments.MaybeReportError(status.GetMakefile())) { + return true; } if (!unparsedArguments.empty()) { @@ -2543,56 +2519,41 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, return false; } - bool mandatoryOptionsSpecified = false; - if (parsedKeywords.size() > 1) { - const bool outputOprionSpecified = parsedKeywords[0] == "OUTPUT"_s; - const bool inputOrContentSpecified = - parsedKeywords[1] == "INPUT"_s || parsedKeywords[1] == "CONTENT"_s; - if (outputOprionSpecified && inputOrContentSpecified) { - mandatoryOptionsSpecified = true; - } + if (!arguments.Output || arguments.ParsedKeywords[0] != "OUTPUT"_s) { + status.SetError("GENERATE requires OUTPUT as first option."); + return false; } - if (!mandatoryOptionsSpecified) { - status.SetError("Incorrect arguments to GENERATE subcommand."); + std::string const& output = *arguments.Output; + + if (!arguments.Input && !arguments.Content) { + status.SetError("GENERATE requires INPUT or CONTENT option."); return false; } + const bool inputIsContent = arguments.ParsedKeywords[1] == "CONTENT"_s; + if (!inputIsContent && arguments.ParsedKeywords[1] == "INPUT") { + status.SetError("Unknown argument to GENERATE subcommand."); + } + std::string const& input = + inputIsContent ? *arguments.Content : *arguments.Input; - const bool conditionOptionSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), "CONDITION"_s) != - parsedKeywords.end(); - if (conditionOptionSpecified && arguments.Condition.empty()) { + if (arguments.Condition && arguments.Condition->empty()) { status.SetError("CONDITION of sub-command GENERATE must not be empty " "if specified."); return false; } + std::string const& condition = + arguments.Condition ? *arguments.Condition : std::string(); - const bool targetOptionSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), "TARGET"_s) != - parsedKeywords.end(); - if (targetOptionSpecified && arguments.Target.empty()) { + if (arguments.Target && arguments.Target->empty()) { status.SetError("TARGET of sub-command GENERATE must not be empty " "if specified."); return false; } + std::string const& target = + arguments.Target ? *arguments.Target : std::string(); - const bool outputOptionSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), "OUTPUT"_s) != - parsedKeywords.end(); - if (outputOptionSpecified && parsedKeywords[0] != "OUTPUT"_s) { - status.SetError("Incorrect arguments to GENERATE subcommand."); - return false; - } - - const bool inputIsContent = parsedKeywords[1] != "INPUT"_s; - if (inputIsContent && parsedKeywords[1] != "CONTENT") { - status.SetError("Unknown argument to GENERATE subcommand."); - } - - const bool newLineStyleSpecified = - std::find(parsedKeywords.begin(), parsedKeywords.end(), - "NEWLINE_STYLE"_s) != parsedKeywords.end(); cmNewLineStyle newLineStyle; - if (newLineStyleSpecified) { + if (arguments.NewLineStyle) { std::string errorMessage; if (!newLineStyle.ReadFromArguments(args, errorMessage)) { status.SetError(cmStrCat("GENERATE ", errorMessage)); @@ -2600,11 +2561,6 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, } } - std::string input = arguments.Input; - if (inputIsContent) { - input = arguments.Content; - } - if (arguments.NoSourcePermissions && arguments.UseSourcePermissions) { status.SetError("given both NO_SOURCE_PERMISSIONS and " "USE_SOURCE_PERMISSIONS. Only one option allowed."); @@ -2662,8 +2618,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args, } } - AddEvaluationFile(input, arguments.Target, arguments.Output, - arguments.Condition, inputIsContent, + AddEvaluationFile(input, target, output, condition, inputIsContent, newLineStyle.GetCharacters(), permissions, status); return true; } @@ -2842,7 +2797,11 @@ bool HandleTimestampCommand(std::vector<std::string> const& args, unsigned int argsIndex = 1; - const std::string& filename = args[argsIndex++]; + std::string filename = args[argsIndex++]; + if (!cmsys::SystemTools::FileIsFullPath(filename)) { + filename = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', + filename); + } const std::string& outputVariable = args[argsIndex++]; @@ -3003,11 +2962,23 @@ bool HandleCreateLinkCommand(std::vector<std::string> const& args, // Check if the command requires a symbolic link. if (arguments.Symbolic) { - completed = static_cast<bool>( - cmSystemTools::CreateSymlink(fileName, newFileName, &result)); + cmsys::Status linked = + cmSystemTools::CreateSymlinkQuietly(fileName, newFileName); + if (linked) { + completed = true; + } else { + result = cmStrCat("failed to create symbolic link '", newFileName, + "': ", linked.GetString()); + } } else { - completed = static_cast<bool>( - cmSystemTools::CreateLink(fileName, newFileName, &result)); + cmsys::Status linked = + cmSystemTools::CreateLinkQuietly(fileName, newFileName); + if (linked) { + completed = true; + } else { + result = cmStrCat("failed to create link '", newFileName, + "': ", linked.GetString()); + } } // Check if copy-on-error is enabled in the arguments. @@ -3066,24 +3037,25 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, "\n ]])"); } - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string ResolvedDependenciesVar; std::string UnresolvedDependenciesVar; std::string ConflictingDependenciesPrefix; std::string RPathPrefix; std::string BundleExecutable; - std::vector<std::string> Executables; - std::vector<std::string> Libraries; - std::vector<std::string> Directories; - std::vector<std::string> Modules; - std::vector<std::string> PreIncludeRegexes; - std::vector<std::string> PreExcludeRegexes; - std::vector<std::string> PostIncludeRegexes; - std::vector<std::string> PostExcludeRegexes; - std::vector<std::string> PostIncludeFiles; - std::vector<std::string> PostExcludeFiles; - std::vector<std::string> PostExcludeFilesStrict; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Executables; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Libraries; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Modules; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> + PostExcludeFilesStrict; }; static auto const parser = @@ -3108,10 +3080,8 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, .Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3119,26 +3089,9 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, return false; } - const std::vector<std::string> LIST_ARGS = { - "DIRECTORIES", - "EXECUTABLES", - "LIBRARIES", - "MODULES", - "POST_EXCLUDE_FILES", - "POST_EXCLUDE_FILES_STRICT", - "POST_EXCLUDE_REGEXES", - "POST_INCLUDE_FILES", - "POST_INCLUDE_REGEXES", - "PRE_EXCLUDE_REGEXES", - "PRE_INCLUDE_REGEXES", - }; - auto kwbegin = keywordsMissingValues.cbegin(); - auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); - if (kwend != kwbegin) { - status.SetError(cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } cmRuntimeDependencyArchive archive( @@ -3238,13 +3191,14 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, bool HandleConfigureCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::string Output; - std::string Content; + cm::optional<std::string> Output; + cm::optional<std::string> Content; bool EscapeQuotes = false; bool AtOnly = false; - std::string NewlineStyle; + // "NEWLINE_STYLE" requires one value, but we use a custom check below. + ArgumentParser::Maybe<std::string> NewlineStyle; }; static auto const parser = @@ -3256,11 +3210,8 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingArguments; - std::vector<std::string> parsedKeywords; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingArguments, &parsedKeywords); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { @@ -3270,28 +3221,20 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, return false; } - std::vector<std::string> mandatoryOptions{ "OUTPUT", "CONTENT" }; - for (auto const& e : mandatoryOptions) { - const bool optionHasNoValue = - std::find(keywordsMissingArguments.begin(), - keywordsMissingArguments.end(), - e) != keywordsMissingArguments.end(); - if (optionHasNoValue) { - status.SetError(cmStrCat("CONFIGURE ", e, " option needs a value.")); - cmSystemTools::SetFatalErrorOccurred(); - return false; - } + if (parsedArgs.MaybeReportError(status.GetMakefile())) { + cmSystemTools::SetFatalErrorOccurred(); + return true; } - for (auto const& e : mandatoryOptions) { - const bool optionGiven = - std::find(parsedKeywords.begin(), parsedKeywords.end(), e) != - parsedKeywords.end(); - if (!optionGiven) { - status.SetError(cmStrCat("CONFIGURE ", e, " option is mandatory.")); - cmSystemTools::SetFatalErrorOccurred(); - return false; - } + if (!parsedArgs.Output) { + status.SetError("CONFIGURE OUTPUT option is mandatory."); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + if (!parsedArgs.Content) { + status.SetError("CONFIGURE CONTENT option is mandatory."); + cmSystemTools::SetFatalErrorOccurred(); + return false; } std::string errorMessage; @@ -3303,7 +3246,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, // Check for generator expressions std::string outputFile = cmSystemTools::CollapseFullPath( - parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory()); + *parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory()); std::string::size_type pos = outputFile.find_first_of("<>"); if (pos != std::string::npos) { @@ -3352,7 +3295,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, fout.SetCopyIfDifferent(true); // copy input to output and expand variables from input at the same time - std::stringstream sin(parsedArgs.Content, std::ios::in); + std::stringstream sin(*parsedArgs.Content, std::ios::in); std::string inLine; std::string outLine; bool hasNewLine = false; @@ -3375,15 +3318,19 @@ bool HandleConfigureCommand(std::vector<std::string> const& args, bool HandleArchiveCreateCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string Output; std::string Format; std::string Compression; std::string CompressionLevel; - std::string MTime; + // "MTIME" should require one value, but it has long been accidentally + // accepted without one and treated as if an empty value were given. + // Fixing this would require a policy. + ArgumentParser::Maybe<std::string> MTime; bool Verbose = false; - std::vector<std::string> Paths; + // "PATHS" requires at least one value, but use a custom check below. + ArgumentParser::MaybeEmpty<std::vector<std::string>> Paths; }; static auto const parser = @@ -3397,10 +3344,8 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, .Bind("PATHS"_s, &Arguments::Paths); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3408,16 +3353,9 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, return false; } - 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) { - status.SetError(cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } const char* knownFormats[] = { @@ -3506,13 +3444,13 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args, bool HandleArchiveExtractCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { std::string Input; bool Verbose = false; bool ListOnly = false; std::string Destination; - std::vector<std::string> Patterns; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Patterns; bool Touch = false; }; @@ -3525,10 +3463,8 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, .Bind("TOUCH"_s, &Arguments::Touch); std::vector<std::string> unrecognizedArguments; - std::vector<std::string> keywordsMissingValues; auto parsedArgs = - parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments, - &keywordsMissingValues); + parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments); auto argIt = unrecognizedArguments.begin(); if (argIt != unrecognizedArguments.end()) { status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\"")); @@ -3536,15 +3472,9 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return false; } - const std::vector<std::string> LIST_ARGS = { "INPUT", "DESTINATION", - "PATTERNS" }; - auto kwbegin = keywordsMissingValues.cbegin(); - auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS); - if (kwend != kwbegin) { - status.SetError(cmStrCat("Keywords missing values:\n ", - cmJoin(cmMakeRange(kwbegin, kwend), "\n "))); + if (parsedArgs.MaybeReportError(status.GetMakefile())) { cmSystemTools::SetFatalErrorOccurred(); - return false; + return true; } std::string inFile = parsedArgs.Input; @@ -3599,10 +3529,15 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args, return true; } -bool ValidateAndConvertPermissions(const std::vector<std::string>& permissions, - mode_t& perms, cmExecutionStatus& status) +bool ValidateAndConvertPermissions( + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> const& + permissions, + mode_t& perms, cmExecutionStatus& status) { - for (const auto& i : permissions) { + if (!permissions) { + return true; + } + for (const auto& i : *permissions) { if (!cmFSPermissions::stringToModeT(i, perms)) { status.SetError(i + " is an invalid permission specifier"); cmSystemTools::SetFatalErrorOccurred(); @@ -3634,11 +3569,14 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, globber.SetRecurse(recurse); globber.SetRecurseListDirs(recurse); - struct Arguments + struct Arguments : public ArgumentParser::ParseResult { - std::vector<std::string> Permissions; - std::vector<std::string> FilePermissions; - std::vector<std::string> DirectoryPermissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + Permissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + FilePermissions; + cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> + DirectoryPermissions; }; static auto const parser = @@ -3648,21 +3586,20 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, .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); + Arguments parsedArgs = + parser.Parse(cmMakeRange(args).advance(1), &pathEntries); // check validity of arguments - if (parsedArgs.Permissions.empty() && parsedArgs.FilePermissions.empty() && - parsedArgs.DirectoryPermissions.empty()) // no permissions given + if (!parsedArgs.Permissions && !parsedArgs.FilePermissions && + !parsedArgs.DirectoryPermissions) // no permissions given { status.SetError("No permissions given"); cmSystemTools::SetFatalErrorOccurred(); return false; } - if (!parsedArgs.Permissions.empty() && !parsedArgs.FilePermissions.empty() && - !parsedArgs.DirectoryPermissions.empty()) // all keywords are used + if (parsedArgs.Permissions && parsedArgs.FilePermissions && + parsedArgs.DirectoryPermissions) // all keywords are used { status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or " "DIRECTORY_PERMISSIONS from the invocation"); @@ -3670,12 +3607,9 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, return false; } - if (!keywordsMissingValues.empty()) { - for (const auto& i : keywordsMissingValues) { - status.SetError(i + " is not given any arguments"); - cmSystemTools::SetFatalErrorOccurred(); - } - return false; + if (parsedArgs.MaybeReportError(status.GetMakefile())) { + cmSystemTools::SetFatalErrorOccurred(); + return true; } // validate permissions @@ -3719,7 +3653,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, if (cmSystemTools::FileExists(i, true)) { bool success = true; const mode_t& filePermissions = - parsedArgs.FilePermissions.empty() ? perms : fperms; + parsedArgs.FilePermissions ? fperms : perms; if (filePermissions) { success = SetPermissions(i, filePermissions, status); } @@ -3731,7 +3665,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse, else if (cmSystemTools::FileIsDirectory(i)) { bool success = true; const mode_t& directoryPermissions = - parsedArgs.DirectoryPermissions.empty() ? perms : dperms; + parsedArgs.DirectoryPermissions ? dperms : perms; if (directoryPermissions) { success = SetPermissions(i, directoryPermissions, status); } diff --git a/Source/cmFileCopier.cxx b/Source/cmFileCopier.cxx index 1667807..ef55abf 100644 --- a/Source/cmFileCopier.cxx +++ b/Source/cmFileCopier.cxx @@ -15,7 +15,11 @@ #include "cmValue.h" #ifdef _WIN32 +# include <winerror.h> + # include "cmsys/FStream.hxx" +#else +# include <cerrno> #endif #include <cstring> @@ -504,11 +508,12 @@ bool cmFileCopier::InstallSymlinkChain(std::string& fromFile, cmSystemTools::RemoveFile(toFile); cmSystemTools::MakeDirectory(toFilePath); - if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) { - std::ostringstream e; - e << this->Name << " cannot create symlink \"" << toFile - << "\": " << cmSystemTools::GetLastSystemError() << "."; - this->Status.SetError(e.str()); + cmsys::Status status = + cmSystemTools::CreateSymlinkQuietly(symlinkTarget, toFile); + if (!status) { + std::string e = cmStrCat(this->Name, " cannot create symlink\n ", + toFile, "\nbecause: ", status.GetString()); + this->Status.SetError(e); return false; } } @@ -557,12 +562,24 @@ bool cmFileCopier::InstallSymlink(const std::string& fromFile, cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile)); // Create the symlink. - if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) { - std::ostringstream e; - e << this->Name << " cannot duplicate symlink \"" << fromFile - << "\" at \"" << toFile - << "\": " << cmSystemTools::GetLastSystemError() << "."; - this->Status.SetError(e.str()); + cmsys::Status status = + cmSystemTools::CreateSymlinkQuietly(symlinkTarget, toFile); + if (!status) { +#ifdef _WIN32 + bool const errorFileExists = status.GetWindows() == ERROR_FILE_EXISTS; +#else + bool const errorFileExists = status.GetPOSIX() == EEXIST; +#endif + std::string reason; + if (errorFileExists && cmSystemTools::FileIsDirectory(toFile)) { + reason = "A directory already exists at that location"; + } else { + reason = status.GetString(); + } + std::string e = + cmStrCat(this->Name, " cannot duplicate symlink\n ", fromFile, + "\nat\n ", toFile, "\nbecause: ", reason); + this->Status.SetError(e); return false; } } @@ -630,7 +647,10 @@ bool cmFileCopier::InstallDirectory(const std::string& source, { // Inform the user about this directory installation. this->ReportCopy(destination, TypeDir, - !cmSystemTools::FileIsDirectory(destination)); + !( // Report "Up-to-date:" for existing directories, + // but not symlinks to them. + cmSystemTools::FileIsDirectory(destination) && + !cmSystemTools::FileIsSymlink(destination))); // check if default dir creation permissions were set mode_t default_dir_mode_v = 0; diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 1da995a..b8d345f 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -2,15 +2,20 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmFindBase.h" +#include <algorithm> #include <cstddef> #include <deque> +#include <functional> #include <map> #include <utility> #include <cm/optional> #include <cmext/algorithm> +#include <cmext/string_view> #include "cmCMakePath.h" +#include "cmExecutionStatus.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" @@ -24,8 +29,6 @@ #include "cmWindowsRegistry.h" #include "cmake.h" -class cmExecutionStatus; - cmFindBase::cmFindBase(std::string findCommandName, cmExecutionStatus& status) : cmFindCommon(status) , FindCommandName(std::move(findCommandName)) @@ -138,6 +141,31 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn) cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[j])); return false; } + } else if (args[j] == "VALIDATOR") { + if (++j == args.size()) { + this->SetError("missing required argument for \"VALIDATOR\""); + return false; + } + auto command = this->Makefile->GetState()->GetCommand(args[j]); + if (command == nullptr) { + this->SetError(cmStrCat( + "command specified for \"VALIDATOR\" is undefined: ", args[j], '.')); + return false; + } + // ensure a macro is not specified as validator + const auto& validatorName = args[j]; + auto macros = cmExpandedList(this->Makefile->GetProperty("MACROS")); + if (std::find_if(macros.begin(), macros.end(), + [&validatorName](const std::string& item) { + return cmSystemTools::Strucmp(validatorName.c_str(), + item.c_str()) == 0; + }) != macros.end()) { + this->SetError(cmStrCat( + "command specified for \"VALIDATOR\" is not a function: ", args[j], + '.')); + return false; + } + this->ValidatorName = args[j]; } else if (this->CheckCommonArgument(args[j])) { doing = DoingNone; } else { @@ -188,6 +216,36 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn) return true; } +bool cmFindBase::Validate(const std::string& path) const +{ + if (this->ValidatorName.empty()) { + return true; + } + + // The validator command will be executed in an isolated scope. + cmMakefile::ScopePushPop varScope(this->Makefile); + cmMakefile::PolicyPushPop polScope(this->Makefile); + static_cast<void>(varScope); + static_cast<void>(polScope); + + auto resultName = + cmStrCat("CMAKE_"_s, cmSystemTools::UpperCase(this->FindCommandName), + "_VALIDATOR_STATUS"_s); + + this->Makefile->AddDefinitionBool(resultName, true); + + cmListFileFunction validator( + this->ValidatorName, 0, 0, + { cmListFileArgument(resultName, cmListFileArgument::Unquoted, 0), + cmListFileArgument(path, cmListFileArgument::Quoted, 0) }); + cmExecutionStatus status(*this->Makefile); + + if (this->Makefile->ExecuteCommand(validator, status)) { + return this->Makefile->GetDefinition(resultName).IsOn(); + } + return false; +} + void cmFindBase::ExpandPaths() { if (!this->NoDefaultPath) { diff --git a/Source/cmFindBase.h b/Source/cmFindBase.h index d197424..75d9a6d 100644 --- a/Source/cmFindBase.h +++ b/Source/cmFindBase.h @@ -31,6 +31,11 @@ public: */ virtual bool ParseArguments(std::vector<std::string> const& args); + /** + * To check validity of a found path using user's validator, if any + */ + bool Validate(const std::string& path) const; + protected: friend class cmFindBaseDebugState; void ExpandPaths(); @@ -63,6 +68,8 @@ protected: bool Required = false; + std::string ValidatorName; + private: // Add pieces of the search. void FillPackageRootPath(); diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx index 1c4039b..6296a60 100644 --- a/Source/cmFindLibraryCommand.cxx +++ b/Source/cmFindLibraryCommand.cxx @@ -192,6 +192,7 @@ struct cmFindLibraryHelper // Context information. cmMakefile* Makefile; + cmFindBase const* FindBase; cmGlobalGenerator* GG; // List of valid prefixes and suffixes. @@ -239,6 +240,11 @@ struct cmFindLibraryHelper bool CheckDirectory(std::string const& path); bool CheckDirectoryForName(std::string const& path, Name& name); + bool Validate(const std::string& path) const + { + return this->FindBase->Validate(path); + } + cmFindBaseDebugState DebugSearches; void DebugLibraryFailed(std::string const& name, std::string const& path) @@ -291,6 +297,7 @@ std::string const& get_suffixes(cmMakefile* mf) cmFindLibraryHelper::cmFindLibraryHelper(std::string debugName, cmMakefile* mf, cmFindBase const* base) : Makefile(mf) + , FindBase(base) , DebugMode(base->DebugModeEnabled()) , DebugSearches(std::move(debugName), base) { @@ -416,10 +423,13 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path, if (!exists) { this->DebugLibraryFailed(name.Raw, path); } else { - this->DebugLibraryFound(name.Raw, path); - this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath); - cmSystemTools::ConvertToUnixSlashes(this->BestPath); - return true; + auto testPath = cmSystemTools::CollapseFullPath(this->TestPath); + if (this->Validate(testPath)) { + this->DebugLibraryFound(name.Raw, path); + this->BestPath = testPath; + return true; + } + this->DebugLibraryFailed(name.Raw, path); } } @@ -443,8 +453,11 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path, this->TestPath = cmStrCat(path, origName); // Make sure the path is readable and is not a directory. if (cmSystemTools::FileExists(this->TestPath, true)) { - this->DebugLibraryFound(name.Raw, dir); + if (!this->Validate(cmSystemTools::CollapseFullPath(this->TestPath))) { + continue; + } + this->DebugLibraryFound(name.Raw, dir); // This is a matching file. Check if it is better than the // best name found so far. Earlier prefixes are preferred, // followed by earlier suffixes. For OpenBSD, shared library @@ -541,7 +554,10 @@ std::string cmFindLibraryCommand::FindFrameworkLibraryNamesPerDir() for (std::string const& n : this->Names) { fwPath = cmStrCat(d, n, ".framework"); if (cmSystemTools::FileIsDirectory(fwPath)) { - return cmSystemTools::CollapseFullPath(fwPath); + auto finalPath = cmSystemTools::CollapseFullPath(fwPath); + if (this->Validate(finalPath)) { + return finalPath; + } } } } @@ -558,7 +574,10 @@ std::string cmFindLibraryCommand::FindFrameworkLibraryDirsPerName() for (std::string const& d : this->SearchPaths) { fwPath = cmStrCat(d, n, ".framework"); if (cmSystemTools::FileIsDirectory(fwPath)) { - return cmSystemTools::CollapseFullPath(fwPath); + auto finalPath = cmSystemTools::CollapseFullPath(fwPath); + if (this->Validate(finalPath)) { + return finalPath; + } } } } diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 4ad9124..3f8378b 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -5,7 +5,6 @@ #include <algorithm> #include <cassert> #include <cstdio> -#include <cstring> #include <deque> #include <functional> #include <iterator> @@ -43,8 +42,405 @@ # include <StorageDefs.h> #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +# include <windows.h> +// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx +# if !defined(KEY_WOW64_32KEY) +# define KEY_WOW64_32KEY 0x0200 +# endif +# if !defined(KEY_WOW64_64KEY) +# define KEY_WOW64_64KEY 0x0100 +# endif +#endif + class cmExecutionStatus; -class cmFileList; + +namespace { + +template <template <typename> class Op> +struct StrverscmpOp +{ + bool operator()(const std::string& lhs, const std::string& rhs) const + { + return Op<int>()(cmSystemTools::strverscmp(lhs, rhs), 0); + } +}; + +std::size_t collectPathsForDebug(std::string& buffer, + cmSearchPath const& searchPath, + std::size_t const startIndex = 0) +{ + const auto& paths = searchPath.GetPaths(); + if (paths.empty()) { + buffer += " none\n"; + return 0; + } + for (auto i = startIndex; i < paths.size(); i++) { + buffer += " " + paths[i].Path + "\n"; + } + return paths.size(); +} + +#if !(defined(_WIN32) && !defined(__CYGWIN__)) +class cmFindPackageCommandHoldFile +{ + const char* File; + +public: + cmFindPackageCommandHoldFile(const char* const f) + : File(f) + { + } + ~cmFindPackageCommandHoldFile() + { + if (this->File) { + cmSystemTools::RemoveFile(this->File); + } + } + cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete; + cmFindPackageCommandHoldFile& operator=( + const cmFindPackageCommandHoldFile&) = delete; + void Release() { this->File = nullptr; } +}; +#endif + +bool isDirentryToIgnore(const char* const fname) +{ + assert(fname != nullptr); + assert(fname[0] != 0); + return fname[0] == '.' && + (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0)); +} + +class cmAppendPathSegmentGenerator +{ +public: + cmAppendPathSegmentGenerator(cm::string_view dirName) + : DirName{ dirName } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->NeedReset) { + return {}; + } + this->NeedReset = true; + return cmStrCat(parent, '/', this->DirName); + } + + void Reset() { this->NeedReset = false; } + +private: + const cm::string_view DirName; + bool NeedReset = false; +}; + +class cmEnumPathSegmentsGenerator +{ +public: + cmEnumPathSegmentsGenerator(const std::vector<cm::string_view>& init) + : Names{ init } + , Current{ this->Names.get().cbegin() } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->Current != this->Names.get().cend()) { + return cmStrCat(parent, '/', *this->Current++); + } + return {}; + } + + void Reset() { this->Current = this->Names.get().cbegin(); } + +private: + std::reference_wrapper<const std::vector<cm::string_view>> Names; + std::vector<cm::string_view>::const_iterator Current; +}; + +class cmCaseInsensitiveDirectoryListGenerator +{ +public: + cmCaseInsensitiveDirectoryListGenerator(cm::string_view name) + : DirectoryLister{} + , DirName{ name } + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (!this->Loaded) { + this->CurrentIdx = 0ul; + this->Loaded = true; + if (!this->DirectoryLister.Load(parent)) { + return {}; + } + } + + while (this->CurrentIdx < this->DirectoryLister.GetNumberOfFiles()) { + const char* const fname = + this->DirectoryLister.GetFile(this->CurrentIdx++); + if (isDirentryToIgnore(fname)) { + continue; + } + if (cmsysString_strcasecmp(fname, this->DirName.data()) == 0) { + auto candidate = cmStrCat(parent, '/', fname); + if (cmSystemTools::FileIsDirectory(candidate)) { + return candidate; + } + } + } + return {}; + } + + void Reset() { this->Loaded = false; } + +private: + cmsys::Directory DirectoryLister; + const cm::string_view DirName; + unsigned long CurrentIdx = 0ul; + bool Loaded = false; +}; + +class cmDirectoryListGenerator +{ +public: + cmDirectoryListGenerator(std::vector<std::string> const& names) + : Names{ names } + , Matches{} + , Current{ this->Matches.cbegin() } + { + } + virtual ~cmDirectoryListGenerator() = default; + + std::string GetNextCandidate(const std::string& parent) + { + // Construct a list of matches if not yet + if (this->Matches.empty()) { + cmsys::Directory directoryLister; + // ALERT `Directory::Load()` keeps only names + // internally and LOST entry type from `dirent`. + // So, `Directory::FileIsDirectory` gonna use + // `SystemTools::FileIsDirectory()` and waste a syscall. + // TODO Need to enhance the `Directory` class. + directoryLister.Load(parent); + + // ATTENTION Is it guaranteed that first two entries are + // `.` and `..`? + // TODO If so, just start with index 2 and drop the + // `isDirentryToIgnore(i)` condition to check. + for (auto i = 0ul; i < directoryLister.GetNumberOfFiles(); ++i) { + const char* const fname = directoryLister.GetFile(i); + if (isDirentryToIgnore(fname)) { + continue; + } + + for (const auto& n : this->Names.get()) { + // NOTE Customization point for `cmMacProjectDirectoryListGenerator` + const auto name = this->TransformNameBeforeCmp(n); + // Skip entries that don't match and non-directories. + // ATTENTION BTW, original code also didn't check if it's a symlink + // to a directory! + const auto equal = + (cmsysString_strncasecmp(fname, name.c_str(), name.length()) == 0); + if (equal && directoryLister.FileIsDirectory(i)) { + this->Matches.emplace_back(fname); + } + } + } + // NOTE Customization point for `cmProjectDirectoryListGenerator` + this->OnMatchesLoaded(); + + this->Current = this->Matches.cbegin(); + } + + if (this->Current != this->Matches.cend()) { + auto candidate = cmStrCat(parent, '/', *this->Current++); + return candidate; + } + + return {}; + } + + void Reset() + { + this->Matches.clear(); + this->Current = this->Matches.cbegin(); + } + +protected: + virtual void OnMatchesLoaded() {} + virtual std::string TransformNameBeforeCmp(std::string same) { return same; } + + std::reference_wrapper<const std::vector<std::string>> Names; + std::vector<std::string> Matches; + std::vector<std::string>::const_iterator Current; +}; + +class cmProjectDirectoryListGenerator : public cmDirectoryListGenerator +{ +public: + cmProjectDirectoryListGenerator(std::vector<std::string> const& names, + cmFindPackageCommand::SortOrderType so, + cmFindPackageCommand::SortDirectionType sd) + : cmDirectoryListGenerator{ names } + , SortOrder{ so } + , SortDirection{ sd } + { + } + +protected: + void OnMatchesLoaded() override + { + // check if there is a specific sorting order to perform + if (this->SortOrder != cmFindPackageCommand::None) { + cmFindPackageCommand::Sort(this->Matches.begin(), this->Matches.end(), + this->SortOrder, this->SortDirection); + } + } + +private: + // sort parameters + const cmFindPackageCommand::SortOrderType SortOrder; + const cmFindPackageCommand::SortDirectionType SortDirection; +}; + +class cmMacProjectDirectoryListGenerator : public cmDirectoryListGenerator +{ +public: + cmMacProjectDirectoryListGenerator(const std::vector<std::string>& names, + cm::string_view ext) + : cmDirectoryListGenerator{ names } + , Extension{ ext } + { + } + +protected: + std::string TransformNameBeforeCmp(std::string name) override + { + return cmStrCat(name, this->Extension); + } + +private: + const cm::string_view Extension; +}; + +class cmFileListGeneratorGlob +{ +public: + cmFileListGeneratorGlob(cm::string_view pattern) + : Pattern(pattern) + , Files{} + , Current{} + { + } + + std::string GetNextCandidate(const std::string& parent) + { + if (this->Files.empty()) { + // Glob the set of matching files. + std::string expr = cmStrCat(parent, this->Pattern); + cmsys::Glob g; + if (!g.FindFiles(expr)) { + return {}; + } + this->Files = g.GetFiles(); + this->Current = this->Files.cbegin(); + } + + // Skip non-directories + for (; this->Current != this->Files.cend() && + !cmSystemTools::FileIsDirectory(*this->Current); + ++this->Current) { + } + + return (this->Current != this->Files.cend()) ? *this->Current++ + : std::string{}; + } + + void Reset() + { + this->Files.clear(); + this->Current = this->Files.cbegin(); + } + +private: + cm::string_view Pattern; + std::vector<std::string> Files; + std::vector<std::string>::const_iterator Current; +}; + +#if defined(__LCC__) +# define CM_LCC_DIAG_SUPPRESS_1222 +# pragma diag_suppress 1222 // invalid error number (3288, but works anyway) +# define CM_LCC_DIAG_SUPPRESS_3288 +# pragma diag_suppress 3288 // parameter was declared but never referenced +#endif + +void ResetGenerator() +{ +} + +template <typename Generator> +void ResetGenerator(Generator&& generator) +{ + std::forward<Generator&&>(generator).Reset(); +} + +template <typename Generator, typename... Generators> +void ResetGenerator(Generator&& generator, Generators&&... generators) +{ + ResetGenerator(std::forward<Generator&&>(generator)); + ResetGenerator(std::forward<Generators&&>(generators)...); +} + +template <typename CallbackFn> +bool TryGeneratedPaths(CallbackFn&& filesCollector, + const std::string& fullPath) +{ + assert(!fullPath.empty() && fullPath.back() != '/'); + return std::forward<CallbackFn&&>(filesCollector)(fullPath + '/'); +} + +template <typename CallbackFn, typename Generator, typename... Rest> +bool TryGeneratedPaths(CallbackFn&& filesCollector, + const std::string& startPath, Generator&& gen, + Rest&&... tail) +{ + ResetGenerator(std::forward<Generator&&>(gen)); + for (auto path = gen.GetNextCandidate(startPath); !path.empty(); + path = gen.GetNextCandidate(startPath)) { + ResetGenerator(std::forward<Rest&&>(tail)...); + if (TryGeneratedPaths(std::forward<CallbackFn&&>(filesCollector), path, + std::forward<Rest&&>(tail)...)) { + return true; + } + } + return false; +} + +#ifdef CM_LCC_DIAG_SUPPRESS_3288 +# undef CM_LCC_DIAG_SUPPRESS_3288 +# pragma diag_default 3288 +#endif + +#ifdef CM_LCC_DIAG_SUPPRESS_1222 +# undef CM_LCC_DIAG_SUPPRESS_1222 +# pragma diag_default 1222 +#endif + +// Parse the version number and store the results that were +// successfully parsed. +int parseVersion(const std::string& version, unsigned int& major, + unsigned int& minor, unsigned int& patch, unsigned int& tweak) +{ + return std::sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch, + &tweak); +} + +} // anonymous namespace cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::PackageRedirect("PACKAGE_REDIRECT"); @@ -60,25 +456,10 @@ const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_INCLUDED( const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_EXCLUDED( "EXCLUDE"); -struct StrverscmpGreater -{ - bool operator()(const std::string& lhs, const std::string& rhs) const - { - return cmSystemTools::strverscmp(lhs, rhs) > 0; - } -}; - -struct StrverscmpLesser -{ - bool operator()(const std::string& lhs, const std::string& rhs) const - { - return cmSystemTools::strverscmp(lhs, rhs) < 0; - } -}; - void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin, std::vector<std::string>::iterator end, - SortOrderType order, SortDirectionType dir) + SortOrderType const order, + SortDirectionType const dir) { if (order == Name_order) { if (dir == Dec) { @@ -86,14 +467,13 @@ void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin, } else { std::sort(begin, end); } - } else if (order == Natural) - // natural order uses letters and numbers (contiguous numbers digit are - // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 - { + } else if (order == Natural) { + // natural order uses letters and numbers (contiguous numbers digit are + // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 if (dir == Dec) { - std::sort(begin, end, StrverscmpGreater()); + std::sort(begin, end, StrverscmpOp<std::greater>()); } else { - std::sort(begin, end, StrverscmpLesser()); + std::sort(begin, end, StrverscmpOp<std::less>()); } } // else do not sort @@ -113,11 +493,10 @@ cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status) void cmFindPackageCommand::AppendSearchPathGroups() { - std::vector<cmFindCommon::PathLabel>* labels; - // Update the All group with new paths. Note that package redirection must // take precedence over everything else, so it has to be first in the array. - labels = &this->PathGroupLabelMap[PathGroup::All]; + std::vector<cmFindCommon::PathLabel>* const labels = + &this->PathGroupLabelMap[PathGroup::All]; labels->insert(labels->begin(), PathLabel::PackageRedirect); labels->insert( std::find(labels->begin(), labels->end(), PathLabel::CMakeSystem), @@ -147,15 +526,15 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Lookup required version of CMake. - if (cmValue rv = + if (cmValue const rv = this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) { unsigned int v[3] = { 0, 0, 0 }; - sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]); + std::sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]); this->RequiredCMakeVersion = CMake_VERSION_ENCODE(v[0], v[1], v[2]); } // Lookup target architecture, if any. - if (cmValue arch = + if (cmValue const arch = this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) { this->LibraryArchitecture = *arch; } @@ -184,7 +563,7 @@ 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 (cmValue def = + if (cmValue const def = this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) { this->NoUserRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) { @@ -194,7 +573,7 @@ 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 (cmValue def = this->Makefile->GetDefinition( + if (cmValue const def = this->Makefile->GetDefinition( "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) { this->NoSystemRegistry = !cmIsOn(*def); } else if (this->Makefile->IsOn( @@ -208,7 +587,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } // Check if Sorting should be enabled - if (cmValue so = + if (cmValue const so = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) { if (*so == "NAME") { @@ -219,7 +598,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->SortOrder = None; } } - if (cmValue sd = + if (cmValue const sd = this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) { this->SortDirection = (*sd == "ASC") ? Asc : Dec; } @@ -265,9 +644,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) 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; - for (unsigned int i = 1; i < args.size(); ++i) { + std::vector<std::size_t> configArgs; + std::vector<std::size_t> moduleArgs; + for (std::size_t i = 1u; i < args.size(); ++i) { if (args[i] == "QUIET") { this->Quiet = true; doing = DoingNone; @@ -281,17 +660,17 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) this->GlobalScope = true; doing = DoingNone; } else if (args[i] == "MODULE") { - moduleArgs.insert(i); + moduleArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "CONFIG") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "NO_MODULE") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "REQUIRED") { this->Required = true; @@ -301,36 +680,36 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } else if (args[i] == "OPTIONAL_COMPONENTS") { doing = DoingOptionalComponents; } else if (args[i] == "NAMES") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNames; } else if (args[i] == "PATHS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingPaths; } else if (args[i] == "HINTS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingHints; } else if (args[i] == "PATH_SUFFIXES") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingPathSuffixes; } else if (args[i] == "CONFIGS") { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingConfigs; } else if (args[i] == "NO_POLICY_SCOPE") { this->PolicyScope = false; doing = DoingNone; } else if (args[i] == "NO_CMAKE_PACKAGE_REGISTRY") { this->NoUserRegistry = true; - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "NO_CMAKE_SYSTEM_PACKAGE_REGISTRY") { this->NoSystemRegistry = true; - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 // NOLINTNEXTLINE(bugprone-branch-clone) } else if (args[i] == "NO_CMAKE_BUILDS_PATH") { // Ignore legacy option. - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if (args[i] == "REGISTRY_VIEW") { if (++i == args.size()) { @@ -347,7 +726,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) return false; } } else if (this->CheckCommonArgument(args[i])) { - configArgs.insert(i); + configArgs.push_back(i); doing = DoingNone; } else if ((doing == DoingComponents) || (doing == DoingOptionalComponents)) { @@ -361,8 +740,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) requiredComponents.insert(args[i]); } - std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i]; - componentVarDefs.emplace_back(req_var, isRequired); + componentVarDefs.emplace_back(this->Name + "_FIND_REQUIRED_" + args[i], + isRequired); // Append to the list of required components. components += components_sep; @@ -420,11 +799,11 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) if (!this->UseFindModules && !this->UseConfigFiles) { std::ostringstream e; e << "given options exclusive to Module mode:\n"; - for (unsigned int si : moduleArgs) { + for (auto si : moduleArgs) { e << " " << args[si] << "\n"; } e << "and options exclusive to Config mode:\n"; - for (unsigned int si : configArgs) { + for (auto si : configArgs) { e << " " << args[si] << "\n"; } e << "The options are incompatible."; @@ -443,20 +822,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) 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"); + std::string const mod = cmStrCat(this->Name, "_FIND_MODULE"); if (this->Makefile->IsOn(mod)) { if (this->VersionComplete.empty()) { // Get version information from the outer call if necessary. // Requested version string. - std::string ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE"); + std::string const 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"); + std::string const exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT"); this->VersionExact = this->Makefile->IsOn(exact); } if (components.empty()) { - std::string components_var = this->Name + "_FIND_COMPONENTS"; + std::string const components_var = this->Name + "_FIND_COMPONENTS"; components = this->Makefile->GetSafeDefinition(components_var); } } @@ -497,15 +876,6 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) 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()) { this->VersionCount = parseVersion(this->Version, this->VersionMajor, this->VersionMinor, @@ -533,7 +903,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) } } - std::string disableFindPackageVar = + std::string const disableFindPackageVar = cmStrCat("CMAKE_DISABLE_FIND_PACKAGE_", this->Name); if (this->Makefile->IsOn(disableFindPackageVar)) { if (this->Required) { @@ -557,8 +927,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) // A dependency provider (if set) gets first look before other methods. // We do this before modifying the package root path stack because a // provider might use methods that ignore that. - cmState* state = this->Makefile->GetState(); - cmState::Command providerCommand = state->GetDependencyProviderCommand( + cmState* const state = this->Makefile->GetState(); + cmState::Command const providerCommand = state->GetDependencyProviderCommand( cmDependencyProvider::Method::FindPackage); if (bypassProvider) { if (this->DebugMode && providerCommand) { @@ -725,11 +1095,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args) "package configuration file provided by " << this->Name << " (" << this->Name << "Config.cmake or " << cmSystemTools::LowerCase(this->Name) - << "-config.cmake). " - "Otherwise make Find" - << this->Name - << ".cmake available in " - "CMAKE_MODULE_PATH."; + << "-config.cmake). Otherwise make Find" << this->Name + << ".cmake available in CMAKE_MODULE_PATH."; } aw << "\n" "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this " @@ -813,9 +1180,9 @@ bool cmFindPackageCommand::FindPackageUsingConfigMode() 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) + const std::string& prefix, const std::string& version, + const unsigned int count, const unsigned int major, const unsigned int minor, + const unsigned int patch, const unsigned int tweak) { addDefinition(prefix, version); @@ -910,7 +1277,7 @@ void cmFindPackageCommand::SetModuleVariables( } void cmFindPackageCommand::AddFindDefinition(const std::string& var, - cm::string_view value) + const cm::string_view value) { if (cmValue old = this->Makefile->GetDefinition(var)) { this->OriginalDefs[var].exists = true; @@ -954,7 +1321,7 @@ bool cmFindPackageCommand::FindModule(bool& found) if (!mfile.empty()) { if (system) { - auto it = this->DeprecatedFindModules.find(this->Name); + auto const it = this->DeprecatedFindModules.find(this->Name); if (it != this->DeprecatedFindModules.end()) { cmPolicies::PolicyStatus status = this->Makefile->GetPolicyStatus(it->second); @@ -978,13 +1345,13 @@ bool cmFindPackageCommand::FindModule(bool& found) // Load the module we found, and set "<name>_FIND_MODULE" to true // while inside it. found = true; - std::string var = cmStrCat(this->Name, "_FIND_MODULE"); + std::string const var = cmStrCat(this->Name, "_FIND_MODULE"); 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"); + std::string const foundVar = cmStrCat(this->Name, "_FOUND"); if (this->Makefile->IsDefinitionSet(foundVar) && !this->Makefile->IsOn(foundVar)) { @@ -999,7 +1366,7 @@ bool cmFindPackageCommand::FindModule(bool& found) } bool cmFindPackageCommand::HandlePackageMode( - HandlePackageModeType handlePackageModeType) + const HandlePackageModeType handlePackageModeType) { this->ConsideredConfigs.clear(); @@ -1042,8 +1409,9 @@ bool cmFindPackageCommand::HandlePackageMode( } } - std::string foundVar = cmStrCat(this->Name, "_FOUND"); - std::string notFoundMessageVar = cmStrCat(this->Name, "_NOT_FOUND_MESSAGE"); + std::string const foundVar = cmStrCat(this->Name, "_FOUND"); + std::string const notFoundMessageVar = + cmStrCat(this->Name, "_NOT_FOUND_MESSAGE"); std::string notFoundMessage; // If the directory for the config file was found, try to read the file. @@ -1123,8 +1491,9 @@ bool cmFindPackageCommand::HandlePackageMode( << (this->VersionExact ? "exactly matches" : "is compatible with") << " requested version " << (this->VersionRange.empty() ? "" : "range ") << "\"" - << this->VersionComplete << "\".\n" - << "The following configuration files were considered but not " + << this->VersionComplete + << "\".\n" + "The following configuration files were considered but not " "accepted:\n"; for (ConfigFileInfo const& info : @@ -1172,8 +1541,9 @@ bool cmFindPackageCommand::HandlePackageMode( "package or SDK, be sure it has been installed."; } else // if(!this->UseFindModules && !this->UseConfigFiles) { - e << "No \"Find" << this->Name << ".cmake\" found in " - << "CMAKE_MODULE_PATH."; + e << "No \"Find" << this->Name + << ".cmake\" found in " + "CMAKE_MODULE_PATH."; aw << "Find" << this->Name @@ -1217,16 +1587,16 @@ bool cmFindPackageCommand::HandlePackageMode( this->Makefile->AddDefinition(foundVar, found ? "1" : "0"); // Set a variable naming the configuration file that was found. - std::string fileVar = cmStrCat(this->Name, "_CONFIG"); + std::string const fileVar = cmStrCat(this->Name, "_CONFIG"); if (found) { this->Makefile->AddDefinition(fileVar, this->FileFound); } else { this->Makefile->RemoveDefinition(fileVar); } - std::string consideredConfigsVar = + std::string const consideredConfigsVar = cmStrCat(this->Name, "_CONSIDERED_CONFIGS"); - std::string consideredVersionsVar = + std::string const consideredVersionsVar = cmStrCat(this->Name, "_CONSIDERED_VERSIONS"); std::string consideredConfigFiles; @@ -1312,7 +1682,7 @@ bool cmFindPackageCommand::FindConfig() void cmFindPackageCommand::SetConfigDirCacheVariable(const std::string& value) { - std::string help = + std::string const help = cmStrCat("The directory containing a CMake configuration file for ", this->Name, '.'); this->Makefile->AddCacheDefinition(this->Variable, value, help.c_str(), @@ -1351,7 +1721,7 @@ bool cmFindPackageCommand::FindAppBundleConfig() } bool cmFindPackageCommand::ReadListFile(const std::string& f, - PolicyScopeRule psr) + const PolicyScopeRule psr) { const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope; @@ -1362,12 +1732,12 @@ bool cmFindPackageCommand::ReadListFile(const std::string& f, if (this->Makefile->ReadDependentFile(f, noPolicyScope)) { return true; } - std::string e = cmStrCat("Error reading CMake code from \"", f, "\"."); + std::string const e = cmStrCat("Error reading CMake code from \"", f, "\"."); this->SetError(e); return false; } -void cmFindPackageCommand::AppendToFoundProperty(bool found) +void cmFindPackageCommand::AppendToFoundProperty(const bool found) { std::vector<std::string> foundContents; cmValue foundProp = @@ -1410,27 +1780,28 @@ void cmFindPackageCommand::AppendToFoundProperty(bool found) void cmFindPackageCommand::AppendSuccessInformation() { { - std::string transitivePropName = + std::string const transitivePropName = cmStrCat("_CMAKE_", this->Name, "_TRANSITIVE_DEPENDENCY"); this->Makefile->GetState()->SetGlobalProperty(transitivePropName, "False"); } - std::string found = cmStrCat(this->Name, "_FOUND"); - std::string upperFound = cmSystemTools::UpperCase(found); + std::string const found = cmStrCat(this->Name, "_FOUND"); + std::string const upperFound = cmSystemTools::UpperCase(found); - bool upperResult = this->Makefile->IsOn(upperFound); - bool result = this->Makefile->IsOn(found); - bool packageFound = (result || upperResult); + bool const upperResult = this->Makefile->IsOn(upperFound); + bool const result = this->Makefile->IsOn(found); + bool const packageFound = (result || upperResult); this->AppendToFoundProperty(packageFound); // Record whether the find was quiet or not, so this can be used // e.g. in FeatureSummary.cmake - std::string quietInfoPropName = cmStrCat("_CMAKE_", this->Name, "_QUIET"); + std::string const quietInfoPropName = + cmStrCat("_CMAKE_", this->Name, "_QUIET"); this->Makefile->GetState()->SetGlobalProperty( quietInfoPropName, this->Quiet ? "TRUE" : "FALSE"); // set a global property to record the required version of this package - std::string versionInfoPropName = + std::string const versionInfoPropName = cmStrCat("_CMAKE_", this->Name, "_REQUIRED_VERSION"); std::string versionInfo; if (!this->VersionRange.empty()) { @@ -1442,28 +1813,13 @@ void cmFindPackageCommand::AppendSuccessInformation() this->Makefile->GetState()->SetGlobalProperty(versionInfoPropName, versionInfo.c_str()); if (this->Required) { - std::string requiredInfoPropName = + std::string const requiredInfoPropName = cmStrCat("_CMAKE_", this->Name, "_TYPE"); this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName, "REQUIRED"); } } -inline std::size_t collectPathsForDebug(std::string& buffer, - cmSearchPath const& searchPath, - std::size_t startIndex = 0) -{ - const auto& paths = searchPath.GetPaths(); - if (paths.empty()) { - buffer += " none\n"; - return 0; - } - for (std::size_t i = startIndex; i < paths.size(); i++) { - buffer += " " + paths[i].Path + "\n"; - } - return paths.size(); -} - void cmFindPackageCommand::ComputePrefixes() { this->FillPrefixesPackageRedirect(); @@ -1674,14 +2030,6 @@ void cmFindPackageCommand::FillPrefixesSystemRegistry() } #if defined(_WIN32) && !defined(__CYGWIN__) -# include <windows.h> -// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx -# if !defined(KEY_WOW64_32KEY) -# define KEY_WOW64_32KEY 0x0200 -# endif -# if !defined(KEY_WOW64_64KEY) -# define KEY_WOW64_64KEY 0x0100 -# endif void cmFindPackageCommand::LoadPackageRegistryWinUser() { // HKEY_CURRENT_USER\\Software shares 32-bit and 64-bit views. @@ -1704,7 +2052,8 @@ void cmFindPackageCommand::LoadPackageRegistryWinSystem() } } -void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view, +void cmFindPackageCommand::LoadPackageRegistryWin(const bool user, + const unsigned int view, cmSearchPath& outPaths) { std::wstring key = L"Software\\Kitware\\CMake\\Packages\\"; @@ -1756,28 +2105,8 @@ void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view, RegCloseKey(hKey); } } -#else -class cmFindPackageCommandHoldFile -{ - const char* File; - -public: - cmFindPackageCommandHoldFile(const char* f) - : File(f) - { - } - ~cmFindPackageCommandHoldFile() - { - if (this->File) { - cmSystemTools::RemoveFile(this->File); - } - } - cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete; - cmFindPackageCommandHoldFile& operator=( - const cmFindPackageCommandHoldFile&) = delete; - void Release() { this->File = nullptr; } -}; +#else void cmFindPackageCommand::LoadPackageRegistryDir(std::string const& dir, cmSearchPath& outPaths) { @@ -1877,7 +2206,7 @@ void cmFindPackageCommand::FillPrefixesCMakeSystemVariable() std::vector<std::string> expanded = cmExpandedList(*prefix_paths); long count = 0; for (const auto& path : expanded) { - bool to_add = + bool const to_add = !(path == install_path_to_remove && ++count == install_prefix_count); if (to_add) { paths.AddPath(path); @@ -1941,7 +2270,7 @@ bool cmFindPackageCommand::SearchDirectory(std::string const& dir) std::string d = dir; if (!s.empty()) { d += s; - d += "/"; + d += '/'; } if (this->CheckDirectory(d)) { return true; @@ -1955,7 +2284,7 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir) assert(!dir.empty() && dir.back() == '/'); // Look for the file in this directory. - std::string d = dir.substr(0, dir.size() - 1); + std::string const d = dir.substr(0, dir.size() - 1); if (this->FindConfigFile(d, this->FileFound)) { // Remove duplicate slashes. cmSystemTools::ConvertToUnixSlashes(this->FileFound); @@ -2028,8 +2357,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, std::string& result_version) { // The version file will be loaded in an isolated scope. - cmMakefile::ScopePushPop varScope(this->Makefile); - cmMakefile::PolicyPushPop polScope(this->Makefile); + cmMakefile::ScopePushPop const varScope(this->Makefile); + cmMakefile::PolicyPushPop const polScope(this->Makefile); static_cast<void>(varScope); static_cast<void>(polScope); @@ -2076,7 +2405,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, if (this->ReadListFile(version_file, NoPolicyScope)) { // Check the output variables. bool okay = this->Makefile->IsOn("PACKAGE_VERSION_EXACT"); - bool unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE"); + bool const unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE"); if (!okay && !this->VersionExact) { okay = this->Makefile->IsOn("PACKAGE_VERSION_COMPATIBLE"); } @@ -2096,8 +2425,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, unsigned int parsed_patch; unsigned int parsed_tweak; this->VersionFoundCount = - sscanf(this->VersionFound.c_str(), "%u.%u.%u.%u", &parsed_major, - &parsed_minor, &parsed_patch, &parsed_tweak); + parseVersion(this->VersionFound, parsed_major, parsed_minor, + parsed_patch, parsed_tweak); switch (this->VersionFoundCount) { case 4: this->VersionFoundTweak = parsed_tweak; @@ -2129,7 +2458,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file, void cmFindPackageCommand::StoreVersionFound() { // Store the whole version string. - std::string ver = cmStrCat(this->Name, "_VERSION"); + std::string const ver = cmStrCat(this->Name, "_VERSION"); auto addDefinition = [this](const std::string& variable, cm::string_view value) { this->Makefile->AddDefinition(variable, value); @@ -2145,357 +2474,6 @@ void cmFindPackageCommand::StoreVersionFound() } } -class cmFileListGeneratorBase -{ -public: - virtual ~cmFileListGeneratorBase() = default; - -protected: - bool Consider(std::string const& fullPath, cmFileList& listing); - -private: - bool Search(cmFileList&); - virtual bool Search(std::string const& parent, cmFileList&) = 0; - virtual std::unique_ptr<cmFileListGeneratorBase> Clone() const = 0; - friend class cmFileList; - cmFileListGeneratorBase* SetNext(cmFileListGeneratorBase const& next); - std::unique_ptr<cmFileListGeneratorBase> Next; -}; - -class cmFileList -{ -public: - virtual ~cmFileList() = default; - cmFileList& operator/(cmFileListGeneratorBase const& rhs) - { - if (this->Last) { - this->Last = this->Last->SetNext(rhs); - } else { - this->First = rhs.Clone(); - this->Last = this->First.get(); - } - return *this; - } - bool Search() - { - if (this->First) { - return this->First->Search(*this); - } - return false; - } - -private: - virtual bool Visit(std::string const& fullPath) = 0; - friend class cmFileListGeneratorBase; - std::unique_ptr<cmFileListGeneratorBase> First; - cmFileListGeneratorBase* Last = nullptr; -}; - -class cmFindPackageFileList : public cmFileList -{ -public: - cmFindPackageFileList(cmFindPackageCommand* fpc, bool use_suffixes = true) - : FPC(fpc) - , UseSuffixes(use_suffixes) - { - } - -private: - bool Visit(std::string const& fullPath) override - { - if (this->UseSuffixes) { - return this->FPC->SearchDirectory(fullPath); - } - return this->FPC->CheckDirectory(fullPath); - } - cmFindPackageCommand* FPC; - bool UseSuffixes; -}; - -bool cmFileListGeneratorBase::Search(cmFileList& listing) -{ - return this->Search("", listing); -} - -cmFileListGeneratorBase* cmFileListGeneratorBase::SetNext( - cmFileListGeneratorBase const& next) -{ - this->Next = next.Clone(); - return this->Next.get(); -} - -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); - } - return listing.Visit(fullPath + "/"); -} - -class cmFileListGeneratorFixed : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorFixed(std::string str) - : String(std::move(str)) - { - } - cmFileListGeneratorFixed(cmFileListGeneratorFixed const& r) - : String(r.String) - { - } - -private: - std::string String; - bool Search(std::string const& parent, cmFileList& lister) override - { - std::string fullPath = parent + this->String; - return this->Consider(fullPath, lister); - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorFixed(*this)); - return g; - } -}; - -class cmFileListGeneratorEnumerate : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorEnumerate(std::vector<std::string> const& v) - : Vector(v) - { - } - cmFileListGeneratorEnumerate(cmFileListGeneratorEnumerate const& r) - : Vector(r.Vector) - { - } - -private: - std::vector<std::string> const& Vector; - bool Search(std::string const& parent, cmFileList& lister) override - { - for (std::string const& i : this->Vector) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorEnumerate(*this)); - return g; - } -}; - -class cmFileListGeneratorProject : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorProject(std::vector<std::string> const& names, - cmFindPackageCommand::SortOrderType so, - cmFindPackageCommand::SortDirectionType sd) - : Names(names) - { - this->SetSort(so, sd); - } - cmFileListGeneratorProject(cmFileListGeneratorProject const& r) - : Names(r.Names) - { - this->SetSort(r.SortOrder, r.SortDirection); - } - - void SetSort(cmFindPackageCommand::SortOrderType o, - cmFindPackageCommand::SortDirectionType d) - { - this->SortOrder = o; - this->SortDirection = d; - } - -protected: - // sort parameters - cmFindPackageCommand::SortOrderType SortOrder; - cmFindPackageCommand::SortDirectionType SortDirection; - -private: - std::vector<std::string> const& Names; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Construct a list of matches. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - for (std::string const& n : this->Names) { - if (cmsysString_strncasecmp(fname, n.c_str(), n.length()) == 0) { - matches.emplace_back(fname); - } - } - } - - // before testing the matches check if there is a specific sorting order to - // perform - if (this->SortOrder != cmFindPackageCommand::None) { - cmFindPackageCommand::Sort(matches.begin(), matches.end(), - this->SortOrder, this->SortDirection); - } - - for (std::string const& i : matches) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorProject(*this)); - return g; - } -}; - -class cmFileListGeneratorMacProject : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorMacProject(std::vector<std::string> const& names, - const char* ext) - : Names(names) - , Extension(ext) - { - } - cmFileListGeneratorMacProject(cmFileListGeneratorMacProject const& r) - : Names(r.Names) - , Extension(r.Extension) - { - } - -private: - std::vector<std::string> const& Names; - std::string Extension; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Construct a list of matches. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - for (std::string name : this->Names) { - name += this->Extension; - if (cmsysString_strcasecmp(fname, name.c_str()) == 0) { - matches.emplace_back(fname); - } - } - } - - for (std::string const& i : matches) { - if (this->Consider(parent + i, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorMacProject(*this)); - return g; - } -}; - -class cmFileListGeneratorCaseInsensitive : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorCaseInsensitive(std::string str) - : String(std::move(str)) - { - } - cmFileListGeneratorCaseInsensitive( - cmFileListGeneratorCaseInsensitive const& r) - : String(r.String) - { - } - -private: - std::string String; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Look for matching files. - std::vector<std::string> matches; - cmsys::Directory d; - d.Load(parent); - for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { - const char* fname = d.GetFile(i); - if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) { - continue; - } - if (cmsysString_strcasecmp(fname, this->String.c_str()) == 0) { - if (this->Consider(parent + fname, lister)) { - return true; - } - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - std::unique_ptr<cmFileListGeneratorBase> g( - new cmFileListGeneratorCaseInsensitive(*this)); - return g; - } -}; - -class cmFileListGeneratorGlob : public cmFileListGeneratorBase -{ -public: - cmFileListGeneratorGlob(std::string str) - : Pattern(std::move(str)) - { - } - cmFileListGeneratorGlob(cmFileListGeneratorGlob const& r) - : Pattern(r.Pattern) - { - } - -private: - std::string Pattern; - bool Search(std::string const& parent, cmFileList& lister) override - { - // Glob the set of matching files. - std::string expr = cmStrCat(parent, this->Pattern); - cmsys::Glob g; - if (!g.FindFiles(expr)) { - return false; - } - std::vector<std::string> const& files = g.GetFiles(); - - // Look for directories among the matches. - for (std::string const& f : files) { - if (this->Consider(f, lister)) { - return true; - } - } - return false; - } - std::unique_ptr<cmFileListGeneratorBase> Clone() const override - { - return cm::make_unique<cmFileListGeneratorGlob>(*this); - } -}; - bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) { assert(!prefix_in.empty() && prefix_in.back() == '/'); @@ -2515,148 +2493,101 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) return false; } - // PREFIX/ (useful on windows or in build trees) + // PREFIX/ (useful on windows or in build trees) if (this->SearchDirectory(prefix_in)) { return true; } // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); - // PREFIX/(cmake|CMake)/ (useful on windows or in build trees) - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }; + auto firstPkgDirGen = + cmProjectDirectoryListGenerator{ this->Names, this->SortOrder, + this->SortDirection }; + + // PREFIX/(cmake|CMake)/ (useful on windows or in build trees) + if (TryGeneratedPaths(searchFn, prefix, iCMakeGen)) { + return true; } - // PREFIX/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + // PREFIX/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen)) { + return true; } - // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen)) { + return true; + } + + auto secondPkgDirGen = + cmProjectDirectoryListGenerator{ this->Names, this->SortOrder, + this->SortDirection }; + + // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen, + secondPkgDirGen)) { + return true; } // Construct list of common install locations (lib and share). - std::vector<std::string> common; + std::vector<cm::string_view> common; + std::string libArch; if (!this->LibraryArchitecture.empty()) { - common.push_back("lib/" + this->LibraryArchitecture); + libArch = "lib/" + this->LibraryArchitecture; + common.emplace_back(libArch); } if (this->UseLib32Paths) { - common.emplace_back("lib32"); + common.emplace_back("lib32"_s); } if (this->UseLib64Paths) { - common.emplace_back("lib64"); + common.emplace_back("lib64"_s); } if (this->UseLibx32Paths) { - common.emplace_back("libx32"); + common.emplace_back("libx32"_s); } - common.emplace_back("lib"); - common.emplace_back("share"); + common.emplace_back("lib"_s); + common.emplace_back("share"_s); - // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorFixed("cmake") / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + auto cmnGen = cmEnumPathSegmentsGenerator{ common }; + auto cmakeGen = cmAppendPathSegmentGenerator{ "cmake"_s }; + + // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, cmakeGen, firstPkgDirGen)) { + return true; } - // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen)) { + return true; } - // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ + if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen, iCMakeGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorFixed("cmake") / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, cmakeGen, + secondPkgDirGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, + secondPkgDirGen)) { + return true; } // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorEnumerate(common) / - cmFileListGeneratorProject(this->Names, this->SortOrder, - this->SortDirection) / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, + secondPkgDirGen, iCMakeGen); } bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in) @@ -2665,56 +2596,36 @@ bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in) // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); + + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }; + auto fwGen = + cmMacProjectDirectoryListGenerator{ this->Names, ".framework"_s }; + auto rGen = cmAppendPathSegmentGenerator{ "Resources"_s }; + auto vGen = cmAppendPathSegmentGenerator{ "Versions"_s }; + auto grGen = cmFileListGeneratorGlob{ "/*/Resources"_s }; // <prefix>/Foo.framework/Resources/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen)) { + return true; } + // <prefix>/Foo.framework/Resources/CMake/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen, iCMakeGen)) { + return true; } // <prefix>/Foo.framework/Versions/*/Resources/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Versions") / - cmFileListGeneratorGlob("*/Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen)) { + return true; } // <prefix>/Foo.framework/Versions/*/Resources/CMake/ - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".framework") / - cmFileListGeneratorFixed("Versions") / - cmFileListGeneratorGlob("*/Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen, iCMakeGen); } bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in) @@ -2723,32 +2634,24 @@ bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in) // Strip the trailing slash because the path generator is about to // add one. - std::string prefix = prefix_in.substr(0, prefix_in.size() - 1); + std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1); + + auto searchFn = [this](const std::string& fullPath) -> bool { + return this->SearchDirectory(fullPath); + }; + + auto appGen = cmMacProjectDirectoryListGenerator{ this->Names, ".app"_s }; + auto crGen = cmAppendPathSegmentGenerator{ "Contents/Resources"_s }; // <prefix>/Foo.app/Contents/Resources - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".app") / - cmFileListGeneratorFixed("Contents/Resources"); - if (lister.Search()) { - return true; - } + if (TryGeneratedPaths(searchFn, prefix, appGen, crGen)) { + return true; } // <prefix>/Foo.app/Contents/Resources/CMake - { - cmFindPackageFileList lister(this); - lister / cmFileListGeneratorFixed(prefix) / - cmFileListGeneratorMacProject(this->Names, ".app") / - cmFileListGeneratorFixed("Contents/Resources") / - cmFileListGeneratorCaseInsensitive("cmake"); - if (lister.Search()) { - return true; - } - } - - return false; + return TryGeneratedPaths( + searchFn, prefix, appGen, crGen, + cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s }); } // TODO: Debug cmsys::Glob double slash problem. diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 80fd8f8..28e00a1 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -153,8 +153,6 @@ private: bool SearchFrameworkPrefix(std::string const& prefix_in); bool SearchAppBundlePrefix(std::string const& prefix_in); - friend class cmFindPackageFileList; - struct OriginalDef { bool exists; diff --git a/Source/cmFindPathCommand.cxx b/Source/cmFindPathCommand.cxx index 27074ff..74a69d8 100644 --- a/Source/cmFindPathCommand.cxx +++ b/Source/cmFindPathCommand.cxx @@ -88,7 +88,8 @@ std::string cmFindPathCommand::FindHeaderInFramework( if (!frameWorkName.empty()) { std::string fpath = cmStrCat(dir, frameWorkName, ".framework"); std::string intPath = cmStrCat(fpath, "/Headers/", fileName); - if (cmSystemTools::FileExists(intPath)) { + if (cmSystemTools::FileExists(intPath) && + this->Validate(this->IncludeFileInPath ? intPath : fpath)) { debug.FoundAt(intPath); if (this->IncludeFileInPath) { return intPath; @@ -124,7 +125,8 @@ std::string cmFindPathCommand::FindNormalHeader(cmFindBaseDebugState& debug) for (std::string const& n : this->Names) { for (std::string const& sp : this->SearchPaths) { tryPath = cmStrCat(sp, n); - if (cmSystemTools::FileExists(tryPath)) { + if (cmSystemTools::FileExists(tryPath) && + this->Validate(this->IncludeFileInPath ? tryPath : sp)) { debug.FoundAt(tryPath); if (this->IncludeFileInPath) { return tryPath; diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx index a64e0e4..8a2a69e 100644 --- a/Source/cmFindProgramCommand.cxx +++ b/Source/cmFindProgramCommand.cxx @@ -27,6 +27,7 @@ struct cmFindProgramHelper cmFindBase const* base) : DebugSearches(std::move(debugName), base) , Makefile(makefile) + , FindBase(base) , PolicyCMP0109(makefile->GetPolicyStatus(cmPolicies::CMP0109)) { #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) @@ -56,6 +57,7 @@ struct cmFindProgramHelper // Debug state cmFindBaseDebugState DebugSearches; cmMakefile* Makefile; + cmFindBase const* FindBase; cmPolicies::PolicyStatus PolicyCMP0109; @@ -94,7 +96,7 @@ struct cmFindProgramHelper this->TestNameExt = cmStrCat(name, ext); this->TestPath = cmSystemTools::CollapseFullPath( this->TestNameExt, path); - bool exists = this->FileIsExecutable(this->TestPath); + bool exists = this->FileIsValid(this->TestPath); exists ? this->DebugSearches.FoundAt(this->TestPath) : this->DebugSearches.FailedAt(this->TestPath); if (exists) { @@ -104,12 +106,12 @@ struct cmFindProgramHelper return false; }); } - bool FileIsExecutable(std::string const& file) const + bool FileIsValid(std::string const& file) const { -#ifdef _WIN32 if (!this->FileIsExecutableCMP0109(file)) { return false; } +#ifdef _WIN32 // Pretend the Windows "python" app installer alias does not exist. if (cmSystemTools::LowerCase(file).find("/windowsapps/python") != std::string::npos) { @@ -119,10 +121,8 @@ struct cmFindProgramHelper return false; } } - return true; -#else - return this->FileIsExecutableCMP0109(file); #endif + return this->FindBase->Validate(file); } bool FileIsExecutableCMP0109(std::string const& file) const { diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx index b9400c9..3465c23 100644 --- a/Source/cmForEachCommand.cxx +++ b/Source/cmForEachCommand.cxx @@ -260,7 +260,7 @@ auto cmForEachFunctionBlocker::invoke( cmExecutionStatus status(mf); mf.ExecuteCommand(func, status); if (status.GetReturnInvoked()) { - inStatus.SetReturnInvoked(); + inStatus.SetReturnInvoked(status.GetReturnVariables()); result.Break = true; break; } diff --git a/Source/cmFunctionBlocker.cxx b/Source/cmFunctionBlocker.cxx index 40e692d..523482a 100644 --- a/Source/cmFunctionBlocker.cxx +++ b/Source/cmFunctionBlocker.cxx @@ -24,10 +24,11 @@ bool cmFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, auto self = mf.RemoveFunctionBlocker(); assert(self.get() == this); - if (!this->ArgumentsMatch(lff, mf)) { - cmListFileContext const& lfc = this->GetStartingContext(); - cmListFileContext closingContext = - cmListFileContext::FromListFileFunction(lff, lfc.FilePath); + cmListFileContext const& lfc = this->GetStartingContext(); + cmListFileContext closingContext = + cmListFileContext::FromListFileFunction(lff, lfc.FilePath); + if (this->EndCommandSupportsArguments() && + !this->ArgumentsMatch(lff, mf)) { std::ostringstream e; /* clang-format off */ e << "A logical block opening on the line\n" @@ -37,6 +38,15 @@ bool cmFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff, << "with mis-matching arguments."; /* clang-format on */ mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); + } else if (!this->EndCommandSupportsArguments() && + !lff.Arguments().empty()) { + std::ostringstream e; + /* clang-format off */ + e << "A logical block closing on the line\n" + " " << closingContext << "\n" + "has unexpected arguments."; + /* clang-format on */ + mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str()); } return this->Replay(std::move(this->Functions), status); diff --git a/Source/cmFunctionBlocker.h b/Source/cmFunctionBlocker.h index 38abeba..3e096f2 100644 --- a/Source/cmFunctionBlocker.h +++ b/Source/cmFunctionBlocker.h @@ -38,6 +38,8 @@ private: virtual cm::string_view StartCommandName() const = 0; virtual cm::string_view EndCommandName() const = 0; + virtual bool EndCommandSupportsArguments() const { return true; } + virtual bool ArgumentsMatch(cmListFileFunction const& lff, cmMakefile& mf) const = 0; diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx index 1359009..f4768b6 100644 --- a/Source/cmFunctionCommand.cxx +++ b/Source/cmFunctionCommand.cxx @@ -120,6 +120,7 @@ bool cmFunctionHelperCommand::operator()( return false; } if (status.GetReturnInvoked()) { + makefile.RaiseScope(status.GetReturnVariables()); break; } } diff --git a/Source/cmGeneratedFileStream.cxx b/Source/cmGeneratedFileStream.cxx index b529b8f..c72d6a7 100644 --- a/Source/cmGeneratedFileStream.cxx +++ b/Source/cmGeneratedFileStream.cxx @@ -14,11 +14,10 @@ #endif cmGeneratedFileStream::cmGeneratedFileStream(Encoding encoding) - : OriginalLocale(this->getloc()) { #ifndef CMAKE_BOOTSTRAP if (encoding != codecvt::None) { - this->imbue(std::locale(this->OriginalLocale, new codecvt(encoding))); + this->imbue(std::locale(this->getloc(), new codecvt(encoding))); } #else static_cast<void>(encoding); @@ -124,10 +123,10 @@ cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase() void cmGeneratedFileStreamBase::Open(std::string const& name) { // Save the original name of the file. - this->Name = name; + this->Name = cmSystemTools::CollapseFullPath(name); // Create the name of the temporary file. - this->TempName = name; + this->TempName = this->Name; #if defined(__VMS) this->TempName += "_"; #else @@ -231,7 +230,7 @@ int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname, void cmGeneratedFileStream::SetName(const std::string& fname) { - this->Name = fname; + this->Name = cmSystemTools::CollapseFullPath(fname); } void cmGeneratedFileStream::SetTempExt(std::string const& ext) @@ -239,13 +238,16 @@ void cmGeneratedFileStream::SetTempExt(std::string const& ext) this->TempExt = ext; } -void cmGeneratedFileStream::WriteRaw(std::string const& data) +void cmGeneratedFileStream::WriteAltEncoding(std::string const& data, + Encoding encoding) { #ifndef CMAKE_BOOTSTRAP - std::locale activeLocale = this->imbue(this->OriginalLocale); + std::locale prevLocale = + this->imbue(std::locale(this->getloc(), new codecvt(encoding))); this->write(data.data(), data.size()); - this->imbue(activeLocale); + this->imbue(prevLocale); #else + static_cast<void>(encoding); this->write(data.data(), data.size()); #endif } diff --git a/Source/cmGeneratedFileStream.h b/Source/cmGeneratedFileStream.h index bb7e3bf..bfc121f 100644 --- a/Source/cmGeneratedFileStream.h +++ b/Source/cmGeneratedFileStream.h @@ -148,12 +148,8 @@ public: void SetTempExt(std::string const& ext); /** - * Writes the given string directly to the file without changing the - * encoding. + * Write a specific string using an alternate encoding. + * Afterward, the original encoding is restored. */ - void WriteRaw(std::string const& data); - -private: - // The original locale of the stream (performs no encoding conversion). - std::locale OriginalLocale; + void WriteAltEncoding(std::string const& data, Encoding encoding); }; diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 6235a2a..7d43eb1 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -808,11 +808,16 @@ void handleSystemIncludesDep(cmLocalGenerator* lg, dagChecker, depTgt, language), result); } - if (!depTgt->IsImported() || excludeImported) { + if (!depTgt->GetPropertyAsBool("SYSTEM")) { return; } - if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) { - return; + if (depTgt->IsImported()) { + if (excludeImported) { + return; + } + if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) { + return; + } } if (cmValue dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) { @@ -912,11 +917,19 @@ bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang, return false; } - if (lang != "C" && lang != "CXX" && lang != "Fortran") { + if (lang != "C" && lang != "CXX" && lang != "CUDA" && lang != "Fortran") { // We do not define IPO behavior for other languages. return false; } + if (lang == "CUDA") { + // CUDA IPO requires both CUDA_ARCHITECTURES and CUDA_SEPARABLE_COMPILATION + if (cmIsOff(this->GetSafeProperty("CUDA_ARCHITECTURES")) || + cmIsOff(this->GetSafeProperty("CUDA_SEPARABLE_COMPILATION"))) { + return false; + } + } + cmPolicies::PolicyStatus cmp0069 = this->GetPolicyStatusCMP0069(); if (cmp0069 == cmPolicies::OLD || cmp0069 == cmPolicies::WARN) { @@ -1708,7 +1721,8 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget, } } if (!found) { - if (fileSet->GetType() == "HEADERS"_s) { + if (fileSet->GetType() == "HEADERS"_s || + fileSet->GetType() == "CXX_MODULE_HEADER_UNITS"_s) { headTarget->Makefile->GetOrCreateSourceGroup("Header Files") ->AddGroupFile(path); } @@ -1729,6 +1743,20 @@ void AddFileSetEntries(cmGeneratorTarget const* headTarget, addFileSetEntry(headTarget, config, dagChecker, headerSet, entries); } } + for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) { + for (auto const& name : cmExpandedList(entry.Value)) { + auto const* cxxModuleSet = headTarget->Target->GetFileSet(name); + addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries); + } + } + for (auto const& entry : + headTarget->Target->GetCxxModuleHeaderSetsEntries()) { + for (auto const& name : cmExpandedList(entry.Value)) { + auto const* cxxModuleHeaderSet = headTarget->Target->GetFileSet(name); + addFileSetEntry(headTarget, config, dagChecker, cxxModuleHeaderSet, + entries); + } + } } bool processSources(cmGeneratorTarget const* tgt, @@ -3409,7 +3437,9 @@ void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags, "EXPLICIT_LANGUAGE"); } -void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const +void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink, + const std::string& config, + std::string& flags) const { std::string property = this->GetSafeProperty("CUDA_ARCHITECTURES"); @@ -3441,6 +3471,7 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const std::string const& compiler = this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID"); + const bool ipoEnabled = this->IsIPOEnabled("CUDA", config); // Check for special modes: `all`, `all-major`. if (property == "all" || property == "all-major") { @@ -3520,6 +3551,13 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const } if (compiler == "NVIDIA") { + if (ipoEnabled && compileOrLink == cmBuildStep::Link) { + if (cmValue cudaIPOFlags = + this->Makefile->GetDefinition("CMAKE_CUDA_LINK_OPTIONS_IPO")) { + flags += cudaIPOFlags; + } + } + for (CudaArchitecture& architecture : architectures) { flags += " --generate-code=arch=compute_" + architecture.name + ",code=["; @@ -3532,7 +3570,13 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const } } - if (architecture.real) { + if (ipoEnabled) { + if (compileOrLink == cmBuildStep::Compile) { + flags += "lto_" + architecture.name; + } else if (compileOrLink == cmBuildStep::Link) { + flags += "sm_" + architecture.name; + } + } else if (architecture.real) { flags += "sm_" + architecture.name; } @@ -5410,9 +5454,6 @@ std::string cmGeneratorTarget::GetObjectDirectory( std::string obj_dir = this->GlobalGenerator->ExpandCFGIntDir(this->ObjectDirectory, config); #if defined(__APPLE__) - // find and replace $(PROJECT_NAME) xcode placeholder - const std::string projectName = this->LocalGenerator->GetProjectName(); - cmSystemTools::ReplaceString(obj_dir, "$(PROJECT_NAME)", projectName); // Replace Xcode's placeholder for the object file directory since // installation and export scripts need to know the real directory. // Xcode has build-time settings (e.g. for sanitizers) that affect this, @@ -8719,8 +8760,92 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile( cmGeneratedFileStream fout(filename); fout.SetCopyIfDifferent(true); - fout << "#include <" << headerFilename << ">\n"; + // IWYU pragma: associated allows include what you use to + // consider the headerFile as part of the entire language + // unit within include-what-you-use and as a result allows + // one to get IWYU advice for headers :) + fout << "#include <" << headerFilename << "> // IWYU pragma: associated\n"; fout.close(); return filename; } + +bool cmGeneratorTarget::HaveCxx20ModuleSources() const +{ + auto const& fs_names = this->Target->GetAllFileSetNames(); + return std::any_of(fs_names.begin(), fs_names.end(), + [this](std::string const& name) -> bool { + auto const* file_set = this->Target->GetFileSet(name); + if (!file_set) { + this->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", this->Target->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found.")); + return false; + } + + auto const& fs_type = file_set->GetType(); + return fs_type == "CXX_MODULES"_s || + fs_type == "CXX_MODULE_HEADER_UNITS"_s; + }); +} + +cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport( + std::string const& config) const +{ + auto const* state = this->Makefile->GetState(); + if (!state->GetLanguageEnabled("CXX")) { + return Cxx20SupportLevel::MissingCxx; + } + cmValue standardDefault = + this->Target->GetMakefile()->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT"); + if (standardDefault && !standardDefault->empty()) { + cmStandardLevelResolver standardResolver(this->Makefile); + if (!standardResolver.HaveStandardAvailable(this, "CXX", config, + "cxx_std_20")) { + return Cxx20SupportLevel::NoCxx20; + } + } + // Else, an empty CMAKE_CXX_STANDARD_DEFAULT means CMake does not detect and + // set a default standard level for this compiler, so assume all standards + // are available. + if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) { + return Cxx20SupportLevel::MissingExperimentalFlag; + } + return Cxx20SupportLevel::Supported; +} + +void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const +{ + // Check for `CXX_MODULE*` file sets and a lack of support. + if (this->HaveCxx20ModuleSources()) { + switch (this->HaveCxxModuleSupport(config)) { + case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GetName(), + "\" target has C++ module sources but the \"CXX\" language " + "has not been enabled")); + break; + case cmGeneratorTarget::Cxx20SupportLevel::MissingExperimentalFlag: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GetName(), + "\" target has C++ module sources but its experimental " + "support has not been requested")); + break; + case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "The \"", this->GetName(), + "\" target has C++ module sources but is not using at least " + "\"cxx_std_20\"")); + break; + case cmGeneratorTarget::Cxx20SupportLevel::Supported: + // All is well. + break; + } + } +} diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h index 6bce7d2..25e6a81 100644 --- a/Source/cmGeneratorTarget.h +++ b/Source/cmGeneratorTarget.h @@ -23,6 +23,7 @@ #include "cmStateTypes.h" #include "cmValue.h" +enum class cmBuildStep; class cmComputeLinkInformation; class cmCustomCommand; class cmGlobalGenerator; @@ -471,7 +472,9 @@ public: void AddExplicitLanguageFlags(std::string& flags, cmSourceFile const& sf) const; - void AddCUDAArchitectureFlags(std::string& flags) const; + void AddCUDAArchitectureFlags(cmBuildStep compileOrLink, + const std::string& config, + std::string& flags) const; void AddCUDAToolkitFlags(std::string& flags) const; void AddHIPArchitectureFlags(std::string& flags) const; @@ -1196,4 +1199,34 @@ public: bool operator()(cmGeneratorTarget const* t1, cmGeneratorTarget const* t2) const; }; + + // C++20 module support queries. + + /** + * Query whether the target expects C++20 module support. + * + * This will inspect the target itself to see if C++20 module + * support is expected to work based on its sources. + */ + bool HaveCxx20ModuleSources() const; + + enum class Cxx20SupportLevel + { + // C++ is not available. + MissingCxx, + // The experimental feature is not available. + MissingExperimentalFlag, + // The target does not require at least C++20. + NoCxx20, + // C++20 modules are available and working. + Supported, + }; + /** + * Query whether the target has C++20 module support available (regardless of + * whether it is required or not). + */ + Cxx20SupportLevel HaveCxxModuleSupport(std::string const& config) const; + + // Check C++ module status for the target. + void CheckCxxModuleStatus(std::string const& config) const; }; diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index bf019c3..138d3f1 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -183,8 +183,8 @@ void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config, auto i = this->FlagsByLanguage.find(language); if (i == this->FlagsByLanguage.end()) { std::string flags; - this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, - language, config); + this->LocalGenerator->AddLanguageFlags( + flags, this->GeneratorTarget, cmBuildStep::Compile, language, config); this->LocalGenerator->AddCMP0018Flags(flags, this->GeneratorTarget, language, config); this->LocalGenerator->AddVisibilityPresetFlags( diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 0fe55f0..c2bf888 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -2554,9 +2554,9 @@ bool cmGlobalGenerator::NameResolvesToFramework( // 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) -cm::optional<std::pair<std::string, std::string>> +cm::optional<cmGlobalGenerator::FrameworkDescriptor> cmGlobalGenerator::SplitFrameworkPath(const std::string& path, - bool extendedFormat) const + FrameworkFormat format) const { // Check for framework structure: // (/path/to/)?FwName.framework @@ -2571,20 +2571,29 @@ cmGlobalGenerator::SplitFrameworkPath(const std::string& path, auto name = frameworkPath.match(3); auto libname = cmSystemTools::GetFilenameWithoutExtension(frameworkPath.match(6)); + if (format == FrameworkFormat::Strict && libname.empty()) { + return cm::nullopt; + } if (!libname.empty() && !cmHasPrefix(libname, name)) { return cm::nullopt; } - return std::pair<std::string, std::string>{ frameworkPath.match(2), name }; + + if (libname.empty() || name.size() == libname.size()) { + return FrameworkDescriptor{ frameworkPath.match(2), name }; + } + + return FrameworkDescriptor{ frameworkPath.match(2), name, + libname.substr(name.size()) }; } - if (extendedFormat) { + if (format == FrameworkFormat::Extended) { // path format can be more flexible: (/path/to/)?fwName(.framework)? auto fwDir = cmSystemTools::GetParentDirectory(path); auto name = cmSystemTools::GetFilenameLastExtension(path) == ".framework" ? cmSystemTools::GetFilenameWithoutExtension(path) : cmSystemTools::GetFilenameName(path); - return std::pair<std::string, std::string>{ fwDir, name }; + return FrameworkDescriptor{ fwDir, name }; } return cm::nullopt; diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 6e3072b..076b041 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -17,6 +17,7 @@ #include <cm/optional> #include <cmext/algorithm> +#include <cmext/string_view> #include "cm_codecvt.hxx" @@ -367,13 +368,61 @@ public: /** Determine if a name resolves to a framework on disk or a built target that is a framework. */ bool NameResolvesToFramework(const std::string& libname) const; - /** Split a framework path to the directory and name of the framework - * returns std::nullopt if the path does not match with framework format + /** Split a framework path to the directory and name of the framework as well + * as optional suffix. + * Returns std::nullopt if the path does not match with framework format * when extendedFormat is true, required format is relaxed (i.e. extension * `.framework' is optional). Used when FRAMEWORK link feature is * specified */ - cm::optional<std::pair<std::string, std::string>> SplitFrameworkPath( - const std::string& path, bool extendedFormat = false) const; + struct FrameworkDescriptor + { + FrameworkDescriptor(std::string directory, std::string name) + : Directory(std::move(directory)) + , Name(std::move(name)) + { + } + FrameworkDescriptor(std::string directory, std::string name, + std::string suffix) + : Directory(std::move(directory)) + , Name(std::move(name)) + , Suffix(std::move(suffix)) + { + } + std::string GetLinkName() const + { + return this->Suffix.empty() ? this->Name + : cmStrCat(this->Name, ',', this->Suffix); + } + std::string GetFullName() const + { + return cmStrCat(this->Name, ".framework/"_s, this->Name, this->Suffix); + } + std::string GetFrameworkPath() const + { + return this->Directory.empty() + ? cmStrCat(this->Name, ".framework"_s) + : cmStrCat(this->Directory, '/', this->Name, ".framework"_s); + } + std::string GetFullPath() const + { + return this->Directory.empty() + ? this->GetFullName() + : cmStrCat(this->Directory, '/', this->GetFullName()); + } + + const std::string Directory; + const std::string Name; + const std::string Suffix; + }; + enum class FrameworkFormat + { + Strict, + Relaxed, + Extended + }; + cm::optional<FrameworkDescriptor> SplitFrameworkPath( + const std::string& path, + FrameworkFormat format = FrameworkFormat::Relaxed) const; cmMakefile* FindMakefile(const std::string& start_dir) const; cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 6248f09..077de42 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -3,8 +3,10 @@ #include "cmGlobalNinjaGenerator.h" #include <algorithm> +#include <cassert> #include <cctype> #include <cstdio> +#include <functional> #include <sstream> #include <utility> @@ -14,6 +16,7 @@ #include <cm/string_view> #include <cmext/algorithm> #include <cmext/memory> +#include <cmext/string_view> #include <cm3p/json/reader.h> #include <cm3p/json/value.h> @@ -21,7 +24,9 @@ #include "cmsys/FStream.hxx" +#include "cmCxxModuleMapper.h" #include "cmDocumentationEntry.h" +#include "cmFileSet.h" #include "cmFortranParser.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpressionEvaluationFile.h" @@ -2467,13 +2472,53 @@ 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, std::string const& arg_dd, std::vector<std::string> const& arg_ddis, std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, - std::string const& arg_lang, std::string const& arg_modmapfmt) + std::string const& arg_lang, std::string const& arg_modmapfmt, + CxxModuleExportInfo const& export_info) { // Setup path conversions. { @@ -2498,13 +2543,15 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( objects.push_back(std::move(info)); } + CxxModuleUsage usages; + // Map from module name to module file path, if known. std::map<std::string, std::string> mod_files; // Populate the module map with those provided by linked targets first. for (std::string const& linked_target_dir : linked_target_dirs) { std::string const ltmn = - cmStrCat(linked_target_dir, "/", arg_lang, "Modules.json"); + cmStrCat(linked_target_dir, '/', arg_lang, "Modules.json"); Json::Value ltm; cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary); Json::Reader reader; @@ -2515,21 +2562,71 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( return false; } if (ltm.isObject()) { - for (Json::Value::iterator i = ltm.begin(); i != ltm.end(); ++i) { - mod_files[i.key().asString()] = i->asString(); + Json::Value const& target_modules = ltm["modules"]; + if (target_modules.isObject()) { + for (auto i = target_modules.begin(); i != target_modules.end(); ++i) { + mod_files[i.key().asString()] = i->asString(); + } + } + Json::Value const& target_modules_references = ltm["references"]; + if (target_modules_references.isObject()) { + for (auto i = target_modules_references.begin(); + i != target_modules_references.end(); ++i) { + if (i->isObject()) { + Json::Value const& reference_path = (*i)["path"]; + CxxModuleReference module_reference; + if (reference_path.isString()) { + module_reference.Path = reference_path.asString(); + } + Json::Value const& reference_method = (*i)["lookup-method"]; + if (reference_method.isString()) { + std::string reference = reference_method.asString(); + if (reference == "by-name") { + module_reference.Method = LookupMethod::ByName; + } else if (reference == "include-angle") { + module_reference.Method = LookupMethod::IncludeAngle; + } else if (reference == "include-quote") { + module_reference.Method = LookupMethod::IncludeQuote; + } + } + usages.Reference[i.key().asString()] = module_reference; + } + } + } + Json::Value const& target_modules_usage = ltm["usages"]; + if (target_modules_usage.isObject()) { + for (auto i = target_modules_usage.begin(); + i != target_modules_usage.end(); ++i) { + if (i->isArray()) { + for (auto j = i->begin(); j != i->end(); ++j) { + usages.Usage[i.key().asString()].insert(j->asString()); + } + } + } } } } - const char* module_ext = ""; - if (arg_modmapfmt == "gcc") { - module_ext = ".gcm"; + cm::optional<CxxModuleMapFormat> modmap_fmt; + if (arg_modmapfmt.empty()) { + // nothing to do. + } else if (arg_modmapfmt == "gcc") { + modmap_fmt = CxxModuleMapFormat::Gcc; + } else if (arg_modmapfmt == "msvc") { + modmap_fmt = CxxModuleMapFormat::Msvc; + } else { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_dyndep does not understand the ", arg_modmapfmt, + " module map format")); + return false; } + auto module_ext = CxxModuleMapExtension(modmap_fmt); + // Extend the module map with those provided by this target. // We do this after loading the modules provided by linked targets // in case we have one of the same name that must be preferred. - Json::Value tm = Json::objectValue; + Json::Value target_modules = Json::objectValue; for (cmScanDepInfo const& object : objects) { for (auto const& p : object.Provides) { std::string mod; @@ -2542,12 +2639,13 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( } } else { // Assume the module file path matches the logical module name. - std::string safe_logical_name = p.LogicalName; + std::string safe_logical_name = + p.LogicalName; // TODO: needs fixing for header units cmSystemTools::ReplaceString(safe_logical_name, ":", "-"); mod = cmStrCat(module_dir, safe_logical_name, module_ext); } mod_files[p.LogicalName] = mod; - tm[p.LogicalName] = mod; + target_modules[p.LogicalName] = mod; } } @@ -2555,6 +2653,32 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( ddf << "ninja_dyndep_version = 1.0\n"; { + CxxModuleLocations locs; + locs.RootDirectory = "."; + locs.PathForGenerator = [this](std::string const& path) -> std::string { + return this->ConvertToNinjaPath(path); + }; + locs.BmiLocationForModule = + [&mod_files](std::string const& logical) -> cm::optional<std::string> { + auto m = mod_files.find(logical); + if (m != mod_files.end()) { + return m->second; + } + return {}; + }; + + // Insert information about the current target's modules. + if (modmap_fmt) { + auto cycle_modules = CxxModuleUsageSeed(locs, objects, usages); + if (!cycle_modules.empty()) { + cmSystemTools::Error( + cmStrCat("Circular dependency detected in the C++ module import " + "graph. See modules named: \"", + cmJoin(cycle_modules, R"(", ")"_s), '"')); + return false; + } + } + cmNinjaBuild build("dyndep"); build.Outputs.emplace_back(""); for (cmScanDepInfo const& object : objects) { @@ -2576,60 +2700,332 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( build.Variables.emplace("restat", "1"); } - if (arg_modmapfmt.empty()) { - // nothing to do. - } else { - std::stringstream mm; - if (arg_modmapfmt == "gcc") { - // Documented in GCC's documentation. The format is a series of lines - // with a module name and the associated filename separated by - // spaces. The first line may use `$root` as the module name to - // specify a "repository root". That is used to anchor any relative - // paths present in the file (CMake should never generate any). - - // Write the root directory to use for module paths. - mm << "$root .\n"; - - for (auto const& l : object.Provides) { - auto m = mod_files.find(l.LogicalName); - if (m != mod_files.end()) { - mm << l.LogicalName << " " << this->ConvertToNinjaPath(m->second) - << "\n"; - } - } - for (auto const& r : object.Requires) { - auto m = mod_files.find(r.LogicalName); - if (m != mod_files.end()) { - mm << r.LogicalName << " " << this->ConvertToNinjaPath(m->second) - << "\n"; - } - } - } else { - cmSystemTools::Error( - cmStrCat("-E cmake_ninja_dyndep does not understand the ", - arg_modmapfmt, " module map format")); - return false; - } + if (modmap_fmt) { + auto mm = CxxModuleMapContent(*modmap_fmt, locs, object, usages); // XXX(modmap): If changing this path construction, change // `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the // corresponding file path. cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap")); - mmf << mm.str(); + mmf << mm; } this->WriteBuild(ddf, build); } } + Json::Value target_module_info = Json::objectValue; + target_module_info["modules"] = target_modules; + + auto& target_usages = target_module_info["usages"] = Json::objectValue; + for (auto const& u : usages.Usage) { + auto& mod_usage = target_usages[u.first] = Json::arrayValue; + for (auto const& v : u.second) { + mod_usage.append(v); + } + } + + auto name_for_method = [](LookupMethod method) -> cm::static_string_view { + switch (method) { + case LookupMethod::ByName: + return "by-name"_s; + case LookupMethod::IncludeAngle: + return "include-angle"_s; + case LookupMethod::IncludeQuote: + return "include-quote"_s; + } + assert(false && "unsupported lookup method"); + return ""_s; + }; + + auto& target_references = target_module_info["references"] = + Json::objectValue; + for (auto const& r : usages.Reference) { + auto& mod_ref = target_references[r.first] = Json::objectValue; + mod_ref["path"] = r.second.Path; + mod_ref["lookup-method"] = std::string(name_for_method(r.second.Method)); + } + // Store the map of modules provided by this target in a file for // use by dependents that reference this target in linked-target-dirs. std::string const target_mods_file = cmStrCat( cmSystemTools::GetFilenamePath(arg_dd), '/', arg_lang, "Modules.json"); cmGeneratedFileStream tmf(target_mods_file); - tmf << tm; + 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))); + }; - return true; + // 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; + } + } + } + } + + return result; } int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, @@ -2703,6 +3099,59 @@ 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(); + } + } + } + cmake cm(cmake::RoleInternal, cmState::Unknown); cm.SetHomeDirectory(dir_top_src); cm.SetHomeOutputDirectory(dir_top_bld); @@ -2710,7 +3159,8 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, if (!ggd || !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)) { + module_dir, linked_target_dirs, arg_lang, arg_modmapfmt, + export_info)) { return 1; } return 0; diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 03387a8..defa264 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -417,13 +417,15 @@ 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, std::string const& arg_dd, std::vector<std::string> const& arg_ddis, std::string const& module_dir, std::vector<std::string> const& linked_target_dirs, - std::string const& arg_lang, std::string const& arg_modmapfmt); + std::string const& arg_lang, std::string const& arg_modmapfmt, + CxxModuleExportInfo const& export_info); virtual std::string BuildAlias(const std::string& alias, const std::string& /*config*/) const diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx index 29eeb5a..bea2ae7 100644 --- a/Source/cmGlobalVisualStudio10Generator.cxx +++ b/Source/cmGlobalVisualStudio10Generator.cxx @@ -20,7 +20,6 @@ #include "cmDocumentationEntry.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" -#include "cmGlobalGeneratorFactory.h" #include "cmGlobalVisualStudio71Generator.h" #include "cmGlobalVisualStudio7Generator.h" #include "cmGlobalVisualStudioGenerator.h" @@ -38,7 +37,6 @@ #include "cmXMLWriter.h" #include "cmake.h" -static const char vs10generatorName[] = "Visual Studio 10 2010"; static std::map<std::string, std::vector<cmIDEFlagTable>> loadedFlagJsonFiles; static void ConvertToWindowsSlashes(std::string& s) @@ -51,137 +49,14 @@ static void ConvertToWindowsSlashes(std::string& s) } } -// Map generator name without year to name with year. -static const char* cmVS10GenName(const std::string& name, std::string& genName) -{ - if (strncmp(name.c_str(), vs10generatorName, - sizeof(vs10generatorName) - 6) != 0) { - return 0; - } - const char* p = name.c_str() + sizeof(vs10generatorName) - 6; - if (cmHasLiteralPrefix(p, " 2010")) { - p += 5; - } - genName = std::string(vs10generatorName) + p; - return p; -} - -class cmGlobalVisualStudio10Generator::Factory - : public cmGlobalGeneratorFactory -{ -public: - std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator( - const std::string& name, bool allowArch, cmake* cm) const override - { - std::string genName; - const char* p = cmVS10GenName(name, genName); - if (!p) { - return std::unique_ptr<cmGlobalGenerator>(); - } - if (!*p) { - return std::unique_ptr<cmGlobalGenerator>( - new cmGlobalVisualStudio10Generator(cm, genName, "")); - } - if (!allowArch || *p++ != ' ') { - return std::unique_ptr<cmGlobalGenerator>(); - } - if (strcmp(p, "Win64") == 0) { - return std::unique_ptr<cmGlobalGenerator>( - new cmGlobalVisualStudio10Generator(cm, genName, "x64")); - } - if (strcmp(p, "IA64") == 0) { - return std::unique_ptr<cmGlobalGenerator>( - new cmGlobalVisualStudio10Generator(cm, genName, "Itanium")); - } - return std::unique_ptr<cmGlobalGenerator>(); - } - - void GetDocumentation(cmDocumentationEntry& entry) const override - { - entry.Name = std::string(vs10generatorName) + " [arch]"; - entry.Brief = "Deprecated. Generates Visual Studio 2010 project files. " - "Optional [arch] can be \"Win64\" or \"IA64\"."; - } - - std::vector<std::string> GetGeneratorNames() const override - { - std::vector<std::string> names; - names.push_back(vs10generatorName); - return names; - } - - std::vector<std::string> GetGeneratorNamesWithPlatform() const override - { - std::vector<std::string> names; - names.push_back(vs10generatorName + std::string(" IA64")); - names.push_back(vs10generatorName + std::string(" Win64")); - return names; - } - - bool SupportsToolset() const override { return true; } - bool SupportsPlatform() const override { return true; } - - std::vector<std::string> GetKnownPlatforms() const override - { - std::vector<std::string> platforms; - platforms.emplace_back("x64"); - platforms.emplace_back("Win32"); - platforms.emplace_back("Itanium"); - return platforms; - } - - std::string GetDefaultPlatformName() const override { return "Win32"; } -}; - -std::unique_ptr<cmGlobalGeneratorFactory> -cmGlobalVisualStudio10Generator::NewFactory() -{ - return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory); -} - cmGlobalVisualStudio10Generator::cmGlobalVisualStudio10Generator( cmake* cm, const std::string& name, std::string const& platformInGeneratorName) : cmGlobalVisualStudio8Generator(cm, name, platformInGeneratorName) { - std::string vc10Express; - this->ExpressEdition = cmSystemTools::ReadRegistryValue( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\10.0\\Setup\\VC;" - "ProductDir", - vc10Express, cmSystemTools::KeyWOW64_32); - this->CudaEnabled = false; - { - std::string envPlatformToolset; - if (cmSystemTools::GetEnv("PlatformToolset", envPlatformToolset) && - envPlatformToolset == "Windows7.1SDK") { - // We are running from a Windows7.1SDK command prompt. - this->DefaultPlatformToolset = "Windows7.1SDK"; - } else { - this->DefaultPlatformToolset = "v100"; - } - } - this->DefaultCLFlagTableName = "v10"; - this->DefaultCSharpFlagTableName = "v10"; - this->DefaultLibFlagTableName = "v10"; - this->DefaultLinkFlagTableName = "v10"; this->DefaultCudaFlagTableName = "v10"; this->DefaultCudaHostFlagTableName = "v10"; - this->DefaultMasmFlagTableName = "v10"; this->DefaultNasmFlagTableName = "v10"; - this->DefaultRCFlagTableName = "v10"; - - this->Version = VSVersion::VS10; - this->PlatformToolsetNeedsDebugEnum = false; -} - -bool cmGlobalVisualStudio10Generator::MatchesGeneratorName( - const std::string& name) const -{ - std::string genName; - if (cmVS10GenName(name, genName)) { - return genName == this->GetName(); - } - return false; } bool cmGlobalVisualStudio10Generator::SetSystemName(std::string const& s, @@ -195,21 +70,6 @@ bool cmGlobalVisualStudio10Generator::SetSystemName(std::string const& s, return this->cmGlobalVisualStudio8Generator::SetSystemName(s, mf); } -bool cmGlobalVisualStudio10Generator::SetGeneratorPlatform( - std::string const& p, cmMakefile* mf) -{ - if (!this->cmGlobalVisualStudio8Generator::SetGeneratorPlatform(p, mf)) { - return false; - } - if (this->GetPlatformName() == "Itanium" || - this->GetPlatformName() == "x64") { - if (this->IsExpressEdition() && !this->Find64BitTools(mf)) { - return false; - } - } - return true; -} - static void cmCudaToolVersion(std::string& s) { // "CUDA x.y.props" => "x.y" @@ -389,6 +249,27 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset( this->GeneratorToolsetVersion.clear(); this->GeneratorToolsetVersionProps = {}; } break; + case AuxToolset::PropsIndeterminate: { + std::ostringstream e; + /* clang-format off */ + e << + "Generator\n" + " " << this->GetName() << "\n" + "given toolset and version specification\n" + " " << this->GetPlatformToolsetString() << ",version=" << + this->GeneratorToolsetVersion << "\n" + "has multiple matches installed at\n" << + " " << auxProps << "\n" << + "The toolset and version specification must resolve \n" << + "to a single installed toolset"; + ; + /* clang-format on */ + mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); + + // Clear the configured tool-set + this->GeneratorToolsetVersion.clear(); + this->GeneratorToolsetVersionProps = {}; + } break; } } @@ -1286,44 +1167,6 @@ cmGlobalVisualStudio10Generator::GenerateBuildCommand( return makeCommands; } -bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf) -{ - if (this->DefaultPlatformToolset == "v100") { - // The v100 64-bit toolset does not exist in the express edition. - this->DefaultPlatformToolset.clear(); - } - if (this->GetPlatformToolset()) { - return true; - } - // This edition does not come with 64-bit tools. Look for them. - // - // TODO: Detect available tools? x64\v100 exists but does not work? - // HKLM\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\4.0;VCTargetsPath - // c:/Program Files (x86)/MSBuild/Microsoft.Cpp/v4.0/Platforms/ - // {Itanium,Win32,x64}/PlatformToolsets/{v100,v90,Windows7.1SDK} - std::string winSDK_7_1; - if (cmSystemTools::ReadRegistryValue( - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\" - "Windows\\v7.1;InstallationFolder", - winSDK_7_1)) { - std::ostringstream m; - m << "Found Windows SDK v7.1: " << winSDK_7_1; - mf->DisplayStatus(m.str(), -1); - this->DefaultPlatformToolset = "Windows7.1SDK"; - return true; - } else { - std::ostringstream e; - /* clang-format off */ - e << "Cannot enable 64-bit tools with Visual Studio 2010 Express.\n" - << "Install the Microsoft Windows SDK v7.1 to get 64-bit tools:\n" - << " http://msdn.microsoft.com/en-us/windows/bb980924.aspx"; - /* clang-format on */ - mf->IssueMessage(MessageType::FATAL_ERROR, e.str().c_str()); - cmSystemTools::SetFatalErrorOccurred(); - return false; - } -} - std::string cmGlobalVisualStudio10Generator::GenerateRuleFile( std::string const& output) const { @@ -1361,7 +1204,6 @@ const char* cmGlobalVisualStudio10Generator::GetToolsVersion() const { switch (this->Version) { case cmGlobalVisualStudioGenerator::VSVersion::VS9: - case cmGlobalVisualStudioGenerator::VSVersion::VS10: case cmGlobalVisualStudioGenerator::VSVersion::VS11: return "4.0"; diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h index 4977a84..b32c0a7 100644 --- a/Source/cmGlobalVisualStudio10Generator.h +++ b/Source/cmGlobalVisualStudio10Generator.h @@ -14,7 +14,6 @@ #include "cmGlobalVisualStudio8Generator.h" class cmGeneratorTarget; -class cmGlobalGeneratorFactory; class cmLocalGenerator; class cmMakefile; class cmSourceFile; @@ -29,14 +28,9 @@ struct cmIDEFlagTable; class cmGlobalVisualStudio10Generator : public cmGlobalVisualStudio8Generator { public: - static std::unique_ptr<cmGlobalGeneratorFactory> NewFactory(); - bool IsVisualStudioAtLeast10() const override { return true; } - bool MatchesGeneratorName(const std::string& name) const override; - bool SetSystemName(std::string const& s, cmMakefile* mf) override; - bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override; bool SetGeneratorToolset(std::string const& ts, bool build, cmMakefile* mf) override; @@ -131,7 +125,6 @@ public: bool TargetsAndroid() const { return this->SystemIsAndroid; } const char* GetCMakeCFGIntDir() const override { return "$(Configuration)"; } - bool Find64BitTools(cmMakefile* mf); /** Generate an <output>.rule file path for a given command output. */ std::string GenerateRuleFile(std::string const& output) const override; @@ -200,7 +193,8 @@ protected: None, Default, PropsExist, - PropsMissing + PropsMissing, + PropsIndeterminate }; virtual AuxToolset FindAuxToolset(std::string& version, std::string& props) const; @@ -243,9 +237,6 @@ protected: bool MSBuildCommandInitialized = false; private: - class Factory; - friend class Factory; - struct LongestSourcePath { LongestSourcePath() @@ -269,7 +260,7 @@ private: std::string GeneratorToolsetVersion; - bool PlatformToolsetNeedsDebugEnum; + bool PlatformToolsetNeedsDebugEnum = false; bool ParseGeneratorToolset(std::string const& ts, cmMakefile* mf); @@ -290,7 +281,7 @@ private: std::string VCTargetsPath; bool FindVCTargetsPath(cmMakefile* mf); - bool CudaEnabled; + bool CudaEnabled = false; // We do not use the reload macros for VS >= 10. std::string GetUserMacrosDirectory() override { return ""; } diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx index 10dc258..086d3af 100644 --- a/Source/cmGlobalVisualStudio11Generator.cxx +++ b/Source/cmGlobalVisualStudio11Generator.cxx @@ -77,7 +77,7 @@ public: void GetDocumentation(cmDocumentationEntry& entry) const override { entry.Name = std::string(vs11generatorName) + " [arch]"; - entry.Brief = "Generates Visual Studio 2012 project files. " + entry.Brief = "Deprecated. Generates Visual Studio 2012 project files. " "Optional [arch] can be \"Win64\" or \"ARM\"."; } diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index f7f7317..ff76762 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -304,23 +304,23 @@ void cmGlobalVisualStudio7Generator::Generate() GetSLNFile(this->LocalGenerators[0].get())); } - if (this->Version == VSVersion::VS10 && + if (this->Version == VSVersion::VS11 && !this->CMakeInstance->GetIsInTryCompile()) { - std::string cmakeWarnVS10; + std::string cmakeWarnVS11; if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue( - "CMAKE_WARN_VS10")) { - this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS10"); - cmakeWarnVS10 = *cached; + "CMAKE_WARN_VS11")) { + this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS11"); + cmakeWarnVS11 = *cached; } else { - cmSystemTools::GetEnv("CMAKE_WARN_VS10", cmakeWarnVS10); + cmSystemTools::GetEnv("CMAKE_WARN_VS11", cmakeWarnVS11); } - if (cmakeWarnVS10.empty() || !cmIsOff(cmakeWarnVS10)) { + if (cmakeWarnVS11.empty() || !cmIsOff(cmakeWarnVS11)) { this->CMakeInstance->IssueMessage( MessageType::WARNING, - "The \"Visual Studio 10 2010\" generator is deprecated " + "The \"Visual Studio 11 2012\" generator is deprecated " "and will be removed in a future version of CMake." "\n" - "Add CMAKE_WARN_VS10=OFF to the cache to disable this warning."); + "Add CMAKE_WARN_VS11=OFF to the cache to disable this warning."); } } } @@ -395,12 +395,27 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution( { VisualStudioFolders.clear(); + std::vector<std::string> configs = + root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig); + for (cmGeneratorTarget const* target : projectTargets) { if (!this->IsInSolution(target)) { continue; } bool written = false; + for (auto const& c : configs) { + target->CheckCxxModuleStatus(c); + } + + if (target->HaveCxx20ModuleSources() && !this->SupportsCxxModuleDyndep()) { + root->GetMakefile()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", target->GetName(), + "\" target contains C++ module sources which are not " + "supported by the generator")); + } + // handle external vc project files cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT"); if (expath) { diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index a55cf45..288069c 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -157,6 +157,8 @@ protected: cmValue typeGuid, const std::set<BT<std::pair<std::string, bool>>>& dependencies) = 0; + virtual bool SupportsCxxModuleDyndep() const { return false; } + std::string ConvertToSolutionPath(const std::string& path); std::set<std::string> IsPartOfDefaultBuild( diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 819d6be..9d168d0 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -99,8 +99,6 @@ const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const switch (this->Version) { case cmGlobalVisualStudioGenerator::VSVersion::VS9: return "9.0"; - case cmGlobalVisualStudioGenerator::VSVersion::VS10: - return "10.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS11: return "11.0"; case cmGlobalVisualStudioGenerator::VSVersion::VS12: @@ -128,14 +126,6 @@ void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout) fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n"; fout << "# Visual Studio 2008\n"; break; - case cmGlobalVisualStudioGenerator::VSVersion::VS10: - fout << "Microsoft Visual Studio Solution File, Format Version 11.00\n"; - if (this->ExpressEdition) { - fout << "# Visual C++ Express 2010\n"; - } else { - fout << "# Visual Studio 2010\n"; - } - break; case cmGlobalVisualStudioGenerator::VSVersion::VS11: fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n"; if (this->ExpressEdition) { diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index f45b4d4..576e4f2 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -35,7 +35,6 @@ public: enum class VSVersion : uint16_t { VS9 = 90, - VS10 = 100, VS11 = 110, VS12 = 120, /* VS13 = 130 was skipped */ diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx index 7e36881..be318c1 100644 --- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx +++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx @@ -127,8 +127,6 @@ static unsigned int VSVersionToMajor( switch (v) { case cmGlobalVisualStudioGenerator::VSVersion::VS9: return 9; - case cmGlobalVisualStudioGenerator::VSVersion::VS10: - return 10; case cmGlobalVisualStudioGenerator::VSVersion::VS11: return 11; case cmGlobalVisualStudioGenerator::VSVersion::VS12: @@ -151,8 +149,6 @@ static const char* VSVersionToToolset( switch (v) { case cmGlobalVisualStudioGenerator::VSVersion::VS9: return "v90"; - case cmGlobalVisualStudioGenerator::VSVersion::VS10: - return "v100"; case cmGlobalVisualStudioGenerator::VSVersion::VS11: return "v110"; case cmGlobalVisualStudioGenerator::VSVersion::VS12: @@ -175,8 +171,6 @@ static std::string VSVersionToMajorString( switch (v) { case cmGlobalVisualStudioGenerator::VSVersion::VS9: return "9"; - case cmGlobalVisualStudioGenerator::VSVersion::VS10: - return "10"; case cmGlobalVisualStudioGenerator::VSVersion::VS11: return "11"; case cmGlobalVisualStudioGenerator::VSVersion::VS12: @@ -198,7 +192,6 @@ static const char* VSVersionToAndroidToolset( { switch (v) { case cmGlobalVisualStudioGenerator::VSVersion::VS9: - case cmGlobalVisualStudioGenerator::VSVersion::VS10: case cmGlobalVisualStudioGenerator::VSVersion::VS11: case cmGlobalVisualStudioGenerator::VSVersion::VS12: return ""; @@ -500,7 +493,6 @@ bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName( std::string genName; switch (this->Version) { case cmGlobalVisualStudioGenerator::VSVersion::VS9: - case cmGlobalVisualStudioGenerator::VSVersion::VS10: case cmGlobalVisualStudioGenerator::VSVersion::VS11: case cmGlobalVisualStudioGenerator::VSVersion::VS12: case cmGlobalVisualStudioGenerator::VSVersion::VS14: @@ -743,7 +735,6 @@ cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision() { switch (this->Version) { case cmGlobalVisualStudioGenerator::VSVersion::VS9: - case cmGlobalVisualStudioGenerator::VSVersion::VS10: case cmGlobalVisualStudioGenerator::VSVersion::VS11: case cmGlobalVisualStudioGenerator::VSVersion::VS12: return ""; @@ -770,12 +761,15 @@ cmGlobalVisualStudioVersionedGenerator::FindAuxToolset( cmSystemTools::ConvertToUnixSlashes(instancePath); // Translate three-component format accepted by "vcvarsall -vcvars_ver=". - cmsys::RegularExpression threeComponent( + cmsys::RegularExpression threeComponentRegex( "^([0-9]+\\.[0-9]+)\\.[0-9][0-9][0-9][0-9][0-9]$"); - if (threeComponent.find(version)) { + // The two-component format represents the two major components of the + // three-component format + cmsys::RegularExpression twoComponentRegex("^([0-9]+\\.[0-9]+)$"); + if (threeComponentRegex.find(version)) { // Load "VC/Auxiliary/Build/*/Microsoft.VCToolsVersion.*.txt" files // with two matching components to check their three-component version. - std::string const& twoComponent = threeComponent.match(1); + std::string const& twoComponent = threeComponentRegex.match(1); std::string pattern = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent, "*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s); @@ -801,6 +795,36 @@ cmGlobalVisualStudioVersionedGenerator::FindAuxToolset( } } } + } else if (twoComponentRegex.find(version)) { + std::string const& twoComponent = twoComponentRegex.match(1); + std::string pattern = + cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent, + "*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s); + cmsys::Glob glob; + glob.SetRecurseThroughSymlinks(false); + if (glob.FindFiles(pattern) && !glob.GetFiles().empty()) { + // Since we are only using the first two components of the + // toolset version, we require a single match. + if (glob.GetFiles().size() == 1) { + std::string const& txt = glob.GetFiles()[0]; + std::string ver; + cmsys::ifstream fin(txt.c_str()); + if (fin && std::getline(fin, ver)) { + // Strip trailing whitespace. + ver = ver.substr(0, ver.find_first_not_of("0123456789.")); + // We assume the version is correct, since it is the only one that + // matched. + cmsys::RegularExpression extractVersion( + "VCToolsVersion\\.([0-9.]+)\\.txt$"); + if (extractVersion.find(txt)) { + version = extractVersion.match(1); + } + } + } else { + props = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s); + return AuxToolset::PropsIndeterminate; + } + } } if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) { diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 23c365a..116e510 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -468,6 +468,10 @@ bool cmGlobalXCodeGenerator::Open(const std::string& bindir, } CFRelease(cfStr); } +#else + (void)bindir; + (void)projectName; + (void)dryRun; #endif return ret; @@ -603,7 +607,6 @@ std::string cmGlobalXCodeGenerator::PostBuildMakeTarget( } #define CMAKE_CHECK_BUILD_SYSTEM_TARGET "ZERO_CHECK" -#define OBJECT_LIBRARY_ARTIFACT_DIR std::string() void cmGlobalXCodeGenerator::AddExtraTargets( cmLocalGenerator* root, std::vector<cmLocalGenerator*>& gens) @@ -1188,13 +1191,9 @@ std::string GetTargetObjectDirArch(T const& target, std::string cmGlobalXCodeGenerator::GetLibraryOrFrameworkPath( const std::string& path) const { - auto fwItems = this->SplitFrameworkPath(path); - if (fwItems) { - if (fwItems->first.empty()) { - return cmStrCat(fwItems->second, ".framework"); - } else { - return cmStrCat(fwItems->first, '/', fwItems->second, ".framework"); - } + auto fwDescriptor = this->SplitFrameworkPath(path); + if (fwDescriptor) { + return fwDescriptor->GetFrameworkPath(); } return path; @@ -1372,6 +1371,18 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget( return true; } + for (std::string const& configName : this->CurrentConfigurationTypes) { + gtgt->CheckCxxModuleStatus(configName); + } + + if (gtgt->HaveCxx20ModuleSources()) { + gtgt->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", gtgt->GetName(), + "\" target contains C++ module sources which are not " + "supported by the generator")); + } + auto& gtgt_visited = this->CommandsVisited[gtgt]; auto& deps = this->GetTargetDirectDepends(gtgt); for (auto& d : deps) { @@ -2352,8 +2363,8 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, std::string& flags = cflags[lang]; // Add language-specific flags. - this->CurrentLocalGenerator->AddLanguageFlags(flags, gtgt, lang, - configName); + this->CurrentLocalGenerator->AddLanguageFlags( + flags, gtgt, cmBuildStep::Compile, lang, configName); if (gtgt->IsIPOEnabled(lang, configName)) { this->CurrentLocalGenerator->AppendFeatureOptions(flags, lang, "IPO"); @@ -2376,7 +2387,20 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, gtgt->GetName()); return; } - std::string const& langForPreprocessor = llang; + + // Choose a language to use for target-wide preprocessor definitions. + static const char* ppLangs[] = { "CXX", "C", "OBJCXX", "OBJC" }; + std::string langForPreprocessorDefinitions; + if (cm::contains(ppLangs, llang)) { + langForPreprocessorDefinitions = llang; + } else { + for (const char* l : ppLangs) { + if (languages.count(l)) { + langForPreprocessorDefinitions = l; + break; + } + } + } if (gtgt->IsIPOEnabled(llang, configName)) { const char* ltoValue = @@ -2393,25 +2417,45 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, // Add preprocessor definitions for this target and configuration. BuildObjectListOrString ppDefs(this, true); - if (languages.count("Swift")) { - // FIXME: Xcode warns that Swift does not support definition values. - // C/CXX sources mixed in Swift targets will not see CMAKE_INTDIR. - } else { - this->AppendDefines( - ppDefs, "CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\""); - } + this->AppendDefines( + ppDefs, "CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\""); if (const std::string* exportMacro = gtgt->GetExportMacro()) { // Add the export symbol definition for shared library objects. this->AppendDefines(ppDefs, exportMacro->c_str()); } std::vector<std::string> targetDefines; - if (!langForPreprocessor.empty()) { + if (!langForPreprocessorDefinitions.empty()) { gtgt->GetCompileDefinitions(targetDefines, configName, - langForPreprocessor); + langForPreprocessorDefinitions); } this->AppendDefines(ppDefs, targetDefines); buildSettings->AddAttribute("GCC_PREPROCESSOR_DEFINITIONS", ppDefs.CreateList()); + if (languages.count("Swift")) { + // Swift uses a separate attribute for definitions. + std::vector<std::string> targetSwiftDefines; + gtgt->GetCompileDefinitions(targetSwiftDefines, configName, "Swift"); + // Remove the '=value' parts, as Swift does not support them. + std::for_each(targetSwiftDefines.begin(), targetSwiftDefines.end(), + [](std::string& def) { + std::string::size_type pos = def.find('='); + if (pos != std::string::npos) { + def.erase(pos); + } + }); + if (this->XcodeVersion < 80) { + std::string defineString; + std::set<std::string> defines(targetSwiftDefines.begin(), + targetSwiftDefines.end()); + this->CurrentLocalGenerator->JoinDefines(defines, defineString, "Swift"); + cflags["Swift"] += " " + defineString; + } else { + BuildObjectListOrString swiftDefs(this, true); + this->AppendDefines(swiftDefs, targetSwiftDefines); + buildSettings->AddAttribute("SWIFT_ACTIVE_COMPILATION_CONDITIONS", + swiftDefs.CreateList()); + } + } std::string extraLinkOptionsVar; std::string extraLinkOptions; @@ -2499,18 +2543,28 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, soName += *soversion; } + if (gtgt->CanCompileSources()) { + std::string const tmpDir = + this->GetTargetTempDir(gtgt, this->GetCMakeCFGIntDir()); + buildSettings->AddAttribute("TARGET_TEMP_DIR", this->CreateString(tmpDir)); + + std::string outDir; + if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) { + // We cannot suppress the archive, so hide it with intermediate files. + outDir = tmpDir; + } else { + outDir = gtgt->GetDirectory(configName); + } + buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR", + this->CreateString(outDir)); + } + // Set attributes to specify the proper name for the target. std::string pndir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory(); if (gtgt->GetType() == cmStateEnums::STATIC_LIBRARY || gtgt->GetType() == cmStateEnums::SHARED_LIBRARY || gtgt->GetType() == cmStateEnums::MODULE_LIBRARY || gtgt->GetType() == cmStateEnums::EXECUTABLE) { - if (!gtgt->UsesDefaultOutputDir(configName, - cmStateEnums::RuntimeBinaryArtifact)) { - std::string pncdir = gtgt->GetDirectory(configName); - buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR", - this->CreateString(pncdir)); - } if (gtgt->IsFrameworkOnApple() || gtgt->IsCFBundleOnApple()) { pnprefix = ""; @@ -2520,20 +2574,10 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, this->CreateString(pnprefix)); buildSettings->AddAttribute("EXECUTABLE_SUFFIX", this->CreateString(pnsuffix)); - } else if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) { - pnprefix = "lib"; - pnbase = gtgt->GetName(); - pnsuffix = ".a"; - - std::string pncdir = this->GetObjectsDirectory( - this->CurrentProject, configName, gtgt, OBJECT_LIBRARY_ARTIFACT_DIR); - buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR", - this->CreateString(pncdir)); } // Store the product name for all target types. buildSettings->AddAttribute("PRODUCT_NAME", this->CreateString(realName)); - buildSettings->AddAttribute("SYMROOT", this->CreateString(pndir)); // Handle settings for each target type. switch (gtgt->GetType()) { @@ -2681,10 +2725,12 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, BuildObjectListOrString sysfdirs(this, true); const bool emitSystemIncludes = this->XcodeVersion >= 83; + // Choose a language to use for target-wide include directories. + std::string const& langForIncludes = llang; std::vector<std::string> includes; - if (!langForPreprocessor.empty()) { + if (!langForIncludes.empty()) { this->CurrentLocalGenerator->GetIncludeDirectories( - includes, gtgt, langForPreprocessor, configName); + includes, gtgt, langForIncludes, configName); } std::set<std::string> emitted; emitted.insert("/System/Library/Frameworks"); @@ -2697,7 +2743,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, std::string incpath = this->XCodeEscapePath(frameworkDir); if (emitSystemIncludes && gtgt->IsSystemIncludeDirectory(include, configName, - langForPreprocessor)) { + langForIncludes)) { sysfdirs.Add(incpath); } else { fdirs.Add(incpath); @@ -2707,7 +2753,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, std::string incpath = this->XCodeEscapePath(include); if (emitSystemIncludes && gtgt->IsSystemIncludeDirectory(include, configName, - langForPreprocessor)) { + langForIncludes)) { sysdirs.Add(incpath); } else { dirs.Add(incpath); @@ -2721,7 +2767,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, std::string incpath = this->XCodeEscapePath(fwDir); if (emitSystemIncludes && gtgt->IsSystemIncludeDirectory(fwDir, configName, - langForPreprocessor)) { + langForIncludes)) { sysfdirs.Add(incpath); } else { fdirs.Add(incpath); @@ -2734,6 +2780,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, } if (!dirs.IsEmpty()) { buildSettings->AddAttribute("HEADER_SEARCH_PATHS", dirs.CreateList()); + if (languages.count("Swift")) { + buildSettings->AddAttribute("SWIFT_INCLUDE_PATHS", dirs.CreateList()); + } } if (!sysfdirs.IsEmpty()) { buildSettings->AddAttribute("SYSTEM_FRAMEWORK_SEARCH_PATHS", @@ -3532,28 +3581,37 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) libItem.IsPath == cmComputeLinkInformation::ItemIsPath::Yes && forceLinkPhase))) { std::string libName; - bool canUseLinkPhase = true; - if (libItem.Target) { - if (libItem.Target->GetType() == cmStateEnums::UNKNOWN_LIBRARY) { - canUseLinkPhase = canUseLinkPhase && forceLinkPhase; + bool canUseLinkPhase = !libItem.HasFeature() || + libItem.GetFeatureName() == "__CMAKE_LINK_FRAMEWORK"_s || + libItem.GetFeatureName() == "FRAMEWORK"_s || + libItem.GetFeatureName() == "WEAK_FRAMEWORK"_s || + libItem.GetFeatureName() == "WEAK_LIBRARY"_s; + if (canUseLinkPhase) { + 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 { - // 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 = 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; } } - 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 @@ -3609,6 +3667,7 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) // separately. std::vector<std::string> linkSearchPaths; std::vector<std::string> frameworkSearchPaths; + std::set<std::pair<cmXCodeObject*, std::string>> linkBuildFileSet; for (auto const& libItem : linkPhaseTargetVector) { // Add target output directory as a library search path std::string linkDir; @@ -3618,9 +3677,10 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) linkDir = libItem->Value.Value; } if (cmHasSuffix(libItem->GetFeatureName(), "FRAMEWORK"_s)) { - auto fwItems = this->SplitFrameworkPath(linkDir, true); - if (fwItems && !fwItems->first.empty()) { - linkDir = std::move(fwItems->first); + auto fwDescriptor = this->SplitFrameworkPath( + linkDir, cmGlobalGenerator::FrameworkFormat::Extended); + if (fwDescriptor && !fwDescriptor->Directory.empty()) { + linkDir = fwDescriptor->Directory; if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(), linkDir) == frameworkSearchPaths.end()) { frameworkSearchPaths.push_back(linkDir); @@ -3710,8 +3770,30 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) cmSystemTools::Error("Missing files of PBXFrameworksBuildPhase"); continue; } - if (buildFile && !buildFiles->HasObject(buildFile)) { - buildFiles->AddObject(buildFile); + if (buildFile) { + if (cmHasPrefix(libItem->GetFeatureName(), "WEAK_"_s)) { + auto key = std::make_pair(buildFile->GetAttribute("fileRef"), + libItem->GetFeatureName()); + if (linkBuildFileSet.find(key) != linkBuildFileSet.end()) { + continue; + } + linkBuildFileSet.insert(key); + + cmXCodeObject* buildObject = + this->CreateObject(cmXCodeObject::PBXBuildFile); + buildObject->AddAttribute("fileRef", key.first); + // Add settings, ATTRIBUTES, Weak flag + cmXCodeObject* settings = + this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP); + cmXCodeObject* attrs = this->CreateObject(cmXCodeObject::OBJECT_LIST); + attrs->AddObject(this->CreateString("Weak")); + settings->AddAttribute("ATTRIBUTES", attrs); + buildObject->AddAttribute("settings", settings); + buildFile = buildObject; + } + if (!buildFiles->HasObject(buildFile)) { + buildFiles->AddObject(buildFile); + } } } @@ -3753,14 +3835,20 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) // add the library search paths { BuildObjectListOrString libSearchPaths(this, true); + std::string linkDirs; for (auto const& libDir : cli->GetDirectories()) { if (!libDir.empty() && libDir != "/usr/lib") { - libSearchPaths.Add(this->XCodeEscapePath( - libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)")); + cmPolicies::PolicyStatus cmp0142 = + target->GetTarget()->GetPolicyStatusCMP0142(); + if (cmp0142 == cmPolicies::OLD || cmp0142 == cmPolicies::WARN) { + 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) { @@ -3800,6 +3888,7 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) const auto& fwPaths = cli->GetFrameworkPaths(); emitted.insert(fwPaths.begin(), fwPaths.end()); BuildObjectListOrString libPaths(this, true); + BuildObjectListOrString fwSearchPaths(this, true); for (auto const& libItem : configItemMap[configName]) { auto const& libName = *libItem; if (libName.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) { @@ -3810,13 +3899,14 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) bool isFramework = cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s); if (isFramework) { - const auto fwItems = - this->SplitFrameworkPath(cleanPath, isFramework); - if (!fwItems->first.empty() && - emitted.insert(fwItems->first).second) { + const auto fwDescriptor = this->SplitFrameworkPath( + cleanPath, cmGlobalGenerator::FrameworkFormat::Extended); + if (!fwDescriptor->Directory.empty() && + emitted.insert(fwDescriptor->Directory).second) { // This is a search path we had not added before and it isn't // an implicit search path, so we need it - libPaths.Add("-F " + this->XCodeEscapePath(fwItems->first)); + fwSearchPaths.Add( + this->XCodeEscapePath(fwDescriptor->Directory)); } if (libName.GetFeatureName() == "__CMAKE_LINK_FRAMEWORK"_s) { // use the full path @@ -3824,10 +3914,10 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) libName.GetFormattedItem(this->XCodeEscapePath(cleanPath)) .Value); } else { - libPaths.Add( - libName - .GetFormattedItem(this->XCodeEscapePath(fwItems->second)) - .Value); + libPaths.Add(libName + .GetFormattedItem(this->XCodeEscapePath( + fwDescriptor->GetLinkName())) + .Value); } } else { libPaths.Add( @@ -3855,9 +3945,16 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target) target->AddDependTarget(configName, libName.Target->GetName()); } } - this->AppendBuildSettingAttribute(target, - this->GetTargetLinkFlagsVar(gt), - libPaths.CreateList(), configName); + if (!libPaths.IsEmpty()) { + this->AppendBuildSettingAttribute(target, + this->GetTargetLinkFlagsVar(gt), + libPaths.CreateList(), configName); + } + if (!fwSearchPaths.IsEmpty()) { + this->AppendBuildSettingAttribute(target, "FRAMEWORK_SEARCH_PATHS", + fwSearchPaths.CreateList(), + configName); + } } } } @@ -4339,7 +4436,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( this->CreateString(swiftVersion)); } - std::string symroot = cmStrCat(root->GetCurrentBinaryDirectory(), "/build"); + std::string const symroot = this->GetSymrootDir(); buildSettings->AddAttribute("SYMROOT", this->CreateString(symroot)); // Inside a try_compile project, do not require signing on any platform. @@ -4438,14 +4535,17 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects( return true; } -std::string cmGlobalXCodeGenerator::GetObjectsDirectory( - const std::string& projName, const std::string& configName, - const cmGeneratorTarget* t, const std::string& variant) const +std::string cmGlobalXCodeGenerator::GetSymrootDir() const { - std::string dir = cmStrCat( - t->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/', projName, - ".build/", configName, '/', t->GetName(), ".build/", variant); - return dir; + return cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), "/build"); +} + +std::string cmGlobalXCodeGenerator::GetTargetTempDir( + cmGeneratorTarget const* gt, std::string const& configName) const +{ + // Use a path inside the SYMROOT. + return cmStrCat(this->GetSymrootDir(), '/', gt->GetName(), ".build/", + configName); } void cmGlobalXCodeGenerator::ComputeArchitectures(cmMakefile* mf) @@ -4571,10 +4671,8 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackMakefile( for (auto objLib : objlibs) { const std::string objLibName = objLib->GetName(); - std::string d = cmStrCat( - this->GetObjectsDirectory(this->CurrentProject, configName, objLib, - OBJECT_LIBRARY_ARTIFACT_DIR), - "lib", objLibName, ".a"); + std::string d = cmStrCat(this->GetTargetTempDir(gt, configName), + "/lib", objLibName, ".a"); std::string dependency = this->ConvertToRelativeForMake(d); makefileStream << "\\\n\t" << dependency; @@ -4588,8 +4686,8 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackMakefile( // if building for more than one architecture // then remove those executables as well if (this->Architectures.size() > 1) { - std::string universal = this->GetObjectsDirectory( - this->CurrentProject, configName, gt, "$(OBJDIR)/"); + std::string universal = + cmStrCat(this->GetTargetTempDir(gt, configName), "/$(OBJDIR)/"); for (const auto& architecture : this->Architectures) { std::string universalFile = cmStrCat(universal, architecture, '/', gt->GetFullName(configName)); @@ -4986,14 +5084,10 @@ bool cmGlobalXCodeGenerator::ShouldStripResourcePath(cmMakefile*) const void cmGlobalXCodeGenerator::ComputeTargetObjectDirectory( cmGeneratorTarget* gt) const { - std::string configName = this->GetCMakeCFGIntDir(); auto objectDirArch = GetTargetObjectDirArch(*gt, this->ObjectDirArch); - - std::string dir = - cmStrCat(this->GetObjectsDirectory("$(PROJECT_NAME)", configName, gt, - "$(OBJECT_FILE_DIR_normal:base)/"), - objectDirArch, '/'); - gt->ObjectDirectory = dir; + gt->ObjectDirectory = + cmStrCat(this->GetTargetTempDir(gt, this->GetCMakeCFGIntDir()), + "/$(OBJECT_FILE_DIR_normal:base)/", objectDirArch, '/'); } std::string cmGlobalXCodeGenerator::GetDeploymentPlatform(const cmMakefile* mf) diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h index 92e4528..9ae75fb 100644 --- a/Source/cmGlobalXCodeGenerator.h +++ b/Source/cmGlobalXCodeGenerator.h @@ -339,10 +339,9 @@ private: std::string GetLibraryOrFrameworkPath(const std::string& path) const; - std::string GetObjectsDirectory(const std::string& projName, - const std::string& configName, - const cmGeneratorTarget* t, - const std::string& variant) const; + std::string GetSymrootDir() const; + std::string GetTargetTempDir(cmGeneratorTarget const* gt, + std::string const& configName) const; static std::string GetDeploymentPlatform(const cmMakefile* mf); diff --git a/Source/cmIfCommand.cxx b/Source/cmIfCommand.cxx index 0da72b1..c2a09c1 100644 --- a/Source/cmIfCommand.cxx +++ b/Source/cmIfCommand.cxx @@ -150,7 +150,7 @@ bool cmIfFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, cmExecutionStatus status(mf); mf.ExecuteCommand(func, status); if (status.GetReturnInvoked()) { - inStatus.SetReturnInvoked(); + inStatus.SetReturnInvoked(status.GetReturnVariables()); return true; } if (status.GetBreakInvoked()) { diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 7ca5b23..82adca8 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -12,18 +12,22 @@ #include <utility> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/string_view> #include "cmsys/Glob.hxx" #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" +#include "cmExperimental.h" #include "cmExportSet.h" #include "cmFileSet.h" #include "cmGeneratorExpression.h" #include "cmGlobalGenerator.h" #include "cmInstallCommandArguments.h" +#include "cmInstallCxxModuleBmiGenerator.h" #include "cmInstallDirectoryGenerator.h" #include "cmInstallExportGenerator.h" #include "cmInstallFileSetGenerator.h" @@ -54,13 +58,13 @@ namespace { struct RuntimeDependenciesArgs { - std::vector<std::string> Directories; - std::vector<std::string> PreIncludeRegexes; - std::vector<std::string> PreExcludeRegexes; - std::vector<std::string> PostIncludeRegexes; - std::vector<std::string> PostExcludeRegexes; - std::vector<std::string> PostIncludeFiles; - std::vector<std::string> PostExcludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles; }; auto const RuntimeDependenciesArgHelper = @@ -109,6 +113,8 @@ public: const cmInstallCommandArguments* args) const; std::string GetLibraryDestination( const cmInstallCommandArguments* args) const; + std::string GetCxxModulesBmiDestination( + const cmInstallCommandArguments* args) const; std::string GetIncludeDestination( const cmInstallCommandArguments* args) const; std::string GetSysconfDestination( @@ -401,16 +407,17 @@ bool HandleTargetsMode(std::vector<std::string> const& args, struct ArgVectors { - std::vector<std::string> Archive; - std::vector<std::string> Library; - std::vector<std::string> Runtime; - std::vector<std::string> Object; - std::vector<std::string> Framework; - std::vector<std::string> Bundle; - std::vector<std::string> Includes; - std::vector<std::string> PrivateHeader; - std::vector<std::string> PublicHeader; - std::vector<std::string> Resource; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Archive; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Library; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Object; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Includes; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PrivateHeader; + ArgumentParser::MaybeEmpty<std::vector<std::string>> PublicHeader; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Resource; + ArgumentParser::MaybeEmpty<std::vector<std::string>> CxxModulesBmi; std::vector<std::vector<std::string>> FileSets; }; @@ -426,7 +433,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, .Bind("PRIVATE_HEADER"_s, &ArgVectors::PrivateHeader) .Bind("PUBLIC_HEADER"_s, &ArgVectors::PublicHeader) .Bind("RESOURCE"_s, &ArgVectors::Resource) - .Bind("FILE_SET"_s, &ArgVectors::FileSets); + .Bind("FILE_SET"_s, &ArgVectors::FileSets) + .Bind("CXX_MODULES_BMI"_s, &ArgVectors::CxxModulesBmi); std::vector<std::string> genericArgVector; ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector); @@ -434,26 +442,25 @@ bool HandleTargetsMode(std::vector<std::string> const& args, // now parse the generic args (i.e. the ones not specialized on LIBRARY/ // ARCHIVE, RUNTIME etc. (see above) // These generic args also contain the targets and the export stuff - std::vector<std::string> targetList; + ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList; std::string exports; - std::vector<std::string> runtimeDependenciesArgVector; + cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> + runtimeDependenciesArgVector; std::string runtimeDependencySetArg; std::vector<std::string> unknownArgs; - std::vector<std::string> parsedArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("TARGETS"_s, targetList); genericArgs.Bind("EXPORT"_s, exports); genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); - genericArgs.Parse(genericArgVector, &unknownArgs, nullptr, &parsedArgs); + genericArgs.Parse(genericArgVector, &unknownArgs); bool success = genericArgs.Finalize(); - bool withRuntimeDependencies = - std::find(parsedArgs.begin(), parsedArgs.end(), "RUNTIME_DEPENDENCIES") != - parsedArgs.end(); RuntimeDependenciesArgs runtimeDependenciesArgs = - RuntimeDependenciesArgHelper.Parse(runtimeDependenciesArgVector, - &unknownArgs); + runtimeDependenciesArgVector + ? RuntimeDependenciesArgHelper.Parse(*runtimeDependenciesArgVector, + &unknownArgs) + : RuntimeDependenciesArgs(); cmInstallCommandArguments archiveArgs(helper.DefaultComponentName); cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); @@ -467,6 +474,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, cmInstallCommandIncludesArgument includesArgs; std::vector<cmInstallCommandFileSetArguments> fileSetArgs( argVectors.FileSets.size(), { helper.DefaultComponentName }); + cmInstallCommandArguments cxxModuleBmiArgs(helper.DefaultComponentName); // now parse the args for specific parts of the target (e.g. LIBRARY, // RUNTIME, ARCHIVE etc. @@ -490,6 +498,15 @@ bool HandleTargetsMode(std::vector<std::string> const& args, fileSetArgs[i] = std::move(fileSetArg); } + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi); + if (!supportCxx20FileSetTypes) { + std::copy(argVectors.CxxModulesBmi.begin(), argVectors.CxxModulesBmi.end(), + std::back_inserter(unknownArgs)); + } else { + cxxModuleBmiArgs.Parse(argVectors.CxxModulesBmi, &unknownArgs); + } + if (!unknownArgs.empty()) { // Unknown argument. status.SetError( @@ -510,6 +527,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto& fileSetArg : fileSetArgs) { fileSetArg.SetGenericArguments(&genericArgs); } + cxxModuleBmiArgs.SetGenericArguments(&genericArgs); success = success && archiveArgs.Finalize(); success = success && libraryArgs.Finalize(); @@ -523,6 +541,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto& fileSetArg : fileSetArgs) { success = success && fileSetArg.Finalize(); } + if (supportCxx20FileSetTypes) { + success = success && cxxModuleBmiArgs.Finalize(); + } if (!success) { return false; @@ -536,7 +557,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return fileSetArg.GetNamelinkOnly(); })) { + -> bool { return fileSetArg.GetNamelinkOnly(); }) || + cxxModuleBmiArgs.GetNamelinkOnly()) { status.SetError( "TARGETS given NAMELINK_ONLY option not in LIBRARY group. " "The NAMELINK_ONLY option may be specified only following LIBRARY."); @@ -548,7 +570,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return fileSetArg.GetNamelinkSkip(); })) { + -> bool { return fileSetArg.GetNamelinkSkip(); }) || + cxxModuleBmiArgs.GetNamelinkSkip()) { status.SetError( "TARGETS given NAMELINK_SKIP option not in LIBRARY group. " "The NAMELINK_SKIP option may be specified only following LIBRARY."); @@ -564,7 +587,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, resourceArgs.HasNamelinkComponent() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return fileSetArg.HasNamelinkComponent(); })) { + -> bool { return fileSetArg.HasNamelinkComponent(); }) || + cxxModuleBmiArgs.HasNamelinkComponent()) { status.SetError( "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. " "The NAMELINK_COMPONENT option may be specified only following " @@ -583,7 +607,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args, !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty() || std::any_of(fileSetArgs.begin(), fileSetArgs.end(), [](const cmInstallCommandFileSetArguments& fileSetArg) - -> bool { return !fileSetArg.GetType().empty(); })) { + -> bool { return !fileSetArg.GetType().empty(); }) || + !cxxModuleBmiArgs.GetType().empty()) { status.SetError( "TARGETS given TYPE option. The TYPE option may only be specified in " " install(FILES) and install(DIRECTORIES)."); @@ -597,7 +622,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr; - if (withRuntimeDependencies) { + if (runtimeDependenciesArgVector) { if (!runtimeDependencySetArg.empty()) { status.SetError("TARGETS cannot have both RUNTIME_DEPENDENCIES and " "RUNTIME_DEPENDENCY_SET."); @@ -706,6 +731,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, bool installsPublicHeader = false; bool installsResource = false; std::vector<bool> installsFileSet(fileSetArgs.size(), false); + bool installsCxxModuleBmi = false; // Generate install script code to install the given targets. for (cmTarget* ti : targets) { @@ -722,6 +748,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, std::unique_ptr<cmInstallFilesGenerator> publicHeaderGenerator; std::unique_ptr<cmInstallFilesGenerator> resourceGenerator; std::vector<std::unique_ptr<cmInstallFileSetGenerator>> fileSetGenerators; + std::unique_ptr<cmInstallCxxModuleBmiGenerator> cxxModuleBmiGenerator; // Avoid selecting default destinations for PUBLIC_HEADER and // PRIVATE_HEADER if any artifacts are specified. @@ -760,6 +787,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto const& gen : fileSetGenerators) { te->FileSetGenerators[gen->GetFileSet()] = gen.get(); } + te->CxxModuleBmiGenerator = cxxModuleBmiGenerator.get(); target.AddInstallIncludeDirectories( *te, cmMakeRange(includesArgs.GetIncludeDirs())); te->NamelinkOnly = namelinkOnly; @@ -1105,6 +1133,19 @@ bool HandleTargetsMode(std::vector<std::string> const& args, } } + if (supportCxx20FileSetTypes && + !cxxModuleBmiArgs.GetDestination().empty()) { + cxxModuleBmiGenerator = cm::make_unique<cmInstallCxxModuleBmiGenerator>( + target.GetName(), + helper.GetCxxModulesBmiDestination(&cxxModuleBmiArgs), + cxxModuleBmiArgs.GetPermissions(), + cxxModuleBmiArgs.GetConfigurations(), cxxModuleBmiArgs.GetComponent(), + cmInstallGenerator::SelectMessageLevel(target.GetMakefile()), + cxxModuleBmiArgs.GetExcludeFromAll(), cxxModuleBmiArgs.GetOptional(), + helper.Makefile->GetBacktrace()); + target.SetHaveInstallRule(true); + } + // Add this install rule to an export if one was specified. if (!addTargetExport()) { return false; @@ -1121,6 +1162,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args, installsPrivateHeader = installsPrivateHeader || privateHeaderGenerator; installsPublicHeader = installsPublicHeader || publicHeaderGenerator; installsResource = installsResource || resourceGenerator; + installsCxxModuleBmi = installsCxxModuleBmi || cxxModuleBmiGenerator; helper.Makefile->AddInstallGenerator(std::move(archiveGenerator)); helper.Makefile->AddInstallGenerator(std::move(libraryGenerator)); @@ -1135,9 +1177,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args, for (auto& gen : fileSetGenerators) { helper.Makefile->AddInstallGenerator(std::move(gen)); } + helper.Makefile->AddInstallGenerator(std::move(cxxModuleBmiGenerator)); } - if (withRuntimeDependencies && !runtimeDependencySet->Empty()) { + if (runtimeDependenciesArgVector && !runtimeDependencySet->Empty()) { AddInstallRuntimeDependenciesGenerator( helper, runtimeDependencySet, runtimeArgs, libraryArgs, frameworkArgs, std::move(runtimeDependenciesArgs), installsRuntime, installsLibrary, @@ -1192,6 +1235,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args, fileSetArgs[i].GetComponent()); } } + if (installsCxxModuleBmi) { + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( + cxxModuleBmiArgs.GetComponent()); + } return true; } @@ -1206,10 +1253,10 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, struct ArgVectors { - std::vector<std::string> Library; - std::vector<std::string> Runtime; - std::vector<std::string> Framework; - std::vector<std::string> Bundle; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Library; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle; }; static auto const argHelper = cmArgumentParser<ArgVectors>{} @@ -1223,7 +1270,7 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, // now parse the generic args (i.e. the ones not specialized on LIBRARY, // RUNTIME etc. (see above) - std::vector<std::string> targetList; + ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList; std::string runtimeDependencySetArg; std::vector<std::string> unknownArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); @@ -1464,7 +1511,7 @@ bool HandleFilesMode(std::vector<std::string> const& args, // This is the FILES mode. bool programs = (args[0] == "PROGRAMS"); cmInstallCommandArguments ica(helper.DefaultComponentName); - std::vector<std::string> files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> files; ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files); std::vector<std::string> unknownArgs; ica.Parse(args, &unknownArgs); @@ -1950,7 +1997,7 @@ bool HandleExportAndroidMKMode(std::vector<std::string> const& args, cm::make_unique<cmInstallExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, exportOld, true, + ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true, helper.Makefile->GetBacktrace())); return true; @@ -1973,12 +2020,19 @@ bool HandleExportMode(std::vector<std::string> const& args, std::string name_space; bool exportOld = false; std::string filename; + std::string cxx_modules_directory; ica.Bind("EXPORT"_s, exp); ica.Bind("NAMESPACE"_s, name_space); ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld); ica.Bind("FILE"_s, filename); + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi); + if (supportCxx20FileSetTypes) { + ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory); + } + std::vector<std::string> unknownArgs; ica.Parse(args, &unknownArgs); @@ -2064,8 +2118,8 @@ bool HandleExportMode(std::vector<std::string> const& args, cm::make_unique<cmInstallExportGenerator>( &exportSet, ica.GetDestination(), ica.GetPermissions(), ica.GetConfigurations(), ica.GetComponent(), message, - ica.GetExcludeFromAll(), fname, name_space, exportOld, false, - helper.Makefile->GetBacktrace())); + ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory, + exportOld, false, helper.Makefile->GetBacktrace())); return true; } @@ -2088,9 +2142,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args, struct ArgVectors { - std::vector<std::string> Library; - std::vector<std::string> Runtime; - std::vector<std::string> Framework; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Library; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework; }; static auto const argHelper = cmArgumentParser<ArgVectors>{} @@ -2106,11 +2160,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args, // These generic args also contain the runtime dependency set std::string runtimeDependencySetArg; std::vector<std::string> runtimeDependencyArgVector; - std::vector<std::string> parsedArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); - genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector, nullptr, - &parsedArgs); + genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector); bool success = genericArgs.Finalize(); cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); @@ -2280,6 +2332,15 @@ std::string Helper::GetLibraryDestination( return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib"); } +std::string Helper::GetCxxModulesBmiDestination( + const cmInstallCommandArguments* args) const +{ + if (args) { + return args->GetDestination(); + } + return {}; +} + std::string Helper::GetIncludeDestination( const cmInstallCommandArguments* args) const { diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 79bd945..6e46aac 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -8,6 +8,7 @@ #include <vector> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" class cmInstallCommandArguments : public cmArgumentParser<void> { @@ -44,8 +45,8 @@ private: std::string NamelinkComponent; bool ExcludeFromAll = false; std::string Rename; - std::vector<std::string> Permissions; - std::vector<std::string> Configurations; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Permissions; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Configurations; bool Optional = false; bool NamelinkOnly = false; bool NamelinkSkip = false; diff --git a/Source/cmInstallCxxModuleBmiGenerator.cxx b/Source/cmInstallCxxModuleBmiGenerator.cxx new file mode 100644 index 0000000..1ef1eaa --- /dev/null +++ b/Source/cmInstallCxxModuleBmiGenerator.cxx @@ -0,0 +1,75 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallCxxModuleBmiGenerator.h" + +#include <ostream> +#include <utility> + +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmOutputConverter.h" +#include "cmStringAlgorithms.h" + +cmInstallCxxModuleBmiGenerator::cmInstallCxxModuleBmiGenerator( + std::string target, std::string const& dest, std::string file_permissions, + std::vector<std::string> const& configurations, std::string const& component, + MessageLevel message, bool exclude_from_all, bool optional, + cmListFileBacktrace backtrace) + : cmInstallGenerator(dest, configurations, component, message, + exclude_from_all, false, std::move(backtrace)) + , TargetName(std::move(target)) + , FilePermissions(std::move(file_permissions)) + , Optional(optional) +{ + this->ActionsPerConfig = true; +} + +cmInstallCxxModuleBmiGenerator::~cmInstallCxxModuleBmiGenerator() = default; + +bool cmInstallCxxModuleBmiGenerator::Compute(cmLocalGenerator* lg) +{ + this->LocalGenerator = lg; + + this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName); + if (!this->Target) { + // If no local target has been found, find it in the global scope. + this->Target = + lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName); + } + + return true; +} + +std::string cmInstallCxxModuleBmiGenerator::GetScriptLocation( + std::string const& config) const +{ + char const* config_name = config.c_str(); + if (config.empty()) { + config_name = "noconfig"; + } + return cmStrCat(this->Target->GetSupportDirectory(), + "/install-cxx-module-bmi-", config_name, ".cmake"); +} + +std::string cmInstallCxxModuleBmiGenerator::GetDestination( + std::string const& config) const +{ + return cmGeneratorExpression::Evaluate(this->Destination, + this->LocalGenerator, config); +} + +void cmInstallCxxModuleBmiGenerator::GenerateScriptForConfig( + std::ostream& os, const std::string& config, Indent indent) +{ + auto const& loc = this->GetScriptLocation(config); + if (loc.empty()) { + return; + } + os << indent << "include(\"" + << cmOutputConverter::EscapeForCMake( + loc, cmOutputConverter::WrapQuotes::NoWrap) + << "\" OPTIONAL)\n"; +} diff --git a/Source/cmInstallCxxModuleBmiGenerator.h b/Source/cmInstallCxxModuleBmiGenerator.h new file mode 100644 index 0000000..21edb2e --- /dev/null +++ b/Source/cmInstallCxxModuleBmiGenerator.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 <iosfwd> +#include <string> +#include <vector> + +#include "cmInstallGenerator.h" +#include "cmScriptGenerator.h" + +class cmGeneratorTarget; +class cmListFileBacktrace; +class cmLocalGenerator; + +/** \class cmInstallCxxModuleBmiGenerator + * \brief Generate C++ module BMI installation rules. + */ +class cmInstallCxxModuleBmiGenerator : public cmInstallGenerator +{ +public: + cmInstallCxxModuleBmiGenerator( + std::string target, std::string const& dest, std::string file_permissions, + std::vector<std::string> const& configurations, + std::string const& component, MessageLevel message, bool exclude_from_all, + bool optional, cmListFileBacktrace backtrace); + ~cmInstallCxxModuleBmiGenerator() override; + + bool Compute(cmLocalGenerator* lg) override; + + std::string const& GetFilePermissions() const + { + return this->FilePermissions; + } + std::string GetDestination(std::string const& config) const; + std::string GetScriptLocation(std::string const& config) const; + cmGeneratorTarget const* GetTarget() const { return this->Target; } + bool GetOptional() const { return this->Optional; } + MessageLevel GetMessageLevel() const { return this->Message; } + +protected: + void GenerateScriptForConfig(std::ostream& os, const std::string& config, + Indent indent) override; + + std::string const TargetName; + cmGeneratorTarget const* Target = nullptr; + cmLocalGenerator* LocalGenerator = nullptr; + std::string const FilePermissions; + bool const Optional; +}; diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index b80437d..1d81b0b 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -23,7 +23,8 @@ cmInstallExportGenerator::cmInstallExportGenerator( cmExportSet* exportSet, std::string const& destination, std::string file_permissions, std::vector<std::string> const& configurations, std::string const& component, MessageLevel message, bool exclude_from_all, - std::string filename, std::string name_space, bool exportOld, bool android, + std::string filename, std::string name_space, + std::string cxx_modules_directory, bool exportOld, bool android, cmListFileBacktrace backtrace) : cmInstallGenerator(destination, configurations, component, message, exclude_from_all, false, std::move(backtrace)) @@ -31,6 +32,7 @@ cmInstallExportGenerator::cmInstallExportGenerator( , FilePermissions(std::move(file_permissions)) , FileName(std::move(filename)) , Namespace(std::move(name_space)) + , CxxModulesDirectory(std::move(cxx_modules_directory)) , ExportOld(exportOld) { if (android) { @@ -141,6 +143,75 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, os << indent << "endif()\n"; files.clear(); } + + // Now create a configuration-specific install rule for the C++ module import + // property file of each configuration. + auto cxx_module_dest = + cmStrCat(this->Destination, '/', this->CxxModulesDirectory); + std::string config_file_example; + for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) { + config_file_example = i.second; + break; + } + if (!config_file_example.empty()) { + // Remove old per-configuration export files if the main changes. + std::string installedDir = cmStrCat( + "$ENV{DESTDIR}", ConvertToAbsoluteDestination(cxx_module_dest), '/'); + std::string installedFile = cmStrCat(installedDir, "/cxx-modules.cmake"); + std::string toInstallFile = + cmStrCat(cmSystemTools::GetFilenamePath(config_file_example), + "/cxx-modules.cmake"); + os << indent << "if(EXISTS \"" << installedFile << "\")\n"; + Indent indentN = indent.Next(); + Indent indentNN = indentN.Next(); + Indent indentNNN = indentNN.Next(); + /* clang-format off */ + os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n" + << indentN << " \"" << installedFile << "\"\n" + << indentN << " \"" << toInstallFile << "\")\n"; + os << indentN << "if(_cmake_export_file_changed)\n"; + os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir + << this->EFGen->GetConfigImportFileGlob() << "\")\n"; + os << indentNN << "if(_cmake_old_config_files)\n"; + os << indentNNN << "string(REPLACE \";\" \", \" _cmake_old_config_files_text \"${_cmake_old_config_files}\")\n"; + os << indentNNN << R"(message(STATUS "Old C++ module export file \")" << installedFile + << "\\\" will be replaced. Removing files [${_cmake_old_config_files_text}].\")\n"; + os << indentNNN << "unset(_cmake_old_config_files_text)\n"; + os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n"; + os << indentNN << "endif()\n"; + os << indentNN << "unset(_cmake_old_config_files)\n"; + os << indentN << "endif()\n"; + os << indentN << "unset(_cmake_export_file_changed)\n"; + os << indent << "endif()\n"; + /* clang-format on */ + + // All of these files are siblings; get its location to know where the + // "anchor" file is. + files.push_back(toInstallFile); + this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files, + false, this->FilePermissions.c_str(), nullptr, + nullptr, nullptr, indent); + files.clear(); + } + for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) { + files.push_back(i.second); + std::string config_test = this->CreateConfigTest(i.first); + os << indent << "if(" << config_test << ")\n"; + this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files, + false, this->FilePermissions.c_str(), nullptr, + nullptr, nullptr, indent.Next()); + os << indent << "endif()\n"; + files.clear(); + } + for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) { + std::string config_test = this->CreateConfigTest(i.first); + os << indent << "if(" << config_test << ")\n"; + this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, i.second, + false, this->FilePermissions.c_str(), nullptr, + nullptr, nullptr, indent.Next()); + os << indent << "endif()\n"; + files.clear(); + } } void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os, diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index 02fe1fa..346ca67 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -28,7 +28,8 @@ public: const std::vector<std::string>& configurations, std::string const& component, MessageLevel message, bool exclude_from_all, std::string filename, - std::string name_space, bool exportOld, + std::string name_space, + std::string cxx_modules_directory, bool exportOld, bool android, cmListFileBacktrace backtrace); cmInstallExportGenerator(const cmInstallExportGenerator&) = delete; ~cmInstallExportGenerator() override; @@ -50,6 +51,10 @@ public: std::string GetDestinationFile() const; std::string GetFileName() const { return this->FileName; } std::string GetTempDir() const; + std::string GetCxxModuleDirectory() const + { + return this->CxxModulesDirectory; + } protected: void GenerateScript(std::ostream& os) override; @@ -64,6 +69,7 @@ protected: std::string const FilePermissions; std::string const FileName; std::string const Namespace; + std::string const CxxModulesDirectory; bool const ExportOld; cmLocalGenerator* LocalGenerator = nullptr; diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx index 43f1b8e..b06dc3d 100644 --- a/Source/cmLinkLineDeviceComputer.cxx +++ b/Source/cmLinkLineDeviceComputer.cxx @@ -57,7 +57,6 @@ bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking( // For this we only consider targets using ItemVector = cmComputeLinkInformation::ItemVector; ItemVector const& items = cli.GetItems(); - std::string config = cli.GetConfig(); return std::any_of( items.begin(), items.end(), [](cmComputeLinkInformation::Item const& item) -> bool { @@ -69,6 +68,26 @@ bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking( }); } +bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinkingIPOFlag( + cmComputeLinkInformation& cli) +{ + // Determine if this item might requires device linking. + // For this we only consider targets + using ItemVector = cmComputeLinkInformation::ItemVector; + ItemVector const& items = cli.GetItems(); + std::string config = cli.GetConfig(); + return std::any_of( + items.begin(), items.end(), + [config](cmComputeLinkInformation::Item const& item) -> bool { + return item.Target && + item.Target->GetType() == cmStateEnums::STATIC_LIBRARY && + // this dependency requires us to device link it + !item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") && + item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION") && + item.Target->IsIPOEnabled("CUDA", config); + }); +} + void cmLinkLineDeviceComputer::ComputeLinkLibraries( cmComputeLinkInformation& cli, std::string const& stdLibString, std::vector<BT<std::string>>& linkLibraries) diff --git a/Source/cmLinkLineDeviceComputer.h b/Source/cmLinkLineDeviceComputer.h index dee625b..0916307 100644 --- a/Source/cmLinkLineDeviceComputer.h +++ b/Source/cmLinkLineDeviceComputer.h @@ -30,6 +30,7 @@ public: delete; bool ComputeRequiresDeviceLinking(cmComputeLinkInformation& cli); + bool ComputeRequiresDeviceLinkingIPOFlag(cmComputeLinkInformation& cli); void ComputeLinkLibraries( cmComputeLinkInformation& cli, std::string const& stdLibString, diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx index 91157cb..6270c82 100644 --- a/Source/cmListFileCache.cxx +++ b/Source/cmListFileCache.cxx @@ -347,6 +347,7 @@ enum class NestingStateEnum Foreach, Function, Macro, + Block }; struct NestingState @@ -434,6 +435,16 @@ cm::optional<cmListFileContext> cmListFileParser::CheckNesting() const return cmListFileContext::FromListFileFunction(func, this->FileName); } stack.pop_back(); + } else if (name == "block") { + stack.push_back({ + NestingStateEnum::Block, + cmListFileContext::FromListFileFunction(func, this->FileName), + }); + } else if (name == "endblock") { + if (!TopIs(stack, NestingStateEnum::Block)) { + return cmListFileContext::FromListFileFunction(func, this->FileName); + } + stack.pop_back(); } } diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 99bd05f..b2b724a 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -15,6 +15,7 @@ #include <vector> #include <cm/memory> +#include <cm/optional> #include <cm/string_view> #include <cmext/algorithm> #include <cmext/string_view> @@ -36,6 +37,7 @@ #include "cmInstallScriptGenerator.h" #include "cmInstallTargetGenerator.h" #include "cmLinkLineComputer.h" +#include "cmLinkLineDeviceComputer.h" #include "cmMakefile.h" #include "cmRange.h" #include "cmRulePlaceholderExpander.h" @@ -1385,7 +1387,7 @@ std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags( } void cmLocalGenerator::GetDeviceLinkFlags( - cmLinkLineComputer& linkLineComputer, const std::string& config, + cmLinkLineDeviceComputer& linkLineComputer, const std::string& config, std::string& linkLibs, std::string& linkFlags, std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target) { @@ -1393,6 +1395,18 @@ void cmLocalGenerator::GetDeviceLinkFlags( cmComputeLinkInformation* pcli = target->GetLinkInformation(config); + auto linklang = linkLineComputer.GetLinkerLanguage(target, config); + auto ipoEnabled = target->IsIPOEnabled(linklang, config); + if (!ipoEnabled) { + ipoEnabled = linkLineComputer.ComputeRequiresDeviceLinkingIPOFlag(*pcli); + } + if (ipoEnabled) { + if (cmValue cudaIPOFlags = this->Makefile->GetDefinition( + "CMAKE_CUDA_DEVICE_LINK_OPTIONS_IPO")) { + linkFlags += cudaIPOFlags; + } + } + if (pcli) { // Compute the required device link libraries when // resolving gpu lang device symbols @@ -1400,6 +1414,8 @@ void cmLocalGenerator::GetDeviceLinkFlags( linkPath); } + // iterate link deps and see if any of them need IPO + std::vector<std::string> linkOpts; target->GetLinkOptions(linkOpts, config, "CUDA"); // LINK_OPTIONS are escaped. @@ -1594,7 +1610,8 @@ std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags( cmMakefile* mf = this->GetMakefile(); // Add language-specific flags. - this->AddLanguageFlags(compileFlags, target, lang, config); + this->AddLanguageFlags(compileFlags, target, cmBuildStep::Compile, lang, + config); if (target->IsIPOEnabled(lang, config)) { this->AppendFeatureOptions(compileFlags, lang, "IPO"); @@ -1907,6 +1924,7 @@ void cmLocalGenerator::AddArchitectureFlags(std::string& flags, void cmLocalGenerator::AddLanguageFlags(std::string& flags, cmGeneratorTarget const* target, + cmBuildStep compileOrLink, const std::string& lang, const std::string& config) { @@ -1930,7 +1948,7 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, } } } else if (lang == "CUDA") { - target->AddCUDAArchitectureFlags(flags); + target->AddCUDAArchitectureFlags(compileOrLink, config, flags); target->AddCUDAToolkitFlags(flags); } else if (lang == "ISPC") { target->AddISPCTargetFlags(flags); @@ -2027,6 +2045,31 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags, } } } + + // Add MSVC debug information format flags if CMP0141 is NEW. + if (cm::optional<std::string> msvcDebugInformationFormat = + this->GetMSVCDebugFormatName(config, target)) { + if (!msvcDebugInformationFormat->empty()) { + if (cmValue msvcDebugInformationFormatOptions = + this->Makefile->GetDefinition( + cmStrCat("CMAKE_", lang, + "_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_", + *msvcDebugInformationFormat))) { + this->AppendCompileOptions(flags, *msvcDebugInformationFormatOptions); + } else if ((this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_COMPILER_ID")) == "MSVC"_s || + this->Makefile->GetSafeDefinition( + cmStrCat("CMAKE_", lang, "_SIMULATE_ID")) == "MSVC"_s) && + !cmSystemTools::GetErrorOccurredFlag()) { + // The compiler uses the MSVC ABI so it needs a known runtime library. + this->IssueMessage(MessageType::FATAL_ERROR, + cmStrCat("MSVC_DEBUG_INFORMATION_FORMAT value '", + *msvcDebugInformationFormat, + "' not known for this ", lang, + " compiler.")); + } + } + } } void cmLocalGenerator::AddLanguageFlagsForLinking( @@ -2042,7 +2085,7 @@ void cmLocalGenerator::AddLanguageFlagsForLinking( this->AddCompilerRequirementFlag(flags, target, lang, config); } - this->AddLanguageFlags(flags, target, lang, config); + this->AddLanguageFlags(flags, target, cmBuildStep::Link, lang, config); if (target->IsIPOEnabled(lang, config)) { this->AppendFeatureOptions(flags, lang, "IPO"); @@ -2578,7 +2621,9 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) if (pchSource.empty() || pchHeader.empty()) { if (this->GetGlobalGenerator()->IsXcode() && !pchLangSet.empty()) { for (auto* sf : sources) { - if (pchLangSet.find(sf->GetLanguage()) == pchLangSet.end()) { + const auto sourceLanguage = sf->GetLanguage(); + if (!sourceLanguage.empty() && + pchLangSet.find(sourceLanguage) == pchLangSet.end()) { sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON"); } } @@ -2630,13 +2675,24 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) this->Makefile->GetSafeDefinition( cmStrCat("CMAKE_", lang, "_FLAGS_", configUpper)); - bool editAndContinueDebugInfo = - langFlags.find("/ZI") != std::string::npos || - langFlags.find("-ZI") != std::string::npos; - - bool enableDebuggingInformation = - langFlags.find("/Zi") != std::string::npos || - langFlags.find("-Zi") != std::string::npos; + bool editAndContinueDebugInfo = false; + bool programDatabaseDebugInfo = false; + cm::optional<std::string> msvcDebugInformationFormat = + this->GetMSVCDebugFormatName(config, target); + if (msvcDebugInformationFormat && + !msvcDebugInformationFormat->empty()) { + editAndContinueDebugInfo = + *msvcDebugInformationFormat == "EditAndContinue"; + programDatabaseDebugInfo = + *msvcDebugInformationFormat == "ProgramDatabase"; + } else { + editAndContinueDebugInfo = + langFlags.find("/ZI") != std::string::npos || + langFlags.find("-ZI") != std::string::npos; + programDatabaseDebugInfo = + langFlags.find("/Zi") != std::string::npos || + langFlags.find("-Zi") != std::string::npos; + } // MSVC 2008 is producing both .pdb and .idb files with /Zi. bool msvc2008OrLess = @@ -2652,7 +2708,7 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) if (editAndContinueDebugInfo || msvc2008OrLess) { this->CopyPchCompilePdb(config, target, *ReuseFrom, reuseTarget, { ".pdb", ".idb" }); - } else if (enableDebuggingInformation) { + } else if (programDatabaseDebugInfo) { this->CopyPchCompilePdb(config, target, *ReuseFrom, reuseTarget, { ".pdb" }); } @@ -2671,7 +2727,7 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) cmStrCat(linkerProperty, configUpper), cmStrCat(" ", this->ConvertToOutputFormat(pchSourceObj, SHELL)), - true); + cm::nullopt, true); } else if (reuseTarget->GetType() == cmStateEnums::OBJECT_LIBRARY) { // FIXME: This can propagate more than one level, unlike @@ -2816,6 +2872,26 @@ void cmLocalGenerator::CopyPchCompilePdb( target_compile_pdb_dir); } +cm::optional<std::string> cmLocalGenerator::GetMSVCDebugFormatName( + std::string const& config, cmGeneratorTarget const* target) +{ + // MSVC debug information format selection is activated by the presence + // of a default whether or not it is overridden by a property. + cm::optional<std::string> msvcDebugInformationFormat; + cmValue msvcDebugInformationFormatDefault = this->Makefile->GetDefinition( + "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT"); + if (cmNonempty(msvcDebugInformationFormatDefault)) { + cmValue msvcDebugInformationFormatValue = + target->GetProperty("MSVC_DEBUG_INFORMATION_FORMAT"); + if (!msvcDebugInformationFormatValue) { + msvcDebugInformationFormatValue = msvcDebugInformationFormatDefault; + } + msvcDebugInformationFormat = cmGeneratorExpression::Evaluate( + *msvcDebugInformationFormatValue, this, config, target); + } + return msvcDebugInformationFormat; +} + namespace { inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf, diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 7cae1fc..765441c 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -35,6 +35,7 @@ class cmGeneratorTarget; class cmGlobalGenerator; class cmImplicitDependsList; class cmLinkLineComputer; +class cmLinkLineDeviceComputer; class cmMakefile; class cmRulePlaceholderExpander; class cmSourceFile; @@ -59,6 +60,13 @@ enum class cmDependencyScannerKind Compiler }; +/** What to compute language flags for */ +enum class cmBuildStep +{ + Compile, + Link +}; + /** Target and source file which have a specific output. */ struct cmSourcesWithOutput { @@ -143,7 +151,8 @@ public: const std::string& filterArch = std::string()); void AddLanguageFlags(std::string& flags, cmGeneratorTarget const* target, - const std::string& lang, const std::string& config); + cmBuildStep compileOrLink, const std::string& lang, + const std::string& config); void AddLanguageFlagsForLinking(std::string& flags, cmGeneratorTarget const* target, const std::string& lang, @@ -476,7 +485,7 @@ public: /** Fill out these strings for the given target. Libraries to link, * flags, and linkflags. */ - void GetDeviceLinkFlags(cmLinkLineComputer& linkLineComputer, + void GetDeviceLinkFlags(cmLinkLineDeviceComputer& linkLineComputer, const std::string& config, std::string& linkLibs, std::string& linkFlags, std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target); @@ -637,6 +646,10 @@ private: cmGeneratorTarget* reuseTarget, std::vector<std::string> const& extensions); + // Returns MSVC_DEBUG_INFORMATION_FORMAT value if CMP0141 is NEW. + cm::optional<std::string> GetMSVCDebugFormatName( + std::string const& config, cmGeneratorTarget const* target); + struct UnityBatchedSource { cmSourceFile* Source = nullptr; diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 106f76b..c11f5b4 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/Source/cmLocalNinjaGenerator.cxx @@ -88,27 +88,11 @@ void cmLocalNinjaGenerator::Generate() cmGlobalNinjaGenerator::WriteComment(this->GetRulesFileStream(), "localized /showIncludes string"); 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->GetGlobalGenerator()->GetMakefileEncoding() != codecvt::None) { - this->GetRulesFileStream().WriteRaw(showIncludesPrefix); - } else { - // Ninja 1.11 and above uses the UTF-8 code page if it's supported, so - // in that case we can write it normally without using raw bytes. - this->GetRulesFileStream() << showIncludesPrefix; - } -#else - // It's safe to use the standard encoding on other platforms. - this->GetRulesFileStream() << showIncludesPrefix; -#endif + // 'cl /showIncludes' encodes output in the console output code page. + // It may differ from the encoding used for file paths in 'build.ninja'. + // Ninja matches the showIncludes prefix using its raw byte sequence. + this->GetRulesFileStream().WriteAltEncoding( + showIncludesPrefix, cmGeneratedFileStream::Encoding::ConsoleOutput); this->GetRulesFileStream() << "\n\n"; } } diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index e125470..de1d3cd 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -1412,13 +1412,16 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( if (needRescanDependInfo || needRescanDirInfo || needRescanDependencies) { // The dependencies must be regenerated. - std::string targetName = cmSystemTools::GetFilenameName(targetDir); - targetName = targetName.substr(0, targetName.length() - 4); - std::string message = - cmStrCat("Scanning dependencies of target ", targetName); - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, color); + if (verbose) { + std::string targetName = cmSystemTools::GetFilenameName(targetDir); + targetName = targetName.substr(0, targetName.length() - 4); + std::string message = + cmStrCat("Scanning dependencies of target ", targetName); + cmSystemTools::MakefileColorEcho( + cmsysTerminal_Color_ForegroundMagenta | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, color); + } status = this->ScanDependencies(targetDir, dependFile, internalDependFile, validDependencies); @@ -1447,13 +1450,19 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies( this->GetBinaryDirectory()) : std::function<bool(const std::string&)>())) { // regenerate dependencies files - std::string targetName = - cmCMakePath(targetDir).GetFileName().RemoveExtension().GenericString(); - auto message = cmStrCat( - "Consolidate compiler generated dependencies of target ", targetName); - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, color); + if (verbose) { + std::string targetName = cmCMakePath(targetDir) + .GetFileName() + .RemoveExtension() + .GenericString(); + auto message = + cmStrCat("Consolidate compiler generated dependencies of target ", + targetName); + cmSystemTools::MakefileColorEcho( + cmsysTerminal_Color_ForegroundMagenta | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, color); + } // Open the make depends file. This should be copy-if-different // because the make tool may try to reload it needlessly otherwise. diff --git a/Source/cmLocalVisualStudio10Generator.h b/Source/cmLocalVisualStudio10Generator.h index 75fe262..7bfe3b7 100644 --- a/Source/cmLocalVisualStudio10Generator.h +++ b/Source/cmLocalVisualStudio10Generator.h @@ -15,8 +15,8 @@ class cmMakefile; /** \class cmLocalVisualStudio10Generator * \brief Write Visual Studio 10 project files. * - * cmLocalVisualStudio10Generator produces a Visual Studio 10 project - * file for each target in its directory. + * cmLocalVisualStudio10Generator produces a MSBuild project file for each + * target in its directory. */ class cmLocalVisualStudio10Generator : public cmLocalVisualStudio7Generator { diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index f65add1..af2d31d 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -195,10 +195,10 @@ void cmLocalVisualStudio7Generator::GenerateTarget(cmGeneratorTarget* target) this->FortranProject = gg->TargetIsFortranOnly(target); this->WindowsCEProject = gg->TargetsWindowsCE(); - // Intel Fortran for VS10 uses VS9 format ".vfproj" files. + // Intel Fortran always uses VS9 format ".vfproj" files. cmGlobalVisualStudioGenerator::VSVersion realVersion = gg->GetVersion(); if (this->FortranProject && - gg->GetVersion() >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { + gg->GetVersion() >= cmGlobalVisualStudioGenerator::VSVersion::VS11) { gg->SetVersion(cmGlobalVisualStudioGenerator::VSVersion::VS9); } @@ -680,7 +680,8 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( langForClCompile = linkLanguage; if (langForClCompile == "C" || langForClCompile == "CXX" || langForClCompile == "Fortran") { - this->AddLanguageFlags(flags, target, langForClCompile, configName); + this->AddLanguageFlags(flags, target, cmBuildStep::Compile, + langForClCompile, configName); } // set the correct language if (linkLanguage == "C") { diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index ef12487..47ad749 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx @@ -126,7 +126,7 @@ bool cmMacroHelperCommand::operator()( return false; } if (status.GetReturnInvoked()) { - inStatus.SetReturnInvoked(); + inStatus.SetReturnInvoked(status.GetReturnVariables()); return true; } if (status.GetBreakInvoked()) { diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 469eac3..6e0d704 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -149,6 +149,29 @@ void cmMakefile::IssueMessage(MessageType t, std::string const& text) const this->GetCMakeInstance()->IssueMessage(t, text, this->Backtrace); } +Message::LogLevel cmMakefile::GetCurrentLogLevel() const +{ + const cmake* cmakeInstance = this->GetCMakeInstance(); + + const Message::LogLevel logLevelCliOrDefault = cmakeInstance->GetLogLevel(); + assert("Expected a valid log level here" && + logLevelCliOrDefault != Message::LogLevel::LOG_UNDEFINED); + + Message::LogLevel result = logLevelCliOrDefault; + + // If the log-level was set via the command line option, it takes precedence + // over the CMAKE_MESSAGE_LOG_LEVEL variable. + if (!cmakeInstance->WasLogLevelSetViaCLI()) { + const Message::LogLevel logLevelFromVar = cmake::StringToLogLevel( + this->GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL")); + if (logLevelFromVar != Message::LogLevel::LOG_UNDEFINED) { + result = logLevelFromVar; + } + } + + return result; +} + bool cmMakefile::CheckCMP0037(std::string const& targetName, cmStateEnums::TargetType targetType) const { @@ -766,6 +789,7 @@ void cmMakefile::RunListFile(cmListFile const& listFile, break; } if (status.GetReturnInvoked()) { + this->RaiseScope(status.GetReturnVariables()); // Exit early due to return command. break; } @@ -1759,7 +1783,8 @@ void cmMakefile::ConfigureSubDirectory(cmMakefile* mf) void cmMakefile::AddSubDirectory(const std::string& srcPath, const std::string& binPath, - bool excludeFromAll, bool immediate) + bool excludeFromAll, bool immediate, + bool system) { if (this->DeferRunning) { this->IssueMessage( @@ -1789,6 +1814,9 @@ void cmMakefile::AddSubDirectory(const std::string& srcPath, if (excludeFromAll) { subMf->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); } + if (system) { + subMf->SetProperty("SYSTEM", "TRUE"); + } if (immediate) { this->ConfigureSubDirectory(subMf); @@ -3456,7 +3484,7 @@ void cmMakefile::AddTargetObject(std::string const& tgtName, #endif } -void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, +void cmMakefile::EnableLanguage(std::vector<std::string> const& languages, bool optional) { if (this->DeferRunning) { @@ -3468,24 +3496,48 @@ void cmMakefile::EnableLanguage(std::vector<std::string> const& lang, if (const char* def = this->GetGlobalGenerator()->GetCMakeCFGIntDir()) { this->AddDefinition("CMAKE_CFG_INTDIR", def); } + + std::vector<std::string> unique_languages; + { + std::vector<std::string> duplicate_languages; + for (std::string const& language : languages) { + if (!cm::contains(unique_languages, language)) { + unique_languages.push_back(language); + } else if (!cm::contains(duplicate_languages, language)) { + duplicate_languages.push_back(language); + } + } + if (!duplicate_languages.empty()) { + auto quantity = duplicate_languages.size() == 1 ? std::string(" has") + : std::string("s have"); + this->IssueMessage(MessageType::AUTHOR_WARNING, + "Languages to be enabled may not be specified more " + "than once at the same time. The following language" + + quantity + " been specified multiple times: " + + cmJoin(duplicate_languages, ", ")); + } + } + // If RC is explicitly listed we need to do it after other languages. // On some platforms we enable RC implicitly while enabling others. // Do not let that look like recursive enable_language(RC). - std::vector<std::string> langs; - std::vector<std::string> langsRC; - langs.reserve(lang.size()); - for (std::string const& i : lang) { - if (i == "RC") { - langsRC.push_back(i); + std::vector<std::string> languages_without_RC; + std::vector<std::string> languages_for_RC; + languages_without_RC.reserve(unique_languages.size()); + for (std::string const& language : unique_languages) { + if (language == "RC") { + languages_for_RC.push_back(language); } else { - langs.push_back(i); + languages_without_RC.push_back(language); } } - if (!langs.empty()) { - this->GetGlobalGenerator()->EnableLanguage(langs, this, optional); + if (!languages_without_RC.empty()) { + this->GetGlobalGenerator()->EnableLanguage(languages_without_RC, this, + optional); } - if (!langsRC.empty()) { - this->GetGlobalGenerator()->EnableLanguage(langsRC, this, optional); + if (!languages_for_RC.empty()) { + this->GetGlobalGenerator()->EnableLanguage(languages_for_RC, this, + optional); } } @@ -4116,6 +4168,18 @@ void cmMakefile::RaiseScope(const std::string& var, const char* varDef) #endif } +void cmMakefile::RaiseScope(const std::vector<std::string>& variables) +{ + for (auto const& varName : variables) { + if (this->IsNormalDefinitionSet(varName)) { + this->RaiseScope(varName, this->GetDefinition(varName)); + } else { + // unset variable in parent scope + this->RaiseScope(varName, nullptr); + } + } +} + cmTarget* cmMakefile::AddImportedTarget(const std::string& name, cmStateEnums::TargetType type, bool global) @@ -4406,7 +4470,7 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, } // Deprecate old policies. - if (status == cmPolicies::OLD && id <= cmPolicies::CMP0097 && + if (status == cmPolicies::OLD && id <= cmPolicies::CMP0102 && !(this->GetCMakeInstance()->GetIsInTryCompile() && ( // Policies set by cmCoreTryCompile::TryCompileCode. @@ -4471,6 +4535,19 @@ bool cmMakefile::SetPolicyVersion(std::string const& version_min, cmPolicies::WarnCompat::On); } +cmMakefile::VariablePushPop::VariablePushPop(cmMakefile* m) + : Makefile(m) +{ + this->Makefile->StateSnapshot = + this->Makefile->GetState()->CreateVariableScopeSnapshot( + this->Makefile->StateSnapshot); +} + +cmMakefile::VariablePushPop::~VariablePushPop() +{ + this->Makefile->PopSnapshot(); +} + bool cmMakefile::HasCMP0054AlreadyBeenReported( cmListFileContext const& context) const { diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 27838b2..3866aca 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -270,7 +270,7 @@ public: */ void AddSubDirectory(const std::string& fullSrcDir, const std::string& fullBinDir, bool excludeFromAll, - bool immediate); + bool immediate, bool system); void Configure(); @@ -376,6 +376,20 @@ public: }; friend class PolicyPushPop; + /** Helper class to push and pop variables scopes automatically. */ + class VariablePushPop + { + public: + VariablePushPop(cmMakefile* m); + ~VariablePushPop(); + + VariablePushPop(VariablePushPop const&) = delete; + VariablePushPop& operator=(VariablePushPop const&) = delete; + + private: + cmMakefile* Makefile; + }; + /** * Determine if the given context, name pair has already been reported * in context of CMP0054. @@ -861,6 +875,11 @@ public: void PushScope(); void PopScope(); void RaiseScope(const std::string& var, const char* value); + void RaiseScope(const std::string& var, cmValue value) + { + this->RaiseScope(var, value.GetCStr()); + } + void RaiseScope(const std::vector<std::string>& variables); // push and pop loop scopes void PushLoopBlockBarrier(); @@ -924,6 +943,7 @@ public: }; void IssueMessage(MessageType t, std::string const& text) const; + Message::LogLevel GetCurrentLogLevel() const; /** Set whether or not to report a CMP0000 violation. */ void SetCheckCMP0000(bool b) { this->CheckCMP0000 = b; } diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 3849c6f..54f03b9 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -136,17 +136,11 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule( std::vector<std::string> depends; this->AppendLinkDepends(depends, linkLanguage); - // Build a list of compiler flags and linker flags. - std::string langFlags; - std::string linkFlags; - // Add language feature flags. + std::string langFlags; this->LocalGenerator->AddLanguageFlagsForLinking( langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName()); - // Add device-specific linker flags. - this->GetDeviceLinkFlags(linkFlags, linkLanguage); - // Construct a list of files associated with this executable that // may need to be cleaned. std::vector<std::string> exeCleanFiles; @@ -173,23 +167,32 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule( // Set path conversion for link script shells. this->LocalGenerator->SetLinkScriptShell(useLinkScript); - std::unique_ptr<cmLinkLineComputer> linkLineComputer( + std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer( new cmLinkLineDeviceComputer( this->LocalGenerator, this->LocalGenerator->GetStateSnapshot().GetDirectory())); linkLineComputer->SetForResponse(useResponseFileForLibs); linkLineComputer->SetRelink(relink); + // Create set of linking flags. + std::string linkFlags; + std::string ignored_; + this->LocalGenerator->GetDeviceLinkFlags( + *linkLineComputer, this->GetConfigName(), ignored_, linkFlags, ignored_, + ignored_, this->GeneratorTarget); + // Collect up flags to link in needed libraries. std::string linkLibs; - this->CreateLinkLibs(linkLineComputer.get(), linkLibs, - useResponseFileForLibs, depends); + this->CreateLinkLibs( + linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; - this->CreateObjectLists(useLinkScript, false, useResponseFileForObjects, - buildObjs, depends, false); + this->CreateObjectLists( + useLinkScript, false, useResponseFileForObjects, buildObjs, depends, + false, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); cmRulePlaceholderExpander::RuleVariables vars; std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index f30ec27..45ef8c8 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/Source/cmMakefileLibraryTargetGenerator.cxx @@ -287,10 +287,6 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules( 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( @@ -315,22 +311,31 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules( // Collect up flags to link in needed libraries. std::string linkLibs; - std::unique_ptr<cmLinkLineComputer> linkLineComputer( + std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer( new cmLinkLineDeviceComputer( this->LocalGenerator, this->LocalGenerator->GetStateSnapshot().GetDirectory())); linkLineComputer->SetForResponse(useResponseFileForLibs); linkLineComputer->SetRelink(relink); - this->CreateLinkLibs(linkLineComputer.get(), linkLibs, - useResponseFileForLibs, depends); + // Create set of linking flags. + std::string linkFlags; + std::string ignored_; + this->LocalGenerator->GetDeviceLinkFlags( + *linkLineComputer, this->GetConfigName(), ignored_, linkFlags, ignored_, + ignored_, this->GeneratorTarget); + + this->CreateLinkLibs( + linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); // Construct object file lists that may be needed to expand the // rule. std::string buildObjs; - this->CreateObjectLists(useLinkScript, false, // useArchiveRules - useResponseFileForObjects, buildObjs, depends, - false); + this->CreateObjectLists( + useLinkScript, false, // useArchiveRules + useResponseFileForObjects, buildObjs, depends, false, + cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink); std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); objectDir = this->LocalGenerator->ConvertToOutputFormat( diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index aec6577..d19bbb9 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -21,6 +21,7 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommand.h" #include "cmCustomCommandGenerator.h" +#include "cmFileSet.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" @@ -46,6 +47,7 @@ #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" +#include "cmTarget.h" #include "cmValue.h" #include "cmake.h" @@ -107,7 +109,7 @@ std::unique_ptr<cmMakefileTargetGenerator> cmMakefileTargetGenerator::New( return result; } -std::string cmMakefileTargetGenerator::GetConfigName() +std::string cmMakefileTargetGenerator::GetConfigName() const { auto const& configNames = this->LocalGenerator->GetConfigNames(); assert(configNames.size() == 1); @@ -190,6 +192,16 @@ void cmMakefileTargetGenerator::CreateRuleFile() void cmMakefileTargetGenerator::WriteTargetBuildRules() { + this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName()); + + if (this->GeneratorTarget->HaveCxx20ModuleSources()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GeneratorTarget->GetName(), + "\" target contains C++ module sources which are not supported " + "by the generator")); + } + // -- Write the custom commands for this target // Evaluates generator expressions and expands prop_value @@ -302,6 +314,40 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() } } + std::map<std::string, std::string> file_set_map; + + auto const* tgt = this->GeneratorTarget->Target; + for (auto const& name : tgt->GetAllFileSetNames()) { + auto const* file_set = tgt->GetFileSet(name); + if (!file_set) { + this->Makefile->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found.")); + continue; + } + + auto fileEntries = file_set->CompileFileEntries(); + auto directoryEntries = file_set->CompileDirectoryEntries(); + auto directories = file_set->EvaluateDirectoryEntries( + directoryEntries, this->LocalGenerator, this->GetConfigName(), + this->GeneratorTarget); + + std::map<std::string, std::vector<std::string>> files; + for (auto const& entry : fileEntries) { + file_set->EvaluateFileEntry(directories, files, entry, + this->LocalGenerator, this->GetConfigName(), + this->GeneratorTarget); + } + + for (auto const& it : files) { + for (auto const& filename : it.second) { + file_set_map[filename] = file_set->GetType(); + } + } + } + std::vector<cmSourceFile const*> objectSources; this->GeneratorTarget->GetObjectSources(objectSources, this->GetConfigName()); @@ -314,6 +360,25 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules() this->WriteObjectRuleFiles(*sf); } } + + for (cmSourceFile const* sf : objectSources) { + auto const& path = sf->GetFullPath(); + auto const it = file_set_map.find(path); + if (it != file_set_map.end()) { + auto const& file_set_type = it->second; + if (file_set_type == "CXX_MODULES"_s || + file_set_type == "CXX_MODULE_HEADER_UNITS"_s) { + if (sf->GetLanguage() != "CXX"_s) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Target \"", tgt->GetName(), "\" contains the source\n ", path, + "\nin a file set of type \"", file_set_type, + R"(" but the source is not classified as a "CXX" source.)")); + } + } + } + } } void cmMakefileTargetGenerator::WriteCommonCodeRules() @@ -977,8 +1042,10 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles( lang == "OBJCXX")) { std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER"; cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop); - if (cmNonempty(clauncher)) { - compilerLauncher = *clauncher; + std::string evaluatedClauncher = cmGeneratorExpression::Evaluate( + *clauncher, this->LocalGenerator, config); + if (!evaluatedClauncher.empty()) { + compilerLauncher = evaluatedClauncher; } } @@ -2080,7 +2147,7 @@ bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries( } std::string cmMakefileTargetGenerator::CreateResponseFile( - const char* name, std::string const& options, + const std::string& name, std::string const& options, std::vector<std::string>& makefile_depends) { // FIXME: Find a better way to determine the response file encoding, @@ -2126,7 +2193,8 @@ cmMakefileTargetGenerator::CreateLinkLineComputer( void cmMakefileTargetGenerator::CreateLinkLibs( cmLinkLineComputer* linkLineComputer, std::string& linkLibs, - bool useResponseFile, std::vector<std::string>& makefile_depends) + bool useResponseFile, std::vector<std::string>& makefile_depends, + ResponseFlagFor responseMode) { std::string frameworkPath; std::string linkPath; @@ -2139,20 +2207,13 @@ void cmMakefileTargetGenerator::CreateLinkLibs( if (useResponseFile && linkLibs.find_first_not_of(' ') != std::string::npos) { // Lookup the response file reference flag. - std::string responseFlagVar = - cmStrCat("CMAKE_", - this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), - "_RESPONSE_FILE_LINK_FLAG"); - std::string responseFlag; - if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) { - responseFlag = *p; - } else { - responseFlag = "@"; - } + std::string responseFlag = this->GetResponseFlag(responseMode); // Create this response file. + std::string responseFileName = + (responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp"; std::string link_rsp = - this->CreateResponseFile("linklibs.rsp", linkLibs, makefile_depends); + this->CreateResponseFile(responseFileName, linkLibs, makefile_depends); // Reference the response file. linkLibs = cmStrCat(responseFlag, @@ -2164,7 +2225,7 @@ void cmMakefileTargetGenerator::CreateLinkLibs( void cmMakefileTargetGenerator::CreateObjectLists( bool useLinkScript, bool useArchiveRules, bool useResponseFile, std::string& buildObjs, std::vector<std::string>& makefile_depends, - bool useWatcomQuote) + bool useWatcomQuote, ResponseFlagFor responseMode) { std::string variableName; std::string variableNameExternal; @@ -2179,27 +2240,19 @@ void cmMakefileTargetGenerator::CreateObjectLists( this->WriteObjectsStrings(object_strings, responseFileLimit); // Lookup the response file reference flag. - std::string responseFlagVar = - cmStrCat("CMAKE_", - this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), - "_RESPONSE_FILE_LINK_FLAG"); - std::string responseFlag; - if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) { - responseFlag = *p; - } else { - responseFlag = "@"; - } + std::string responseFlag = this->GetResponseFlag(responseMode); // Write a response file for each string. const char* sep = ""; for (unsigned int i = 0; i < object_strings.size(); ++i) { // Number the response files. - char rsp[32]; - snprintf(rsp, sizeof(rsp), "objects%u.rsp", i + 1); + std::string responseFileName = + (responseMode == Link) ? "objects" : "deviceObjects"; + responseFileName += std::to_string(i + 1); // Create this response file. - std::string objects_rsp = - this->CreateResponseFile(rsp, object_strings[i], makefile_depends); + std::string objects_rsp = this->CreateResponseFile( + responseFileName, object_strings[i], makefile_depends); // Separate from previous response file references. buildObjs += sep; @@ -2251,7 +2304,7 @@ void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, } std::string name = cmStrCat("includes_", lang, ".rsp"); std::string arg = std::move(responseFlag) + - this->CreateResponseFile(name.c_str(), includeFlags, + this->CreateResponseFile(name, includeFlags, this->FlagFileDepends[lang]); this->LocalGenerator->AppendFlags(flags, arg); } else { @@ -2304,3 +2357,22 @@ void cmMakefileTargetGenerator::GenDefFile( fout << src->GetFullPath() << "\n"; } } + +std::string cmMakefileTargetGenerator::GetResponseFlag( + ResponseFlagFor mode) const +{ + std::string responseFlag = "@"; + std::string responseFlagVar; + + auto lang = this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()); + if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Link) { + responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG"); + } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) { + responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG"; + } + + if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) { + responseFlag = *p; + } + return responseFlag; +} diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h index cb804e0..dafa650 100644 --- a/Source/cmMakefileTargetGenerator.h +++ b/Source/cmMakefileTargetGenerator.h @@ -56,7 +56,7 @@ public: cmGeneratorTarget* GetGeneratorTarget() { return this->GeneratorTarget; } - std::string GetConfigName(); + std::string GetConfigName() const; protected: void GetDeviceLinkFlags(std::string& linkFlags, @@ -157,22 +157,31 @@ protected: /** Create a response file with the given set of options. Returns the relative path from the target build working directory to the response file name. */ - std::string CreateResponseFile(const char* name, std::string const& options, + std::string CreateResponseFile(const std::string& name, + std::string const& options, std::vector<std::string>& makefile_depends); bool CheckUseResponseFileForObjects(std::string const& l) const; bool CheckUseResponseFileForLibraries(std::string const& l) const; + enum ResponseFlagFor + { + Link, + DeviceLink + }; + /** Create list of flags for link libraries. */ void CreateLinkLibs(cmLinkLineComputer* linkLineComputer, std::string& linkLibs, bool useResponseFile, - std::vector<std::string>& makefile_depends); + std::vector<std::string>& makefile_depends, + ResponseFlagFor responseMode = ResponseFlagFor::Link); /** Create lists of object files for linking and cleaning. */ void CreateObjectLists(bool useLinkScript, bool useArchiveRules, bool useResponseFile, std::string& buildObjs, std::vector<std::string>& makefile_depends, - bool useWatcomQuote); + bool useWatcomQuote, + ResponseFlagFor responseMode = ResponseFlagFor::Link); /** Add commands for generate def files */ void GenDefFile(std::vector<std::string>& real_link_commands); @@ -180,6 +189,9 @@ protected: void AddIncludeFlags(std::string& flags, const std::string& lang, const std::string& config) override; + /** Return the response flag for the given configuration */ + std::string GetResponseFlag(ResponseFlagFor mode) const; + virtual void CloseFileStreams(); cmLocalUnixMakefileGenerator3* LocalGenerator; cmGlobalUnixMakefileGenerator3* GlobalGenerator; diff --git a/Source/cmMessageCommand.cxx b/Source/cmMessageCommand.cxx index cd57600..fa29ec9 100644 --- a/Source/cmMessageCommand.cxx +++ b/Source/cmMessageCommand.cxx @@ -81,94 +81,83 @@ bool cmMessageCommand(std::vector<std::string> const& args, auto type = MessageType::MESSAGE; auto fatal = false; - auto level = cmake::LogLevel::LOG_UNDEFINED; + auto level = Message::LogLevel::LOG_UNDEFINED; auto checkingType = CheckingType::UNDEFINED; if (*i == "SEND_ERROR") { type = MessageType::FATAL_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; ++i; } else if (*i == "FATAL_ERROR") { fatal = true; type = MessageType::FATAL_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; ++i; } else if (*i == "WARNING") { type = MessageType::WARNING; - level = cmake::LogLevel::LOG_WARNING; + level = Message::LogLevel::LOG_WARNING; ++i; } else if (*i == "AUTHOR_WARNING") { if (mf.IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") && !mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) { fatal = true; type = MessageType::AUTHOR_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; } else if (!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { type = MessageType::AUTHOR_WARNING; - level = cmake::LogLevel::LOG_WARNING; + level = Message::LogLevel::LOG_WARNING; } else { return true; } ++i; } else if (*i == "CHECK_START") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; checkingType = CheckingType::CHECK_START; ++i; } else if (*i == "CHECK_PASS") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; checkingType = CheckingType::CHECK_PASS; ++i; } else if (*i == "CHECK_FAIL") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; checkingType = CheckingType::CHECK_FAIL; ++i; } else if (*i == "STATUS") { - level = cmake::LogLevel::LOG_STATUS; + level = Message::LogLevel::LOG_STATUS; ++i; } else if (*i == "VERBOSE") { - level = cmake::LogLevel::LOG_VERBOSE; + level = Message::LogLevel::LOG_VERBOSE; ++i; } else if (*i == "DEBUG") { - level = cmake::LogLevel::LOG_DEBUG; + level = Message::LogLevel::LOG_DEBUG; ++i; } else if (*i == "TRACE") { - level = cmake::LogLevel::LOG_TRACE; + level = Message::LogLevel::LOG_TRACE; ++i; } else if (*i == "DEPRECATION") { if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) { fatal = true; type = MessageType::DEPRECATION_ERROR; - level = cmake::LogLevel::LOG_ERROR; + level = Message::LogLevel::LOG_ERROR; } else if (!mf.IsSet("CMAKE_WARN_DEPRECATED") || mf.IsOn("CMAKE_WARN_DEPRECATED")) { type = MessageType::DEPRECATION_WARNING; - level = cmake::LogLevel::LOG_WARNING; + level = Message::LogLevel::LOG_WARNING; } else { return true; } ++i; } else if (*i == "NOTICE") { // `NOTICE` message type is going to be output to stderr - level = cmake::LogLevel::LOG_NOTICE; + level = Message::LogLevel::LOG_NOTICE; ++i; } else { // Messages w/o any type are `NOTICE`s - level = cmake::LogLevel::LOG_NOTICE; + level = Message::LogLevel::LOG_NOTICE; } assert("Message log level expected to be set" && - level != cmake::LogLevel::LOG_UNDEFINED); - - auto desiredLevel = mf.GetCMakeInstance()->GetLogLevel(); - assert("Expected a valid log level here" && - desiredLevel != cmake::LogLevel::LOG_UNDEFINED); - - // Command line option takes precedence over the cache variable - if (!mf.GetCMakeInstance()->WasLogLevelSetViaCLI()) { - const auto desiredLevelFromCache = - cmake::StringToLogLevel(mf.GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL")); - if (desiredLevelFromCache != cmake::LogLevel::LOG_UNDEFINED) { - desiredLevel = desiredLevelFromCache; - } - } + level != Message::LogLevel::LOG_UNDEFINED); + + Message::LogLevel desiredLevel = mf.GetCurrentLogLevel(); if (desiredLevel < level) { // Suppress the message @@ -178,17 +167,17 @@ bool cmMessageCommand(std::vector<std::string> const& args, auto message = cmJoin(cmMakeRange(i, args.cend()), ""); switch (level) { - case cmake::LogLevel::LOG_ERROR: - case cmake::LogLevel::LOG_WARNING: + case Message::LogLevel::LOG_ERROR: + case Message::LogLevel::LOG_WARNING: // we've overridden the message type, above, so display it directly mf.GetMessenger()->DisplayMessage(type, message, mf.GetBacktrace()); break; - case cmake::LogLevel::LOG_NOTICE: + case Message::LogLevel::LOG_NOTICE: cmSystemTools::Message(IndentText(message, mf)); break; - case cmake::LogLevel::LOG_STATUS: + case Message::LogLevel::LOG_STATUS: switch (checkingType) { case CheckingType::CHECK_START: mf.DisplayStatus(IndentText(message, mf), -1); @@ -209,9 +198,9 @@ bool cmMessageCommand(std::vector<std::string> const& args, } break; - case cmake::LogLevel::LOG_VERBOSE: - case cmake::LogLevel::LOG_DEBUG: - case cmake::LogLevel::LOG_TRACE: + case Message::LogLevel::LOG_VERBOSE: + case Message::LogLevel::LOG_DEBUG: + case Message::LogLevel::LOG_TRACE: mf.DisplayStatus(IndentText(message, mf), -1); break; diff --git a/Source/cmMessageType.h b/Source/cmMessageType.h index 44de429..decb4b3 100644 --- a/Source/cmMessageType.h +++ b/Source/cmMessageType.h @@ -16,3 +16,19 @@ enum class MessageType DEPRECATION_ERROR, DEPRECATION_WARNING }; + +namespace Message { + +/** \brief Define log level constants. */ +enum class LogLevel +{ + LOG_UNDEFINED, + LOG_ERROR, + LOG_WARNING, + LOG_NOTICE, + LOG_STATUS, + LOG_VERBOSE, + LOG_DEBUG, + LOG_TRACE +}; +} diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index d4f1608..bda8a5f 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -537,7 +537,6 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd() // this target requires separable cuda compilation // now build the correct command depending on if the target is // an executable or a dynamic library. - std::string linkCmd; switch (this->GetGeneratorTarget()->GetType()) { case cmStateEnums::STATIC_LIBRARY: case cmStateEnums::SHARED_LIBRARY: @@ -1086,10 +1085,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( this->GetGeneratorTarget()->GetObjectSources(sources, config); cmLocalGenerator const* LocalGen = this->GetLocalGenerator(); for (const auto& source : sources) { + const std::string sourcePath = source->GetLanguage() == "Swift" + ? this->GetCompiledSourceNinjaPath(source) + : this->GetObjectFilePath(source, config); oss << " " - << LocalGen->ConvertToOutputFormat( - this->GetCompiledSourceNinjaPath(source), - cmOutputConverter::SHELL); + << LocalGen->ConvertToOutputFormat(sourcePath, + cmOutputConverter::SHELL); } return oss.str(); }(); @@ -1107,10 +1108,15 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement( std::vector<cmSourceFile const*> sources; gt->GetObjectSources(sources, config); for (const auto& source : sources) { - linkBuild.Outputs.push_back( - this->ConvertToNinjaPath(this->GetObjectFilePath(source, config))); - linkBuild.ExplicitDeps.emplace_back( - this->GetCompiledSourceNinjaPath(source)); + if (source->GetLanguage() == "Swift") { + linkBuild.Outputs.push_back( + this->ConvertToNinjaPath(this->GetObjectFilePath(source, config))); + linkBuild.ExplicitDeps.emplace_back( + this->GetCompiledSourceNinjaPath(source)); + } else { + linkBuild.ExplicitDeps.emplace_back( + this->GetObjectFilePath(source, config)); + } } linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]); } else { diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 3fac7f5..e4427f5 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -21,11 +21,17 @@ #include "cmComputeLinkInformation.h" #include "cmCustomCommandGenerator.h" +#include "cmExportBuildFileGenerator.h" +#include "cmExportSet.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" @@ -36,12 +42,12 @@ #include "cmRange.h" #include "cmRulePlaceholderExpander.h" #include "cmSourceFile.h" -#include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cmTargetExport.h" #include "cmValue.h" #include "cmake.h" @@ -153,17 +159,12 @@ std::string cmNinjaTargetGenerator::LanguageDyndepRule( bool cmNinjaTargetGenerator::NeedCxxModuleSupport( std::string const& lang, std::string const& config) const { - if (lang != "CXX") { + if (lang != "CXX"_s) { return false; } - if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) { - return false; - } - cmGeneratorTarget const* tgt = this->GetGeneratorTarget(); - cmStandardLevelResolver standardResolver(this->Makefile); - bool const uses_cxx20 = - standardResolver.HaveStandardAvailable(tgt, "CXX", config, "cxx_std_20"); - return uses_cxx20 && this->GetGlobalGenerator()->CheckCxxModuleSupport(); + return this->GetGeneratorTarget()->HaveCxxModuleSupport(config) == + cmGeneratorTarget::Cxx20SupportLevel::Supported && + this->GetGlobalGenerator()->CheckCxxModuleSupport(); } bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang, @@ -255,51 +256,53 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject( flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS)); } - if (this->NeedCxxModuleSupport(language, config)) { - auto const& path = source->GetFullPath(); - auto const* tgt = this->GeneratorTarget->Target; + auto const& path = source->GetFullPath(); + auto const* tgt = this->GeneratorTarget->Target; - std::string file_set_type; + std::string file_set_type; - for (auto const& name : tgt->GetAllFileSetNames()) { - auto const* file_set = tgt->GetFileSet(name); - if (!file_set) { - this->GetMakefile()->IssueMessage( - MessageType::INTERNAL_ERROR, - cmStrCat("Target `", tgt->GetName(), - "` is tracked to have file set `", name, - "`, but it was not found.")); - continue; - } + for (auto const& name : tgt->GetAllFileSetNames()) { + auto const* file_set = tgt->GetFileSet(name); + if (!file_set) { + this->GetMakefile()->IssueMessage( + MessageType::INTERNAL_ERROR, + cmStrCat("Target \"", tgt->GetName(), + "\" is tracked to have file set \"", name, + "\", but it was not found.")); + continue; + } - auto fileEntries = file_set->CompileFileEntries(); - auto directoryEntries = file_set->CompileDirectoryEntries(); - auto directories = file_set->EvaluateDirectoryEntries( - directoryEntries, this->LocalGenerator, config, this->GeneratorTarget); + auto fileEntries = file_set->CompileFileEntries(); + auto directoryEntries = file_set->CompileDirectoryEntries(); + auto directories = file_set->EvaluateDirectoryEntries( + directoryEntries, this->LocalGenerator, config, this->GeneratorTarget); - std::map<std::string, std::vector<std::string>> files; - for (auto const& entry : fileEntries) { - file_set->EvaluateFileEntry(directories, files, entry, - this->LocalGenerator, config, - this->GeneratorTarget); - } + std::map<std::string, std::vector<std::string>> files; + for (auto const& entry : fileEntries) { + file_set->EvaluateFileEntry(directories, files, entry, + this->LocalGenerator, config, + this->GeneratorTarget); + } - for (auto const& it : files) { - for (auto const& filename : it.second) { - if (filename == path) { - file_set_type = file_set->GetType(); - break; - } + for (auto const& it : files) { + for (auto const& filename : it.second) { + if (filename == path) { + file_set_type = file_set->GetType(); + break; } } + } - if (!file_set_type.empty()) { - std::string source_type_var = cmStrCat( - "CMAKE_EXPERIMENTAL_CXX_MODULE_SOURCE_TYPE_FLAG_", file_set_type); - cmMakefile* mf = this->GetMakefile(); - if (cmValue source_type_flag = mf->GetDefinition(source_type_var)) { - this->LocalGenerator->AppendFlags(flags, *source_type_flag); - } + if (file_set_type == "CXX_MODULES"_s || + file_set_type == "CXX_MODULE_HEADER_UNITS"_s) { + if (source->GetLanguage() != "CXX"_s) { + this->GetMakefile()->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat( + "Target \"", tgt->GetName(), "\" contains the source\n ", path, + "\nin a file set of type \"", file_set_type, + R"(" but the source is not classified as a "CXX" source.)")); + continue; } } } @@ -909,8 +912,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang, lang == "OBJCXX")) { std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER"); cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop); - if (cmNonempty(clauncher)) { - compilerLauncher = *clauncher; + std::string evaluatedClauncher = cmGeneratorExpression::Evaluate( + *clauncher, this->LocalGenerator, config); + if (!evaluatedClauncher.empty()) { + compilerLauncher = evaluatedClauncher; } } @@ -1038,6 +1043,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( const std::string& config, const std::string& fileConfig, bool firstForConfig) { + this->GeneratorTarget->CheckCxxModuleStatus(config); + // Write comments. cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig)); this->GetImplFileStream(fileConfig) @@ -1338,9 +1345,11 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement( } } - this->ExportObjectCompileCommand( - language, sourceFilePath, objectDir, objectFileName, objectFileDir, - vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); + if (firstForConfig) { + this->ExportObjectCompileCommand( + language, sourceFilePath, objectDir, objectFileName, objectFileDir, + vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config); + } objBuild.Outputs.push_back(objectFileName); if (firstForConfig) { @@ -1616,8 +1625,9 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, mod_dir = this->GeneratorTarget->GetFortranModuleDirectory( this->Makefile->GetHomeOutputDirectory()); } else if (lang == "CXX") { - mod_dir = - cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory); + mod_dir = this->GetGlobalGenerator()->ExpandCFGIntDir( + cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory), + config); } if (mod_dir.empty()) { mod_dir = this->Makefile->GetCurrentBinaryDirectory(); @@ -1654,6 +1664,215 @@ 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; + + // 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); + } + std::string const tdin = this->GetTargetDependInfoPath(lang, config); cmGeneratedFileStream tdif(tdin); tdif << tdi; diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx index 6883535..299ab3a 100644 --- a/Source/cmOutputConverter.cxx +++ b/Source/cmOutputConverter.cxx @@ -527,6 +527,13 @@ bool cmOutputConverter::Shell_ArgumentNeedsQuotes(cm::string_view in, } } + /* UNC paths in MinGW Makefiles need quotes. */ + if ((flags & Shell_Flag_MinGWMake) && (flags & Shell_Flag_Make)) { + if (in.size() > 1 && in[0] == '\\' && in[1] == '\\') { + return true; + } + } + return false; } diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx index 95f3e7e..7e19566 100644 --- a/Source/cmParseArgumentsCommand.cxx +++ b/Source/cmParseArgumentsCommand.cxx @@ -10,6 +10,7 @@ #include <cm/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" @@ -41,11 +42,18 @@ namespace { using options_map = std::map<std::string, bool>; using single_map = std::map<std::string, std::string>; -using multi_map = std::map<std::string, std::vector<std::string>>; -using options_set = std::set<std::string>; +using multi_map = + std::map<std::string, ArgumentParser::NonEmpty<std::vector<std::string>>>; +using options_set = std::set<cm::string_view>; struct UserArgumentParser : public cmArgumentParser<void> { + void BindKeywordsMissingValue(std::vector<cm::string_view>& ref) + { + this->cmArgumentParser<void>::BindKeywordMissingValue( + [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); }); + } + template <typename T, typename H> void Bind(std::vector<std::string> const& names, std::map<std::string, T>& ref, H duplicateKey) @@ -208,9 +216,10 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args, } } - std::vector<std::string> keywordsMissingValues; + std::vector<cm::string_view> keywordsMissingValues; + parser.BindKeywordsMissingValue(keywordsMissingValues); - parser.Parse(list, &unparsed, &keywordsMissingValues); + parser.Parse(list, &unparsed); PassParsedArguments( prefix, status.GetMakefile(), options, singleValArgs, multiValArgs, diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index cb7402c..4643868 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -421,7 +421,17 @@ class cmMakefile; SELECT( \ POLICY, CMP0139, \ "The if() command supports path comparisons using PATH_EQUAL operator.", \ - 3, 24, 0, cmPolicies::WARN) + 3, 24, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0140, "The return() command checks its arguments.", 3, \ + 25, 0, cmPolicies::WARN) \ + SELECT( \ + POLICY, CMP0141, \ + "MSVC debug information format flags are selected by an abstraction.", 3, \ + 25, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0142, \ + "The Xcode generator does not append per-config suffixes to " \ + "library search paths.", \ + 3, 25, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -458,7 +468,8 @@ class cmMakefile; F(CMP0112) \ F(CMP0113) \ F(CMP0119) \ - F(CMP0131) + F(CMP0131) \ + F(CMP0142) /** \class cmPolicies * \brief Handles changes in CMake behavior and policies diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 40f3ab5..96649ab 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -1792,13 +1792,16 @@ void cmQtAutoGenInitializer::AddGeneratedSource(ConfigString const& filename, // XXX(xcode-per-cfg-src): Drop the Xcode-specific part of the condition // when the Xcode generator supports per-config sources. if (!this->MultiConfig || this->GlobalGen->IsXcode()) { - this->AddGeneratedSource(filename.Default, genVars, prepend); + cmSourceFile* sf = + this->AddGeneratedSource(filename.Default, genVars, prepend); + handleSkipPch(sf); return; } for (auto const& cfg : this->ConfigsList) { std::string const& filenameCfg = filename.Config.at(cfg); // Register source at makefile - this->RegisterGeneratedSource(filenameCfg); + cmSourceFile* sf = this->RegisterGeneratedSource(filenameCfg); + handleSkipPch(sf); // Add source file to target for this configuration. this->GenTarget->AddSource( cmStrCat("$<$<CONFIG:"_s, cfg, ">:"_s, filenameCfg, ">"_s), prepend); @@ -1847,8 +1850,7 @@ void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName, void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName) { - this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName, - false); + this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName); } void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString, @@ -2159,3 +2161,18 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars, return true; } + +void cmQtAutoGenInitializer::handleSkipPch(cmSourceFile* sf) +{ + bool skipPch = true; + for (auto const& pair : this->AutogenTarget.Sources) { + if (!pair.first->GetIsGenerated() && + !pair.first->GetProperty("SKIP_PRECOMPILE_HEADERS")) { + skipPch = false; + } + } + + if (skipPch) { + sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON"); + } +} diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index 33749ba..6d5261a 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -154,6 +154,8 @@ private: bool GetQtExecutable(GenVarsT& genVars, const std::string& executable, bool ignoreMissingTarget) const; + void handleSkipPch(cmSourceFile* sf); + cmQtAutoGenGlobalInitializer* GlobalInitializer = nullptr; cmGeneratorTarget* GenTarget = nullptr; cmGlobalGenerator* GlobalGen = nullptr; diff --git a/Source/cmReturnCommand.cxx b/Source/cmReturnCommand.cxx index 5905669..765b772 100644 --- a/Source/cmReturnCommand.cxx +++ b/Source/cmReturnCommand.cxx @@ -2,12 +2,52 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmReturnCommand.h" +#include <cm/string_view> +#include <cmext/string_view> + #include "cmExecutionStatus.h" +#include "cmMakefile.h" +#include "cmMessageType.h" +#include "cmPolicies.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" // cmReturnCommand -bool cmReturnCommand(std::vector<std::string> const&, +bool cmReturnCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { - status.SetReturnInvoked(); + if (!args.empty()) { + switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0140)) { + case cmPolicies::WARN: + status.GetMakefile().IssueMessage( + MessageType::AUTHOR_WARNING, + cmStrCat( + cmPolicies::GetPolicyWarning(cmPolicies::CMP0140), '\n', + "return() checks its arguments when the policy is set to NEW. " + "Since the policy is not set the OLD behavior will be used so " + "the arguments will be ignored.")); + CM_FALLTHROUGH; + case cmPolicies::OLD: + return true; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + status.GetMakefile().IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat('\n', cmPolicies::GetPolicyWarning(cmPolicies::CMP0140))); + cmSystemTools::SetFatalErrorOccurred(); + return false; + default: + break; + } + if (args[0] != "PROPAGATE"_s) { + status.SetError( + cmStrCat("called with unsupported argument \"", args[0], '"')); + cmSystemTools::SetFatalErrorOccurred(); + return false; + } + status.SetReturnInvoked({ args.begin() + 1, args.end() }); + } else { + status.SetReturnInvoked(); + } return true; } diff --git a/Source/cmScanDepFormat.cxx b/Source/cmScanDepFormat.cxx index 82a374a..81ef3da 100644 --- a/Source/cmScanDepFormat.cxx +++ b/Source/cmScanDepFormat.cxx @@ -188,6 +188,19 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp, return false; } + if (provide.isMember("is-interface")) { + Json::Value const& is_interface = provide["is-interface"]; + if (!is_interface.isBool()) { + cmSystemTools::Error( + cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp, + ": is-interface is not a boolean")); + return false; + } + provide_info.IsInterface = is_interface.asBool(); + } else { + provide_info.IsInterface = true; + } + info->Provides.push_back(provide_info); } } @@ -308,6 +321,8 @@ bool cmScanDepFormat_P1689_Write(std::string const& path, provide_obj["source-path"] = EncodeFilename(provide.SourcePath); } + provide_obj["is-interface"] = provide.IsInterface; + provides.append(provide_obj); } diff --git a/Source/cmScanDepFormat.h b/Source/cmScanDepFormat.h index dae28d9..dc55bf1 100644 --- a/Source/cmScanDepFormat.h +++ b/Source/cmScanDepFormat.h @@ -18,6 +18,11 @@ struct cmSourceReqInfo std::string SourcePath; std::string CompiledModulePath; bool UseSourcePath = false; + + // Provides-only fields. + bool IsInterface = true; + + // Requires-only fields. LookupMethod Method = LookupMethod::ByName; }; diff --git a/Source/cmScriptGenerator.cxx b/Source/cmScriptGenerator.cxx index 166ee56..32f9bec 100644 --- a/Source/cmScriptGenerator.cxx +++ b/Source/cmScriptGenerator.cxx @@ -133,7 +133,7 @@ void cmScriptGenerator::GenerateScriptActionsOnce(std::ostream& os, std::string config_test = this->CreateConfigTest(this->Configurations); os << indent << "if(" << config_test << ")\n"; this->GenerateScriptActions(os, indent.Next()); - os << indent << "endif(" << config_test << ")\n"; + os << indent << "endif()\n"; } } diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx index db10cd4..521cf63 100644 --- a/Source/cmSetPropertyCommand.cxx +++ b/Source/cmSetPropertyCommand.cxx @@ -9,6 +9,7 @@ #include "cmExecutionStatus.h" #include "cmGlobalGenerator.h" #include "cmInstalledFile.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" @@ -561,7 +562,8 @@ bool HandleTarget(cmTarget* target, cmMakefile& makefile, { // Set or append the property. if (appendMode) { - target->AppendProperty(propertyName, propertyValue, appendAsString); + target->AppendProperty(propertyName, propertyValue, + makefile.GetBacktrace(), appendAsString); } else { if (remove) { target->SetProperty(propertyName, nullptr); diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx index 785f356..d2eac0c 100644 --- a/Source/cmStandardLevelResolver.cxx +++ b/Source/cmStandardLevelResolver.cxx @@ -18,6 +18,7 @@ #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" @@ -378,25 +379,29 @@ std::unordered_map<std::string, StandardLevelComputer> "C", std::vector<int>{ 90, 99, 11, 17, 23 }, std::vector<std::string>{ "90", "99", "11", "17", "23" } } }, { "CXX", - StandardLevelComputer{ - "CXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 }, - std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } }, + StandardLevelComputer{ "CXX", + std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 }, + std::vector<std::string>{ "98", "11", "14", "17", + "20", "23", "26" } } }, { "CUDA", - StandardLevelComputer{ - "CUDA", std::vector<int>{ 03, 11, 14, 17, 20, 23 }, - std::vector<std::string>{ "03", "11", "14", "17", "20", "23" } } }, + StandardLevelComputer{ "CUDA", + std::vector<int>{ 03, 11, 14, 17, 20, 23, 26 }, + std::vector<std::string>{ "03", "11", "14", "17", + "20", "23", "26" } } }, { "OBJC", StandardLevelComputer{ "OBJC", std::vector<int>{ 90, 99, 11, 17, 23 }, std::vector<std::string>{ "90", "99", "11", "17", "23" } } }, { "OBJCXX", - StandardLevelComputer{ - "OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 }, - std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } }, + StandardLevelComputer{ "OBJCXX", + std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 }, + std::vector<std::string>{ "98", "11", "14", "17", + "20", "23", "26" } } }, { "HIP", - StandardLevelComputer{ - "HIP", std::vector<int>{ 98, 11, 14, 17, 20, 23 }, - std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } } + StandardLevelComputer{ "HIP", + std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 }, + std::vector<std::string>{ "98", "11", "14", "17", + "20", "23", "26" } } } }; } @@ -416,7 +421,8 @@ 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); + target->AppendProperty("COMPILE_FEATURES", feature, + this->Makefile->GetBacktrace()); return true; } @@ -426,7 +432,8 @@ bool cmStandardLevelResolver::AddRequiredTargetFeature( return false; } - target->AppendProperty("COMPILE_FEATURES", feature); + target->AppendProperty("COMPILE_FEATURES", feature, + this->Makefile->GetBacktrace()); // FIXME: Add a policy to avoid updating the <LANG>_STANDARD target // property due to COMPILE_FEATURES. The language standard selection diff --git a/Source/cmState.cxx b/Source/cmState.cxx index 3d38e73..e54ccfc 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -281,7 +281,7 @@ cmStateSnapshot cmState::Reset() it->CompileOptions.clear(); it->LinkOptions.clear(); it->LinkDirectories.clear(); - it->DirectoryEnd = pos; + it->CurrentScope = pos; it->NormalTargetNames.clear(); it->ImportedTargetNames.clear(); it->Properties.Clear(); @@ -819,7 +819,7 @@ cmStateSnapshot cmState::CreateBaseSnapshot() pos->CompileOptionsPosition = 0; pos->LinkOptionsPosition = 0; pos->LinkDirectoriesPosition = 0; - pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->BuildSystemDirectory->CurrentScope = pos; pos->Policies = this->PolicyStack.Root(); pos->PolicyRoot = this->PolicyStack.Root(); pos->PolicyScope = this->PolicyStack.Root(); @@ -846,7 +846,7 @@ cmStateSnapshot cmState::CreateBuildsystemDirectorySnapshot( originSnapshot.Position->BuildSystemDirectory); pos->ExecutionListFile = this->ExecutionListFiles.Push(originSnapshot.Position->ExecutionListFile); - pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->BuildSystemDirectory->CurrentScope = pos; pos->Policies = originSnapshot.Position->Policies; pos->PolicyRoot = originSnapshot.Position->Policies; pos->PolicyScope = originSnapshot.Position->Policies; @@ -876,7 +876,7 @@ cmStateSnapshot cmState::CreateDeferCallSnapshot( pos->ExecutionListFile = this->ExecutionListFiles.Push( originSnapshot.Position->ExecutionListFile, fileName); assert(originSnapshot.Position->Vars.IsValid()); - pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; return { this, pos }; } @@ -891,7 +891,7 @@ cmStateSnapshot cmState::CreateFunctionCallSnapshot( pos->Keep = false; pos->ExecutionListFile = this->ExecutionListFiles.Push( originSnapshot.Position->ExecutionListFile, fileName); - pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; assert(originSnapshot.Position->Vars.IsValid()); cmLinkedTree<cmDefinitions>::iterator origin = originSnapshot.Position->Vars; @@ -910,7 +910,7 @@ cmStateSnapshot cmState::CreateMacroCallSnapshot( pos->ExecutionListFile = this->ExecutionListFiles.Push( originSnapshot.Position->ExecutionListFile, fileName); assert(originSnapshot.Position->Vars.IsValid()); - pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; return { this, pos }; } @@ -925,7 +925,7 @@ cmStateSnapshot cmState::CreateIncludeFileSnapshot( pos->ExecutionListFile = this->ExecutionListFiles.Push( originSnapshot.Position->ExecutionListFile, fileName); assert(originSnapshot.Position->Vars.IsValid()); - pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; return { this, pos }; } @@ -938,6 +938,7 @@ cmStateSnapshot cmState::CreateVariableScopeSnapshot( pos->ScopeParent = originSnapshot.Position; pos->SnapshotType = cmStateEnums::VariableScopeType; pos->Keep = false; + pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; assert(originSnapshot.Position->Vars.IsValid()); @@ -957,7 +958,7 @@ cmStateSnapshot cmState::CreateInlineListFileSnapshot( pos->Keep = true; pos->ExecutionListFile = this->ExecutionListFiles.Push( originSnapshot.Position->ExecutionListFile, fileName); - pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; return { this, pos }; } @@ -969,7 +970,7 @@ cmStateSnapshot cmState::CreatePolicyScopeSnapshot( this->SnapshotData.Push(originSnapshot.Position, *originSnapshot.Position); pos->SnapshotType = cmStateEnums::PolicyScopeType; pos->Keep = false; - pos->BuildSystemDirectory->DirectoryEnd = pos; + pos->BuildSystemDirectory->CurrentScope = pos; pos->PolicyScope = originSnapshot.Position->Policies; return { this, pos }; } @@ -989,7 +990,7 @@ cmStateSnapshot cmState::Pop(cmStateSnapshot const& originSnapshot) prevPos->BuildSystemDirectory->LinkOptions.size(); prevPos->LinkDirectoriesPosition = prevPos->BuildSystemDirectory->LinkDirectories.size(); - prevPos->BuildSystemDirectory->DirectoryEnd = prevPos; + prevPos->BuildSystemDirectory->CurrentScope = prevPos; if (!pos->Keep && this->SnapshotData.IsLast(pos)) { if (pos->Vars != prevPos->Vars) { diff --git a/Source/cmStatePrivate.h b/Source/cmStatePrivate.h index fd46eed..ec14834 100644 --- a/Source/cmStatePrivate.h +++ b/Source/cmStatePrivate.h @@ -62,7 +62,7 @@ struct cmStateDetail::PolicyStackEntry : public cmPolicies::PolicyMap struct cmStateDetail::BuildsystemDirectoryStateType { - cmStateDetail::PositionType DirectoryEnd; + cmStateDetail::PositionType CurrentScope; std::string Location; std::string OutputLocation; diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index f73df8f..cb5f11f 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx @@ -64,7 +64,7 @@ bool cmStateSnapshot::IsValid() const cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectory() const { - return { this->State, this->Position->BuildSystemDirectory->DirectoryEnd }; + return { this->State, this->Position->BuildSystemDirectory->CurrentScope }; } cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectoryParent() const @@ -76,7 +76,7 @@ cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectoryParent() const cmStateDetail::PositionType parentPos = this->Position->DirectoryParent; if (parentPos != this->State->SnapshotData.Root()) { snapshot = cmStateSnapshot(this->State, - parentPos->BuildSystemDirectory->DirectoryEnd); + parentPos->BuildSystemDirectory->CurrentScope); } return snapshot; @@ -177,9 +177,9 @@ cmPolicies::PolicyStatus cmStateSnapshot::GetPolicy(cmPolicies::PolicyID id, while (true) { assert(dir.IsValid()); cmLinkedTree<cmStateDetail::PolicyStackEntry>::iterator leaf = - dir->DirectoryEnd->Policies; + dir->CurrentScope->Policies; cmLinkedTree<cmStateDetail::PolicyStackEntry>::iterator root = - dir->DirectoryEnd->PolicyRoot; + dir->CurrentScope->PolicyRoot; for (; leaf != root; ++leaf) { if (parent_scope) { parent_scope = false; @@ -190,7 +190,7 @@ cmPolicies::PolicyStatus cmStateSnapshot::GetPolicy(cmPolicies::PolicyID id, return status; } } - cmStateDetail::PositionType e = dir->DirectoryEnd; + cmStateDetail::PositionType e = dir->CurrentScope; cmStateDetail::PositionType p = e->DirectoryParent; if (p == this->State->SnapshotData.Root()) { break; @@ -317,6 +317,25 @@ void cmStateSnapshot::SetDefaultDefinitions() this->SetDefinition("CMAKE_HOST_SOLARIS", "1"); #endif +#if defined(__OpenBSD__) + this->SetDefinition("BSD", "OpenBSD"); + this->SetDefinition("CMAKE_HOST_BSD", "OpenBSD"); +#elif defined(__FreeBSD__) + this->SetDefinition("BSD", "FreeBSD"); + this->SetDefinition("CMAKE_HOST_BSD", "FreeBSD"); +#elif defined(__NetBSD__) + this->SetDefinition("BSD", "NetBSD"); + this->SetDefinition("CMAKE_HOST_BSD", "NetBSD"); +#elif defined(__DragonFly__) + this->SetDefinition("BSD", "DragonFlyBSD"); + this->SetDefinition("CMAKE_HOST_BSD", "DragonFlyBSD"); +#endif + +#if defined(__linux__) + this->SetDefinition("LINUX", "1"); + this->SetDefinition("CMAKE_HOST_LINUX", "1"); +#endif + this->SetDefinition("CMAKE_MAJOR_VERSION", std::to_string(cmVersion::GetMajorVersion())); this->SetDefinition("CMAKE_MINOR_VERSION", diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index fe311d1..c12d1fe 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx @@ -1014,7 +1014,7 @@ int ParseIndex( 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(), + throw json_error({ "expected an index less than "_s, sizeStr.View(), " got '"_s, str, "'"_s }, progress); } diff --git a/Source/cmSubdirCommand.cxx b/Source/cmSubdirCommand.cxx index 2477d7a..47082f1 100644 --- a/Source/cmSubdirCommand.cxx +++ b/Source/cmSubdirCommand.cxx @@ -32,7 +32,7 @@ bool cmSubdirCommand(std::vector<std::string> const& args, std::string srcPath = mf.GetCurrentSourceDirectory() + "/" + i; if (cmSystemTools::FileIsDirectory(srcPath)) { std::string binPath = mf.GetCurrentBinaryDirectory() + "/" + i; - mf.AddSubDirectory(srcPath, binPath, excludeFromAll, false); + mf.AddSubDirectory(srcPath, binPath, excludeFromAll, false, false); } // otherwise it is a full path else if (cmSystemTools::FileIsDirectory(i)) { @@ -40,7 +40,7 @@ bool cmSubdirCommand(std::vector<std::string> const& args, // element from the source path and use that std::string binPath = mf.GetCurrentBinaryDirectory() + "/" + cmSystemTools::GetFilenameName(i); - mf.AddSubDirectory(i, binPath, excludeFromAll, false); + mf.AddSubDirectory(i, binPath, excludeFromAll, false, false); } else { status.SetError(cmStrCat("Incorrect SUBDIRS command. Directory: ", i, " does not exist.")); diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 41fc02a..ee74908 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -11,11 +11,17 @@ // NOLINTNEXTLINE(bugprone-reserved-identifier) # define _XOPEN_SOURCE 700 #endif +#if defined(__APPLE__) +// Restore Darwin APIs removed by _POSIX_C_SOURCE. +// NOLINTNEXTLINE(bugprone-reserved-identifier) +# define _DARWIN_C_SOURCE +#endif #include "cmSystemTools.h" #include <cm/optional> #include <cmext/algorithm> +#include <cmext/string_view> #include <cm3p/uv.h> @@ -87,7 +93,6 @@ # include <unistd.h> # include <sys/time.h> -# include <sys/types.h> #endif #if defined(_WIN32) && \ @@ -1000,6 +1005,93 @@ void cmSystemTools::InitializeLibUV() #endif } +#if defined(_WIN32) +# include <random> + +# include <wctype.h> +# ifdef _MSC_VER +using mode_t = cmSystemTools::SystemTools::mode_t; +# endif +#else +# include <sys/stat.h> +#endif + +inline int Mkdir(const char* dir, const mode_t* mode) +{ +#if defined(_WIN32) + int ret = _wmkdir(cmSystemTools::ConvertToWindowsExtendedPath(dir).c_str()); + if (ret == 0 && mode) + cmSystemTools::SystemTools::SetPermissions(dir, *mode); + return ret; +#else + return mkdir(dir, mode ? *mode : 0777); +#endif +} + +cmsys::Status cmSystemTools::MakeTempDirectory(std::string& path, + const mode_t* mode) +{ + if (path.empty()) { + return cmsys::Status::POSIX(EINVAL); + } + return cmSystemTools::MakeTempDirectory(&path.front(), mode); +} + +cmsys::Status cmSystemTools::MakeTempDirectory(char* path, const mode_t* mode) +{ + if (!path) { + return cmsys::Status::POSIX(EINVAL); + } + + // verify that path ends with "XXXXXX" + const auto l = std::strlen(path); + if (!cmHasLiteralSuffix(cm::string_view{ path, l }, "XXXXXX")) { + return cmsys::Status::POSIX(EINVAL); + } + + // create parent directories + auto* sep = path; + while ((sep = strchr(sep, '/'))) { + // all underlying functions use C strings, + // so temporarily end the string here + *sep = '\0'; + Mkdir(path, mode); + + *sep = '/'; + ++sep; + } + +#ifdef _WIN32 + const int nchars = 36; + const char chars[nchars + 1] = "abcdefghijklmnopqrstuvwxyz0123456789"; + + std::random_device rd; + std::mt19937 rg{ rd() }; + std::uniform_int_distribution<int> dist{ 0, nchars - 1 }; + + for (auto tries = 100; tries; --tries) { + for (auto n = l - 6; n < l; ++n) { + path[n] = chars[dist(rg)]; + } + if (Mkdir(path, mode) == 0) { + return cmsys::Status::Success(); + } else if (errno != EEXIST) { + return cmsys::Status::POSIX_errno(); + } + } + return cmsys::Status::POSIX(EAGAIN); +#else + if (mkdtemp(path)) { + if (mode) { + chmod(path, *mode); + } + } else { + return cmsys::Status::POSIX_errno(); + } + return cmsys::Status::Success(); +#endif +} + #ifdef _WIN32 namespace { bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname, @@ -1539,8 +1631,7 @@ std::string cmSystemTools::RelativeIfUnder(std::string const& top, bool cmSystemTools::UnsetEnv(const char* value) { # if !defined(HAVE_UNSETENV) - std::string var = cmStrCat(value, '='); - return cmSystemTools::PutEnv(var); + return cmSystemTools::UnPutEnv(value); # else unsetenv(value); return true; @@ -1551,9 +1642,18 @@ std::vector<std::string> cmSystemTools::GetEnvironmentVariables() { std::vector<std::string> env; int cc; +# ifdef _WIN32 + // if program starts with main, _wenviron is initially NULL, call to + // _wgetenv and create wide-character string environment + _wgetenv(L""); + for (cc = 0; _wenviron[cc]; ++cc) { + env.emplace_back(cmsys::Encoding::ToNarrow(_wenviron[cc])); + } +# else for (cc = 0; environ[cc]; ++cc) { env.emplace_back(environ[cc]); } +# endif return env; } @@ -1564,6 +1664,144 @@ void cmSystemTools::AppendEnv(std::vector<std::string> const& env) } } +void cmSystemTools::EnvDiff::AppendEnv(std::vector<std::string> const& env) +{ + for (std::string const& eit : env) { + this->PutEnv(eit); + } +} + +void cmSystemTools::EnvDiff::PutEnv(const std::string& env) +{ + auto const eq_loc = env.find('='); + if (eq_loc != std::string::npos) { + std::string name = env.substr(0, eq_loc); + diff[name] = env.substr(eq_loc + 1); + } else { + this->UnPutEnv(env); + } +} + +void cmSystemTools::EnvDiff::UnPutEnv(const std::string& env) +{ + diff[env] = {}; +} + +bool cmSystemTools::EnvDiff::ParseOperation(const std::string& envmod) +{ + char path_sep = GetSystemPathlistSeparator(); + + auto apply_diff = [this](const std::string& name, + std::function<void(std::string&)> const& apply) { + cm::optional<std::string> old_value = diff[name]; + std::string output; + if (old_value) { + output = *old_value; + } else { + const char* curval = cmSystemTools::GetEnv(name); + if (curval) { + output = curval; + } + } + apply(output); + diff[name] = output; + }; + + // Split on `=` + auto const eq_loc = envmod.find_first_of('='); + if (eq_loc == std::string::npos) { + cmSystemTools::Error(cmStrCat( + "Error: Missing `=` after the variable name in: ", envmod, '\n')); + return false; + } + + auto const name = envmod.substr(0, eq_loc); + + // Split value on `:` + auto const op_value_start = eq_loc + 1; + auto const colon_loc = envmod.find_first_of(':', op_value_start); + if (colon_loc == std::string::npos) { + cmSystemTools::Error( + cmStrCat("Error: Missing `:` after the operation in: ", envmod, '\n')); + return false; + } + auto const op = envmod.substr(op_value_start, colon_loc - op_value_start); + + auto const value_start = colon_loc + 1; + auto const value = envmod.substr(value_start); + + // Determine what to do with the operation. + if (op == "reset"_s) { + auto entry = diff.find(name); + if (entry != diff.end()) { + diff.erase(entry); + } + } else if (op == "set"_s) { + diff[name] = value; + } else if (op == "unset"_s) { + diff[name] = {}; + } else if (op == "string_append"_s) { + apply_diff(name, [&value](std::string& output) { output += value; }); + } else if (op == "string_prepend"_s) { + apply_diff(name, + [&value](std::string& output) { output.insert(0, value); }); + } else if (op == "path_list_append"_s) { + apply_diff(name, [&value, path_sep](std::string& output) { + if (!output.empty()) { + output += path_sep; + } + output += value; + }); + } else if (op == "path_list_prepend"_s) { + apply_diff(name, [&value, path_sep](std::string& output) { + if (!output.empty()) { + output.insert(output.begin(), path_sep); + } + output.insert(0, value); + }); + } else if (op == "cmake_list_append"_s) { + apply_diff(name, [&value](std::string& output) { + if (!output.empty()) { + output += ';'; + } + output += value; + }); + } else if (op == "cmake_list_prepend"_s) { + apply_diff(name, [&value](std::string& output) { + if (!output.empty()) { + output.insert(output.begin(), ';'); + } + output.insert(0, value); + }); + } else { + cmSystemTools::Error(cmStrCat( + "Error: Unrecognized environment manipulation argument: ", op, '\n')); + return false; + } + + return true; +} + +void cmSystemTools::EnvDiff::ApplyToCurrentEnv(std::ostringstream* measurement) +{ + for (auto const& env_apply : diff) { + if (env_apply.second) { + auto const env_update = + cmStrCat(env_apply.first, '=', *env_apply.second); + cmSystemTools::PutEnv(env_update); + if (measurement) { + *measurement << env_update << std::endl; + } + } else { + cmSystemTools::UnsetEnv(env_apply.first.c_str()); + if (measurement) { + // Signify that this variable is being actively unset + *measurement << '#' << env_apply.first << "=\n"; + } + } + } +} + cmSystemTools::SaveRestoreEnvironment::SaveRestoreEnvironment() { this->Env = cmSystemTools::GetEnvironmentVariables(); @@ -3352,8 +3590,19 @@ std::string cmSystemTools::EncodeURL(std::string const& in, bool escapeSlashes) } cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName, - std::string const& newName, - std::string* errorMessage) + std::string const& newName) +{ + cmsys::Status status = + cmSystemTools::CreateSymlinkQuietly(origName, newName); + if (!status) { + cmSystemTools::Error(cmStrCat("failed to create symbolic link '", newName, + "': ", status.GetString())); + } + return status; +} + +cmsys::Status cmSystemTools::CreateSymlinkQuietly(std::string const& origName, + std::string const& newName) { uv_fs_t req; int flags = 0; @@ -3373,20 +3622,23 @@ cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName, #else status = cmsys::Status::POSIX(-err); #endif - std::string e = cmStrCat("failed to create symbolic link '", newName, - "': ", status.GetString()); - if (errorMessage) { - *errorMessage = std::move(e); - } else { - cmSystemTools::Error(e); - } } return status; } cmsys::Status cmSystemTools::CreateLink(std::string const& origName, - std::string const& newName, - std::string* errorMessage) + std::string const& newName) +{ + cmsys::Status status = cmSystemTools::CreateLinkQuietly(origName, newName); + if (!status) { + cmSystemTools::Error( + cmStrCat("failed to create link '", newName, "': ", status.GetString())); + } + return status; +} + +cmsys::Status cmSystemTools::CreateLinkQuietly(std::string const& origName, + std::string const& newName) { uv_fs_t req; int err = @@ -3400,13 +3652,6 @@ cmsys::Status cmSystemTools::CreateLink(std::string const& origName, #else status = cmsys::Status::POSIX(-err); #endif - std::string e = - cmStrCat("failed to create link '", newName, "': ", status.GetString()); - if (errorMessage) { - *errorMessage = std::move(e); - } else { - cmSystemTools::Error(e); - } } return status; } diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index ec650f7..87b354c 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -4,11 +4,18 @@ #include "cmConfigure.h" // IWYU pragma: keep +#if !defined(_WIN32) +# include <sys/types.h> +#endif + #include <cstddef> #include <functional> +#include <map> +#include <sstream> #include <string> #include <vector> +#include <cm/optional> #include <cm/string_view> #include "cmsys/Process.h" @@ -148,6 +155,27 @@ public: Failure, }; +#if defined(_MSC_VER) + /** Visual C++ does not define mode_t. */ + using mode_t = unsigned short; +#endif + + /** + * Make a new temporary directory. The path must end in "XXXXXX", and will + * be modified to reflect the name of the directory created. This function + * is similar to POSIX mkdtemp (and is implemented using the same where that + * function is available). + * + * This function can make a full path even if none of the directories existed + * prior to calling this function. + * + * Note that this function may modify \p path even if it does not succeed. + */ + static cmsys::Status MakeTempDirectory(char* path, + const mode_t* mode = nullptr); + static cmsys::Status MakeTempDirectory(std::string& path, + const mode_t* mode = nullptr); + /** Copy a file. */ static bool CopySingleFile(const std::string& oldname, const std::string& newname); @@ -377,6 +405,42 @@ public: /** Append multiple variables to the current environment. */ static void AppendEnv(std::vector<std::string> const& env); + /** + * Helper class to represent an environment diff directly. This is to avoid + * repeated in-place environment modification (i.e. via setenv/putenv), which + * could be slow. + */ + class EnvDiff + { + public: + /** Append multiple variables to the current environment diff */ + void AppendEnv(std::vector<std::string> const& env); + + /** + * Add a single variable (or remove if no = sign) to the current + * environment diff. + */ + void PutEnv(const std::string& env); + + /** Remove a single variable from the current environment diff. */ + void UnPutEnv(const std::string& env); + + /** + * Apply an ENVIRONMENT_MODIFICATION operation to this diff. Returns + * false and issues an error on parse failure. + */ + bool ParseOperation(const std::string& envmod); + + /** + * Apply this diff to the actual environment, optionally writing out the + * modifications to a CTest-compatible measurement stream. + */ + void ApplyToCurrentEnv(std::ostringstream* measurement = nullptr); + + private: + std::map<std::string, cm::optional<std::string>> diff; + }; + /** Helper class to save and restore the environment. Instantiate this class as an automatic variable on the stack. Its constructor saves a copy of the current @@ -531,14 +595,16 @@ public: /** Create a symbolic link if the platform supports it. Returns whether creation succeeded. */ static cmsys::Status CreateSymlink(std::string const& origName, - std::string const& newName, - std::string* errorMessage = nullptr); + std::string const& newName); + static cmsys::Status CreateSymlinkQuietly(std::string const& origName, + std::string const& newName); /** Create a hard link if the platform supports it. Returns whether creation succeeded. */ static cmsys::Status CreateLink(std::string const& origName, - std::string const& newName, - std::string* errorMessage = nullptr); + std::string const& newName); + static cmsys::Status CreateLinkQuietly(std::string const& origName, + std::string const& newName); /** Get the system name. */ static cm::string_view GetSystemName(); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index cbe5d7d..874195b 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -272,6 +272,8 @@ public: cmListFileBacktrace Backtrace; FileSetType HeadersFileSets; + FileSetType CxxModulesFileSets; + FileSetType CxxModuleHeadersFileSets; cmTargetInternals(); @@ -293,6 +295,12 @@ public: cm::string_view fileSetType) const; cmValue GetFileSetPaths(cmTarget const* self, std::string const& fileSetName, cm::string_view fileSetType) const; + + cmListFileBacktrace GetBacktrace( + cm::optional<cmListFileBacktrace> const& bt) const + { + return bt ? *bt : this->Makefile->GetBacktrace(); + } }; cmTargetInternals::cmTargetInternals() @@ -301,6 +309,19 @@ cmTargetInternals::cmTargetInternals() "The default header set"_s, "Header set"_s, FileSetEntries("HEADER_SETS"_s), FileSetEntries("INTERFACE_HEADER_SETS"_s)) + , CxxModulesFileSets("CXX_MODULES"_s, "CXX_MODULE_DIRS"_s, + "CXX_MODULE_SET"_s, "CXX_MODULE_DIRS_"_s, + "CXX_MODULE_SET_"_s, "C++ module"_s, + "The default C++ module set"_s, "C++ module set"_s, + FileSetEntries("CXX_MODULE_SETS"_s), + FileSetEntries("INTERFACE_CXX_MODULE_SETS"_s)) + , CxxModuleHeadersFileSets( + "CXX_MODULE_HEADER_UNITS"_s, "CXX_MODULE_HEADER_UNIT_DIRS"_s, + "CXX_MODULE_HEADER_UNIT_SET"_s, "CXX_MODULE_HEADER_UNIT_DIRS_"_s, + "CXX_MODULE_HEADER_UNIT_SET_"_s, "C++ module header"_s, + "The default C++ module header set"_s, "C++ module header set"_s, + FileSetEntries("CXX_MODULE_HEADER_UNIT_SETS"_s), + FileSetEntries("INTERFACE_CXX_MODULE_HEADER_UNIT_SETS"_s)) { } @@ -542,6 +563,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("AUTORCC_OPTIONS"); initProp("LINK_DEPENDS_NO_SHARED"); initProp("LINK_INTERFACE_LIBRARIES"); + initProp("MSVC_DEBUG_INFORMATION_FORMAT"); initProp("MSVC_RUNTIME_LIBRARY"); initProp("WATCOM_RUNTIME_LIBRARY"); initProp("WIN32_EXECUTABLE"); @@ -605,12 +627,16 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, initProp("XCODE_SCHEME_THREAD_SANITIZER_STOP"); initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER"); initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP"); + initProp("XCODE_SCHEME_LAUNCH_CONFIGURATION"); + initProp("XCODE_SCHEME_ENABLE_GPU_API_VALIDATION"); + initProp("XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION"); initProp("XCODE_SCHEME_WORKING_DIRECTORY"); initProp("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"); initProp("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP"); initProp("XCODE_SCHEME_MALLOC_SCRIBBLE"); initProp("XCODE_SCHEME_MALLOC_GUARD_EDGES"); initProp("XCODE_SCHEME_GUARD_MALLOC"); + initProp("XCODE_SCHEME_LAUNCH_MODE"); initProp("XCODE_SCHEME_ZOMBIE_OBJECTS"); initProp("XCODE_SCHEME_MALLOC_STACK"); initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"); @@ -760,6 +786,10 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, } } + if (this->IsImported() || mf->GetPropertyAsBool("SYSTEM")) { + this->SetProperty("SYSTEM", "ON"); + } + for (auto const& prop : mf->GetState()->GetPropertyDefinitions().GetMap()) { if (prop.first.second == cmProperty::TARGET && !prop.second.GetInitializeFromVariable().empty()) { @@ -1223,7 +1253,8 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib, ? targetNameGenex(lib) : lib; this->AppendProperty("LINK_LIBRARIES", - this->GetDebugGeneratorExpressions(libName, llt)); + this->GetDebugGeneratorExpressions(libName, llt), + mf.GetBacktrace()); } if (cmGeneratorExpression::Find(lib) != std::string::npos || @@ -1367,11 +1398,32 @@ cmBTStringRange cmTarget::GetHeaderSetsEntries() const return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries); } +cmBTStringRange cmTarget::GetCxxModuleSetsEntries() const +{ + return cmMakeRange(this->impl->CxxModulesFileSets.SelfEntries.Entries); +} + +cmBTStringRange cmTarget::GetCxxModuleHeaderSetsEntries() const +{ + return cmMakeRange(this->impl->CxxModuleHeadersFileSets.SelfEntries.Entries); +} + cmBTStringRange cmTarget::GetInterfaceHeaderSetsEntries() const { return cmMakeRange(this->impl->HeadersFileSets.InterfaceEntries.Entries); } +cmBTStringRange cmTarget::GetInterfaceCxxModuleSetsEntries() const +{ + return cmMakeRange(this->impl->CxxModulesFileSets.InterfaceEntries.Entries); +} + +cmBTStringRange cmTarget::GetInterfaceCxxModuleHeaderSetsEntries() const +{ + return cmMakeRange( + this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries); +} + namespace { #define MAKE_PROP(PROP) const std::string prop##PROP = #PROP MAKE_PROP(C_STANDARD); @@ -1631,13 +1683,21 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value) } else if (this->impl->HeadersFileSets.WriteProperties( this, this->impl.get(), prop, value, true)) { /* Handled in the `if` condition. */ + } else if (this->impl->CxxModulesFileSets.WriteProperties( + this, this->impl.get(), prop, value, true)) { + /* Handled in the `if` condition. */ + } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties( + this, this->impl.get(), prop, value, true)) { + /* Handled in the `if` condition. */ } else { this->impl->Properties.SetProperty(prop, value); } } void cmTarget::AppendProperty(const std::string& prop, - const std::string& value, bool asString) + const std::string& value, + cm::optional<cmListFileBacktrace> const& bt, + bool asString) { if (prop == "NAME") { this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, @@ -1668,32 +1728,32 @@ void cmTarget::AppendProperty(const std::string& prop, } if (prop == "INCLUDE_DIRECTORIES") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->IncludeDirectoriesEntries.emplace_back(value, lfbt); } } else if (prop == "COMPILE_OPTIONS") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->CompileOptionsEntries.emplace_back(value, lfbt); } } else if (prop == "COMPILE_FEATURES") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->CompileFeaturesEntries.emplace_back(value, lfbt); } } else if (prop == "COMPILE_DEFINITIONS") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->CompileDefinitionsEntries.emplace_back(value, lfbt); } } else if (prop == "LINK_OPTIONS") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkOptionsEntries.emplace_back(value, lfbt); } } else if (prop == "LINK_DIRECTORIES") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkDirectoriesEntries.emplace_back(value, lfbt); } } else if (prop == "PRECOMPILE_HEADERS") { @@ -1706,32 +1766,32 @@ void cmTarget::AppendProperty(const std::string& prop, return; } if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->PrecompileHeadersEntries.emplace_back(value, lfbt); } } else if (prop == "LINK_LIBRARIES") { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkImplementationPropertyEntries.emplace_back(value, lfbt); } } else if (prop == propINTERFACE_LINK_LIBRARIES) { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkInterfacePropertyEntries.emplace_back(value, lfbt); } } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkInterfaceDirectPropertyEntries.emplace_back(value, lfbt); } } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) { if (!value.empty()) { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->LinkInterfaceDirectExcludePropertyEntries.emplace_back(value, lfbt); } } else if (prop == "SOURCES") { - cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace(); + cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt); this->impl->SourceEntries.emplace_back(value, lfbt); } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME")) { this->impl->Makefile->IssueMessage( @@ -1742,6 +1802,13 @@ void cmTarget::AppendProperty(const std::string& prop, this->impl->Makefile->IssueMessage( MessageType::FATAL_ERROR, prop + " property may not be appended."); } else if (this->impl->HeadersFileSets.WriteProperties( + this, this->impl.get(), prop, value, + false)) { // NOLINT(bugprone-branch-clone) + /* Handled in the `if` condition. */ + } else if (this->impl->CxxModulesFileSets.WriteProperties( + this, this->impl.get(), prop, value, false)) { + /* Handled in the `if` condition. */ + } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties( this, this->impl.get(), prop, value, false)) { /* Handled in the `if` condition. */ } else { @@ -2307,6 +2374,17 @@ cmValue cmTarget::GetProperty(const std::string& prop) const if (headers.first) { return headers.second; } + auto cxx_modules = this->impl->CxxModulesFileSets.ReadProperties( + this, this->impl.get(), prop); + if (cxx_modules.first) { + return cxx_modules.second; + } + auto cxx_module_headers = + this->impl->CxxModuleHeadersFileSets.ReadProperties( + this, this->impl.get(), prop); + if (cxx_module_headers.first) { + return cxx_module_headers.second; + } } cmValue retVal = this->impl->Properties.GetPropertyValue(prop); @@ -2584,6 +2662,11 @@ std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet( auto bt = this->impl->Makefile->GetBacktrace(); if (type == this->impl->HeadersFileSets.TypeName) { this->impl->HeadersFileSets.AddFileSet(name, vis, std::move(bt)); + } else if (type == this->impl->CxxModulesFileSets.TypeName) { + this->impl->CxxModulesFileSets.AddFileSet(name, vis, std::move(bt)); + } else if (type == this->impl->CxxModuleHeadersFileSets.TypeName) { + this->impl->CxxModuleHeadersFileSets.AddFileSet(name, vis, + std::move(bt)); } } return std::make_pair(&result.first->second, result.second); @@ -2594,6 +2677,12 @@ std::string cmTarget::GetFileSetsPropertyName(const std::string& type) if (type == "HEADERS") { return "HEADER_SETS"; } + if (type == "CXX_MODULES") { + return "CXX_MODULE_SETS"; + } + if (type == "CXX_MODULE_HEADER_UNITS") { + return "CXX_MODULE_HEADER_UNIT_SETS"; + } return ""; } @@ -2602,6 +2691,12 @@ std::string cmTarget::GetInterfaceFileSetsPropertyName(const std::string& type) if (type == "HEADERS") { return "INTERFACE_HEADER_SETS"; } + if (type == "CXX_MODULES") { + return "INTERFACE_CXX_MODULE_SETS"; + } + if (type == "CXX_MODULE_HEADER_UNITS") { + return "INTERFACE_CXX_MODULE_HEADER_UNIT_SETS"; + } return ""; } @@ -2629,6 +2724,8 @@ std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const }; appendEntries(this->impl->HeadersFileSets.InterfaceEntries.Entries); + appendEntries(this->impl->CxxModulesFileSets.InterfaceEntries.Entries); + appendEntries(this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries); return result; } diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 467c4da..38bd036 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -185,8 +185,10 @@ public: { this->SetProperty(prop, cmValue(value)); } - void AppendProperty(const std::string& prop, const std::string& value, - bool asString = false); + void AppendProperty( + const std::string& prop, const std::string& value, + cm::optional<cmListFileBacktrace> const& bt = cm::nullopt, + bool asString = false); //! Might return a nullptr if the property is not set or invalid cmValue GetProperty(const std::string& prop) const; //! Always returns a valid pointer @@ -281,8 +283,12 @@ public: cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const; cmBTStringRange GetHeaderSetsEntries() const; + cmBTStringRange GetCxxModuleSetsEntries() const; + cmBTStringRange GetCxxModuleHeaderSetsEntries() const; cmBTStringRange GetInterfaceHeaderSetsEntries() const; + cmBTStringRange GetInterfaceCxxModuleSetsEntries() const; + cmBTStringRange GetInterfaceCxxModuleHeaderSetsEntries() const; std::string ImportedGetFullPath(const std::string& config, cmStateEnums::ArtifactType artifact) const; diff --git a/Source/cmTargetCompileDefinitionsCommand.cxx b/Source/cmTargetCompileDefinitionsCommand.cxx index b56b245..268bfac 100644 --- a/Source/cmTargetCompileDefinitionsCommand.cxx +++ b/Source/cmTargetCompileDefinitionsCommand.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTargetCompileDefinitionsCommand.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" @@ -28,7 +29,8 @@ private: const std::vector<std::string>& content, bool /*prepend*/, bool /*system*/) override { - tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content)); + tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content), + this->Makefile->GetBacktrace()); return true; // Successfully handled. } diff --git a/Source/cmTargetExport.h b/Source/cmTargetExport.h index 885ac74..1cef888 100644 --- a/Source/cmTargetExport.h +++ b/Source/cmTargetExport.h @@ -8,6 +8,7 @@ class cmFileSet; class cmGeneratorTarget; +class cmInstallCxxModuleBmiGenerator; class cmInstallFileSetGenerator; class cmInstallFilesGenerator; class cmInstallTargetGenerator; @@ -32,6 +33,7 @@ public: cmInstallTargetGenerator* BundleGenerator; cmInstallFilesGenerator* HeaderGenerator; std::map<cmFileSet*, cmInstallFileSetGenerator*> FileSetGenerators; + cmInstallCxxModuleBmiGenerator* CxxModuleBmiGenerator; ///@} bool NamelinkOnly = false; diff --git a/Source/cmTargetIncludeDirectoriesCommand.cxx b/Source/cmTargetIncludeDirectoriesCommand.cxx index b4b4319..cb83873 100644 --- a/Source/cmTargetIncludeDirectoriesCommand.cxx +++ b/Source/cmTargetIncludeDirectoriesCommand.cxx @@ -88,7 +88,8 @@ void TargetIncludeDirectoriesImpl::HandleInterfaceContent( system); if (system) { std::string joined = this->Join(content); - tgt->AppendProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", joined); + tgt->AppendProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", joined, + this->Makefile->GetBacktrace()); } } diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx index af870da..0b123b2 100644 --- a/Source/cmTargetLinkLibrariesCommand.cxx +++ b/Source/cmTargetLinkLibrariesCommand.cxx @@ -622,7 +622,7 @@ bool TLL::HandleLibrary(ProcessingState currentProcessingState, void TLL::AppendProperty(std::string const& prop, std::string const& value) { this->AffectsProperty(prop); - this->Target->AppendProperty(prop, value); + this->Target->AppendProperty(prop, value, this->Makefile.GetBacktrace()); } void TLL::AffectsProperty(std::string const& prop) @@ -633,14 +633,16 @@ void TLL::AffectsProperty(std::string const& prop) // Add a wrapper to the expression to tell LookupLinkItem to look up // names in the caller's directory. if (this->Props.insert(prop).second) { - this->Target->AppendProperty(prop, this->DirectoryId); + this->Target->AppendProperty(prop, this->DirectoryId, + this->Makefile.GetBacktrace()); } } TLL::~TLL() { for (std::string const& prop : this->Props) { - this->Target->AppendProperty(prop, CMAKE_DIRECTORY_ID_SEP); + this->Target->AppendProperty(prop, CMAKE_DIRECTORY_ID_SEP, + this->Makefile.GetBacktrace()); } } diff --git a/Source/cmTargetPrecompileHeadersCommand.cxx b/Source/cmTargetPrecompileHeadersCommand.cxx index a5066cc..4dd158d 100644 --- a/Source/cmTargetPrecompileHeadersCommand.cxx +++ b/Source/cmTargetPrecompileHeadersCommand.cxx @@ -5,6 +5,7 @@ #include <utility> #include "cmGeneratorExpression.h" +#include "cmListFileCache.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" @@ -48,7 +49,8 @@ private: { std::string const& base = this->Makefile->GetCurrentSourceDirectory(); tgt->AppendProperty("PRECOMPILE_HEADERS", - this->Join(ConvertToAbsoluteContent(content, base))); + this->Join(ConvertToAbsoluteContent(content, base)), + this->Makefile->GetBacktrace()); return true; } diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx index b1367e1..53e25b5 100644 --- a/Source/cmTargetSourcesCommand.cxx +++ b/Source/cmTargetSourcesCommand.cxx @@ -9,6 +9,8 @@ #include <cmext/string_view> #include "cmArgumentParser.h" +#include "cmArgumentParserTypes.h" +#include "cmExperimental.h" #include "cmFileSet.h" #include "cmGeneratorExpression.h" #include "cmListFileCache.h" @@ -27,8 +29,8 @@ struct FileSetArgs { std::string Type; std::string FileSet; - std::vector<std::string> BaseDirs; - std::vector<std::string> Files; + ArgumentParser::MaybeEmpty<std::vector<std::string>> BaseDirs; + ArgumentParser::MaybeEmpty<std::vector<std::string>> Files; }; auto const FileSetArgsParser = cmArgumentParser<FileSetArgs>() @@ -77,7 +79,8 @@ private: { tgt->AppendProperty("SOURCES", this->Join(this->ConvertToAbsoluteContent( - tgt, content, IsInterface::No, CheckCMP0076::Yes))); + tgt, content, IsInterface::No, CheckCMP0076::Yes)), + this->Makefile->GetBacktrace()); return true; // Successfully handled. } @@ -196,7 +199,7 @@ std::vector<std::string> TargetSourcesImpl::ConvertToAbsoluteContent( bool TargetSourcesImpl::HandleFileSetMode( const std::string& scope, const std::vector<std::string>& content) { - auto args = FileSetsArgsParser.Parse(content); + auto args = FileSetsArgsParser.Parse(content, /*unparsedArguments=*/nullptr); for (auto& argList : args.FileSets) { argList.emplace(argList.begin(), "FILE_SET"_s); @@ -256,9 +259,31 @@ bool TargetSourcesImpl::HandleOneFileSet( this->SetError("Must specify a TYPE when creating file set"); return false; } - if (type != "HEADERS"_s) { - this->SetError("File set TYPE may only be \"HEADERS\""); - return false; + bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled( + *this->Makefile, cmExperimental::Feature::CxxModuleCMakeApi); + + if (supportCxx20FileSetTypes) { + if (type != "HEADERS"_s && type != "CXX_MODULES"_s && + type != "CXX_MODULE_HEADER_UNITS"_s) { + this->SetError( + R"(File set TYPE may only be "HEADERS", "CXX_MODULES", or "CXX_MODULE_HEADER_UNITS")"); + return false; + } + + if (cmFileSetVisibilityIsForInterface(visibility) && + !cmFileSetVisibilityIsForSelf(visibility) && + !this->Target->IsImported()) { + if (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s) { + this->SetError( + R"(File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS" may not have "INTERFACE" visibility)"); + return false; + } + } + } else { + if (type != "HEADERS"_s) { + this->SetError("File set TYPE may only be \"HEADERS\""); + return false; + } } if (args.BaseDirs.empty()) { @@ -294,17 +319,19 @@ bool TargetSourcesImpl::HandleOneFileSet( if (!baseDirectories.empty()) { fileSet.first->AddDirectoryEntry( BT<std::string>(baseDirectories, this->Makefile->GetBacktrace())); - if (type == "HEADERS"_s) { + if (type == "HEADERS"_s || type == "CXX_MODULE_HEADER_UNITS"_s) { for (auto const& dir : cmExpandedList(baseDirectories)) { auto interfaceDirectoriesGenex = cmStrCat("$<BUILD_INTERFACE:", dir, ">"); if (cmFileSetVisibilityIsForSelf(visibility)) { this->Target->AppendProperty("INCLUDE_DIRECTORIES", - interfaceDirectoriesGenex); + interfaceDirectoriesGenex, + this->Makefile->GetBacktrace()); } if (cmFileSetVisibilityIsForInterface(visibility)) { this->Target->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES", - interfaceDirectoriesGenex); + interfaceDirectoriesGenex, + this->Makefile->GetBacktrace()); } } } diff --git a/Source/cmTryCompileCommand.cxx b/Source/cmTryCompileCommand.cxx index 130c228..a2c4ce1 100644 --- a/Source/cmTryCompileCommand.cxx +++ b/Source/cmTryCompileCommand.cxx @@ -2,34 +2,69 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmTryCompileCommand.h" +#include "cmCoreTryCompile.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" +#include "cmRange.h" +#include "cmState.h" +#include "cmStateTypes.h" +#include "cmStringAlgorithms.h" +#include "cmValue.h" #include "cmake.h" -class cmExecutionStatus; - -// cmTryCompileCommand -bool cmTryCompileCommand::InitialPass(std::vector<std::string> const& argv, - cmExecutionStatus&) +bool cmTryCompileCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) { - if (argv.size() < 3) { + cmMakefile& mf = status.GetMakefile(); + + if (args.size() < 3) { + mf.IssueMessage( + MessageType::FATAL_ERROR, + "The try_compile() command requires at least 3 arguments."); return false; } - if (this->Makefile->GetCMakeInstance()->GetWorkingMode() == - cmake::FIND_PACKAGE_MODE) { - this->Makefile->IssueMessage( + if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { + mf.IssueMessage( MessageType::FATAL_ERROR, "The try_compile() command is not supported in --find-package mode."); return false; } - this->TryCompileCode(argv, false); + cmStateEnums::TargetType targetType = cmStateEnums::EXECUTABLE; + cmValue tt = mf.GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE"); + if (cmNonempty(tt)) { + if (*tt == cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE)) { + targetType = cmStateEnums::EXECUTABLE; + } else if (*tt == + cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY)) { + targetType = cmStateEnums::STATIC_LIBRARY; + } else { + mf.IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("Invalid value '", *tt, + "' for CMAKE_TRY_COMPILE_TARGET_TYPE. Only '", + cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE), + "' and '", + cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY), + "' are allowed.")); + return false; + } + } + + cmCoreTryCompile tc(&mf); + cmCoreTryCompile::Arguments arguments = + tc.ParseArgs(cmMakeRange(args), false); + if (!arguments) { + return true; + } + tc.TryCompileCode(arguments, targetType); // if They specified clean then we clean up what we can - if (this->SrcFileSignature) { - if (!this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) { - this->CleanupFiles(this->BinaryDirectory); + if (tc.SrcFileSignature) { + if (!mf.GetCMakeInstance()->GetDebugTryCompile()) { + tc.CleanupFiles(tc.BinaryDirectory); } } return true; diff --git a/Source/cmTryCompileCommand.h b/Source/cmTryCompileCommand.h index d8cc16e..6a3430b 100644 --- a/Source/cmTryCompileCommand.h +++ b/Source/cmTryCompileCommand.h @@ -7,33 +7,7 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmCoreTryCompile.h" - class cmExecutionStatus; -/** \class cmTryCompileCommand - * \brief Specifies where to install some files - * - * cmTryCompileCommand is used to test if source code can be compiled - */ -class cmTryCompileCommand : public cmCoreTryCompile -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTryCompileCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; -}; +bool cmTryCompileCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx index c82ac64..1e81195 100644 --- a/Source/cmTryRunCommand.cxx +++ b/Source/cmTryRunCommand.cxx @@ -4,9 +4,14 @@ #include <cstdio> +#include <cm/optional> + #include "cmsys/FStream.hxx" +#include "cmArgumentParserTypes.h" +#include "cmCoreTryCompile.h" #include "cmDuration.h" +#include "cmExecutionStatus.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmRange.h" @@ -17,157 +22,157 @@ #include "cmValue.h" #include "cmake.h" -class cmExecutionStatus; +namespace { -// cmTryRunCommand -bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, - cmExecutionStatus&) +class TryRunCommandImpl : public cmCoreTryCompile { - if (argv.size() < 4) { - return false; - } - - if (this->Makefile->GetCMakeInstance()->GetWorkingMode() == - cmake::FIND_PACKAGE_MODE) { - this->Makefile->IssueMessage( - MessageType::FATAL_ERROR, - "The try_run() command is not supported in --find-package mode."); - return false; +public: + TryRunCommandImpl(cmMakefile* mf) + : cmCoreTryCompile(mf) + { } - // build an arg list for TryCompile and extract the runArgs, - std::vector<std::string> tryCompile; - - this->CompileResultVariable.clear(); - this->RunResultVariable.clear(); - this->OutputVariable.clear(); - this->RunOutputVariable.clear(); - this->CompileOutputVariable.clear(); - - std::string runArgs; - unsigned int i; - for (i = 1; i < argv.size(); ++i) { - if (argv[i] == "ARGS") { - ++i; - while (i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" && - argv[i] != "CMAKE_FLAGS" && argv[i] != "LINK_OPTIONS" && - argv[i] != "LINK_LIBRARIES") { - runArgs += " "; - runArgs += argv[i]; - ++i; - } - if (i < argv.size()) { - tryCompile.push_back(argv[i]); - } - } else { - if (argv[i] == "OUTPUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "OUTPUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->OutputVariable = argv[i]; - } else if (argv[i] == "RUN_OUTPUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "RUN_OUTPUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->RunOutputVariable = argv[i]; - } else if (argv[i] == "COMPILE_OUTPUT_VARIABLE") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "COMPILE_OUTPUT_VARIABLE specified but there is no variable"); - return false; - } - i++; - this->CompileOutputVariable = argv[i]; - } else if (argv[i] == "WORKING_DIRECTORY") { - if (argv.size() <= (i + 1)) { - cmSystemTools::Error( - "WORKING_DIRECTORY specified but there is no variable"); - return false; - } - i++; - this->WorkingDirectory = argv[i]; - } else { - tryCompile.push_back(argv[i]); - } - } + bool TryRunCode(std::vector<std::string> const& args); + + void RunExecutable(const std::string& runArgs, + cm::optional<std::string> const& workDir, + std::string* runOutputContents, + std::string* runOutputStdOutContents, + std::string* runOutputStdErrContents); + void DoNotRunExecutable(const std::string& runArgs, + const std::string& srcFile, + std::string const& compileResultVariable, + std::string* runOutputContents, + std::string* runOutputStdOutContents, + std::string* runOutputStdErrContents); + + bool NoCache; + std::string RunResultVariable; +}; + +bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) +{ + this->RunResultVariable = argv[0]; + cmCoreTryCompile::Arguments arguments = + this->ParseArgs(cmMakeRange(argv).advance(1), true); + if (!arguments) { + return true; } + this->NoCache = arguments.NoCache; // although they could be used together, don't allow it, because // using OUTPUT_VARIABLE makes crosscompiling harder - if (!this->OutputVariable.empty() && - (!this->RunOutputVariable.empty() || - !this->CompileOutputVariable.empty())) { + if (arguments.OutputVariable && + (arguments.CompileOutputVariable || arguments.RunOutputVariable || + arguments.RunOutputStdOutVariable || + arguments.RunOutputStdErrVariable)) { cmSystemTools::Error( "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE " - "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or " - "RUN_OUTPUT_VARIABLE."); + ", RUN_OUTPUT_VARIABLE, RUN_OUTPUT_STDOUT_VARIABLE or " + "RUN_OUTPUT_STDERR_VARIABLE. " + "Please use only COMPILE_OUTPUT_VARIABLE, RUN_OUTPUT_VARIABLE, " + "RUN_OUTPUT_STDOUT_VARIABLE " + "and/or RUN_OUTPUT_STDERR_VARIABLE."); + return false; + } + + if ((arguments.RunOutputStdOutVariable || + arguments.RunOutputStdErrVariable) && + arguments.RunOutputVariable) { + cmSystemTools::Error( + "You cannot use RUN_OUTPUT_STDOUT_VARIABLE or " + "RUN_OUTPUT_STDERR_VARIABLE together " + "with RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE or " + "RUN_OUTPUT_STDOUT_VARIABLE and/or RUN_OUTPUT_STDERR_VARIABLE."); return false; } - if (!this->WorkingDirectory.empty()) { - if (!cmSystemTools::MakeDirectory(this->WorkingDirectory)) { + if (arguments.RunWorkingDirectory) { + if (!cmSystemTools::MakeDirectory(*arguments.RunWorkingDirectory)) { cmSystemTools::Error(cmStrCat("Error creating working directory \"", - this->WorkingDirectory, "\".")); + *arguments.RunWorkingDirectory, "\".")); return false; } } bool captureRunOutput = false; - if (!this->OutputVariable.empty()) { + bool captureRunOutputStdOutErr = false; + if (arguments.OutputVariable) { captureRunOutput = true; - tryCompile.emplace_back("OUTPUT_VARIABLE"); - tryCompile.push_back(this->OutputVariable); + } else if (arguments.CompileOutputVariable) { + arguments.OutputVariable = arguments.CompileOutputVariable; } - if (!this->CompileOutputVariable.empty()) { - tryCompile.emplace_back("OUTPUT_VARIABLE"); - tryCompile.push_back(this->CompileOutputVariable); - } - if (!this->RunOutputVariable.empty()) { + if (arguments.RunOutputStdOutVariable || arguments.RunOutputStdErrVariable) { + captureRunOutputStdOutErr = true; + } else if (arguments.RunOutputVariable) { captureRunOutput = true; } - this->RunResultVariable = argv[0]; - this->CompileResultVariable = argv[1]; - // do the try compile - int res = this->TryCompileCode(tryCompile, true); + bool compiled = this->TryCompileCode(arguments, cmStateEnums::EXECUTABLE); // now try running the command if it compiled - if (!res) { + if (compiled) { if (this->OutputFile.empty()) { cmSystemTools::Error(this->FindErrorMessage); } else { + std::string runArgs; + if (arguments.RunArgs) { + runArgs = cmStrCat(" ", cmJoin(*arguments.RunArgs, " ")); + } + // "run" it and capture the output std::string runOutputContents; + std::string runOutputStdOutContents; + std::string runOutputStdErrContents; if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") && !this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR")) { this->DoNotRunExecutable( - runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr); + runArgs, *arguments.SourceDirectoryOrFile, + *arguments.CompileResultVariable, + captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable + ? &runOutputStdOutContents + : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable + ? &runOutputStdErrContents + : nullptr); } else { - this->RunExecutable(runArgs, &runOutputContents); + this->RunExecutable( + runArgs, arguments.RunWorkingDirectory, + captureRunOutput ? &runOutputContents : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable + ? &runOutputStdOutContents + : nullptr, + captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable + ? &runOutputStdErrContents + : nullptr); } // now put the output into the variables - if (!this->RunOutputVariable.empty()) { - this->Makefile->AddDefinition(this->RunOutputVariable, + if (arguments.RunOutputVariable) { + this->Makefile->AddDefinition(*arguments.RunOutputVariable, runOutputContents); } + if (arguments.RunOutputStdOutVariable) { + this->Makefile->AddDefinition(*arguments.RunOutputStdOutVariable, + runOutputStdOutContents); + } + if (arguments.RunOutputStdErrVariable) { + this->Makefile->AddDefinition(*arguments.RunOutputStdErrVariable, + runOutputStdErrContents); + } - if (!this->OutputVariable.empty()) { + if (arguments.OutputVariable && !arguments.CompileOutputVariable) { // if the TryCompileCore saved output in this outputVariable then // prepend that output to this output cmValue compileOutput = - this->Makefile->GetDefinition(this->OutputVariable); + this->Makefile->GetDefinition(*arguments.OutputVariable); if (compileOutput) { runOutputContents = *compileOutput + runOutputContents; } - this->Makefile->AddDefinition(this->OutputVariable, runOutputContents); + this->Makefile->AddDefinition(*arguments.OutputVariable, + runOutputContents); } } } @@ -179,8 +184,10 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv, return true; } -void cmTryRunCommand::RunExecutable(const std::string& runArgs, - std::string* out) +void TryRunCommandImpl::RunExecutable(const std::string& runArgs, + cm::optional<std::string> const& workDir, + std::string* out, std::string* stdOut, + std::string* stdErr) { int retVal = -1; @@ -204,9 +211,10 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs, finalCommand += runArgs; } bool worked = cmSystemTools::RunSingleCommand( - finalCommand, out, out, &retVal, - this->WorkingDirectory.empty() ? nullptr : this->WorkingDirectory.c_str(), - cmSystemTools::OUTPUT_NONE, cmDuration::zero()); + finalCommand, stdOut || stdErr ? stdOut : out, + stdOut || stdErr ? stdErr : out, &retVal, + workDir ? workDir->c_str() : nullptr, cmSystemTools::OUTPUT_NONE, + cmDuration::zero()); // set the run var char retChar[16]; const char* retStr; @@ -216,18 +224,23 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs, } else { retStr = "FAILED_TO_RUN"; } - this->Makefile->AddCacheDefinition(this->RunResultVariable, retStr, - "Result of try_run()", - cmStateEnums::INTERNAL); + if (this->NoCache) { + this->Makefile->AddDefinition(this->RunResultVariable, retStr); + } else { + this->Makefile->AddCacheDefinition(this->RunResultVariable, retStr, + "Result of try_run()", + cmStateEnums::INTERNAL); + } } /* This is only used when cross compiling. Instead of running the executable, two cache variables are created which will hold the results the executable would have produced. */ -void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, - const std::string& srcFile, - std::string* out) +void TryRunCommandImpl::DoNotRunExecutable( + const std::string& runArgs, const std::string& srcFile, + std::string const& compileResultVariable, std::string* out, + std::string* stdOut, std::string* stdErr) { // copy the executable out of the CMakeFiles/ directory, so it is not // removed at the end of try_run() and the user can run it manually @@ -246,6 +259,10 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, std::string internalRunOutputName = this->RunResultVariable + "__TRYRUN_OUTPUT"; + std::string internalRunOutputStdOutName = + this->RunResultVariable + "__TRYRUN_OUTPUT_STDOUT"; + std::string internalRunOutputStdErrName = + this->RunResultVariable + "__TRYRUN_OUTPUT_STDERR"; bool error = false; if (!this->Makefile->GetDefinition(this->RunResultVariable)) { @@ -269,7 +286,51 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, } // is the output from the executable used ? - if (out) { + if (stdOut || stdErr) { + if (!this->Makefile->GetDefinition(internalRunOutputStdOutName)) { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment = cmStrCat( + "Output of try_run(), contains the text, which the executable " + "would have printed on stdout on its target platform.\n", + detailsString); + + this->Makefile->AddCacheDefinition( + internalRunOutputStdOutName, "PLEASE_FILL_OUT-NOTFOUND", + comment.c_str(), cmStateEnums::STRING); + cmState* state = this->Makefile->GetState(); + cmValue existing = + state->GetCacheEntryValue(internalRunOutputStdOutName); + if (existing) { + state->SetCacheEntryProperty(internalRunOutputStdOutName, "ADVANCED", + "1"); + } + + error = true; + } + + if (!this->Makefile->GetDefinition(internalRunOutputStdErrName)) { + // if the variables doesn't exist, create it with a helpful error text + // and mark it as advanced + std::string comment = cmStrCat( + "Output of try_run(), contains the text, which the executable " + "would have printed on stderr on its target platform.\n", + detailsString); + + this->Makefile->AddCacheDefinition( + internalRunOutputStdErrName, "PLEASE_FILL_OUT-NOTFOUND", + comment.c_str(), cmStateEnums::STRING); + cmState* state = this->Makefile->GetState(); + cmValue existing = + state->GetCacheEntryValue(internalRunOutputStdErrName); + if (existing) { + state->SetCacheEntryProperty(internalRunOutputStdErrName, "ADVANCED", + "1"); + } + + error = true; + } + } else if (out) { if (!this->Makefile->GetDefinition(internalRunOutputName)) { // if the variables doesn't exist, create it with a helpful error text // and mark it as advanced @@ -317,7 +378,34 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, " to\n" " the exit code (in many cases 0 for success), otherwise " "enter \"FAILED_TO_RUN\".\n"); - if (out) { + if (stdOut || stdErr) { + if (stdOut) { + comment += internalRunOutputStdOutName; + comment += + "\n contains the text the executable " + "would have printed on stdout.\n" + " If the executable would not have been able to run, set "; + comment += internalRunOutputStdOutName; + comment += " empty.\n" + " Otherwise check if the output is evaluated by the " + "calling CMake code. If so,\n" + " check what the source file would have printed when " + "called with the given arguments.\n"; + } + if (stdErr) { + comment += internalRunOutputStdErrName; + comment += + "\n contains the text the executable " + "would have printed on stderr.\n" + " If the executable would not have been able to run, set "; + comment += internalRunOutputStdErrName; + comment += " empty.\n" + " Otherwise check if the output is evaluated by the " + "calling CMake code. If so,\n" + " check what the source file would have printed when " + "called with the given arguments.\n"; + } + } else if (out) { comment += internalRunOutputName; comment += "\n contains the text the executable " @@ -330,8 +418,9 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, " check what the source file would have printed when " "called with the given arguments.\n"; } + comment += "The "; - comment += this->CompileResultVariable; + comment += compileResultVariable; comment += " variable holds the build result for this try_run().\n\n" "Source file : "; comment += srcFile + "\n"; @@ -370,7 +459,37 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs, return; } - if (out) { + if (stdOut || stdErr) { + if (stdOut) { + (*stdOut) = *this->Makefile->GetDefinition(internalRunOutputStdOutName); + } + if (stdErr) { + (*stdErr) = *this->Makefile->GetDefinition(internalRunOutputStdErrName); + } + } else if (out) { (*out) = *this->Makefile->GetDefinition(internalRunOutputName); } } +} + +bool cmTryRunCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + cmMakefile& mf = status.GetMakefile(); + + if (args.size() < 4) { + mf.IssueMessage(MessageType::FATAL_ERROR, + "The try_run() command requires at least 4 arguments."); + return false; + } + + if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) { + mf.IssueMessage( + MessageType::FATAL_ERROR, + "The try_run() command is not supported in --find-package mode."); + return false; + } + + TryRunCommandImpl tr(&mf); + return tr.TryRunCode(args); +} diff --git a/Source/cmTryRunCommand.h b/Source/cmTryRunCommand.h index d45acd8..38e3638 100644 --- a/Source/cmTryRunCommand.h +++ b/Source/cmTryRunCommand.h @@ -7,47 +7,7 @@ #include <string> #include <vector> -#include <cm/memory> - -#include "cmCommand.h" -#include "cmCoreTryCompile.h" - class cmExecutionStatus; -/** \class cmTryRunCommand - * \brief Specifies where to install some files - * - * cmTryRunCommand is used to test if source code can be compiled - */ -class cmTryRunCommand : public cmCoreTryCompile -{ -public: - /** - * This is a virtual constructor for the command. - */ - std::unique_ptr<cmCommand> Clone() override - { - return cm::make_unique<cmTryRunCommand>(); - } - - /** - * This is called when the command is first encountered in - * the CMakeLists.txt file. - */ - bool InitialPass(std::vector<std::string> const& args, - cmExecutionStatus& status) override; - -private: - void RunExecutable(const std::string& runArgs, - std::string* runOutputContents); - void DoNotRunExecutable(const std::string& runArgs, - const std::string& srcFile, - std::string* runOutputContents); - - std::string CompileResultVariable; - std::string RunResultVariable; - std::string OutputVariable; - std::string RunOutputVariable; - std::string CompileOutputVariable; - std::string WorkingDirectory; -}; +bool cmTryRunCommand(std::vector<std::string> const& args, + cmExecutionStatus& status); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9f3d620..8882c45 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -354,6 +354,18 @@ std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString( void cmVisualStudio10TargetGenerator::Generate() { + for (std::string const& config : this->Configurations) { + this->GeneratorTarget->CheckCxxModuleStatus(config); + } + + if (this->GeneratorTarget->HaveCxx20ModuleSources()) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + cmStrCat("The \"", this->GeneratorTarget->GetName(), + "\" target contains C++ module sources which are not supported " + "by the generator")); + } + this->ProjectType = computeProjectType(this->GeneratorTarget); this->Managed = this->ProjectType == VsProjectType::csproj; const std::string ProjectFileExtension = @@ -483,7 +495,7 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( e1.Element("PreferredToolArchitecture", hostArch); } - // ALL_BUILD and ZERO_CHECK projects transitively include + // The ALL_BUILD, PACKAGE, and ZERO_CHECK projects transitively include // Microsoft.Common.CurrentVersion.targets which triggers Target // ResolveNugetPackageAssets when SDK-style targets are in the project. // However, these projects have no nuget packages to reference and the @@ -491,7 +503,7 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile( // Setting ResolveNugetPackages to false skips this target and the build // succeeds. cm::string_view targetName{ this->GeneratorTarget->GetName() }; - if (targetName == "ALL_BUILD" || + if (targetName == "ALL_BUILD" || targetName == "PACKAGE" || targetName == CMAKE_CHECK_BUILD_SYSTEM_TARGET) { Elem e1(e0, "PropertyGroup"); e1.Element("ResolveNugetPackages", "false"); @@ -899,9 +911,11 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( e1.Element("TargetFrameworks", *targetFramework); } else { e1.Element("TargetFramework", *targetFramework); + e1.Element("AppendTargetFrameworkToOutputPath", "false"); } } else { e1.Element("TargetFramework", "net5.0"); + e1.Element("AppendTargetFrameworkToOutputPath", "false"); } std::string outputType; @@ -957,6 +971,10 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile( std::string outDir = this->GeneratorTarget->GetDirectory(config) + "/"; ConvertToWindowsSlash(outDir); e1.Element("OutputPath", outDir); + + Options& o = *(this->ClOptions[config]); + OptionsHelper oh(o, e1); + oh.OutputFlagMap(); } this->WriteDotNetDocumentationFile(e0); @@ -1507,6 +1525,10 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues( this->ASanEnabledConfigurations.end()) { e1.Element("EnableAsan", "true"); } + if (this->FuzzerEnabledConfigurations.find(config) != + this->FuzzerEnabledConfigurations.end()) { + e1.Element("EnableFuzzer", "true"); + } { auto s = this->SpectreMitigation.find(config); if (s != this->SpectreMitigation.end()) { @@ -1788,11 +1810,8 @@ void cmVisualStudio10TargetGenerator::WriteCustomRuleCpp( e2.WritePlatformConfigTag("Command", cond, script); e2.WritePlatformConfigTag("AdditionalInputs", cond, additional_inputs); e2.WritePlatformConfigTag("Outputs", cond, outputs); - if (this->LocalGenerator->GetVersion() > - cmGlobalVisualStudioGenerator::VSVersion::VS10) { - // VS >= 11 let us turn off linking of custom command outputs. - e2.WritePlatformConfigTag("LinkObjects", cond, "false"); - } + // Turn off linking of custom command outputs. + e2.WritePlatformConfigTag("LinkObjects", cond, "false"); if (symbolic && this->LocalGenerator->GetVersion() >= cmGlobalVisualStudioGenerator::VSVersion::VS16) { @@ -2357,28 +2376,6 @@ void cmVisualStudio10TargetGenerator::WriteSource(Elem& e2, // we must use relative paths. bool forceRelative = sf->GetLanguage() == "CUDA"; std::string sourceFile = this->ConvertPath(sf->GetFullPath(), forceRelative); - if (this->LocalGenerator->GetVersion() == - cmGlobalVisualStudioGenerator::VSVersion::VS10 && - cmSystemTools::FileIsFullPath(sourceFile)) { - // Normal path conversion resulted in a full path. VS 10 (but not 11) - // refuses to show the property page in the IDE for a source file with a - // full path (not starting in a '.' or '/' AFAICT). CMake <= 2.8.4 used a - // relative path but to allow deeper build trees CMake 2.8.[5678] used a - // full path except for custom commands. Custom commands do not work - // without a relative path, but they do not seem to be involved in tools - // with the above behavior. For other sources we now use a relative path - // when the combined path will not be too long so property pages appear. - std::string sourceRel = this->ConvertPath(sf->GetFullPath(), true); - size_t const maxLen = 250; - if (sf->GetCustomCommand() || - ((this->LocalGenerator->GetCurrentBinaryDirectory().length() + 1 + - sourceRel.length()) <= maxLen)) { - forceRelative = true; - sourceFile = sourceRel; - } else { - this->GlobalGenerator->PathTooLong(this->GeneratorTarget, sf, sourceRel); - } - } ConvertToWindowsSlash(sourceFile); e2.Attribute("Include", sourceFile); @@ -2875,7 +2872,7 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions( Elem& e0) { cmStateEnums::TargetType ttype = this->GeneratorTarget->GetType(); - if (ttype > cmStateEnums::GLOBAL_TARGET) { + if (ttype > cmStateEnums::INTERFACE_LIBRARY) { return; } if (this->ProjectType == VsProjectType::csproj) { @@ -3117,6 +3114,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( this->LangForClCompile = langForClCompile; if (!langForClCompile.empty()) { this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, + cmBuildStep::Compile, langForClCompile, configName); this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget, langForClCompile, configName); @@ -3128,10 +3126,17 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } // Check if ASan is enabled. - if (flags.find("/fsanitize=address") != std::string::npos) { + if (flags.find("/fsanitize=address") != std::string::npos || + flags.find("-fsanitize=address") != std::string::npos) { this->ASanEnabledConfigurations.insert(configName); } + // Check if (lib)Fuzzer is enabled. + if (flags.find("/fsanitize=fuzzer") != std::string::npos || + flags.find("-fsanitize=fuzzer") != std::string::npos) { + this->FuzzerEnabledConfigurations.insert(configName); + } + // Precompile Headers std::string pchHeader = this->GeneratorTarget->GetPchHeader(configName, linkLanguage); @@ -3173,7 +3178,9 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( // anymore, because cmGeneratorTarget may not be aware that the // target uses C++/CLI. if (flags.find("/clr") != std::string::npos || - defineFlags.find("/clr") != std::string::npos) { + flags.find("-clr") != std::string::npos || + defineFlags.find("/clr") != std::string::npos || + defineFlags.find("-clr") != std::string::npos) { if (configName == this->Configurations[0]) { std::string message = "For the target \"" + this->GeneratorTarget->GetName() + @@ -3492,8 +3499,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( // Get compile flags for CUDA in this directory. std::string flags; - this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, "CUDA", - configName); + this->LocalGenerator->AddLanguageFlags( + flags, this->GeneratorTarget, cmBuildStep::Compile, "CUDA", configName); this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget, "CUDA", configName); @@ -3685,21 +3692,28 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions( this->GeneratorTarget->GetLinkOptions(linkOpts, configName, "CUDA"); // LINK_OPTIONS are escaped. this->LocalGenerator->AppendCompileOptions(linkFlags, linkOpts); + + cmComputeLinkInformation* pcli = + this->GeneratorTarget->GetLinkInformation(configName); + if (doDeviceLinking && pcli) { + + cmLinkLineDeviceComputer computer( + this->LocalGenerator, + this->LocalGenerator->GetStateSnapshot().GetDirectory()); + std::string ignored_; + this->LocalGenerator->GetDeviceLinkFlags(computer, configName, ignored_, + linkFlags, ignored_, ignored_, + this->GeneratorTarget); + + this->LocalGenerator->AddLanguageFlagsForLinking( + linkFlags, this->GeneratorTarget, "CUDA", configName); + } cudaLinkOptions.AppendFlagString("AdditionalOptions", linkFlags); // For static libraries that have device linking enabled compute // the libraries if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY && doDeviceLinking) { - cmComputeLinkInformation* pcli = - this->GeneratorTarget->GetLinkInformation(configName); - if (!pcli) { - cmSystemTools::Error( - "CMake can not compute cmComputeLinkInformation for target: " + - this->Name); - return false; - } - cmComputeLinkInformation& cli = *pcli; cmLinkLineDeviceComputer computer( this->LocalGenerator, @@ -3755,9 +3769,14 @@ bool cmVisualStudio10TargetGenerator::ComputeMasmOptions( this->LocalGenerator, Options::MasmCompiler, gg->GetMasmFlagTable()); Options& masmOptions = *pOptions; + // MSBuild enables debug information by default. + // Disable it explicitly unless a flag parsed below re-enables it. + masmOptions.AddFlag("GenerateDebugInformation", "false"); + std::string flags; this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, - "ASM_MASM", configName); + cmBuildStep::Compile, "ASM_MASM", + configName); masmOptions.Parse(flags); @@ -3809,7 +3828,8 @@ bool cmVisualStudio10TargetGenerator::ComputeNasmOptions( std::string flags; this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, - "ASM_NASM", configName); + cmBuildStep::Compile, "ASM_NASM", + configName); flags += " -f"; flags += this->Makefile->GetSafeDefinition("CMAKE_ASM_NASM_OBJECT_FORMAT"); nasmOptions.Parse(flags); diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 8d777a3..17dcecd 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -231,6 +231,7 @@ private: bool TargetCompileAsWinRT; std::set<std::string> IPOEnabledConfigurations; std::set<std::string> ASanEnabledConfigurations; + std::set<std::string> FuzzerEnabledConfigurations; std::map<std::string, std::string> SpectreMitigation; cmGlobalVisualStudio10Generator* const GlobalGenerator; cmLocalVisualStudio10Generator* const LocalGenerator; diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 00c65ed..e6f5ece 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -75,7 +75,6 @@ void cmVisualStudioGeneratorOptions::FixExceptionHandlingDefault() // the flag to disable exception handling. When the user does // remove the flag we need to override the IDE default of on. switch (this->Version) { - case cmGlobalVisualStudioGenerator::VSVersion::VS10: case cmGlobalVisualStudioGenerator::VSVersion::VS11: case cmGlobalVisualStudioGenerator::VSVersion::VS12: case cmGlobalVisualStudioGenerator::VSVersion::VS14: @@ -101,14 +100,12 @@ void cmVisualStudioGeneratorOptions::SetVerboseMakefile(bool verbose) // to the generated project to disable logo suppression. Otherwise // the GUI default is to enable suppression. // - // On Visual Studio 10 (and later!), the value of this attribute should be - // an empty string, instead of "FALSE", in order to avoid a warning: - // "cl ... warning D9035: option 'nologo-' has been deprecated" - // + // On Visual Studio 9, the value of this attribute should be + // "FALSE", instead of an empty string. if (verbose && this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) { this->FlagMap["SuppressStartupBanner"] = - this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10 ? "FALSE" + this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9 ? "FALSE" : ""; } } @@ -161,71 +158,12 @@ bool cmVisualStudioGeneratorOptions::UsingSBCS() const void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration() { - // Extract temporary values stored by our flag table. - FlagValue arch = this->TakeFlag("cmake-temp-arch"); - FlagValue code = this->TakeFlag("cmake-temp-code"); - FlagValue gencode = this->TakeFlag("cmake-temp-gencode"); - - // No -code allowed without -arch. - if (arch.empty()) { - code.clear(); - } - - // Create a CodeGeneration field with [arch],[code] syntax in each entry. - // CUDA will convert it to `-gencode=arch=[arch],code="[code],[arch]"`. - FlagValue& result = this->FlagMap["CodeGeneration"]; - - // If there are no flags, leave the CodeGeneration field empty. - if (arch.empty() && gencode.empty()) { - return; - } - - // First entries for the -arch=<arch> [-code=<code>,...] pair. - if (!arch.empty()) { - std::string arch_name = arch[0]; - if (arch_name == "all" || arch_name == "all-major" || - arch_name == "native") { - AppendFlagString("AdditionalOptions", "-arch=" + arch_name); - return; - } - std::vector<std::string> codes; - if (!code.empty()) { - codes = cmTokenize(code[0], ","); - } - if (codes.empty()) { - codes.push_back(arch_name); - // nvcc -arch=<arch> has a special case that allows a real - // architecture to be specified instead of a virtual arch. - // It translates to -arch=<virtual> -code=<real>. - cmSystemTools::ReplaceString(arch_name, "sm_", "compute_"); - } - for (std::string const& c : codes) { - std::string entry = arch_name + "," + c; - result.push_back(entry); - } - } - - // Now add entries for the following signatures: - // -gencode=<arch>,<code> - // -gencode=<arch>,[<code1>,<code2>] - // -gencode=<arch>,"<code1>,<code2>" - for (std::string const& e : gencode) { - std::string entry = e; - cmSystemTools::ReplaceString(entry, "arch=", ""); - cmSystemTools::ReplaceString(entry, "code=", ""); - cmSystemTools::ReplaceString(entry, "[", ""); - cmSystemTools::ReplaceString(entry, "]", ""); - cmSystemTools::ReplaceString(entry, "\"", ""); - - std::vector<std::string> codes = cmTokenize(entry, ","); - if (codes.size() >= 2) { - auto gencode_arch = cm::cbegin(codes); - for (auto ci = gencode_arch + 1; ci != cm::cend(codes); ++ci) { - std::string code_entry = *gencode_arch + "," + *ci; - result.push_back(code_entry); - } - } - } + // Create an empty CodeGeneration field, and pass the the actual + // compile flags via additional options so that we have consistent + // behavior and avoid issues with MSBuild extensions injecting + // virtual code when we request real only. + FlagValue& code_gen_flag = this->FlagMap["CodeGeneration"]; + code_gen_flag = ""; } void cmVisualStudioGeneratorOptions::FixManifestUACFlags() @@ -432,7 +370,7 @@ void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions( } std::ostringstream oss; - if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { + if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { oss << "%(" << tag << ")"; } std::vector<std::string>::const_iterator de = @@ -440,13 +378,13 @@ void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions( for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) { // Escape the definition for the compiler. std::string define; - if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10) { + if (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9) { define = this->LocalGenerator->EscapeForShell(di, true); } else { define = di; } // Escape this flag for the MSBuild. - if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { + if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { cmVS10EscapeForMSBuild(define); if (lang == "RC") { cmSystemTools::ReplaceString(define, "\"", "\\\""); @@ -488,7 +426,7 @@ void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories( } // Escape this include for the MSBuild. - if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { + if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { cmVS10EscapeForMSBuild(include); } oss << sep << include; @@ -500,7 +438,7 @@ void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories( } } - if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { + if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { oss << sep << "%(" << tag << ")"; } @@ -514,7 +452,7 @@ void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout, std::ostringstream oss; const char* sep = ""; for (std::string i : m.second) { - if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) { + if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) { cmVS10EscapeForMSBuild(i); } oss << sep << i; diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx index fb94273..e80d1fc 100644 --- a/Source/cmWhileCommand.cxx +++ b/Source/cmWhileCommand.cxx @@ -96,7 +96,7 @@ bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions, cmExecutionStatus status(mf); mf.ExecuteCommand(fn, status); if (status.GetReturnInvoked()) { - inStatus.SetReturnInvoked(); + inStatus.SetReturnInvoked(status.GetReturnVariables()); return true; } if (status.GetBreakInvoked()) { diff --git a/Source/cmWindowsRegistry.h b/Source/cmWindowsRegistry.h index 2eed297..f4a0e7b 100644 --- a/Source/cmWindowsRegistry.h +++ b/Source/cmWindowsRegistry.h @@ -2,6 +2,8 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #pragma once +#include "cmConfigure.h" // IWYU pragma: keep + #include <cstdint> // IWYU pragma: keep #include <string> #include <vector> diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx index adc500a..e727d22 100644 --- a/Source/cmXCodeScheme.cxx +++ b/Source/cmXCodeScheme.cxx @@ -67,9 +67,14 @@ void cmXCodeScheme::WriteXCodeXCScheme(std::ostream& fout, xout.Attribute("LastUpgradeVersion", WriteVersionString()); xout.Attribute("version", "1.3"); + cmValue propDftCfg = + Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_CONFIGURATION"); + std::string launchConfiguration = + !propDftCfg.IsEmpty() ? *propDftCfg : "Debug"; + WriteBuildAction(xout, container); WriteTestAction(xout, FindConfiguration("Debug"), container); - WriteLaunchAction(xout, FindConfiguration("Debug"), container); + WriteLaunchAction(xout, FindConfiguration(launchConfiguration), container); WriteProfileAction(xout, FindConfiguration("Release")); WriteAnalyzeAction(xout, FindConfiguration("Debug")); WriteArchiveAction(xout, FindConfiguration("Release")); @@ -147,7 +152,15 @@ void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout, "Xcode.DebuggerFoundation.Debugger.LLDB"); xout.Attribute("selectedLauncherIdentifier", "Xcode.DebuggerFoundation.Launcher.LLDB"); - xout.Attribute("launchStyle", "0"); + { + cmValue launchMode = + this->Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_MODE"); + std::string value = "0"; // == 'AUTO' + if (launchMode && *launchMode == "WAIT") { + value = "1"; + } + xout.Attribute("launchStyle", value); + } WriteCustomWorkingDirectory(xout, configuration); xout.Attribute("ignoresPersistentStateOnLaunch", "NO"); @@ -190,6 +203,23 @@ void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout, WriteLaunchActionAttribute(xout, "enableUBSanitizer", "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER"); + + if (cmValue value = this->Target->GetTarget()->GetProperty( + "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION")) { + if (value.IsOff()) { + xout.Attribute("enableGPUValidationMode", + "1"); // unset means YES, "1" means NO + } + } + + if (cmValue value = this->Target->GetTarget()->GetProperty( + "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION")) { + if (value.IsOn()) { + xout.Attribute("enableGPUShaderValidationMode", + "2"); // unset means NO, "2" means YES + } + } + WriteLaunchActionAttribute( xout, "stopOnEveryUBSanitizerIssue", "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP"); diff --git a/Source/cm_codecvt.cxx b/Source/cm_codecvt.cxx index 8115306..2d2a377 100644 --- a/Source/cm_codecvt.cxx +++ b/Source/cm_codecvt.cxx @@ -19,6 +19,12 @@ codecvt::codecvt(Encoding e) #endif { switch (e) { + case codecvt::ConsoleOutput: +#if defined(_WIN32) + m_noconv = false; + m_codepage = GetConsoleOutputCP(); + break; +#endif case codecvt::ANSI: #if defined(_WIN32) m_noconv = false; diff --git a/Source/cm_codecvt.hxx b/Source/cm_codecvt.hxx index 9af083f..f628de7 100644 --- a/Source/cm_codecvt.hxx +++ b/Source/cm_codecvt.hxx @@ -15,7 +15,8 @@ public: None, UTF8, UTF8_WITH_BOM, - ANSI + ANSI, + ConsoleOutput, }; #ifndef CMAKE_BOOTSTRAP diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 1c1cab3..013a87b 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -3,6 +3,7 @@ #include "cmake.h" #include <algorithm> +#include <array> #include <cstdio> #include <cstdlib> #include <cstring> @@ -22,6 +23,10 @@ #include <cmext/algorithm> #include <cmext/string_view> +#if !defined(CMAKE_BOOTSTRAP) && !defined(_WIN32) +# include <unistd.h> +#endif + #include "cmsys/FStream.hxx" #include "cmsys/Glob.hxx" #include "cmsys/RegularExpression.hxx" @@ -55,6 +60,7 @@ #include "cmSystemTools.h" #include "cmTarget.h" #include "cmTargetLinkLibraryType.h" +#include "cmUVProcessChain.h" #include "cmUtils.hxx" #include "cmVersionConfig.h" #include "cmWorkingDirectory.h" @@ -62,6 +68,7 @@ #if !defined(CMAKE_BOOTSTRAP) # include <unordered_map> +# include <cm3p/curl/curl.h> # include <cm3p/json/writer.h> # include "cmFileAPI.h" @@ -81,7 +88,6 @@ # include "cmGlobalBorlandMakefileGenerator.h" # include "cmGlobalJOMMakefileGenerator.h" # include "cmGlobalNMakeMakefileGenerator.h" -# include "cmGlobalVisualStudio10Generator.h" # include "cmGlobalVisualStudio11Generator.h" # include "cmGlobalVisualStudio12Generator.h" # include "cmGlobalVisualStudio14Generator.h" @@ -251,6 +257,8 @@ Json::Value cmake::ReportCapabilitiesJson() const std::vector<cmake::GeneratorInfo> generatorInfoList; this->GetRegisteredGenerators(generatorInfoList); + auto* curlVersion = curl_version_info(CURLVERSION_FIRST); + JsonValueMapType generatorMap; for (cmake::GeneratorInfo const& gi : generatorInfoList) { if (gi.isAlias) { // skip aliases, they are there for compatibility reasons @@ -285,6 +293,7 @@ Json::Value cmake::ReportCapabilitiesJson() const obj["generators"] = generators; obj["fileApi"] = cmFileAPI::ReportCapabilities(); obj["serverMode"] = false; + obj["tls"] = static_cast<bool>(curlVersion->features & CURL_VERSION_SSL); return obj; } @@ -777,6 +786,8 @@ enum class ListPresets Configure, Build, Test, + Package, + Workflow, All, }; } @@ -951,7 +962,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) CommandArgument::Values::One, [](std::string const& value, cmake* state) -> bool { const auto logLevel = StringToLogLevel(value); - if (logLevel == LogLevel::LOG_UNDEFINED) { + if (logLevel == Message::LogLevel::LOG_UNDEFINED) { cmSystemTools::Error( "Invalid level specified for --log-level"); return false; @@ -967,7 +978,7 @@ void cmake::SetArgs(const std::vector<std::string>& args) CommandArgument::Values::One, [](std::string const& value, cmake* state) -> bool { const auto logLevel = StringToLogLevel(value); - if (logLevel == LogLevel::LOG_UNDEFINED) { + if (logLevel == Message::LogLevel::LOG_UNDEFINED) { cmSystemTools::Error( "Invalid level specified for --loglevel"); return false; @@ -1132,12 +1143,16 @@ void cmake::SetArgs(const std::vector<std::string>& args) listPresets = ListPresets::Build; } else if (value == "test") { listPresets = ListPresets::Test; + } else if (value == "package") { + listPresets = ListPresets::Package; + } else if (value == "workflow") { + listPresets = ListPresets::Workflow; } else if (value == "all") { listPresets = ListPresets::All; } else { cmSystemTools::Error( "Invalid value specified for --list-presets.\n" - "Valid values are configure, build, test, or all. " + "Valid values are configure, build, test, package, or all. " "When no value is passed the default is configure."); return false; } @@ -1282,9 +1297,13 @@ void cmake::SetArgs(const std::vector<std::string>& args) cmCMakePresetsGraph presetsGraph; auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory()); if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) { - cmSystemTools::Error( + std::string errorMsg = cmStrCat("Could not read presets from ", this->GetHomeDirectory(), - ": ", cmCMakePresetsGraph::ResultToString(result))); + ": ", cmCMakePresetsGraph::ResultToString(result)); + if (!presetsGraph.errors.empty()) { + errorMsg = cmStrCat(errorMsg, "\nErrors:\n", presetsGraph.errors); + } + cmSystemTools::Error(errorMsg); return; } @@ -1295,6 +1314,10 @@ void cmake::SetArgs(const std::vector<std::string>& args) presetsGraph.PrintBuildPresetList(); } else if (listPresets == ListPresets::Test) { presetsGraph.PrintTestPresetList(); + } else if (listPresets == ListPresets::Package) { + presetsGraph.PrintPackagePresetList(); + } else if (listPresets == ListPresets::Workflow) { + presetsGraph.PrintWorkflowPresetList(); } else if (listPresets == ListPresets::All) { presetsGraph.PrintAllPresets(); } @@ -1398,23 +1421,52 @@ void cmake::SetArgs(const std::vector<std::string>& args) #endif } -cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr) -{ - using LevelsPair = std::pair<std::string, LogLevel>; - static const std::vector<LevelsPair> levels = { - { "error", LogLevel::LOG_ERROR }, { "warning", LogLevel::LOG_WARNING }, - { "notice", LogLevel::LOG_NOTICE }, { "status", LogLevel::LOG_STATUS }, - { "verbose", LogLevel::LOG_VERBOSE }, { "debug", LogLevel::LOG_DEBUG }, - { "trace", LogLevel::LOG_TRACE } +namespace { +using LevelsPair = std::pair<cm::string_view, Message::LogLevel>; +using LevelsPairArray = std::array<LevelsPair, 7>; +const LevelsPairArray& getStringToLogLevelPairs() +{ + static const LevelsPairArray levels = { + { { "error", Message::LogLevel::LOG_ERROR }, + { "warning", Message::LogLevel::LOG_WARNING }, + { "notice", Message::LogLevel::LOG_NOTICE }, + { "status", Message::LogLevel::LOG_STATUS }, + { "verbose", Message::LogLevel::LOG_VERBOSE }, + { "debug", Message::LogLevel::LOG_DEBUG }, + { "trace", Message::LogLevel::LOG_TRACE } } }; + return levels; +} +} // namespace - const auto levelStrLowCase = cmSystemTools::LowerCase(levelStr); +Message::LogLevel cmake::StringToLogLevel(cm::string_view levelStr) +{ + const LevelsPairArray& levels = getStringToLogLevelPairs(); + + const auto levelStrLowCase = + cmSystemTools::LowerCase(std::string{ levelStr }); + // NOLINTNEXTLINE(readability-qualified-auto) const auto it = std::find_if(levels.cbegin(), levels.cend(), [&levelStrLowCase](const LevelsPair& p) { return p.first == levelStrLowCase; }); - return (it != levels.cend()) ? it->second : LogLevel::LOG_UNDEFINED; + return (it != levels.cend()) ? it->second : Message::LogLevel::LOG_UNDEFINED; +} + +std::string cmake::LogLevelToString(Message::LogLevel level) +{ + const LevelsPairArray& levels = getStringToLogLevelPairs(); + + // NOLINTNEXTLINE(readability-qualified-auto) + const auto it = + std::find_if(levels.cbegin(), levels.cend(), + [&level](const LevelsPair& p) { return p.second == level; }); + const cm::string_view levelStrLowerCase = + (it != levels.cend()) ? it->first : "undefined"; + std::string levelStrUpperCase = + cmSystemTools::UpperCase(std::string{ levelStrLowerCase }); + return levelStrUpperCase; } cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr) @@ -2096,6 +2148,9 @@ int cmake::ActualConfigure() this->UpdateConversionPathTable(); this->CleanupCommandsAndMacros(); + cmSystemTools::RemoveADirectory(this->GetHomeOutputDirectory() + + "/CMakeFiles/CMakeScratch"); + int res = this->DoPreConfigureChecks(); if (res < 0) { return -2; @@ -2319,7 +2374,6 @@ std::unique_ptr<cmGlobalGenerator> cmake::EvaluateDefaultGlobalGenerator() { "14.0", "Visual Studio 14 2015" }, // { "12.0", "Visual Studio 12 2013" }, // { "11.0", "Visual Studio 11 2012" }, // - { "10.0", "Visual Studio 10 2010" }, // { "9.0", "Visual Studio 9 2008" } }; static const char* const vsEntries[] = { @@ -2648,7 +2702,6 @@ void cmake::AddDefaultGenerators() this->Generators.push_back(cmGlobalVisualStudio14Generator::NewFactory()); this->Generators.push_back(cmGlobalVisualStudio12Generator::NewFactory()); this->Generators.push_back(cmGlobalVisualStudio11Generator::NewFactory()); - this->Generators.push_back(cmGlobalVisualStudio10Generator::NewFactory()); this->Generators.push_back(cmGlobalVisualStudio9Generator::NewFactory()); this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory()); this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory()); @@ -3619,6 +3672,214 @@ bool cmake::Open(const std::string& dir, bool dryRun) return gen->Open(dir, *cachedProjectName, dryRun); } +#if !defined(CMAKE_BOOTSTRAP) +template <typename T> +const T* cmake::FindPresetForWorkflow( + cm::static_string_view type, + const std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets, + const cmCMakePresetsGraph::WorkflowPreset::WorkflowStep& step) +{ + auto it = presets.find(step.PresetName); + if (it == presets.end()) { + cmSystemTools::Error(cmStrCat("No such ", type, " preset in ", + this->GetHomeDirectory(), ": \"", + step.PresetName, '"')); + return nullptr; + } + + if (it->second.Unexpanded.Hidden) { + cmSystemTools::Error(cmStrCat("Cannot use hidden ", type, " preset in ", + this->GetHomeDirectory(), ": \"", + step.PresetName, '"')); + return nullptr; + } + + if (!it->second.Expanded) { + cmSystemTools::Error(cmStrCat("Could not evaluate ", type, " preset \"", + step.PresetName, + "\": Invalid macro expansion")); + return nullptr; + } + + if (!it->second.Expanded->ConditionResult) { + cmSystemTools::Error(cmStrCat("Cannot use disabled ", type, " preset in ", + this->GetHomeDirectory(), ": \"", + step.PresetName, '"')); + return nullptr; + } + + return &*it->second.Expanded; +} + +std::function<int()> cmake::BuildWorkflowStep( + const std::vector<std::string>& args) +{ + cmUVProcessChainBuilder builder; + builder + .AddCommand(args) +# ifdef _WIN32 + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, _fileno(stdout)) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, _fileno(stderr)); +# else + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, STDOUT_FILENO) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, STDERR_FILENO); +# endif + return [builder]() -> int { + auto chain = builder.Start(); + chain.Wait(); + return static_cast<int>(chain.GetStatus().front()->ExitStatus); + }; +} +#endif + +int cmake::Workflow(const std::string& presetName, + WorkflowListPresets listPresets, WorkflowFresh fresh) +{ +#ifndef CMAKE_BOOTSTRAP + this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory()); + this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory()); + + cmCMakePresetsGraph settingsFile; + auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory()); + if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) { + cmSystemTools::Error( + cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ": ", + cmCMakePresetsGraph::ResultToString(result))); + return 1; + } + + if (listPresets == WorkflowListPresets::Yes) { + settingsFile.PrintWorkflowPresetList(); + return 0; + } + + auto presetPair = settingsFile.WorkflowPresets.find(presetName); + if (presetPair == settingsFile.WorkflowPresets.end()) { + cmSystemTools::Error(cmStrCat("No such workflow preset in ", + this->GetHomeDirectory(), ": \"", presetName, + '"')); + settingsFile.PrintWorkflowPresetList(); + return 1; + } + + if (presetPair->second.Unexpanded.Hidden) { + cmSystemTools::Error(cmStrCat("Cannot use hidden workflow preset in ", + this->GetHomeDirectory(), ": \"", presetName, + '"')); + settingsFile.PrintWorkflowPresetList(); + return 1; + } + + auto const& expandedPreset = presetPair->second.Expanded; + if (!expandedPreset) { + cmSystemTools::Error(cmStrCat("Could not evaluate workflow preset \"", + presetName, "\": Invalid macro expansion")); + settingsFile.PrintWorkflowPresetList(); + return 1; + } + + if (!expandedPreset->ConditionResult) { + cmSystemTools::Error(cmStrCat("Cannot use disabled workflow preset in ", + this->GetHomeDirectory(), ": \"", presetName, + '"')); + settingsFile.PrintWorkflowPresetList(); + return 1; + } + + struct CalculatedStep + { + int StepNumber; + cm::static_string_view Type; + std::string Name; + std::function<int()> Action; + + CalculatedStep(int stepNumber, cm::static_string_view type, + std::string name, std::function<int()> action) + : StepNumber(stepNumber) + , Type(type) + , Name(std::move(name)) + , Action(std::move(action)) + { + } + }; + + std::vector<CalculatedStep> steps; + steps.reserve(expandedPreset->Steps.size()); + int stepNumber = 1; + for (auto const& step : expandedPreset->Steps) { + switch (step.PresetType) { + case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type:: + Configure: { + auto const* configurePreset = this->FindPresetForWorkflow( + "configure"_s, settingsFile.ConfigurePresets, step); + if (!configurePreset) { + return 1; + } + std::vector<std::string> args{ cmSystemTools::GetCMakeCommand(), + "--preset", step.PresetName }; + if (fresh == WorkflowFresh::Yes) { + args.emplace_back("--fresh"); + } + steps.emplace_back(stepNumber, "configure"_s, step.PresetName, + this->BuildWorkflowStep(args)); + } break; + case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Build: { + auto const* buildPreset = this->FindPresetForWorkflow( + "build"_s, settingsFile.BuildPresets, step); + if (!buildPreset) { + return 1; + } + steps.emplace_back( + stepNumber, "build"_s, step.PresetName, + this->BuildWorkflowStep({ cmSystemTools::GetCMakeCommand(), + "--build", "--preset", step.PresetName })); + } break; + case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Test: { + auto const* testPreset = this->FindPresetForWorkflow( + "test"_s, settingsFile.TestPresets, step); + if (!testPreset) { + return 1; + } + steps.emplace_back( + stepNumber, "test"_s, step.PresetName, + this->BuildWorkflowStep({ cmSystemTools::GetCTestCommand(), + "--preset", step.PresetName })); + } break; + case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Package: { + auto const* packagePreset = this->FindPresetForWorkflow( + "package"_s, settingsFile.PackagePresets, step); + if (!packagePreset) { + return 1; + } + steps.emplace_back( + stepNumber, "package"_s, step.PresetName, + this->BuildWorkflowStep({ cmSystemTools::GetCPackCommand(), + "--preset", step.PresetName })); + } break; + } + stepNumber++; + } + + int stepResult; + bool first = true; + for (auto const& step : steps) { + if (!first) { + std::cout << "\n"; + } + std::cout << "Executing workflow step " << step.StepNumber << " of " + << steps.size() << ": " << step.Type << " preset \"" << step.Name + << "\"\n\n" + << std::flush; + if ((stepResult = step.Action()) != 0) { + return stepResult; + } + first = false; + } +#endif + + return 0; +} + void cmake::WatchUnusedCli(const std::string& var) { #ifndef CMAKE_BOOTSTRAP diff --git a/Source/cmake.h b/Source/cmake.h index 3c6af17..3183577 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -16,6 +16,7 @@ #include <vector> #include <cm/string_view> +#include <cmext/string_view> #include "cmGeneratedFileStream.h" #include "cmInstalledFile.h" @@ -119,19 +120,6 @@ public: FIND_PACKAGE_MODE }; - /** \brief Define log level constants. */ - enum LogLevel - { - LOG_UNDEFINED, - LOG_ERROR, - LOG_WARNING, - LOG_NOTICE, - LOG_STATUS, - LOG_VERBOSE, - LOG_DEBUG, - LOG_TRACE - }; - /** \brief Define supported trace formats **/ enum TraceFormat { @@ -469,9 +457,10 @@ public: bool WasLogLevelSetViaCLI() const { return this->LogLevelWasSetViaCLI; } //! Get the selected log level for `message()` commands during the cmake run. - LogLevel GetLogLevel() const { return this->MessageLogLevel; } - void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; } - static LogLevel StringToLogLevel(const std::string& levelStr); + Message::LogLevel GetLogLevel() const { return this->MessageLogLevel; } + void SetLogLevel(Message::LogLevel level) { this->MessageLogLevel = level; } + static Message::LogLevel StringToLogLevel(cm::string_view levelStr); + static std::string LogLevelToString(Message::LogLevel level); static TraceFormat StringToTraceFormat(const std::string& levelStr); bool HasCheckInProgress() const @@ -612,6 +601,20 @@ public: //! run the --open option bool Open(const std::string& dir, bool dryRun); + //! run the --workflow option + enum class WorkflowListPresets + { + No, + Yes, + }; + enum class WorkflowFresh + { + No, + Yes, + }; + int Workflow(const std::string& presetName, WorkflowListPresets listPresets, + WorkflowFresh fresh); + void UnwatchUnusedCli(const std::string& var); void WatchUnusedCli(const std::string& var); @@ -732,7 +735,7 @@ private: std::set<std::string> DebugFindPkgs; std::set<std::string> DebugFindVars; - LogLevel MessageLogLevel = LogLevel::LOG_STATUS; + Message::LogLevel MessageLogLevel = Message::LogLevel::LOG_STATUS; bool LogLevelWasSetViaCLI = false; bool LogContext = false; @@ -752,6 +755,16 @@ private: void AppendExtraGeneratorsDocumentation(std::vector<cmDocumentationEntry>&); #if !defined(CMAKE_BOOTSTRAP) + template <typename T> + const T* FindPresetForWorkflow( + cm::static_string_view type, + const std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets, + const cmCMakePresetsGraph::WorkflowPreset::WorkflowStep& step); + + std::function<int()> BuildWorkflowStep(const std::vector<std::string>& args); +#endif + +#if !defined(CMAKE_BOOTSTRAP) std::unique_ptr<cmMakefileProfilingData> ProfilingOutput; #endif }; @@ -873,6 +886,7 @@ private: F(cxx_std_17) \ F(cxx_std_20) \ F(cxx_std_23) \ + F(cxx_std_26) \ FOR_EACH_CXX98_FEATURE(F) \ FOR_EACH_CXX11_FEATURE(F) \ FOR_EACH_CXX14_FEATURE(F) @@ -883,7 +897,8 @@ private: F(cuda_std_14) \ F(cuda_std_17) \ F(cuda_std_20) \ - F(cuda_std_23) + F(cuda_std_23) \ + F(cuda_std_26) #define FOR_EACH_HIP_FEATURE(F) \ F(hip_std_98) \ @@ -891,4 +906,5 @@ private: F(hip_std_14) \ F(hip_std_17) \ F(hip_std_20) \ - F(hip_std_23) + F(hip_std_23) \ + F(hip_std_26) diff --git a/Source/cmake.version.manifest b/Source/cmake.version.manifest index e7010c9..79e3d19 100644 --- a/Source/cmake.version.manifest +++ b/Source/cmake.version.manifest @@ -1,6 +1,6 @@ <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" - xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" > + xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" > <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <!-- Windows Vista --> diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx index f931e9d..723932e 100644 --- a/Source/cmakemain.cxx +++ b/Source/cmakemain.cxx @@ -71,7 +71,7 @@ const char* cmDocumentationUsageNote[][2] = { const char* cmDocumentationOptions[][2] = { CMAKE_STANDARD_OPTIONS_TABLE, { "--preset <preset>,--preset=<preset>", "Specify a configure preset." }, - { "--list-presets", "List available presets." }, + { "--list-presets[=<type>]", "List available presets." }, { "-E", "CMake command mode." }, { "-L[A][H]", "List non-advanced cached variables." }, { "--fresh", @@ -82,9 +82,9 @@ const char* cmDocumentationOptions[][2] = { { "-N", "View mode only." }, { "-P <file>", "Process script mode." }, { "--find-package", "Legacy pkg-config like mode. Do not use." }, - { "--graphviz=[file]", - "Generate graphviz of dependencies, see " - "CMakeGraphVizOptions.cmake for more." }, + { "--graphviz=<file>", + "Generate graphviz of dependencies, see CMakeGraphVizOptions.cmake for " + "more." }, { "--system-information [file]", "Dump information about this system." }, { "--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>", "Set the verbosity of messages from CMake files. " @@ -109,8 +109,7 @@ const char* cmDocumentationOptions[][2] = { { "--warn-uninitialized", "Warn about uninitialized values." }, { "--no-warn-unused-cli", "Don't warn about command line options." }, { "--check-system-vars", - "Find problems with variable usage in system " - "files." }, + "Find problems with variable usage in system files." }, { "--compile-no-warning-as-error", "Ignore COMPILE_WARNING_AS_ERROR property and " "CMAKE_COMPILE_WARNING_AS_ERROR variable." }, @@ -616,7 +615,7 @@ int do_build(int ac, char const* const* av) " <dir> = Project binary directory to be built.\n" " --preset <preset>, --preset=<preset>\n" " = Specify a build preset.\n" - " --list-presets\n" + " --list-presets[=<type>]\n" " = List available build presets.\n" " --parallel [<jobs>], -j [<jobs>]\n" " = Build in parallel using the given number of jobs. \n" @@ -627,14 +626,14 @@ int do_build(int ac, char const* const* av) " specifies a default parallel level when this " "option\n" " is not given.\n" - " --target <tgt>..., -t <tgt>... \n" + " -t <tgt>..., --target <tgt>...\n" " = Build <tgt> instead of default targets.\n" " --config <cfg> = For multi-configuration tools, choose <cfg>.\n" " --clean-first = Build target 'clean' first, then build.\n" " (To clean only, use --target 'clean'.)\n" " --resolve-package-references={on|only|off}\n" " = Restore/resolve package references during build.\n" - " --verbose, -v = Enable verbose output - if supported - including\n" + " -v, --verbose = Enable verbose output - if supported - including\n" " the build commands to be executed. \n" " -- = Pass remaining options to the native tool.\n" ; @@ -912,6 +911,90 @@ int do_install(int ac, char const* const* av) #endif } +int do_workflow(int ac, char const* const* av) +{ +#ifdef CMAKE_BOOTSTRAP + std::cerr << "This cmake does not support --workflow\n"; + return -1; +#else + using WorkflowListPresets = cmake::WorkflowListPresets; + using WorkflowFresh = cmake::WorkflowFresh; + std::string presetName; + auto listPresets = WorkflowListPresets::No; + auto fresh = WorkflowFresh::No; + + using CommandArgument = + cmCommandLineArgument<bool(std::string const& value)>; + + std::vector<CommandArgument> arguments = { + CommandArgument{ "--preset", CommandArgument::Values::One, + CommandArgument::setToValue(presetName) }, + CommandArgument{ "--list-presets", CommandArgument::Values::Zero, + [&listPresets](const std::string&) -> bool { + listPresets = WorkflowListPresets::Yes; + return true; + } }, + CommandArgument{ "--fresh", CommandArgument::Values::Zero, + [&fresh](const std::string&) -> bool { + fresh = WorkflowFresh::Yes; + return true; + } }, + }; + + std::vector<std::string> inputArgs; + + inputArgs.reserve(ac - 2); + cm::append(inputArgs, av + 2, av + ac); + + decltype(inputArgs.size()) i = 0; + for (; i < inputArgs.size(); ++i) { + std::string const& arg = inputArgs[i]; + bool matched = false; + bool parsed = false; + for (auto const& m : arguments) { + matched = m.matches(arg); + if (matched) { + parsed = m.parse(arg, i, inputArgs); + break; + } + } + if (!(matched && parsed)) { + if (!matched) { + presetName.clear(); + listPresets = WorkflowListPresets::No; + std::cerr << "Unknown argument " << arg << std::endl; + } + break; + } + } + + if (presetName.empty() && listPresets == WorkflowListPresets::No) { + /* clang-format off */ + std::cerr << + "Usage: cmake --workflow [options]\n" + "Options:\n" + " --preset <preset> = Workflow preset to execute.\n" + " --list-presets = List available workflow presets.\n" + " --fresh = Configure a fresh build tree, removing any " + "existing cache file.\n" + ; + /* clang-format on */ + return 1; + } + + cmake cm(cmake::RoleInternal, cmState::Project); + cmSystemTools::SetMessageCallback( + [&cm](const std::string& msg, const cmMessageMetadata& md) { + cmakemainMessageCallback(msg, md, &cm); + }); + cm.SetProgressCallback([&cm](const std::string& msg, float prog) { + cmakemainProgressCallback(msg, prog, &cm); + }); + + return cm.Workflow(presetName, listPresets, fresh); +#endif +} + int do_open(int ac, char const* const* av) { #ifdef CMAKE_BOOTSTRAP @@ -981,6 +1064,9 @@ int main(int ac, char const* const* av) if (strcmp(av[1], "--open") == 0) { return do_open(ac, av); } + if (strcmp(av[1], "--workflow") == 0) { + return do_workflow(ac, av); + } if (strcmp(av[1], "-E") == 0) { return do_command(ac, av, std::move(consoleBuf)); } diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx index 8921aa0..69eb19e 100644 --- a/Source/cmcldeps.cxx +++ b/Source/cmcldeps.cxx @@ -273,6 +273,7 @@ int main() std::string clrest = rest; // rc: /fo x.dir\x.rc.res -> cl: /out:x.dir\x.rc.res.dep.obj clrest = replace(clrest, "/fo ", "/out:"); + clrest = replace(clrest, "-fo ", "-out:"); clrest = replace(clrest, objfile, objfile + ".dep.obj "); cl = "\"" + cl + "\" /P /DRC_INVOKED /TC "; diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 9ab39f1..67394f9 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -360,17 +360,29 @@ int HandleIWYU(const std::string& runCmd, const std::string& /* sourceFile */, int HandleTidy(const std::string& runCmd, const std::string& sourceFile, const std::vector<std::string>& orig_cmd) { - // Construct the clang-tidy command line by taking what was given - // and adding our compiler command line. The clang-tidy tool will - // automatically skip over the compiler itself and extract the - // options. - int ret; std::vector<std::string> tidy_cmd = cmExpandedList(runCmd, true); tidy_cmd.push_back(sourceFile); - tidy_cmd.emplace_back("--"); - cm::append(tidy_cmd, orig_cmd); + + // clang-tidy supports working out the compile commands from a + // compile_commands.json file in a directory given by a "-p" option, or by + // passing the compiler command line arguments after --. When the latter + // strategy is used and the build is using a compiler other than the system + // default, clang-tidy may erroneously use the system default compiler's + // headers instead of those from the custom compiler. It doesn't do that if + // given a compile_commands.json to work with instead, so prefer to use the + // compile_commands.json file when "-p" is present. + if (!cm::contains(tidy_cmd.cbegin(), tidy_cmd.cend() - 1, "-p")) { + // Construct the clang-tidy command line by taking what was given + // and adding our compiler command line. The clang-tidy tool will + // automatically skip over the compiler itself and extract the + // options. If the compiler is a custom compiler, clang-tidy might + // not correctly handle that with this approach. + tidy_cmd.emplace_back("--"); + cm::append(tidy_cmd, orig_cmd); + } // Run the tidy command line. Capture its stdout and hide its stderr. + int ret; std::string stdOut; std::string stdErr; if (!cmSystemTools::RunSingleCommand(tidy_cmd, &stdOut, &stdErr, &ret, @@ -791,6 +803,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } if (args[1] == "env") { +#ifndef CMAKE_BOOTSTRAP + cmSystemTools::EnvDiff env; +#endif + auto ai = args.cbegin() + 2; auto ae = args.cend(); for (; ai != ae; ++ai) { @@ -803,16 +819,40 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } if (cmHasLiteralPrefix(a, "--unset=")) { // Unset environment variable. +#ifdef CMAKE_BOOTSTRAP cmSystemTools::UnPutEnv(a.substr(8)); +#else + env.UnPutEnv(a.substr(8)); +#endif + } else if (a == "--modify") { +#ifdef CMAKE_BOOTSTRAP + std::cerr + << "cmake -E env: --modify not available during bootstrapping\n"; + return 1; +#else + if (++ai == ae) { + std::cerr << "cmake -E env: --modify missing a parameter\n"; + return 1; + } + std::string const& op = *ai; + if (!env.ParseOperation(op)) { + std::cerr << "cmake -E env: invalid parameter to --modify: " << op + << '\n'; + return 1; + } +#endif } else if (!a.empty() && a[0] == '-') { // Environment variable and command names cannot start in '-', // so this must be an unknown option. - std::cerr << "cmake -E env: unknown option '" << a << '\'' - << std::endl; + std::cerr << "cmake -E env: unknown option '" << a << "'\n"; return 1; } else if (a.find('=') != std::string::npos) { // Set environment variable. +#ifdef CMAKE_BOOTSTRAP cmSystemTools::PutEnv(a); +#else + env.PutEnv(a); +#endif } else { // This is the beginning of the command. break; @@ -820,10 +860,14 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args, } if (ai == ae) { - std::cerr << "cmake -E env: no command given" << std::endl; + std::cerr << "cmake -E env: no command given\n"; return 1; } +#ifndef CMAKE_BOOTSTRAP + env.ApplyToCurrentEnv(); +#endif + // Execute command from remaining arguments. std::vector<std::string> cmd(ai, ae); int retval; @@ -1668,16 +1712,15 @@ cmsys::Status cmcmd::SymlinkInternal(std::string const& file, } std::string linktext = cmSystemTools::GetFilenameName(file); #if defined(_WIN32) && !defined(__CYGWIN__) - std::string errorMessage; - cmsys::Status status = - cmSystemTools::CreateSymlink(linktext, link, &errorMessage); + cmsys::Status status = cmSystemTools::CreateSymlinkQuietly(linktext, link); // Creating a symlink will fail with ERROR_PRIVILEGE_NOT_HELD if the user // does not have SeCreateSymbolicLinkPrivilege, or if developer mode is not // active. In that case, we try to copy the file. if (status.GetWindows() == ERROR_PRIVILEGE_NOT_HELD) { status = cmSystemTools::CopyFileAlways(file, link); } else if (!status) { - cmSystemTools::Error(errorMessage); + cmSystemTools::Error(cmStrCat("failed to create symbolic link '", link, + "': ", status.GetString())); } return status; #else @@ -2242,13 +2285,18 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg, // Parse the link command to extract information we need. for (; arg != argEnd; ++arg) { if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL:YES") == 0 || - cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0) { + cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL:YES") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL") == 0) { this->Incremental = true; - } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0) { + } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0 || + cmSystemTools::Strucmp(arg->c_str(), "-MANIFEST:NO") == 0) { this->LinkGeneratesManifest = false; - } else if (cmHasLiteralPrefix(*arg, "/Fe")) { + } else if (cmHasLiteralPrefix(*arg, "/Fe") || + cmHasLiteralPrefix(*arg, "-Fe")) { this->TargetFile = arg->substr(3); - } else if (cmHasLiteralPrefix(*arg, "/out:")) { + } else if (cmHasLiteralPrefix(*arg, "/out:") || + cmHasLiteralPrefix(*arg, "-out:")) { this->TargetFile = arg->substr(5); } } diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx index d520c14..f239576 100644 --- a/Source/kwsys/Directory.cxx +++ b/Source/kwsys/Directory.cxx @@ -43,12 +43,12 @@ public: { std::string Name; #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t FindData; + WIN32_FIND_DATAW FindData; #endif FileData(std::string name #if defined(_WIN32) && !defined(__CYGWIN__) , - _wfinddata_t data + WIN32_FIND_DATAW data #endif ) : Name(std::move(name)) @@ -115,8 +115,8 @@ std::string Directory::GetFilePath(std::size_t i) const bool Directory::FileIsDirectory(std::size_t i) const { #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t const& data = this->Internal->Files[i].FindData; - return (data.attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; + auto const& data = this->Internal->Files[i].FindData; + return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; #else std::string const& path = this->GetFilePath(i); return kwsys::SystemTools::FileIsDirectory(path); @@ -127,9 +127,9 @@ bool Directory::FileIsSymlink(std::size_t i) const { std::string const& path = this->GetFilePath(i); #if defined(_WIN32) && !defined(__CYGWIN__) - _wfinddata_t const& data = this->Internal->Files[i].FindData; + auto const& data = this->Internal->Files[i].FindData; return kwsys::SystemTools::FileIsSymlinkWithAttr( - Encoding::ToWindowsExtendedPath(path), data.attrib); + Encoding::ToWindowsExtendedPath(path), data.dwFileAttributes); #else return kwsys::SystemTools::FileIsSymlink(path); #endif @@ -157,7 +157,7 @@ namespace KWSYS_NAMESPACE { Status Directory::Load(std::string const& name, std::string* errorMessage) { this->Clear(); - intptr_t srchHandle; + HANDLE srchHandle; char* buf; size_t bufLength; size_t n = name.size(); @@ -176,14 +176,14 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) snprintf(buf, bufLength, "%s/*", name.c_str()); } } - struct _wfinddata_t data; // data of current file + WIN32_FIND_DATAW data; // data of current file // Now put them into the file array srchHandle = - _wfindfirst((wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data); + FindFirstFileW(Encoding::ToWindowsExtendedPath(buf).c_str(), &data); delete[] buf; - if (srchHandle == -1) { + if (srchHandle == INVALID_HANDLE_VALUE) { Status status = Status::POSIX_errno(); if (errorMessage) { *errorMessage = status.GetString(); @@ -193,10 +193,11 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) // Loop through names do { - this->Internal->Files.emplace_back(Encoding::ToNarrow(data.name), data); - } while (_wfindnext(srchHandle, &data) != -1); + this->Internal->Files.emplace_back(Encoding::ToNarrow(data.cFileName), + data); + } while (FindNextFileW(srchHandle, &data)); this->Internal->Path = name; - if (_findclose(srchHandle) == -1) { + if (!FindClose(srchHandle)) { Status status = Status::POSIX_errno(); if (errorMessage) { *errorMessage = status.GetString(); @@ -209,7 +210,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage) unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, std::string* errorMessage) { - intptr_t srchHandle; + HANDLE srchHandle; char* buf; size_t bufLength; size_t n = name.size(); @@ -222,13 +223,13 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, buf = new char[n + 2 + 1]; snprintf(buf, bufLength, "%s/*", name.c_str()); } - struct _wfinddata_t data; // data of current file + WIN32_FIND_DATAW data; // data of current file // Now put them into the file array - srchHandle = _wfindfirst((wchar_t*)Encoding::ToWide(buf).c_str(), &data); + srchHandle = FindFirstFileW(Encoding::ToWide(buf).c_str(), &data); delete[] buf; - if (srchHandle == -1) { + if (srchHandle == INVALID_HANDLE_VALUE) { if (errorMessage) { if (unsigned int errorId = GetLastError()) { LPSTR message = nullptr; @@ -250,8 +251,8 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name, unsigned long count = 0; do { count++; - } while (_wfindnext(srchHandle, &data) != -1); - _findclose(srchHandle); + } while (FindNextFileW(srchHandle, &data)); + FindClose(srchHandle); return count; } diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 5889a4b..a20901c 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -536,9 +536,11 @@ public: StringMap TranslationMap; #endif #ifdef _WIN32 - static std::string GetCasePathName(std::string const& pathIn); + static std::string GetCasePathName(std::string const& pathIn, + bool const cache); static std::string GetActualCaseForPathCached(std::string const& path); static const char* GetEnvBuffered(const char* key); + std::map<std::string, std::string, SystemToolsPathCaseCmp> FindFileMap; std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap; std::map<std::string, std::string> EnvMap; #endif @@ -571,7 +573,8 @@ public: static SystemToolsStatic* SystemToolsStatics; #ifdef _WIN32 -std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) +std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn, + bool const cache) { std::string casePath; @@ -623,14 +626,31 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) } else { std::string test_str = casePath; test_str += path_components[idx]; - WIN32_FIND_DATAW findData; - HANDLE hFind = - ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); - if (INVALID_HANDLE_VALUE != hFind) { - path_components[idx] = Encoding::ToNarrow(findData.cFileName); - ::FindClose(hFind); - } else { - converting = false; + + bool found_in_cache = false; + if (cache) { + auto const it = SystemToolsStatics->FindFileMap.find(test_str); + if (it != SystemToolsStatics->FindFileMap.end()) { + path_components[idx] = it->second; + found_in_cache = true; + } + } + + if (!found_in_cache) { + WIN32_FIND_DATAW findData; + HANDLE hFind = + ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData); + if (INVALID_HANDLE_VALUE != hFind) { + auto case_file_name = Encoding::ToNarrow(findData.cFileName); + if (cache) { + SystemToolsStatics->FindFileMap.emplace(test_str, + case_file_name); + } + path_components[idx] = std::move(case_file_name); + ::FindClose(hFind); + } else { + converting = false; + } } } } @@ -642,19 +662,16 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn) std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p) { - // Check to see if actual case has already been called - // for this path, and the result is stored in the PathCaseMap - auto& pcm = SystemToolsStatics->PathCaseMap; - { - auto itr = pcm.find(p); - if (itr != pcm.end()) { - return itr->second; - } - } - std::string casePath = SystemToolsStatic::GetCasePathName(p); - if (casePath.size() <= MAX_PATH) { - pcm[p] = casePath; + std::string casePath; + + auto it = SystemToolsStatics->PathCaseMap.find(p); + if (it != SystemToolsStatics->PathCaseMap.end()) { + casePath = it->second; + } else { + casePath = SystemToolsStatic::GetCasePathName(p, true); + SystemToolsStatics->PathCaseMap.emplace(p, casePath); } + return casePath; } #endif @@ -3067,17 +3084,14 @@ std::string SystemTools::GetRealPath(const std::string& path, return ret; } -bool SystemTools::FileIsDirectory(const std::string& inName) +// Remove any trailing slash from the name except in a root component. +static const char* RemoveTrailingSlashes( + const std::string& inName, char (&local_buffer)[KWSYS_SYSTEMTOOLS_MAXPATH], + std::string& string_buffer) { - if (inName.empty()) { - return false; - } size_t length = inName.size(); const char* name = inName.c_str(); - // Remove any trailing slash from the name except in a root component. - char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; - std::string string_buffer; size_t last = length - 1; if (last > 0 && (name[last] == '/' || name[last] == '\\') && strcmp(name, "/") != 0 && name[last - 1] != ':') { @@ -3091,6 +3105,19 @@ bool SystemTools::FileIsDirectory(const std::string& inName) } } + return name; +} + +bool SystemTools::FileIsDirectory(const std::string& inName) +{ + if (inName.empty()) { + return false; + } + + char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; + std::string string_buffer; + const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer); + // Now check the file node type. #if defined(_WIN32) DWORD attr = @@ -3107,9 +3134,21 @@ bool SystemTools::FileIsDirectory(const std::string& inName) } } -bool SystemTools::FileIsExecutable(const std::string& name) +bool SystemTools::FileIsExecutable(const std::string& inName) { - return !FileIsDirectory(name) && TestFileAccess(name, TEST_FILE_EXECUTE); +#ifdef _WIN32 + char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH]; + std::string string_buffer; + const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer); + const auto attr = + GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str()); + + // On Windows any file that exists and is not a directory is considered + // readable and therefore also executable: + return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY); +#else + return !FileIsDirectory(inName) && TestFileAccess(inName, TEST_FILE_EXECUTE); +#endif } #if defined(_WIN32) @@ -3655,7 +3694,7 @@ std::string SystemTools::RelativePath(const std::string& local, std::string SystemTools::GetActualCaseForPath(const std::string& p) { #ifdef _WIN32 - return SystemToolsStatic::GetCasePathName(p); + return SystemToolsStatic::GetCasePathName(p, false); #else return p; #endif |