file(READ_SYMLINK): Add RESULT option to capture errors

Closes: #27753
This commit is contained in:
scivision
2026-04-17 10:51:28 -04:00
committed by Brad King
parent b5b72b0378
commit 3f3ba04d81
11 changed files with 67 additions and 10 deletions
+12 -4
View File
@@ -43,7 +43,7 @@ Synopsis
file(`COPY_FILE`_ <oldname> <newname> [...])
file({`COPY`_ | `INSTALL`_} <file>... DESTINATION <dir> [...])
file(`SIZE`_ <filename> <out-var>)
file(`READ_SYMLINK`_ <linkname> <out-var>)
file(`READ_SYMLINK`_ <linkname> <out-var> [...])
file(`CREATE_LINK`_ <original> <linkname> [...])
file(`CHMOD`_ <files>... <directories>... PERMISSIONS <permissions>... [...])
file(`CHMOD_RECURSE`_ <files>... <directories>... PERMISSIONS <permissions>... [...])
@@ -577,13 +577,21 @@ Filesystem
pointing to a file and is readable.
.. signature::
file(READ_SYMLINK <linkname> <variable>)
file(READ_SYMLINK <linkname> <variable> [RESULT <result>])
.. versionadded:: 3.14
Query the symlink ``<linkname>`` and stores the path it points to
in the result ``<variable>``. If ``<linkname>`` does not exist
or is not a symlink, CMake issues a fatal error.
in the result ``<variable>``.
The options are:
``RESULT <result>``
.. versionadded:: 4.4
Capture the status of the operation in a ``<result>`` variable. The
variable is set to ``0`` on success or an error message otherwise.
If not specified, and the operation fails, a fatal error is emitted.
Note that this command returns the raw symlink path and does not resolve
a relative path. The following is an example of how to ensure that an
+5
View File
@@ -0,0 +1,5 @@
read-symlink-result
-------------------
* The :command:`file(READ_SYMLINK)` command gained a ``RESULT``
option to capture errors without failing.
+28 -3
View File
@@ -3165,7 +3165,7 @@ bool HandleSizeCommand(std::vector<std::string> const& args,
bool HandleReadSymlinkCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
if (args.size() != 3) {
if (args.size() < 3) {
status.SetError(
cmStrCat(args[0], " requires a file name and output variable"));
return false;
@@ -3174,14 +3174,39 @@ bool HandleReadSymlinkCommand(std::vector<std::string> const& args,
std::string const& filename = args[1];
std::string const& outputVariable = args[2];
struct Arguments
{
std::string Result;
};
static auto const parser =
cmArgumentParser<Arguments>{}.Bind("RESULT"_s, &Arguments::Result);
std::vector<std::string> unconsumedArgs;
Arguments const arguments =
parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
if (!unconsumedArgs.empty()) {
status.SetError(
cmStrCat("READ_SYMLINK unknown argument:\n ", unconsumedArgs.front()));
return false;
}
std::string result;
if (!cmSystemTools::ReadSymlink(filename, result)) {
status.SetError(cmStrCat(
"READ_SYMLINK requested of path that is not a symlink:\n ", filename));
std::string const error = cmStrCat(
"READ_SYMLINK requested of path that is not a symlink:\n ", filename);
if (!arguments.Result.empty()) {
status.GetMakefile().AddDefinition(arguments.Result, error);
return true;
}
status.SetError(error);
return false;
}
status.GetMakefile().AddDefinition(outputVariable, result);
if (!arguments.Result.empty()) {
status.GetMakefile().AddDefinition(arguments.Result, "0");
}
return true;
}
@@ -1 +1,2 @@
^-- target: '[^']*/Tests/RunCMake/file/READ_SYMLINK-target.txt'$
^-- result: '0'
-- target: '[^']*/Tests/RunCMake/file/READ_SYMLINK-target.txt'$
@@ -1,5 +1,6 @@
file(READ_SYMLINK "${link}" target)
file(READ_SYMLINK "${link}" target RESULT result)
if(CMAKE_HOST_WIN32)
string(REPLACE [[\]] [[/]] target "${target}")
endif()
message(STATUS "result: '${result}'")
message(STATUS "target: '${target}'")
@@ -0,0 +1,2 @@
-- result: 'READ_SYMLINK requested of path that is not a symlink:
[^']*/Tests/RunCMake/file/READ_SYMLINK-noexist-capture-build/rel\.sym'
@@ -0,0 +1,5 @@
file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/rel.sym" target RESULT result)
message(STATUS "result: '${result}'")
if(DEFINED target)
message(FATAL_ERROR "Target variable should not be defined on failure, got:\n '${target}'")
endif()
@@ -0,0 +1,2 @@
-- result: 'READ_SYMLINK requested of path that is not a symlink:
[^']*/Tests/RunCMake/file/READ_SYMLINK-notsymlink-capture-build/rel\.sym'
@@ -0,0 +1,5 @@
file(READ_SYMLINK "${CMAKE_CURRENT_BINARY_DIR}/rel.sym" target RESULT result)
message(STATUS "result: '${result}'")
if(DEFINED target)
message(FATAL_ERROR "Target variable should not be defined on failure, got:\n '${target}'")
endif()
@@ -1 +1,2 @@
^-- target: 'READ_SYMLINK-target.txt'$
^-- result: '0'
-- target: 'READ_SYMLINK-target.txt'$
+2
View File
@@ -121,7 +121,9 @@ if(READ_SYMLINK-link-abs)
run_cmake_script(READ_SYMLINK-abs "-Dlink=${READ_SYMLINK-link-abs}")
endif()
run_cmake_script(READ_SYMLINK-noexist)
run_cmake_script(READ_SYMLINK-noexist-capture)
run_cmake_script(READ_SYMLINK-notsymlink)
run_cmake_script(READ_SYMLINK-notsymlink-capture)
run_cmake(REAL_PATH-non-existing)
run_cmake(REAL_PATH-existing)