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()