Commit 5239fc5c authored by Johnny Jazeix's avatar Johnny Jazeix Committed by Kyle Edwards

cmake -E: Add rm with improved semantics over remove and remove_directory

parent 449fceeb
......@@ -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.
......
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))
......
^CMake Error: Unknown -E rm argument: -rd$
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()
^CMake Error: Missing file/directory to remove$
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()
if(EXISTS ${out}/existing.txt)
set(RunCMake_TEST_FAILED "${out}/existing.txt not removed")
endif()
if(NOT EXISTS ${out}/existing.txt)
set(RunCMake_TEST_FAILED "${out}/existing.txt should exist (we only removed the link)")
endif()
if(EXISTS ${out}/existing.txt)
set(RunCMake_TEST_FAILED "${out}/existing.txt not removed")
endif()
^CMake Error: File to remove does not exist and force is not set: .*/rm_tests/not_existing.txt
if(EXISTS ${out}/existing.txt)
set(RunCMake_TEST_FAILED "${out}/existing.txt not removed")
endif()
^CMake Error: File to remove does not exist and force is not set: .*/rm_tests/not_existing.txt
if(EXISTS ${out})
set(RunCMake_TEST_FAILED "${out} not removed")
endif()
^CMake Error: Missing file/directory to remove$
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()
^Error removing directory ".*/rm_tests/d1" without recursive option\.
Error removing directory ".*/rm_tests/d2" without recursive option\.$
if(EXISTS ${out}/d1 OR EXISTS ${out}/d2)
set(RunCMake_TEST_FAILED "${out}/d1 or ${out}/d2 should be removed")
endif()
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()
if(NOT EXISTS ${out}/existing.txt)
set(RunCMake_TEST_FAILED "${out}/existing.txt should exist (we only removed the link)")
endif()
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment