CMake/Source/cmGeneratorExpression.cxx
Brad King 08428ba38a Allow '.' in target names in generator expressions (#12002)
Simply add this character to the allowed list in the regular expression
used to parse generator expression components.
2011-03-22 16:26:20 -04:00

194 lines
5.9 KiB
C++

/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmGeneratorExpression.h"
#include "cmMakefile.h"
#include "cmTarget.h"
//----------------------------------------------------------------------------
cmGeneratorExpression::cmGeneratorExpression(
cmMakefile* mf, const char* config,
cmListFileBacktrace const& backtrace, bool quiet):
Makefile(mf), Config(config), Backtrace(backtrace), Quiet(quiet)
{
this->TargetInfo.compile("^\\$<TARGET"
"(|_SONAME|_LINKER)" // File with what purpose?
"_FILE(|_NAME|_DIR):" // Filename component.
"([A-Za-z0-9_.-]+)" // Target name.
">$");
}
//----------------------------------------------------------------------------
const char* cmGeneratorExpression::Process(std::string const& input)
{
return this->Process(input.c_str());
}
//----------------------------------------------------------------------------
const char* cmGeneratorExpression::Process(const char* input)
{
this->Data.clear();
// We construct and evaluate expressions directly in the output
// buffer. Each expression is replaced by its own output value
// after evaluation. A stack of barriers records the starting
// indices of open (pending) expressions.
for(const char* c = input; *c; ++c)
{
if(c[0] == '$' && c[1] == '<')
{
this->Barriers.push(this->Data.size());
this->Data.push_back('$');
this->Data.push_back('<');
c += 1;
}
else if(c[0] == '>' && !this->Barriers.empty())
{
this->Data.push_back('>');
if(!this->Evaluate()) { break; }
this->Barriers.pop();
}
else
{
this->Data.push_back(c[0]);
}
}
// Return a null-terminated output value.
this->Data.push_back('\0');
return &*this->Data.begin();
}
//----------------------------------------------------------------------------
bool cmGeneratorExpression::Evaluate()
{
// The top-most barrier points at the beginning of the expression.
size_t barrier = this->Barriers.top();
// Construct a null-terminated representation of the expression.
this->Data.push_back('\0');
const char* expr = &*(this->Data.begin()+barrier);
// Evaluate the expression.
std::string result;
if(this->Evaluate(expr, result))
{
// Success. Replace the expression with its evaluation result.
this->Data.erase(this->Data.begin()+barrier, this->Data.end());
this->Data.insert(this->Data.end(), result.begin(), result.end());
return true;
}
else if(!this->Quiet)
{
// Failure. Report the error message.
cmOStringStream e;
e << "Error evaluating generator expression:\n"
<< " " << expr << "\n"
<< result;
this->Makefile->GetCMakeInstance()
->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(),
this->Backtrace);
return false;
}
return true;
}
//----------------------------------------------------------------------------
bool cmGeneratorExpression::Evaluate(const char* expr, std::string& result)
{
if(this->TargetInfo.find(expr))
{
if(!this->EvaluateTargetInfo(result))
{
return false;
}
}
else if(strcmp(expr, "$<CONFIGURATION>") == 0)
{
result = this->Config? this->Config : "";
}
else
{
result = "Expression syntax not recognized.";
return false;
}
return true;
}
//----------------------------------------------------------------------------
bool cmGeneratorExpression::EvaluateTargetInfo(std::string& result)
{
// Lookup the referenced target.
std::string name = this->TargetInfo.match(3);
cmTarget* target = this->Makefile->FindTargetToUse(name.c_str());
if(!target)
{
result = "No target \"" + name + "\"";
return false;
}
if(target->GetType() >= cmTarget::UTILITY &&
target->GetType() != cmTarget::UNKNOWN_LIBRARY)
{
result = "Target \"" + name + "\" is not an executable or library.";
return false;
}
this->Targets.insert(target);
// Lookup the target file with the given purpose.
std::string purpose = this->TargetInfo.match(1);
if(purpose == "")
{
// The target implementation file (.so.1.2, .dll, .exe, .a).
result = target->GetFullPath(this->Config, false, true);
}
else if(purpose == "_LINKER")
{
// The file used to link to the target (.so, .lib, .a).
if(!target->IsLinkable())
{
result = ("TARGET_LINKER_FILE is allowed only for libraries and "
"executables with ENABLE_EXPORTS.");
return false;
}
result = target->GetFullPath(this->Config, target->HasImportLibrary());
}
else if(purpose == "_SONAME")
{
// The target soname file (.so.1).
if(target->IsDLLPlatform())
{
result = "TARGET_SONAME_FILE is not allowed for DLL target platforms.";
return false;
}
if(target->GetType() != cmTarget::SHARED_LIBRARY)
{
result = "TARGET_SONAME_FILE is allowed only for SHARED libraries.";
return false;
}
result = target->GetDirectory(this->Config);
result += "/";
result += target->GetSOName(this->Config);
}
// Extract the requested portion of the full path.
std::string part = this->TargetInfo.match(2);
if(part == "_NAME")
{
result = cmSystemTools::GetFilenameName(result);
}
else if(part == "_DIR")
{
result = cmSystemTools::GetFilenamePath(result);
}
return true;
}