file: Add CHMOD and CHMOD_RECURSE subcommands
Fixes: #21057 Signed-off-by: Sibi Siddharthan <sibisiddharthan.github@gmail.com>
This commit is contained in:
parent
675be013e9
commit
7de60beddf
@ -30,6 +30,8 @@ Synopsis
|
||||
file(`SIZE`_ <filename> <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>... [...])
|
||||
|
||||
`Path Conversion`_
|
||||
file(`RELATIVE_PATH`_ <out-var> <directory> <file>)
|
||||
@ -741,6 +743,51 @@ creating the link fails. It can be useful for handling situations such as
|
||||
``<original>`` and ``<linkname>`` being on different drives or mount points,
|
||||
which would make them unable to support a hard link.
|
||||
|
||||
.. _CHMOD:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
file(CHMOD <files>... <directories>... [PERMISSIONS <permissions>...]
|
||||
[FILE_PERMISSIONS <permissions>...]
|
||||
[DIRECTORY_PERMISSIONS <permissions>...])
|
||||
|
||||
Set the permissions for the ``<files>...`` and ``<directories>...`` specified.
|
||||
Valid permissions are ``OWNER_READ``, ``OWNER_WRITE``, ``OWNER_EXECUTE``,
|
||||
``GROUP_READ``, ``GROUP_WRITE``, ``GROUP_EXECUTE``, ``WORLD_READ``,
|
||||
``WORLD_WRITE``, ``WORLD_EXECUTE``.
|
||||
|
||||
Valid combination of keywords are:
|
||||
|
||||
``PERMISSIONS``
|
||||
all items are changed
|
||||
|
||||
``FILE_PERMISSIONS``
|
||||
only files are changed
|
||||
|
||||
``DIRECTORY_PERMISSIONS``
|
||||
only directories are changed
|
||||
|
||||
``PERMISSIONS`` and ``FILE_PERMISSIONS``
|
||||
``FILE_PERMISSIONS`` overrides ``PERMISSIONS`` for files
|
||||
|
||||
``PERMISSIONS`` and ``DIRECTORY_PERMISSIONS``
|
||||
``DIRECTORY_PERMISSIONS`` overrides ``PERMISSIONS`` for directories
|
||||
|
||||
``FILE_PERMISSIONS`` and ``DIRECTORY_PERMISSIONS``
|
||||
use ``FILE_PERMISSIONS`` for files and ``DIRECTORY_PERMISSIONS`` for
|
||||
directories
|
||||
|
||||
|
||||
.. _CHMOD_RECURSE:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
file(CHMOD_RECURSE <files>... <directories>... PERMISSIONS <permissions>...
|
||||
FILE_PERMISSIONS <permissions>... DIRECTORY_PERMISSIONS <permissions>...)
|
||||
|
||||
Same as `CHMOD`_, but change the permissions of files and directories present in
|
||||
the ``<directories>..`` recursively.
|
||||
|
||||
Path Conversion
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
|
5
Help/release/dev/file-CHMOD.rst
Normal file
5
Help/release/dev/file-CHMOD.rst
Normal file
@ -0,0 +1,5 @@
|
||||
file-CHMOD
|
||||
----------
|
||||
|
||||
* Add :command:`file(CHMOD)` and :command:`file(CHMOD_RECURSE)` to
|
||||
set permissions of files and directories.
|
@ -30,6 +30,7 @@
|
||||
#include "cmArgumentParser.h"
|
||||
#include "cmCryptoHash.h"
|
||||
#include "cmExecutionStatus.h"
|
||||
#include "cmFSPermissions.h"
|
||||
#include "cmFileCopier.h"
|
||||
#include "cmFileInstaller.h"
|
||||
#include "cmFileLockPool.h"
|
||||
@ -3160,6 +3161,163 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateAndConvertPermissions(const std::vector<std::string>& permissions,
|
||||
mode_t& perms, cmExecutionStatus& status)
|
||||
{
|
||||
for (const auto& i : permissions) {
|
||||
if (!cmFSPermissions::stringToModeT(i, perms)) {
|
||||
status.SetError(i + " is an invalid permission specifier");
|
||||
cmSystemTools::SetFatalErrorOccured();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetPermissions(const std::string& filename, const mode_t& perms,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
if (!cmSystemTools::SetPermissions(filename, perms)) {
|
||||
status.SetError("Failed to set permissions for " + filename);
|
||||
cmSystemTools::SetFatalErrorOccured();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
mode_t perms = 0;
|
||||
mode_t fperms = 0;
|
||||
mode_t dperms = 0;
|
||||
cmsys::Glob globber;
|
||||
|
||||
globber.SetRecurse(recurse);
|
||||
globber.SetRecurseListDirs(recurse);
|
||||
|
||||
struct Arguments
|
||||
{
|
||||
std::vector<std::string> Permissions;
|
||||
std::vector<std::string> FilePermissions;
|
||||
std::vector<std::string> DirectoryPermissions;
|
||||
};
|
||||
|
||||
static auto const parser =
|
||||
cmArgumentParser<Arguments>{}
|
||||
.Bind("PERMISSIONS"_s, &Arguments::Permissions)
|
||||
.Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions)
|
||||
.Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions);
|
||||
|
||||
std::vector<std::string> pathEntries;
|
||||
std::vector<std::string> keywordsMissingValues;
|
||||
Arguments parsedArgs = parser.Parse(cmMakeRange(args).advance(1),
|
||||
&pathEntries, &keywordsMissingValues);
|
||||
|
||||
// check validity of arguments
|
||||
if (parsedArgs.Permissions.empty() && parsedArgs.FilePermissions.empty() &&
|
||||
parsedArgs.DirectoryPermissions.empty()) // no permissions given
|
||||
{
|
||||
status.SetError("No permissions given");
|
||||
cmSystemTools::SetFatalErrorOccured();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parsedArgs.Permissions.empty() && !parsedArgs.FilePermissions.empty() &&
|
||||
!parsedArgs.DirectoryPermissions.empty()) // all keywords are used
|
||||
{
|
||||
status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or "
|
||||
"DIRECTORY_PERMISSIONS from the invocation");
|
||||
cmSystemTools::SetFatalErrorOccured();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!keywordsMissingValues.empty()) {
|
||||
for (const auto& i : keywordsMissingValues) {
|
||||
status.SetError(i + " is not given any arguments");
|
||||
cmSystemTools::SetFatalErrorOccured();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate permissions
|
||||
bool validatePermissions =
|
||||
ValidateAndConvertPermissions(parsedArgs.Permissions, perms, status) &&
|
||||
ValidateAndConvertPermissions(parsedArgs.FilePermissions, fperms,
|
||||
status) &&
|
||||
ValidateAndConvertPermissions(parsedArgs.DirectoryPermissions, dperms,
|
||||
status);
|
||||
if (!validatePermissions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> allPathEntries;
|
||||
|
||||
if (recurse) {
|
||||
std::vector<std::string> tempPathEntries;
|
||||
for (const auto& i : pathEntries) {
|
||||
if (cmSystemTools::FileIsDirectory(i)) {
|
||||
globber.FindFiles(i + "/*");
|
||||
tempPathEntries = globber.GetFiles();
|
||||
allPathEntries.insert(allPathEntries.end(), tempPathEntries.begin(),
|
||||
tempPathEntries.end());
|
||||
allPathEntries.emplace_back(i);
|
||||
} else {
|
||||
allPathEntries.emplace_back(i); // We validate path entries below
|
||||
}
|
||||
}
|
||||
} else {
|
||||
allPathEntries = std::move(pathEntries);
|
||||
}
|
||||
|
||||
// chmod
|
||||
for (const auto& i : allPathEntries) {
|
||||
if (!(cmSystemTools::FileExists(i) || cmSystemTools::FileIsDirectory(i))) {
|
||||
status.SetError(cmStrCat("does not exist:\n ", i));
|
||||
cmSystemTools::SetFatalErrorOccured();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cmSystemTools::FileExists(i, true)) {
|
||||
bool success = true;
|
||||
const mode_t& filePermissions =
|
||||
parsedArgs.FilePermissions.empty() ? perms : fperms;
|
||||
if (filePermissions) {
|
||||
success = SetPermissions(i, filePermissions, status);
|
||||
}
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (cmSystemTools::FileIsDirectory(i)) {
|
||||
bool success = true;
|
||||
const mode_t& directoryPermissions =
|
||||
parsedArgs.DirectoryPermissions.empty() ? perms : dperms;
|
||||
if (directoryPermissions) {
|
||||
success = SetPermissions(i, directoryPermissions, status);
|
||||
}
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleChmodCommand(std::vector<std::string> const& args,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
return HandleChmodCommandImpl(args, false, status);
|
||||
}
|
||||
|
||||
bool HandleChmodRecurseCommand(std::vector<std::string> const& args,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
return HandleChmodCommandImpl(args, true, status);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool cmFileCommand(std::vector<std::string> const& args,
|
||||
@ -3216,6 +3374,8 @@ bool cmFileCommand(std::vector<std::string> const& args,
|
||||
{ "CONFIGURE"_s, HandleConfigureCommand },
|
||||
{ "ARCHIVE_CREATE"_s, HandleArchiveCreateCommand },
|
||||
{ "ARCHIVE_EXTRACT"_s, HandleArchiveExtractCommand },
|
||||
{ "CHMOD"_s, HandleChmodCommand },
|
||||
{ "CHMOD_RECURSE"_s, HandleChmodRecurseCommand },
|
||||
};
|
||||
|
||||
return subcommand(args[0], args, status);
|
||||
|
@ -322,6 +322,7 @@ add_RunCMake_test(ctest_update)
|
||||
add_RunCMake_test(ctest_upload)
|
||||
add_RunCMake_test(ctest_fixtures)
|
||||
add_RunCMake_test(file)
|
||||
add_RunCMake_test(file-CHMOD)
|
||||
add_RunCMake_test(find_file)
|
||||
add_RunCMake_test(find_library -DCYGWIN=${CYGWIN})
|
||||
add_RunCMake_test(find_package)
|
||||
|
1
Tests/RunCMake/file-CHMOD/CHMOD-all-perms-result.txt
Normal file
1
Tests/RunCMake/file-CHMOD/CHMOD-all-perms-result.txt
Normal file
@ -0,0 +1 @@
|
||||
1
|
5
Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt
Normal file
5
Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt
Normal file
@ -0,0 +1,5 @@
|
||||
CMake Error at CHMOD-all-perms\.cmake:[0-9]+ \(file\):
|
||||
file Remove either PERMISSIONS or FILE_PERMISSIONS or DIRECTORY_PERMISSIONS
|
||||
from the invocation
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
6
Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake
Normal file
6
Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake
Normal file
@ -0,0 +1,6 @@
|
||||
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
|
||||
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
|
||||
file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ
|
||||
FILE_PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS OWNER_READ)
|
1
Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-result.txt
Normal file
1
Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-result.txt
Normal file
@ -0,0 +1 @@
|
||||
1
|
6
Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt
Normal file
6
Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt
Normal file
@ -0,0 +1,6 @@
|
||||
CMake Error at CHMOD-invalid-path\.cmake:[0-9]+ \(file\):
|
||||
file does not exist:
|
||||
|
||||
.*/chmod-tests/I_dont_exist
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
4
Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake
Normal file
4
Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake
Normal file
@ -0,0 +1,4 @@
|
||||
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
|
||||
file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/I_dont_exist PERMISSIONS OWNER_READ)
|
1
Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-result.txt
Normal file
1
Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-result.txt
Normal file
@ -0,0 +1 @@
|
||||
1
|
4
Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt
Normal file
4
Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt
Normal file
@ -0,0 +1,4 @@
|
||||
CMake Error at CHMOD-invalid-perms\.cmake:[0-9]+ \(file\):
|
||||
file INVALID_PERMISSION is an invalid permission specifier
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
5
Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake
Normal file
5
Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake
Normal file
@ -0,0 +1,5 @@
|
||||
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
|
||||
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
|
||||
file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS INVALID_PERMISSION)
|
1
Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-result.txt
Normal file
1
Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-result.txt
Normal file
@ -0,0 +1 @@
|
||||
1
|
4
Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt
Normal file
4
Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt
Normal file
@ -0,0 +1,4 @@
|
||||
CMake Error at CHMOD-no-keyword\.cmake:[0-9]+ \(file\):
|
||||
file No permissions given
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
5
Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake
Normal file
5
Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake
Normal file
@ -0,0 +1,5 @@
|
||||
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
|
||||
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
|
||||
file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
|
1
Tests/RunCMake/file-CHMOD/CHMOD-no-perms-result.txt
Normal file
1
Tests/RunCMake/file-CHMOD/CHMOD-no-perms-result.txt
Normal file
@ -0,0 +1 @@
|
||||
1
|
4
Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt
Normal file
4
Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt
Normal file
@ -0,0 +1,4 @@
|
||||
CMake Error at CHMOD-no-perms\.cmake:[0-9]+ \(file\):
|
||||
file No permissions given
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
5
Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake
Normal file
5
Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake
Normal file
@ -0,0 +1,5 @@
|
||||
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
|
||||
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
|
||||
file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS)
|
5
Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake
Normal file
5
Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake
Normal file
@ -0,0 +1,5 @@
|
||||
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
|
||||
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
|
||||
file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ)
|
6
Tests/RunCMake/file-CHMOD/CHMOD-override.cmake
Normal file
6
Tests/RunCMake/file-CHMOD/CHMOD-override.cmake
Normal file
@ -0,0 +1,6 @@
|
||||
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
|
||||
file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
|
||||
file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ
|
||||
FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
|
1
Tests/RunCMake/file-CHMOD/CHMOD-write-only-result.txt
Normal file
1
Tests/RunCMake/file-CHMOD/CHMOD-write-only-result.txt
Normal file
@ -0,0 +1 @@
|
||||
1
|
6
Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt
Normal file
6
Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt
Normal file
@ -0,0 +1,6 @@
|
||||
CMake Error at CHMOD-write-only\.cmake:[0-9]+ \(file\):
|
||||
file failed to open for reading \(Permission denied\):
|
||||
|
||||
.*/chmod-tests/a
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:[0-9]+ \(include\)
|
6
Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake
Normal file
6
Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake
Normal file
@ -0,0 +1,6 @@
|
||||
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
|
||||
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a "CONTENT")
|
||||
file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_WRITE)
|
||||
file(READ ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a content)
|
3
Tests/RunCMake/file-CHMOD/CMakeLists.txt
Normal file
3
Tests/RunCMake/file-CHMOD/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
19
Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake
Normal file
19
Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake
Normal file
@ -0,0 +1,19 @@
|
||||
include(RunCMake)
|
||||
|
||||
run_cmake(CHMOD-no-perms)
|
||||
run_cmake(CHMOD-no-keyword)
|
||||
run_cmake(CHMOD-all-perms)
|
||||
run_cmake(CHMOD-invalid-perms)
|
||||
run_cmake(CHMOD-invalid-path)
|
||||
run_cmake(CHMOD-ok)
|
||||
run_cmake(CHMOD-override)
|
||||
|
||||
if(UNIX)
|
||||
execute_process(COMMAND id -u $ENV{USER}
|
||||
OUTPUT_VARIABLE uid
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT "${uid}" STREQUAL "0")
|
||||
run_cmake(CHMOD-write-only)
|
||||
endif()
|
Loading…
Reference in New Issue
Block a user