Pārlūkot izejas kodu

Merge topic 'generator-expression-refactor'

91011bd cmGeneratorExpression: Port users to two-stage processing
f1eacf0 cmGeneratorExpression: Re-write for multi-stage evaluation
Brad King 13 gadi atpakaļ
vecāks
revīzija
d407dcdbc8

+ 6 - 0
Source/CMakeLists.txt

@@ -183,6 +183,12 @@ set(SRCS
   cmFileTimeComparison.cxx
   cmFileTimeComparison.h
   cmGeneratedFileStream.cxx
+  cmGeneratorExpressionEvaluator.cxx
+  cmGeneratorExpressionEvaluator.h
+  cmGeneratorExpressionLexer.cxx
+  cmGeneratorExpressionLexer.h
+  cmGeneratorExpressionParser.cxx
+  cmGeneratorExpressionParser.h
   cmGeneratorExpression.cxx
   cmGeneratorExpression.h
   cmGeneratorTarget.cxx

+ 4 - 3
Source/cmCustomCommandGenerator.cxx

@@ -21,7 +21,7 @@ cmCustomCommandGenerator::cmCustomCommandGenerator(
   cmCustomCommand const& cc, const char* config, cmMakefile* mf):
   CC(cc), Config(config), Makefile(mf), LG(mf->GetLocalGenerator()),
   OldStyle(cc.GetEscapeOldStyle()), MakeVars(cc.GetEscapeAllowMakeVars()),
-  GE(new cmGeneratorExpression(mf, config, cc.GetBacktrace()))
+  GE(new cmGeneratorExpression(cc.GetBacktrace()))
 {
 }
 
@@ -47,7 +47,7 @@ std::string cmCustomCommandGenerator::GetCommand(unsigned int c) const
     {
     return target->GetLocation(this->Config);
     }
-  return this->GE->Process(argv0);
+  return this->GE->Parse(argv0).Evaluate(this->Makefile, this->Config);
 }
 
 //----------------------------------------------------------------------------
@@ -58,7 +58,8 @@ cmCustomCommandGenerator
   cmCustomCommandLine const& commandLine = this->CC.GetCommandLines()[c];
   for(unsigned int j=1;j < commandLine.size(); ++j)
     {
-    std::string arg = this->GE->Process(commandLine[j]);
+    std::string arg = this->GE->Parse(commandLine[j]).Evaluate(this->Makefile,
+                                                               this->Config);
     cmd += " ";
     if(this->OldStyle)
       {

+ 72 - 193
Source/cmGeneratorExpression.cxx

@@ -16,233 +16,112 @@
 
 #include <cmsys/String.h>
 
+#include "cmGeneratorExpressionEvaluator.h"
+#include "cmGeneratorExpressionLexer.h"
+#include "cmGeneratorExpressionParser.h"
+
 //----------------------------------------------------------------------------
 cmGeneratorExpression::cmGeneratorExpression(
-  cmMakefile* mf, const char* config,
-  cmListFileBacktrace const& backtrace, bool quiet):
-  Makefile(mf), Config(config), Backtrace(backtrace), Quiet(quiet)
+  cmListFileBacktrace const& backtrace):
+  Backtrace(backtrace), CompiledExpression(0)
 {
-  this->TargetInfo.compile("^\\$<TARGET"
-                           "(|_SONAME|_LINKER)"  // File with what purpose?
-                           "_FILE(|_NAME|_DIR):" // Filename component.
-                           "([A-Za-z0-9_.-]+)"   // Target name.
-                           ">$");
-  this->TestConfig.compile("^\\$<CONFIG:([A-Za-z0-9_]*)>$");
 }
 
 //----------------------------------------------------------------------------
-const char* cmGeneratorExpression::Process(std::string const& input)
+const cmCompiledGeneratorExpression &
+cmGeneratorExpression::Parse(std::string const& input)
 {
-  return this->Process(input.c_str());
+  return this->Parse(input.c_str());
 }
 
 //----------------------------------------------------------------------------
-const char* cmGeneratorExpression::Process(const char* input)
+const cmCompiledGeneratorExpression &
+cmGeneratorExpression::Parse(const char* input)
 {
-  this->Data.clear();
+  cmGeneratorExpressionLexer l;
+  std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(input);
+  bool needsParsing = l.GetSawGeneratorExpression();
+  std::vector<cmGeneratorExpressionEvaluator*> evaluators;
 
-  // 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 (needsParsing)
     {
-    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]);
-      }
+    cmGeneratorExpressionParser p(tokens);
+    p.Parse(evaluators);
     }
 
-  // Return a null-terminated output value.
-  this->Data.push_back('\0');
-  return &*this->Data.begin();
+  delete this->CompiledExpression;
+  this->CompiledExpression = new cmCompiledGeneratorExpression(
+                                      this->Backtrace,
+                                      evaluators,
+                                      input,
+                                      needsParsing);
+  return *this->CompiledExpression;
 }
 
-//----------------------------------------------------------------------------
-bool cmGeneratorExpression::Evaluate()
+cmGeneratorExpression::~cmGeneratorExpression()
 {
-  // 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;
+  delete this->CompiledExpression;
 }
 
 //----------------------------------------------------------------------------
-static bool cmGeneratorExpressionBool(const char* c, std::string& result,
-                                      const char* name,
-                                      const char* a, const char* b)
+const char *cmCompiledGeneratorExpression::Evaluate(
+  cmMakefile* mf, const char* config, bool quiet) const
 {
-  result = a;
-  while((c[0] == '0' || c[0] == '1') && (c[1] == ',' || c[1] == '>'))
-    {
-    if(c[0] == b[0]) { result = b; }
-    c += 2;
-    }
-  if(c[0])
+  if (!this->NeedsParsing)
     {
-    result = name;
-    result += " requires one or more comma-separated '0' or '1' values.";
-    return false;
+    return this->Input;
     }
-  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 if(strncmp(expr, "$<0:",4) == 0)
-    {
-    result = "";
-    }
-  else if(strncmp(expr, "$<1:",4) == 0)
-    {
-    result = std::string(expr+4, strlen(expr)-5);
-    }
-  else if(strncmp(expr, "$<NOT:",6) == 0)
+  this->Output = "";
+
+  std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
+                                                  = this->Evaluators.begin();
+  const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
+                                                  = this->Evaluators.end();
+
+  cmGeneratorExpressionContext context;
+  context.Makefile = mf;
+  context.Config = config;
+  context.Quiet = quiet;
+  context.HadError = false;
+  context.Backtrace = this->Backtrace;
+
+  for ( ; it != end; ++it)
     {
-    const char* c = expr+6;
-    if((c[0] != '0' && c[0] != '1') || c[1] != '>' || c[2])
+    this->Output += (*it)->Evaluate(&context);
+    if (context.HadError)
       {
-      result = "NOT requires exactly one '0' or '1' value.";
-      return false;
+      this->Output = "";
+      break;
       }
-    result = c[0] == '1'? "0" : "1";
     }
-  else if(strncmp(expr, "$<AND:",6) == 0)
-    {
-    return cmGeneratorExpressionBool(expr+6, result, "AND", "1", "0");
-    }
-  else if(strncmp(expr, "$<OR:",5) == 0)
-    {
-    return cmGeneratorExpressionBool(expr+5, result, "OR", "0", "1");
-    }
-  else if(this->TestConfig.find(expr))
-    {
-    result = cmsysString_strcasecmp(this->TestConfig.match(1).c_str(),
-                                    this->Config? this->Config:"") == 0
-      ? "1":"0";
-    }
-  else
-    {
-    result = "Expression syntax not recognized.";
-    return false;
-    }
-  return true;
+
+  this->Targets = context.Targets;
+  // TODO: Return a std::string from here instead?
+  return this->Output.c_str();
 }
 
