Browse Source

cmSystemTools: Add RenameFile option to not replace destination

Brad King 4 years ago
parent
commit
3600c6cd8c
3 changed files with 41 additions and 10 deletions
  1. 3 1
      Source/cmFileCommand.cxx
  2. 30 8
      Source/cmSystemTools.cxx
  3. 8 1
      Source/cmSystemTools.h

+ 3 - 1
Source/cmFileCommand.cxx

@@ -1348,12 +1348,14 @@ bool HandleRename(std::vector<std::string> const& args,
   }
 
   std::string err;
-  switch (cmSystemTools::RenameFile(oldname, newname, &err)) {
+  switch (cmSystemTools::RenameFile(oldname, newname,
+                                    cmSystemTools::Replace::Yes, &err)) {
     case cmSystemTools::RenameResult::Success:
       if (!arguments.Result.empty()) {
         status.GetMakefile().AddDefinition(arguments.Result, "0");
       }
       return true;
+    case cmSystemTools::RenameResult::NoReplace:
     case cmSystemTools::RenameResult::Failure:
       if (!arguments.Result.empty()) {
         status.GetMakefile().AddDefinition(arguments.Result, err);

+ 30 - 8
Source/cmSystemTools.cxx

@@ -973,14 +973,19 @@ void cmSystemTools::InitializeLibUV()
 
 #ifdef _WIN32
 namespace {
-bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname)
+bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname,
+                cmSystemTools::Replace replace)
 {
   // Not only ignore any previous error, but clear any memory of it.
   SetLastError(0);
 
-  // Use MOVEFILE_REPLACE_EXISTING to replace an existing destination file.
-  return MoveFileExW(oldname.c_str(), newname.c_str(),
-                     MOVEFILE_REPLACE_EXISTING);
+  DWORD flags = 0;
+  if (replace == cmSystemTools::Replace::Yes) {
+    // Use MOVEFILE_REPLACE_EXISTING to replace an existing destination file.
+    flags = flags | MOVEFILE_REPLACE_EXISTING;
+  }
+
+  return MoveFileExW(oldname.c_str(), newname.c_str(), flags);
 }
 }
 #endif
@@ -988,12 +993,13 @@ bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname)
 bool cmSystemTools::RenameFile(const std::string& oldname,
                                const std::string& newname)
 {
-  return cmSystemTools::RenameFile(oldname, newname, nullptr) ==
+  return cmSystemTools::RenameFile(oldname, newname, Replace::Yes) ==
     RenameResult::Success;
 }
 
 cmSystemTools::RenameResult cmSystemTools::RenameFile(
-  std::string const& oldname, std::string const& newname, std::string* err)
+  std::string const& oldname, std::string const& newname, Replace replace,
+  std::string* err)
 {
 #ifdef _WIN32
 #  ifndef INVALID_FILE_ATTRIBUTES
@@ -1016,7 +1022,7 @@ cmSystemTools::RenameResult cmSystemTools::RenameFile(
     oldname_wstr, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
 
   DWORD move_last_error = 0;
-  while (!cmMoveFile(oldname_wstr, newname_wstr) && --retry.Count) {
+  while (!cmMoveFile(oldname_wstr, newname_wstr, replace) && --retry.Count) {
     move_last_error = GetLastError();
 
     // There was no error ==> the operation is not yet complete.
@@ -1032,6 +1038,9 @@ cmSystemTools::RenameResult cmSystemTools::RenameFile(
     // 3) Windows Explorer has an associated directory already opened.
     if (move_last_error != ERROR_ACCESS_DENIED &&
         move_last_error != ERROR_SHARING_VIOLATION) {
+      if (replace == Replace::No && move_last_error == ERROR_ALREADY_EXISTS) {
+        return RenameResult::NoReplace;
+      }
       ReportError(err);
       return RenameResult::Failure;
     }
@@ -1060,10 +1069,23 @@ cmSystemTools::RenameResult cmSystemTools::RenameFile(
   if (retry.Count > 0) {
     return RenameResult::Success;
   }
+  if (replace == Replace::No && GetLastError() == ERROR_ALREADY_EXISTS) {
+    return RenameResult::NoReplace;
+  }
   ReportError(err);
   return RenameResult::Failure;
 #else
-  /* On UNIX we have an OS-provided call to do this atomically.  */
+  // On UNIX we have OS-provided calls to create 'newname' atomically.
+  if (replace == Replace::No) {
+    if (link(oldname.c_str(), newname.c_str()) == 0) {
+      return RenameResult::Success;
+    }
+    if (errno == EEXIST) {
+      return RenameResult::NoReplace;
+    }
+    ReportError(err);
+    return RenameResult::Failure;
+  }
   if (rename(oldname.c_str(), newname.c_str()) == 0) {
     return RenameResult::Success;
   }

+ 8 - 1
Source/cmSystemTools.h

@@ -128,9 +128,15 @@ public:
   static bool SimpleGlob(const std::string& glob,
                          std::vector<std::string>& files, int type = 0);
 
+  enum class Replace
+  {
+    Yes,
+    No,
+  };
   enum class RenameResult
   {
     Success,
+    NoReplace,
     Failure,
   };
 
@@ -139,7 +145,8 @@ public:
   static bool RenameFile(const std::string& oldname,
                          const std::string& newname);
   static RenameResult RenameFile(std::string const& oldname,
-                                 std::string const& newname, std::string* err);
+                                 std::string const& newname, Replace replace,
+                                 std::string* err = nullptr);
 
   //! Rename a file if contents are different, delete the source otherwise
   static void MoveFileIfDifferent(const std::string& source,