find_program: Find programs that are executable but not readable

This fix was first made by commit 86e6349ef7 (find_program: Find
programs that are executable but not readable, 2020-04-04,
v3.18.0-rc1~372^2) but was reverted for compatibility.  Re-introduce it
with a policy for compatibility.

Fixes: #10468
This commit is contained in:
Brad King
2020-06-10 10:23:00 -04:00
parent 43b10e2411
commit 9d45a8be08
15 changed files with 132 additions and 4 deletions

View File

@@ -54,6 +54,14 @@ functions.
Policies Introduced by CMake 3.18
=================================
.. toctree::
:maxdepth: 1
CMP0109: find_program() requires permission to execute but not to read. </policy/CMP0109>
Policies Introduced by CMake 3.18
=================================
.. toctree::
:maxdepth: 1

22
Help/policy/CMP0109.rst Normal file
View File

@@ -0,0 +1,22 @@
CMP0109
-------
:command:`find_program` requires permission to execute but not to read.
In CMake 3.18 and below, the :command:`find_program` command on UNIX
would find files that are readable without requiring execute permission,
and would not find files that are executable without read permission.
In CMake 3.19 and above, ``find_program`` now prefers to require execute
permission but not read permission. This policy provides compatibility
with projects that have not been updated to expect the new behavior.
The ``OLD`` behavior for this policy is for ``find_program`` to require
read permission but not execute permission.
The ``NEW`` behavior for this policy is for ``find_program`` to require
execute permission but not read permission.
This policy was introduced in CMake version 3.19. CMake version |release|
warns when the policy is not set and uses ``OLD`` behavior. Use the
:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
.. include:: DEPRECATED.txt

View File

@@ -0,0 +1,5 @@
find_program-exe-no-read
------------------------
* The :command:`find_program` command now requires permission to execute
but not to read the file found. See policy :policy:`CMP0109`.

View File

@@ -4,6 +4,7 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@@ -19,6 +20,7 @@ struct cmFindProgramHelper
cmFindProgramHelper(cmMakefile* makefile, cmFindBase const* base)
: DebugSearches("find_program", base)
, Makefile(makefile)
, PolicyCMP0109(makefile->GetPolicyStatus(cmPolicies::CMP0109))
{
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
// Consider platform-specific extensions.
@@ -48,6 +50,8 @@ struct cmFindProgramHelper
cmFindBaseDebugState DebugSearches;
cmMakefile* Makefile;
cmPolicies::PolicyStatus PolicyCMP0109;
void AddName(std::string const& name) { this->Names.push_back(name); }
void SetName(std::string const& name)
{
@@ -85,7 +89,7 @@ struct cmFindProgramHelper
this->TestNameExt = cmStrCat(name, ext);
this->TestPath =
cmSystemTools::CollapseFullPath(this->TestNameExt, path);
bool exists = cmSystemTools::FileExists(this->TestPath, true);
bool exists = this->FileIsExecutable(this->TestPath);
exists ? this->DebugSearches.FoundAt(this->TestPath)
: this->DebugSearches.FailedAt(this->TestPath);
if (exists) {
@@ -95,6 +99,48 @@ struct cmFindProgramHelper
}
return false;
}
bool FileIsExecutable(std::string const& file) const
{
switch (this->PolicyCMP0109) {
case cmPolicies::OLD:
return cmSystemTools::FileExists(file, true);
case cmPolicies::NEW:
case cmPolicies::REQUIRED_ALWAYS:
case cmPolicies::REQUIRED_IF_USED:
return cmSystemTools::FileIsExecutable(file);
default:
break;
}
bool const isExeOld = cmSystemTools::FileExists(file, true);
bool const isExeNew = cmSystemTools::FileIsExecutable(file);
if (isExeNew == isExeOld) {
return isExeNew;
}
if (isExeNew) {
this->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0109),
"\n"
"The file\n"
" ",
file,
"\n"
"is executable but not readable. "
"CMake is ignoring it for compatibility."));
} else {
this->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0109),
"\n"
"The file\n"
" ",
file,
"\n"
"is readable but not executable. "
"CMake is using it for compatibility."));
}
return isExeOld;
}
};
cmFindProgramCommand::cmFindProgramCommand(cmExecutionStatus& status)

