cmake -E copy_directory*: Add -t argument support

Fixes: #24568
This commit is contained in:
Alex Reinking
2026-02-24 13:38:13 -05:00
committed by Brad King
parent 29dcbe4169
commit 16b8a1ba2a
10 changed files with 110 additions and 26 deletions
+29 -10
View File
@@ -1218,7 +1218,8 @@ Available commands are:
.. program:: cmake-E
.. option:: copy <file>... <destination>, copy -t <destination> <file>...
.. option:: copy <file>... <destination>,
copy -t <destination> <file>...
Copy files to ``<destination>`` (either file or directory).
If multiple files are specified, or if ``-t`` is specified, the
@@ -1233,10 +1234,13 @@ Available commands are:
.. versionadded:: 3.26
Support for ``-t`` argument.
.. option:: copy_directory <dir>... <destination>
.. option:: copy_directory <dir>... <destination>,
copy_directory -t <destination> <dir>...
Copy content of ``<dir>...`` directories to ``<destination>`` directory.
If ``<destination>`` directory does not exist it will be created.
Copy content of ``<dir>...`` directories to a ``<destination>`` directory.
If the ``<destination>`` directory does not exist it will be created.
If ``-t`` is not specified, the last argument is assumed to be the
``<destination>``.
``copy_directory`` does follow symlinks.
.. versionadded:: 3.5
@@ -1246,29 +1250,44 @@ Available commands are:
The command now fails when the source directory does not exist.
Previously it succeeded by creating an empty destination directory.
.. option:: copy_directory_if_different <dir>... <destination>
.. versionadded:: 4.4
Support for the ``-t`` argument.
.. option:: copy_directory_if_different <dir>... <destination>,
copy_directory_if_different -t <destination> <dir>...
.. versionadded:: 3.26
Copy changed content of ``<dir>...`` directories to ``<destination>`` directory.
If ``<destination>`` directory does not exist it will be created.
Copy changed content of ``<dir>...`` directories to a ``<destination>`` directory.
If the ``<destination>`` directory does not exist it will be created.
If ``-t`` is not specified, the last argument is assumed to be the
``<destination>``.
``copy_directory_if_different`` does follow symlinks.
The command fails when the source directory does not exist.
.. option:: copy_directory_if_newer <dir>... <destination>
.. versionadded:: 4.4
Support for the ``-t`` argument.
.. option:: copy_directory_if_newer <dir>... <destination>,
copy_directory_if_newer -t <destination> <dir>...
.. versionadded:: 4.2
Copy content of ``<dir>...`` directories to ``<destination>`` directory
Copy content of ``<dir>...`` directories to a ``<destination>`` directory
if source files are newer than destination files (based on file timestamps).
If ``<destination>`` directory does not exist it will be created.
If the ``<destination>`` directory does not exist it will be created.
If ``-t`` is not specified, the last argument is assumed to be the
``<destination>``.
``copy_directory_if_newer`` does follow symlinks.
The command fails when the source directory does not exist.
This is faster than ``copy_directory_if_different`` as it only compares
file timestamps instead of file contents.
.. versionadded:: 4.4
Support for the ``-t`` argument.
.. option:: copy_if_different <file>... <destination>,
copy_if_different -t <destination> <file>...
+5 -2
View File
@@ -2,6 +2,9 @@ cmake-E-copy-t-flag
-------------------
* The :option:`cmake -E` commands
:option:`copy_if_different <cmake-E copy_if_different>` and
:option:`copy_if_newer <cmake-E copy_if_newer>`
:option:`copy_if_different <cmake-E copy_if_different>`,
:option:`copy_if_newer <cmake-E copy_if_newer>`,
:option:`copy_directory <cmake-E copy_directory>`,
:option:`copy_directory_if_different <cmake-E copy_directory_if_different>`,
and :option:`copy_directory_if_newer <cmake-E copy_directory_if_newer>`
gained support for the ``-t`` argument.
+58 -14
View File
@@ -108,9 +108,12 @@ char const* const HELP_AVAILABLE_COMMANDS = R"(Available commands:
- check if file1 is same as file2
copy <file>... destination | -t <destination> <file>...
- copy files to destination (either file or directory)
copy_directory <dir>... destination - copy content of <dir>... directories to 'destination' directory
copy_directory_if_different <dir>... destination - copy changed content of <dir>... directories to 'destination' directory
copy_directory_if_newer <dir>... destination - copy newer content of <dir>... directories to 'destination' directory
copy_directory <dir>... destination | -t <destination> <dir>...
- copy content of <dir>... directories to 'destination' directory
copy_directory_if_different <dir>... destination | -t <destination> <dir>...
- copy changed content of <dir>... directories to 'destination' directory
copy_directory_if_newer <dir>... destination | -t <destination> <dir>...
- copy newer content of <dir>... directories to 'destination' directory
copy_if_different <file>... destination | -t <destination> <file>...
- copy files if source has changed
copy_if_newer <file>... destination | -t <destination> <file>...
@@ -1086,26 +1089,67 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
}
// Copy directory contents
if ((args[1] == "copy_directory" ||
args[1] == "copy_directory_if_different" ||
args[1] == "copy_directory_if_newer") &&
args.size() > 3) {
// If error occurs we want to continue copying next files.
bool return_value = false;
if (args[1] == "copy_directory" ||
args[1] == "copy_directory_if_different" ||
args[1] == "copy_directory_if_newer") {
cmsys::SystemTools::CopyWhen when = cmsys::SystemTools::CopyWhen::Always;
if (args[1] == "copy_directory_if_different") {
when = cmsys::SystemTools::CopyWhen::OnlyIfDifferent;
} else if (args[1] == "copy_directory_if_newer") {
when = cmsys::SystemTools::CopyWhen::OnlyIfNewer;
}
std::string const& cmdName = args[1];
for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) {
using CommandArgument =
cmCommandLineArgument<bool(std::string const& value)>;
cm::optional<std::string> targetArg;
std::vector<CommandArgument> argParsers{
{ "-t", CommandArgument::Values::One,
CommandArgument::setToValue(targetArg) },
};
std::vector<std::string> dirs;
for (decltype(args.size()) i = 2; i < args.size(); i++) {
std::string const& arg = args[i];
bool matched = false;
for (auto const& m : argParsers) {
if (m.matches(arg)) {
matched = true;
if (m.parse(arg, i, args)) {
break;
}
return 1; // failed to parse
}
}
if (!matched) {
dirs.push_back(arg);
}
}
if (!targetArg) {
if (dirs.size() < 2) {
std::cerr << "Error: No directories or target specified (for "
<< cmdName << " command).\n";
return 1;
}
targetArg = dirs.back();
dirs.pop_back();
}
if (dirs.empty()) {
std::cerr << "Error: No source directories specified (for " << cmdName
<< " command).\n";
return 1;
}
// If error occurs we want to continue copying next files.
bool return_value = false;
for (auto const& dir : dirs) {
cmsys::Status const status =
cmSystemTools::CopyADirectory(arg, args.back(), when);
cmSystemTools::CopyADirectory(dir, *targetArg, when);
if (!status) {
std::cerr << "Error copying directory from \"" << arg << "\" to \""
<< args.back() << "\": " << status.GetString() << '\n';
std::cerr << "Error copying directory from \"" << dir << "\" to \""
<< *targetArg << "\": " << status.GetString() << '\n';
return_value = true;
}
}
@@ -0,0 +1 @@
^Error: No source directories specified \(for copy_directory command\)\.$
@@ -0,0 +1 @@
^Error: No source directories specified \(for copy_directory_if_different command\)\.$
@@ -0,0 +1 @@
^Error: No source directories specified \(for copy_directory_if_newer command\)\.$
@@ -690,8 +690,16 @@ file(REMOVE_RECURSE "${out}")
file(MAKE_DIRECTORY ${out})
run_cmake_command(E_copy_directory_if_different
${CMAKE_COMMAND} -E copy_directory_if_different ${in} ${out})
run_cmake_command(E_copy_directory_if_different-t-argument
${CMAKE_COMMAND} -E copy_directory_if_different -t ${out} ${in})
run_cmake_command(E_copy_directory_if_different-t-argument-no-source-dirs
${CMAKE_COMMAND} -E copy_directory_if_different -t ${out})
run_cmake_command(E_copy_directory_if_newer
${CMAKE_COMMAND} -E copy_directory_if_newer ${in} ${out})
run_cmake_command(E_copy_directory_if_newer-t-argument
${CMAKE_COMMAND} -E copy_directory_if_newer -t ${out} ${in})
run_cmake_command(E_copy_directory_if_newer-t-argument-no-source-dirs
${CMAKE_COMMAND} -E copy_directory_if_newer -t ${out})
run_cmake_command(E_copy_directory_if_newer-nonexistent-source
${CMAKE_COMMAND} -E copy_directory_if_newer ${in}/nonexistent ${out}/target)
unset(in)
@@ -709,6 +717,10 @@ run_cmake_command(E_copy_directory-three-source-files-target-is-file
${CMAKE_COMMAND} -E copy_directory ${in}/d1 ${in}/d2 ${in}/d3 ${outfile})
run_cmake_command(E_copy_directory-three-source-files-target-is-not-exist
${CMAKE_COMMAND} -E copy_directory ${in}/d1 ${in}/d2 ${in}/d3 ${out}/not_existing_directory)
run_cmake_command(E_copy_directory-t-argument
${CMAKE_COMMAND} -E copy_directory -t ${out} ${in}/d1 ${in}/d2 ${in}/d3)
run_cmake_command(E_copy_directory-t-argument-no-source-dirs
${CMAKE_COMMAND} -E copy_directory -t ${out})
unset(in)
unset(out)
unset(outfile)