-//----------------------------------------------------------------------------
-bool cmGeneratorExpression::EvaluateTargetInfo(std::string& result)
+cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
+              cmListFileBacktrace const& backtrace,
+              const std::vector<cmGeneratorExpressionEvaluator*> &evaluators,
+              const char *input, bool needsParsing)
+  : Backtrace(backtrace), Evaluators(evaluators), Input(input),
+    NeedsParsing(needsParsing)
 {
-  // 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")
+
+//----------------------------------------------------------------------------
+cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression()
+{
+  std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
+                                                  = this->Evaluators.begin();
+  const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
+                                                  = this->Evaluators.end();
+
+  for ( ; it != end; ++it)
     {
-    result = cmSystemTools::GetFilenamePath(result);
+    delete *it;
     }
-  return true;
 }

+ 41 - 18
Source/cmGeneratorExpression.h

@@ -19,6 +19,10 @@ class cmTarget;
 class cmMakefile;
 class cmListFileBacktrace;
 
+struct cmGeneratorExpressionEvaluator;
+
+class cmCompiledGeneratorExpression;
+
 /** \class cmGeneratorExpression
  * \brief Evaluate generate-time query expression syntax.
  *
@@ -31,29 +35,48 @@ class cmListFileBacktrace;
 class cmGeneratorExpression
 {
 public:
-  /** Construct with an evaluation context and configuration.  */
-  cmGeneratorExpression(cmMakefile* mf, const char* config,
-                        cmListFileBacktrace const& backtrace,
-                        bool quiet = false);
+  /** Construct. */
+  cmGeneratorExpression(cmListFileBacktrace const& backtrace);
+  ~cmGeneratorExpression();
+
+  const cmCompiledGeneratorExpression& Parse(std::string const& input);
+  const cmCompiledGeneratorExpression& Parse(const char* input);
+
+private:
+  cmGeneratorExpression(const cmGeneratorExpression &);
+  void operator=(const cmGeneratorExpression &);
+
+  cmListFileBacktrace const& Backtrace;
+  cmCompiledGeneratorExpression *CompiledExpression;
+};
 
-  /** Evaluate generator expressions in a string.  */
-  const char* Process(std::string const& input);
-  const char* Process(const char* input);
+class cmCompiledGeneratorExpression
+{
+public:
+  const char* Evaluate(cmMakefile* mf, const char* config,
+                        bool quiet = false) const;
 
   /** Get set of targets found during evaluations.  */
   std::set<cmTarget*> const& GetTargets() const
     { return this->Targets; }
+
+  ~cmCompiledGeneratorExpression();
+
 private:
-  cmMakefile* Makefile;
-  const char* Config;
+  cmCompiledGeneratorExpression(cmListFileBacktrace const& backtrace,
+              const std::vector<cmGeneratorExpressionEvaluator*> &evaluators,
+              const char *input, bool needsParsing);
+
+  friend class cmGeneratorExpression;
+
+  cmCompiledGeneratorExpression(const cmCompiledGeneratorExpression &);
+  void operator=(const cmCompiledGeneratorExpression &);
+
   cmListFileBacktrace const& Backtrace;
-  bool Quiet;
-  std::vector<char> Data;
-  std::stack<size_t> Barriers;
-  cmsys::RegularExpression TargetInfo;
-  cmsys::RegularExpression TestConfig;
-  std::set<cmTarget*> Targets;
-  bool Evaluate();
-  bool Evaluate(const char* expr, std::string& result);
-  bool EvaluateTargetInfo(std::string& result);
+  const std::vector<cmGeneratorExpressionEvaluator*> Evaluators;
+  const char* const Input;
+  const bool NeedsParsing;
+
+  mutable std::set<cmTarget*> Targets;
+  mutable std::string Output;
 };

+ 568 - 0
Source/cmGeneratorExpressionEvaluator.cxx

