Merge topic 'cmake-E-copy-t-flag'

16b8a1ba2a cmake -E copy_directory*: Add -t argument support
29dcbe4169 cmake -E copy_if_*: Add -t argument support

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !11693
This commit is contained in:
Brad King
2026-02-25 09:15:30 -05:00
committed by Kitware Robot
14 changed files with 179 additions and 94 deletions
+44 -17
View File
@@ -1218,11 +1218,12 @@ 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
``<destination>`` must be directory and it must exist. If ``-t`` is not
``<destination>`` must be a directory and it must exist. If ``-t`` is not
specified, the last argument is assumed to be the ``<destination>``.
Wildcards are not supported. ``copy`` does follow symlinks. That means it
does not copy symlinks, but the files or directories it point to.
@@ -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,52 +1250,75 @@ 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.
.. option:: copy_if_different <file>... <destination>
.. versionadded:: 4.4
Support for the ``-t`` argument.
.. option:: copy_if_different <file>... <destination>,
copy_if_different -t <destination> <file>...
Copy files to ``<destination>`` (either file or directory) if
they have changed.
If multiple files are specified, the ``<destination>`` must be
directory and it must exist.
If multiple files are specified, or if ``-t`` is specified,
the ``<destination>`` must be a directory and it must exist.
``copy_if_different`` does follow symlinks.
.. versionadded:: 3.5
Support for multiple input files.
.. option:: copy_if_newer <file>... <destination>
.. versionadded:: 4.4
Support for the ``-t`` argument.
.. option:: copy_if_newer <file>... <destination>,
copy_if_newer -t <destination> <file>...
.. versionadded:: 4.2
Copy files to ``<destination>`` (either file or directory) if
source files are newer than destination files (based on file timestamps).
If multiple files are specified, the ``<destination>`` must be
directory and it must exist.
If multiple files are specified, or if ``-t`` is specified,
the ``<destination>`` must be a directory and it must exist.
``copy_if_newer`` does follow symlinks.
This is faster than ``copy_if_different`` as it only compares
file timestamps instead of file contents.
.. versionadded:: 4.4
Support for the ``-t`` argument.
.. option:: create_symlink <old> <new>
Create a symbolic link ``<new>`` naming ``<old>``.
+10
View File
@@ -0,0 +1,10 @@
cmake-E-copy-t-flag
-------------------
* The :option:`cmake -E` commands
: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.
+91 -77
View File
@@ -105,13 +105,19 @@ char const* const HELP_AVAILABLE_COMMANDS = R"(Available commands:
cat [--] <files>... - concat the files and print them to the standard output
chdir dir cmd [args...] - run command in a given directory
compare_files [--ignore-eol] file1 file2
- check if file1 is same as file2
copy <file>... destination - 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_if_different <file>... destination - copy files if it has changed
copy_if_newer <file>... destination - copy files if source is newer than destination
- 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 | -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>...
- copy files if source is newer than destination
echo [<string>...] - displays arguments as text
echo_append [<string>...] - displays arguments as text but no new line
env [--unset=NAME ...] [NAME=VALUE ...] [--] <command> [<arg>...]
@@ -1005,8 +1011,26 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
{
// IF YOU ADD A NEW COMMAND, DOCUMENT IT ABOVE and in cmakemain.cxx
if (args.size() > 1) {
// Copy file
if (args[1] == "copy" && args.size() > 3) {
// Copy file, copy file if different, copy file if newer.
if ((args[1] == "copy" || args[1] == "copy_if_different" ||
args[1] == "copy_if_newer") &&
args.size() > 3) {
using CopyFn = cmsys::SystemTools::CopyStatus (*)(std::string const&,
std::string const&);
CopyFn copyFn;
std::string copyErrPrefix;
if (args[1] == "copy") {
copyFn = cmSystemTools::CopyFileAlways;
copyErrPrefix = "Error copying file";
} else if (args[1] == "copy_if_different") {
copyFn = cmSystemTools::CopyFileIfDifferent;
copyErrPrefix = "Error copying file (if different) from";
} else {
copyFn = cmSystemTools::CopyFileIfNewer;
copyErrPrefix = "Error copying file (if newer) from";
}
std::string const& cmdName = args[1];
using CommandArgument =
cmCommandLineArgument<bool(std::string const& value)>;
@@ -1041,14 +1065,14 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
files.pop_back();
}
if (targetArg && (!cmSystemTools::FileIsDirectory(*targetArg))) {
std::cerr << "Error: Target (for copy command) \"" << *targetArg
<< "\" is not a directory.\n";
std::cerr << "Error: Target (for " << cmdName << " command) \""
<< *targetArg << "\" is not a directory.\n";
return 1;
}
if (!targetArg) {
if (files.size() < 2) {
std::cerr
<< "Error: No files or target specified (for copy command).\n";
std::cerr << "Error: No files or target specified (for " << cmdName
<< " command).\n";
return 1;
}
targetArg = files.back();
@@ -1057,10 +1081,9 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
// If error occurs we want to continue copying next files.
bool return_value = false;
for (auto const& file : files) {
cmsys::SystemTools::CopyStatus const status =
cmSystemTools::CopyFileAlways(file, *targetArg);
cmsys::SystemTools::CopyStatus const status = copyFn(file, *targetArg);
if (!status) {
std::cerr << "Error copying file \"" << file << "\" to \""
std::cerr << copyErrPrefix << " \"" << file << "\" to \""
<< *targetArg << "\": " << status.GetString() << '\n';
return_value = true;
}
@@ -1068,77 +1091,68 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
return return_value;
}
// Copy file if different.
if (args[1] == "copy_if_different" && args.size() > 3) {
// If multiple source files specified,
// then destination must be directory
if ((args.size() > 4) &&
(!cmSystemTools::FileIsDirectory(args.back()))) {
std::cerr << "Error: Target (for copy_if_different command) \""
<< args.back() << "\" is not a directory.\n";
return 1;
}
// If error occurs we want to continue copying next files.
bool return_value = false;
for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) {
cmsys::SystemTools::CopyStatus const status =
cmSystemTools::CopyFileIfDifferent(arg, args.back());
if (!status) {
std::cerr << "Error copying file (if different) from \"" << arg
<< "\" to \"" << args.back()
<< "\": " << status.GetString() << '\n';
return_value = true;
}
}
return return_value;
}
// Copy file if newer.
if (args[1] == "copy_if_newer" && args.size() > 3) {
// If multiple source files specified,
// then destination must be directory
if ((args.size() > 4) &&
(!cmSystemTools::FileIsDirectory(args.back()))) {
std::cerr << "Error: Target (for copy_if_newer command) \""
<< args.back() << "\" is not a directory.\n";
return 1;
}
// If error occurs we want to continue copying next files.
bool return_value = false;
for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) {
cmsys::SystemTools::CopyStatus const status =
cmSystemTools::CopyFileIfNewer(arg, args.back());
if (!status) {
std::cerr << "Error copying file (if newer) from \"" << arg
<< "\" to \"" << args.back()
<< "\": " << status.GetString() << '\n';
return_value = true;
}
}
return return_value;
}
// 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\)\.$
@@ -0,0 +1 @@
^Error: Target \(for copy_if_different command\).* is not a directory\.$
@@ -0,0 +1 @@
^Error: Target \(for copy_if_newer command\).* is not a directory\.$
@@ -653,6 +653,12 @@ run_cmake_command(E_copy-t-argument-target-is-file
${CMAKE_COMMAND} -E copy ${in}/f1.txt -t ${out}/f1.txt ${in}/f3.txt)
run_cmake_command(E_copy-t-argument-no-source-files
${CMAKE_COMMAND} -E copy -t ${out})
run_cmake_command(E_copy_if_different-t-argument
${CMAKE_COMMAND} -E copy_if_different ${in}/f1.txt -t ${out} ${in}/f3.txt)
run_cmake_command(E_copy_if_different-t-argument-target-is-file
${CMAKE_COMMAND} -E copy_if_different ${in}/f1.txt -t ${out}/f1.txt ${in}/f3.txt)
run_cmake_command(E_copy_if_different-t-argument-no-source-files
${CMAKE_COMMAND} -E copy_if_different -t ${out})
run_cmake_command(E_copy_if_different-one-source-directory-target-is-directory
${CMAKE_COMMAND} -E copy_if_different ${in}/f1.txt ${out})
run_cmake_command(E_copy_if_different-three-source-files-target-is-directory
@@ -661,6 +667,12 @@ run_cmake_command(E_copy_if_different-three-source-files-target-is-file
${CMAKE_COMMAND} -E copy_if_different ${in}/f1.txt ${in}/f2.txt ${in}/f3.txt ${out}/f1.txt)
run_cmake_command(E_copy_if_different-nonexistent-source
${CMAKE_COMMAND} -E copy_if_different ${in}/nonexistent.txt ${out})
run_cmake_command(E_copy_if_newer-t-argument
${CMAKE_COMMAND} -E copy_if_newer ${in}/f1.txt -t ${out} ${in}/f3.txt)
run_cmake_command(E_copy_if_newer-t-argument-target-is-file
${CMAKE_COMMAND} -E copy_if_newer ${in}/f1.txt -t ${out}/f1.txt ${in}/f3.txt)
run_cmake_command(E_copy_if_newer-t-argument-no-source-files
${CMAKE_COMMAND} -E copy_if_newer -t ${out})
run_cmake_command(E_copy_if_newer-one-source-directory-target-is-directory
${CMAKE_COMMAND} -E copy_if_newer ${in}/f1.txt ${out})
run_cmake_command(E_copy_if_newer-three-source-files-target-is-directory
@@ -678,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)
@@ -697,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)