View File

@@ -320,7 +320,10 @@ class cmMakefile;
SELECT(POLICY, CMP0107, "An ALIAS target cannot overwrite another target.", \
3, 18, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0108, "A target cannot link to itself through an alias.", \
3, 18, 0, cmPolicies::WARN)
3, 18, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0109, \
"find_program() requires permission to execute but not to read.", 3, \
19, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \

View File

@@ -1,4 +1,7 @@
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ExeNoRead" "#!/bin/sh\n")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ReadNoExe" "#ReadNoExe")
execute_process(COMMAND chmod -r+x "${CMAKE_CURRENT_BINARY_DIR}/ExeNoRead")
find_program(ExeNoRead_EXECUTABLE NAMES ExeNoRead NO_DEFAULT_PATH PATHS "${CMAKE_CURRENT_BINARY_DIR}")
message(STATUS "ExeNoRead_EXECUTABLE='${ExeNoRead_EXECUTABLE}'")
find_program(ReadNoExe_EXECUTABLE NAMES ReadNoExe NO_DEFAULT_PATH PATHS "${CMAKE_CURRENT_BINARY_DIR}")
message(STATUS "ReadNoExe_EXECUTABLE='${ReadNoExe_EXECUTABLE}'")

View File

@@ -0,0 +1,2 @@
-- ExeNoRead_EXECUTABLE='.*/Tests/RunCMake/find_program/CMP0109-NEW-build/ExeNoRead'
-- ReadNoExe_EXECUTABLE='ReadNoExe_EXECUTABLE-NOTFOUND'

View File

@@ -0,0 +1,2 @@
cmake_policy(SET CMP0109 NEW)
include(CMP0109-Common.cmake)

View File

@@ -0,0 +1,2 @@
-- ExeNoRead_EXECUTABLE='ExeNoRead_EXECUTABLE-NOTFOUND'
-- ReadNoExe_EXECUTABLE='.*/Tests/RunCMake/find_program/CMP0109-OLD-build/ReadNoExe'

View File

@@ -0,0 +1,2 @@
cmake_policy(SET CMP0109 OLD)
include(CMP0109-Common.cmake)

View File

@@ -0,0 +1,29 @@
^CMake Warning \(dev\) at CMP0109-Common.cmake:4 \(find_program\):
Policy CMP0109 is not set: find_program\(\) requires permission to execute
but not to read. Run "cmake --help-policy CMP0109" for policy details.
Use the cmake_policy command to set the policy and suppress this warning.
The file
.*/Tests/RunCMake/find_program/CMP0109-WARN-build/ExeNoRead
is executable but not readable. CMake is ignoring it for compatibility.
Call Stack \(most recent call first\):
CMP0109-WARN.cmake:1 \(include\)
CMakeLists.txt:3 \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.
+
CMake Warning \(dev\) at CMP0109-Common.cmake:6 \(find_program\):
Policy CMP0109 is not set: find_program\(\) requires permission to execute
but not to read. Run "cmake --help-policy CMP0109" for policy details.
Use the cmake_policy command to set the policy and suppress this warning.
The file
.*/Tests/RunCMake/find_program/CMP0109-WARN-build/ReadNoExe
is readable but not executable. CMake is using it for compatibility.
Call Stack \(most recent call first\):
CMP0109-WARN.cmake:1 \(include\)
CMakeLists.txt:3 \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.$

View File

@@ -0,0 +1,2 @@
-- ExeNoRead_EXECUTABLE='ExeNoRead_EXECUTABLE-NOTFOUND'
-- ReadNoExe_EXECUTABLE='.*/Tests/RunCMake/find_program/CMP0109-WARN-build/ReadNoExe'

View File

@@ -0,0 +1 @@
include(CMP0109-Common.cmake)

View File

@@ -1 +0,0 @@
-- ExeNoRead_EXECUTABLE='ExeNoRead_EXECUTABLE-NOTFOUND'

View File

@@ -17,7 +17,9 @@ else()
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT "${uid}" STREQUAL "0")
run_cmake(ExeNoRead)
run_cmake(CMP0109-WARN)
run_cmake(CMP0109-OLD)
run_cmake(CMP0109-NEW)
endif()
endif()