Browse Source

file: Add READ_SYMLINK sub-command

Kyle Edwards 7 years ago
parent
commit
98a39be6cf

+ 24 - 0
Help/command/file.rst

@@ -26,6 +26,7 @@ Synopsis
     file(`MAKE_DIRECTORY`_ [<dir>...])
     file({`COPY`_ | `INSTALL`_} <file>... DESTINATION <dir> [...])
     file(`SIZE`_ <filename> <out-var>)
+    file(`READ_SYMLINK`_ <filename> <out-var>)
 
   `Path Conversion`_
     file(`RELATIVE_PATH`_ <out-var> <directory> <file>)
@@ -344,6 +345,29 @@ Determine the file size of the ``<filename>`` and put the result in
 ``<variable>`` variable. Requires that ``<filename>`` is a valid path
 pointing to a file and is readable.
 
+.. _READ_SYMLINK:
+
+.. code-block:: cmake
+
+  file(READ_SYMLINK <filename> <variable>)
+
+Read the symlink at ``<filename>`` and put the result in ``<variable>``.
+Requires that ``<filename>`` is a valid path pointing to a symlink. If
+``<filename>`` does not exist, or is not a symlink, an error is thrown.
+
+Note that this command returns the raw symlink path and does not resolve
+relative symlinks. If you want to resolve the relative symlink yourself, you
+could do something like this:
+
+.. code-block:: cmake
+
+  set(filename "/path/to/foo.sym")
+  file(READ_SYMLINK "${filename}" result)
+  if(NOT IS_ABSOLUTE "${result}")
+    get_filename_component(dir "${filename}" DIRECTORY)
+    set(result "${dir}/${result}")
+  endif()
+
 Path Conversion
 ^^^^^^^^^^^^^^^
 

+ 5 - 0
Help/release/dev/file-read_symlink.rst

@@ -0,0 +1,5 @@
+file-read_symlink
+-----------------
+
+* The :command:`file` command learned a new sub-command, ``READ_SYMLINK``,
+  which can be used to determine the path that a symlink points to.

+ 30 - 0
Source/cmFileCommand.cxx

@@ -180,6 +180,9 @@ bool cmFileCommand::InitialPass(std::vector<std::string> const& args,
   if (subCommand == "SIZE") {
     return this->HandleSizeCommand(args);
   }
+  if (subCommand == "READ_SYMLINK") {
+    return this->HandleReadSymlinkCommand(args);
+  }
 
   std::string e = "does not recognize sub-command " + subCommand;
   this->SetError(e);
@@ -3638,3 +3641,30 @@ bool cmFileCommand::HandleSizeCommand(std::vector<std::string> const& args)
 
   return true;
 }
+
+bool cmFileCommand::HandleReadSymlinkCommand(
+  std::vector<std::string> const& args)
+{
+  if (args.size() != 3) {
+    std::ostringstream e;
+    e << args[0] << " requires a file name and output variable";
+    this->SetError(e.str());
+    return false;
+  }
+
+  const std::string& filename = args[1];
+  const std::string& outputVariable = args[2];
+
+  std::string result;
+  if (!cmSystemTools::ReadSymlink(filename, result)) {
+    std::ostringstream e;
+    e << "READ_SYMLINK requested of path that is not a symlink:\n  "
+      << filename;
+    this->SetError(e.str());
+    return false;
+  }
+
+  this->Makefile->AddDefinition(outputVariable, result.c_str());
+
+  return true;
+}

+ 1 - 0
Source/cmFileCommand.h

@@ -60,6 +60,7 @@ protected:
   bool HandleGenerateCommand(std::vector<std::string> const& args);
   bool HandleLockCommand(std::vector<std::string> const& args);
   bool HandleSizeCommand(std::vector<std::string> const& args);
+  bool HandleReadSymlinkCommand(std::vector<std::string> const& args);
 
 private:
   void AddEvaluationFile(const std::string& inputName,

+ 1 - 0
Tests/RunCMake/file/READ_SYMLINK-noexist-result.txt

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

+ 6 - 0
Tests/RunCMake/file/READ_SYMLINK-noexist-stderr.txt

@@ -0,0 +1,6 @@
+^CMake Error at READ_SYMLINK-noexist\.cmake:[0-9]+ \(file\):
+  file READ_SYMLINK requested of path that is not a symlink:
+
+    .*/Tests/RunCMake/file/READ_SYMLINK-noexist-build/rel\.sym
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 1 - 0
Tests/RunCMake/file/READ_SYMLINK-noexist.cmake

@@ -0,0 +1 @@
+file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/rel.sym" result)

+ 1 - 0
Tests/RunCMake/file/READ_SYMLINK-notsymlink-result.txt

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

+ 6 - 0
Tests/RunCMake/file/READ_SYMLINK-notsymlink-stderr.txt

@@ -0,0 +1,6 @@
+^CMake Error at READ_SYMLINK-notsymlink\.cmake:[0-9]+ \(file\):
+  file READ_SYMLINK requested of path that is not a symlink:
+
+    .*/Tests/RunCMake/file/READ_SYMLINK-notsymlink-build/rel\.sym
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$

+ 2 - 0
Tests/RunCMake/file/READ_SYMLINK-notsymlink.cmake

@@ -0,0 +1,2 @@
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/rel.sym" "")
+file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/rel.sym" result)

+ 13 - 0
Tests/RunCMake/file/READ_SYMLINK.cmake

@@ -0,0 +1,13 @@
+execute_process(COMMAND
+  ${CMAKE_COMMAND} -E create_symlink "test.txt" "${CMAKE_CURRENT_BINARY_DIR}/rel.sym")
+file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/rel.sym" result)
+if(NOT result STREQUAL "test.txt")
+  message(SEND_ERROR "Relative symlink is \"${result}\", should be \"test.txt\"")
+endif()
+
+execute_process(COMMAND
+  ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_BINARY_DIR}/test.txt" "${CMAKE_CURRENT_BINARY_DIR}/abs.sym")
+file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/abs.sym" result)
+if(NOT result MATCHES "^.*/Tests/RunCMake/file/READ_SYMLINK-build/test\\.txt$")
+  message(SEND_ERROR "Absolute symlink is \"${result}\", should be \"*/Tests/RunCMake/file/READ_SYMLINK-build/test.txt\"")
+endif()

+ 3 - 0
Tests/RunCMake/file/RunCMakeTest.cmake

@@ -55,6 +55,9 @@ run_cmake_command(GLOB-error-CONFIGURE_DEPENDS-SCRIPT_MODE ${CMAKE_COMMAND} -P
 if(NOT WIN32 OR CYGWIN)
   run_cmake(GLOB_RECURSE-cyclic-recursion)
   run_cmake(INSTALL-SYMLINK)
+  run_cmake(READ_SYMLINK)
+  run_cmake(READ_SYMLINK-noexist)
+  run_cmake(READ_SYMLINK-notsymlink)
 endif()
 
 if(RunCMake_GENERATOR STREQUAL "Ninja")