Skip to content

Commit 2fad009

Browse files
cmake: Add -E create_hardlink
Fixes: #20950 Signed-off-by: Sibi Siddharthan <sibisiddharthan.github@gmail.com>
1 parent 2da7786 commit 2fad009

13 files changed

+90
-0
lines changed

Help/manual/cmake.1.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,13 @@ Available commands are:
598598
.. note::
599599
Path to where ``<new>`` symbolic link will be created has to exist beforehand.
600600

601+
``create_hardlink <old> <new>``
602+
Create a hard link ``<new>`` naming ``<old>``.
603+
604+
.. note::
605+
Path to where ``<new>`` hard link will be created has to exist beforehand.
606+
``<old>`` has to exist beforehand.
607+
601608
``echo [<string>...]``
602609
Displays arguments as text.
603610

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
cmake-E-create_hardlink
2+
-----------------------
3+
4+
* The :manual:`cmake(1)` gained a ``-E create_hardlink`` command-line tool
5+
that can be used to create hardlinks between files.

Source/cmcmd.cxx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ void CMakeCommandUsage(const char* program)
126126
<< " touch <file>... - touch a <file>.\n"
127127
<< " touch_nocreate <file>... - touch a <file> but do not create it.\n"
128128
<< " create_symlink old new - create a symbolic link new -> old\n"
129+
<< " create_hardlink old new - create a hard link new -> old\n"
129130
<< " true - do nothing with an exit code of 0\n"
130131
<< " false - do nothing with an exit code of 1\n"
131132
#if defined(_WIN32) && !defined(__CYGWIN__)
@@ -1034,6 +1035,34 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args)
10341035
return 0;
10351036
}
10361037

1038+
// Command to create a hard link. Fails on platforms not
1039+
// supporting them.
1040+
if (args[1] == "create_hardlink" && args.size() == 4) {
1041+
const char* SouceFileName = args[2].c_str();
1042+
const char* destinationFileName = args[3].c_str();
1043+
1044+
if (!cmSystemTools::FileExists(SouceFileName)) {
1045+
std::cerr << "failed to create hard link because source path '"
1046+
<< SouceFileName << "' does not exist \n";
1047+
return 1;
1048+
}
1049+
1050+
if ((cmSystemTools::FileExists(destinationFileName) ||
1051+
cmSystemTools::FileIsSymlink(destinationFileName)) &&
1052+
!cmSystemTools::RemoveFile(destinationFileName)) {
1053+
std::string emsg = cmSystemTools::GetLastSystemError();
1054+
std::cerr << "failed to create hard link '" << destinationFileName
1055+
<< "' because existing path cannot be removed: " << emsg
1056+
<< "\n";
1057+
return 1;
1058+
}
1059+
1060+
if (!cmSystemTools::CreateLink(args[2], args[3])) {
1061+
return 1;
1062+
}
1063+
return 0;
1064+
}
1065+
10371066
// Command to do nothing with an exit code of 0.
10381067
if (args[1] == "true") {
10391068
return 0;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
^CMake Error: cmake version .*
2+
Usage: .* -E <command> \[arguments\.\.\.\]
3+
Available commands:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
^CMake Error: failed to create link .* no such file or directory
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
^failed to create hard link because source path .* does not exist
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
if(${actual_stderr_var} MATCHES "operation not permitted")
2+
unset(msg)
3+
endif()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
^failed to create hard link because source path .* does not exist

Tests/RunCMake/CommandLine/RunCMakeTest.cmake

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,42 @@ run_cmake_command(E_create_symlink-no-replace-dir
348348
${CMAKE_COMMAND} -E create_symlink T .
349349
)
350350

351+
#create hard link tests
352+
run_cmake_command(E_create_hardlink-no-arg
353+
${CMAKE_COMMAND} -E create_hardlink
354+
)
355+
356+
set(dir ${RunCMake_BINARY_DIR}/hardlink_tests)
357+
file(REMOVE_RECURSE "${dir}")
358+
file(MAKE_DIRECTORY ${dir})
359+
360+
run_cmake_command(E_create_hardlink-non-existent-source
361+
${CMAKE_COMMAND} -E create_hardlink ${dir}/I_dont_exist ${dir}/link
362+
)
363+
364+
file(TOUCH ${dir}/1)
365+
366+
run_cmake_command(E_create_hardlink-ok
367+
${CMAKE_COMMAND} -E create_hardlink ${dir}/1 ${dir}/1-link
368+
)
369+
370+
run_cmake_command(E_create_hardlink-no-directory
371+
${CMAKE_COMMAND} -E create_hardlink ${dir}/1 ${dir}/a/1-link
372+
)
373+
374+
#On Windows, if the user does not have sufficient privileges
375+
#don't fail this test
376+
set(RunCMake_DEFAULT_stderr "(operation not permitted)?")
377+
run_cmake_command(E_create_hardlink-unresolved-symlink-prereq
378+
${CMAKE_COMMAND} -E create_symlink ${dir}/1 ${dir}/1-symlink
379+
)
380+
file(REMOVE ${dir}/1)
381+
382+
run_cmake_command(E_create_hardlink-unresolved-symlink
383+
${CMAKE_COMMAND} -E create_hardlink ${dir}/1-symlink ${dir}/1s-link
384+
)
385+
unset(RunCMake_DEFAULT_stderr)
386+
351387
set(in ${RunCMake_SOURCE_DIR}/copy_input)
352388
set(out ${RunCMake_BINARY_DIR}/copy_output)
353389
file(REMOVE_RECURSE "${out}")

0 commit comments

Comments
 (0)