if(): add operators IS_READABLE, IS_WRITABLE and IS_EXECUTABLE.

Offers possibility to check for file or directory permissions.
This commit is contained in:
Marc Chevrier 2023-10-21 17:16:13 +02:00
parent fdf6435427
commit 337bc5662c
6 changed files with 238 additions and 1 deletions

View File

@ -178,6 +178,47 @@ File Operations
False if the given path is an empty string.
.. warning::
To check the readability of a file, use preferably ``if(IS_READABLE)``
because this test will evolve to check file existence only in a future
release.
.. signature:: if(IS_READABLE <path-to-file-or-directory>)
.. versionadded:: 3.29
True if the named file or directory is readable. Behavior
is well-defined only for explicit full paths (a leading ``~/`` is not
expanded as a home directory and is considered a relative path).
Resolves symbolic links, i.e. if the named file or directory is a
symbolic link, returns true if the target of the symbolic link is readable.
False if the given path is an empty string.
.. signature:: if(IS_WRITABLE <path-to-file-or-directory>)
.. versionadded:: 3.29
True if the named file or directory is writable. Behavior
is well-defined only for explicit full paths (a leading ``~/`` is not
expanded as a home directory and is considered a relative path).
Resolves symbolic links, i.e. if the named file or directory is a
symbolic link, returns true if the target of the symbolic link is writable.
False if the given path is an empty string.
.. signature:: if(IS_EXECUTABLE <path-to-file-or-directory>)
.. versionadded:: 3.29
True if the named file or directory is executable. Behavior
is well-defined only for explicit full paths (a leading ``~/`` is not
expanded as a home directory and is considered a relative path).
Resolves symbolic links, i.e. if the named file or directory is a
symbolic link, returns true if the target of the symbolic link is executable.
False if the given path is an empty string.
.. signature:: if(<file1> IS_NEWER_THAN <file2>)
:target: IS_NEWER_THAN

View File

@ -0,0 +1,5 @@
if-check-file-permissions
-------------------------
* The :command:`if` command gained new tests ``IS_READABLE``, ``IS_WRITABLE``
and ``IS_EXECUTABLE`` to check file or directory permissions.

View File

