[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