Merge topic 'patch-FindPackageMessage' ee398e8946 FindPackageMessage: Update documentation Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !10441
diff --git a/.gitlab/ci/configure_macos_common.cmake b/.gitlab/ci/configure_macos_common.cmake index e78c9ce..614f211 100644 --- a/.gitlab/ci/configure_macos_common.cmake +++ b/.gitlab/ci/configure_macos_common.cmake
@@ -16,3 +16,9 @@ # when CMake itself is configured. Use a version that is not # newer than the macOS version running on any CI host. set(CMake_TEST_XCTest_DEPLOYMENT_TARGET "10.15" CACHE STRING "") + +if("$ENV{CMAKE_CONFIGURATION}" MATCHES "macos_arm64") + set(CMake_TEST_APPLE_SILICON ON CACHE BOOL "") +else() + set(CMake_TEST_APPLE_SILICON OFF CACHE BOOL "") +endif()
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst index b7232d6..089a3d6 100644 --- a/Help/command/find_package.rst +++ b/Help/command/find_package.rst
@@ -956,3 +956,37 @@ .. _cps-version_schema: https://cps-org.github.io/cps/schema.html#version-schema .. |cps-version_schema| replace:: ``version_schema`` + +CPS Transitive Requirements +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A |CPS| package description consists of one or more components which may in +turn depend on other components either internal or external to the package. +When external components are required, the providing package is noted as +a package-level requirement of the package. Additionally, the set of required +components is typically noted in said external package requirement. + +Where a CMake-script package description would use the +:command:`find_dependency` command to handle transitive dependencies, CMake +handles transitive dependencies for CPS itself using an internally nested +``find_package`` call. This call can resolve CPS package dependencies via +*either* another CPS package, or via a CMake-script package. The manner in +which the CPS component dependencies are handled is subject to some caveats. + +When the candidate for resolving a transitive dependency is another CPS +package, things are simple; ``COMPONENTS`` and CPS "components" are directly +comparable (and are effectively synonymous with CMake "imported targets"). +CMake-script packages, however, are encouraged to (and often do) check that +required components were found, whether or not the package describes separate +components. Additionally, even those that do describe components typically do +not have the same correlation to imported targets that is normal for CPS. As +a result, passing the set of required components declared by a CPS package to +``COMPONENTS`` would result in spurious failures to resolve dependencies. + +To address this, if a candidate for resolving a CPS transitive dependency is a +CMake-script package, CMake passes the required components as declared by the +consuming CPS package as ``OPTIONAL_COMPONENTS`` and performs a separate, +internal check that the candidate package supplied the required imported +targets. Those targets must be named ``<PackageName>::<ComponentName>``, in +conformance with CPS convention, or the check will consider the package not +found.
diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake index f3e3d1f..9fe3951 100644 --- a/Modules/CMakeDetermineCompilerABI.cmake +++ b/Modules/CMakeDetermineCompilerABI.cmake
@@ -67,6 +67,8 @@ # executables. This may lead to issues when their stderr output (which # contains the relevant compiler internals) becomes interweaved. string(REGEX REPLACE "(^| )-pipe( |$)" " " ${v} "${${v}}") + # Suppress any formatting of warnings and/or errors + string(REGEX REPLACE "(-f|/)diagnostics(-|:)color(=[a-z]+)?" "" ${v} "${${v}}") endforeach() # Save the current LC_ALL, LC_MESSAGES, and LANG environment variables
diff --git a/Modules/CMakePrintSystemInformation.cmake b/Modules/CMakePrintSystemInformation.cmake index 45ad38e..98e48e5 100644 --- a/Modules/CMakePrintSystemInformation.cmake +++ b/Modules/CMakePrintSystemInformation.cmake
@@ -5,10 +5,26 @@ CMakePrintSystemInformation --------------------------- -Print system information. +This module can be used for diagnostics to print system information. -This module serves diagnostic purposes. Just include it in a -project to see various internal CMake variables. +Examples +^^^^^^^^ + +Including this module in a project: + +.. code-block:: cmake + + include(CMakePrintSystemInformation) + +prints various internal CMake variables. For example:: + + CMAKE_SYSTEM is Linux-6.11.0-17-generic Linux 6.11.0-17-generic x86_64 + CMAKE_SYSTEM file is Platform/Linux + CMAKE_C_COMPILER is /usr/bin/cc + CMAKE_CXX_COMPILER is /usr/bin/c++ + CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS is -shared + CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS is -shared + ... #]=======================================================================] message("CMAKE_SYSTEM is ${CMAKE_SYSTEM} ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION} ${CMAKE_SYSTEM_PROCESSOR}")
diff --git a/Modules/CheckIPOSupported.cmake b/Modules/CheckIPOSupported.cmake index 9c4f61e..3db7ae4 100644 --- a/Modules/CheckIPOSupported.cmake +++ b/Modules/CheckIPOSupported.cmake
@@ -7,9 +7,9 @@ .. versionadded:: 3.9 -Check whether the compiler supports an interprocedural optimization (IPO/LTO). -Use this before enabling the :prop_tgt:`INTERPROCEDURAL_OPTIMIZATION` target -property. +This module provides a function to check whether the compiler supports an +interprocedural optimization (IPO/LTO). Use this module before enabling the +:prop_tgt:`INTERPROCEDURAL_OPTIMIZATION` target property. .. command:: check_ipo_supported @@ -60,21 +60,33 @@ Examples ^^^^^^^^ +Checking whether the compiler supports IPO and emitting a fatal error if it is +not supported: + .. code-block:: cmake + include(CheckIPOSupported) check_ipo_supported() # fatal error if IPO is not supported set_property(TARGET foo PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) +The following example demonstrates how to use the ``CheckIPOSupported`` module +to enable IPO for the target only when supported by the compiler and to issue a +warning if it is not. Additionally, projects may want to provide a +configuration option to control when IPO is enabled. For example: + .. code-block:: cmake - # Optional IPO. Do not use IPO if it's not supported by compiler. - check_ipo_supported(RESULT result OUTPUT output) - if(result) - set_property(TARGET foo PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) - else() - message(WARNING "IPO is not supported: ${output}") - endif() + option(FOO_ENABLE_IPO "Enable IPO/LTO") + if(FOO_ENABLE_IPO) + include(CheckIPOSupported) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + set_property(TARGET foo PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(WARNING "IPO is not supported: ${output}") + endif() + endif() #]=======================================================================] # X_RESULT - name of the final result variable
diff --git a/Modules/CheckIncludeFile.cmake b/Modules/CheckIncludeFile.cmake index f070ba5..a3a2cee 100644 --- a/Modules/CheckIncludeFile.cmake +++ b/Modules/CheckIncludeFile.cmake
@@ -35,9 +35,24 @@ .. include:: /module/CMAKE_REQUIRED_QUIET.txt -See the :module:`CheckIncludeFiles` module to check for multiple headers -at once. See the :module:`CheckIncludeFileCXX` module to check for headers -using the ``CXX`` language. +Examples +^^^^^^^^ + +Checking whether the ``C`` header ``<unistd.h>`` exists and storing the check +result in the ``HAVE_UNISTD_H`` cache variable: + +.. code-block:: cmake + + include(CheckIncludeFile) + + check_include_file(unistd.h HAVE_UNISTD_H) + +See Also +^^^^^^^^ + +* The :module:`CheckIncludeFileCXX` module to check for single ``C++`` header. +* The :module:`CheckIncludeFiles` module to check for one or more ``C`` or + ``C++`` headers at once. #]=======================================================================] include_guard(GLOBAL)
diff --git a/Modules/CheckIncludeFileCXX.cmake b/Modules/CheckIncludeFileCXX.cmake index ec02b36..756abf1 100644 --- a/Modules/CheckIncludeFileCXX.cmake +++ b/Modules/CheckIncludeFileCXX.cmake
@@ -35,8 +35,24 @@ .. include:: /module/CMAKE_REQUIRED_QUIET.txt -See modules :module:`CheckIncludeFile` and :module:`CheckIncludeFiles` -to check for one or more ``C`` headers. +Examples +^^^^^^^^ + +Checking whether the ``C++23`` header ``<stdfloat>`` exists and storing the +check result in the ``HAVE_STDFLOAT_HEADER`` cache variable: + +.. code-block:: cmake + + include(CheckIncludeFileCXX) + + check_include_file_cxx(stdfloat HAVE_STDFLOAT_HEADER) + +See Also +^^^^^^^^ + +* The :module:`CheckIncludeFile` module to check for single ``C`` header. +* The :module:`CheckIncludeFiles` module to check for one or more ``C`` or + ``C++`` headers at once. #]=======================================================================] include_guard(GLOBAL)
diff --git a/Modules/CheckIncludeFiles.cmake b/Modules/CheckIncludeFiles.cmake index 6dfb41d..855d450 100644 --- a/Modules/CheckIncludeFiles.cmake +++ b/Modules/CheckIncludeFiles.cmake
@@ -41,8 +41,41 @@ .. include:: /module/CMAKE_REQUIRED_QUIET.txt -See modules :module:`CheckIncludeFile` and :module:`CheckIncludeFileCXX` -to check for a single header file in ``C`` or ``CXX`` languages. +Examples +^^^^^^^^ + +Checking whether one or more ``C`` headers exist and storing the check result +in cache variables: + +.. code-block:: cmake + + include(CheckIncludeFiles) + + check_include_files(sys/socket.h HAVE_SYS_SOCKET_H) + + if(HAVE_SYS_SOCKET_H) + # The <net/if.h> header on Darwin and BSD-like systems is not self-contained + # and also requires <sys/socket.h> + check_include_files("sys/socket.h;net/if.h" HAVE_NET_IF_H) + else() + check_include_files(net/if.h HAVE_NET_IF_H) + endif() + +The ``LANGUAGE`` option can be used to specify which compiler to use. For +example, checking multiple ``C++`` headers, when both ``C`` and ``CXX`` +languages are enabled in the project: + +.. code-block:: cmake + + include(CheckIncludeFiles) + + check_include_files("header_1.hpp;header_2.hpp" HAVE_HEADERS LANGUAGE CXX) + +See Also +^^^^^^^^ + +* The :module:`CheckIncludeFile` module to check for single ``C`` header. +* The :module:`CheckIncludeFileCXX` module to check for single ``C++`` header. #]=======================================================================] include_guard(GLOBAL)
diff --git a/Modules/CheckPIESupported.cmake b/Modules/CheckPIESupported.cmake index 9922aec..c0cc3c0 100644 --- a/Modules/CheckPIESupported.cmake +++ b/Modules/CheckPIESupported.cmake
@@ -7,10 +7,16 @@ .. versionadded:: 3.14 -Check whether the linker supports Position Independent Code (PIE) or No -Position Independent Code (NO_PIE) for executables. -Use this to ensure that the :prop_tgt:`POSITION_INDEPENDENT_CODE` target -property for executables will be honored at link time. +This module provides the ``check_pie_supported()`` function to check whether the +linker supports Position Independent Code (PIE) or No Position Independent Code +(NO_PIE) for executables. + +When setting the :prop_tgt:`POSITION_INDEPENDENT_CODE` target property, +PIC-related compile and link options are added when building library objects, +and PIE-related compile options are added when building objects of executable +targets, regardless of this module. Use this module to ensure that the +``POSITION_INDEPENDENT_CODE`` target property for executables is also honored at +link time. .. command:: check_pie_supported @@ -36,14 +42,16 @@ ``OBJC``, ``OBJCXX``, ``CUDA``, and ``HIP`` are supported. -It makes no sense to use this module when :policy:`CMP0083` is set to ``OLD``, -so the command will return an error in this case. See policy :policy:`CMP0083` -for details. + .. note:: + + To use ``check_pie_supported()``, policy :policy:`CMP0083` must be set to + ``NEW``; otherwise, a fatal error will occur. Variables ^^^^^^^^^ -For each language checked, two boolean cache variables are defined. +For each language checked, the ``check_pie_supported()`` function defines two +boolean cache variables: ``CMAKE_<lang>_LINK_PIE_SUPPORTED`` Set to true if ``PIE`` is supported by the linker and false otherwise. @@ -53,21 +61,44 @@ Examples ^^^^^^^^ +To enable PIE on an executable target at link time as well, include this module +and call ``check_pie_supported()`` before setting the +``POSITION_INDEPENDENT_CODE`` target property. This will determine whether the +linker for each checked language supports PIE-related link options. For +example: + .. code-block:: cmake + add_executable(foo ...) + + include(CheckPIESupported) check_pie_supported() set_property(TARGET foo PROPERTY POSITION_INDEPENDENT_CODE TRUE) +Since not all linkers require or support PIE-related link options (for example, +``MSVC``), retrieving any error messages might be useful for logging purposes: + .. code-block:: cmake - # Retrieve any error message. + add_executable(foo ...) + + include(CheckPIESupported) check_pie_supported(OUTPUT_VARIABLE output LANGUAGES C) set_property(TARGET foo PROPERTY POSITION_INDEPENDENT_CODE TRUE) if(NOT CMAKE_C_LINK_PIE_SUPPORTED) - message(WARNING "PIE is not supported at link time: ${output}.\n" + message(WARNING "PIE is not supported at link time:\n${output}" "PIE link options will not be passed to linker.") endif() +Setting the ``POSITION_INDEPENDENT_CODE`` target property on an executable +without this module will set PIE-related compile options but not PIE-related +link options, which might not be sufficient in certain cases: + +.. code-block:: cmake + + add_executable(foo ...) + set_property(TARGET foo PROPERTY POSITION_INDEPENDENT_CODE TRUE) + #]=======================================================================]
diff --git a/Modules/FindHDF5.cmake b/Modules/FindHDF5.cmake index 2f0c3c1..89058ca 100644 --- a/Modules/FindHDF5.cmake +++ b/Modules/FindHDF5.cmake
@@ -218,6 +218,13 @@ set(HDF5_Fortran_COMPILER_NAMES h5fc h5pfc) endif() +# Prefer h5hl<LANG> compilers if HDF5_FIND_HL is enabled +if(HDF5_FIND_HL) + list(PREPEND HDF5_C_COMPILER_NAMES h5hlcc) + list(PREPEND HDF5_CXX_COMPILER_NAMES h5hlc++) + list(PREPEND HDF5_Fortran_COMPILER_NAMES h5hlfc) +endif() + # Test first if the current compilers automatically wrap HDF5 function(_HDF5_test_regular_compiler_C success version is_parallel) if(NOT ${success} OR
diff --git a/Modules/FindSQLite3.cmake b/Modules/FindSQLite3.cmake index fcacbef..60eb541 100644 --- a/Modules/FindSQLite3.cmake +++ b/Modules/FindSQLite3.cmake
@@ -66,7 +66,7 @@ include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SQLite3 - REQUIRED_VARS SQLite3_INCLUDE_DIR SQLite3_LIBRARY + REQUIRED_VARS SQLite3_LIBRARY SQLite3_INCLUDE_DIR VERSION_VAR SQLite3_VERSION) # Create the imported target
diff --git a/Modules/Use_wxWindows.cmake b/Modules/Use_wxWindows.cmake index 5f46784..e3c1870 100644 --- a/Modules/Use_wxWindows.cmake +++ b/Modules/Use_wxWindows.cmake
@@ -7,39 +7,41 @@ .. deprecated:: 2.8.10 - Use ``find_package(wxWidgets)`` and ``include(${wxWidgets_USE_FILE})`` instead. + Use :module:`find_package(wxWidgets) <FindwxWidgets>` instead. -This convenience include finds if wxWindows is installed and set the -appropriate libs, incdirs, flags etc. author Jan Woetzel <jw -at- -mip.informatik.uni-kiel.de> (07/2003) +This convenience include finds if wxWindows library is installed and sets the +appropriate libraries, include directories, flags, etc. -USAGE: +Examples +^^^^^^^^ -:: +Include ``Use_wxWindows`` module in project's ``CMakeLists.txt``: - just include Use_wxWindows.cmake - in your projects CMakeLists.txt +.. code-block:: cmake -include( ${CMAKE_MODULE_PATH}/Use_wxWindows.cmake) + # CMakeLists.txt + include(Use_wxWindows) -:: +When the GL support is required, set ``WXWINDOWS_USE_GL`` *before* including +this module: - if you are sure you need GL then +.. code-block:: cmake -set(WXWINDOWS_USE_GL 1) - -:: - - *before* you include this file. + set(WXWINDOWS_USE_GL ON) + include(Use_wxWindows) #]=======================================================================] +# Author: Jan Woetzel <jw -at- mip.informatik.uni-kiel.de> (07/2003) + # ----------------------------------------------------- # 16.Feb.2004: changed INCLUDE to FIND_PACKAGE to read from users own non-system CMAKE_MODULE_PATH (Jan Woetzel JW) # 07/2006: rewrite as FindwxWidgets.cmake, kept for backward compatibility JW -message(STATUS "Use_wxWindows.cmake is DEPRECATED. \n" -"Please use find_package(wxWidgets) and include(${wxWidgets_USE_FILE}) instead. (JW)") - +message( + DEPRECATION + "Use_wxWindows module is DEPRECATED.\n" + "Please use find_package(wxWidgets) instead. (JW)" +) # ------------------------
diff --git a/Modules/UsewxWidgets.cmake b/Modules/UsewxWidgets.cmake index 7152afe..3751025 100644 --- a/Modules/UsewxWidgets.cmake +++ b/Modules/UsewxWidgets.cmake
@@ -5,39 +5,46 @@ UsewxWidgets ------------ -Convenience include for using wxWidgets library. +This module calls :command:`include_directories` and +:command:`link_directories`, sets compile definitions for the current directory +and appends some compile flags to use wxWidgets library after calling the +:module:`find_package(wxWidgets) <FindwxWidgets>`. -Determines if wxWidgets was FOUND and sets the appropriate libs, -incdirs, flags, etc. INCLUDE_DIRECTORIES and LINK_DIRECTORIES are -called. +Examples +^^^^^^^^ -USAGE +Include ``UsewxWidgets`` module in project's ``CMakeLists.txt``: .. code-block:: cmake - # Note that for MinGW users the order of libs is important! + # Note that for MinGW users the order of libraries is important. find_package(wxWidgets REQUIRED net gl core base) + + # Above also sets the wxWidgets_USE_FILE variable that points to this module. include(${wxWidgets_USE_FILE}) - # and for each of your dependent executable/library targets: - target_link_libraries(<YourTarget> ${wxWidgets_LIBRARIES}) + # Link wxWidgets libraries for each dependent executable/library target. + target_link_libraries(<ProjectTarget> ${wxWidgets_LIBRARIES}) +As of CMake 3.27, a better approach is to link only the +:module:`wxWidgets::wxWidgets <FindwxWidgets>` ``IMPORTED`` target to specific +targets that require it, rather than including this module. Imported targets +provide better control of the package usage properties, such as include +directories and compile flags, by applying them only to the targets they are +linked to, avoiding unnecessary propagation to all targets in the current +directory. -DEPRECATED +.. code-block:: cmake -:: + # CMakeLists.txt + find_package(wxWidgets) - LINK_LIBRARIES is not called in favor of adding dependencies per target. - - - -AUTHOR - -:: - - Jan Woetzel <jw -at- mip.informatik.uni-kiel.de> + # Link the imported target for each dependent executable/library target. + target_link_libraries(<ProjectTarget> wxWidgets::wxWidgets) #]=======================================================================] +# Author: Jan Woetzel <jw -at- mip.informatik.uni-kiel.de> + if (wxWidgets_FOUND) if (wxWidgets_INCLUDE_DIRS) if(wxWidgets_INCLUDE_DIRS_NO_SYSTEM)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 30dfb3b..45be758 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 4) set(CMake_VERSION_MINOR 0) -set(CMake_VERSION_PATCH 20250308) +set(CMake_VERSION_PATCH 20250311) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0)
diff --git a/Source/cmCurl.cxx b/Source/cmCurl.cxx index 1b8c765..4d8d6a5 100644 --- a/Source/cmCurl.cxx +++ b/Source/cmCurl.cxx
@@ -179,7 +179,7 @@ std::string const& netrc_file) { std::string e; - CURL_NETRC_OPTION curl_netrc_level = CURL_NETRC_LAST; + long curl_netrc_level = CURL_NETRC_LAST; ::CURLcode res; if (!netrc_level.empty()) {
diff --git a/Source/cmPathResolver.cxx b/Source/cmPathResolver.cxx index 71ce7b4..32f3ef7 100644 --- a/Source/cmPathResolver.cxx +++ b/Source/cmPathResolver.cxx
@@ -492,6 +492,14 @@ static constexpr Options::Symlinks Symlinks = Options::Symlinks::None; static constexpr Options::Existence Existence = Options::Existence::Agnostic; }; +struct CasePath +{ +#if defined(_WIN32) || defined(__APPLE__) + static constexpr Options::ActualCase ActualCase = Options::ActualCase::Yes; +#endif + static constexpr Options::Symlinks Symlinks = Options::Symlinks::None; + static constexpr Options::Existence Existence = Options::Existence::Agnostic; +}; struct RealPath { #if defined(_WIN32) || defined(__APPLE__) @@ -512,6 +520,8 @@ #if defined(__SUNPRO_CC) constexpr Options::Symlinks NaivePath::Symlinks; constexpr Options::Existence NaivePath::Existence; +constexpr Options::Symlinks CasePath::Symlinks; +constexpr Options::Existence CasePath::Existence; constexpr Options::Symlinks RealPath::Symlinks; constexpr Options::Existence RealPath::Existence; constexpr Options::Symlinks LogicalPath::Symlinks; @@ -535,6 +545,7 @@ template class Resolver<Policies::LogicalPath>; template class Resolver<Policies::RealPath>; +template class Resolver<Policies::CasePath>; template class Resolver<Policies::NaivePath>; }
diff --git a/Source/cmPathResolver.h b/Source/cmPathResolver.h index 1f96e0a..e2399ba 100644 --- a/Source/cmPathResolver.h +++ b/Source/cmPathResolver.h
@@ -78,13 +78,18 @@ /** Normalizes paths while resolving symlinks only when followed by '..' components. Does not require paths to exist, but - reads on-disk case of paths that do exist (on Windows). */ + reads on-disk case of paths that do exist (on Windows and macOS). */ struct LogicalPath; -/** Normalizes paths while resolving all symlinks. - Requires paths to exist, and reads their on-disk case (on Windows). */ +/** Normalizes paths while resolving all symlinks. Requires paths to exist, + and reads their on-disk case (on Windows and macOS). */ struct RealPath; +/** Normalizes paths while assuming components followed by '..' + components are not symlinks. Does not require paths to exist, but + reads on-disk case of paths that do exist (on Windows and macOS). */ +struct CasePath; + /** Normalizes paths in memory without disk access. Assumes components followed by '..' components are not symlinks. */ struct NaivePath; @@ -94,6 +99,7 @@ extern template class Resolver<Policies::LogicalPath>; extern template class Resolver<Policies::RealPath>; +extern template class Resolver<Policies::CasePath>; extern template class Resolver<Policies::NaivePath>; }
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 45e59ec..0755148 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx
@@ -1305,23 +1305,77 @@ return filepath_.empty(); } +std::string cmSystemTools::GetRealPathResolvingWindowsSubst( + std::string const& path, std::string* errorMessage) +{ +#ifdef _WIN32 + // uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found + std::string resolved_path; + uv_fs_t req; + int err = uv_fs_realpath(nullptr, &req, path.c_str(), nullptr); + if (!err) { + resolved_path = std::string((char*)req.ptr); + cmSystemTools::ConvertToUnixSlashes(resolved_path); + } else if (err == UV_ENOSYS) { + resolved_path = cmsys::SystemTools::GetRealPath(path, errorMessage); + } else if (errorMessage) { + cmsys::Status status = + cmsys::Status::Windows(uv_fs_get_system_error(&req)); + *errorMessage = status.GetString(); + resolved_path.clear(); + } else { + resolved_path = path; + } + // Normalize to upper-case drive letter as cm::PathResolver does. + if (resolved_path.size() > 1 && resolved_path[1] == ':') { + resolved_path[0] = toupper(resolved_path[0]); + } + return resolved_path; +#else + return cmsys::SystemTools::GetRealPath(path, errorMessage); +#endif +} + std::string cmSystemTools::GetRealPath(std::string const& path, std::string* errorMessage) { #ifdef _WIN32 - std::string resolved_path; - using namespace cm::PathResolver; - // IWYU pragma: no_forward_declare cm::PathResolver::Policies::RealPath - static Resolver<Policies::RealPath> const resolver(RealOS); - cmsys::Status status = resolver.Resolve(path, resolved_path); - if (!status) { - if (errorMessage) { - *errorMessage = status.GetString(); - resolved_path.clear(); - } else { - resolved_path = path; + std::string resolved_path = + cmSystemTools::GetRealPathResolvingWindowsSubst(path, errorMessage); + + // If the original path used a subst drive and the real path starts + // with the substitution, restore the subst drive prefix. This may + // incorrectly restore a subst drive if the underlying drive was + // encountered via an absolute symlink, but this is an acceptable + // limitation to otherwise preserve susbt drives. + if (resolved_path.size() >= 2 && resolved_path[1] == ':' && + path.size() >= 2 && path[1] == ':' && + toupper(resolved_path[0]) != toupper(path[0])) { + // FIXME: Add thread_local or mutex if we use threads. + static std::map<char, std::string> substMap; + char const drive = static_cast<char>(toupper(path[0])); + std::string maybe_subst = cmStrCat(drive, ":/"); + auto smi = substMap.find(drive); + if (smi == substMap.end()) { + smi = substMap + .emplace( + drive, + cmSystemTools::GetRealPathResolvingWindowsSubst(maybe_subst)) + .first; + } + std::string const& resolved_subst = smi->second; + std::string::size_type const ns = resolved_subst.size(); + if (ns > 0) { + std::string::size_type const np = resolved_path.size(); + if (ns == np && resolved_path == resolved_subst) { + resolved_path = maybe_subst; + } else if (ns > 0 && ns < np && resolved_path[ns] == '/' && + resolved_path.compare(0, ns, resolved_subst) == 0) { + resolved_path.replace(0, ns + 1, maybe_subst); + } } } + return resolved_path; #else return cmsys::SystemTools::GetRealPath(path, errorMessage); @@ -1991,8 +2045,13 @@ std::string cmSystemTools::ToNormalizedPathOnDisk(std::string p) { using namespace cm::PathResolver; +#ifdef _WIN32 + // IWYU pragma: no_forward_declare cm::PathResolver::Policies::CasePath + static Resolver<Policies::CasePath> const resolver(RealOS); +#else // IWYU pragma: no_forward_declare cm::PathResolver::Policies::LogicalPath static Resolver<Policies::LogicalPath> const resolver(RealOS); +#endif resolver.Resolve(std::move(p), p); return p; }
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index f7ac20b..16c5caa 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h
@@ -441,9 +441,11 @@ /** Convert an input path to an absolute path with no '/..' components. Backslashes in the input path are converted to forward slashes. Relative paths are interpreted w.r.t. GetLogicalWorkingDirectory. - On Windows, the on-disk capitalization is loaded for existing paths. This is similar to 'realpath', but preserves symlinks that are - not erased by '../' components. */ + not erased by '../' components. + + On Windows and macOS, the on-disk capitalization is loaded for + existing paths. */ static std::string ToNormalizedPathOnDisk(std::string p); #ifndef CMAKE_BOOTSTRAP @@ -649,6 +651,12 @@ static std::string GetComspec(); #endif + /** Get the real path for a given path, removing all symlinks. + This variant of GetRealPath also works on Windows but will + resolve subst drives too. */ + static std::string GetRealPathResolvingWindowsSubst( + std::string const& path, std::string* errorMessage = nullptr); + /** Get the real path for a given path, removing all symlinks. */ static std::string GetRealPath(std::string const& path, std::string* errorMessage = nullptr);
diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx index d921a89..35913c7 100644 --- a/Source/cmTimestamp.cxx +++ b/Source/cmTimestamp.cxx
@@ -63,7 +63,8 @@ std::string const& formatString, bool utcFlag) const { - std::string real_path = cmSystemTools::GetRealPath(path); + std::string real_path = + cmSystemTools::GetRealPathResolvingWindowsSubst(path); if (!cmsys::SystemTools::FileExists(real_path)) { return std::string();
diff --git a/Tests/CMakeLib/testPathResolver.cxx b/Tests/CMakeLib/testPathResolver.cxx index d79afd9..d5cee74 100644 --- a/Tests/CMakeLib/testPathResolver.cxx +++ b/Tests/CMakeLib/testPathResolver.cxx
@@ -24,6 +24,7 @@ // IWYU pragma: no_forward_declare cm::PathResolver::Policies::LogicalPath // IWYU pragma: no_forward_declare cm::PathResolver::Policies::NaivePath +// IWYU pragma: no_forward_declare cm::PathResolver::Policies::CasePath // IWYU pragma: no_forward_declare cm::PathResolver::Policies::RealPath namespace { @@ -213,6 +214,22 @@ }); { + Resolver<Policies::CasePath> const r(os); + EXPECT_RESOLVE("/link-a", "/link-a"); + EXPECT_RESOLVE("/link-a-excess", "/link-a-excess"); + EXPECT_RESOLVE("/link-a-excess/b", "/link-a-excess/b"); + EXPECT_RESOLVE("/link-broken", "/link-broken"); + EXPECT_RESOLVE("/link-a/../missing", "/missing"); + EXPECT_RESOLVE("/a/b/link-c", "/a/b/link-c"); + EXPECT_RESOLVE("/a/link-b/c", "/a/link-b/c"); + EXPECT_RESOLVE("/a/link-b/link-c/..", "/a/link-b"); + EXPECT_RESOLVE("/a/b/c/link-..|..", "/a/b/c/link-..|.."); + EXPECT_RESOLVE("/a/b/c/link-..|../link-b", "/a/b/c/link-..|../link-b"); + EXPECT_RESOLVE("/a/link-|1|2/3", "/a/link-|1|2/3"); + EXPECT_RESOLVE("/a/link-|1|2/../2/3", "/a/2/3"); + } + + { Resolver<Policies::LogicalPath> const r(os); EXPECT_RESOLVE("/link-a", "/link-a"); EXPECT_RESOLVE("/link-a-excess", "/link-a-excess"); @@ -264,6 +281,16 @@ }); { + Resolver<Policies::CasePath> const r(os); + EXPECT_RESOLVE("/mIxEd/MiSsInG", "/MiXeD/MiSsInG"); + EXPECT_RESOLVE("/mIxEd/link-MiXeD", "/MiXeD/LiNk-MiXeD"); + EXPECT_RESOLVE("/mIxEd/link-c-MiXeD", "/MiXeD/LiNk-C-MiXeD"); + EXPECT_RESOLVE("/upper/mIsSiNg", "/UPPER/mIsSiNg"); + EXPECT_RESOLVE("/upper/link-upper", "/UPPER/LINK-UPPER"); + EXPECT_RESOLVE("/upper/link-c-upper", "/UPPER/LINK-C-UPPER"); + } + + { Resolver<Policies::LogicalPath> const r(os); EXPECT_RESOLVE("/mIxEd/MiSsInG", "/MiXeD/MiSsInG"); EXPECT_RESOLVE("/mIxEd/link-MiXeD", "/MiXeD/LiNk-MiXeD"); @@ -302,6 +329,16 @@ EXPECT_RESOLVE("C:/..", "C:/"); EXPECT_RESOLVE("c:/../", "c:/"); } + { + Resolver<Policies::CasePath> const r(os); + EXPECT_RESOLVE("c:/", "C:/"); + EXPECT_RESOLVE("C:/", "C:/"); + EXPECT_RESOLVE("c://", "C:/"); + EXPECT_RESOLVE("C:/.", "C:/"); + EXPECT_RESOLVE("c:/./", "C:/"); + EXPECT_RESOLVE("C:/..", "C:/"); + EXPECT_RESOLVE("c:/../", "C:/"); + } os.SetPaths({ { "c:/", { {}, {} } }, { "//host/", { {}, {} } }, @@ -361,6 +398,16 @@ }); { + Resolver<Policies::CasePath> const r(os); + EXPECT_RESOLVE("c:/mIxEd/MiSsInG", "C:/MiXeD/MiSsInG"); + EXPECT_RESOLVE("c:/mIxEd/link-MiXeD", "C:/MiXeD/LiNk-MiXeD"); + EXPECT_RESOLVE("c:/mIxEd/link-c-MiXeD", "C:/MiXeD/LiNk-C-MiXeD"); + EXPECT_RESOLVE("c:/upper/mIsSiNg", "C:/UPPER/mIsSiNg"); + EXPECT_RESOLVE("c:/upper/link-upper", "C:/UPPER/LINK-UPPER"); + EXPECT_RESOLVE("c:/upper/link-c-upper", "C:/UPPER/LINK-C-UPPER"); + } + + { Resolver<Policies::LogicalPath> const r(os); EXPECT_RESOLVE("c:/mIxEd/MiSsInG", "C:/MiXeD/MiSsInG"); EXPECT_RESOLVE("c:/mIxEd/link-MiXeD", "C:/MiXeD/LiNk-MiXeD"); @@ -441,6 +488,27 @@ EXPECT_RESOLVE("E:.", "E:/"); EXPECT_RESOLVE("E:..", "E:/"); } + { + Resolver<Policies::CasePath> const r(os); + EXPECT_RESOLVE("c:", "C:/cwd"); + EXPECT_RESOLVE("c:.", "C:/cwd"); + EXPECT_RESOLVE("c:..", "C:/"); + EXPECT_RESOLVE("C:", "C:/cwd"); + EXPECT_RESOLVE("C:.", "C:/cwd"); + EXPECT_RESOLVE("C:..", "C:/"); + EXPECT_RESOLVE("d:", "D:/cwd-d"); + EXPECT_RESOLVE("d:.", "D:/cwd-d"); + EXPECT_RESOLVE("d:..", "D:/"); + EXPECT_RESOLVE("D:", "D:/cwd-d"); + EXPECT_RESOLVE("D:.", "D:/cwd-d"); + EXPECT_RESOLVE("D:..", "D:/"); + EXPECT_RESOLVE("e:", "E:/"); + EXPECT_RESOLVE("e:.", "E:/"); + EXPECT_RESOLVE("e:..", "E:/"); + EXPECT_RESOLVE("E:", "E:/"); + EXPECT_RESOLVE("E:.", "E:/"); + EXPECT_RESOLVE("E:..", "E:/"); + } os.SetPaths({ { "c:/", { {}, {} } }, { "c:/cwd", { {}, {} } }, @@ -496,6 +564,13 @@ EXPECT_RESOLVE("link-to-host-share/..", "//host/"); EXPECT_RESOLVE("link-to-host-share/../missing", "//host/missing"); } + + { + Resolver<Policies::CasePath> const r(os); + EXPECT_RESOLVE("link-to-host-share", "C:/cwd/link-to-host-share"); + EXPECT_RESOLVE("link-to-host-share/..", "C:/cwd"); + EXPECT_RESOLVE("link-to-host-share/../missing", "C:/cwd/missing"); + } return true; } #endif
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/CheckCompilerLinkerId.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/CheckCompilerLinkerId.cmake new file mode 100644 index 0000000..a6a56ca --- /dev/null +++ b/Tests/RunCMake/ParseImplicitLinkInfo/CheckCompilerLinkerId.cmake
@@ -0,0 +1,5 @@ +enable_language(C) + +if(NOT CMAKE_C_COMPILER_LINKER OR NOT CMAKE_C_COMPILER_LINKER_ID) + message(FATAL_ERROR "Failed to determine Linker.") +endif()
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake index d50d403..df03f15 100644 --- a/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake +++ b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
@@ -39,3 +39,7 @@ ) endif() endif() + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES Clang) + run_cmake_with_options(CheckCompilerLinkerId "-DCMAKE_C_FLAGS=-fdiagnostics-color=always") +endif()