@@ -0,0 +1,568 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <[email protected]>
+
+  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 "cmMakefile.h"
+
+#include "cmGeneratorExpressionEvaluator.h"
+#include "cmGeneratorExpressionParser.h"
+
+//----------------------------------------------------------------------------
+static void reportError(cmGeneratorExpressionContext *context,
+                        const std::string &expr, const std::string &result)
+{
+  context->HadError = true;
+  if (context->Quiet)
+    {
+    return;
+    }
+
+  cmOStringStream e;
+  e << "Error evaluating generator expression:\n"
+    << "  " << expr << "\n"
+    << result;
+  context->Makefile->GetCMakeInstance()
+    ->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(),
+                    context->Backtrace);
+}
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionNode
+{
+  virtual ~cmGeneratorExpressionNode() {}
+
+  virtual bool GeneratesContent() const { return true; }
+
+  virtual bool AcceptsSingleArbitraryContentParameter() const
+    { return false; }
+
+  virtual int NumExpectedParameters() const { return 1; }
+
+  virtual std::string Evaluate(const std::vector<std::string> &parameters,
+                               cmGeneratorExpressionContext *context,
+                               const GeneratorExpressionContent *content
+                              ) const = 0;
+};
+
+//----------------------------------------------------------------------------
+static const struct ZeroNode : public cmGeneratorExpressionNode
+{
+  ZeroNode() {}
+
+  virtual bool GeneratesContent() const { return false; }
+
+  std::string Evaluate(const std::vector<std::string> &,
+                       cmGeneratorExpressionContext *,
+                       const GeneratorExpressionContent *) const
+  {
+    // Unreachable
+    return std::string();
+  }
+} zeroNode;
+
+//----------------------------------------------------------------------------
+static const struct OneNode : public cmGeneratorExpressionNode
+{
+  OneNode() {}
+
+  virtual bool AcceptsSingleArbitraryContentParameter() const { return true; }
+
+  std::string Evaluate(const std::vector<std::string> &,
+                       cmGeneratorExpressionContext *,
+                       const GeneratorExpressionContent *) const
+  {
+    // Unreachable
+    return std::string();
+  }
+} oneNode;
+
+//----------------------------------------------------------------------------
+#define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \
+static const struct OP ## Node : public cmGeneratorExpressionNode \
+{ \
+  OP ## Node () {} \
+/* We let -1 carry the meaning 'at least one' */ \
+  virtual int NumExpectedParameters() const { return -1; } \
+ \
+  std::string Evaluate(const std::vector<std::string> &parameters, \
+                       cmGeneratorExpressionContext *context, \
+                       const GeneratorExpressionContent *content) const \
+  { \
+    std::vector<std::string>::const_iterator it = parameters.begin(); \
+    const std::vector<std::string>::const_iterator end = parameters.end(); \
+    for ( ; it != end; ++it) \
+      { \
+      if (*it == #FAILURE_VALUE) \
+        { \
+        return #FAILURE_VALUE; \
+        } \
+      else if (*it != #SUCCESS_VALUE) \
+        { \
+        reportError(context, content->GetOriginalExpression(), \
+        "Parameters to $<" #OP "> must resolve to either '0' or '1'."); \
+        return std::string(); \
+        } \
+      } \
+    return #SUCCESS_VALUE; \
+  } \
+} OPNAME;
+
+BOOLEAN_OP_NODE(andNode, AND, 1, 0)
+BOOLEAN_OP_NODE(orNode, OR, 0, 1)
+
+#undef BOOLEAN_OP_NODE
+
+//----------------------------------------------------------------------------
+static const struct NotNode : public cmGeneratorExpressionNode
+{
+  NotNode() {}
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content) const
+  {
+    if (*parameters.begin() != "0" && *parameters.begin() != "1")
+      {
+      reportError(context, content->GetOriginalExpression(),
+            "$<NOT> parameter must resolve to exactly one '0' or '1' value.");
+      return std::string();
+      }
+    return *parameters.begin() == "0" ? "1" : "0";
+  }
+} notNode;
+
+//----------------------------------------------------------------------------
+static const struct ConfigurationNode : public cmGeneratorExpressionNode
+{
+  ConfigurationNode() {}
+  virtual int NumExpectedParameters() const { return 0; }
+
+  std::string Evaluate(const std::vector<std::string> &,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *) const
+  {
+    return context->Config ? context->Config : "";
+  }
+} configurationNode;
+
+//----------------------------------------------------------------------------
+static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
+{
+  ConfigurationTestNode() {}
+
+  virtual int NumExpectedParameters() const { return 1; }
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content) const
+  {
+    if (!context->Config)
+      {
+      return std::string();
+      }
+
+    cmsys::RegularExpression configValidator;
+    configValidator.compile("^[A-Za-z0-9_]*$");
+    if (!configValidator.find(parameters.begin()->c_str()))
+      {
+      reportError(context, content->GetOriginalExpression(),
+                  "Expression syntax not recognized.");
+      return std::string();
+      }
+    return *parameters.begin() == context->Config ? "1" : "0";
+  }
+} configurationTestNode;
+
+//----------------------------------------------------------------------------
+template<bool linker, bool soname>
+struct TargetFilesystemArtifactResultCreator
+{
+  static std::string Create(cmTarget* target,
+                            cmGeneratorExpressionContext *context,
+                            const GeneratorExpressionContent *content);
+};
+
+//----------------------------------------------------------------------------
+template<>
+struct TargetFilesystemArtifactResultCreator<false, true>
+{
+  static std::string Create(cmTarget* target,
+                            cmGeneratorExpressionContext *context,
+                            const GeneratorExpressionContent *content)
+  {
+    // The target soname file (.so.1).
+    if(target->IsDLLPlatform())
+      {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "TARGET_SONAME_FILE is not allowed "
+                    "for DLL target platforms.");
+      return std::string();
+      }
+    if(target->GetType() != cmTarget::SHARED_LIBRARY)
+      {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "TARGET_SONAME_FILE is allowed only for "
+                    "SHARED libraries.");
+      return std::string();
+      }
+    std::string result = target->GetDirectory(context->Config);
+    result += "/";
+    result += target->GetSOName(context->Config);
+    return result;
+  }
+};
+
+//----------------------------------------------------------------------------
+template<>
+struct TargetFilesystemArtifactResultCreator<true, false>
+{
+  static std::string Create(cmTarget* target,
+                            cmGeneratorExpressionContext *context,
+                            const GeneratorExpressionContent *content)
+  {
+    // The file used to link to the target (.so, .lib, .a).
+    if(!target->IsLinkable())
+      {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "TARGET_LINKER_FILE is allowed only for libraries and "
+                    "executables with ENABLE_EXPORTS.");
+      return std::string();
+      }
+    return target->GetFullPath(context->Config,
+                               target->HasImportLibrary());
+  }
+};
+
+//----------------------------------------------------------------------------
+template<>
+struct TargetFilesystemArtifactResultCreator<false, false>
+{
+  static std::string Create(cmTarget* target,
+                            cmGeneratorExpressionContext *context,
+                            const GeneratorExpressionContent *)
+  {
+    return target->GetFullPath(context->Config, false, true);
+  }
+};
+
+
+//----------------------------------------------------------------------------
+template<bool dirQual, bool nameQual>
+struct TargetFilesystemArtifactResultGetter
+{
+  static std::string Get(const std::string &result);
+};
+
+//----------------------------------------------------------------------------
+template<>
+struct TargetFilesystemArtifactResultGetter<false, true>
+{
+  static std::string Get(const std::string &result)
+  { return cmSystemTools::GetFilenameName(result); }
+};
+
+//----------------------------------------------------------------------------
+template<>
+struct TargetFilesystemArtifactResultGetter<true, false>
+{
+  static std::string Get(const std::string &result)
+  { return cmSystemTools::GetFilenamePath(result); }
+};
+
+//----------------------------------------------------------------------------
+template<>
+struct TargetFilesystemArtifactResultGetter<false, false>
+{
+  static std::string Get(const std::string &result)
+  { return result; }
+};
+
+//----------------------------------------------------------------------------
+template<bool linker, bool soname, bool dirQual, bool nameQual>
+struct TargetFilesystemArtifact : public cmGeneratorExpressionNode
+{
+  TargetFilesystemArtifact() {}
+
+  virtual int NumExpectedParameters() const { return 1; }
+
+  std::string Evaluate(const std::vector<std::string> &parameters,
+                       cmGeneratorExpressionContext *context,
+                       const GeneratorExpressionContent *content) const
+  {
+    // Lookup the referenced target.
+    std::string name = *parameters.begin();
+
+    cmsys::RegularExpression targetValidator;
+    targetValidator.compile("^[A-Za-z0-9_]+$");
+    if (!targetValidator.find(name.c_str()))
+      {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "Expression syntax not recognized.");
+      return std::string();
+      }
+    cmTarget* target = context->Makefile->FindTargetToUse(name.c_str());
+    if(!target)
+      {
+      ::reportError(context, content->GetOriginalExpression(),
+                    "No target \"" + name + "\"");
+      return std::string();
+      }
+    if(target->GetType() >= cmTarget::UTILITY &&
+      target->GetType() != cmTarget::UNKNOWN_LIBRARY)
+      {
+      ::reportError(context, content->GetOriginalExpression(),
+                  "Target \"" + name + "\" is not an executable or library.");
+      return std::string();
+      }
+    context->Targets.insert(target);
+
+    std::string result =
+                TargetFilesystemArtifactResultCreator<linker, soname>::Create(
+                          target,
+                          context,
+                          content);
+    if (context->HadError)
+      {
+      return std::string();
+      }
+    return
+        TargetFilesystemArtifactResultGetter<dirQual, nameQual>::Get(result);
+  }
+};
+
+//----------------------------------------------------------------------------
+static const
+TargetFilesystemArtifact<false, false, false, false> targetFileNode;
+static const
+TargetFilesystemArtifact<true, false, false, false> targetLinkerFileNode;
+static const
+TargetFilesystemArtifact<false, true, false, false> targetSoNameFileNode;
+static const
+TargetFilesystemArtifact<false, false, false, true> targetFileNameNode;
+static const
+TargetFilesystemArtifact<true, false, false, true> targetLinkerFileNameNode;
+static const
+TargetFilesystemArtifact<false, true, false, true> targetSoNameFileNameNode;
+static const
+TargetFilesystemArtifact<false, false, true, false> targetFileDirNode;
+static const
+TargetFilesystemArtifact<true, false, true, false> targetLinkerFileDirNode;
+static const
+TargetFilesystemArtifact<false, true, true, false> targetSoNameFileDirNode;
+
+//----------------------------------------------------------------------------
+static const
+cmGeneratorExpressionNode* GetNode(const std::string &identifier)
+{
+  if (identifier == "0")
+    return &zeroNode;
+  if (identifier == "1")
+    return &oneNode;
+  if (identifier == "AND")
+    return &andNode;
+  if (identifier == "OR")
+    return &orNode;
+  if (identifier == "NOT")
+    return &notNode;
+  else if (identifier == "CONFIGURATION")
+    return &configurationNode;
+  else if (identifier == "CONFIG")
+    return &configurationTestNode;
+  else if (identifier == "TARGET_FILE")
+    return &targetFileNode;
+  else if (identifier == "TARGET_LINKER_FILE")
+    return &targetLinkerFileNode;
+  else if (identifier == "TARGET_SONAME_FILE")
+    return &targetSoNameFileNode;
+  else if (identifier == "TARGET_FILE_NAME")
+    return &targetFileNameNode;
+  else if (identifier == "TARGET_LINKER_FILE_NAME")
+    return &targetLinkerFileNameNode;
+  else if (identifier == "TARGET_SONAME_FILE_NAME")
+    return &targetSoNameFileNameNode;
+  else if (identifier == "TARGET_FILE_DIR")
+    return &targetFileDirNode;
+  else if (identifier == "TARGET_LINKER_FILE_DIR")
+    return &targetLinkerFileDirNode;
+  else if (identifier == "TARGET_SONAME_FILE_DIR")
+    return &targetSoNameFileDirNode;
+  return 0;
+}
+
+//----------------------------------------------------------------------------
+GeneratorExpressionContent::GeneratorExpressionContent(
+                                                    const char *startContent,
+                                                    unsigned int length)
+  : StartContent(startContent), ContentLength(length)
+{
+
+}
+
+//----------------------------------------------------------------------------
+std::string GeneratorExpressionContent::GetOriginalExpression() const
+{
+  return std::string(this->StartContent, this->ContentLength);
+}
+
+//----------------------------------------------------------------------------
+std::string GeneratorExpressionContent::Evaluate(
+                                  cmGeneratorExpressionContext *context) const
+{
+  std::string identifier;
+  {
+  std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
+                                          = this->IdentifierChildren.begin();
+  const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
+                                          = this->IdentifierChildren.end();
+  for ( ; it != end; ++it)
+    {
+    identifier += (*it)->Evaluate(context);
+    if (context->HadError)
+      {
+      return std::string();
+      }
+    }
+  }
+
+  const cmGeneratorExpressionNode *node = GetNode(identifier);
+
+  if (!node)
+    {
+    reportError(context, this->GetOriginalExpression(),
+              "Expression did not evaluate to a known generator expression");
+    return std::string();
+    }
+
+  if (!node->GeneratesContent())
+    {
+    return std::string();
+    }
+
+  if (node->AcceptsSingleArbitraryContentParameter())
+    {
+    std::string result;
+    std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
+                                        pit = this->ParamChildren.begin();
+    const
+    std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
+                                        pend = this->ParamChildren.end();
+    for ( ; pit != pend; ++pit)
+      {
+      if (!result.empty())
+        {
+        result += ",";
+        }
+
+      std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
+                                                              = pit->begin();
+      const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
+                                                                = pit->end();
+      for ( ; it != end; ++it)
+        {
+        result += (*it)->Evaluate(context);
+        if (context->HadError)
+          {
+          return std::string();
+          }
+        }
+      }
+    return result;
+    }
+
+  std::vector<std::string> parameters;
+  {
+  std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
+                                        pit = this->ParamChildren.begin();
+  const
+  std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
+                                        pend = this->ParamChildren.end();
+  for ( ; pit != pend; ++pit)
+    {
+    std::string parameter;
+    std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it =
+                                                              pit->begin();
+    const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end =
+                                                              pit->end();
+    for ( ; it != end; ++it)
+      {
+      parameter += (*it)->Evaluate(context);
+      if (context->HadError)
+        {
+        return std::string();
+        }
+      }
+    parameters.push_back(parameter);
+    }
+  }
+
+  int numExpected = node->NumExpectedParameters();
+  if ((numExpected != -1 && (unsigned int)numExpected != parameters.size()))
+    {
+    if (numExpected == 0)
+      {
+      reportError(context, this->GetOriginalExpression(),
+                  "$<" + identifier + "> expression requires no parameters.");
+      }
+    else if (numExpected == 1)
+      {
+      reportError(context, this->GetOriginalExpression(),
+                  "$<" + identifier + "> expression requires "
+                  "exactly one parameter.");
+      }
+    else
+      {
+      cmOStringStream e;
+      e << "$<" + identifier + "> expression requires "
+        << numExpected
+        << " comma separated parameters, but got "
+        << parameters.size() << " instead.";
+      reportError(context, this->GetOriginalExpression(), e.str());
+      }
+    return std::string();
+    }
+
+  if (numExpected == -1 && parameters.empty())
+    {
+    reportError(context, this->GetOriginalExpression(), "$<" + identifier
+                      + "> expression requires at least one parameter.");
+    return std::string();
+    }
+
+  return node->Evaluate(parameters, context, this);
+}
+
+//----------------------------------------------------------------------------
+static void deleteAll(const std::vector<cmGeneratorExpressionEvaluator*> &c)
+{
+  std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
+                                                  = c.begin();
+  const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
+                                                  = c.end();
+  for ( ; it != end; ++it)
+    {
+    delete *it;
+    }
+}
+
+//----------------------------------------------------------------------------
+GeneratorExpressionContent::~GeneratorExpressionContent()
+{
+  deleteAll(this->IdentifierChildren);
+
+  typedef std::vector<cmGeneratorExpressionEvaluator*> EvaluatorVector;
+  typedef std::vector<cmGeneratorExpressionToken> TokenVector;
+  std::vector<EvaluatorVector>::const_iterator pit =
+                                                  this->ParamChildren.begin();
+  const std::vector<EvaluatorVector>::const_iterator pend =
+                                                  this->ParamChildren.end();
+  for ( ; pit != pend; ++pit)
+    {
+    deleteAll(*pit);
+    }
+}

