From b424df917dc14ea7376940c6c22a3a274aedcc2b Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 17 Jan 2008 09:02:31 -0500 Subject: [PATCH] ENH: Major improvements to the FIND_PACKAGE command. See bug #3659. - Use CMAKE_PREFIX_PATH and CMAKE_SYSTEM_PREFIX_PATH among other means to locate package configuration files. - Create cmFindCommon as base for cmFindBase and cmFindPackageCommand - Move common functionality up to cmFindCommon - Improve documentation of FIND_* commands. - Fix FIND_* commands to not add framework/app paths in wrong place. --- Source/cmBootstrapCommands.cxx | 1 + Source/cmFindBase.cxx | 386 +-------- Source/cmFindBase.h | 34 +- Source/cmFindCommon.cxx | 479 +++++++++++ Source/cmFindCommon.h | 95 +++ Source/cmFindLibraryCommand.cxx | 6 + Source/cmFindPackageCommand.cxx | 1372 ++++++++++++++++++++++++++----- Source/cmFindPackageCommand.h | 78 +- Source/cmFindPathCommand.cxx | 6 + Source/cmFindProgramCommand.cxx | 6 + 10 files changed, 1862 insertions(+), 601 deletions(-) create mode 100644 Source/cmFindCommon.cxx create mode 100644 Source/cmFindCommon.h diff --git a/Source/cmBootstrapCommands.cxx b/Source/cmBootstrapCommands.cxx index 5075c61a2..77adce5d4 100644 --- a/Source/cmBootstrapCommands.cxx +++ b/Source/cmBootstrapCommands.cxx @@ -42,6 +42,7 @@ #include "cmExecProgramCommand.cxx" #include "cmExternalMakefileProjectGenerator.cxx" #include "cmFindBase.cxx" +#include "cmFindCommon.cxx" #include "cmFileCommand.cxx" #include "cmFindFileCommand.cxx" #include "cmFindLibraryCommand.cxx" diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index d8e218d4a..63a589b8c 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx @@ -18,26 +18,10 @@ cmFindBase::cmFindBase() { + cmSystemTools::ReplaceString(this->GenericDocumentationPathsOrder, + "FIND_ARGS_XXX", " NAMES name"); this->AlreadyInCache = false; this->AlreadyInCacheWithoutMetaInfo = false; - this->NoDefaultPath = false; - this->NoCMakePath = false; - this->NoCMakeEnvironmentPath = false; - this->NoSystemEnvironmentPath = false; - this->NoCMakeSystemPath = false; - this->FindRootPathMode = RootPathModeBoth; - // default is to search frameworks first on apple -#if defined(__APPLE__) - this->SearchFrameworkFirst = true; - this->SearchAppBundleFirst = true; -#else - this->SearchFrameworkFirst = false; - this->SearchAppBundleFirst = false; -#endif - this->SearchFrameworkOnly = false; - this->SearchFrameworkLast = false; - this->SearchAppBundleOnly = false; - this->SearchAppBundleLast = false; this->GenericDocumentation = " FIND_XXX( name1 path1 path2 ...)\n" "This is the short-hand signature for the command that " @@ -54,8 +38,9 @@ cmFindBase::cmFindBase() " [NO_CMAKE_PATH]\n" " [NO_SYSTEM_ENVIRONMENT_PATH]\n" " [NO_CMAKE_SYSTEM_PATH]\n" - " [CMAKE_FIND_ROOT_PATH_BOTH | ONLY_CMAKE_FIND_ROOT_PATH | " - "NO_CMAKE_FIND_ROOT_PATH ]\n" + " [CMAKE_FIND_ROOT_PATH_BOTH |\n" + " ONLY_CMAKE_FIND_ROOT_PATH |\n" + " NO_CMAKE_FIND_ROOT_PATH]\n" " )\n" "" "This command is used to find a SEARCH_XXX_DESC. " @@ -84,9 +69,8 @@ cmFindBase::cmFindBase() "can be skipped if NO_CMAKE_ENVIRONMENT_PATH is passed.\n" "" " /XXX_SUBDIR for each in CMAKE_PREFIX_PATH\n" - " CMAKE_FRAMEWORK_PATH\n" - " CMAKE_APPBUNDLE_PATH\n" " CMAKE_XXX_PATH\n" + " CMAKE_XXX_MAC_PATH\n" "2. Search cmake variables with the same names as " "the cmake specific environment variables. These " "are intended to be used on the command line with a " @@ -94,9 +78,8 @@ cmFindBase::cmFindBase() "is passed.\n" "" " /XXX_SUBDIR for each in CMAKE_PREFIX_PATH\n" - " CMAKE_FRAMEWORK_PATH\n" - " CMAKE_APPBUNDLE_PATH\n" " CMAKE_XXX_PATH\n" + " CMAKE_XXX_MAC_PATH\n" "3. Search the standard system environment variables. " "This can be skipped if NO_SYSTEM_ENVIRONMENT_PATH is an argument.\n" " PATH\n" @@ -105,51 +88,14 @@ cmFindBase::cmFindBase() "for the current system. This can be skipped if NO_CMAKE_SYSTEM_PATH " "is passed.\n" " /XXX_SUBDIR for each in CMAKE_SYSTEM_PREFIX_PATH\n" - " CMAKE_SYSTEM_FRAMEWORK_PATH\n" - " CMAKE_SYSTEM_APPBUNDLE_PATH\n" " CMAKE_SYSTEM_XXX_PATH\n" + " CMAKE_SYSTEM_XXX_MAC_PATH\n" "5. Search the paths specified after PATHS or in the short-hand version " "of the command.\n" - "On Darwin or systems supporting OSX Frameworks, the cmake variable" - " CMAKE_FIND_FRAMEWORK can be set to empty or one of the following:\n" - " \"FIRST\" - Try to find frameworks before standard\n" - " libraries or headers. This is the default on Darwin.\n" - " \"LAST\" - Try to find frameworks after standard\n" - " libraries or headers.\n" - " \"ONLY\" - Only try to find frameworks.\n" - " \"NEVER\". - Never try to find frameworks.\n" - "On Darwin or systems supporting OSX Application Bundles, the cmake " - "variable CMAKE_FIND_APPBUNDLE can be set to empty or one of the " - "following:\n" - " \"FIRST\" - Try to find application bundles before standard\n" - " programs. This is the default on Darwin.\n" - " \"LAST\" - Try to find application bundles after standard\n" - " programs.\n" - " \"ONLY\" - Only try to find application bundles.\n" - " \"NEVER\". - Never try to find application bundles.\n" - "The CMake variable CMAKE_FIND_ROOT_PATH specifies one or more " - "directories to be prepended to all other search directories. " - "This effectively \"re-roots\" the entire search under given locations. " - "By default it is empty. It is especially useful when " - "cross-compiling to point to the root directory of the " - "target environment and CMake will search there too. By default at first " - "the directories listed in CMAKE_FIND_ROOT_PATH and then the non-rooted " - "directories will be searched. " - "The default behavior can be adjusted by setting " - "CMAKE_FIND_ROOT_PATH_MODE_XXX. This behavior can be manually " - "overridden on a per-call basis. " - "By using CMAKE_FIND_ROOT_PATH_BOTH the search order will " - "be as described above. If NO_CMAKE_FIND_ROOT_PATH is used " - "then CMAKE_FIND_ROOT_PATH will not be used. If ONLY_CMAKE_FIND_ROOT_PATH " - "is used then only the re-rooted directories will be searched.\n" - "The reason the paths listed in the call to the command are searched " - "last is that most users of CMake would expect things to be found " - "first in the locations specified by their environment. Projects may " - "override this behavior by simply calling the command twice:\n" - " FIND_XXX( NAMES name PATHS paths NO_DEFAULT_PATH)\n" - " FIND_XXX( NAMES name)\n" - "Once one of these calls succeeds the result variable will be set " - "and stored in the cache so that neither call will search again."; + ; + this->GenericDocumentation += this->GenericDocumentationMacPolicy; + this->GenericDocumentation += this->GenericDocumentationRootPath; + this->GenericDocumentation += this->GenericDocumentationPathsOrder; } bool cmFindBase::ParseArguments(std::vector const& argsIn) @@ -159,57 +105,6 @@ bool cmFindBase::ParseArguments(std::vector const& argsIn) this->SetError("called with incorrect number of arguments"); return false; } - std::string ff = this->Makefile->GetSafeDefinition("CMAKE_FIND_FRAMEWORK"); - if(ff == "NEVER") - { - this->SearchFrameworkLast = false; - this->SearchFrameworkFirst = false; - this->SearchFrameworkOnly = false; - } - else if (ff == "ONLY") - { - this->SearchFrameworkLast = false; - this->SearchFrameworkFirst = false; - this->SearchFrameworkOnly = true; - } - else if (ff == "FIRST") - { - this->SearchFrameworkLast = false; - this->SearchFrameworkFirst = true; - this->SearchFrameworkOnly = false; - } - else if (ff == "LAST") - { - this->SearchFrameworkLast = true; - this->SearchFrameworkFirst = false; - this->SearchFrameworkOnly = false; - } - - std::string fab = this->Makefile->GetSafeDefinition("CMAKE_FIND_APPBUNDLE"); - if(fab == "NEVER") - { - this->SearchAppBundleLast = false; - this->SearchAppBundleFirst = false; - this->SearchAppBundleOnly = false; - } - else if (fab == "ONLY") - { - this->SearchAppBundleLast = false; - this->SearchAppBundleFirst = false; - this->SearchAppBundleOnly = true; - } - else if (fab == "FIRST") - { - this->SearchAppBundleLast = false; - this->SearchAppBundleFirst = true; - this->SearchAppBundleOnly = false; - } - else if (fab == "LAST") - { - this->SearchAppBundleLast = true; - this->SearchAppBundleFirst = false; - this->SearchAppBundleOnly = false; - } // CMake versions below 2.3 did not search all these extra // locations. Preserve compatibility unless a modern argument is @@ -271,23 +166,12 @@ bool cmFindBase::ParseArguments(std::vector const& argsIn) return true; } this->AlreadyInCache = false; - - std::string findRootPathVar = "CMAKE_FIND_ROOT_PATH_MODE_"; - findRootPathVar += this->CMakePathName; - std::string rootPathMode = - this->Makefile->GetSafeDefinition(findRootPathVar.c_str()); - if (rootPathMode=="NEVER") - { - this->FindRootPathMode = RootPathModeNoRootPath; - } - else if (rootPathMode=="ONLY") - { - this->FindRootPathMode = RootPathModeOnlyRootPath; - } - else if (rootPathMode=="BOTH") - { - this->FindRootPathMode = RootPathModeBoth; - } + + // Find the current root path mode. + this->SelectDefaultRootPathMode(); + + // Find the current bundle/framework search policy. + this->SelectDefaultMacMode(); std::vector userPaths; std::string doc; @@ -327,60 +211,12 @@ bool cmFindBase::ParseArguments(std::vector const& argsIn) doingNames = false; this->NoDefaultPath = true; } - else if (args[j] == "NO_DEFAULT_PATH") + else if (this->CheckCommonArgument(args[j])) { compatibility = false; doingPaths = false; doingPathSuf = false; doingNames = false; - this->NoDefaultPath = true; - } - else if (args[j] == "NO_CMAKE_ENVIRONMENT_PATH") - { - compatibility = false; - doingPaths = false; - doingPathSuf = false; - doingNames = false; - this->NoCMakeEnvironmentPath = true; - } - else if (args[j] == "NO_CMAKE_PATH") - { - compatibility = false; - doingPaths = false; - doingPathSuf = false; - doingNames = false; - this->NoCMakePath = true; - } - else if (args[j] == "NO_SYSTEM_ENVIRONMENT_PATH") - { - compatibility = false; - doingPaths = false; - doingPathSuf = false; - doingNames = false; - this->NoSystemEnvironmentPath = true; - } - else if (args[j] == "NO_CMAKE_SYSTEM_PATH") - { - compatibility = false; - doingPaths = false; - doingPathSuf = false; - doingNames = false; - this->NoCMakeSystemPath = true; - } - else if (args[j] == "NO_CMAKE_FIND_ROOT_PATH") - { - compatibility = false; - this->FindRootPathMode = RootPathModeNoRootPath; - } - else if (args[j] == "ONLY_CMAKE_FIND_ROOT_PATH") - { - compatibility = false; - this->FindRootPathMode = RootPathModeOnlyRootPath; - } - else if (args[j] == "CMAKE_FIND_ROOT_PATH_BOTH") - { - compatibility = false; - this->FindRootPathMode = RootPathModeBoth; } else { @@ -393,8 +229,8 @@ bool cmFindBase::ParseArguments(std::vector const& argsIn) userPaths.push_back(args[j]); } else if(doingPathSuf) - { - this->SearchPathSuffixes.push_back(args[j]); + { + this->AddPathSuffix(args[j]); } } } @@ -446,8 +282,9 @@ bool cmFindBase::ParseArguments(std::vector const& argsIn) } } this->ExpandPaths(userPaths); - - this->HandleCMakeFindRootPath(); + + // Handle search root stuff. + this->RerootPaths(this->SearchPaths); return true; } @@ -457,11 +294,11 @@ void cmFindBase::ExpandPaths(std::vector userPaths) // standard search paths. if(!this->NoDefaultPath) { - if(this->SearchFrameworkFirst) + if(this->SearchFrameworkFirst || this->SearchFrameworkOnly) { this->AddFrameWorkPaths(); } - if(this->SearchAppBundleFirst) + if(this->SearchAppBundleFirst || this->SearchAppBundleOnly) { this->AddAppBundlePaths(); } @@ -509,55 +346,7 @@ void cmFindBase::ExpandPaths(std::vector userPaths) this->AddPaths(paths); } -void cmFindBase::HandleCMakeFindRootPath() -{ - if (this->FindRootPathMode == RootPathModeNoRootPath) - { - return; - } - - const char* rootPath = this->Makefile->GetDefinition("CMAKE_FIND_ROOT_PATH"); - if ((rootPath == 0) || (strlen(rootPath) == 0)) - { - return; - } - - std::vector roots; - cmSystemTools::ExpandListArgument(rootPath, roots); - - std::vector unrootedPaths=this->SearchPaths; - this->SearchPaths.clear(); - - for (std::vector::const_iterator rootIt = roots.begin(); - rootIt != roots.end(); - ++rootIt ) - { - for (std::vector::const_iterator it = unrootedPaths.begin(); - it != unrootedPaths.end(); - ++it ) - { - // if the current directory is already inside the current root, don't - // add the root again - std::string rootedDir; - if (cmSystemTools::IsSubDirectory(it->c_str(), rootIt->c_str())) - { - rootedDir = *it; - } - else - { - rootedDir=*rootIt; - rootedDir+=*it; - } - this->SearchPaths.push_back(rootedDir); - } - } - - if (this->FindRootPathMode == RootPathModeBoth) - { - this->AddPaths(unrootedPaths); - } -} - +//---------------------------------------------------------------------------- void cmFindBase::AddEnvironmentVariables() { std::vector paths; @@ -570,14 +359,6 @@ void cmFindBase::AddEnvironmentVariables() var += this->CMakePathName; var += "_PATH"; cmSystemTools::GetPath(paths, var.c_str()); - if(this->SearchAppBundleLast) - { - cmSystemTools::GetPath(paths, "CMAKE_APPBUNDLE_PATH"); - } - if(this->SearchFrameworkLast) - { - cmSystemTools::GetPath(paths, "CMAKE_FRAMEWORK_PATH"); - } this->AddPaths(paths); } @@ -585,15 +366,15 @@ void cmFindBase::AddFindPrefix(std::vector& dest, const std::vector& src) { // default for programs - std::string subdir = "/bin"; + std::string subdir = "bin"; if (this->CMakePathName == "INCLUDE") { - subdir = "/include"; + subdir = "include"; } else if (this->CMakePathName == "LIBRARY") { - subdir = "/lib"; + subdir = "lib"; } else if (this->CMakePathName == "FRAMEWORK") { @@ -604,14 +385,15 @@ void cmFindBase::AddFindPrefix(std::vector& dest, it != src.end(); ++it) { - std::string dirWithSubdir = it->c_str(); - dirWithSubdir += subdir; - dest.push_back(dirWithSubdir); - if (subdir == "/bin") + std::string dir = it->c_str(); + if(!subdir.empty() && !dir.empty() && dir[dir.size()-1] != '/') { - dirWithSubdir = it->c_str(); - dirWithSubdir += "/sbin"; - dest.push_back(dirWithSubdir); + dir += "/"; + } + dest.push_back(dir + subdir); + if (subdir == "bin") + { + dest.push_back(dir + "sbin"); } if(!subdir.empty()) { @@ -622,35 +404,9 @@ void cmFindBase::AddFindPrefix(std::vector& dest, void cmFindBase::AddFrameWorkPaths() { - if(this->NoDefaultPath) - { - return; - } std::vector paths; - // first environment variables - if(!this->NoCMakeEnvironmentPath) - { - cmSystemTools::GetPath(paths, "CMAKE_FRAMEWORK_PATH"); - } - // add cmake variables - if(!this->NoCMakePath) - { - if(const char* path = - this->Makefile->GetDefinition("CMAKE_FRAMEWORK_PATH")) - { - cmSystemTools::ExpandListArgument(path, paths); - } - } - // AddCMakeSystemVariables - if(!this->NoCMakeSystemPath) - { - if(const char* path = - this->Makefile->GetDefinition("CMAKE_SYSTEM_FRAMEWORK_PATH")) - { - cmSystemTools::ExpandListArgument(path, paths); - } - } - this->AddPaths(paths); + this->GetFrameworkPaths(paths); + this->AddPaths(paths); } void cmFindBase::AddPaths(std::vector & paths) @@ -665,35 +421,9 @@ void cmFindBase::AddPaths(std::vector & paths) void cmFindBase::AddAppBundlePaths() { - if(this->NoDefaultPath) - { - return; - } std::vector paths; - // first environment variables - if(!this->NoCMakeEnvironmentPath) - { - cmSystemTools::GetPath(paths, "CMAKE_APPBUNDLE_PATH"); - } - // add cmake variables - if(!this->NoCMakePath) - { - if(const char* path = - this->Makefile->GetDefinition("CMAKE_APPBUNDLE_PATH")) - { - cmSystemTools::ExpandListArgument(path, paths); - } - } - // AddCMakeSystemVariables - if(!this->NoCMakeSystemPath) - { - if(const char* path = - this->Makefile->GetDefinition("CMAKE_SYSTEM_APPBUNDLE_PATH")) - { - cmSystemTools::ExpandListArgument(path, paths); - } - } - this->AddPaths(paths); + this->GetAppBundlePaths(paths); + this->AddPaths(paths); } void cmFindBase::AddCMakeVariables() @@ -715,22 +445,6 @@ void cmFindBase::AddCMakeVariables() { cmSystemTools::ExpandListArgument(path, paths); } - if(this->SearchAppBundleLast) - { - if(const char* path = - this->Makefile->GetDefinition("CMAKE_APPBUNDLE_PATH")) - { - cmSystemTools::ExpandListArgument(path, paths); - } - } - if(this->SearchFrameworkLast) - { - if(const char* path = - this->Makefile->GetDefinition("CMAKE_FRAMEWORK_PATH")) - { - cmSystemTools::ExpandListArgument(path, paths); - } - } this->AddPaths(paths); } @@ -764,22 +478,6 @@ void cmFindBase::AddCMakeSystemVariables() { cmSystemTools::ExpandListArgument(path, paths); } - if(this->SearchAppBundleLast) - { - if(const char* path = - this->Makefile->GetDefinition("CMAKE_SYSTEM_APPBUNDLE_PATH")) - { - cmSystemTools::ExpandListArgument(path, paths); - } - } - if(this->SearchFrameworkLast) - { - if(const char* path = - this->Makefile->GetDefinition("CMAKE_SYSTEM_FRAMEWORK_PATH")) - { - cmSystemTools::ExpandListArgument(path, paths); - } - } this->AddPaths(paths); } diff --git a/Source/cmFindBase.h b/Source/cmFindBase.h index 5efbe2ad1..86a76f75a 100644 --- a/Source/cmFindBase.h +++ b/Source/cmFindBase.h @@ -17,15 +17,15 @@ #ifndef cmFindBase_h #define cmFindBase_h -#include "cmCommand.h" +#include "cmFindCommon.h" /** \class cmFindBase - * \brief Define a command to search for an executable program. + * \brief Base class for most FIND_XXX commands. * * cmFindBase is a parent class for cmFindProgramCommand, cmFindPathCommand, - * and cmFindLibraryCommand, cmFindFile + * and cmFindLibraryCommand, cmFindFileCommand */ -class cmFindBase : public cmCommand +class cmFindBase : public cmFindCommon { public: cmFindBase(); @@ -34,20 +34,15 @@ public: * the CMakeLists.txt file. */ virtual bool ParseArguments(std::vector const& args); - cmTypeMacro(cmFindBase, cmCommand); + cmTypeMacro(cmFindBase, cmFindCommon); virtual const char* GetFullDocumentation() {return this->GenericDocumentation.c_str();} - enum RootPathMode { RootPathModeBoth, - RootPathModeOnlyRootPath, - RootPathModeNoRootPath }; - protected: void PrintFindStuff(); void ExpandPaths(std::vector userPaths); - void HandleCMakeFindRootPath(); - + // add to the SearchPaths void AddPaths(std::vector& paths); void AddFrameWorkPaths(); @@ -70,29 +65,12 @@ protected: cmStdString VariableName; std::vector Names; std::vector SearchPaths; - std::vector SearchPathSuffixes; // CMAKE_*_PATH CMAKE_SYSTEM_*_PATH FRAMEWORK|LIBRARY|INCLUDE|PROGRAM - cmStdString CMakePathName; cmStdString EnvironmentPath; // LIB,INCLUDE bool AlreadyInCache; bool AlreadyInCacheWithoutMetaInfo; - bool NoDefaultPath; - bool NoCMakePath; - bool NoCMakeEnvironmentPath; - bool NoSystemEnvironmentPath; - bool NoCMakeSystemPath; - RootPathMode FindRootPathMode; - - bool SearchFrameworkFirst; - bool SearchFrameworkOnly; - bool SearchFrameworkLast; - - bool SearchAppBundleFirst; - bool SearchAppBundleOnly; - bool SearchAppBundleLast; - }; diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx new file mode 100644 index 000000000..0c6af0daf --- /dev/null +++ b/Source/cmFindCommon.cxx @@ -0,0 +1,479 @@ +/*========================================================================= + + Program: CMake - Cross-Platform Makefile Generator + Module: $RCSfile$ + Language: C++ + Date: $Date$ + Version: $Revision$ + + Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. + See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#include "cmFindCommon.h" + +//---------------------------------------------------------------------------- +cmFindCommon::cmFindCommon() +{ + this->FindRootPathMode = RootPathModeBoth; + this->NoDefaultPath = false; + this->NoCMakePath = false; + this->NoCMakeEnvironmentPath = false; + this->NoSystemEnvironmentPath = false; + this->NoCMakeSystemPath = false; + + // OS X Bundle and Framework search policy. The default is to + // search frameworks first on apple. +#if defined(__APPLE__) + this->SearchFrameworkFirst = true; + this->SearchAppBundleFirst = true; +#else + this->SearchFrameworkFirst = false; + this->SearchAppBundleFirst = false; +#endif + this->SearchFrameworkOnly = false; + this->SearchFrameworkLast = false; + this->SearchAppBundleOnly = false; + this->SearchAppBundleLast = false; + + // Documentation components. + this->GenericDocumentationMacPolicy = + "On Darwin or systems supporting OS X Frameworks, the cmake variable" + " CMAKE_FIND_FRAMEWORK can be set to empty or one of the following:\n" + " \"FIRST\" - Try to find frameworks before standard\n" + " libraries or headers. This is the default on Darwin.\n" + " \"LAST\" - Try to find frameworks after standard\n" + " libraries or headers.\n" + " \"ONLY\" - Only try to find frameworks.\n" + " \"NEVER\". - Never try to find frameworks.\n" + "On Darwin or systems supporting OS X Application Bundles, the cmake " + "variable CMAKE_FIND_APPBUNDLE can be set to empty or one of the " + "following:\n" + " \"FIRST\" - Try to find application bundles before standard\n" + " programs. This is the default on Darwin.\n" + " \"LAST\" - Try to find application bundles after standard\n" + " programs.\n" + " \"ONLY\" - Only try to find application bundles.\n" + " \"NEVER\". - Never try to find application bundles.\n"; + this->GenericDocumentationRootPath = + "The CMake variable CMAKE_FIND_ROOT_PATH specifies one or more " + "directories to be prepended to all other search directories. " + "This effectively \"re-roots\" the entire search under given locations. " + "By default it is empty. It is especially useful when " + "cross-compiling to point to the root directory of the " + "target environment and CMake will search there too. By default at first " + "the directories listed in CMAKE_FIND_ROOT_PATH and then the non-rooted " + "directories will be searched. " + "The default behavior can be adjusted by setting " + "CMAKE_FIND_ROOT_PATH_MODE_XXX. This behavior can be manually " + "overridden on a per-call basis. " + "By using CMAKE_FIND_ROOT_PATH_BOTH the search order will " + "be as described above. If NO_CMAKE_FIND_ROOT_PATH is used " + "then CMAKE_FIND_ROOT_PATH will not be used. If ONLY_CMAKE_FIND_ROOT_PATH " + "is used then only the re-rooted directories will be searched.\n"; + this->GenericDocumentationPathsOrder = + "The reason the paths listed in the call to the command are searched " + "last is that most users of CMake would expect things to be found " + "first in the locations specified by their environment. Projects may " + "override this behavior by simply calling the command twice:\n" + " FIND_XXX(FIND_ARGS_XXX PATHS paths... NO_DEFAULT_PATH)\n" + " FIND_XXX(FIND_ARGS_XXX)\n" + "Once one of these calls succeeds the result variable will be set " + "and stored in the cache so that neither call will search again."; +} + +//---------------------------------------------------------------------------- +cmFindCommon::~cmFindCommon() +{ +} + +//---------------------------------------------------------------------------- +void cmFindCommon::SelectDefaultRootPathMode() +{ + // Use both by default. + this->FindRootPathMode = RootPathModeBoth; + + // Check the policy variable for this find command type. + std::string findRootPathVar = "CMAKE_FIND_ROOT_PATH_MODE_"; + findRootPathVar += this->CMakePathName; + std::string rootPathMode = + this->Makefile->GetSafeDefinition(findRootPathVar.c_str()); + if (rootPathMode=="NEVER") + { + this->FindRootPathMode = RootPathModeNoRootPath; + } + else if (rootPathMode=="ONLY") + { + this->FindRootPathMode = RootPathModeOnlyRootPath; + } + else if (rootPathMode=="BOTH") + { + this->FindRootPathMode = RootPathModeBoth; + } +} + +//---------------------------------------------------------------------------- +void cmFindCommon::SelectDefaultMacMode() +{ + std::string ff = this->Makefile->GetSafeDefinition("CMAKE_FIND_FRAMEWORK"); + if(ff == "NEVER") + { + this->SearchFrameworkLast = false; + this->SearchFrameworkFirst = false; + this->SearchFrameworkOnly = false; + } + else if(ff == "ONLY") + { + this->SearchFrameworkLast = false; + this->SearchFrameworkFirst = false; + this->SearchFrameworkOnly = true; + } + else if(ff == "FIRST") + { + this->SearchFrameworkLast = false; + this->SearchFrameworkFirst = true; + this->SearchFrameworkOnly = false; + } + else if(ff == "LAST") + { + this->SearchFrameworkLast = true; + this->SearchFrameworkFirst = false; + this->SearchFrameworkOnly = false; + } + + std::string fab = this->Makefile->GetSafeDefinition("CMAKE_FIND_APPBUNDLE"); + if(fab == "NEVER") + { + this->SearchAppBundleLast = false; + this->SearchAppBundleFirst = false; + this->SearchAppBundleOnly = false; + } + else if(fab == "ONLY") + { + this->SearchAppBundleLast = false; + this->SearchAppBundleFirst = false; + this->SearchAppBundleOnly = true; + } + else if(fab == "FIRST") + { + this->SearchAppBundleLast = false; + this->SearchAppBundleFirst = true; + this->SearchAppBundleOnly = false; + } + else if(fab == "LAST") + { + this->SearchAppBundleLast = true; + this->SearchAppBundleFirst = false; + this->SearchAppBundleOnly = false; + } +} + +//---------------------------------------------------------------------------- +void cmFindCommon::RerootPaths(std::vector& paths) +{ +#if 0 + for(std::vector::const_iterator i = paths.begin(); + i != paths.end(); ++i) + { + fprintf(stderr, "[%s]\n", i->c_str()); + } +#endif + + // Short-circuit if there is nothing to do. + if(this->FindRootPathMode == RootPathModeNoRootPath) + { + return; + } + const char* rootPath = + this->Makefile->GetDefinition("CMAKE_FIND_ROOT_PATH"); + if((rootPath == 0) || (strlen(rootPath) == 0)) + { + return; + } + + // Construct the list of path roots with no trailing slashes. + std::vector roots; + cmSystemTools::ExpandListArgument(rootPath, roots); + for(std::vector::iterator ri = roots.begin(); + ri != roots.end(); ++ri) + { + cmSystemTools::ConvertToUnixSlashes(*ri); + } + + // Copy the original set of unrooted paths. + std::vector unrootedPaths = paths; + paths.clear(); + + for(std::vector::const_iterator ri = roots.begin(); + ri != roots.end(); ++ri) + { + for(std::vector::const_iterator ui = unrootedPaths.begin(); + ui != unrootedPaths.end(); ++ui) + { + // Place the unrooted path under the current root if it is not + // already inside. Skip the unrooted path if it is relative to + // a user home directory or is empty. + std::string rootedDir; + if(cmSystemTools::IsSubDirectory(ui->c_str(), ri->c_str())) + { + rootedDir = *ui; + } + else if(!ui->empty() && (*ui)[0] != '~') + { + // Start with the new root. + rootedDir = *ri; + rootedDir += "/"; + + // Append the original path with its old root removed. + rootedDir += cmSystemTools::SplitPathRootComponent(ui->c_str()); + } + + // Store the new path. + paths.push_back(rootedDir); + } + } + + // If searching both rooted and unrooted paths add the original + // paths again. + if(this->FindRootPathMode == RootPathModeBoth) + { + paths.insert(paths.end(), unrootedPaths.begin(), unrootedPaths.end()); + } +} + +//---------------------------------------------------------------------------- +bool cmFindCommon::CheckCommonArgument(std::string const& arg) +{ + if(arg == "NO_DEFAULT_PATH") + { + this->NoDefaultPath = true; + } + else if(arg == "NO_CMAKE_ENVIRONMENT_PATH") + { + this->NoCMakeEnvironmentPath = true; + } + else if(arg == "NO_CMAKE_PATH") + { + this->NoCMakePath = true; + } + else if(arg == "NO_SYSTEM_ENVIRONMENT_PATH") + { + this->NoSystemEnvironmentPath = true; + } + else if(arg == "NO_CMAKE_SYSTEM_PATH") + { + this->NoCMakeSystemPath = true; + } + else if(arg == "NO_CMAKE_FIND_ROOT_PATH") + { + this->FindRootPathMode = RootPathModeNoRootPath; + } + else if(arg == "ONLY_CMAKE_FIND_ROOT_PATH") + { + this->FindRootPathMode = RootPathModeOnlyRootPath; + } + else if(arg == "CMAKE_FIND_ROOT_PATH_BOTH") + { + this->FindRootPathMode = RootPathModeBoth; + } + else + { + // The argument is not one of the above. + return false; + } + + // The argument is one of the above. + return true; +} + +//---------------------------------------------------------------------------- +void cmFindCommon::AddPathSuffix(std::string const& arg) +{ + std::string suffix = arg; + + // Strip leading and trailing slashes. + if(suffix.empty()) + { + return; + } + if(suffix[0] == '/') + { + suffix = suffix.substr(1, suffix.npos); + } + if(suffix.empty()) + { + return; + } + if(suffix[suffix.size()-1] == '/') + { + suffix = suffix.substr(0, suffix.size()-1); + } + if(suffix.empty()) + { + return; + } + + // Store the suffix. + this->SearchPathSuffixes.push_back(suffix); +} + +//---------------------------------------------------------------------------- +void cmFindCommon::GetAppBundlePaths(std::vector& paths) +{ + if(this->NoDefaultPath) + { + return; + } + std::vector tmp; + + // first environment variables + if(!this->NoCMakeEnvironmentPath) + { + cmSystemTools::GetPath(tmp, "CMAKE_APPBUNDLE_PATH"); + this->AddPathsInternal(paths, tmp, EnvPath); + tmp.clear(); + } + + // add cmake variables + if(!this->NoCMakePath) + { + if(const char* path = + this->Makefile->GetDefinition("CMAKE_APPBUNDLE_PATH")) + { + cmSystemTools::ExpandListArgument(path, tmp); + this->AddPathsInternal(paths, tmp, CMakePath); + tmp.clear(); + } + } + + // add cmake system variables + if(!this->NoCMakeSystemPath) + { + if(const char* path = + this->Makefile->GetDefinition("CMAKE_SYSTEM_APPBUNDLE_PATH")) + { + cmSystemTools::ExpandListArgument(path, tmp); + this->AddPathsInternal(paths, tmp, CMakePath); + tmp.clear(); + } + } +} + +//---------------------------------------------------------------------------- +void cmFindCommon::GetFrameworkPaths(std::vector& paths) +{ + if(this->NoDefaultPath) + { + return; + } + std::vector tmp; + + // first environment variables + if(!this->NoCMakeEnvironmentPath) + { + cmSystemTools::GetPath(tmp, "CMAKE_FRAMEWORK_PATH"); + this->AddPathsInternal(paths, tmp, EnvPath); + tmp.clear(); + } + + // add cmake variables + if(!this->NoCMakePath) + { + if(const char* path = + this->Makefile->GetDefinition("CMAKE_FRAMEWORK_PATH")) + { + cmSystemTools::ExpandListArgument(path, tmp); + this->AddPathsInternal(paths, tmp, CMakePath); + tmp.clear(); + } + } + + // add cmake system variables + if(!this->NoCMakeSystemPath) + { + if(const char* path = + this->Makefile->GetDefinition("CMAKE_SYSTEM_FRAMEWORK_PATH")) + { + cmSystemTools::ExpandListArgument(path, tmp); + this->AddPathsInternal(paths, tmp, CMakePath); + tmp.clear(); + } + } +} + +//---------------------------------------------------------------------------- +void cmFindCommon::AddCMakePath(std::vector& out_paths, + const char* variable, + std::set* emmitted) +{ + // Get a path from a CMake variable. + if(const char* varPath = this->Makefile->GetDefinition(variable)) + { + std::vector tmp; + cmSystemTools::ExpandListArgument(varPath, tmp); + + // Relative paths are interpreted with respect to the current + // source directory. + this->AddPathsInternal(out_paths, tmp, CMakePath, emmitted); + } +} + +//---------------------------------------------------------------------------- +void cmFindCommon::AddEnvPath(std::vector& out_paths, + const char* variable, + std::set* emmitted) +{ + // Get a path from the environment. + std::vector tmp; + cmSystemTools::GetPath(tmp, variable); + + // Relative paths are interpreted with respect to the current + // working directory. + this->AddPathsInternal(out_paths, tmp, EnvPath, emmitted); +} + +//---------------------------------------------------------------------------- +void cmFindCommon::AddPathsInternal(std::vector& out_paths, + std::vector const& in_paths, + PathType pathType, + std::set* emmitted) +{ + for(std::vector::const_iterator i = in_paths.begin(); + i != in_paths.end(); ++i) + { + this->AddPathInternal(out_paths, *i, pathType, emmitted); + } +} + +//---------------------------------------------------------------------------- +void cmFindCommon::AddPathInternal(std::vector& out_paths, + std::string const& in_path, + PathType pathType, + std::set* emmitted) +{ + if(in_path.empty()) + { + return; + } + + // Select the base path with which to interpret relative paths. + const char* relbase = 0; + if(pathType == CMakePath) + { + relbase = this->Makefile->GetCurrentDirectory(); + } + + // Convert to clean full path. + std::string fullPath = + cmSystemTools::CollapseFullPath(in_path.c_str(), relbase); + + // Insert the path if has not already been emmitted. + if(!emmitted || emmitted->insert(fullPath).second) + { + out_paths.push_back(fullPath.c_str()); + } +} diff --git a/Source/cmFindCommon.h b/Source/cmFindCommon.h new file mode 100644 index 000000000..962a017bc --- /dev/null +++ b/Source/cmFindCommon.h @@ -0,0 +1,95 @@ +/*========================================================================= + + Program: CMake - Cross-Platform Makefile Generator + Module: $RCSfile$ + Language: C++ + Date: $Date$ + Version: $Revision$ + + Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. + See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef cmFindCommon_h +#define cmFindCommon_h + +#include "cmCommand.h" + +/** \class cmFindCommon + * \brief Base class for FIND_XXX implementations. + * + * cmFindCommon is a parent class for cmFindBase, + * cmFindProgramCommand, cmFindPathCommand, cmFindLibraryCommand, + * cmFindFileCommand, and cmFindPackageCommand. + */ +class cmFindCommon : public cmCommand +{ +public: + cmFindCommon(); + ~cmFindCommon(); + cmTypeMacro(cmFindCommon, cmCommand); + +protected: + + enum RootPathMode { RootPathModeBoth, + RootPathModeOnlyRootPath, + RootPathModeNoRootPath }; + + enum PathType { FullPath, CMakePath, EnvPath }; + + /** Place a set of search paths under the search roots. */ + void RerootPaths(std::vector& paths); + + /** Compute the current default root path mode. */ + void SelectDefaultRootPathMode(); + + /** Compute the current default bundle/framework search policy. */ + void SelectDefaultMacMode(); + + cmStdString CMakePathName; + RootPathMode FindRootPathMode; + + bool CheckCommonArgument(std::string const& arg); + void AddPathSuffix(std::string const& arg); + void GetAppBundlePaths(std::vector& paths); + void GetFrameworkPaths(std::vector& paths); + + void AddCMakePath(std::vector& out_paths, + const char* variable, std::set* emmitted = 0); + void AddEnvPath(std::vector& out_paths, + const char* variable, std::set* emmitted = 0); + void AddPathsInternal(std::vector& out_paths, + std::vector const& in_paths, + PathType pathType, + std::set* emmitted = 0); + void AddPathInternal(std::vector& out_paths, + std::string const& in_path, + PathType pathType, + std::set* emmitted = 0); + + bool NoDefaultPath; + bool NoCMakePath; + bool NoCMakeEnvironmentPath; + bool NoSystemEnvironmentPath; + bool NoCMakeSystemPath; + + std::vector SearchPathSuffixes; + + std::string GenericDocumentationMacPolicy; + std::string GenericDocumentationRootPath; + std::string GenericDocumentationPathsOrder; + + bool SearchFrameworkFirst; + bool SearchFrameworkOnly; + bool SearchFrameworkLast; + + bool SearchAppBundleFirst; + bool SearchAppBundleOnly; + bool SearchAppBundleLast; +}; + +#endif diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx index fb836c854..bae60c4c1 100644 --- a/Source/cmFindLibraryCommand.cxx +++ b/Source/cmFindLibraryCommand.cxx @@ -23,6 +23,12 @@ cmFindLibraryCommand::cmFindLibraryCommand() "FIND_XXX", "find_library"); cmSystemTools::ReplaceString(this->GenericDocumentation, "CMAKE_XXX_PATH", "CMAKE_LIBRARY_PATH"); + cmSystemTools::ReplaceString(this->GenericDocumentation, + "CMAKE_XXX_MAC_PATH", + "CMAKE_FRAMEWORK_PATH"); + cmSystemTools::ReplaceString(this->GenericDocumentation, + "CMAKE_SYSTEM_XXX_MAC_PATH", + "CMAKE_SYSTEM_FRAMEWORK_PATH"); cmSystemTools::ReplaceString(this->GenericDocumentation, "XXX_SYSTEM", "LIB"); cmSystemTools::ReplaceString(this->GenericDocumentation, diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 96afe175a..26b5197c2 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -47,6 +47,158 @@ void cmFindPackageNeedBackwardsCompatibility(const std::string& variable, #endif } +//---------------------------------------------------------------------------- +cmFindPackageCommand::cmFindPackageCommand() +{ + cmSystemTools::ReplaceString(this->GenericDocumentationRootPath, + "CMAKE_FIND_ROOT_PATH_MODE_XXX", + "CMAKE_FIND_ROOT_PATH_MODE_PACKAGE"); + cmSystemTools::ReplaceString(this->GenericDocumentationPathsOrder, + "FIND_ARGS_XXX", ""); + cmSystemTools::ReplaceString(this->GenericDocumentationPathsOrder, + "FIND_XXX", "find_package"); + this->CMakePathName = "PACKAGE"; + this->Quiet = false; + this->Required = false; + this->NoBuilds = false; + this->NoModule = false; + this->DebugMode = false; + this->CommandDocumentation = + " find_package( [major.minor] [QUIET] [NO_MODULE]\n" + " [[REQUIRED|COMPONENTS] [components...]]\n" + " [NAMES name1 [name2 ...]]\n" + " [CONFIGS config1 [config2 ...]]\n" + " [PATHS path1 [path2 ... ]]\n" + " [PATH_SUFFIXES suffix1 [suffix2 ...]]\n" + " [NO_DEFAULT_PATH]\n" + " [NO_CMAKE_ENVIRONMENT_PATH]\n" + " [NO_CMAKE_PATH]\n" + " [NO_SYSTEM_ENVIRONMENT_PATH]\n" + " [NO_CMAKE_BUILDS_PATH]\n" + " [NO_CMAKE_SYSTEM_PATH]\n" + " [CMAKE_FIND_ROOT_PATH_BOTH |\n" + " ONLY_CMAKE_FIND_ROOT_PATH |\n" + " NO_CMAKE_FIND_ROOT_PATH])\n" + "Finds and loads settings from an external project. _FOUND " + "will be set to indicate whether the package was found. Settings that " + "can be used when _FOUND is true are package-specific. " + "A package-specific list of components may be listed after the " + "REQUIRED option, or after the COMPONENTS option if no REQUIRED " + "option is given. The \"major.minor\" version argument is currently " + "a placeholder for future use and is ignored. " + "The command has two modes by which it searches for packages: " + "\"Module\" mode and \"Config\" mode." + "\n" + "Module mode has a reduced signature:\n" + " find_package( [major.minor] [QUIET]\n" + " [[REQUIRED|COMPONENTS] [components...]])\n" + "CMake searches for a file called \"Find.cmake\" in " + "the CMAKE_MODULE_PATH followed by the CMake installation. " + "If the file is found, it is read and processed by CMake. " + "It is responsible for finding the package " + "or producing an error message if package content cannot be found. " + "Otherwise the command proceeds to Config mode. The NO_MODULE " + "option may be used to skip Module mode explicitly, but the option " + "is implied by use of options not specified in the reduced signature." + "\n" + "Config mode attempts to locate a configuration file provided by the " + "package to be found. A cache entry called _DIR is created to " + "hold the directory containing the file. " + "By default the command searches for a package with the name . " + "If the NAMES option is given the names following it are used instead " + "of . " + "The command searches for a file called \"Config.cmake\" or " + "\"-config.cmake\" for each name specified. " + "A replacement set of possible configuration file names may be given " + "using the CONFIGS option. " + "The search procedure is specified below. Once found, the configuration " + "file is read and processed by CMake. Since the file is provided by the " + "package it already knows the location of package contents. " + "The full path to the configuration file is stored in the cmake " + "variable _CONFIG." + "\n" + "If the package configuration file cannot be found CMake " + "will generate an error describing the problem unless the QUIET " + "argument is specified. If REQUIRED is specified and the package " + "is not found a fatal error is generated and the configure step stops " + "executing. If _DIR has been set to a directory not containing " + "a configuration file a fatal error is always generated because user " + "intervention is required." + "\n" + "CMake constructs a set of possible installation prefixes for the " + "package. Under each prefix the following directories are searched " + "for a configuration file:\n" + " / (W)\n" + " /(cmake|CMake)/ (W)\n" + " /(share|lib)/*/ (P)\n" + " /(share|lib)/*/(cmake|CMake)/ (P)\n" + "On systems supporting OS X Frameworks and Application Bundles " + "the following directories are searched for frameworks or bundles " + "containing a configuration file:\n" + " /.framework/Resources/ (A)\n" + " /.framework/Resources/CMake/ (A)\n" + " /.framework/Versions/*/Resources/ (A)\n" + " /.framework/Versions/*/Resources/CMake/ (A)\n" + " /.app/Contents/Resources/ (A)\n" + " /.app/Contents/Resources/CMake/ (A)\n" + "In all cases the is treated as case-insensitive and corresponds " + "to any of the names specified ( or names given by NAMES). " + "If PATH_SUFFIXES is specified the suffixes are appended to each " + "(W) or (P) directory entry one-by-one.\n" + "This set of directories is intended to work in cooperation with " + "projects that provide configuration files in their installation trees. " + "Directories above marked with (W) are intended for installations on " + "Windows where the prefix may point at the top of an application's " + "installation directory. Those marked with (P) are intended for " + "installations on POSIX platforms where the prefix is shared by " + "multiple packages. This is merely a convention, so all (W) and (P) " + "directories are still searched on all platforms. " + "Directories marked with (A) are intended for installations on " + "Apple platforms. The cmake variables CMAKE_FIND_FRAMEWORK and " + "CMAKE_FIND_APPBUNDLE determine the order of preference " + "as specified below.\n" + "The set of installation prefixes is constructed using the following " + "steps. If NO_DEFAULT_PATH is specified steps 1-5 are skipped.\n" + "1. Search cmake specific environment variables. This " + "can be skipped if NO_CMAKE_ENVIRONMENT_PATH is passed.\n" + " CMAKE_PREFIX_PATH\n" + " CMAKE_FRAMEWORK_PATH\n" + " CMAKE_APPBUNDLE_PATH\n" + "2. Search cmake variables with the same names as the cmake specific " + "environment variables. These are intended to be used on the command " + "line with a -DVAR=value. This can be skipped if NO_CMAKE_PATH " + "is passed.\n" + " CMAKE_PREFIX_PATH\n" + " CMAKE_FRAMEWORK_PATH\n" + " CMAKE_APPBUNDLE_PATH\n" + "3. Search the standard system environment variables. " + "This can be skipped if NO_SYSTEM_ENVIRONMENT_PATH is passed. " + "Path entries ending in \"/bin\" or \"/sbin\" are automatically " + "converted to their parent directories.\n" + " PATH\n" + "4. Search project build trees recently configured in a CMake GUI. " + "This can be skipped if NO_CMAKE_BUILDS_PATH is passed. " + "It is intended for the case when a user is building multiple " + "dependent projects one after another.\n" + "5. Search cmake variables defined in the Platform files " + "for the current system. This can be skipped if NO_CMAKE_SYSTEM_PATH " + "is passed.\n" + " CMAKE_SYSTEM_PREFIX_PATH\n" + " CMAKE_SYSTEM_FRAMEWORK_PATH\n" + " CMAKE_SYSTEM_APPBUNDLE_PATH\n" + "6. Search paths specified by the PATHS option.\n" + ; + this->CommandDocumentation += this->GenericDocumentationMacPolicy; + this->CommandDocumentation += this->GenericDocumentationRootPath; + this->CommandDocumentation += this->GenericDocumentationPathsOrder; +} + +//---------------------------------------------------------------------------- +const char* cmFindPackageCommand::GetFullDocumentation() +{ + return this->CommandDocumentation.c_str(); +} + //---------------------------------------------------------------------------- bool cmFindPackageCommand::InitialPass(std::vector const& args) { @@ -56,40 +208,94 @@ bool cmFindPackageCommand::InitialPass(std::vector const& args) return false; } + // Check for debug mode. + this->DebugMode = this->Makefile->IsOn("CMAKE_FIND_DEBUG_MODE"); + + // Find the current root path mode. + this->SelectDefaultRootPathMode(); + + // Find the current bundle/framework search policy. + this->SelectDefaultMacMode(); + // Record options. this->Name = args[0]; - bool quiet = false; - bool required = false; - bool no_module = false; std::string components; const char* components_sep = ""; + // Check ancient compatibility. + this->Compatibility_1_6 = + this->Makefile->GetLocalGenerator() + ->NeedBackwardsCompatibility(1, 6); + + // Always search directly in a generated path. + this->SearchPathSuffixes.push_back(""); + // Parse the arguments. - bool doing_components = false; + enum Doing { DoingNone, DoingComponents, DoingNames, DoingPaths, + DoingPathSuffixes, DoingConfigs }; + Doing doing = DoingNone; cmsys::RegularExpression version("^[0-9.]+$"); bool haveVersion = false; for(unsigned int i=1; i < args.size(); ++i) { if(args[i] == "QUIET") { - quiet = true; - doing_components = false; + this->Quiet = true; + doing = DoingNone; } else if(args[i] == "NO_MODULE") { - no_module = true; - doing_components = false; + this->NoModule = true; + doing = DoingNone; } else if(args[i] == "REQUIRED") { - required = true; - doing_components = true; + this->Required = true; + doing = DoingComponents; } else if(args[i] == "COMPONENTS") { - doing_components = true; + this->Compatibility_1_6 = false; + doing = DoingComponents; } - else if(doing_components) + else if(args[i] == "NAMES") + { + this->NoModule = true; + this->Compatibility_1_6 = false; + doing = DoingNames; + } + else if(args[i] == "PATHS") + { + this->NoModule = true; + this->Compatibility_1_6 = false; + doing = DoingPaths; + } + else if(args[i] == "PATH_SUFFIXES") + { + this->NoModule = true; + this->Compatibility_1_6 = false; + doing = DoingPathSuffixes; + } + else if(args[i] == "CONFIGS") + { + this->NoModule = true; + this->Compatibility_1_6 = false; + doing = DoingConfigs; + } + else if(args[i] == "NO_CMAKE_BUILDS_PATH") + { + this->NoBuilds = true; + this->NoModule = true; + this->Compatibility_1_6 = false; + doing = DoingNone; + } + else if(this->CheckCommonArgument(args[i])) + { + this->NoModule = true; + this->Compatibility_1_6 = false; + doing = DoingNone; + } + else if(doing == DoingComponents) { // Set a variable telling the find script this component // is required. @@ -101,6 +307,22 @@ bool cmFindPackageCommand::InitialPass(std::vector const& args) components += args[i]; components_sep = ";"; } + else if(doing == DoingNames) + { + this->Names.push_back(args[i]); + } + else if(doing == DoingPaths) + { + this->AddUserPath(args[i]); + } + else if(doing == DoingPathSuffixes) + { + this->AddPathSuffix(args[i]); + } + else if(doing == DoingConfigs) + { + this->Configs.push_back(args[i]); + } else if(!haveVersion && version.find(args[i].c_str())) { haveVersion = true; @@ -118,48 +340,105 @@ bool cmFindPackageCommand::InitialPass(std::vector const& args) std::string components_var = Name + "_FIND_COMPONENTS"; this->Makefile->AddDefinition(components_var.c_str(), components.c_str()); - // See if there is a Find.cmake module. - if(!no_module) + // See if there is a Find.cmake module. + if(!this->NoModule) { bool foundModule = false; - if(!this->FindModule(foundModule, quiet, required)) + if(!this->FindModule(foundModule)) { - this->AppendSuccessInformation(quiet); + this->AppendSuccessInformation(); return false; } if(foundModule) { - this->AppendSuccessInformation(quiet); + this->AppendSuccessInformation(); return true; } } // No find module. Assume the project has a CMake config file. Use - // a _DIR cache variable to locate it. + // a _DIR cache variable to locate it. this->Variable = this->Name; this->Variable += "_DIR"; - this->Config = this->Name; - this->Config += "Config.cmake"; + // Add the default name. + if(this->Names.empty()) + { + this->Names.push_back(this->Name); + } + + // Add the default configs. + if(this->Configs.empty()) + { + for(std::vector::const_iterator ni = this->Names.begin(); + ni != this->Names.end(); ++ni) + { + std::string config = *ni; + config += "Config.cmake"; + this->Configs.push_back(config); + + config = cmSystemTools::LowerCase(*ni); + config += "-config.cmake"; + this->Configs.push_back(config); + } + } + + // Find and load the package. + bool result = this->HandlePackageMode(); + this->AppendSuccessInformation(); + return result; +} + +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::FindModule(bool& found) +{ + std::string module = "Find"; + module += this->Name; + module += ".cmake"; + std::string mfile = this->Makefile->GetModulesFile(module.c_str()); + if ( mfile.size() ) + { + if(this->Quiet) + { + // Tell the module that is about to be read that it should find + // quietly. + std::string quietly = this->Name; + quietly += "_FIND_QUIETLY"; + this->Makefile->AddDefinition(quietly.c_str(), "1"); + } + + if(this->Required) + { + // Tell the module that is about to be read that it should report + // a fatal error if the package is not found. + std::string req = this->Name; + req += "_FIND_REQUIRED"; + this->Makefile->AddDefinition(req.c_str(), "1"); + } + + // Load the module we found. + found = true; + return this->ReadListFile(mfile.c_str()); + } + return true; +} + +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::HandlePackageMode() +{ // Support old capitalization behavior. std::string upperDir = cmSystemTools::UpperCase(this->Name); std::string upperFound = cmSystemTools::UpperCase(this->Name); upperDir += "_DIR"; upperFound += "_FOUND"; - bool needCompatibility = false; - if(!(upperDir == this->Variable)) + if(upperDir == this->Variable) { - const char* versionValue = - this->Makefile->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY"); - if(atof(versionValue) < 1.7) - { - needCompatibility = true; - } + this->Compatibility_1_6 = false; } // Try to find the config file. const char* def = this->Makefile->GetDefinition(this->Variable.c_str()); - if(needCompatibility && cmSystemTools::IsOff(def)) + if(this->Compatibility_1_6 && cmSystemTools::IsOff(def)) { // Use the setting of the old name of the variable to provide the // value of the new. @@ -170,70 +449,104 @@ bool cmFindPackageCommand::InitialPass(std::vector const& args) def = this->Makefile->GetDefinition(this->Variable.c_str()); } } + + // Search for the config file if it is not already found. if(cmSystemTools::IsOff(def)) { - if(!this->FindConfig()) - { - this->AppendSuccessInformation(quiet); - return false; - } + this->FindConfig(); + def = this->Makefile->GetDefinition(this->Variable.c_str()); } // If the config file was found, load it. + std::string file; bool result = true; bool found = false; - def = this->Makefile->GetDefinition(this->Variable.c_str()); if(!cmSystemTools::IsOff(def)) { - std::string f = def; - cmSystemTools::ConvertToUnixSlashes(f); - f += "/"; - f += this->Config; - if(!cmSystemTools::FileIsFullPath(f.c_str())) + // Get the directory from the variable value. + std::string dir = def; + cmSystemTools::ConvertToUnixSlashes(dir); + + // Treat relative paths with respect to the current source dir. + if(!cmSystemTools::FileIsFullPath(dir.c_str())) { - f = "/" + f; - f = this->Makefile->GetCurrentDirectory() + f; + dir = "/" + dir; + dir = this->Makefile->GetCurrentDirectory() + dir; } - if(cmSystemTools::FileExists(f.c_str())) + // Find the configuration file. + if(this->FindConfigFile(dir, file)) { - if(this->ReadListFile(f.c_str())) + // Parse the configuration file. + if(this->ReadListFile(file.c_str())) { + // The package has been found. found = true; } else { + // The configuration file is invalid. result = false; } } else { + // The variable setting is wrong. cmOStringStream e; - e << this->Variable << " is set to \"" << def << "\", which is " - << "not a directory containing " << this->Config; - cmSystemTools::Error(e.str().c_str()); - if(required) - { - cmSystemTools::SetFatalErrorOccured(); - } - result = true; + e << "cannot find package " << this->Name << " because " + << this->Variable << " is set to \"" << def << "\" " + << "which is not a directory containing a package configuration " + << "file. " + << "Please set the cache entry " << this->Variable << " " + << "to the correct directory, or delete it to ask CMake " + << "to search."; + this->SetError(e.str().c_str()); + result = false; } } - else if(!quiet || required) + else if(!this->Quiet || this->Required) { + // The variable is not set. cmOStringStream e; - e << "FIND_PACKAGE could not find Find" << this->Name - << ".cmake nor config file " << this->Config << ".\n" - << "Adjust CMAKE_MODULE_PATH to find Find" << this->Name - << ".cmake or set " << this->Variable - << "\nto the directory containing " << this->Config - << " in order to use " << this->Name << "."; - cmSystemTools::Error(e.str().c_str()); - if(required) + e << "could not find "; + if(!this->NoModule) { - cmSystemTools::SetFatalErrorOccured(); + e << "module Find" << this->Name << ".cmake or "; + } + e << "a configuration file for package " << this->Name << ".\n"; + if(!this->NoModule) + { + e << "Adjust CMAKE_MODULE_PATH to find Find" + << this->Name << ".cmake or set "; + } + else + { + e << "Set "; + } + e << this->Variable << " to the directory containing a CMake " + << "configuration file for " << this->Name << ". "; + if(this->Configs.size() == 1) + { + e << "The file will be called " << this->Configs[0]; + } + else + { + e << "The file will have one of the following names:\n"; + for(std::vector::const_iterator ci = this->Configs.begin(); + ci != this->Configs.end(); ++ci) + { + e << " " << *ci << "\n"; + } + } + if(this->Required) + { + this->SetError(e.str().c_str()); + result = false; + } + else + { + cmSystemTools::Error("find_package ", e.str().c_str()); } - result = true; } // Set a variable marking whether the package was found. @@ -241,7 +554,20 @@ bool cmFindPackageCommand::InitialPass(std::vector const& args) foundVar += "_FOUND"; this->Makefile->AddDefinition(foundVar.c_str(), found? "1":"0"); - if(needCompatibility) + // Set a variable naming the configuration file that was found. + std::string fileVar = this->Name; + fileVar += "_CONFIG"; + if(found) + { + this->Makefile->AddDefinition(fileVar.c_str(), file.c_str()); + } + else + { + this->Makefile->RemoveDefinition(fileVar.c_str()); + } + + // Handle some ancient compatibility stuff. + if(this->Compatibility_1_6) { // Listfiles will be looking for the capitalized version of the // name. Provide it. @@ -256,7 +582,7 @@ bool cmFindPackageCommand::InitialPass(std::vector const& args) #ifdef CMAKE_BUILD_WITH_CMAKE if(!(upperDir == this->Variable)) { - if(needCompatibility) + if(this->Compatibility_1_6) { // Listfiles may use the capitalized version of the name. // Remove any previously added watch. @@ -277,155 +603,107 @@ bool cmFindPackageCommand::InitialPass(std::vector const& args) } #endif - this->AppendSuccessInformation(quiet); return result; } //---------------------------------------------------------------------------- -bool cmFindPackageCommand::FindModule(bool& found, bool quiet, bool required) +void cmFindPackageCommand::FindConfig() { - std::string module = "Find"; - module += this->Name; - module += ".cmake"; - std::string mfile = this->Makefile->GetModulesFile(module.c_str()); - if ( mfile.size() ) - { - if(quiet) - { - // Tell the module that is about to be read that it should find - // quietly. - std::string quietly = this->Name; - quietly += "_FIND_QUIETLY"; - this->Makefile->AddDefinition(quietly.c_str(), "1"); - } - - if(required) - { - // Tell the module that is about to be read that it should report - // a fatal error if the package is not found. - std::string req = this->Name; - req += "_FIND_REQUIRED"; - this->Makefile->AddDefinition(req.c_str(), "1"); - } - - // Load the module we found. - found = true; - return this->ReadListFile(mfile.c_str()); - } - return true; -} - -//---------------------------------------------------------------------------- -bool cmFindPackageCommand::FindConfig() -{ - std::string help = "The directory containing "; - help += this->Config; - help += "."; - - // Construct the list of relative paths to each prefix to be - // searched. - std::string rel = "/lib/"; - rel += cmSystemTools::LowerCase(this->Name); - this->Relatives.push_back(rel); - rel = "/lib/"; - rel += this->Name; - this->Relatives.push_back(rel); - - // It is likely that CMake will have recently built the project. - for(int i=1; i <= 10; ++i) - { - cmOStringStream r; - r << "[HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\" - "Settings\\StartPath;WhereBuild" << i << "]"; - std::string entry = r.str(); - cmSystemTools::ExpandRegistryValues(entry); - cmSystemTools::ConvertToUnixSlashes(entry); - if(cmSystemTools::FileIsDirectory(entry.c_str())) - { - this->Builds.push_back(entry); - } - } - - // The project may be installed. Use the system search path to - // construct a list of possible install prefixes. - std::vector systemPath; - cmSystemTools::GetPath(systemPath); - for(std::vector::iterator i = systemPath.begin(); - i != systemPath.end(); ++i) - { - *i += "/.."; - if(cmSystemTools::FileIsDirectory(i->c_str())) - { - this->Prefixes.push_back(cmSystemTools::CollapseFullPath(i->c_str())); - } - } -#if !defined(WIN32) || defined(__CYGWIN__) - this->Prefixes.push_back("/usr/local"); - this->Prefixes.push_back("/usr"); -#endif + // Compute the set of search prefixes. + this->ComputePrefixes(); // Look for the project's configuration file. - std::string init = this->SearchForConfig(); + bool found = false; + + // Search for frameworks. + if(!found && this->SearchFrameworkFirst || this->SearchFrameworkOnly) + { + found = this->FindFrameworkConfig(); + } + + // Search for apps. + if(!found && this->SearchAppBundleFirst || this->SearchAppBundleOnly) + { + found = this->FindAppBundleConfig(); + } + + // Search prefixes. + if(!found && !(this->SearchFrameworkOnly || this->SearchAppBundleOnly)) + { + found = this->FindPrefixedConfig(); + } + + // Search for frameworks. + if(!found && this->SearchFrameworkLast) + { + found = this->FindFrameworkConfig(); + } + + // Search for apps. + if(!found && this->SearchAppBundleLast) + { + found = this->FindAppBundleConfig(); + } // Store the entry in the cache so it can be set by the user. + std::string init; + if(found) + { + init = cmSystemTools::GetFilenamePath(this->FileFound); + } + else + { + init = this->Variable + "-NOTFOUND"; + } + std::string help = + "The directory containing a CMake configuration file for "; + help += this->Name; + help += "."; this->Makefile->AddCacheDefinition(this->Variable.c_str(), - init.c_str(), - help.c_str(), - cmCacheManager::PATH); - return true; + init.c_str(), help.c_str(), + cmCacheManager::PATH); } //---------------------------------------------------------------------------- -std::string cmFindPackageCommand::SearchForConfig() const +bool cmFindPackageCommand::FindPrefixedConfig() { - // Check the environment variable. - std::string env; - if(cmSystemTools::GetEnv(this->Variable.c_str(), env) && env.length() > 0) + for(std::vector::const_iterator pi = this->Prefixes.begin(); + pi != this->Prefixes.end(); ++pi) { - cmSystemTools::ConvertToUnixSlashes(env); - std::string f = env; - f += "/"; - f += this->Config; - if(cmSystemTools::FileExists(f.c_str())) + if(this->SearchPrefix(*pi)) { - return env; + return true; } } + return false; +} - // Search the build directories. - for(std::vector::const_iterator b = this->Builds.begin(); - b != this->Builds.end(); ++b) +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::FindFrameworkConfig() +{ + for(std::vector::const_iterator i = this->Prefixes.begin(); + i != this->Prefixes.end(); ++i) { - std::string f = *b; - f += "/"; - f += this->Config; - if(cmSystemTools::FileExists(f.c_str())) + if(this->SearchFrameworkPrefix(*i)) { - return *b; + return true; } } + return false; +} - // Search paths relative to each installation prefix. - for(std::vector::const_iterator p = this->Prefixes.begin(); - p != this->Prefixes.end(); ++p) +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::FindAppBundleConfig() +{ + for(std::vector::const_iterator i = this->Prefixes.begin(); + i != this->Prefixes.end(); ++i) { - std::string prefix = *p; - for(std::vector::const_iterator r = this->Relatives.begin(); - r != this->Relatives.end(); ++r) + if(this->SearchAppBundlePrefix(*i)) { - std::string dir = prefix; - dir += *r; - std::string f = dir; - f += "/"; - f += this->Config; - if(cmSystemTools::FileExists(f.c_str())) - { - return dir; - } + return true; } } - - return this->Variable + "-NOTFOUND"; + return false; } //---------------------------------------------------------------------------- @@ -480,7 +758,7 @@ void cmFindPackageCommand::AppendToProperty(const char* propertyName) } //---------------------------------------------------------------------------- -void cmFindPackageCommand::AppendSuccessInformation(bool quiet) +void cmFindPackageCommand::AppendSuccessInformation() { std::string found = this->Name; found += "_FOUND"; @@ -491,7 +769,7 @@ void cmFindPackageCommand::AppendSuccessInformation(bool quiet) if ((cmSystemTools::IsOn(result)) || (cmSystemTools::IsOn(upperResult))) { this->AppendToProperty("PACKAGES_FOUND"); - if (!quiet) + if (!this->Quiet) { this->AppendToProperty("ENABLED_FEATURES"); } @@ -499,9 +777,725 @@ void cmFindPackageCommand::AppendSuccessInformation(bool quiet) else { this->AppendToProperty("PACKAGES_NOT_FOUND"); - if (!quiet) + if (!this->Quiet) { this->AppendToProperty("DISABLED_FEATURES"); } } } + +//---------------------------------------------------------------------------- +void cmFindPackageCommand::AddUserPath(std::string const& p) +{ + std::string userPath = p; + cmSystemTools::ExpandRegistryValues(userPath); + this->UserPaths.push_back(userPath); +} + +//---------------------------------------------------------------------------- +void cmFindPackageCommand::ComputePrefixes() +{ + std::vector& prefixes = this->Prefixes; + std::set emmitted; + + if(!this->NoCMakeEnvironmentPath && !this->NoDefaultPath) + { + // Check the environment variable with the same name as the cache + // entry. + std::string env; + if(cmSystemTools::GetEnv(this->Variable.c_str(), env) && env.length() > 0) + { + cmSystemTools::ConvertToUnixSlashes(env); + this->AddPathInternal(prefixes, env, EnvPath, &emmitted); + } + + this->AddEnvPath(prefixes, "CMAKE_PREFIX_PATH", &emmitted); + this->AddEnvPath(prefixes, "CMAKE_FRAMEWORK_PATH", &emmitted); + this->AddEnvPath(prefixes, "CMAKE_APPBUNDLE_PATH", &emmitted); + } + + if(!this->NoCMakePath && !this->NoDefaultPath) + { + this->AddCMakePath(prefixes, "CMAKE_PREFIX_PATH", &emmitted); + this->AddCMakePath(prefixes, "CMAKE_FRAMEWORK_PATH", &emmitted); + this->AddCMakePath(prefixes, "CMAKE_APPBUNDLE_PATH", &emmitted); + } + + if(!this->NoSystemEnvironmentPath && !this->NoDefaultPath) + { + // Use the system search path to generate prefixes. + // Relative paths are interpreted with respect to the current + // working directory. + std::vector tmp; + cmSystemTools::GetPath(tmp); + for(std::vector::iterator i = tmp.begin(); + i != tmp.end(); ++i) + { + std::string const& d = *i; + + // If the path is a PREFIX/bin case then add its parent instead. + if(d.size() >= 4 && strcmp(d.c_str()+d.size()-4, "/bin") == 0 || + d.size() >= 5 && strcmp(d.c_str()+d.size()-5, "/sbin") == 0) + { + this->AddPathInternal(prefixes, + cmSystemTools::GetFilenamePath(d), + EnvPath, &emmitted); + } + else + { + this->AddPathInternal(prefixes, d, EnvPath, &emmitted); + } + } + } + + if(!this->NoBuilds && !this->NoDefaultPath) + { + // It is likely that CMake will have recently built the project. + for(int i=1; i <= 10; ++i) + { + cmOStringStream r; + r << + "[HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\" + "Settings\\StartPath;WhereBuild" << i << "]"; + std::string f = r.str(); + cmSystemTools::ExpandRegistryValues(f); + cmSystemTools::ConvertToUnixSlashes(f); + if(cmSystemTools::FileIsFullPath(f.c_str()) && + cmSystemTools::FileIsDirectory(f.c_str())) + { + this->AddPathInternal(prefixes, f, FullPath, &emmitted); + } + } + } + + if(!this->NoCMakeSystemPath && !this->NoDefaultPath) + { + this->AddCMakePath(prefixes, "CMAKE_SYSTEM_PREFIX_PATH", &emmitted); + this->AddCMakePath(prefixes, "CMAKE_SYSTEM_FRAMEWORK_PATH", &emmitted); + this->AddCMakePath(prefixes, "CMAKE_SYSTEM_APPBUNDLE_PATH", &emmitted); + } + + if(!this->UserPaths.empty()) + { + // Add paths specified by the caller. + this->AddPathsInternal(prefixes, this->UserPaths, CMakePath, &emmitted); + } + + // Construct the final set of prefixes. + this->RerootPaths(prefixes); + + // Add a trailing slash to all prefixes to aid the search process. + for(std::vector::iterator i = prefixes.begin(); + i != prefixes.end(); ++i) + { + std::string& prefix = *i; + if(prefix[prefix.size()-1] != '/') + { + prefix += "/"; + } + } +} + +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::SearchDirectory(std::string const& dir) +{ + assert(!dir.empty() && dir[dir.size()-1] == '/'); + + // Check each path suffix on this directory. + for(std::vector::const_iterator + si = this->SearchPathSuffixes.begin(); + si != this->SearchPathSuffixes.end(); ++si) + { + std::string d = dir; + if(!si->empty()) + { + d += *si; + d += "/"; + } + if(this->CheckDirectory(d)) + { + return true; + } + } + return false; +} + +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::CheckDirectory(std::string const& dir) +{ + assert(!dir.empty() && dir[dir.size()-1] == '/'); + + // Look for the file in this directory. + std::string d = dir.substr(0, dir.size()-1); + if(this->FindConfigFile(d, this->FileFound)) + { + return true; + } + return false; +} + +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::FindConfigFile(std::string const& dir, + std::string& file) +{ + for(std::vector::const_iterator ci = this->Configs.begin(); + ci != this->Configs.end(); ++ci) + { + file = dir; + file += "/"; + file += *ci; + if(this->DebugMode) + { + fprintf(stderr, "Checking file [%s]\n", file.c_str()); + } + if(cmSystemTools::FileExists(file.c_str(), true)) + { + return true; + } + } + return false; +} + +//---------------------------------------------------------------------------- +#include +#include +#include +#include + +class cmFileList; +class cmFileListGeneratorBase +{ +protected: + bool Consider(std::string const& fullPath, cmFileList& listing); +private: + bool Search(cmFileList&); + virtual bool Search(std::string const& parent, cmFileList&) = 0; + virtual cmsys::auto_ptr Clone() const = 0; + friend class cmFileList; + cmFileListGeneratorBase* SetNext(cmFileListGeneratorBase const& next); + cmsys::auto_ptr Next; +}; + +class cmFileList +{ +public: + cmFileList(): First(), Last(0) {} + cmFileList& operator/(cmFileListGeneratorBase const& rhs) + { + if(this->Last) + { + this->Last = this->Last->SetNext(rhs); + } + else + { + this->First = rhs.Clone(); + this->Last = this->First.get(); + } + return *this; + } + bool Search() + { + if(this->First.get()) + { + return this->First->Search(*this); + } + return false; + } +private: + virtual bool Visit(std::string const& fullPath) = 0; + friend class cmFileListGeneratorBase; + cmsys::auto_ptr First; + cmFileListGeneratorBase* Last; +}; + +class cmFindPackageFileList: public cmFileList +{ +public: + cmFindPackageFileList(cmFindPackageCommand* fpc, + bool use_suffixes = true): + cmFileList(), FPC(fpc), UseSuffixes(use_suffixes) {} +private: + bool Visit(std::string const& fullPath) + { + if(this->UseSuffixes) + { + return this->FPC->SearchDirectory(fullPath); + } + else + { + return this->FPC->CheckDirectory(fullPath); + } + } + cmFindPackageCommand* FPC; + bool UseSuffixes; +}; + +bool cmFileListGeneratorBase::Search(cmFileList& listing) +{ + return this->Search("", listing); +} + +cmFileListGeneratorBase* +cmFileListGeneratorBase::SetNext(cmFileListGeneratorBase const& next) +{ + this->Next = next.Clone(); + return this->Next.get(); +} + +bool cmFileListGeneratorBase::Consider(std::string const& fullPath, + cmFileList& listing) +{ + if(this->Next.get()) + { + return this->Next->Search(fullPath + "/", listing); + } + else + { + return listing.Visit(fullPath + "/"); + } +} + +class cmFileListGeneratorFixed: public cmFileListGeneratorBase +{ +public: + cmFileListGeneratorFixed(std::string const& str): + cmFileListGeneratorBase(), String(str) {} + cmFileListGeneratorFixed(cmFileListGeneratorFixed const& r): + cmFileListGeneratorBase(), String(r.String) {} +private: + std::string String; + virtual bool Search(std::string const& parent, cmFileList& lister) + { + std::string fullPath = parent + this->String; + return this->Consider(fullPath, lister); + } + virtual cmsys::auto_ptr Clone() const + { + cmsys::auto_ptr + g(new cmFileListGeneratorFixed(*this)); + return g; + } +}; + +class cmFileListGeneratorEnumerate: public cmFileListGeneratorBase +{ +public: + cmFileListGeneratorEnumerate(const char* p1, const char* p2): + cmFileListGeneratorBase() + { + this->Vector.push_back(p1); + this->Vector.push_back(p2); + } + cmFileListGeneratorEnumerate(cmFileListGeneratorEnumerate const& r): + cmFileListGeneratorBase(), Vector(r.Vector) {} +private: + std::vector Vector; + virtual bool Search(std::string const& parent, cmFileList& lister) + { + for(std::vector::const_iterator i = this->Vector.begin(); + i != this->Vector.end(); ++i) + { + if(this->Consider(parent + *i, lister)) + { + return true; + } + } + return false; + } + virtual cmsys::auto_ptr Clone() const + { + cmsys::auto_ptr + g(new cmFileListGeneratorEnumerate(*this)); + return g; + } +}; + +class cmFileListGeneratorProject: public cmFileListGeneratorBase +{ +public: + cmFileListGeneratorProject(std::vector const& names): + cmFileListGeneratorBase(), Names(names) {} + cmFileListGeneratorProject(cmFileListGeneratorProject const& r): + cmFileListGeneratorBase(), Names(r.Names) {} +private: + std::vector const& Names; + virtual bool Search(std::string const& parent, cmFileList& lister) + { + // Construct a list of matches. + std::vector matches; + cmsys::Directory d; + d.Load(parent.c_str()); + for(unsigned long i=0; i < d.GetNumberOfFiles(); ++i) + { + const char* fname = d.GetFile(i); + if(strcmp(fname, ".") == 0 || + strcmp(fname, "..") == 0) + { + continue; + } + for(std::vector::const_iterator ni = this->Names.begin(); + ni != this->Names.end(); ++ni) + { + if(cmsysString_strncasecmp(fname, ni->c_str(), + ni->length()) == 0) + { + matches.push_back(fname); + } + } + } + + for(std::vector::const_iterator i = matches.begin(); + i != matches.end(); ++i) + { + if(this->Consider(parent + *i, lister)) + { + return true; + } + } + return false; + } + virtual cmsys::auto_ptr Clone() const + { + cmsys::auto_ptr + g(new cmFileListGeneratorProject(*this)); + return g; + } +}; + +class cmFileListGeneratorMacProject: public cmFileListGeneratorBase +{ +public: + cmFileListGeneratorMacProject(std::vector const& names, + const char* ext): + cmFileListGeneratorBase(), Names(names), Extension(ext) {} + cmFileListGeneratorMacProject(cmFileListGeneratorMacProject const& r): + cmFileListGeneratorBase(), Names(r.Names), Extension(r.Extension) {} +private: + std::vector const& Names; + std::string Extension; + virtual bool Search(std::string const& parent, cmFileList& lister) + { + // Construct a list of matches. + std::vector matches; + cmsys::Directory d; + d.Load(parent.c_str()); + for(unsigned long i=0; i < d.GetNumberOfFiles(); ++i) + { + const char* fname = d.GetFile(i); + if(strcmp(fname, ".") == 0 || + strcmp(fname, "..") == 0) + { + continue; + } + for(std::vector::const_iterator ni = this->Names.begin(); + ni != this->Names.end(); ++ni) + { + std::string name = *ni; + name += this->Extension; + if(cmsysString_strcasecmp(fname, name.c_str()) == 0) + { + matches.push_back(fname); + } + } + } + + for(std::vector::const_iterator i = matches.begin(); + i != matches.end(); ++i) + { + if(this->Consider(parent + *i, lister)) + { + return true; + } + } + return false; + } + virtual cmsys::auto_ptr Clone() const + { + cmsys::auto_ptr + g(new cmFileListGeneratorMacProject(*this)); + return g; + } +}; + +class cmFileListGeneratorCaseInsensitive: public cmFileListGeneratorBase +{ +public: + cmFileListGeneratorCaseInsensitive(std::string const& str): + cmFileListGeneratorBase(), String(str) {} + cmFileListGeneratorCaseInsensitive(cmFileListGeneratorCaseInsensitive const& r): + cmFileListGeneratorBase(), String(r.String) {} +private: + std::string String; + virtual bool Search(std::string const& parent, cmFileList& lister) + { + // Look for matching files. + std::vector matches; + cmsys::Directory d; + d.Load(parent.c_str()); + for(unsigned long i=0; i < d.GetNumberOfFiles(); ++i) + { + const char* fname = d.GetFile(i); + if(strcmp(fname, ".") == 0 || + strcmp(fname, "..") == 0) + { + continue; + } + if(cmsysString_strcasecmp(fname, this->String.c_str()) == 0) + { + if(this->Consider(parent + fname, lister)) + { + return true; + } + } + } + return false; + } + virtual cmsys::auto_ptr Clone() const + { + cmsys::auto_ptr + g(new cmFileListGeneratorCaseInsensitive(*this)); + return g; + } +}; + +class cmFileListGeneratorGlob: public cmFileListGeneratorBase +{ +public: + cmFileListGeneratorGlob(std::string const& str): + cmFileListGeneratorBase(), Pattern(str) {} + cmFileListGeneratorGlob(cmFileListGeneratorGlob const& r): + cmFileListGeneratorBase(), Pattern(r.Pattern) {} +private: + std::string Pattern; + virtual bool Search(std::string const& parent, cmFileList& lister) + { + // Glob the set of matching files. + std::string expr = parent; + expr += this->Pattern; + cmsys::Glob g; + if(!g.FindFiles(expr)) + { + return false; + } + std::vector const& files = g.GetFiles(); + + // Look for directories among the matches. + for(std::vector::const_iterator fi = files.begin(); + fi != files.end(); ++fi) + { + if(cmSystemTools::FileIsDirectory(fi->c_str())) + { + if(this->Consider(*fi, lister)) + { + return true; + } + } + } + return false; + } + virtual cmsys::auto_ptr Clone() const + { + cmsys::auto_ptr + g(new cmFileListGeneratorGlob(*this)); + return g; + } +}; + +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in) +{ + assert(!prefix_in.empty() && prefix_in[prefix_in.size()-1] == '/'); + if(this->DebugMode) + { + fprintf(stderr, "Checking prefix [%s]\n", prefix_in.c_str()); + } + + // Skip this if the prefix does not exist. + if(!cmSystemTools::FileIsDirectory(prefix_in.c_str())) + { + return false; + } + + // PREFIX/ (useful on windows or in build trees) + if(this->SearchDirectory(prefix_in)) + { + return true; + } + + // Strip the trailing slash because the path generator is about to + // add one. + std::string prefix = prefix_in.substr(0, prefix_in.size()-1); + + // PREFIX/(cmake|CMake)/ (useful on windows or in build trees) + { + cmFindPackageFileList lister(this); + lister + / cmFileListGeneratorFixed(prefix) + / cmFileListGeneratorCaseInsensitive("cmake"); + if(lister.Search()) + { + return true; + } + } + + // PREFIX/(share|lib)/(Foo|foo|FOO).*/ + { + cmFindPackageFileList lister(this); + lister + / cmFileListGeneratorFixed(prefix) + / cmFileListGeneratorEnumerate("lib", "share") + / cmFileListGeneratorProject(this->Names); + if(lister.Search()) + { + return true; + } + } + + // PREFIX/(share|lib)/(Foo|foo|FOO).*/(cmake|CMake)/ + { + cmFindPackageFileList lister(this); + lister + / cmFileListGeneratorFixed(prefix) + / cmFileListGeneratorEnumerate("lib", "share") + / cmFileListGeneratorProject(this->Names) + / cmFileListGeneratorCaseInsensitive("cmake"); + if(lister.Search()) + { + return true; + } + } + + return false; +} + +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in) +{ + assert(!prefix_in.empty() && prefix_in[prefix_in.size()-1] == '/'); + if(this->DebugMode) + { + fprintf(stderr, "Checking framework prefix [%s]\n", prefix_in.c_str()); + } + + // Strip the trailing slash because the path generator is about to + // add one. + std::string prefix = prefix_in.substr(0, prefix_in.size()-1); + + // /Foo.framework/Resources/ + { + cmFindPackageFileList lister(this); + lister + / cmFileListGeneratorFixed(prefix) + / cmFileListGeneratorMacProject(this->Names, ".framework") + / cmFileListGeneratorFixed("Resources"); + if(lister.Search()) + { + return true; + } + } + // /Foo.framework/Resources/CMake/ + { + cmFindPackageFileList lister(this); + lister + / cmFileListGeneratorFixed(prefix) + / cmFileListGeneratorMacProject(this->Names, ".framework") + / cmFileListGeneratorFixed("Resources") + / cmFileListGeneratorCaseInsensitive("cmake"); + if(lister.Search()) + { + return true; + } + } + + // /Foo.framework/Versions/*/Resources/ + { + cmFindPackageFileList lister(this); + lister + / cmFileListGeneratorFixed(prefix) + / cmFileListGeneratorMacProject(this->Names, ".framework") + / cmFileListGeneratorFixed("Versions") + / cmFileListGeneratorGlob("*/Resources"); + if(lister.Search()) + { + return true; + } + } + + // /Foo.framework/Versions/*/Resources/CMake/ + { + cmFindPackageFileList lister(this); + lister + / cmFileListGeneratorFixed(prefix) + / cmFileListGeneratorMacProject(this->Names, ".framework") + / cmFileListGeneratorFixed("Versions") + / cmFileListGeneratorGlob("*/Resources") + / cmFileListGeneratorCaseInsensitive("cmake"); + if(lister.Search()) + { + return true; + } + } + + return false; +} + +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in) +{ + assert(!prefix_in.empty() && prefix_in[prefix_in.size()-1] == '/'); + if(this->DebugMode) + { + fprintf(stderr, "Checking bundle prefix [%s]\n", prefix_in.c_str()); + } + + // Strip the trailing slash because the path generator is about to + // add one. + std::string prefix = prefix_in.substr(0, prefix_in.size()-1); + + // /Foo.app/Contents/Resources + { + cmFindPackageFileList lister(this); + lister + / cmFileListGeneratorFixed(prefix) + / cmFileListGeneratorMacProject(this->Names, ".app") + / cmFileListGeneratorFixed("Contents/Resources"); + if(lister.Search()) + { + return true; + } + } + + // /Foo.app/Contents/Resources/CMake + { + cmFindPackageFileList lister(this); + lister + / cmFileListGeneratorFixed(prefix) + / cmFileListGeneratorMacProject(this->Names, ".app") + / cmFileListGeneratorFixed("Contents/Resources") + / cmFileListGeneratorCaseInsensitive("cmake"); + if(lister.Search()) + { + return true; + } + } + + return false; +} + +// TODO: Version numbers? Perhaps have a listing component class that +// sorts by lexicographic and numerical ordering. Also try to match +// some command argument for the version. Alternatively provide an +// API that just returns a list of valid directories? Perhaps push a +// scope and try loading the target file just to get its version +// number? Could add a foo-version.cmake or FooVersion.cmake file +// in the projects that contains just version information. + +// TODO: Debug cmsys::Glob double slash problem. + +// TODO: Add registry entries after cmake system search path? +// Currently the user must specify them with the PATHS option. +// +// [HKEY_CURRENT_USER\Software\*\Foo*;InstallDir] +// [HKEY_CURRENT_USER\Software\*\*\Foo*;InstallDir] +// [HKEY_LOCAL_MACHINE\Software\*\Foo*;InstallDir] +// [HKEY_LOCAL_MACHINE\Software\*\*\Foo*;InstallDir] diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index 26c609075..af4104856 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -17,16 +17,20 @@ #ifndef cmFindPackageCommand_h #define cmFindPackageCommand_h -#include "cmCommand.h" +#include "cmFindCommon.h" + +class cmFindPackageFileList; /** \class cmFindPackageCommand * \brief Load settings from an external project. * * cmFindPackageCommand */ -class cmFindPackageCommand : public cmCommand +class cmFindPackageCommand : public cmFindCommon { public: + cmFindPackageCommand(); + /** * This is a virtual constructor for the command. */ @@ -62,51 +66,45 @@ public: /** * More documentation. */ - virtual const char* GetFullDocumentation() - { - return - " find_package( [major.minor] [QUIET] [NO_MODULE]\n" - " [[REQUIRED|COMPONENTS] [components...]])\n" - "Finds and loads settings from an external project. _FOUND will " - "be set to indicate whether the package was found. Settings that " - "can be used when _FOUND is true are package-specific. The " - "package is found through several steps. " - "Directories listed in CMAKE_MODULE_PATH are searched for files called " - "\"Find.cmake\". If such a file is found, it is read and " - "processed by CMake, and is responsible for finding the package. " - "This first step may be skipped by using the NO_MODULE option. " - "If no such file is found, it is expected that the package is another " - "project built by CMake that has a \"Config.cmake\" file. " - "A cache entry called _DIR is created and is expected to be set " - "to the directory containing this file. If the file is found, it is " - "read and processed by CMake to load the settings of the package. If " - "_DIR has not been set during a configure step, the command " - "will generate an error describing the problem unless the QUIET " - "argument is specified. If _DIR has been set to a directory " - "not containing a \"Config.cmake\" file, an error is always " - "generated. If REQUIRED is specified and the package is not found, " - "a FATAL_ERROR is generated and the configure step stops executing. " - "A package-specific list of components may be listed after the " - "REQUIRED option, or after the COMPONENTS option if no REQUIRED " - "option is given."; - } + virtual const char* GetFullDocumentation(); - cmTypeMacro(cmFindPackageCommand, cmCommand); + cmTypeMacro(cmFindPackageCommand, cmFindCommon); private: - void AppendSuccessInformation(bool quiet); + void AppendSuccessInformation(); void AppendToProperty(const char* propertyName); - bool FindModule(bool& found, bool quiet, bool required); - bool FindConfig(); - std::string SearchForConfig() const; + bool FindModule(bool& found); + bool HandlePackageMode(); + void FindConfig(); + bool FindPrefixedConfig(); + bool FindFrameworkConfig(); + bool FindAppBundleConfig(); bool ReadListFile(const char* f); + void AddUserPath(std::string const& p); + void ComputePrefixes(); + bool SearchDirectory(std::string const& dir); + bool CheckDirectory(std::string const& dir); + bool FindConfigFile(std::string const& dir, std::string& file); + bool SearchPrefix(std::string const& prefix); + bool SearchFrameworkPrefix(std::string const& prefix_in); + bool SearchAppBundlePrefix(std::string const& prefix_in); + + friend class cmFindPackageFileList; + + std::string CommandDocumentation; cmStdString Name; cmStdString Variable; - cmStdString Config; - std::vector Builds; - std::vector Prefixes; - std::vector Relatives; + cmStdString FileFound; + bool Quiet; + bool Required; + bool Compatibility_1_6; + bool NoModule; + bool NoBuilds; + bool DebugMode; + std::vector Names; + std::vector Configs; + std::vector Prefixes; + std::vector UserPaths; }; - #endif diff --git a/Source/cmFindPathCommand.cxx b/Source/cmFindPathCommand.cxx index 00c08ec2b..f322b43f4 100644 --- a/Source/cmFindPathCommand.cxx +++ b/Source/cmFindPathCommand.cxx @@ -27,6 +27,12 @@ cmFindPathCommand::cmFindPathCommand() "FIND_XXX", "find_path"); cmSystemTools::ReplaceString(this->GenericDocumentation, "CMAKE_XXX_PATH", "CMAKE_INCLUDE_PATH"); + cmSystemTools::ReplaceString(this->GenericDocumentation, + "CMAKE_XXX_MAC_PATH", + "CMAKE_FRAMEWORK_PATH"); + cmSystemTools::ReplaceString(this->GenericDocumentation, + "CMAKE_SYSTEM_XXX_MAC_PATH", + "CMAKE_SYSTEM_FRAMEWORK_PATH"); cmSystemTools::ReplaceString(this->GenericDocumentation, "XXX_SYSTEM", "INCLUDE"); cmSystemTools::ReplaceString(this->GenericDocumentation, diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx index fb563e285..318dc134b 100644 --- a/Source/cmFindProgramCommand.cxx +++ b/Source/cmFindProgramCommand.cxx @@ -28,6 +28,12 @@ cmFindProgramCommand::cmFindProgramCommand() "FIND_XXX", "find_program"); cmSystemTools::ReplaceString(this->GenericDocumentation, "CMAKE_XXX_PATH", "CMAKE_PROGRAM_PATH"); + cmSystemTools::ReplaceString(this->GenericDocumentation, + "CMAKE_XXX_MAC_PATH", + "CMAKE_APPBUNDLE_PATH"); + cmSystemTools::ReplaceString(this->GenericDocumentation, + "CMAKE_SYSTEM_XXX_MAC_PATH", + "CMAKE_SYSTEM_APPBUNDLE_PATH"); cmSystemTools::ReplaceString(this->GenericDocumentation, "XXX_SYSTEM", ""); cmSystemTools::ReplaceString(this->GenericDocumentation,