file(REAL_PATH): add option EXPAND_TILDE

This option enables the replacement of any leading tilde with the path
to the user's home directory.
This commit is contained in:
Marc Chevrier 2021-04-20 18:46:26 +02:00 committed by Craig Scott
parent 63ffe21036
commit e4b793c614
6 changed files with 77 additions and 21 deletions

View File

@ -687,7 +687,8 @@ When the ``NORMALIZE`` option is specified, the path is :ref:`normalized
<Normalization>` after the path computation.
Because ``cmake_path()`` does not access the filesystem, symbolic links are
not resolved. To compute a real path with symbolic links resolved, use the
not resolved and any leading tilde is not expanded. To compute a real path
with symbolic links resolved and leading tildes expanded, use the
:command:`file(REAL_PATH)` command instead.
Native Conversion

View File

@ -50,7 +50,7 @@ Synopsis
file(`CHMOD_RECURSE`_ <files>... <directories>... PERMISSIONS <permissions>... [...])
`Path Conversion`_
file(`REAL_PATH`_ <path> <out-var> [BASE_DIRECTORY <dir>])
file(`REAL_PATH`_ <path> <out-var> [BASE_DIRECTORY <dir>] [EXPAND_TILDE])
file(`RELATIVE_PATH`_ <out-var> <directory> <file>)
file({`TO_CMAKE_PATH`_ | `TO_NATIVE_PATH`_} <path> <out-var>)
@ -924,16 +924,26 @@ Path Conversion
.. code-block:: cmake
file(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>])
file(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>] [EXPAND_TILDE])
.. versionadded:: 3.19
Compute the absolute path to an existing file or directory with symlinks
resolved.
If the provided ``<path>`` is a relative path, it is evaluated relative to the
given base directory ``<dir>``. If no base directory is provided, the default
base directory will be :variable:`CMAKE_CURRENT_SOURCE_DIR`.
``BASE_DIRECTORY <dir>``
If the provided ``<path>`` is a relative path, it is evaluated relative to the
given base directory ``<dir>``. If no base directory is provided, the default
base directory will be :variable:`CMAKE_CURRENT_SOURCE_DIR`.
``EXPAND_TILDE``
.. versionadded:: 3.21
If the ``<path>`` is ``~`` or starts with ``~/``, the ``~`` is replaced by
the user's home directory. The path to the home directory is obtained from
environment variables. On Windows, the ``USERPROFILE`` environment variable
is used, falling back to the ``HOME`` environment variable if ``USERPROFILE``
is not defined. On all other platforms, only ``HOME`` is used.
.. _RELATIVE_PATH:

View File

@ -0,0 +1,5 @@
file-REAL_PATH-EXPAND_TILDE
---------------------------
* The :command:`file(REAL_PATH)` command gained the option ``EXPAND_TILDE`` to
replace any leading tilde with the path to the user's home directory.

View File

@ -1246,9 +1246,12 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
struct Arguments
{
std::string BaseDirectory;
bool ExpandTilde = false;
};
static auto const parser = cmArgumentParser<Arguments>{}.Bind(
"BASE_DIRECTORY"_s, &Arguments::BaseDirectory);
static auto const parser =
cmArgumentParser<Arguments>{}
.Bind("BASE_DIRECTORY"_s, &Arguments::BaseDirectory)
.Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde);
std::vector<std::string> unparsedArguments;
std::vector<std::string> keywordsMissingValue;
@ -1270,7 +1273,21 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
}
cmCMakePath path(args[1]);
auto input = args[1];
if (arguments.ExpandTilde && !input.empty()) {
if (input[0] == '~' && (input.length() == 1 || input[1] == '/')) {
std::string home;
if (
#if defined(_WIN32) && !defined(__CYGWIN__)
cmSystemTools::GetEnv("USERPROFILE", home) ||
#endif
cmSystemTools::GetEnv("HOME", home)) {
input.replace(0, 1, home);
}
}
}
cmCMakePath path(input, cmCMakePath::auto_format);
path = path.Absolute(arguments.BaseDirectory).Normal();
auto realPath = cmSystemTools::GetRealPath(path.GenericString());

View File

@ -1,14 +1,36 @@
file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/test.txt")
file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/test.sym")
file(CREATE_LINK "test.txt" "${CMAKE_CURRENT_BINARY_DIR}/test.sym" SYMBOLIC)
if (NOT WIN32 OR CYGWIN)
file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/test.txt")
file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/test.sym")
file(CREATE_LINK "test.txt" "${CMAKE_CURRENT_BINARY_DIR}/test.sym" SYMBOLIC)
file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/test.sym" real_path)
if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/test.txt")
file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/test.sym" real_path)
if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/test.txt")
message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/test.txt\"")
endif()
file(REAL_PATH "test.sym" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/test.txt")
message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/test.txt\"")
endif()
endif()
file(REAL_PATH "test.sym" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/test.txt")
message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/test.txt\"")
If (WIN32)
cmake_path(SET HOME_DIR "$ENV{USERPROFILE}")
if (NOT HOME_DIR)
cmake_path(SET HOME_DIR "$ENV{HOME}")
endif()
else()
set(HOME_DIR "$ENV{HOME}")
endif()
file(REAL_PATH "~" real_path EXPAND_TILDE)
if (NOT real_path STREQUAL "${HOME_DIR}")
message(SEND_ERROR "real path is \"${real_path}\", should be \"${HOME_DIR}\"")
endif()
file(REAL_PATH "~/test.txt" real_path EXPAND_TILDE)
if (NOT real_path STREQUAL "${HOME_DIR}/test.txt")
message(SEND_ERROR "real path is \"${real_path}\", should be \"${HOME_DIR}/test.txt\"")
endif()

View File

@ -96,11 +96,12 @@ if(NOT WIN32 OR CYGWIN)
run_cmake(READ_SYMLINK-noexist)
run_cmake(READ_SYMLINK-notsymlink)
run_cmake(INSTALL-FOLLOW_SYMLINK_CHAIN)
run_cmake(REAL_PATH-unexpected-arg)
run_cmake(REAL_PATH-no-base-dir)
run_cmake(REAL_PATH)
endif()
run_cmake(REAL_PATH-unexpected-arg)
run_cmake(REAL_PATH-no-base-dir)
run_cmake(REAL_PATH)
if(RunCMake_GENERATOR MATCHES "Ninja")
# Detect ninja version so we know what tests can be supported.
execute_process(