summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Help/command/if.rst41
-rw-r--r--Help/release/dev/if-check-file-permissions.rst5
-rw-r--r--Source/cmConditionEvaluator.cxx21
-rw-r--r--Tests/RunCMake/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/if/FilePermissions.cmake153
-rw-r--r--Tests/RunCMake/if/RunCMakeTest.cmake17
6 files changed, 238 insertions, 1 deletions
diff --git a/Help/command/if.rst b/Help/command/if.rst
index 5d85a1f..1afbe04 100644
--- a/Help/command/if.rst
+++ b/Help/command/if.rst
@@ -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
diff --git a/Help/release/dev/if-check-file-permissions.rst b/Help/release/dev/if-check-file-permissions.rst
new file mode 100644
index 0000000..ec69b00
--- /dev/null
+++ b/Help/release/dev/if-check-file-permissions.rst
@@ -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.
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx
index 6f9f541..eba4c57 100644
--- a/Source/cmConditionEvaluator.cxx
+++ b/Source/cmConditionEvaluator.cxx
@@ -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(
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index ae878c9..1b1ecb5 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -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)
diff --git a/Tests/RunCMake/if/FilePermissions.cmake b/Tests/RunCMake/if/FilePermissions.cmake
new file mode 100644
index 0000000..7881b5f
--- /dev/null
+++ b/Tests/RunCMake/if/FilePermissions.cmake
@@ -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()
diff --git a/Tests/RunCMake/if/RunCMakeTest.cmake b/Tests/RunCMake/if/RunCMakeTest.cmake
index efee116..0bfff90 100644
--- a/Tests/RunCMake/if/RunCMakeTest.cmake
+++ b/Tests/RunCMake/if/RunCMakeTest.cmake
@@ -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)