diff options
author | Brad King <brad.king@kitware.com> | 2009-08-11 13:54:56 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2009-08-11 13:54:56 (GMT) |
commit | d2e1f2b4d6d87c171464b5dc41b00b609c90bf26 (patch) | |
tree | d0108fae5ed9b014939c0f7baee8a6ad4d6daa42 /Source/cmGeneratorExpression.cxx | |
parent | 463b3f03bd848a345ab535d31be31d395fe66b13 (diff) | |
download | CMake-d2e1f2b4d6d87c171464b5dc41b00b609c90bf26.zip CMake-d2e1f2b4d6d87c171464b5dc41b00b609c90bf26.tar.gz CMake-d2e1f2b4d6d87c171464b5dc41b00b609c90bf26.tar.bz2 |
Introduce "generator expressions" to add_test()
This introduces a new syntax called "generator expressions" to the test
COMMAND option of the add_test(NAME) command mode. These expressions
have a syntax like $<TARGET_FILE:mytarget> and are evaluated during
build system generation. This syntax allows per-configuration target
output files to be referenced in test commands and arguments.
Diffstat (limited to 'Source/cmGeneratorExpression.cxx')
-rw-r--r-- | Source/cmGeneratorExpression.cxx | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/Source/cmGeneratorExpression.cxx b/Source/cmGeneratorExpression.cxx new file mode 100644 index 0000000..5c409f7 --- /dev/null +++ b/Source/cmGeneratorExpression.cxx @@ -0,0 +1,196 @@ +/*========================================================================= + + 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 "cmGeneratorExpression.h" + +#include "cmMakefile.h" +#include "cmTarget.h" + +//---------------------------------------------------------------------------- +cmGeneratorExpression::cmGeneratorExpression( + cmMakefile* mf, const char* config, + cmListFileBacktrace const& backtrace): + Makefile(mf), Config(config), Backtrace(backtrace) +{ + 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 + { + // 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; + } +} + +//---------------------------------------------------------------------------- +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; + } + + // 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; +} |