Browse Source

Merge topic 'symlink'

afb7f6e4ff cmake: Add '-E create_symlink' support on Windows

Acked-by: Kitware Robot <[email protected]>
Merge-request: !2144
Brad King 7 years ago
parent
commit
a9df54ec31

+ 0 - 5
Help/manual/cmake.1.rst

@@ -354,11 +354,6 @@ Available commands are:
   Touch a file if it exists but do not create it.  If a file does
   not exist it will be silently ignored.
 
-UNIX-specific Command-Line Tools
---------------------------------
-
-The following ``cmake -E`` commands are available only on UNIX:
-
 ``create_symlink <old> <new>``
   Create a symbolic link ``<new>`` naming ``<old>``.
 

+ 5 - 0
Help/release/dev/create_symlink-windows.rst

@@ -0,0 +1,5 @@
+create_symlink-windows
+-------------------------
+
+* The :manual:`cmake(1)` ``-E create_symlink`` command can now be used
+  on Windows.

+ 23 - 2
Source/cmSystemTools.cxx

@@ -6,6 +6,7 @@
 #include "cmDuration.h"
 #include "cmProcessOutput.h"
 #include "cm_sys_stat.h"
+#include "cm_uv.h"
 
 #if defined(CMAKE_BUILD_WITH_CMAKE)
 #  include "cmArchiveWrite.h"
@@ -55,8 +56,6 @@
 #  include <wincrypt.h>
 
 #  include <fcntl.h> /* _O_TEXT */
-
-#  include "cm_uv.h"
 #else
 #  include <sys/time.h>
 #  include <unistd.h>
@@ -2988,3 +2987,25 @@ bool cmSystemTools::StringToULong(const char* str, unsigned long* value)
   *value = strtoul(str, &endp, 10);
   return (*endp == '\0') && (endp != str) && (errno == 0);
 }
+
+bool cmSystemTools::CreateSymlink(const std::string& origName,
+                                  const std::string& newName)
+{
+  uv_fs_t req;
+  int flags = 0;
+#if defined(_WIN32)
+  if (cmsys::SystemTools::FileIsDirectory(origName)) {
+    flags |= UV_FS_SYMLINK_DIR;
+  }
+#endif
+  int err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(),
+                          flags, nullptr);
+  if (err) {
+    std::string e =
+      "failed to create symbolic link '" + newName + "': " + uv_strerror(err);
+    cmSystemTools::Error(e.c_str());
+    return false;
+  }
+
+  return true;
+}

+ 5 - 0
Source/cmSystemTools.h

@@ -513,6 +513,11 @@ public:
   /** Perform one-time initialization of libuv.  */
   static void InitializeLibUV();
 
+  /** Create a symbolic link if the platform supports it.  Returns whether
+      creation succeeded. */
+  static bool CreateSymlink(const std::string& origName,
+                            const std::string& newName);
+
 private:
   static bool s_ForceUnixPaths;
   static bool s_RunCommandHideConsole;

+ 1 - 6
Source/cmcmd.cxx

@@ -108,6 +108,7 @@ void CMakeCommandUsage(const char* program)
     << "  time command [args...]    - run command and display elapsed time\n"
     << "  touch file                - touch a file.\n"
     << "  touch_nocreate file       - touch a file but do not create it.\n"
+    << "  create_symlink old new    - create a symbolic link new -> old\n"
 #if defined(_WIN32) && !defined(__CYGWIN__)
     << "Available on Windows only:\n"
     << "  delete_regv key           - delete registry value\n"
@@ -116,9 +117,6 @@ void CMakeCommandUsage(const char* program)
     << "  env_vs9_wince sdkname     - displays a batch file which sets the "
        "environment for the provided Windows CE SDK installed in VS2008\n"
     << "  write_regv key value      - write registry value\n"
-#else
-    << "Available on UNIX only:\n"
-    << "  create_symlink old new    - create a symbolic link new -> old\n"
 #endif
     ;
   /* clang-format on */
@@ -868,9 +866,6 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
         return 1;
       }
       if (!cmSystemTools::CreateSymlink(args[2], args[3])) {
-        std::string emsg = cmSystemTools::GetLastSystemError();
-        std::cerr << "failed to create symbolic link '" << destinationFileName
-                  << "': " << emsg << "\n";
         return 1;
       }
       return 0;

+ 9 - 5
Tests/RunCMake/CommandLine/E_create_symlink-broken-create-check.cmake

