Browse Source

Merge topic 'find_program-NAMES_PER_DIR'

8ea7611b find_program: Optionally consider all names in each directory
fc1990c9 cmFindProgramCommand: Re-implement search using more flexible approach
fdbfc9f6 Tests: Add explicit testing for find_program
907a919b cmSystemTools: Drop unused StringEndsWith method
ed4de3c9 cmFindProgramCommand: Use Names member instead of passing it
bf32b95e cmFindLibraryCommand: Avoid repeating search for the same name
Brad King 10 years ago
parent
commit
4da3315db3

+ 6 - 1
Help/command/find_program.rst

@@ -2,7 +2,7 @@ find_program
 ------------
 
 .. |FIND_XXX| replace:: find_program
-.. |NAMES| replace:: NAMES name1 [name2 ...]
+.. |NAMES| replace:: NAMES name1 [name2 ...] [NAMES_PER_DIR]
 .. |SEARCH_XXX| replace:: program
 .. |SEARCH_XXX_DESC| replace:: program
 .. |prefix_XXX_SUBDIR| replace:: ``<prefix>/[s]bin``
@@ -26,3 +26,8 @@ find_program
    :variable:`CMAKE_FIND_ROOT_PATH_MODE_PROGRAM`
 
 .. include:: FIND_XXX.txt
+
+When more than one value is given to the ``NAMES`` option this command by
+default will consider one name at a time and search every directory
+for it.  The ``NAMES_PER_DIR`` option tells this command to consider one
+directory at a time and search for all names in it.

+ 6 - 0
Help/release/dev/find_program-NAMES_PER_DIR.rst

@@ -0,0 +1,6 @@
+find_program-NAMES_PER_DIR
+--------------------------
+
+* The :command:`find_program` command learned a ``NAMES_PER_DIR``
+  option to consdier all given ``NAMES`` in each directory before
+  moving on to the next directory.

+ 9 - 2
Source/cmFindLibraryCommand.cxx

@@ -203,6 +203,7 @@ struct cmFindLibraryHelper
     }
   bool HasValidSuffix(std::string const& name);
   void AddName(std::string const& name);
+  void SetName(std::string const& name);
   bool CheckDirectory(std::string const& path);
   bool CheckDirectoryForName(std::string const& path, Name& name);
 };
@@ -321,6 +322,13 @@ void cmFindLibraryHelper::AddName(std::string const& name)
   this->Names.push_back(entry);
 }
 
+//----------------------------------------------------------------------------
+void cmFindLibraryHelper::SetName(std::string const& name)
+{
+  this->Names.clear();
+  this->AddName(name);
+}
+
 //----------------------------------------------------------------------------
 bool cmFindLibraryHelper::CheckDirectory(std::string const& path)
 {
@@ -459,8 +467,7 @@ std::string cmFindLibraryCommand::FindNormalLibraryDirsPerName()
       ni != this->Names.end() ; ++ni)
     {
     // Switch to searching for this name.
-    std::string const& name = *ni;
-    helper.AddName(name);
+    helper.SetName(*ni);
 
     // Search every directory.
     for(std::vector<std::string>::const_iterator

+ 144 - 9
Source/cmFindProgramCommand.cxx

@@ -16,6 +16,80 @@
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 
+//----------------------------------------------------------------------------
+struct cmFindProgramHelper
+{
+  cmFindProgramHelper()
+    {
+#if defined (_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
+    // Consider platform-specific extensions.
+    this->Extensions.push_back(".com");
+    this->Extensions.push_back(".exe");
+#endif
+    // Consider original name with no extensions.
+    this->Extensions.push_back("");
+    }
+
+  // List of valid extensions.
+  std::vector<std::string> Extensions;
+
+  // Keep track of the best program file found so far.
+  std::string BestPath;
+
+  // Current names under consideration.
+  std::vector<std::string> Names;
+
+  // Current full path under consideration.
+  std::string TestPath;
+
+  void AddName(std::string const& name)
+    {
+    this->Names.push_back(name);
+    }
+  void SetName(std::string const& name)
+    {
+    this->Names.clear();
+    this->AddName(name);
+    }
+  bool CheckDirectory(std::string const& path)
+    {
+    for (std::vector<std::string>::iterator i = this->Names.begin();
+         i != this->Names.end(); ++i)
+      {
+      if (this->CheckDirectoryForName(path, *i))
+        {
+        return true;
+        }
+      }
+    return false;
+    }
+  bool CheckDirectoryForName(std::string const& path, std::string const& name)
+    {
+    for (std::vector<std::string>::iterator ext = this->Extensions.begin();
+         ext != this->Extensions.end(); ++ext)
+      {
+      this->TestPath = path;
+      this->TestPath += name;
+      if (!ext->empty() && cmSystemTools::StringEndsWith(name, ext->c_str()))
+        {
+        continue;
+        }
+      this->TestPath += *ext;
+      if (cmSystemTools::FileExists(this->TestPath, true))
+        {
+        this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath);
+        return true;
+        }
+      }
+    return false;
+    }
+};
+
+cmFindProgramCommand::cmFindProgramCommand()
+{
+  this->NamesPerDirAllowed = true;
+}
+
 // cmFindProgramCommand
 bool cmFindProgramCommand
 ::InitialPass(std::vector<std::string> const& argsIn, cmExecutionStatus &)
@@ -41,7 +115,7 @@ bool cmFindProgramCommand
     return true;
     }
 
