/*========================================================================= 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 "cmFindPackageCommand.h" #include #ifdef CMAKE_BUILD_WITH_CMAKE #include "cmVariableWatch.h" #endif void cmFindPackageNeedBackwardsCompatibility(const std::string& variable, int access_type, void*, const char* newValue, const cmMakefile*) { (void)newValue; #ifdef CMAKE_BUILD_WITH_CMAKE if(access_type == cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS) { std::string message = "An attempt was made to access a variable: "; message += variable; message += " that has not been defined. This variable is created by the " "FIND_PACKAGE command. CMake version 1.6 always converted the " "variable name to upper-case, but this behavior is no longer the " "case. To fix this you might need to set the cache value of " "CMAKE_BACKWARDS_COMPATIBILITY to 1.6 or less. If you are writing a " "CMake listfile, you should change the variable reference to use " "the case of the argument to FIND_PACKAGE."; cmSystemTools::Error(message.c_str()); } #else (void)variable; (void)access_type; #endif } //---------------------------------------------------------------------------- bool cmFindPackageCommand::InitialPass(std::vector const& args) { if(args.size() < 1) { this->SetError("called with incorrect number of arguments"); return false; } // Record options. this->Name = args[0]; bool quiet = false; bool required = false; bool no_module = false; std::string components; const char* components_sep = ""; // Parse the arguments. bool doing_components = false; 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; } else if(args[i] == "NO_MODULE") { no_module = true; doing_components = false; } else if(args[i] == "REQUIRED") { required = true; doing_components = true; } else if(args[i] == "COMPONENTS") { doing_components = true; } else if(doing_components) { // Set a variable telling the find script this component // is required. std::string req_var = Name + "_FIND_REQUIRED_" + args[i]; this->Makefile->AddDefinition(req_var.c_str(), "1"); // Append to the list of required components. components += components_sep; components += args[i]; components_sep = ";"; } else if(!haveVersion && version.find(args[i].c_str())) { haveVersion = true; } else { cmOStringStream e; e << "called with invalid argument \"" << args[i].c_str() << "\""; this->SetError(e.str().c_str()); return false; } } // Store the list of components. 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) { bool foundModule = false; if(!this->FindModule(foundModule, quiet, required)) { this->AppendSuccessInformation(quiet); return false; } if(foundModule) { this->AppendSuccessInformation(quiet); return true; } } // No find module. Assume the project has a CMake config file. Use // a _DIR cache variable to locate it. this->Variable = this->Name; this->Variable += "_DIR"; this->Config = this->Name; this->Config += "Config.cmake"; // 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)) { const char* versionValue = this->Makefile->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY"); if(atof(versionValue) < 1.7) { needCompatibility = true; } } // Try to find the config file. const char* def = this->Makefile->GetDefinition(this->Variable.c_str()); if(needCompatibility && cmSystemTools::IsOff(def)) { // Use the setting of the old name of the variable to provide the // value of the new. const char* oldDef = this->Makefile->GetDefinition(upperDir.c_str()); if(!cmSystemTools::IsOff(oldDef)) { this->Makefile->AddDefinition(this->Variable.c_str(), oldDef); def = this->Makefile->GetDefinition(this->Variable.c_str()); } } if(cmSystemTools::IsOff(def)) { if(!this->FindConfig()) { this->AppendSuccessInformation(quiet); return false; } } // If the config file was found, load it. bool result = true; bool found = false; def = this->Makefile->GetDefinition(this->Variable.c_str()); if(!cmSystemTools::IsOff(def)) { std::string f = def; f += "/"; f += this->Config; if(cmSystemTools::FileExists(f.c_str())) { if(this->ReadListFile(f.c_str())) { found = true; } else { result = false; } } else { 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; } } else if(!quiet || required) { cmOStringStream e; e << this->Variable << " is not set. It must be set to the directory " << "containing " << this->Config << " in order to use " << this->Name << "."; cmSystemTools::Error(e.str().c_str()); if(required) { cmSystemTools::SetFatalErrorOccured(); } result = true; } // Set a variable marking whether the package was found. std::string foundVar = this->Name; foundVar += "_FOUND"; this->Makefile->AddDefinition(foundVar.c_str(), found? "1":"0"); if(needCompatibility) { // Listfiles will be looking for the capitalized version of the // name. Provide it. this->Makefile->AddDefinition (upperDir.c_str(), this->Makefile->GetDefinition(this->Variable.c_str())); this->Makefile->AddDefinition (upperFound.c_str(), this->Makefile->GetDefinition(foundVar.c_str())); } #ifdef CMAKE_BUILD_WITH_CMAKE if(!(upperDir == this->Variable)) { if(needCompatibility) { // Listfiles may use the capitalized version of the name. // Remove any previously added watch. this->Makefile->GetVariableWatch()->RemoveWatch( upperDir.c_str(), cmFindPackageNeedBackwardsCompatibility ); } else { // Listfiles should not be using the capitalized version of the // name. Add a watch to warn the user. this->Makefile->GetVariableWatch()->AddWatch( upperDir.c_str(), cmFindPackageNeedBackwardsCompatibility ); } } #endif this->AppendSuccessInformation(quiet); return result; } //---------------------------------------------------------------------------- bool cmFindPackageCommand::FindModule(bool& found, bool quiet, bool required) { 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 // Look for the project's configuration file. std::string init = this->SearchForConfig(); // Store the entry in the cache so it can be set by the user. this->Makefile->AddCacheDefinition(this->Variable.c_str(), init.c_str(), help.c_str(), cmCacheManager::PATH); return true; } //---------------------------------------------------------------------------- std::string cmFindPackageCommand::SearchForConfig() const { // Check the environment variable. std::string env; if(cmSystemTools::GetEnv(this->Variable.c_str(), env) && env.length() > 0) { cmSystemTools::ConvertToUnixSlashes(env); std::string f = env; f += "/"; f += this->Config; if(cmSystemTools::FileExists(f.c_str())) { return env; } } // Search the build directories. for(std::vector::const_iterator b = this->Builds.begin(); b != this->Builds.end(); ++b) { std::string f = *b; f += "/"; f += this->Config; if(cmSystemTools::FileExists(f.c_str())) { return *b; } } // Search paths relative to each installation prefix. for(std::vector::const_iterator p = this->Prefixes.begin(); p != this->Prefixes.end(); ++p) { std::string prefix = *p; for(std::vector::const_iterator r = this->Relatives.begin(); r != this->Relatives.end(); ++r) { std::string dir = prefix; dir += *r; std::string f = dir; f += "/"; f += this->Config; if(cmSystemTools::FileExists(f.c_str())) { return dir; } } } return this->Variable + "-NOTFOUND"; } //---------------------------------------------------------------------------- bool cmFindPackageCommand::ReadListFile(const char* f) { if(this->Makefile->ReadListFile(this->Makefile->GetCurrentListFile(),f)) { return true; } std::string e = "Error reading CMake code from \""; e += f; e += "\"."; this->SetError(e.c_str()); return false; } //---------------------------------------------------------------------------- void cmFindPackageCommand::AppendToProperty(const char* propertyName) { std::string propertyValue; const char *prop = this->Makefile->GetCMakeInstance()->GetProperty(propertyName); if (prop && *prop) { propertyValue = prop; std::vector contents; cmSystemTools::ExpandListArgument(propertyValue, contents, false); bool alreadyInserted = false; for(std::vector::const_iterator it = contents.begin(); it != contents.end(); ++ it ) { if (*it == this->Name) { alreadyInserted = true; break; } } if (!alreadyInserted) { propertyValue += ";"; propertyValue += this->Name; } } else { propertyValue = this->Name; } this->Makefile->GetCMakeInstance()->SetProperty(propertyName, propertyValue.c_str()); } //---------------------------------------------------------------------------- void cmFindPackageCommand::AppendSuccessInformation(bool quiet) { std::string found = this->Name; found += "_FOUND"; std::string upperFound = cmSystemTools::UpperCase(found); const char* upperResult = this->Makefile->GetDefinition(upperFound.c_str()); const char* result = this->Makefile->GetDefinition(found.c_str()); if ((cmSystemTools::IsOn(result)) || (cmSystemTools::IsOn(upperResult))) { this->AppendToProperty("PACKAGES_FOUND"); if (!quiet) { this->AppendToProperty("ENABLED_FEATURES"); } } else { this->AppendToProperty("PACKAGES_NOT_FOUND"); if (!quiet) { this->AppendToProperty("DISABLED_FEATURES"); } } }