+ 118 - 0
Source/cmGeneratorExpressionEvaluator.h

@@ -0,0 +1,118 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <[email protected]>
+
+  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.
+============================================================================*/
+#ifndef cmGeneratorExpressionEvaluator_h
+#define cmGeneratorExpressionEvaluator_h
+
+#include <vector>
+#include <string>
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionContext
+{
+  cmListFileBacktrace Backtrace;
+  std::set<cmTarget*> Targets;
+  cmMakefile *Makefile;
+  const char *Config;
+  cmTarget *Target;
+  bool Quiet;
+  bool HadError;
+};
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionEvaluator
+{
+  cmGeneratorExpressionEvaluator() {}
+  virtual ~cmGeneratorExpressionEvaluator() {}
+
+  enum Type
+  {
+    Text,
+    Generator
+  };
+
+  virtual Type GetType() const = 0;
+
+  virtual std::string Evaluate(cmGeneratorExpressionContext *context
+                              ) const = 0;
+
+private:
+  cmGeneratorExpressionEvaluator(const cmGeneratorExpressionEvaluator &);
+  void operator=(const cmGeneratorExpressionEvaluator &);
+};
+
+struct TextContent : public cmGeneratorExpressionEvaluator
+{
+  TextContent(const char *start, unsigned int length)
+    : Content(start), Length(length)
+  {
+
+  }
+
+  std::string Evaluate(cmGeneratorExpressionContext *) const
+  {
+    return std::string(this->Content, this->Length);
+  }
+
+  Type GetType() const
+  {
+    return cmGeneratorExpressionEvaluator::Text;
+  }
+
+  void Extend(unsigned int length)
+  {
+    this->Length += length;
+  }
+
+  unsigned int GetLength()
+  {
+    return this->Length;
+  }
+
+private:
+  const char *Content;
+  unsigned int Length;
+};
+
+//----------------------------------------------------------------------------
+struct GeneratorExpressionContent : public cmGeneratorExpressionEvaluator
+{
+  GeneratorExpressionContent(const char *startContent, unsigned int length);
+  void SetIdentifier(std::vector<cmGeneratorExpressionEvaluator*> identifier)
+  {
+    this->IdentifierChildren = identifier;
+  }
+
+  void SetParameters(
+        std::vector<std::vector<cmGeneratorExpressionEvaluator*> > parameters)
+  {
+    this->ParamChildren = parameters;
+  }
+
+  Type GetType() const
+  {
+    return cmGeneratorExpressionEvaluator::Generator;
+  }
+
+  std::string Evaluate(cmGeneratorExpressionContext *context) const;
+
+  std::string GetOriginalExpression() const;
+
+  ~GeneratorExpressionContent();
+
+private:
+  std::vector<cmGeneratorExpressionEvaluator*> IdentifierChildren;
+  std::vector<std::vector<cmGeneratorExpressionEvaluator*> > ParamChildren;
+  const char *StartContent;
+  unsigned int ContentLength;
+};
+
+#endif

