From be36266dabee16d17a4f2e71fa9d05f8ef4769ce Mon Sep 17 00:00:00 2001 From: Marc Chevrier Date: Wed, 26 Aug 2020 14:33:18 +0200 Subject: file(): Add REAL_PATH sub-command --- Help/command/file.rst | 14 +++++++ Help/command/get_filename_component.rst | 5 ++- Help/release/dev/file-REAL_PATH.rst | 5 +++ Source/cmFileCommand.cxx | 46 ++++++++++++++++++++++ .../RunCMake/file/REAL_PATH-no-base-dir-result.txt | 1 + .../RunCMake/file/REAL_PATH-no-base-dir-stderr.txt | 2 + Tests/RunCMake/file/REAL_PATH-no-base-dir.cmake | 2 + .../file/REAL_PATH-unexpected-arg-result.txt | 1 + .../file/REAL_PATH-unexpected-arg-stderr.txt | 2 + Tests/RunCMake/file/REAL_PATH-unexpected-arg.cmake | 2 + Tests/RunCMake/file/REAL_PATH.cmake | 14 +++++++ Tests/RunCMake/file/RunCMakeTest.cmake | 3 ++ 12 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 Help/release/dev/file-REAL_PATH.rst create mode 100644 Tests/RunCMake/file/REAL_PATH-no-base-dir-result.txt create mode 100644 Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt create mode 100644 Tests/RunCMake/file/REAL_PATH-no-base-dir.cmake create mode 100644 Tests/RunCMake/file/REAL_PATH-unexpected-arg-result.txt create mode 100644 Tests/RunCMake/file/REAL_PATH-unexpected-arg-stderr.txt create mode 100644 Tests/RunCMake/file/REAL_PATH-unexpected-arg.cmake create mode 100644 Tests/RunCMake/file/REAL_PATH.cmake diff --git a/Help/command/file.rst b/Help/command/file.rst index f4a817e..d77ad2d 100644 --- a/Help/command/file.rst +++ b/Help/command/file.rst @@ -49,6 +49,7 @@ Synopsis file(`CHMOD_RECURSE`_ ... ... PERMISSIONS ... [...]) `Path Conversion`_ + file(`REAL_PATH`_ [BASE_DIRECTORY ]) file(`RELATIVE_PATH`_ ) file({`TO_CMAKE_PATH`_ | `TO_NATIVE_PATH`_} ) @@ -806,6 +807,19 @@ the ``..`` recursively. Path Conversion ^^^^^^^^^^^^^^^ +.. _REAL_PATH: + +.. code-block:: cmake + + file(REAL_PATH [BASE_DIRECTORY ]) + +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`. + .. _RELATIVE_PATH: .. code-block:: cmake diff --git a/Help/command/get_filename_component.rst b/Help/command/get_filename_component.rst index 9d33a0a..e8c68b2 100644 --- a/Help/command/get_filename_component.rst +++ b/Help/command/get_filename_component.rst @@ -46,8 +46,9 @@ cache. .. note:: - All previous sub-commands, except ``REALPATH``, has been superseded by - :command:`cmake_path` command. + All previous sub-commands has been superseded by + :command:`cmake_path` command, except ``REALPATH`` now offered by + :ref:`file(REAL_PATH) ` command. .. code-block:: cmake diff --git a/Help/release/dev/file-REAL_PATH.rst b/Help/release/dev/file-REAL_PATH.rst new file mode 100644 index 0000000..32768ca --- /dev/null +++ b/Help/release/dev/file-REAL_PATH.rst @@ -0,0 +1,5 @@ +file-REAL_PATH +-------------- + +* The :command:`file` gained sub-command `REAL_PATH` to compute a path with + symlinks resolved. diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index cdb1492..84639a7 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -28,6 +28,7 @@ #include "cmAlgorithms.h" #include "cmArgumentParser.h" +#include "cmCMakePath.h" #include "cmCryptoHash.h" #include "cmExecutionStatus.h" #include "cmFSPermissions.h" @@ -1234,6 +1235,50 @@ bool HandleInstallCommand(std::vector const& args, return installer.Run(args); } +bool HandleRealPathCommand(std::vector const& args, + cmExecutionStatus& status) +{ + if (args.size() < 3) { + status.SetError("REAL_PATH requires a path and an output variable"); + return false; + } + + struct Arguments + { + std::string BaseDirectory; + }; + static auto const parser = cmArgumentParser{}.Bind( + "BASE_DIRECTORY"_s, &Arguments::BaseDirectory); + + std::vector unparsedArguments; + std::vector keywordsMissingValue; + std::vector parsedKeywords; + auto arguments = + parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments, + &keywordsMissingValue, &parsedKeywords); + + 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 (parsedKeywords.empty()) { + arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory(); + } + + cmCMakePath path(args[1]); + path = path.Absolute(arguments.BaseDirectory).Normal(); + auto realPath = cmSystemTools::GetRealPath(path.GenericString()); + + status.GetMakefile().AddDefinition(args[2], realPath); + + return true; +} + bool HandleRelativePathCommand(std::vector const& args, cmExecutionStatus& status) { @@ -3360,6 +3405,7 @@ bool cmFileCommand(std::vector const& args, { "RPATH_CHECK"_s, HandleRPathCheckCommand }, { "RPATH_REMOVE"_s, HandleRPathRemoveCommand }, { "READ_ELF"_s, HandleReadElfCommand }, + { "REAL_PATH"_s, HandleRealPathCommand }, { "RELATIVE_PATH"_s, HandleRelativePathCommand }, { "TO_CMAKE_PATH"_s, HandleCMakePathCommand }, { "TO_NATIVE_PATH"_s, HandleNativePathCommand }, diff --git a/Tests/RunCMake/file/REAL_PATH-no-base-dir-result.txt b/Tests/RunCMake/file/REAL_PATH-no-base-dir-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-no-base-dir-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt b/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt new file mode 100644 index 0000000..7c58aeb --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at REAL_PATH-no-base-dir.cmake:[0-9]+ \(file\): + file BASE_DIRECTORY requires a value diff --git a/Tests/RunCMake/file/REAL_PATH-no-base-dir.cmake b/Tests/RunCMake/file/REAL_PATH-no-base-dir.cmake new file mode 100644 index 0000000..132aee6 --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-no-base-dir.cmake @@ -0,0 +1,2 @@ + +file(REAL_PATH "some-path" real_path BASE_DIRECTORY) diff --git a/Tests/RunCMake/file/REAL_PATH-unexpected-arg-result.txt b/Tests/RunCMake/file/REAL_PATH-unexpected-arg-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-unexpected-arg-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/file/REAL_PATH-unexpected-arg-stderr.txt b/Tests/RunCMake/file/REAL_PATH-unexpected-arg-stderr.txt new file mode 100644 index 0000000..301db75 --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-unexpected-arg-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at REAL_PATH-unexpected-arg.cmake:[0-9]+ \(file\): + file REAL_PATH called with unexpected arguments diff --git a/Tests/RunCMake/file/REAL_PATH-unexpected-arg.cmake b/Tests/RunCMake/file/REAL_PATH-unexpected-arg.cmake new file mode 100644 index 0000000..144f30b --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH-unexpected-arg.cmake @@ -0,0 +1,2 @@ + +file(REAL_PATH "some-path" real_path extra_arg) diff --git a/Tests/RunCMake/file/REAL_PATH.cmake b/Tests/RunCMake/file/REAL_PATH.cmake new file mode 100644 index 0000000..be25706 --- /dev/null +++ b/Tests/RunCMake/file/REAL_PATH.cmake @@ -0,0 +1,14 @@ + +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\"") +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() diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake index 8d84943..22813eb 100644 --- a/Tests/RunCMake/file/RunCMakeTest.cmake +++ b/Tests/RunCMake/file/RunCMakeTest.cmake @@ -72,6 +72,9 @@ 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() if(RunCMake_GENERATOR MATCHES "Ninja") -- cgit v0.12