498 lines
14 KiB
C++
498 lines
14 KiB
C++
/*=========================================================================
|
|
|
|
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 <cmsys/RegularExpression.hxx>
|
|
|
|
#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<std::string> 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<name>.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 <NAME>_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<std::string> systemPath;
|
|
cmSystemTools::GetPath(systemPath);
|
|
for(std::vector<std::string>::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<cmStdString>::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<cmStdString>::const_iterator p = this->Prefixes.begin();
|
|
p != this->Prefixes.end(); ++p)
|
|
{
|
|
std::string prefix = *p;
|
|
for(std::vector<cmStdString>::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<std::string> contents;
|
|
cmSystemTools::ExpandListArgument(propertyValue, contents, false);
|
|
|
|
bool alreadyInserted = false;
|
|
for(std::vector<std::string>::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");
|
|
}
|
|
}
|
|
}
|