summaryrefslogtreecommitdiffstats
path: root/Source/cmGeneratorExpression.cxx
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2009-08-11 13:54:56 (GMT)
committerBrad King <brad.king@kitware.com>2009-08-11 13:54:56 (GMT)
commitd2e1f2b4d6d87c171464b5dc41b00b609c90bf26 (patch)
treed0108fae5ed9b014939c0f7baee8a6ad4d6daa42 /Source/cmGeneratorExpression.cxx
parent463b3f03bd848a345ab535d31be31d395fe66b13 (diff)
downloadCMake-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.cxx196
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;
+}