@@ -1,6 +1,10 @@
-if(NOT IS_SYMLINK ${RunCMake_TEST_BINARY_DIR}/L)
-  set(RunCMake_TEST_FAILED "Symlink 'L' incorrectly not created!")
-endif()
-if(EXISTS ${RunCMake_TEST_BINARY_DIR}/L)
-  set(RunCMake_TEST_FAILED "Symlink 'L' not broken!")
+if(${actual_stderr_var} MATCHES "operation not permitted")
+  unset(msg)
+else()
+  if(NOT IS_SYMLINK ${RunCMake_TEST_BINARY_DIR}/L)
+    set(RunCMake_TEST_FAILED "Symlink 'L' incorrectly not created!")
+  endif()
+  if(EXISTS ${RunCMake_TEST_BINARY_DIR}/L)
+    set(RunCMake_TEST_FAILED "Symlink 'L' not broken!")
+  endif()
 endif()

+ 6 - 2
Tests/RunCMake/CommandLine/E_create_symlink-broken-replace-check.cmake

@@ -1,3 +1,7 @@
-if(NOT IS_DIRECTORY ${RunCMake_TEST_BINARY_DIR}/L)
-  set(RunCMake_TEST_FAILED "Symlink 'L' not replaced correctly!")
+if(${actual_stderr_var} MATCHES "operation not permitted")
+  unset(msg)
+else()
+  if(NOT IS_DIRECTORY ${RunCMake_TEST_BINARY_DIR}/L)
+    set(RunCMake_TEST_FAILED "Symlink 'L' not replaced correctly!")
+  endif()
 endif()

+ 27 - 24
Tests/RunCMake/CommandLine/RunCMakeTest.cmake

@@ -127,32 +127,35 @@ if(RunCMake_GENERATOR STREQUAL "Ninja")
   unset(RunCMake_TEST_NO_CLEAN)
 endif()
 
-if(UNIX)
-  run_cmake_command(E_create_symlink-no-arg
-    ${CMAKE_COMMAND} -E create_symlink
-    )
-  run_cmake_command(E_create_symlink-missing-dir
-    ${CMAKE_COMMAND} -E create_symlink T missing-dir/L
-    )
+run_cmake_command(E_create_symlink-no-arg
+  ${CMAKE_COMMAND} -E create_symlink
+  )
+run_cmake_command(E_create_symlink-missing-dir
+  ${CMAKE_COMMAND} -E create_symlink T missing-dir/L
+  )
 
-  # Use a single build tree for a few tests without cleaning.
-  set(RunCMake_TEST_BINARY_DIR
-    ${RunCMake_BINARY_DIR}/E_create_symlink-broken-build)
-  set(RunCMake_TEST_NO_CLEAN 1)
-  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
-  run_cmake_command(E_create_symlink-broken-create
-    ${CMAKE_COMMAND} -E create_symlink T L
-    )
-  run_cmake_command(E_create_symlink-broken-replace
-    ${CMAKE_COMMAND} -E create_symlink . L
-    )
-  unset(RunCMake_TEST_BINARY_DIR)
-  unset(RunCMake_TEST_NO_CLEAN)
+# Use a single build tree for a few tests without cleaning.
+# These tests are special on Windows since it will only fail if the user
+# running the test does not have the priveldge to create symlinks. If this
+# happens we clear the msg in the -check.cmake and say that the test passes
+set(RunCMake_DEFAULT_stderr "(operation not permitted)?")
+set(RunCMake_TEST_BINARY_DIR
+  ${RunCMake_BINARY_DIR}/E_create_symlink-broken-build)
+set(RunCMake_TEST_NO_CLEAN 1)
+file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+run_cmake_command(E_create_symlink-broken-create
+  ${CMAKE_COMMAND} -E create_symlink T L
+  )
+run_cmake_command(E_create_symlink-broken-replace
+  ${CMAKE_COMMAND} -E create_symlink . L
+  )
+unset(RunCMake_TEST_BINARY_DIR)
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_DEFAULT_stderr)
 
-  run_cmake_command(E_create_symlink-no-replace-dir
-    ${CMAKE_COMMAND} -E create_symlink T .
-    )
-endif()
+run_cmake_command(E_create_symlink-no-replace-dir
+  ${CMAKE_COMMAND} -E create_symlink T .
+  )
 
 set(in ${RunCMake_SOURCE_DIR}/copy_input)
 set(out ${RunCMake_BINARY_DIR}/copy_output)