mirror of
https://github.com/Kitware/CMake.git
synced 2026-03-16 23:41:15 -05:00
file: add COPY_FILE subcommand
The `file(COPY)` subcommand is overloaded and busy for such a simple operation. Instead, make a simpler subcommand with error handling support.
This commit is contained in:
@@ -39,6 +39,7 @@ Synopsis
|
||||
`Filesystem`_
|
||||
file({`GLOB`_ | `GLOB_RECURSE`_} <out-var> [...] [<globbing-expr>...])
|
||||
file(`RENAME`_ <oldname> <newname> [...])
|
||||
file(`COPY_FILE`_ <oldname> <newname> [...])
|
||||
file({`REMOVE`_ | `REMOVE_RECURSE`_ } [<files>...])
|
||||
file(`MAKE_DIRECTORY`_ [<dir>...])
|
||||
file({`COPY`_ | `INSTALL`_} <file>... DESTINATION <dir> [...])
|
||||
@@ -683,6 +684,28 @@ The options are:
|
||||
If ``RESULT <result>`` is used, the result variable will be
|
||||
set to ``NO_REPLACE``. Otherwise, an error is emitted.
|
||||
|
||||
.. _COPY_FILE:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
file(COPY_FILE <oldname> <newname>
|
||||
[RESULT <result>]
|
||||
[ONLY_IF_DIFFERENT])
|
||||
|
||||
Copy a file from ``<oldname>`` to ``<newname>``. Directories are not
|
||||
supported. Symlinks are ignored and ``<oldfile>``'s content is read and
|
||||
written to ``<newname>`` as a new file.
|
||||
|
||||
The options are:
|
||||
|
||||
``RESULT <result>``
|
||||
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.
|
||||
|
||||
``ONLY_IF_DIFFERENT``
|
||||
If the ``<newname>`` path already exists, do not replace it if it is the
|
||||
same as ``<oldname>``. Otherwise, an error is emitted.
|
||||
|
||||
.. _REMOVE:
|
||||
.. _REMOVE_RECURSE:
|
||||
|
||||
|
||||
4
Help/release/dev/file-COPY_FILE.rst
Normal file
4
Help/release/dev/file-COPY_FILE.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
file-COPY_ONLY
|
||||
--------------
|
||||
|
||||
* The :command:`file(COPY_FILE)` command was added to copy a file to another.
|
||||
@@ -1379,6 +1379,97 @@ bool HandleRename(std::vector<std::string> const& args,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HandleCopyFile(std::vector<std::string> const& args,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
if (args.size() < 3) {
|
||||
status.SetError("COPY_FILE must be called with at least two additional "
|
||||
"arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute full path for old and new names.
|
||||
std::string oldname = args[1];
|
||||
if (!cmsys::SystemTools::FileIsFullPath(oldname)) {
|
||||
oldname =
|
||||
cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[1]);
|
||||
}
|
||||
std::string newname = args[2];
|
||||
if (!cmsys::SystemTools::FileIsFullPath(newname)) {
|
||||
newname =
|
||||
cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[2]);
|
||||
}
|
||||
|
||||
struct Arguments
|
||||
{
|
||||
bool OnlyIfDifferent = false;
|
||||
std::string Result;
|
||||
};
|
||||
|
||||
static auto const parser =
|
||||
cmArgumentParser<Arguments>{}
|
||||
.Bind("ONLY_IF_DIFFERENT"_s, &Arguments::OnlyIfDifferent)
|
||||
.Bind("RESULT"_s, &Arguments::Result);
|
||||
|
||||
std::vector<std::string> unconsumedArgs;
|
||||
Arguments const arguments =
|
||||
parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
|
||||
if (!unconsumedArgs.empty()) {
|
||||
status.SetError("COPY_FILE unknown argument:\n " +
|
||||
unconsumedArgs.front());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
if (cmsys::SystemTools::FileIsDirectory(oldname)) {
|
||||
if (!arguments.Result.empty()) {
|
||||
status.GetMakefile().AddDefinition(arguments.Result,
|
||||
"cannot copy a directory");
|
||||
} else {
|
||||
status.SetError(
|
||||
cmStrCat("COPY_FILE cannot copy a directory\n ", oldname));
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (cmsys::SystemTools::FileIsDirectory(newname)) {
|
||||
if (!arguments.Result.empty()) {
|
||||
status.GetMakefile().AddDefinition(arguments.Result,
|
||||
"cannot copy to a directory");
|
||||
} else {
|
||||
status.SetError(
|
||||
cmStrCat("COPY_FILE cannot copy to a directory\n ", newname));
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
cmSystemTools::CopyWhen when;
|
||||
if (arguments.OnlyIfDifferent) {
|
||||
when = cmSystemTools::CopyWhen::OnlyIfDifferent;
|
||||
} else {
|
||||
when = cmSystemTools::CopyWhen::Always;
|
||||
}
|
||||
|
||||
std::string err;
|
||||
if (cmSystemTools::CopySingleFile(oldname, newname, when, &err) ==
|
||||
cmSystemTools::CopyResult::Success) {
|
||||
if (!arguments.Result.empty()) {
|
||||
status.GetMakefile().AddDefinition(arguments.Result, "0");
|
||||
}
|
||||
} else {
|
||||
if (!arguments.Result.empty()) {
|
||||
status.GetMakefile().AddDefinition(arguments.Result, err);
|
||||
} else {
|
||||
status.SetError(cmStrCat("COPY_FILE failed to copy\n ", oldname,
|
||||
"\nto\n ", newname, "\nbecause: ", err, "\n"));
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HandleRemoveImpl(std::vector<std::string> const& args, bool recurse,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
@@ -3609,6 +3700,7 @@ bool cmFileCommand(std::vector<std::string> const& args,
|
||||
{ "GLOB_RECURSE"_s, HandleGlobRecurseCommand },
|
||||
{ "MAKE_DIRECTORY"_s, HandleMakeDirectoryCommand },
|
||||
{ "RENAME"_s, HandleRename },
|
||||
{ "COPY_FILE"_s, HandleCopyFile },
|
||||
{ "REMOVE"_s, HandleRemove },
|
||||
{ "REMOVE_RECURSE"_s, HandleRemoveRecurse },
|
||||
{ "COPY"_s, HandleCopyCommand },
|
||||
|
||||
1
Tests/RunCMake/file/COPY_FILE-arg-missing-result.txt
Normal file
1
Tests/RunCMake/file/COPY_FILE-arg-missing-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
3
Tests/RunCMake/file/COPY_FILE-arg-missing-stderr.txt
Normal file
3
Tests/RunCMake/file/COPY_FILE-arg-missing-stderr.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
^CMake Error at [^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-arg-missing.cmake:1 \(file\):
|
||||
file COPY_FILE must be called with at least two additional arguments
|
||||
1
Tests/RunCMake/file/COPY_FILE-arg-missing.cmake
Normal file
1
Tests/RunCMake/file/COPY_FILE-arg-missing.cmake
Normal file
@@ -0,0 +1 @@
|
||||
file(COPY_FILE "old")
|
||||
1
Tests/RunCMake/file/COPY_FILE-arg-unknown-result.txt
Normal file
1
Tests/RunCMake/file/COPY_FILE-arg-unknown-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
5
Tests/RunCMake/file/COPY_FILE-arg-unknown-stderr.txt
Normal file
5
Tests/RunCMake/file/COPY_FILE-arg-unknown-stderr.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
^CMake Error at [^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-arg-unknown.cmake:1 \(file\):
|
||||
file COPY_FILE unknown argument:
|
||||
|
||||
unknown$
|
||||
1
Tests/RunCMake/file/COPY_FILE-arg-unknown.cmake
Normal file
1
Tests/RunCMake/file/COPY_FILE-arg-unknown.cmake
Normal file
@@ -0,0 +1 @@
|
||||
file(COPY_FILE "old" "new" unknown)
|
||||
@@ -0,0 +1 @@
|
||||
^-- file\(COPY_FILE\) failed with result: cannot copy a directory
|
||||
8
Tests/RunCMake/file/COPY_FILE-dir-to-file-capture.cmake
Normal file
8
Tests/RunCMake/file/COPY_FILE-dir-to-file-capture.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(MAKE_DIRECTORY "${oldname}")
|
||||
file(COPY_FILE "${oldname}" "${newname}" RESULT result)
|
||||
message(STATUS "file(COPY_FILE) failed with result: ${result}")
|
||||
if(EXISTS "${newname}")
|
||||
message(FATAL_ERROR "The new name exists:\n ${newname}")
|
||||
endif()
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,6 @@
|
||||
^CMake Error at [^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-dir-to-file-fail.cmake:[0-9] \(file\):
|
||||
file COPY_FILE cannot copy a directory
|
||||
|
||||
[^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-dir-to-file-fail-build/input
|
||||
4
Tests/RunCMake/file/COPY_FILE-dir-to-file-fail.cmake
Normal file
4
Tests/RunCMake/file/COPY_FILE-dir-to-file-fail.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(MAKE_DIRECTORY "${oldname}")
|
||||
file(COPY_FILE "${oldname}" "${newname}")
|
||||
@@ -0,0 +1 @@
|
||||
^-- file\(COPY_FILE\) failed with result: cannot copy a directory
|
||||
@@ -0,0 +1,8 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(MAKE_DIRECTORY "${oldname}")
|
||||
file(COPY_FILE "${oldname}" "${newname}" RESULT result)
|
||||
message(STATUS "file(COPY_FILE) failed with result: ${result}")
|
||||
if(EXISTS "${newname}")
|
||||
message(FATAL_ERROR "The new name exists:\n ${newname}")
|
||||
endif()
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,6 @@
|
||||
^CMake Error at [^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-dirlink-to-file-fail.cmake:[0-9] \(file\):
|
||||
file COPY_FILE cannot copy a directory
|
||||
|
||||
[^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-dirlink-to-file-fail-build/input
|
||||
4
Tests/RunCMake/file/COPY_FILE-dirlink-to-file-fail.cmake
Normal file
4
Tests/RunCMake/file/COPY_FILE-dirlink-to-file-fail.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(MAKE_DIRECTORY "${oldname}")
|
||||
file(COPY_FILE "${oldname}" "${newname}")
|
||||
@@ -0,0 +1,9 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(WRITE "${oldname}" "")
|
||||
file(MAKE_DIRECTORY "${newname}")
|
||||
file(COPY_FILE "${oldname}" "${newname}" RESULT result ONLY_IF_DIFFERENT)
|
||||
message(STATUS "file(COPY_FILE) failed with result: ${result}")
|
||||
if(NOT EXISTS "${oldname}")
|
||||
message(FATAL_ERROR "The old name still does not exist:\n ${oldname}")
|
||||
endif()
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,6 @@
|
||||
^CMake Error at [^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-file-ONLY_IF_DIFFERENT-fail.cmake:[0-9] \(file\):
|
||||
file COPY_FILE cannot copy to a directory
|
||||
|
||||
[^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-file-ONLY_IF_DIFFERENT-fail-build/output
|
||||
@@ -0,0 +1,5 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(WRITE "${oldname}" "")
|
||||
file(MAKE_DIRECTORY "${newname}")
|
||||
file(COPY_FILE "${oldname}" "${newname}" ONLY_IF_DIFFERENT)
|
||||
@@ -0,0 +1,12 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(WRITE "${oldname}" "")
|
||||
execute_process(COMMAND "${CMAKE_COMMAND} -E sleep 1")
|
||||
file(WRITE "${newname}" "")
|
||||
file(TIMESTAMP "${newname}" before_copy UTC)
|
||||
file(COPY_FILE "${oldname}" "${newname}" RESULT result ONLY_IF_DIFFERENT)
|
||||
file(TIMESTAMP "${newname}" after_copy UTC)
|
||||
if (NOT before_copy STREQUAL after_copy)
|
||||
message(FATAL_ERROR
|
||||
"${newname} was modified even though ONLY_IF_DIFFERENT was specified")
|
||||
endif ()
|
||||
9
Tests/RunCMake/file/COPY_FILE-file-replace.cmake
Normal file
9
Tests/RunCMake/file/COPY_FILE-file-replace.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(WRITE "${oldname}" "a")
|
||||
file(WRITE "${newname}" "b")
|
||||
file(COPY_FILE "${oldname}" "${newname}")
|
||||
file(READ "${newname}" new)
|
||||
if(NOT "${new}" STREQUAL "a")
|
||||
message(FATAL_ERROR "New name:\n ${newname}\ndoes not contain expected content 'a'.")
|
||||
endif()
|
||||
@@ -0,0 +1 @@
|
||||
^-- file\(COPY_FILE\) failed with result: cannot copy to a directory
|
||||
9
Tests/RunCMake/file/COPY_FILE-file-to-dir-capture.cmake
Normal file
9
Tests/RunCMake/file/COPY_FILE-file-to-dir-capture.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(WRITE "${oldname}" "")
|
||||
file(MAKE_DIRECTORY "${newname}")
|
||||
file(COPY_FILE "${oldname}" "${newname}" RESULT result)
|
||||
message(STATUS "file(COPY_FILE) failed with result: ${result}")
|
||||
if(NOT EXISTS "${oldname}")
|
||||
message(FATAL_ERROR "The old name does not exist:\n ${oldname}")
|
||||
endif()
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,6 @@
|
||||
^CMake Error at [^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-file-to-dir-fail.cmake:[0-9] \(file\):
|
||||
file COPY_FILE cannot copy to a directory
|
||||
|
||||
[^
|
||||
]*/Tests/RunCMake/file/COPY_FILE-file-to-dir-fail-build/output
|
||||
5
Tests/RunCMake/file/COPY_FILE-file-to-dir-fail.cmake
Normal file
5
Tests/RunCMake/file/COPY_FILE-file-to-dir-fail.cmake
Normal file
@@ -0,0 +1,5 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(WRITE "${oldname}" "")
|
||||
file(MAKE_DIRECTORY "${newname}")
|
||||
file(COPY_FILE "${oldname}" "${newname}")
|
||||
10
Tests/RunCMake/file/COPY_FILE-file-to-file.cmake
Normal file
10
Tests/RunCMake/file/COPY_FILE-file-to-file.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(WRITE "${oldname}" "")
|
||||
file(COPY_FILE "${oldname}" "${newname}")
|
||||
if(NOT EXISTS "${oldname}")
|
||||
message(FATAL_ERROR "The old name does not exist:\n ${oldname}")
|
||||
endif()
|
||||
if(NOT EXISTS "${newname}")
|
||||
message(FATAL_ERROR "The new name does not exist:\n ${newname}")
|
||||
endif()
|
||||
10
Tests/RunCMake/file/COPY_FILE-link-to-file.cmake
Normal file
10
Tests/RunCMake/file/COPY_FILE-link-to-file.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
set(lnkname "${CMAKE_CURRENT_BINARY_DIR}/link")
|
||||
set(oldname "${CMAKE_CURRENT_BINARY_DIR}/input")
|
||||
set(newname "${CMAKE_CURRENT_BINARY_DIR}/output")
|
||||
file(WRITE "${lnkname}" "a")
|
||||
file(CREATE_LINK "${lnkname}" "${oldname}")
|
||||
file(COPY_FILE "${oldname}" "${newname}")
|
||||
file(READ "${newname}" new)
|
||||
if(NOT "${new}" STREQUAL "a")
|
||||
message(FATAL_ERROR "New name:\n ${newname}\ndoes not contain expected content 'a'.")
|
||||
endif()
|
||||
@@ -50,6 +50,21 @@ run_cmake(SIZE-error-does-not-exist)
|
||||
|
||||
run_cmake(REMOVE-empty)
|
||||
|
||||
run_cmake_script(COPY_FILE-file-replace)
|
||||
run_cmake_script(COPY_FILE-dir-to-file-capture)
|
||||
run_cmake_script(COPY_FILE-dir-to-file-fail)
|
||||
run_cmake_script(COPY_FILE-dirlink-to-file-capture)
|
||||
run_cmake_script(COPY_FILE-dirlink-to-file-fail)
|
||||
run_cmake_script(COPY_FILE-file-to-file)
|
||||
run_cmake_script(COPY_FILE-file-to-dir-capture)
|
||||
run_cmake_script(COPY_FILE-file-to-dir-fail)
|
||||
run_cmake_script(COPY_FILE-file-ONLY_IF_DIFFERENT-capture)
|
||||
run_cmake_script(COPY_FILE-file-ONLY_IF_DIFFERENT-fail)
|
||||
run_cmake_script(COPY_FILE-file-ONLY_IF_DIFFERENT-no-overwrite)
|
||||
run_cmake_script(COPY_FILE-link-to-file)
|
||||
run_cmake_script(COPY_FILE-arg-missing)
|
||||
run_cmake_script(COPY_FILE-arg-unknown)
|
||||
|
||||
run_cmake_script(RENAME-file-replace)
|
||||
run_cmake_script(RENAME-file-to-file)
|
||||
run_cmake_script(RENAME-file-to-dir-capture)
|
||||
|
||||
Reference in New Issue
Block a user