[Cmake-commits] [cmake-commits] king committed CMakeLists.txt 1.423 1.424 cmAddTestCommand.h 1.17 1.18 cmGeneratorExpression.cxx NONE 1.1 cmGeneratorExpression.h NONE 1.1 cmTestGenerator.cxx 1.4 1.5
cmake-commits at cmake.org
cmake-commits at cmake.org
Tue Aug 11 09:54:58 EDT 2009
Update of /cvsroot/CMake/CMake/Source
In directory public:/mounts/ram/cvs-serv13987/Source
Modified Files:
CMakeLists.txt cmAddTestCommand.h cmTestGenerator.cxx
Added Files:
cmGeneratorExpression.cxx cmGeneratorExpression.h
Log Message:
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.
Index: cmAddTestCommand.h
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmAddTestCommand.h,v
retrieving revision 1.17
retrieving revision 1.18
diff -C 2 -d -r1.17 -r1.18
*** cmAddTestCommand.h 16 Mar 2009 14:51:13 -0000 1.17
--- cmAddTestCommand.h 11 Aug 2009 13:54:55 -0000 1.18
***************
*** 80,83 ****
--- 80,106 ----
"If a CONFIGURATIONS option is given then the test will be executed "
"only when testing under one of the named configurations."
+ "\n"
+ "Arguments after COMMAND may use \"generator expressions\" with the "
+ "syntax \"$<...>\". "
+ "These expressions are evaluted during build system generation and "
+ "produce information specific to each generated build configuration. "
+ "Valid expressions are:\n"
+ " $<CONFIGURATION> = configuration name\n"
+ " $<TARGET_FILE:tgt> = main file (.exe, .so.1.2, .a)\n"
+ " $<TARGET_LINKER_FILE:tgt> = file used to link (.a, .lib, .so)\n"
+ " $<TARGET_SONAME_FILE:tgt> = file with soname (.so.3)\n"
+ "where \"tgt\" is the name of a target. "
+ "Target file expressions produce a full path, but _DIR and _NAME "
+ "versions can produce the directory and file name components:\n"
+ " $<TARGET_FILE_DIR:tgt>/$<TARGET_FILE_NAME:tgt>\n"
+ " $<TARGET_LINKER_FILE_DIR:tgt>/$<TARGET_LINKER_FILE_NAME:tgt>\n"
+ " $<TARGET_SONAME_FILE_DIR:tgt>/$<TARGET_SONAME_FILE_NAME:tgt>\n"
+ "Example usage:\n"
+ " add_test(NAME mytest\n"
+ " COMMAND testDriver --config $<CONFIGURATION>\n"
+ " --exe $<TARGET_FILE:myexe>)\n"
+ "This creates a test \"mytest\" whose command runs a testDriver "
+ "tool passing the configuration name and the full path to the "
+ "executable file produced by target \"myexe\"."
;
}
Index: cmTestGenerator.cxx
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/cmTestGenerator.cxx,v
retrieving revision 1.4
retrieving revision 1.5
diff -C 2 -d -r1.4 -r1.5
*** cmTestGenerator.cxx 10 Aug 2009 13:07:59 -0000 1.4
--- cmTestGenerator.cxx 11 Aug 2009 13:54:55 -0000 1.5
***************
*** 17,20 ****
--- 17,21 ----
#include "cmTestGenerator.h"
+ #include "cmGeneratorExpression.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
***************
*** 95,98 ****
--- 96,103 ----
this->TestGenerated = true;
+ // Set up generator expression evaluation context.
+ cmMakefile* mf = this->Test->GetMakefile();
+ cmGeneratorExpression ge(mf, config, this->Test->GetBacktrace());
+
// Start the test command.
os << indent << "ADD_TEST(" << this->Test->GetName() << " ";
***************
*** 104,108 ****
// be translated.
std::string exe = command[0];
- cmMakefile* mf = this->Test->GetMakefile();
cmTarget* target = mf->FindTargetToUse(exe.c_str());
if(target && target->GetType() == cmTarget::EXECUTABLE)
--- 109,112 ----
***************
*** 114,117 ****
--- 118,122 ----
{
// Use the command name given.
+ exe = ge.Process(exe.c_str());
cmSystemTools::ConvertToUnixSlashes(exe);
}
***************
*** 123,127 ****
ci != command.end(); ++ci)
{
! os << " " << lg->EscapeForCMake(ci->c_str());
}
--- 128,132 ----
ci != command.end(); ++ci)
{
! os << " " << lg->EscapeForCMake(ge.Process(*ci));
}
Index: CMakeLists.txt
===================================================================
RCS file: /cvsroot/CMake/CMake/Source/CMakeLists.txt,v
retrieving revision 1.423
retrieving revision 1.424
diff -C 2 -d -r1.423 -r1.424
*** CMakeLists.txt 5 Aug 2009 14:14:08 -0000 1.423
--- CMakeLists.txt 11 Aug 2009 13:54:54 -0000 1.424
***************
*** 160,163 ****
--- 160,165 ----
cmFileTimeComparison.h
cmGeneratedFileStream.cxx
+ cmGeneratorExpression.cxx
+ cmGeneratorExpression.h
cmGlobalGenerator.cxx
cmGlobalGenerator.h
--- NEW FILE: cmGeneratorExpression.h ---
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile: cmGeneratorExpression.h,v $
Language: C++
Date: $Date: 2009-08-11 13:54:55 $
Version: $Revision: 1.1 $
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 "cmStandardIncludes.h"
#include <stack>
#include <cmsys/RegularExpression.hxx>
class cmMakefile;
class cmListFileBacktrace;
/** \class cmGeneratorExpression
* \brief Evaluate generate-time query expression syntax.
*
* cmGeneratorExpression instances are used by build system generator
* implementations to evaluate the $<> generator expression syntax.
* Generator expressions are evaluated just before the generate step
* writes strings into the build system. They have knowledge of the
* build configuration which is not available at configure time.
*/
class cmGeneratorExpression
{
public:
/** Construct with an evaluation context and configuration. */
cmGeneratorExpression(cmMakefile* mf, const char* config,
cmListFileBacktrace const& backtrace);
/** Evaluate generator expressions in a string. */
const char* Process(std::string const& input);
const char* Process(const char* input);
private:
cmMakefile* Makefile;
const char* Config;
cmListFileBacktrace const& Backtrace;
std::vector<char> Data;
std::stack<size_t> Barriers;
cmsys::RegularExpression TargetInfo;
bool Evaluate();
bool Evaluate(const char* expr, std::string& result);
bool EvaluateTargetInfo(std::string& result);
};
--- NEW FILE: cmGeneratorExpression.cxx ---
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile: cmGeneratorExpression.cxx,v $
Language: C++
Date: $Date: 2009-08-11 13:54:55 $
Version: $Revision: 1.1 $
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;
}
More information about the Cmake-commits
mailing list