From ed0650f6ae10911092adc25373b9c61724192124 Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 1 Sep 2009 14:04:27 -0400 Subject: Teach find_package to search a "package registry" A common user workflow is to build a series of dependent projects in order. Each project locates its dependencies with find_package. We introduce a "user package registry" to help find_package locate packages built in non-standard search locations. The registry explicitly stores locations of build trees providing instances of a given package. There is no defined order among the locations specified. These locations should provide package configuration files (-config.cmake) and package version files (-config-version.cmake) so that find_package will recognize the packages and test version numbers. --- Source/cmFindPackageCommand.cxx | 189 +++++++++++++++++++++++++++++++++++++++- Source/cmFindPackageCommand.h | 5 ++ 2 files changed, 191 insertions(+), 3 deletions(-) diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index 9bc1df0..9ac73f8 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -15,6 +15,8 @@ =========================================================================*/ #include "cmFindPackageCommand.h" + +#include #include #ifdef CMAKE_BUILD_WITH_CMAKE @@ -60,6 +62,7 @@ cmFindPackageCommand::cmFindPackageCommand() this->CMakePathName = "PACKAGE"; this->Quiet = false; this->Required = false; + this->NoRegistry = false; this->NoBuilds = false; this->NoModule = false; this->DebugMode = false; @@ -128,6 +131,7 @@ cmFindPackageCommand::cmFindPackageCommand() " [NO_CMAKE_ENVIRONMENT_PATH]\n" " [NO_CMAKE_PATH]\n" " [NO_SYSTEM_ENVIRONMENT_PATH]\n" + " [NO_CMAKE_PACKAGE_REGISTRY]\n" " [NO_CMAKE_BUILDS_PATH]\n" " [NO_CMAKE_SYSTEM_PATH]\n" " [CMAKE_FIND_ROOT_PATH_BOTH |\n" @@ -281,13 +285,19 @@ cmFindPackageCommand::cmFindPackageCommand() "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" - "6. Search cmake variables defined in the Platform files " + "6. Search paths stored in the CMake user package registry. " + "This can be skipped if NO_CMAKE_PACKAGE_REGISTRY is passed. " + "Paths are stored in the registry when CMake configures a project " + "that invokes export(PACKAGE ). " + "See the export(PACKAGE) command documentation for more details." + "\n" + "7. 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" - "7. Search paths specified by the PATHS option. " + "8. Search paths specified by the PATHS option. " "These are typically hard-coded guesses.\n" ; this->CommandDocumentation += this->GenericDocumentationMacPolicy; @@ -420,6 +430,13 @@ bool cmFindPackageCommand this->Compatibility_1_6 = false; doing = DoingNone; } + else if(args[i] == "NO_CMAKE_PACKAGE_REGISTRY") + { + this->NoRegistry = true; + this->NoModule = true; + this->Compatibility_1_6 = false; + doing = DoingNone; + } else if(args[i] == "NO_CMAKE_BUILDS_PATH") { this->NoBuilds = true; @@ -1065,6 +1082,7 @@ void cmFindPackageCommand::ComputePrefixes() this->AddPrefixesCMakeEnvironment(); this->AddPrefixesUserHints(); this->AddPrefixesSystemEnvironment(); + this->AddPrefixesRegistry(); this->AddPrefixesBuilds(); this->AddPrefixesCMakeSystemVariable(); this->AddPrefixesUserGuess(); @@ -1132,6 +1150,172 @@ void cmFindPackageCommand::AddPrefixesSystemEnvironment() } //---------------------------------------------------------------------------- +void cmFindPackageCommand::AddPrefixesRegistry() +{ + if(this->NoRegistry || this->NoDefaultPath) + { + return; + } + +#if defined(_WIN32) && !defined(__CYGWIN__) + this->LoadPackageRegistryWin(); +#else + if(const char* home = cmSystemTools::GetEnv("HOME")) + { + std::string dir = home; + dir += "/.cmake/packages/"; + dir += this->Name; + this->LoadPackageRegistryDir(dir); + } +#endif +} + +#if defined(_WIN32) && !defined(__CYGWIN__) +# include +# undef GetCurrentDirectory +//---------------------------------------------------------------------------- +void cmFindPackageCommand::LoadPackageRegistryWin() +{ + std::string key = "Software\\Kitware\\CMake\\Packages\\"; + key += this->Name; + std::set bad; + HKEY hKey; + if(RegOpenKeyEx(HKEY_CURRENT_USER, key.c_str(), + 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) + { + DWORD valueType = REG_NONE; + char name[16384]; + std::vector data(512); + bool done = false; + DWORD index = 0; + while(!done) + { + DWORD nameSize = static_cast(sizeof(name)); + DWORD dataSize = static_cast(data.size()-1); + switch(RegEnumValue(hKey, index, name, &nameSize, + 0, &valueType, (BYTE*)&data[0], &dataSize)) + { + case ERROR_SUCCESS: + ++index; + if(valueType == REG_SZ) + { + data[dataSize] = 0; + cmsys_ios::stringstream ss(&data[0]); + if(this->CheckPackageRegistryEntry(ss)) + { + // The entry is okay. + continue; + } + } + bad.insert(name); + break; + case ERROR_MORE_DATA: + data.resize(dataSize+1); + break; + case ERROR_NO_MORE_ITEMS: default: done = true; break; + } + } + RegCloseKey(hKey); + } + + // Remove bad values if possible. + if(!bad.empty() && + RegOpenKeyEx(HKEY_CURRENT_USER, key.c_str(), + 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) + { + for(std::set::const_iterator vi = bad.begin(); + vi != bad.end(); ++vi) + { + RegDeleteValue(hKey, vi->c_str()); + } + RegCloseKey(hKey); + } +} +#else +//---------------------------------------------------------------------------- +class cmFindPackageCommandHoldFile +{ + const char* File; +public: + cmFindPackageCommandHoldFile(const char* f): File(f) {} + ~cmFindPackageCommandHoldFile() + { if(this->File) { cmSystemTools::RemoveFile(this->File); } } + void Release() { this->File = 0; } +}; + +//---------------------------------------------------------------------------- +void cmFindPackageCommand::LoadPackageRegistryDir(std::string const& dir) +{ + cmsys::Directory files; + if(!files.Load(dir.c_str())) + { + return; + } + + std::string fname; + for(unsigned long i=0; i < files.GetNumberOfFiles(); ++i) + { + fname = dir; + fname += "/"; + fname += files.GetFile(i); + + if(!cmSystemTools::FileIsDirectory(fname.c_str())) + { + // Hold this file hostage until it behaves. + cmFindPackageCommandHoldFile holdFile(fname.c_str()); + + // Load the file. + std::ifstream fin(fname.c_str(), std::ios::in | cmsys_ios_binary); + if(fin && this->CheckPackageRegistryEntry(fin)) + { + // The file references an existing package, so release it. + holdFile.Release(); + } + } + } + + // TODO: Wipe out the directory if it is empty. +} +#endif + +//---------------------------------------------------------------------------- +bool cmFindPackageCommand::CheckPackageRegistryEntry(std::istream& is) +{ + // Parse the content of one package registry entry. + std::string fname; + if(cmSystemTools::GetLineFromStream(is, fname) && + cmSystemTools::FileIsFullPath(fname.c_str())) + { + // The first line in the stream is the full path to a file or + // directory containing the package. + if(cmSystemTools::FileExists(fname.c_str())) + { + // The path exists. Look for the package here. + if(!cmSystemTools::FileIsDirectory(fname.c_str())) + { + fname = cmSystemTools::GetFilenamePath(fname); + } + this->AddPathInternal(fname, FullPath); + return true; + } + else + { + // The path does not exist. Assume the stream content is + // associated with an old package that no longer exists, and + // delete it to keep the package registry clean. + return false; + } + } + else + { + // The first line in the stream is not the full path to a file or + // directory. Assume the stream content was created by a future + // version of CMake that uses a different format, and leave it. + return true; + } +} + +//---------------------------------------------------------------------------- void cmFindPackageCommand::AddPrefixesBuilds() { if(!this->NoBuilds && !this->NoDefaultPath) @@ -1396,7 +1580,6 @@ void cmFindPackageCommand::StoreVersionFound() } //---------------------------------------------------------------------------- -#include #include #include #include diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index faeb04d..a2ad1c2 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -90,11 +90,15 @@ private: void AddPrefixesCMakeEnvironment(); void AddPrefixesCMakeVariable(); void AddPrefixesSystemEnvironment(); + void AddPrefixesRegistry(); void AddPrefixesBuilds(); void AddPrefixesCMakeSystemVariable(); void AddPrefixesUserGuess(); void AddPrefixesUserHints(); void ComputeFinalPrefixes(); + void LoadPackageRegistryDir(std::string const& dir); + void LoadPackageRegistryWin(); + bool CheckPackageRegistryEntry(std::istream& is); bool SearchDirectory(std::string const& dir); bool CheckDirectory(std::string const& dir); bool FindConfigFile(std::string const& dir, std::string& file); @@ -130,6 +134,7 @@ private: bool Required; bool Compatibility_1_6; bool NoModule; + bool NoRegistry; bool NoBuilds; bool DebugMode; bool UseLib64Paths; -- cgit v0.12