Browse Source

file(): Add REAL_PATH sub-command

Marc Chevrier 5 years ago
parent
commit
be36266dab

+ 14 - 0
Help/command/file.rst

@@ -49,6 +49,7 @@ Synopsis
     file(`CHMOD_RECURSE`_ <files>... <directories>... PERMISSIONS <permissions>... [...])
 
   `Path Conversion`_
+    file(`REAL_PATH`_ <path> <out-var> [BASE_DIRECTORY <dir>])
     file(`RELATIVE_PATH`_ <out-var> <directory> <file>)
     file({`TO_CMAKE_PATH`_ | `TO_NATIVE_PATH`_} <path> <out-var>)
 
@@ -806,6 +807,19 @@ the ``<directories>..`` recursively.
 Path Conversion
 ^^^^^^^^^^^^^^^
 
+.. _REAL_PATH:
+
+.. code-block:: cmake
+
+  file(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>])
+
+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`.
+
 .. _RELATIVE_PATH:
 
 .. code-block:: cmake

+ 3 - 2
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) <REAL_PATH>` command.
 
 .. code-block:: cmake
 

+ 5 - 0
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.

+ 46 - 0
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<std::string> const& args,
   return installer.Run(args);
 }
 
+bool HandleRealPathCommand(std::vector<std::string> 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<Arguments>{}.Bind(
+    "BASE_DIRECTORY"_s, &Arguments::BaseDirectory);
+
+  std::vector<std::string> unparsedArguments;
+  std::vector<std::string> keywordsMissingValue;
+  std::vector<std::string> 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<std::string> const& args,
                                cmExecutionStatus& status)
 {
@@ -3360,6 +3405,7 @@ bool cmFileCommand(std::vector<std::string> 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 },

+ 1 - 0
Tests/RunCMake/file/REAL_PATH-no-base-dir-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
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

+ 2 - 0
Tests/RunCMake/file/REAL_PATH-no-base-dir.cmake

@@ -0,0 +1,2 @@
+
+file(REAL_PATH "some-path" real_path BASE_DIRECTORY)

+ 1 - 0
Tests/RunCMake/file/REAL_PATH-unexpected-arg-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
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

+ 2 - 0
Tests/RunCMake/file/REAL_PATH-unexpected-arg.cmake

@@ -0,0 +1,2 @@
+
+file(REAL_PATH "some-path" real_path extra_arg)

+ 14 - 0
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()

+ 3 - 0
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")