Commit 9bf40d80 authored by Brad King's avatar Brad King

file(RENAME): Add option to not replace existing path

Add a `NO_REPLACE` option that prevents overwriting `<newname>`
if it exists.
parent 3600c6cd
Pipeline #217250 passed with stages
in 89 minutes and 38 seconds
......@@ -666,7 +666,8 @@ Examples of recursive globbing include::
.. code-block:: cmake
file(RENAME <oldname> <newname>
[RESULT <result>])
[RESULT <result>]
Move a file or directory within a filesystem from ``<oldname>`` to
``<newname>``, replacing the destination atomically.
......@@ -677,6 +678,11 @@ The options are:
Set ``<result>`` variable to ``0`` on success or an error message otherwise.
If ``RESULT`` is not specified and the operation fails, an error is emitted.
If the ``<newname>`` path already exists, do not replace it.
If ``RESULT <result>`` is used, the result variable will be
set to ``NO_REPLACE``. Otherwise, an error is emitted.
......@@ -2,4 +2,5 @@ file-RENAME
* The :command:`file(RENAME)` command learned to optionally capture
failure in a result variable.
failure in a result variable. It also gained a ``NO_REPLACE``
option to fail if the destination exists.
......@@ -1333,11 +1333,13 @@ bool HandleRename(std::vector<std::string> const& args,
struct Arguments
bool NoReplace = false;
std::string Result;
static auto const parser =
cmArgumentParser<Arguments>{}.Bind("RESULT"_s, &Arguments::Result);
static auto const parser = cmArgumentParser<Arguments>{}
.Bind("NO_REPLACE"_s, &Arguments::NoReplace)
.Bind("RESULT"_s, &Arguments::Result);
std::vector<std::string> unconsumedArgs;
Arguments const arguments =
......@@ -1349,13 +1351,22 @@ bool HandleRename(std::vector<std::string> const& args,
std::string err;
switch (cmSystemTools::RenameFile(oldname, newname,
cmSystemTools::Replace::Yes, &err)) {
? cmSystemTools::Replace::No
: cmSystemTools::Replace::Yes,
&err)) {
case cmSystemTools::RenameResult::Success:
if (!arguments.Result.empty()) {
status.GetMakefile().AddDefinition(arguments.Result, "0");
return true;
case cmSystemTools::RenameResult::NoReplace:
if (!arguments.Result.empty()) {
err = "NO_REPLACE";
} else {
err = "path not replaced";
case cmSystemTools::RenameResult::Failure:
if (!arguments.Result.empty()) {
status.GetMakefile().AddDefinition(arguments.Result, err);
^-- file\(RENAME\) failed with result: NO_REPLACE$
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
file(WRITE "${oldname}" "a")
file(WRITE "${newname}" "b")
file(RENAME "${oldname}" "${newname}" NO_REPLACE RESULT result)
message(STATUS "file(RENAME) failed with result: ${result}")
if(NOT EXISTS "${oldname}")
message(FATAL_ERROR "The old name does not still exist:\n ${oldname}")
^CMake Error at [^
]*/Tests/RunCMake/file/RENAME-file-NO_REPLACE-fail.cmake:[0-9] \(file\):
file RENAME failed to rename
because: path not replaced$
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
file(WRITE "${oldname}" "a")
file(WRITE "${newname}" "b")
file(RENAME "${oldname}" "${newname}" NO_REPLACE)
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
file(WRITE "${oldname}" "a")
file(WRITE "${newname}" "b")
file(RENAME "${oldname}" "${newname}")
file(READ "${newname}" new)
if(NOT "${new}" STREQUAL "a")
message(FATAL_ERROR "New name:\n ${newname}\ndoes not contain expected content 'a'.")
......@@ -50,9 +50,12 @@ run_cmake(SIZE-error-does-not-exist)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment