From e4b793c614f2a499d4d235ec2267ad6bff56a697 Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Tue, 20 Apr 2021 18:46:26 +0200 Subject: 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. --- Help/command/cmake_path.rst | 3 +- Help/command/file.rst | 20 +++++++++--- Help/release/dev/file-REAL_PATH-EXPAND_TILDE.rst | 5 +++ Source/cmFileCommand.cxx | 23 ++++++++++++-- Tests/RunCMake/file/REAL_PATH.cmake | 40 ++++++++++++++++++------ Tests/RunCMake/file/RunCMakeTest.cmake | 7 +++-- 6 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 Help/release/dev/file-REAL_PATH-EXPAND_TILDE.rst diff --git a/Help/command/cmake_path.rst b/Help/command/cmake_path.rst index a8999f3..193180d 100644 --- a/Help/command/cmake_path.rst +++ b/Help/command/cmake_path.rst @@ -687,7 +687,8 @@ When the ``NORMALIZE`` option is specified, the path is :ref:`normalized ` 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 diff --git a/Help/command/file.rst b/Help/command/file.rst index 62642cf..e170526 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -50,7 +50,7 @@ Synopsis file(`CHMOD_RECURSE`_ ... ... PERMISSIONS ... [...]) `Path Conversion`_ - file(`REAL_PATH`_ [BASE_DIRECTORY ]) + file(`REAL_PATH`_ [BASE_DIRECTORY ] [EXPAND_TILDE]) file(`RELATIVE_PATH`_ ) file({`TO_CMAKE_PATH`_ | `TO_NATIVE_PATH`_} ) @@ -924,16 +924,26 @@ Path Conversion .. code-block:: cmake - file(REAL_PATH [BASE_DIRECTORY ]) + file(REAL_PATH [BASE_DIRECTORY ] [EXPAND_TILDE]) .. versionadded:: 3.19 Compute the absolute path to an existing file or directory with symlinks resolved. -If the provided ```` is a relative path, it is evaluated relative to the -given base directory ````. If no base directory is provided, the default -base directory will be :variable:`CMAKE_CURRENT_SOURCE_DIR`. +``BASE_DIRECTORY `` + If the provided ```` is a relative path, it is evaluated relative to the + given base directory ````. If no base directory is provided, the default + base directory will be :variable:`CMAKE_CURRENT_SOURCE_DIR`. + +``EXPAND_TILDE`` + .. versionadded:: 3.21 + + If the ```` 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: diff --git a/Help/release/dev/file-REAL_PATH-EXPAND_TILDE.rst b/Help/release/dev/file-REAL_PATH-EXPAND_TILDE.rst new file mode 100644 index 0000000..cdf1efa --- /dev/null +++ b/Help/release/dev/file-REAL_PATH-EXPAND_TILDE.rst @@ -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. diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 05ebcca..a943258 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -1246,9 +1246,12 @@ bool HandleRealPathCommand(std::vector const& args, struct Arguments { std::string BaseDirectory; + bool ExpandTilde = false; }; - static auto const parser = cmArgumentParser{}.Bind( - "BASE_DIRECTORY"_s, &Arguments::BaseDirectory); + static auto const parser = + cmArgumentParser{} + .Bind("BASE_DIRECTORY"_s, &Arguments::BaseDirectory) + .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde); std::vector unparsedArguments; std::vector keywordsMissingValue; @@ -1270,7 +1273,21 @@ bool HandleRealPathCommand(std::vector 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()); diff --git a/Tests/RunCMake/file/REAL_PATH.cmake b/Tests/RunCMake/file/REAL_PATH.cmake index be25706..0b5d3c0 100644 --- a/Tests/RunCMake/file/REAL_PATH.cmake +++ b/Tests/RunCMake/file/REAL_PATH.cmake @@ -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") - message(SEND_ERROR "real path is \"${real_path}\", should be \"${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() + + +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.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\"") +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() diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index b4ea9ba..6d1109a 100644 --- a/Tests/RunCMake/file/RunCMakeTest.cmake +++ b/Tests/RunCMake/file/RunCMakeTest.cmake @@ -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( -- cgit v0.12