mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-17 07:11:52 +08:00
cmake -E: Add rm with improved semantics over remove and remove_directory
This commit is contained in:

committed by
Kyle Edwards

parent
449fceeb1f
commit
5239fc5c75
@@ -554,22 +554,38 @@ Available commands are:
|
||||
7a0b54896fe5e70cca6dd643ad6f672614b189bf26f8153061c4d219474b05dad08c4e729af9f4b009f1a1a280cb625454bf587c690f4617c27e3aebdf3b7a2d file2.txt
|
||||
|
||||
``remove [-f] <file>...``
|
||||
Remove the file(s). If any of the listed files already do not
|
||||
exist, the command returns a non-zero exit code, but no message
|
||||
is logged. The ``-f`` option changes the behavior to return a
|
||||
.. deprecated:: 3.17
|
||||
|
||||
Remove the file(s). The planned behaviour was that if any of the
|
||||
listed files already do not exist, the command returns a non-zero exit code,
|
||||
but no message is logged. The ``-f`` option changes the behavior to return a
|
||||
zero exit code (i.e. success) in such situations instead.
|
||||
``remove`` does not follow symlinks. That means it remove only symlinks
|
||||
and not files it point to.
|
||||
|
||||
The implementation was buggy and always returned 0. It cannot be fixed without
|
||||
breaking backwards compatibility. Use ``rm`` instead.
|
||||
|
||||
``remove_directory <dir>...``
|
||||
Remove ``<dir>`` directories and their contents. If a directory does
|
||||
.. deprecated:: 3.17
|
||||
|
||||
Remove ``<dir>`` directories and their contents. If a directory does
|
||||
not exist it will be silently ignored. If ``<dir>`` is a symlink to
|
||||
a directory, just the symlink will be removed.
|
||||
Use ``rm`` instead.
|
||||
|
||||
``rename <oldname> <newname>``
|
||||
Rename a file or directory (on one volume). If file with the ``<newname>`` name
|
||||
already exists, then it will be silently replaced.
|
||||
|
||||
``rm [-rRf] <file> <dir>...``
|
||||
Remove the files ``<file>`` or directories ``dir``.
|
||||
Use ``-r`` or ``-R`` to remove directories and their contents recursively.
|
||||
If any of the listed files/directories do not exist, the command returns a
|
||||
non-zero exit code, but no message is logged. The ``-f`` option changes
|
||||
the behavior to return a zero exit code (i.e. success) in such
|
||||
situations instead.
|
||||
|
||||
``server``
|
||||
Launch :manual:`cmake-server(7)` mode.
|
||||
|
||||
|
12
Help/release/dev/command_rm.rst
Normal file
12
Help/release/dev/command_rm.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
Command-Line
|
||||
--------------------
|
||||
|
||||
* :manual:`cmake(1)` gained a ``rm`` command line
|
||||
option that can be used to remove directories (with ``-r`` or ``-R`` flag)
|
||||
and files.
|
||||
If the ``-f`` flag is not specified, attempting to remove a file that
|
||||
doesn't exist returns an non-zero error code.
|
||||
This command deprecates ``remove`` and ``remove_directory``.
|
||||
The ``remove`` implementation was buggy and always returned 0 when ``force``
|
||||
flag was not present and a file didn't exist. It cannot be fixed without
|
||||
breaking backwards compatibility so we introduced ``rm``.
|
@@ -107,10 +107,12 @@ void CMakeCommandUsage(const char* program)
|
||||
<< " sha384sum <file>... - create SHA384 checksum of files\n"
|
||||
<< " sha512sum <file>... - create SHA512 checksum of files\n"
|
||||
<< " remove [-f] <file>... - remove the file(s), use -f to force "
|
||||
"it\n"
|
||||
<< " remove_directory <dir>... - remove directories and their contents\n"
|
||||
"it (deprecated: use rm instead)\n"
|
||||
<< " remove_directory <dir>... - remove directories and their contents (deprecated: use rm instead)\n"
|
||||
<< " rename oldname newname - rename a file or directory "
|
||||
"(on one volume)\n"
|
||||
<< " rm [-rRf] <file/dir>... - remove files or directories, use -f to "
|
||||
"force it, r or R to remove directories and their contents recursively\n"
|
||||
<< " server - start cmake in server mode\n"
|
||||
<< " sleep <number>... - sleep for given number of seconds\n"
|
||||
<< " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n"
|
||||
@@ -172,6 +174,24 @@ static bool cmTarFilesFrom(std::string const& file,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cmRemoveDirectory(const std::string& dir, bool recursive = true)
|
||||
{
|
||||
if (cmSystemTools::FileIsSymlink(dir)) {
|
||||
if (!cmSystemTools::RemoveFile(dir)) {
|
||||
std::cerr << "Error removing directory symlink \"" << dir << "\".\n";
|
||||
return false;
|
||||
}
|
||||
} else if (!recursive) {
|
||||
std::cerr << "Error removing directory \"" << dir
|
||||
<< "\" without recursive option.\n";
|
||||
return false;
|
||||
} else if (!cmSystemTools::RemoveADirectory(dir)) {
|
||||
std::cerr << "Error removing directory \"" << dir << "\".\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int HandleIWYU(const std::string& runCmd,
|
||||
const std::string& /* sourceFile */,
|
||||
const std::vector<std::string>& orig_cmd)
|
||||
@@ -706,14 +726,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args)
|
||||
bool return_value = false;
|
||||
for (auto const& arg : cmMakeRange(args).advance(2)) {
|
||||
if (cmSystemTools::FileIsDirectory(arg)) {
|
||||
if (cmSystemTools::FileIsSymlink(arg)) {
|
||||
if (!cmSystemTools::RemoveFile(arg)) {
|
||||
std::cerr << "Error removing directory symlink \"" << arg
|
||||
<< "\".\n";
|
||||
return_value = true;
|
||||
}
|
||||
} else if (!cmSystemTools::RemoveADirectory(arg)) {
|
||||
std::cerr << "Error removing directory \"" << arg << "\".\n";
|
||||
if (!cmRemoveDirectory(arg)) {
|
||||
return_value = true;
|
||||
}
|
||||
}
|
||||
@@ -739,6 +752,65 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remove directories or files with rm
|
||||
if (args[1] == "rm" && args.size() > 2) {
|
||||
// If an error occurs, we want to continue removing the remaining
|
||||
// files/directories.
|
||||
int return_value = 0;
|
||||
bool force = false;
|
||||
bool recursive = false;
|
||||
bool doing_options = true;
|
||||
bool at_least_one_file = false;
|
||||
for (auto const& arg : cmMakeRange(args).advance(2)) {
|
||||
if (doing_options && cmHasLiteralPrefix(arg, "-")) {
|
||||
if (arg == "--") {
|
||||
doing_options = false;
|
||||
}
|
||||
if (arg.find('f') != std::string::npos) {
|
||||
force = true;
|
||||
}
|
||||
if (arg.find_first_of("rR") != std::string::npos) {
|
||||
recursive = true;
|
||||
}
|
||||
if (arg.find_first_not_of("-frR") != std::string::npos) {
|
||||
cmSystemTools::Error("Unknown -E rm argument: " + arg);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (arg.empty()) {
|
||||
continue;
|
||||
}
|
||||
at_least_one_file = true;
|
||||
// Complain if the -f option was not given and
|
||||
// either file does not exist or
|
||||
// file could not be removed and still exists
|
||||
bool file_exists_or_forced_remove = cmSystemTools::FileExists(arg) ||
|
||||
cmSystemTools::FileIsSymlink(arg) || force;
|
||||
if (cmSystemTools::FileIsDirectory(arg)) {
|
||||
if (!cmRemoveDirectory(arg, recursive)) {
|
||||
return_value = 1;
|
||||
}
|
||||
} else if ((!file_exists_or_forced_remove) ||
|
||||
(!cmSystemTools::RemoveFile(arg) &&
|
||||
cmSystemTools::FileExists(arg))) {
|
||||
if (!file_exists_or_forced_remove) {
|
||||
cmSystemTools::Error(
|
||||
"File to remove does not exist and force is not set: " + arg);
|
||||
} else {
|
||||
cmSystemTools::Error("File can't be removed and still exist: " +
|
||||
arg);
|
||||
}
|
||||
return_value = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!at_least_one_file) {
|
||||
cmSystemTools::Error("Missing file/directory to remove");
|
||||
return 1;
|
||||
}
|
||||
return return_value;
|
||||
}
|
||||
|
||||
// Touch file
|
||||
if (args[1] == "touch" && args.size() > 2) {
|
||||
for (auto const& arg : cmMakeRange(args).advance(2)) {
|
||||
|
@@ -443,7 +443,7 @@ add_RunCMake_test(target_include_directories)
|
||||
add_RunCMake_test(target_sources)
|
||||
add_RunCMake_test(CheckModules)
|
||||
add_RunCMake_test(CheckIPOSupported)
|
||||
add_RunCMake_test(CommandLine -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
|
||||
add_RunCMake_test(CommandLine -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DCYGWIN=${CYGWIN})
|
||||
add_RunCMake_test(CommandLineTar)
|
||||
|
||||
if(CMAKE_PLATFORM_NO_VERSIONED_SONAME OR (NOT CMAKE_SHARED_LIBRARY_SONAME_FLAG AND NOT CMAKE_SHARED_LIBRARY_SONAME_C_FLAG))
|
||||
|
1
Tests/RunCMake/CommandLine/E_rm_bad_argument-result.txt
Normal file
1
Tests/RunCMake/CommandLine/E_rm_bad_argument-result.txt
Normal file
@@ -0,0 +1 @@
|
||||
1
|
1
Tests/RunCMake/CommandLine/E_rm_bad_argument-stderr.txt
Normal file
1
Tests/RunCMake/CommandLine/E_rm_bad_argument-stderr.txt
Normal file
@@ -0,0 +1 @@
|
||||
^CMake Error: Unknown -E rm argument: -rd$
|
@@ -0,0 +1,3 @@
|
||||
if(NOT EXISTS ${out}/dir/existing.txt)
|
||||
set(RunCMake_TEST_FAILED "${out}/dir/existing.txt should exist (we only removed the link to dir folder)")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1 @@
|
||||
^CMake Error: Missing file/directory to remove$
|
@@ -0,0 +1,8 @@
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E rm ""
|
||||
RESULT_VARIABLE actual_result
|
||||
)
|
||||
|
||||
if(NOT "${actual_result}" EQUAL "1")
|
||||
message(SEND_ERROR "cmake -E rm \"\" should have returned 1, got ${actual_result}")
|
||||
endif()
|
@@ -0,0 +1,3 @@
|
||||
if(EXISTS ${out}/existing.txt)
|
||||
set(RunCMake_TEST_FAILED "${out}/existing.txt not removed")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1,3 @@
|
||||
if(NOT EXISTS ${out}/existing.txt)
|
||||
set(RunCMake_TEST_FAILED "${out}/existing.txt should exist (we only removed the link)")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1,3 @@
|
||||
if(EXISTS ${out}/existing.txt)
|
||||
set(RunCMake_TEST_FAILED "${out}/existing.txt not removed")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -0,0 +1 @@
|
||||
^CMake Error: File to remove does not exist and force is not set: .*/rm_tests/not_existing.txt
|
@@ -0,0 +1,3 @@
|
||||
if(EXISTS ${out}/existing.txt)
|
||||
set(RunCMake_TEST_FAILED "${out}/existing.txt not removed")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -0,0 +1 @@
|
||||
^CMake Error: File to remove does not exist and force is not set: .*/rm_tests/not_existing.txt
|
@@ -0,0 +1,3 @@
|
||||
if(EXISTS ${out})
|
||||
set(RunCMake_TEST_FAILED "${out} not removed")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -0,0 +1 @@
|
||||
^CMake Error: Missing file/directory to remove$
|
@@ -0,0 +1,3 @@
|
||||
if(NOT EXISTS ${out}/d1 OR NOT EXISTS ${out}/d2)
|
||||
set(RunCMake_TEST_FAILED "${out}/d1 or ${out}/d2 is removed but should not")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -0,0 +1,2 @@
|
||||
^Error removing directory ".*/rm_tests/d1" without recursive option\.
|
||||
Error removing directory ".*/rm_tests/d2" without recursive option\.$
|
@@ -0,0 +1,3 @@
|
||||
if(EXISTS ${out}/d1 OR EXISTS ${out}/d2)
|
||||
set(RunCMake_TEST_FAILED "${out}/d1 or ${out}/d2 should be removed")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1,3 @@
|
||||
if(NOT EXISTS ${out}/dir/existing.txt)
|
||||
set(RunCMake_TEST_FAILED "${out}/dir/existing.txt should exist (we only removed the link to dir folder)")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1,3 @@
|
||||
if(NOT EXISTS ${out}/existing.txt)
|
||||
set(RunCMake_TEST_FAILED "${out}/existing.txt should exist (we only removed the link)")
|
||||
endif()
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -388,6 +388,76 @@ endif()
|
||||
unset(out)
|
||||
unset(outfile)
|
||||
|
||||
set(out ${RunCMake_BINARY_DIR}/rm_tests)
|
||||
file(REMOVE_RECURSE "${out}")
|
||||
file(MAKE_DIRECTORY ${out})
|
||||
file(TOUCH ${out}/existing.txt)
|
||||
run_cmake_command(E_rm_file_force_existing
|
||||
${CMAKE_COMMAND} -E rm -f ${out}/existing.txt)
|
||||
file(TOUCH ${out}/existing.txt)
|
||||
run_cmake_command(E_rm_file_non_force_existing
|
||||
${CMAKE_COMMAND} -E rm ${out}/existing.txt)
|
||||
run_cmake_command(E_rm_file_force_non_existing
|
||||
${CMAKE_COMMAND} -E rm -f ${out}/not_existing.txt)
|
||||
run_cmake_command(E_rm_file_non_force_non_existing
|
||||
${CMAKE_COMMAND} -E rm ${out}/not_existing.txt)
|
||||
|
||||
file(TOUCH ${out}/existing.txt)
|
||||
run_cmake_command(E_rm_file_recursive_existing
|
||||
${CMAKE_COMMAND} -E rm -r ${out}/existing.txt)
|
||||
run_cmake_command(E_rm_file_recursive_non_existing
|
||||
${CMAKE_COMMAND} -E rm -r ${out}/not_existing.txt)
|
||||
|
||||
file(MAKE_DIRECTORY ${out}/d1 ${out}/d2)
|
||||
run_cmake_command(E_rm_non_recursive_directory-two-directories
|
||||
${CMAKE_COMMAND} -E rm ${out}/d1 ${out}/d2)
|
||||
|
||||
run_cmake_command(E_rm_recursive_directory-two-directories
|
||||
${CMAKE_COMMAND} -E rm -R ${out}/d1 ${out}/d2)
|
||||
|
||||
run_cmake_command(E_rm_no_file_specified
|
||||
${CMAKE_COMMAND} -E rm -rf)
|
||||
|
||||
run_cmake_command(E_rm_empty_file_specified
|
||||
${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_rm_empty_file_specified.cmake)
|
||||
|
||||
run_cmake_command(E_rm_bad_argument
|
||||
${CMAKE_COMMAND} -E rm -rd ${out}/d1 ${out}/d2)
|
||||
|
||||
file(MAKE_DIRECTORY ${out}/d1 ${out}/d2)
|
||||
file(WRITE ${out}/test.txt "")
|
||||
run_cmake_command(E_rm_force_recursive_directory_with_files
|
||||
${CMAKE_COMMAND} -E rm -rf ${out}/)
|
||||
|
||||
run_cmake_command(E_rm_force_recursive_non_existing_file
|
||||
${CMAKE_COMMAND} -E rm -Rf ${out}/test.txt)
|
||||
|
||||
if(NOT WIN32 AND NOT CYGWIN)
|
||||
file(MAKE_DIRECTORY ${out})
|
||||
file(TOUCH ${out}/existing.txt)
|
||||
file(MAKE_DIRECTORY ${out}/dir)
|
||||
file(TOUCH ${out}/dir/existing.txt) # add a file in the folder
|
||||
file(CREATE_LINK ${out}/dir ${out}/link_dir SYMBOLIC)
|
||||
file(CREATE_LINK ${out}/existing.txt ${out}/existing_file_link.txt SYMBOLIC)
|
||||
file(CREATE_LINK ${out}/non_existing.txt ${out}/non_existing_file_link.txt SYMBOLIC)
|
||||
run_cmake_command(E_rm_file_link_existing
|
||||
${CMAKE_COMMAND} -E rm ${out}/existing_file_link.txt)
|
||||
run_cmake_command(E_rm_directory_link_existing
|
||||
${CMAKE_COMMAND} -E rm ${out}/link_dir)
|
||||
run_cmake_command(E_rm_file_link_non_existing
|
||||
${CMAKE_COMMAND} -E rm ${out}/non_existing_file_link.txt)
|
||||
|
||||
file(CREATE_LINK ${out}/dir ${out}/link_dir SYMBOLIC)
|
||||
file(CREATE_LINK ${out}/existing.txt ${out}/existing_file_link.txt SYMBOLIC)
|
||||
file(CREATE_LINK ${out}/non_existing.txt ${out}/non_existing_file_link.txt SYMBOLIC)
|
||||
run_cmake_command(E_rm_recursive_file_link_existing
|
||||
${CMAKE_COMMAND} -E rm -R ${out}/existing_file_link.txt)
|
||||
run_cmake_command(E_rm_recursive_directory_link_existing
|
||||
${CMAKE_COMMAND} -E rm -r ${out}/link_dir)
|
||||
run_cmake_command(E_rm_recursive_file_link_non_existing
|
||||
${CMAKE_COMMAND} -E rm -r ${out}/non_existing_file_link.txt)
|
||||
endif()
|
||||
unset(out)
|
||||
|
||||
run_cmake_command(E_env-no-command0 ${CMAKE_COMMAND} -E env)
|
||||
run_cmake_command(E_env-no-command1 ${CMAKE_COMMAND} -E env TEST_ENV=1)
|
||||
|
Reference in New Issue
Block a user