Browse Source

ENH: Add get_filename_component(... REALPATH)

This patch from Philip Lowman creates a REALPATH mode in the
get_filename_component command.  It is like ABSOLUTE, but will also
resolve symlinks (which ABSOLUTE once did but was broken long ago).
See issue #8423.
Brad King 16 years ago
parent
commit
ae873d4a89

+ 7 - 1
Source/cmGetFilenameComponentCommand.cxx

@@ -75,7 +75,8 @@ bool cmGetFilenameComponentCommand
     {
     result = cmSystemTools::GetFilenameWithoutExtension(filename);
     }
-  else if (args[2] == "ABSOLUTE")
+  else if (args[2] == "ABSOLUTE" ||
+           args[2] == "REALPATH")
     {
     // If the path given is relative evaluate it relative to the
     // current source directory.
@@ -92,6 +93,11 @@ bool cmGetFilenameComponentCommand
 
     // Collapse the path to its simplest form.
     result = cmSystemTools::CollapseFullPath(filename.c_str());
+    if(args[2] == "REALPATH")
+      {
+      // Resolve symlinks if possible
+      result = cmSystemTools::GetRealPath(filename.c_str());
+      }
     }
   else 
     {

+ 3 - 2
Source/cmGetFilenameComponentCommand.h

@@ -68,11 +68,12 @@ public:
     {
     return
       "  get_filename_component(VarName FileName\n"
-      "                         PATH|ABSOLUTE|NAME|EXT|NAME_WE\n"
+      "                         PATH|ABSOLUTE|NAME|EXT|NAME_WE|REALPATH\n"
       "                         [CACHE])\n"
       "Set VarName to be the path (PATH), file name (NAME), file "
       "extension (EXT), file name without extension (NAME_WE) of FileName, "
-      "or the full path (ABSOLUTE).  "
+      "the full path (ABSOLUTE), or the full path with all symlinks "
+      "resolved (REALPATH).  "
       "Note that the path is converted to Unix slashes format and has no "
       "trailing slashes. The longest file extension is always considered. "
       "If the optional CACHE argument is specified, the result variable is "

+ 1 - 0
Tests/CMakeTests/CMakeLists.txt

@@ -14,6 +14,7 @@ AddCMakeTest(VariableWatch "")
 AddCMakeTest(Include "")
 AddCMakeTest(FindBase "")
 AddCMakeTest(Toolchain "")
+AddCMakeTest(GetFilenameComponentRealpath "")
 
 SET(GetPrerequisites_PreArgs
   "-DCTEST_CONFIGURATION_TYPE:STRING=\\\${CTEST_CONFIGURATION_TYPE}"

+ 57 - 0
Tests/CMakeTests/GetFilenameComponentRealpathTest.cmake.in

@@ -0,0 +1,57 @@
+set(bindir ${CMAKE_CURRENT_BINARY_DIR})
+
+#
+# Test nonexistent REALPATH & ABSOLUTE resolution
+#
+get_filename_component(nonexistent1 ${bindir}/THIS_IS_A_NONEXISTENT_FILE REALPATH)
+get_filename_component(nonexistent2 ${bindir}/THIS_IS_A_NONEXISTENT_FILE ABSOLUTE)
+if(NOT nonexistent1 STREQUAL "${bindir}/THIS_IS_A_NONEXISTENT_FILE")
+    message(FATAL_ERROR "REALPATH is not preserving nonexistent files")
+endif()
+if(NOT nonexistent2 STREQUAL "${bindir}/THIS_IS_A_NONEXISTENT_FILE")
+    message(FATAL_ERROR "ABSOLUTE is not preserving nonexistent files")
+endif()
+
+#
+# Test symbolic link resolution
+#
+if(UNIX)
+    # file1 => file2 => file3 (real)
+    file(WRITE ${bindir}/file3 "test file")
+
+    find_program(LN NAMES "ln")
+    if(LN)
+        # Create symlinks using "ln -s"
+        if(NOT EXISTS ${bindir}/file2)
+            execute_process(COMMAND ${LN} "-s" "${bindir}/file3" "${bindir}/file2")
+        endif()
+        if(NOT EXISTS ${bindir}/file1)
+            execute_process(COMMAND ${LN} "-s" "${bindir}/file2" "${bindir}/file1")
+        endif()
+
+        get_filename_component(file1 ${bindir}/file1 REALPATH)
+        get_filename_component(file2 ${bindir}/file2 REALPATH)
+        get_filename_component(file3 ${bindir}/file3 REALPATH)
+
+        if(NOT file3 STREQUAL "${bindir}/file3")
+            message(FATAL_ERROR "CMake fails resolving REALPATH file file3")
+        endif()
+
+        if(NOT file2 STREQUAL "${bindir}/file3")
+            message(FATAL_ERROR "CMake fails resolving simple symlink")
+        endif()
+
+        if(NOT file1 STREQUAL "${bindir}/file3")
+            message(FATAL_ERROR "CMake fails resolving double symlink")
+        endif()
+
+        # cleanup
+        file(REMOVE ${bindir}/file1)
+        file(REMOVE ${bindir}/file2)
+        if(EXISTS file1 OR EXISTS file2)
+           message(FATAL_ERROR "removal of file1 or file2 failed")
+        endif()
+    endif(LN)
+
+    file(REMOVE ${bindir}/file3)
+endif()