+ 85 - 0
Source/cmGeneratorExpressionLexer.cxx

@@ -0,0 +1,85 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <[email protected]>
+
+  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 "cmGeneratorExpressionLexer.h"
+
+
+//----------------------------------------------------------------------------
+cmGeneratorExpressionLexer::cmGeneratorExpressionLexer()
+  : SawBeginExpression(false), SawGeneratorExpression(false)
+{
+
+}
+
+//----------------------------------------------------------------------------
+static void InsertText(const char *upto, const char *c,
+                        std::vector<cmGeneratorExpressionToken> &result)
+{
+  if (upto != c)
+    {
+    result.push_back(cmGeneratorExpressionToken(
+                          cmGeneratorExpressionToken::Text, upto, c - upto));
+    }
+}
+
+//----------------------------------------------------------------------------
+std::vector<cmGeneratorExpressionToken>
+cmGeneratorExpressionLexer::Tokenize(const char *input)
+{
+  std::vector<cmGeneratorExpressionToken> result;
+  if (!input)
+    return result;
+
+  const char *c = input;
+  const char *upto = c;
+
+  for ( ; *c; ++c)
+  {
+  if(c[0] == '$' && c[1] == '<')
+    {
+    InsertText(upto, c, result);
+    upto = c;
+    result.push_back(cmGeneratorExpressionToken(
+                      cmGeneratorExpressionToken::BeginExpression, upto, 2));
+    upto = c + 2;
+    ++c;
+    SawBeginExpression = true;
+    }
+  else if(c[0] == '>')
+    {
+    InsertText(upto, c, result);
+    upto = c;
+    result.push_back(cmGeneratorExpressionToken(
+                        cmGeneratorExpressionToken::EndExpression, upto, 1));
+    upto = c + 1;
+    SawGeneratorExpression = SawBeginExpression;
+    }
+  else if(c[0] == ':')
+    {
+    InsertText(upto, c, result);
+    upto = c;
+    result.push_back(cmGeneratorExpressionToken(
+                        cmGeneratorExpressionToken::ColonSeparator, upto, 1));
+    upto = c + 1;
+    }
+  else if(c[0] == ',')
+    {
+    InsertText(upto, c, result);
+    upto = c;
+    result.push_back(cmGeneratorExpressionToken(
+                        cmGeneratorExpressionToken::CommaSeparator, upto, 1));
+    upto = c + 1;
+    }
+  }
+  InsertText(upto, c, result);
+
+  return result;
+}