@ -33,6 +33,9 @@ auto const keyCOMMAND = "COMMAND"_s;
auto const keyDEFINED = "DEFINED"_s;
auto const keyEQUAL = "EQUAL"_s;
auto const keyEXISTS = "EXISTS"_s;
auto const keyIS_READABLE = "IS_READABLE"_s;
auto const keyIS_WRITABLE = "IS_WRITABLE"_s;
auto const keyIS_EXECUTABLE = "IS_EXECUTABLE"_s;
auto const keyGREATER = "GREATER"_s;
auto const keyGREATER_EQUAL = "GREATER_EQUAL"_s;
auto const keyIN_LIST = "IN_LIST"_s;
@ -568,6 +571,24 @@ bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&,
newArgs.ReduceOneArg(cmSystemTools::FileExists(args.next->GetValue()),
args);
}
// check if a file is readable
else if (this->IsKeyword(keyIS_READABLE, *args.current)) {
newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
args.next->GetValue(), cmsys::TEST_FILE_READ),
args);
}
// check if a file is writable
else if (this->IsKeyword(keyIS_WRITABLE, *args.current)) {
newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
args.next->GetValue(), cmsys::TEST_FILE_WRITE),
args);
}
// check if a file is executable
else if (this->IsKeyword(keyIS_EXECUTABLE, *args.current)) {
newArgs.ReduceOneArg(cmSystemTools::TestFileAccess(
args.next->GetValue(), cmsys::TEST_FILE_EXECUTE),
args);
}
// does a directory with this name exist
else if (this->IsKeyword(keyIS_DIRECTORY, *args.current)) {
newArgs.ReduceOneArg(

View File

@ -535,7 +535,7 @@ add_RunCMake_test(function)
add_RunCMake_test(block)
add_RunCMake_test(get_filename_component)
add_RunCMake_test(get_property)
add_RunCMake_test(if)
add_RunCMake_test(if -DMSYS=${MSYS})
add_RunCMake_test(include)
add_RunCMake_test(include_directories)
add_RunCMake_test(include_guard)

View File

@ -0,0 +1,153 @@
file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
"${CMAKE_CURRENT_BINARY_DIR}/writable.txt"
"${CMAKE_CURRENT_BINARY_DIR}/executable.txt")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" "foo")
file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/readable.txt" PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" "foo")
file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/writable.txt" PERMISSIONS OWNER_WRITE GROUP_WRITE WORLD_WRITE)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" "foo")
file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/executable.txt" PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
if(NOT WIN32)
file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir"
"${CMAKE_CURRENT_BINARY_DIR}/writable-dir"
"${CMAKE_CURRENT_BINARY_DIR}/executable-dir")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/readable-dir")
file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/readable-dir" PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/writable-dir")
file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/writable-dir" PERMISSIONS OWNER_WRITE GROUP_WRITE WORLD_WRITE)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/executable-dir")
file(CHMOD "${CMAKE_CURRENT_BINARY_DIR}/executable-dir" PERMISSIONS OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
endif()
if(WIN32)
# files are always readable and executable
# directories are always, readable, writable and executable
if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/readable.txt\" failed")
endif()
if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/executable.txt\" failed")
endif()
else()
if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/readable.txt")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/readable.txt\" failed")
endif()
if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt"
OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt"
OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/writable.txt")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/writable.txt\" failed")
endif()
if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt"
OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/executable.txt")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/executable.txt\" failed")
endif()
if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir"
OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/readable-dir")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/readable-dir\" failed")
endif()
if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/writable-dir"
OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/writable-dir"
OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/writable-dir")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/writable-dir\" failed")
endif()
if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/executable-dir"
OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/executable-dir"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/executable-dir")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/executable.txt\" failed")
endif()
endif()
if(UNIX)
#
# Check that file permissions are on the real file, not the symbolic link
#
file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir")
file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/readable.txt"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt"
SYMBOLIC)
file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/writable.txt"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt"
SYMBOLIC)
file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/executable.txt"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt"
SYMBOLIC)
file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/readable-dir"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir"
SYMBOLIC)
file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/writable-dir"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir"
SYMBOLIC)
file(CREATE_LINK "${CMAKE_CURRENT_BINARY_DIR}/executable-dir"
"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir"
SYMBOLIC)
if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt"
OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-readable.txt\" failed")
endif()
if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt"
OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt"
OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable.txt\" failed")
endif()
if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt"
OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable.txt\" failed")
endif()
if(NOT IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir"
OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-readable-dir\" failed")
endif()
if(NOT IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir"
OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir"
OR IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-writable-dir\" failed")
endif()
if(NOT IS_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir"
OR IS_READABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir"
OR IS_WRITABLE "${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir")
message(FATAL_ERROR "checks on \"${CMAKE_CURRENT_BINARY_DIR}/link-to-executable-dir\" failed")
endif()
endif()

View File

@ -2,6 +2,23 @@ include(RunCMake)
run_cmake(InvalidArgument1)
run_cmake(exists)
if(NOT MSYS)
# permissions and symbolic links are broken on MSYS
unset(uid)
unset(status)
if(UNIX)
set(ID "id")
if (CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND EXISTS "/usr/xpg4/bin/id")
set (ID "/usr/xpg4/bin/id")
endif()
# if real user is root, tests are irrelevant
execute_process(COMMAND ${ID} -u $ENV{USER} OUTPUT_VARIABLE uid ERROR_QUIET
RESULT_VARIABLE status OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
if(NOT status AND NOT uid STREQUAL "0")
run_cmake(FilePermissions)
endif()
endif()
run_cmake(IsDirectory)
run_cmake(IsDirectoryLong)
run_cmake(duplicate-deep-else)