find_program: Consider CWD only for paths with separator

find_program() incorrectly prepended search path components
to absolute file paths, and incorrectly searched the current
working directory for files that contained no directory
separators.

* Replace calls cmFindProgramHelper::CheckDirectory(std::string())
  with call of new method cmFindProgramHelper::CheckCompoundNames()
  that checks for the presence of a directory separator in the
  file name.
* Use cmSystemTools::CollapseCombinedPath rather than string
  concatenation to properly combine absolute file names with
  search path components.
* Add unit tests to verify corrections.

Fixes: #18044
This commit is contained in:
Sam Yates 2018-05-31 19:00:45 +02:00 committed by Brad King
parent 575f97763f
commit c76c1ea208
5 changed files with 97 additions and 8 deletions

View File

@ -34,6 +34,9 @@ struct cmFindProgramHelper
// Current names under consideration.
std::vector<std::string> Names;
// Current name with extension under consideration.
std::string TestNameExt;
// Current full path under consideration.
std::string TestPath;
@ -43,6 +46,19 @@ struct cmFindProgramHelper
this->Names.clear();
this->AddName(name);
}
bool CheckCompoundNames()
{
for (std::string const& n : this->Names) {
// Only perform search relative to current directory if the file name
// contains a directory separator.
if (n.find('/') != std::string::npos) {
if (this->CheckDirectoryForName("", n)) {
return true;
}
}
}
return false;
}
bool CheckDirectory(std::string const& path)
{
for (std::string const& n : this->Names) {
@ -55,14 +71,16 @@ struct cmFindProgramHelper
bool CheckDirectoryForName(std::string const& path, std::string const& name)
{
for (std::string const& ext : this->Extensions) {
this->TestPath = path;
this->TestPath += name;
if (!ext.empty() && cmSystemTools::StringEndsWith(name, ext.c_str())) {
continue;
}
this->TestPath += ext;
this->TestNameExt = name;
this->TestNameExt += ext;
this->TestPath =
cmSystemTools::CollapseCombinedPath(path, this->TestNameExt);
if (cmSystemTools::FileExists(this->TestPath, true)) {
this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath);
this->BestPath = this->TestPath;
return true;
}
}
@ -145,8 +163,8 @@ std::string cmFindProgramCommand::FindNormalProgramNamesPerDir()
helper.AddName(n);
}
// Check for the names themselves (e.g. absolute paths).
if (helper.CheckDirectory(std::string())) {
// Check for the names themselves if they contain a directory separator.
if (helper.CheckCompoundNames()) {
return helper.BestPath;
}
@ -168,8 +186,8 @@ std::string cmFindProgramCommand::FindNormalProgramDirsPerName()
// Switch to searching for this name.
helper.SetName(n);
// Check for the name by itself (e.g. an absolute path).
if (helper.CheckDirectory(std::string())) {
// Check for the names themselves if they contain a directory separator.
if (helper.CheckCompoundNames()) {
return helper.BestPath;
}

View File

@ -0,0 +1,6 @@
-- PROG_ABS='PROG_ABS-NOTFOUND'
-- PROG_ABS_NPD='PROG_ABS_NPD-NOTFOUND'
-- PROG_CWD='PROG_CWD-NOTFOUND'
-- PROG_CWD_NPD='PROG_CWD_NPD-NOTFOUND'
-- PROG_CWD_DOT='[^']*/Tests/RunCMake/find_program/testCWD'
-- PROG_CWD_DOT_NPD='[^']*/Tests/RunCMake/find_program/testCWD'

View File

@ -0,0 +1,63 @@
# testNoSuchFile should only be found if the file absolute path is
# incorrectly prepended with the search path.
function(strip_windows_path_prefix p outvar)
if(CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
string(REGEX REPLACE "^.:" "" p "${p}")
endif()
set(${outvar} "${p}" PARENT_SCOPE)
endfunction()
strip_windows_path_prefix("${CMAKE_CURRENT_SOURCE_DIR}" srcdir)
file(MAKE_DIRECTORY "tmp${srcdir}")
configure_file(testCWD "tmp${srcdir}/testNoSuchFile" COPYONLY)
find_program(PROG_ABS
NAMES "${srcdir}/testNoSuchFile"
PATHS "${CMAKE_CURRENT_BINARY_DIR}/tmp"
NO_DEFAULT_PATH
)
message(STATUS "PROG_ABS='${PROG_ABS}'")
find_program(PROG_ABS_NPD
NAMES "${srcdir}/testNoSuchFile"
PATHS "${CMAKE_CURRENT_BINARY_DIR}/tmp"
NAMES_PER_DIR
NO_DEFAULT_PATH
)
message(STATUS "PROG_ABS_NPD='${PROG_ABS_NPD}'")
# ./testCWD should not be found without '.' being in the path list.
configure_file(testCWD testCWD COPYONLY)
find_program(PROG_CWD
NAMES testCWD
NO_DEFAULT_PATH
)
message(STATUS "PROG_CWD='${PROG_CWD}'")
find_program(PROG_CWD_NPD
NAMES testCWD
NAMES_PER_DIR
NO_DEFAULT_PATH
)
message(STATUS "PROG_CWD_NPD='${PROG_CWD_NPD}'")
# Confirm that adding '.' to path does locate ./testCWD.
find_program(PROG_CWD_DOT
NAMES testCWD
PATHS .
NO_DEFAULT_PATH
)
message(STATUS "PROG_CWD_DOT='${PROG_CWD_DOT}'")
find_program(PROG_CWD_DOT_NPD
NAMES testCWD
PATHS .
NAMES_PER_DIR
NO_DEFAULT_PATH
)
message(STATUS "PROG_CWD_DOT_NPD='${PROG_CWD_DOT_NPD}'")

View File

@ -3,6 +3,7 @@ include(RunCMake)
run_cmake(EnvAndHints)
run_cmake(DirsPerName)
run_cmake(NamesPerDir)
run_cmake(RelAndAbsPath)
if(CMAKE_SYSTEM_NAME MATCHES "^(Windows|CYGWIN)$")
run_cmake(WindowsCom)

View File

@ -0,0 +1 @@
#!/bin/sh