+ 58 - 0
Source/cmGeneratorExpressionLexer.h

@@ -0,0 +1,58 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <[email protected]>
+
+  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.
+============================================================================*/
+#ifndef cmGeneratorExpressionLexer_h
+#define cmGeneratorExpressionLexer_h
+
+#include "cmStandardIncludes.h"
+
+#include <vector>
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionToken
+{
+  cmGeneratorExpressionToken(unsigned type, const char *c, unsigned l)
+    : TokenType(type), Content(c), Length(l)
+  {
+  }
+  enum {
+    Text,
+    BeginExpression,
+    EndExpression,
+    ColonSeparator,
+    CommaSeparator
+  };
+  unsigned TokenType;
+  const char *Content;
+  unsigned Length;
+};
+
+/** \class cmGeneratorExpressionLexer
+ *
+ */
+class cmGeneratorExpressionLexer
+{
+public:
+  cmGeneratorExpressionLexer();
+
+  std::vector<cmGeneratorExpressionToken> Tokenize(const char *input);
+
+  bool GetSawGeneratorExpression() const
+  {
+    return this->SawGeneratorExpression;
+  }
+
+private:
+  bool SawBeginExpression;
+  bool SawGeneratorExpression;
+};
+
+#endif

+ 235 - 0
Source/cmGeneratorExpressionParser.cxx

@@ -0,0 +1,235 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <[email protected]>
+
+  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 "cmGeneratorExpressionParser.h"
+
+#include "cmGeneratorExpressionEvaluator.h"
+
+//----------------------------------------------------------------------------
+cmGeneratorExpressionParser::cmGeneratorExpressionParser(
+                      const std::vector<cmGeneratorExpressionToken> &tokens)
+  : Tokens(tokens), NestingLevel(0)
+{
+}
+
+//----------------------------------------------------------------------------
+void cmGeneratorExpressionParser::Parse(
+                        std::vector<cmGeneratorExpressionEvaluator*> &result)
+{
+  it = this->Tokens.begin();
+
+  while (this->it != this->Tokens.end())
+    {
+    this->ParseContent(result);
+    }
+}
+
+//----------------------------------------------------------------------------
+static void extendText(std::vector<cmGeneratorExpressionEvaluator*> &result,
+                  std::vector<cmGeneratorExpressionToken>::const_iterator it)
+{
+  if (result.size() > 0
+      && (*(result.end() - 1))->GetType()
+                                  == cmGeneratorExpressionEvaluator::Text)
+    {
+    TextContent *textContent = static_cast<TextContent*>(*(result.end() - 1));
+    textContent->Extend(it->Length);
+    }
+  else
+    {
+    TextContent *textContent = new TextContent(it->Content, it->Length);
+    result.push_back(textContent);
+    }
+}
+
+//----------------------------------------------------------------------------
+static void extendResult(std::vector<cmGeneratorExpressionEvaluator*> &result,
+                const std::vector<cmGeneratorExpressionEvaluator*> &contents)
+{
+  if (result.size() > 0
+      && (*(result.end() - 1))->GetType()
+                                  == cmGeneratorExpressionEvaluator::Text
+      && (*contents.begin())->GetType()
+                                  == cmGeneratorExpressionEvaluator::Text)
+  {
+    TextContent *textContent = static_cast<TextContent*>(*(result.end() - 1));
+    textContent->Extend(
+                  static_cast<TextContent*>(*contents.begin())->GetLength());
+    delete *contents.begin();
+    result.insert(result.end(), contents.begin() + 1, contents.end());
+  } else {
+    result.insert(result.end(), contents.begin(), contents.end());
+  }
+}
+
+//----------------------------------------------------------------------------
+void cmGeneratorExpressionParser::ParseGeneratorExpression(
+                        std::vector<cmGeneratorExpressionEvaluator*> &result)
+{
+  unsigned int nestedLevel = this->NestingLevel;
+  ++this->NestingLevel;
+
+  std::vector<cmGeneratorExpressionToken>::const_iterator startToken
+                                                              = this->it - 1;
+
+  std::vector<cmGeneratorExpressionEvaluator*> identifier;
+  while(this->it->TokenType != cmGeneratorExpressionToken::EndExpression
+      && this->it->TokenType != cmGeneratorExpressionToken::ColonSeparator)
+    {
+    this->ParseContent(identifier);
+    if (this->it == this->Tokens.end())
+      {
+      break;
+      }
+    }
+  if (identifier.empty())
+    {
+    // ERROR
+    }
+
+  if (this->it->TokenType == cmGeneratorExpressionToken::EndExpression)
+    {
+    GeneratorExpressionContent *content = new GeneratorExpressionContent(
+                startToken->Content, this->it->Content
+                                    - startToken->Content
+                                    + this->it->Length);
+    ++this->it;
+    --this->NestingLevel;
+    content->SetIdentifier(identifier);
+    result.push_back(content);
+    return;
+    }
+
+  std::vector<std::vector<cmGeneratorExpressionEvaluator*> > parameters;
+  std::vector<std::vector<cmGeneratorExpressionToken>::const_iterator>
+                                                            commaTokens;
+  std::vector<cmGeneratorExpressionToken>::const_iterator colonToken;
+  if (this->it->TokenType == cmGeneratorExpressionToken::ColonSeparator)
+    {
+    colonToken = this->it;
+    parameters.resize(parameters.size() + 1);
+    ++this->it;
+    while(this->it->TokenType != cmGeneratorExpressionToken::EndExpression)
+      {
+      this->ParseContent(*(parameters.end() - 1));
+      if (this->it->TokenType == cmGeneratorExpressionToken::CommaSeparator)
+        {
+        commaTokens.push_back(this->it);
+        parameters.resize(parameters.size() + 1);
+        ++this->it;
+        }
+      if (this->it == this->Tokens.end())
+        {
+        break;
+        }
+      }
+      if(this->it->TokenType == cmGeneratorExpressionToken::EndExpression)
+        {
+        --this->NestingLevel;
+        ++this->it;
+        }
+      if (parameters.empty())
+        {
+          // ERROR
+        }
+    }
+
+  if (nestedLevel != this->NestingLevel)
+  {
+    // There was a '$<' in the text, but no corresponding '>'. Rebuild to
+    // treat the '$<' as having been plain text, along with the
+    // corresponding : and , tokens that might have been found.
+    extendText(result, startToken);
+    extendResult(result, identifier);
+    if (!parameters.empty())
+      {
+      extendText(result, colonToken);
+
+      typedef std::vector<cmGeneratorExpressionEvaluator*> EvaluatorVector;
+      typedef std::vector<cmGeneratorExpressionToken> TokenVector;
+      std::vector<EvaluatorVector>::const_iterator pit = parameters.begin();
+      const std::vector<EvaluatorVector>::const_iterator pend =
+                                                         parameters.end();
+      std::vector<TokenVector::const_iterator>::const_iterator commaIt =
+                                                         commaTokens.begin();
+      for ( ; pit != pend; ++pit, ++commaIt)
+        {
+        extendResult(result, *pit);
+        if (commaIt != commaTokens.end())
+          {
+          extendText(result, *commaIt);
+          }
+        }
+      }
+    return;
+  }
+
+  int contentLength = ((this->it - 1)->Content
+                    - startToken->Content)
+                    + (this->it - 1)->Length;
+  GeneratorExpressionContent *content = new GeneratorExpressionContent(
+                            startToken->Content, contentLength);
+  content->SetIdentifier(identifier);
+  content->SetParameters(parameters);
+  result.push_back(content);
+}
+
+//----------------------------------------------------------------------------
+void cmGeneratorExpressionParser::ParseContent(
+                        std::vector<cmGeneratorExpressionEvaluator*> &result)
+{
+  switch(this->it->TokenType)
+    {
+    case cmGeneratorExpressionToken::Text:
+    {
+      if (this->NestingLevel == 0)
+        {
+        if (result.size() > 0
+            && (*(result.end() - 1))->GetType()
+                                      == cmGeneratorExpressionEvaluator::Text)
+          {
+          // A comma in 'plain text' could have split text that should
+          // otherwise be continuous. Extend the last text content instead of
+          // creating a new one.
+          TextContent *textContent =
+                              static_cast<TextContent*>(*(result.end() - 1));
+          textContent->Extend(this->it->Length);
+          ++this->it;
+          return;
+          }
+        }
+      cmGeneratorExpressionEvaluator* n = new TextContent(this->it->Content,
+                                                          this->it->Length);
+      result.push_back(n);
+      ++this->it;
+      return ;
+    }
+    case cmGeneratorExpressionToken::BeginExpression:
+      ++this->it;
+      this->ParseGeneratorExpression(result);
+      return;
+    case cmGeneratorExpressionToken::EndExpression:
+    case cmGeneratorExpressionToken::ColonSeparator:
+    case cmGeneratorExpressionToken::CommaSeparator:
+      if (this->NestingLevel == 0)
+        {
+        extendText(result, this->it);
+        }
+      else
+        {
+        // TODO: Unreachable. Assert?
+        }
+      ++this->it;
+      return;
+    }
+  // Unreachable. Assert?
+}

