cmake: Improve '-E create_symlink' edge case handling (#14713)

The logic added by commit ffc0b5e4 (Overwrite the symlink if it already
exists, 2007-02-15) does not recognize and remove existing broken links
before replacing them.  Improve the logic to remove any existing
destination file or link (but not directory).  On failure, report an
error message explaining why the existing path could not be removed or
the new one could not be created.

Add a RunCMake.CommandLine test to cover 'cmake -E' cases.  Start with
test cases covering 'cmake -E create_symlink' behavior on UNIX platforms.
This commit is contained in:
Brad King 2014-01-20 13:50:31 -05:00
parent d4ca30ae15
commit e0228e2b04
9 changed files with 56 additions and 15 deletions

View File

@ -460,23 +460,25 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
else if (args[1] == "create_symlink" && args.size() == 4) else if (args[1] == "create_symlink" && args.size() == 4)
{ {
const char* destinationFileName = args[3].c_str(); const char* destinationFileName = args[3].c_str();
if ( cmSystemTools::FileExists(destinationFileName) ) if((cmSystemTools::FileExists(destinationFileName) ||
cmSystemTools::FileIsSymlink(destinationFileName)) &&
!cmSystemTools::RemoveFile(destinationFileName))
{ {
if ( cmSystemTools::FileIsSymlink(destinationFileName) ) std::string emsg = cmSystemTools::GetLastSystemError();
{ std::cerr <<
if ( !cmSystemTools::RemoveFile(destinationFileName) || "failed to create symbolic link '" << destinationFileName <<
cmSystemTools::FileExists(destinationFileName) ) "' because existing path cannot be removed: " << emsg << "\n";
{ return 1;
return 0;
}
}
else
{
return 0;
}
} }
return cmSystemTools::CreateSymlink(args[2].c_str(), if(!cmSystemTools::CreateSymlink(args[2].c_str(), args[3].c_str()))
args[3].c_str())? 0:1; {
std::string emsg = cmSystemTools::GetLastSystemError();
std::cerr <<
"failed to create symbolic link '" << destinationFileName <<
"': " << emsg << "\n";
return 1;
}
return 0;
} }
// Internal CMake shared library support. // Internal CMake shared library support.

View File

@ -111,5 +111,6 @@ add_RunCMake_test(File_Generate)
add_RunCMake_test(ExportWithoutLanguage) add_RunCMake_test(ExportWithoutLanguage)
add_RunCMake_test(target_link_libraries) add_RunCMake_test(target_link_libraries)
add_RunCMake_test(CheckModules) add_RunCMake_test(CheckModules)
add_RunCMake_test(CommandLine)
add_RunCMake_test(install) add_RunCMake_test(install)

View File

@ -0,0 +1,6 @@
if(NOT IS_SYMLINK ${RunCMake_TEST_BINARY_DIR}/L)
set(RunCMake_TEST_FAILED "Symlink 'L' incorrectly not created!")
endif()
if(EXISTS ${RunCMake_TEST_BINARY_DIR}/L)
set(RunCMake_TEST_FAILED "Symlink 'L' not broken!")
endif()

View File

@ -0,0 +1,3 @@
if(NOT IS_DIRECTORY ${RunCMake_TEST_BINARY_DIR}/L)
set(RunCMake_TEST_FAILED "Symlink 'L' not replaced correctly!")
endif()

View File

@ -0,0 +1 @@
failed to create symbolic link 'missing-dir/L':

View File

@ -0,0 +1 @@
failed to create symbolic link '\.' because existing path cannot be removed:

View File

@ -0,0 +1,25 @@
include(RunCMake)
if(UNIX)
run_cmake_command(E_create_symlink-missing-dir
${CMAKE_COMMAND} -E create_symlink T missing-dir/L
)
# Use a single build tree for a few tests without cleaning.
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/E_create_symlink-broken-build)
set(RunCMake_TEST_NO_CLEAN 1)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
run_cmake_command(E_create_symlink-broken-create
${CMAKE_COMMAND} -E create_symlink T L
)
run_cmake_command(E_create_symlink-broken-replace
${CMAKE_COMMAND} -E create_symlink . L
)
unset(RunCMake_TEST_BINARY_DIR)
unset(RunCMake_TEST_NO_CLEAN)
run_cmake_command(E_create_symlink-no-replace-dir
${CMAKE_COMMAND} -E create_symlink T .
)
endif()