cmake -E copy: Add support for -t argument

Fixes: #23543
This commit is contained in:
Kyle Edwards 2022-11-14 15:49:37 -05:00 committed by Brad King
parent f8107e7c6b
commit 8d9069e5b6
7 changed files with 82 additions and 12 deletions

View File

@ -848,17 +848,21 @@ Available commands are:
.. program:: cmake-E
.. option:: copy <file>... <destination>
.. option:: copy <file>... <destination>, copy -t <destination> <file>...
Copy files to ``<destination>`` (either file or directory).
If multiple files are specified, the ``<destination>`` must be
directory and it must exist. Wildcards are not supported.
``copy`` does follow symlinks. That means it does not copy symlinks,
but the files or directories it point to.
If multiple files are specified, or if ``-t`` is specified, the
``<destination>`` must be 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.
.. versionadded:: 3.5
Support for multiple input files.
.. versionadded:: 3.26
Support for ``-t`` argument.
.. option:: copy_directory <dir>... <destination>
Copy content of ``<dir>...`` directories to ``<destination>`` directory.

View File

@ -0,0 +1,4 @@
cmake-E-copy-t-arg
------------------
* The :option:`cmake -E copy <cmake-E copy>` argument now supports a ``-t`` argument.

View File

@ -2,6 +2,8 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
#include <cm/optional>
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@ -250,6 +252,15 @@ private:
return true;
};
}
static std::function<bool(const std::string&, CallState...)>
generateSetToValue(cm::optional<std::string>& value1)
{
return [&value1](const std::string& arg, CallState&&...) -> bool {
value1 = arg;
return true;
};
}
};
std::string extract_single_value(std::string const& input,

View File

@ -2,11 +2,15 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmcmd.h"
#include <functional>
#include <cm/optional>
#include <cmext/algorithm>
#include <cm3p/uv.h>
#include <fcntl.h>
#include "cmCommandLineArgument.h"
#include "cmConsoleBuf.h"
#include "cmDuration.h"
#include "cmGlobalGenerator.h"
@ -640,20 +644,59 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
if (args.size() > 1) {
// Copy file
if (args[1] == "copy" && args.size() > 3) {
using CommandArgument =
cmCommandLineArgument<bool(const std::string& value)>;
cm::optional<std::string> targetArg;
std::vector<CommandArgument> argParsers{
{ "-t", CommandArgument::Values::One,
CommandArgument::setToValue(targetArg) },
};
std::vector<std::string> files;
for (decltype(args.size()) i = 2; i < args.size(); i++) {
const std::string& 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) {
files.push_back(arg);
}
}
// If multiple source files specified,
// then destination must be directory
if ((args.size() > 4) &&
(!cmSystemTools::FileIsDirectory(args.back()))) {
std::cerr << "Error: Target (for copy command) \"" << args.back()
if (files.size() > 2 && !targetArg) {
targetArg = files.back();
files.pop_back();
}
if (targetArg && (!cmSystemTools::FileIsDirectory(*targetArg))) {
std::cerr << "Error: Target (for copy 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";
return 1;
}
targetArg = files.back();
files.pop_back();
}
// If error occurs we want to continue copying next files.
bool return_value = false;
for (auto const& arg : cmMakeRange(args).advance(2).retreat(1)) {
if (!cmsys::SystemTools::CopyFileAlways(arg, args.back())) {
std::cerr << "Error copying file \"" << arg << "\" to \""
<< args.back() << "\".\n";
for (auto const& file : files) {
if (!cmsys::SystemTools::CopyFileAlways(file, *targetArg)) {
std::cerr << "Error copying file \"" << file << "\" to \""
<< *targetArg << "\".\n";
return_value = true;
}
}

View File

@ -0,0 +1 @@
^Error: Target \(for copy command\).* is not a directory.$

View File

@ -574,6 +574,12 @@ run_cmake_command(E_copy-three-source-files-target-is-file
${CMAKE_COMMAND} -E copy ${in}/f1.txt ${in}/f2.txt ${in}/f3.txt ${out}/f1.txt)
run_cmake_command(E_copy-two-good-and-one-bad-source-files-target-is-directory
${CMAKE_COMMAND} -E copy ${in}/f1.txt ${in}/not_existing_file.bad ${in}/f3.txt ${out})
run_cmake_command(E_copy-t-argument
${CMAKE_COMMAND} -E copy ${in}/f1.txt -t ${out} ${in}/f3.txt)
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-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