+ 45 - 0
Source/cmGeneratorExpressionParser.h

@@ -0,0 +1,45 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Stephen Kelly <[email protected]>
+
+  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.
+============================================================================*/
+#ifndef cmGeneratorExpressionParser_h
+#define cmGeneratorExpressionParser_h
+
+#include "cmGeneratorExpressionLexer.h"
+
+#include <set>
+#include <vector>
+
+#include "cmListFileCache.h"
+
+class cmMakefile;
+class cmTarget;
+struct cmGeneratorExpressionEvaluator;
+
+//----------------------------------------------------------------------------
+struct cmGeneratorExpressionParser
+{
+  cmGeneratorExpressionParser(
+                      const std::vector<cmGeneratorExpressionToken> &tokens);
+
+  void Parse(std::vector<cmGeneratorExpressionEvaluator*> &result);
+
+private:
+  void ParseContent(std::vector<cmGeneratorExpressionEvaluator*> &);
+  void ParseGeneratorExpression(
+                              std::vector<cmGeneratorExpressionEvaluator*> &);
+
+private:
+  std::vector<cmGeneratorExpressionToken>::const_iterator it;
+  const std::vector<cmGeneratorExpressionToken> Tokens;
+  unsigned int NestingLevel;
+};
+
+#endif

+ 13 - 4
Source/cmTarget.cxx