-  std::string result = FindProgram(this->Names);
+  std::string result = FindProgram();
   if(result != "")
     {
     // Save the value in the cache
@@ -59,31 +133,92 @@ bool cmFindProgramCommand
   return true;
 }
 
-std::string cmFindProgramCommand::FindProgram(std::vector<std::string> names)
+std::string cmFindProgramCommand::FindProgram()
 {
   std::string program = "";
 
   if(this->SearchAppBundleFirst || this->SearchAppBundleOnly)
     {
-    program = FindAppBundle(names);
+    program = FindAppBundle();
     }
   if(program.empty() && !this->SearchAppBundleOnly)
     {
-    program = cmSystemTools::FindProgram(names, this->SearchPaths, true);
+    program = this->FindNormalProgram();
     }
 
   if(program.empty() && this->SearchAppBundleLast)
     {
-    program = this->FindAppBundle(names);
+    program = this->FindAppBundle();
     }
   return program;
 }
 
-std::string cmFindProgramCommand
-::FindAppBundle(std::vector<std::string> names)
+//----------------------------------------------------------------------------
+std::string cmFindProgramCommand::FindNormalProgram()
+{
+  if(this->NamesPerDir)
+    {
+    return this->FindNormalProgramNamesPerDir();
+    }
+  else
+    {
+    return this->FindNormalProgramDirsPerName();
+    }
+}
+
+//----------------------------------------------------------------------------
+std::string cmFindProgramCommand::FindNormalProgramNamesPerDir()
+{
+  // Search for all names in each directory.
+  cmFindProgramHelper helper;
+  for (std::vector<std::string>::const_iterator ni = this->Names.begin();
+       ni != this->Names.end() ; ++ni)
+    {
+    helper.AddName(*ni);
+    }
+  // Search every directory.
+  for (std::vector<std::string>::const_iterator
+         p = this->SearchPaths.begin(); p != this->SearchPaths.end(); ++p)
+    {
+    if(helper.CheckDirectory(*p))
+      {
+      return helper.BestPath;
+      }
+    }
+  // Couldn't find the program.
+  return "";
+}
+
+//----------------------------------------------------------------------------
+std::string cmFindProgramCommand::FindNormalProgramDirsPerName()
+{
+  // Search the entire path for each name.
+  cmFindProgramHelper helper;
+  for (std::vector<std::string>::const_iterator ni = this->Names.begin();
+       ni != this->Names.end() ; ++ni)
+    {
+    // Switch to searching for this name.
+    helper.SetName(*ni);
+
+    // Search every directory.
+    for (std::vector<std::string>::const_iterator
+           p = this->SearchPaths.begin();
+         p != this->SearchPaths.end(); ++p)
+      {
+      if (helper.CheckDirectory(*p))
+        {
+        return helper.BestPath;
+        }
+      }
+    }
+  // Couldn't find the program.
+  return "";
+}
+
+std::string cmFindProgramCommand::FindAppBundle()
 {
-  for(std::vector<std::string>::const_iterator name = names.begin();
-      name != names.end() ; ++name)
+  for(std::vector<std::string>::const_iterator name = this->Names.begin();
+      name != this->Names.end() ; ++name)
     {
 
     std::string appName = *name + std::string(".app");

+ 6 - 4
Source/cmFindProgramCommand.h

@@ -25,6 +25,7 @@
 class cmFindProgramCommand : public cmFindBase
 {
 public:
+  cmFindProgramCommand();
   /**
    * This is a virtual constructor for the command.
    */
@@ -52,11 +53,12 @@ public:
 
   cmTypeMacro(cmFindProgramCommand, cmFindBase);
 
-protected:
-  std::string FindProgram(std::vector<std::string> names);
-
 private:
-  std::string FindAppBundle(std::vector<std::string> names);
+  std::string FindProgram();
+  std::string FindNormalProgram();
+  std::string FindNormalProgramDirsPerName();
+  std::string FindNormalProgramNamesPerDir();
+  std::string FindAppBundle();
   std::string GetBundleExecutable(std::string bundlePath);
 
 };

+ 0 - 9
Source/cmSystemTools.cxx

@@ -1409,15 +1409,6 @@ std::string cmSystemTools::ConvertToRunCommandPath(const char* path)
 #endif
 }
 
-bool cmSystemTools::StringEndsWith(const char* str1, const char* str2)
-{
-  if ( !str1 || !str2 || strlen(str1) < strlen(str2) )
-    {
-    return 0;
-    }
-  return !strncmp(str1 + (strlen(str1)-strlen(str2)), str2, strlen(str2));
-}
-
 // compute the relative path from here to there
 std::string cmSystemTools::RelativePath(const char* local, const char* remote)
 {

+ 0 - 2
Source/cmSystemTools.h

@@ -336,8 +336,6 @@ public:
   // be used when RunCommand is called from cmake, because the
   // running cmake needs paths to be in its format
   static std::string ConvertToRunCommandPath(const char* path);
-  //! Check if the first string ends with the second one.
-  static bool StringEndsWith(const char* str1, const char* str2);
 
   /** compute the relative path from local to remote.  local must
       be a directory.  remote can be a file or a directory.

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -184,6 +184,7 @@ add_RunCMake_test(find_file)
 add_RunCMake_test(find_library)
 add_RunCMake_test(find_package)
 add_RunCMake_test(find_path)
+add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 add_RunCMake_test(get_filename_component)
 add_RunCMake_test(get_property)
 add_RunCMake_test(if)

+ 1 - 0
Tests/RunCMake/find_program/A/testA

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

+ 1 - 0
Tests/RunCMake/find_program/B/testB

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

+ 3 - 0
Tests/RunCMake/find_program/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.3)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 1 - 0
Tests/RunCMake/find_program/DirsPerName-stdout.txt

@@ -0,0 +1 @@
+-- PROG='[^']*/Tests/RunCMake/find_program/B/testB'

+ 6 - 0
Tests/RunCMake/find_program/DirsPerName.cmake

@@ -0,0 +1,6 @@
+find_program(PROG
+  NAMES testB testA
+  PATHS ${CMAKE_CURRENT_SOURCE_DIR}/A ${CMAKE_CURRENT_SOURCE_DIR}/B
+  NO_DEFAULT_PATH
+  )
+message(STATUS "PROG='${PROG}'")

+ 1 - 0
Tests/RunCMake/find_program/NamesPerDir-stdout.txt

@@ -0,0 +1 @@
+-- PROG='[^']*/Tests/RunCMake/find_program/A/testA'

+ 6 - 0
Tests/RunCMake/find_program/NamesPerDir.cmake

@@ -0,0 +1,6 @@
+find_program(PROG
+  NAMES testB testA NAMES_PER_DIR
+  PATHS ${CMAKE_CURRENT_SOURCE_DIR}/A ${CMAKE_CURRENT_SOURCE_DIR}/B
+  NO_DEFAULT_PATH
+  )
+message(STATUS "PROG='${PROG}'")

+ 9 - 0
Tests/RunCMake/find_program/RunCMakeTest.cmake

@@ -0,0 +1,9 @@
+include(RunCMake)
+
+run_cmake(DirsPerName)
+run_cmake(NamesPerDir)
+
+if(CMAKE_SYSTEM_NAME MATCHES "^(Windows|CYGWIN)$")
+  run_cmake(WindowsCom)
+  run_cmake(WindowsExe)
+endif()

+ 0 - 0
Tests/RunCMake/find_program/Win/testCom.com


+ 0 - 0
Tests/RunCMake/find_program/Win/testCom.exe


+ 0 - 0
Tests/RunCMake/find_program/Win/testExe.exe


+ 1 - 0
Tests/RunCMake/find_program/WindowsCom-stdout.txt

@@ -0,0 +1 @@
+-- PROG='[^']*/Tests/RunCMake/find_program/Win/testCom.com'

+ 6 - 0
Tests/RunCMake/find_program/WindowsCom.cmake

@@ -0,0 +1,6 @@
+find_program(PROG
+  NAMES testCom
+  PATHS ${CMAKE_CURRENT_SOURCE_DIR}/Win
+  NO_DEFAULT_PATH
+  )
+message(STATUS "PROG='${PROG}'")

+ 1 - 0
Tests/RunCMake/find_program/WindowsExe-stdout.txt

@@ -0,0 +1 @@
+-- PROG='[^']*/Tests/RunCMake/find_program/Win/testExe.exe'

+ 6 - 0
Tests/RunCMake/find_program/WindowsExe.cmake

@@ -0,0 +1,6 @@
+find_program(PROG
+  NAMES testExe
+  PATHS ${CMAKE_CURRENT_SOURCE_DIR}/Win
+  NO_DEFAULT_PATH
+  )
+message(STATUS "PROG='${PROG}'")