@@ -1623,7 +1623,11 @@ cmTargetTraceDependencies
 {
   // Transform command names that reference targets built in this
   // project to corresponding target-level dependencies.
-  cmGeneratorExpression ge(this->Makefile, 0, cc.GetBacktrace(), true);
+  cmGeneratorExpression ge(cc.GetBacktrace());
+
+  // Add target-level dependencies referenced by generator expressions.
+  std::set<cmTarget*> targets;
+
   for(cmCustomCommandLines::const_iterator cit = cc.GetCommandLines().begin();
       cit != cc.GetCommandLines().end(); ++cit)
     {
@@ -1645,12 +1649,17 @@ cmTargetTraceDependencies
     for(cmCustomCommandLine::const_iterator cli = cit->begin();
         cli != cit->end(); ++cli)
       {
-      ge.Process(*cli);
+      const cmCompiledGeneratorExpression &cge = ge.Parse(*cli);
+      cge.Evaluate(this->Makefile, 0, true);
+      std::set<cmTarget*> geTargets = cge.GetTargets();
+      for(std::set<cmTarget*>::const_iterator it = geTargets.begin();
+          it != geTargets.end(); ++it)
+        {
+        targets.insert(*it);
+        }
       }
     }
 
-  // Add target-level dependencies referenced by generator expressions.
-  std::set<cmTarget*> targets = ge.GetTargets();
   for(std::set<cmTarget*>::iterator ti = targets.begin();
       ti != targets.end(); ++ti)
     {

+ 4 - 4
Source/cmTestGenerator.cxx

@@ -91,8 +91,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
   this->TestGenerated = true;
 
   // Set up generator expression evaluation context.
-  cmMakefile* mf = this->Test->GetMakefile();
-  cmGeneratorExpression ge(mf, config, this->Test->GetBacktrace());
+  cmGeneratorExpression ge(this->Test->GetBacktrace());
 
   // Start the test command.
   os << indent << "ADD_TEST(" << this->Test->GetName() << " ";
@@ -103,6 +102,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
   // Check whether the command executable is a target whose name is to
   // 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)
     {
@@ -112,7 +112,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
   else
     {
     // Use the command name given.
-    exe = ge.Process(exe.c_str());
+    exe = ge.Parse(exe.c_str()).Evaluate(mf, config);
     cmSystemTools::ConvertToUnixSlashes(exe);
     }
 
@@ -122,7 +122,7 @@ void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
   for(std::vector<std::string>::const_iterator ci = command.begin()+1;
       ci != command.end(); ++ci)
     {
-    os << " " << lg->EscapeForCMake(ge.Process(*ci));
+    os << " " << lg->EscapeForCMake(ge.Parse(*ci).Evaluate(mf, config));
     }
 
   // Finish the test command.

+ 29 - 2
Tests/RunCMake/GeneratorExpression/BadAND-stderr.txt

@@ -1,9 +1,18 @@
+CMake Error at BadAND.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<AND>
+
+  \$<AND> expression requires at least one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
 CMake Error at BadAND.cmake:1 \(add_custom_target\):
   Error evaluating generator expression:
 
     \$<AND:>
 
-  AND requires one or more comma-separated '0' or '1' values.
+  Parameters to \$<AND> must resolve to either '0' or '1'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 +
@@ -12,6 +21,24 @@ CMake Error at BadAND.cmake:1 \(add_custom_target\):
 
     \$<AND:,>
 
-  AND requires one or more comma-separated '0' or '1' values.
+  Parameters to \$<AND> must resolve to either '0' or '1'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadAND.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<AND:01>
+
+  Parameters to \$<AND> must resolve to either '0' or '1'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadAND.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<AND:nothing>
+
+  Parameters to \$<AND> must resolve to either '0' or '1'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$

+ 3 - 0
Tests/RunCMake/GeneratorExpression/BadAND.cmake

@@ -1,4 +1,7 @@
 add_custom_target(check ALL COMMAND check
+  $<AND>
   $<AND:>
   $<AND:,>
+  $<AND:01>
+  $<AND:nothing>
   VERBATIM)

+ 36 - 0
Tests/RunCMake/GeneratorExpression/BadCONFIG-stderr.txt

@@ -1,8 +1,44 @@
+CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<CONFIG>
+
+  \$<CONFIG> expression requires exactly one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
 CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
   Error evaluating generator expression:
 
     \$<CONFIG:.>
 
+  Expression syntax not recognized.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<CONFIG:Foo,Bar>
+
+  \$<CONFIG> expression requires exactly one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<CONFIG:Foo-Bar>
+
+  Expression syntax not recognized.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadCONFIG.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<CONFIG:Foo-Nested>
+
   Expression syntax not recognized.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$

+ 4 - 0
Tests/RunCMake/GeneratorExpression/BadCONFIG.cmake

@@ -1,3 +1,7 @@
 add_custom_target(check ALL COMMAND check
+  $<CONFIG>
   $<CONFIG:.>
+  $<CONFIG:Foo,Bar>
+  $<CONFIG:Foo-Bar>
+  $<$<CONFIG:Foo-Nested>:foo>
   VERBATIM)

+ 29 - 3
Tests/RunCMake/GeneratorExpression/BadNOT-stderr.txt

@@ -1,9 +1,17 @@
 CMake Error at BadNOT.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<NOT>
+
+  \$<NOT> expression requires exactly one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++CMake Error at BadNOT.cmake:1 \(add_custom_target\):
   Error evaluating generator expression:
 
     \$<NOT:>
 
-  NOT requires exactly one '0' or '1' value.
+  \$<NOT> parameter must resolve to exactly one '0' or '1' value.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 +
@@ -12,7 +20,7 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\):
 
     \$<NOT:,>
 
-  NOT requires exactly one '0' or '1' value.
+  \$<NOT> parameter must resolve to exactly one '0' or '1' value.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 +
@@ -21,6 +29,24 @@ CMake Error at BadNOT.cmake:1 \(add_custom_target\):
 
     \$<NOT:0,1>
 
-  NOT requires exactly one '0' or '1' value.
+  \$<NOT> expression requires exactly one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadNOT.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<NOT:01>
+
+  \$<NOT> parameter must resolve to exactly one '0' or '1' value.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadNOT.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<NOT:nothing>
+
+  \$<NOT> parameter must resolve to exactly one '0' or '1' value.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$

+ 3 - 0
Tests/RunCMake/GeneratorExpression/BadNOT.cmake

@@ -1,5 +1,8 @@
 add_custom_target(check ALL COMMAND check
+  $<NOT>
   $<NOT:>
   $<NOT:,>
   $<NOT:0,1>
+  $<NOT:01>
+  $<NOT:nothing>
   VERBATIM)

+ 29 - 2
Tests/RunCMake/GeneratorExpression/BadOR-stderr.txt

@@ -1,9 +1,18 @@
+CMake Error at BadOR.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<OR>
+
+  \$<OR> expression requires at least one parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
 CMake Error at BadOR.cmake:1 \(add_custom_target\):
   Error evaluating generator expression:
 
     \$<OR:>
 
-  OR requires one or more comma-separated '0' or '1' values.
+  Parameters to \$<OR> must resolve to either '0' or '1'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 +
@@ -12,6 +21,24 @@ CMake Error at BadOR.cmake:1 \(add_custom_target\):
 
     \$<OR:,>
 
-  OR requires one or more comma-separated '0' or '1' values.
+  Parameters to \$<OR> must resolve to either '0' or '1'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadOR.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<OR:01>
+
+  Parameters to \$<OR> must resolve to either '0' or '1'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at BadOR.cmake:1 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<OR:nothing>
+
+  Parameters to \$<OR> must resolve to either '0' or '1'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)$

+ 3 - 0
Tests/RunCMake/GeneratorExpression/BadOR.cmake

@@ -1,4 +1,7 @@
 add_custom_target(check ALL COMMAND check
+  $<OR>
   $<OR:>
   $<OR:,>
+  $<OR:01>
+  $<OR:nothing>
   VERBATIM)

+ 3 - 0
bootstrap

@@ -202,6 +202,9 @@ CMAKE_CXX_SOURCES="\
   cmInstallDirectoryGenerator \
   cmGeneratedFileStream \
   cmGeneratorTarget \
+  cmGeneratorExpressionEvaluator \
+  cmGeneratorExpressionLexer \
+  cmGeneratorExpressionParser \
   cmGeneratorExpression \
   cmGlobalGenerator \
   cmLocalGenerator \