Browse Source

Merge branch 'custom-command-generator-expressions' into resolve/tests-if-CYGWIN

The tests-if-CYGWIN topic made a change to Tests/Testing/CMakeLists.txt
in code that the custom-command-generator-expressions topic moved to the
Tests/PerConfig/CMakeLists.txt file.  Make the same change to the same
content in the new file.  (Only a small part of the file moved so rename
detection did not do this automatically.)
Brad King 15 years ago
parent
commit
853de2ed12

+ 3 - 0
Source/CMakeLists.txt

@@ -131,6 +131,8 @@ SET(SRCS
   cmComputeTargetDepends.cxx
   cmCustomCommand.cxx
   cmCustomCommand.h
+  cmCustomCommandGenerator.cxx
+  cmCustomCommandGenerator.h
   cmDefinitions.cxx
   cmDefinitions.h
   cmDepends.cxx
@@ -156,6 +158,7 @@ SET(SRCS
   cmDocumentationFormatterText.cxx
   cmDocumentationFormatterUsage.cxx
   cmDocumentationSection.cxx
+  cmDocumentGeneratorExpressions.h
   cmDocumentVariables.cxx
   cmDynamicLoader.cxx
   cmDynamicLoader.h

+ 2 - 13
Source/cmAddTestCommand.h

@@ -13,6 +13,7 @@
 #define cmAddTestCommand_h
 
 #include "cmCommand.h"
+#include "cmDocumentGeneratorExpressions.h"
 
 /** \class cmAddTestCommand
  * \brief Add a test to the lists of tests to run.
@@ -77,19 +78,7 @@ public:
       "\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"
+      CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS
       "Example usage:\n"
       "  add_test(NAME mytest\n"
       "           COMMAND testDriver --config $<CONFIGURATION>\n"

+ 24 - 3
Source/cmCustomCommand.cxx

@@ -11,6 +11,8 @@
 ============================================================================*/
 #include "cmCustomCommand.h"
 
+#include "cmMakefile.h"
+
 //----------------------------------------------------------------------------
 cmCustomCommand::cmCustomCommand()
 {
@@ -28,12 +30,14 @@ cmCustomCommand::cmCustomCommand(const cmCustomCommand& r):
   Comment(r.Comment),
   WorkingDirectory(r.WorkingDirectory),
   EscapeAllowMakeVars(r.EscapeAllowMakeVars),
-  EscapeOldStyle(r.EscapeOldStyle)
+  EscapeOldStyle(r.EscapeOldStyle),
+  Backtrace(new cmListFileBacktrace(*r.Backtrace))
 {
 }
 
 //----------------------------------------------------------------------------
-cmCustomCommand::cmCustomCommand(const std::vector<std::string>& outputs,
+cmCustomCommand::cmCustomCommand(cmMakefile* mf,
+                                 const std::vector<std::string>& outputs,
                                  const std::vector<std::string>& depends,
                                  const cmCustomCommandLines& commandLines,
                                  const char* comment,
@@ -45,10 +49,21 @@ cmCustomCommand::cmCustomCommand(const std::vector<std::string>& outputs,
   Comment(comment?comment:""),
   WorkingDirectory(workingDirectory?workingDirectory:""),
   EscapeAllowMakeVars(false),
-  EscapeOldStyle(true)
+  EscapeOldStyle(true),
+  Backtrace(new cmListFileBacktrace)
 {
   this->EscapeOldStyle = true;
   this->EscapeAllowMakeVars = false;
+  if(mf)
+    {
+    mf->GetBacktrace(*this->Backtrace);
+    }
+}
+
+//----------------------------------------------------------------------------
+cmCustomCommand::~cmCustomCommand()
+{
+  delete this->Backtrace;
 }
 
 //----------------------------------------------------------------------------
@@ -130,6 +145,12 @@ void cmCustomCommand::SetEscapeAllowMakeVars(bool b)
   this->EscapeAllowMakeVars = b;
 }
 
+//----------------------------------------------------------------------------
+cmListFileBacktrace const& cmCustomCommand::GetBacktrace() const
+{
+  return *this->Backtrace;
+}
+
 //----------------------------------------------------------------------------
 cmCustomCommand::ImplicitDependsList const&
 cmCustomCommand::GetImplicitDepends() const

+ 10 - 1
Source/cmCustomCommand.h

@@ -13,6 +13,8 @@
 #define cmCustomCommand_h
 
 #include "cmStandardIncludes.h"
+class cmMakefile;
+class cmListFileBacktrace;
 
 /** \class cmCustomCommand
  * \brief A class to encapsulate a custom command
@@ -27,12 +29,15 @@ public:
   cmCustomCommand(const cmCustomCommand& r);
 
   /** Main constructor specifies all information for the command.  */
-  cmCustomCommand(const std::vector<std::string>& outputs,
+  cmCustomCommand(cmMakefile* mf,
+                  const std::vector<std::string>& outputs,
                   const std::vector<std::string>& depends,
                   const cmCustomCommandLines& commandLines,
                   const char* comment,
                   const char* workingDirectory);
 
+  ~cmCustomCommand();
+
   /** Get the output file produced by the command.  */
   const std::vector<std::string>& GetOutputs() const;
 
@@ -63,6 +68,9 @@ public:
   bool GetEscapeAllowMakeVars() const;
   void SetEscapeAllowMakeVars(bool b);
 
+  /** Backtrace of the command that created this custom command.  */
+  cmListFileBacktrace const& GetBacktrace() const;
+
   typedef std::pair<cmStdString, cmStdString> ImplicitDependsPair;
   class ImplicitDependsList: public std::vector<ImplicitDependsPair> {};
   void SetImplicitDepends(ImplicitDependsList const&);
@@ -78,6 +86,7 @@ private:
   std::string WorkingDirectory;
   bool EscapeAllowMakeVars;
   bool EscapeOldStyle;
+  cmListFileBacktrace* Backtrace;
   ImplicitDependsList ImplicitDepends;
 };
 

+ 64 - 0
Source/cmCustomCommandGenerator.cxx

@@ -0,0 +1,64 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
+
+  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 "cmCustomCommandGenerator.h"
+
+#include "cmMakefile.h"
+#include "cmCustomCommand.h"
+#include "cmLocalGenerator.h"
+
+//----------------------------------------------------------------------------
+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())
+{
+}
+
+//----------------------------------------------------------------------------
+unsigned int cmCustomCommandGenerator::GetNumberOfCommands() const
+{
+  return static_cast<unsigned int>(this->CC.GetCommandLines().size());
+}
+
+//----------------------------------------------------------------------------
+std::string cmCustomCommandGenerator::GetCommand(unsigned int c) const
+{
+  std::string const& argv0 = this->CC.GetCommandLines()[c][0];
+  cmTarget* target = this->Makefile->FindTargetToUse(argv0.c_str());
+  if(target && target->GetType() == cmTarget::EXECUTABLE &&
+     (target->IsImported() || !this->Makefile->IsOn("CMAKE_CROSSCOMPILING")))
+    {
+    return target->GetLocation(this->Config);
+    }
+  return argv0;
+}
+
+//----------------------------------------------------------------------------
+void
+cmCustomCommandGenerator
+::AppendArguments(unsigned int c, std::string& cmd) const
+{
+  cmCustomCommandLine const& commandLine = this->CC.GetCommandLines()[c];
+  for(unsigned int j=1;j < commandLine.size(); ++j)
+    {
+    std::string const& arg = commandLine[j];
+    cmd += " ";
+    if(this->OldStyle)
+      {
+      cmd += this->LG->EscapeForShellOldStyle(arg.c_str());
+      }
+    else
+      {
+      cmd += this->LG->EscapeForShell(arg.c_str(), this->MakeVars);
+      }
+    }
+}

+ 37 - 0
Source/cmCustomCommandGenerator.h

@@ -0,0 +1,37 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
+
+  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 cmCustomCommandGenerator_h
+#define cmCustomCommandGenerator_h
+
+#include "cmStandardIncludes.h"
+
+class cmCustomCommand;
+class cmMakefile;
+class cmLocalGenerator;
+
+class cmCustomCommandGenerator
+{
+  cmCustomCommand const& CC;
+  const char* Config;
+  cmMakefile* Makefile;
+  cmLocalGenerator* LG;
+  bool OldStyle;
+  bool MakeVars;
+public:
+  cmCustomCommandGenerator(cmCustomCommand const& cc, const char* config,
+                           cmMakefile* mf);
+  unsigned int GetNumberOfCommands() const;
+  std::string GetCommand(unsigned int c) const;
+  void AppendArguments(unsigned int c, std::string& cmd) const;
+};
+
+#endif

+ 30 - 0
Source/cmDocumentGeneratorExpressions.h

@@ -0,0 +1,30 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
+
+  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 cmDocumentGeneratorExpressions_h
+#define cmDocumentGeneratorExpressions_h
+
+#define CM_DOCUMENT_COMMAND_GENERATOR_EXPRESSIONS                       \
+  "Generator expressions are evaluted during build system generation "  \
+  "to produce information specific to each 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"
+
+#endif

+ 1 - 1
Source/cmGlobalGenerator.cxx

@@ -1893,7 +1893,7 @@ cmTarget cmGlobalGenerator::CreateGlobalTarget(
   std::vector<std::string> no_outputs;
   std::vector<std::string> no_depends;
   // Store the custom command in the target.
-  cmCustomCommand cc(no_outputs, no_depends, *commandLines, 0,
+  cmCustomCommand cc(0, no_outputs, no_depends, *commandLines, 0,
                      workingDirectory);
   target.GetPostBuildCommands().push_back(cc);
   target.SetProperty("EchoString", message);

+ 13 - 30
Source/cmGlobalXCodeGenerator.cxx

@@ -18,6 +18,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmComputeLinkInformation.h"
 #include "cmSourceFile.h"
+#include "cmCustomCommandGenerator.h"
 
 #include <cmsys/auto_ptr.hxx>
 
@@ -1314,8 +1315,7 @@ void  cmGlobalXCodeGenerator
     cmCustomCommand const& cc = *i;
     if(!cc.GetCommandLines().empty())
       {
-      bool escapeOldStyle = cc.GetEscapeOldStyle();
-      bool escapeAllowMakeVars = cc.GetEscapeAllowMakeVars();
+      cmCustomCommandGenerator ccg(cc, configName, this->CurrentMakefile);
       makefileStream << "\n";
       const std::vector<std::string>& outputs = cc.GetOutputs();
       if(!outputs.empty())
@@ -1334,11 +1334,13 @@ void  cmGlobalXCodeGenerator
           cc.GetDepends().begin();
           d != cc.GetDepends().end(); ++d)
         {
-        std::string dep =
-          this->CurrentLocalGenerator->GetRealDependency(d->c_str(),
-                                                         configName);
-        makefileStream << "\\\n" << this
-          ->ConvertToRelativeForMake(dep.c_str());
+        std::string dep;
+        if(this->CurrentLocalGenerator
+           ->GetRealDependency(d->c_str(), configName, dep))
+          {
+          makefileStream << "\\\n" <<
+            this->ConvertToRelativeForMake(dep.c_str());
+          }
         }
       makefileStream << "\n";
 
@@ -1346,20 +1348,15 @@ void  cmGlobalXCodeGenerator
         {
         std::string echo_cmd = "echo ";
         echo_cmd += (this->CurrentLocalGenerator->
-                     EscapeForShell(comment, escapeAllowMakeVars));
+                     EscapeForShell(comment, cc.GetEscapeAllowMakeVars()));
         makefileStream << "\t" << echo_cmd.c_str() << "\n";
         }
 
       // Add each command line to the set of commands.
-      for(cmCustomCommandLines::const_iterator cl =
-          cc.GetCommandLines().begin();
-          cl != cc.GetCommandLines().end(); ++cl)
+      for(unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c)
         {
         // Build the command line in a single string.
-        const cmCustomCommandLine& commandLine = *cl;
-        std::string cmd2 = this->CurrentLocalGenerator
-                         ->GetRealLocation(commandLine[0].c_str(), configName);
-
+        std::string cmd2 = ccg.GetCommand(c);
         cmSystemTools::ReplaceString(cmd2, "/./", "/");
         cmd2 = this->ConvertToRelativeForMake(cmd2.c_str());
         std::string cmd;
@@ -1370,21 +1367,7 @@ void  cmGlobalXCodeGenerator
           cmd += " && ";
           }
         cmd += cmd2;
-        for(unsigned int j=1; j < commandLine.size(); ++j)
-          {
-          cmd += " ";
-          if(escapeOldStyle)
-            {
-            cmd += (this->CurrentLocalGenerator
-                ->EscapeForShellOldStyle(commandLine[j].c_str()));
-            }
-          else
-            {
-            cmd += (this->CurrentLocalGenerator->
-                EscapeForShell(commandLine[j].c_str(),
-                               escapeAllowMakeVars));
-            }
-          }
+        ccg.AppendArguments(c, cmd);
         makefileStream << "\t" << cmd.c_str() << "\n";
         }
       }

+ 18 - 32
Source/cmLocalGenerator.cxx

@@ -1818,8 +1818,9 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
 }
 
 //----------------------------------------------------------------------------
-std::string cmLocalGenerator::GetRealDependency(const char* inName,
-                                                const char* config)
+bool cmLocalGenerator::GetRealDependency(const char* inName,
+                                         const char* config,
+                                         std::string& dep)
 {
   // Older CMake code may specify the dependency using the target
   // output file rather than the target name.  Such code would have
@@ -1855,7 +1856,8 @@ std::string cmLocalGenerator::GetRealDependency(const char* inName,
         // it is a full path to a depend that has the same name
         // as a target but is in a different location so do not use
         // the target as the depend
-        return inName;
+        dep = inName;
+        return true;
         }
       }
     switch (target->GetType())
@@ -1869,15 +1871,16 @@ std::string cmLocalGenerator::GetRealDependency(const char* inName,
         // Get the location of the target's output file and depend on it.
         if(const char* location = target->GetLocation(config))
           {
-          return location;
+          dep = location;
+          return true;
           }
         }
         break;
       case cmTarget::UTILITY:
       case cmTarget::GLOBAL_TARGET:
-        // Depending on a utility target may not work but just trust
-        // the user to have given a valid name.
-        return inName;
+        // A utility target has no file on which to depend.  This was listed
+        // only to get the target-level dependency.
+        return false;
       case cmTarget::INSTALL_FILES:
       case cmTarget::INSTALL_PROGRAMS:
       case cmTarget::INSTALL_DIRECTORY:
@@ -1889,41 +1892,24 @@ std::string cmLocalGenerator::GetRealDependency(const char* inName,
   if(cmSystemTools::FileIsFullPath(inName))
     {
     // This is a full path.  Return it as given.
-    return inName;
+    dep = inName;
+    return true;
     }
 
   // Check for a source file in this directory that matches the
   // dependency.
   if(cmSourceFile* sf = this->Makefile->GetSource(inName))
     {
-    name = sf->GetFullPath();
-    return name;
+    dep = sf->GetFullPath();
+    return true;
     }
 
   // Treat the name as relative to the source directory in which it
   // was given.
-  name = this->Makefile->GetCurrentDirectory();
-  name += "/";
-  name += inName;
-  return name;
-}
-
-//----------------------------------------------------------------------------
-std::string cmLocalGenerator::GetRealLocation(const char* inName,
-                                              const char* config)
-{
-  std::string outName=inName;
-  // Look for a CMake target with the given name, which is an executable 
-  // and which can be run
-  cmTarget* target = this->Makefile->FindTargetToUse(inName);
-  if ((target != 0)
-       && (target->GetType() == cmTarget::EXECUTABLE)
-       && ((this->Makefile->IsOn("CMAKE_CROSSCOMPILING") == false) 
-            || (target->IsImported() == true)))
-    {
-    outName = target->GetLocation( config );
-    }
-  return outName;
+  dep = this->Makefile->GetCurrentDirectory();
+  dep += "/";
+  dep += inName;
+  return true;
 }
 
 //----------------------------------------------------------------------------

+ 3 - 6
Source/cmLocalGenerator.h

@@ -158,18 +158,15 @@ public:
 
   /** Translate a dependency as given in CMake code to the name to
       appear in a generated build file.  If the given name is that of
+      a utility target, returns false.  If the given name is that of
       a CMake target it will be transformed to the real output
       location of that target for the given configuration.  If the
       given name is the full path to a file it will be returned.
       Otherwise the name is treated as a relative path with respect to
       the source directory of this generator.  This should only be
       used for dependencies of custom commands.  */
-  std::string GetRealDependency(const char* name, const char* config);
-  
-  /** Translate a command as given in CMake code to the location of the 
-      executable if the command is the name of a CMake executable target.
-      If that's not the case, just return the original name. */
-  std::string GetRealLocation(const char* inName, const char* config);
+  bool GetRealDependency(const char* name, const char* config,
+                         std::string& dep);
 
   ///! for existing files convert to output path and short path if spaces
   std::string ConvertToOutputForExisting(const char* remote,

+ 13 - 23
Source/cmLocalUnixMakefileGenerator3.cxx

@@ -19,6 +19,7 @@
 #include "cmake.h"
 #include "cmVersion.h"
 #include "cmFileTimeComparison.h"
+#include "cmCustomCommandGenerator.h"
 
 // Include dependency scanners for supported languages.  Only the
 // C/C++ scanner is needed for bootstrapping CMake.
@@ -902,9 +903,12 @@ cmLocalUnixMakefileGenerator3
       d != cc.GetDepends().end(); ++d)
     {
     // Lookup the real name of the dependency in case it is a CMake target.
-    std::string dep = this->GetRealDependency
-      (d->c_str(), this->ConfigurationName.c_str());
-    depends.push_back(dep);
+    std::string dep;
+    if(this->GetRealDependency(d->c_str(), this->ConfigurationName.c_str(),
+                               dep))
+      {
+      depends.push_back(dep);
+      }
     }
 }
 
@@ -958,18 +962,15 @@ cmLocalUnixMakefileGenerator3
     {
     *content << dir;
     }
-  bool escapeOldStyle = cc.GetEscapeOldStyle();
-  bool escapeAllowMakeVars = cc.GetEscapeAllowMakeVars();
+  cmCustomCommandGenerator ccg(cc, this->ConfigurationName.c_str(),
+                               this->Makefile);
 
   // Add each command line to the set of commands.
   std::vector<std::string> commands1;
-  for(cmCustomCommandLines::const_iterator cl = cc.GetCommandLines().begin();
-      cl != cc.GetCommandLines().end(); ++cl)
+  for(unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c)
     {
     // Build the command line in a single string.
-    const cmCustomCommandLine& commandLine = *cl;
-    std::string cmd = GetRealLocation(commandLine[0].c_str(),
-                                      this->ConfigurationName.c_str());
+    std::string cmd = ccg.GetCommand(c);
     if (cmd.size())
       {
       // Use "call " before any invocations of .bat or .cmd files
@@ -1022,19 +1023,8 @@ cmLocalUnixMakefileGenerator3
       std::string launcher =
         this->MakeLauncher(cc, target, workingDir? NONE : START_OUTPUT);
       cmd = launcher + this->Convert(cmd.c_str(),NONE,SHELL);
-      for(unsigned int j=1; j < commandLine.size(); ++j)
-        {
-        cmd += " ";
-        if(escapeOldStyle)
-          {
-          cmd += this->EscapeForShellOldStyle(commandLine[j].c_str());
-          }
-        else
-          {
-          cmd += this->EscapeForShell(commandLine[j].c_str(),
-                                      escapeAllowMakeVars);
-          }
-        }
+
+      ccg.AppendArguments(c, cmd);
       if(content)
         {
         // Rule content does not include the launcher.

+ 9 - 18
Source/cmLocalVisualStudio6Generator.cxx

@@ -65,13 +65,7 @@ public:
       {
       this->Code += "\\\n\t";
       }
-    this->Code +=
-      this->LG->ConstructScript(cc.GetCommandLines(),
-                                cc.GetWorkingDirectory(),
-                                this->Config,
-                                cc.GetEscapeOldStyle(),
-                                cc.GetEscapeAllowMakeVars(),
-                                "\\\n\t");
+    this->Code += this->LG->ConstructScript(cc, this->Config, "\\\n\t");
     }
 private:
   cmLocalVisualStudio6Generator* LG;
@@ -659,12 +653,7 @@ cmLocalVisualStudio6Generator
     {
     std::string config = this->GetConfigName(*i);
     std::string script =
-      this->ConstructScript(command.GetCommandLines(), 
-                            command.GetWorkingDirectory(),
-                            config.c_str(),
-                            command.GetEscapeOldStyle(),
-                            command.GetEscapeAllowMakeVars(),
-                            "\\\n\t");
+      this->ConstructScript(command, config.c_str(), "\\\n\t");
       
     if (i == this->Configurations.begin())
       {
@@ -686,10 +675,12 @@ cmLocalVisualStudio6Generator
         ++d)
       {
       // Lookup the real name of the dependency in case it is a CMake target.
-      std::string dep = this->GetRealDependency(d->c_str(),
-                                                config.c_str());
-      fout << "\\\n\t" <<
-        this->ConvertToOptionallyRelativeOutputPath(dep.c_str());
+      std::string dep;
+      if(this->GetRealDependency(d->c_str(), config.c_str(), dep))
+        {
+        fout << "\\\n\t" <<
+          this->ConvertToOptionallyRelativeOutputPath(dep.c_str());
+        }
       }
     fout << "\n";
 
@@ -847,7 +838,7 @@ cmLocalVisualStudio6Generator::MaybeCreateOutputDir(cmTarget& target,
   std::vector<std::string> no_depends;
   cmCustomCommandLines commands;
   commands.push_back(command);
-  pcc.reset(new cmCustomCommand(no_output, no_depends, commands, 0, 0));
+  pcc.reset(new cmCustomCommand(0, no_output, no_depends, commands, 0, 0));
   pcc->SetEscapeOldStyle(false);
   pcc->SetEscapeAllowMakeVars(true);
   return pcc;

+ 8 - 15
Source/cmLocalVisualStudio7Generator.cxx

@@ -546,12 +546,7 @@ public:
       {
       this->Stream << this->LG->EscapeForXML("\n");
       }
-    std::string script =
-      this->LG->ConstructScript(cc.GetCommandLines(),
-                                cc.GetWorkingDirectory(),
-                                this->Config,
-                                cc.GetEscapeOldStyle(),
-                                cc.GetEscapeAllowMakeVars());
+    std::string script = this->LG->ConstructScript(cc, this->Config);
     this->Stream << this->LG->EscapeForXML(script.c_str());
     }
 private:
@@ -1591,12 +1586,7 @@ WriteCustomRule(std::ostream& fout,
            << this->EscapeForXML(fc.CompileFlags.c_str()) << "\"/>\n";
       }
 
-    std::string script = 
-      this->ConstructScript(command.GetCommandLines(),
-                            command.GetWorkingDirectory(),
-                            i->c_str(),
-                            command.GetEscapeOldStyle(),
-                            command.GetEscapeAllowMakeVars());
+    std::string script = this->ConstructScript(command, i->c_str());
     fout << "\t\t\t\t\t<Tool\n"
          << "\t\t\t\t\tName=\"" << customTool << "\"\n"
          << "\t\t\t\t\tDescription=\"" 
@@ -1624,9 +1614,12 @@ WriteCustomRule(std::ostream& fout,
           ++d)
         {
         // Get the real name of the dependency in case it is a CMake target.
-        std::string dep = this->GetRealDependency(d->c_str(), i->c_str());
-        fout << this->ConvertToXMLOutputPath(dep.c_str())
-             << ";";
+        std::string dep;
+        if(this->GetRealDependency(d->c_str(), i->c_str(), dep))
+          {
+          fout << this->ConvertToXMLOutputPath(dep.c_str())
+               << ";";
+          }
         }
       }
     fout << "\"\n";

+ 13 - 34
Source/cmLocalVisualStudioGenerator.cxx

@@ -14,6 +14,7 @@
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
 #include "cmSystemTools.h"
+#include "cmCustomCommandGenerator.h"
 #include "windows.h"
 
 //----------------------------------------------------------------------------
@@ -52,7 +53,7 @@ cmLocalVisualStudioGenerator::MaybeCreateImplibDir(cmTarget& target,
   std::vector<std::string> no_depends;
   cmCustomCommandLines commands;
   commands.push_back(command);
-  pcc.reset(new cmCustomCommand(no_output, no_depends, commands, 0, 0));
+  pcc.reset(new cmCustomCommand(0, no_output, no_depends, commands, 0, 0));
   pcc->SetEscapeOldStyle(false);
   pcc->SetEscapeAllowMakeVars(true);
   return pcc;
@@ -151,13 +152,15 @@ void cmLocalVisualStudioGenerator::ComputeObjectNameRequirements
 //----------------------------------------------------------------------------
 std::string
 cmLocalVisualStudioGenerator
-::ConstructScript(const cmCustomCommandLines& commandLines,
-                  const char* workingDirectory,
+::ConstructScript(cmCustomCommand const& cc,
                   const char* configName,
-                  bool escapeOldStyle,
-                  bool escapeAllowMakeVars,
                   const char* newline_text)
 {
+  const cmCustomCommandLines& commandLines = cc.GetCommandLines();
+  const char* workingDirectory = cc.GetWorkingDirectory();
+  cmCustomCommandGenerator ccg(cc, configName, this->Makefile);
+  RelativeRoot relativeRoot = workingDirectory? NONE : START_OUTPUT;
+
   // Avoid leading or trailing newlines.
   const char* newline = "";
 
@@ -196,40 +199,16 @@ cmLocalVisualStudioGenerator
       }
     }
   // Write each command on a single line.
-  for(cmCustomCommandLines::const_iterator cl = commandLines.begin();
-      cl != commandLines.end(); ++cl)
+  for(unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c)
     {
     // Start a new line.
     script += newline;
     newline = newline_text;
 
-    // Start with the command name.
-    const cmCustomCommandLine& commandLine = *cl;
-    std::string commandName = this->GetRealLocation(commandLine[0].c_str(), 
-                                                    configName);
-    if(!workingDirectory)
-      {
-      script += this->Convert(commandName.c_str(),START_OUTPUT,SHELL);
-      }
-    else
-      {
-      script += this->Convert(commandName.c_str(),NONE,SHELL);
-      }
-
-    // Add the arguments.
-    for(unsigned int j=1;j < commandLine.size(); ++j)
-      {
-      script += " ";
-      if(escapeOldStyle)
-        {
-        script += this->EscapeForShellOldStyle(commandLine[j].c_str());
-        }
-      else
-        {
-        script += this->EscapeForShell(commandLine[j].c_str(),
-                                       escapeAllowMakeVars);
-        }
-      }
+    // Add this command line.
+    std::string cmd = ccg.GetCommand(c);
+    script += this->Convert(cmd.c_str(), relativeRoot, SHELL);
+    ccg.AppendArguments(c, script);
 
     // After each custom command, check for an error result.
     // If there was an error, jump to the VCReportError label,

+ 2 - 4
Source/cmLocalVisualStudioGenerator.h

@@ -18,6 +18,7 @@
 
 class cmSourceFile;
 class cmSourceGroup;
+class cmCustomCommand;
 
 /** \class cmLocalVisualStudioGenerator
  * \brief Base class for Visual Studio generators.
@@ -31,11 +32,8 @@ public:
   cmLocalVisualStudioGenerator();
   virtual ~cmLocalVisualStudioGenerator();
   /** Construct a script from the given list of command lines.  */
-  std::string ConstructScript(const cmCustomCommandLines& commandLines,
-                              const char* workingDirectory,
+  std::string ConstructScript(cmCustomCommand const& cc,
                               const char* configName,
-                              bool escapeOldStyle,
-                              bool escapeAllowMakeVars,
                               const char* newline = "\n");
 
 protected:

+ 3 - 2
Source/cmMakefile.cxx

@@ -827,7 +827,8 @@ cmMakefile::AddCustomCommandToTarget(const char* target,
     {
     // Add the command to the appropriate build step for the target.
     std::vector<std::string> no_output;
-    cmCustomCommand cc(no_output, depends, commandLines, comment, workingDir);
+    cmCustomCommand cc(this, no_output, depends,
+                       commandLines, comment, workingDir);
     cc.SetEscapeOldStyle(escapeOldStyle);
     cc.SetEscapeAllowMakeVars(true);
     switch(type)
@@ -947,7 +948,7 @@ cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs,
   if(file)
     {
     cmCustomCommand* cc =
-      new cmCustomCommand(outputs, depends2, commandLines,
+      new cmCustomCommand(this, outputs, depends2, commandLines,
                           comment, workingDir);
     cc->SetEscapeOldStyle(escapeOldStyle);
     cc->SetEscapeAllowMakeVars(true);

+ 4 - 4
Source/cmTarget.cxx

@@ -1357,8 +1357,8 @@ bool cmTargetTraceDependencies::IsUtility(std::string const& dep)
     util = cmSystemTools::GetFilenameWithoutLastExtension(util);
     }
 
-  // Check for a non-imported target with this name.
-  if(cmTarget* t = this->GlobalGenerator->FindTarget(0, util.c_str()))
+  // Check for a target with this name.
+  if(cmTarget* t = this->Makefile->FindTargetToUse(util.c_str()))
     {
     // If we find the target and the dep was given as a full path,
     // then make sure it was not a full path to something else, and
@@ -1406,8 +1406,8 @@ cmTargetTraceDependencies
       cit != cc.GetCommandLines().end(); ++cit)
     {
     std::string const& command = *cit->begin();
-    // Look for a non-imported target with this name.
-    if(cmTarget* t = this->GlobalGenerator->FindTarget(0, command.c_str()))
+    // Check for a target with this name.
+    if(cmTarget* t = this->Makefile->FindTargetToUse(command.c_str()))
       {
       if(t->GetType() == cmTarget::EXECUTABLE)
         {

+ 8 - 18
Source/cmVisualStudio10TargetGenerator.cxx

@@ -367,13 +367,7 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source,
       i != configs->end(); ++i)
     {
     std::string script =
-      cmVS10EscapeXML(
-        lg->ConstructScript(command.GetCommandLines(),
-                            command.GetWorkingDirectory(),
-                            i->c_str(),
-                            command.GetEscapeOldStyle(),
-                            command.GetEscapeAllowMakeVars())
-        );
+      cmVS10EscapeXML(lg->ConstructScript(command, i->c_str()));
     this->WritePlatformConfigTag("Message",i->c_str(), 3);
     (*this->BuildFileStream ) << cmVS10EscapeXML(comment) << "</Message>\n";
     this->WritePlatformConfigTag("Command", i->c_str(), 3);
@@ -386,10 +380,12 @@ cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source,
         d != command.GetDepends().end(); 
         ++d)
       {
-      std::string dep = this->LocalGenerator->
-        GetRealDependency(d->c_str(), i->c_str());
-      this->ConvertToWindowsSlash(dep);
-      (*this->BuildFileStream ) << ";" << dep;
+      std::string dep;
+      if(this->LocalGenerator->GetRealDependency(d->c_str(), i->c_str(), dep))
+        {
+        this->ConvertToWindowsSlash(dep);
+        (*this->BuildFileStream ) << ";" << dep;
+        }
       }
     (*this->BuildFileStream ) << ";%(AdditionalInputs)</AdditionalInputs>\n";
     this->WritePlatformConfigTag("Outputs", i->c_str(), 3);
@@ -1458,13 +1454,7 @@ void cmVisualStudio10TargetGenerator::WriteEvent(
     script += pre;
     pre = "\n";
     script +=
-      cmVS10EscapeXML(
-        lg->ConstructScript(command.GetCommandLines(),
-                            command.GetWorkingDirectory(),
-                            configName.c_str(),
-                            command.GetEscapeOldStyle(),
-                            command.GetEscapeAllowMakeVars())
-        );
+      cmVS10EscapeXML(lg->ConstructScript(command, configName.c_str()));
     }
   comment = cmVS10EscapeComment(comment);
   this->WriteString("<Message>",3);

+ 1 - 4
Tests/CustomCommand/CMakeLists.txt

@@ -130,6 +130,7 @@ ADD_CUSTOM_COMMAND(
 ################################################################
 ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_BINARY_DIR}/foo.pre
   DEPENDS ${PROJECT_SOURCE_DIR}/foo.in
+          TDocument # Ensure doc1.h generates before this target
   COMMAND   ${CMAKE_COMMAND}  
   ARGS      -E copy ${PROJECT_SOURCE_DIR}/foo.in 
   ${PROJECT_BINARY_DIR}/foo.pre
@@ -181,10 +182,6 @@ ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_BINARY_DIR}/generated.c
 
 TARGET_LINK_LIBRARIES(CustomCommand GeneratedHeader)
 
-# must add a dependency on TDocument otherwise it might never build and 
-# the CustomCommand executable really needs doc1.h
-ADD_DEPENDENCIES(CustomCommand TDocument)
-
 ##############################################################################
 # Test for using just the target name as executable in the COMMAND
 # section. Has to be recognized and replaced by CMake with the output

+ 45 - 5
Tests/ExportImport/Import/A/CMakeLists.txt

@@ -75,24 +75,64 @@ foreach(c DEBUG RELWITHDEBINFO)
   set_property(TARGET imp_testExe1b PROPERTY COMPILE_DEFINITIONS_${c} EXE_DBG)
 endforeach(c)
 
+#-----------------------------------------------------------------------------
 # Create a custom target to generate a header for the libraries below.
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+# Drive the header generation through an indirect chain of imported
+# target dependencies.
+
+# testLib2tmp1.h
 add_custom_command(
-  OUTPUT testLib2.h
+  OUTPUT testLib2tmp1.h
   VERBATIM COMMAND
-  ${CMAKE_COMMAND} -E echo "extern int testLib2(void);" > testLib2.h
+    ${CMAKE_COMMAND} -E echo "extern int testLib2(void);" > testLib2tmp1.h
+  )
+
+# hdr_testLib2tmp1 needs testLib2tmp1.h
+add_custom_target(hdr_testLib2tmp1 DEPENDS testLib2tmp1.h)
+
+# exp_testExe2 needs hdr_testLib2tmp1
+add_dependencies(exp_testExe2 hdr_testLib2tmp1)
+
+# testLib2tmp.h needs exp_testExe2
+add_custom_command(
+  OUTPUT testLib2tmp.h
+  VERBATIM COMMAND exp_testExe2
+  COMMAND ${CMAKE_COMMAND} -E copy testLib2tmp1.h testLib2tmp.h
   )
+
+# hdr_testLib2tmp needs testLib2tmp.h
+add_custom_target(hdr_testLib2tmp DEPENDS testLib2tmp.h)
+
+add_library(dep_testLib2tmp UNKNOWN IMPORTED)
+set_property(TARGET dep_testLib2tmp PROPERTY
+  IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/testLib2tmp.h)
+
+# dep_testLib2tmp needs hdr_testLib2tmp
+add_dependencies(dep_testLib2tmp hdr_testLib2tmp)
+
+# testLib2.h needs dep_testLib2tmp
+add_custom_command(
+  OUTPUT testLib2.h
+  VERBATIM COMMAND ${CMAKE_COMMAND} -E copy testLib2tmp.h testLib2.h
+  DEPENDS dep_testLib2tmp
+  )
+
+# hdr_testLib2 needs testLib2.h
 add_custom_target(hdr_testLib2 DEPENDS testLib2.h)
 
-# Drive the header generation through an indirect chain of imported
-# target dependencies.
 add_library(dep_testLib2 UNKNOWN IMPORTED)
+
+# dep_testLib2 needs hdr_testLib2
 add_dependencies(dep_testLib2 hdr_testLib2)
+
+# exp_testLib2 and bld_testLib2 both need dep_testLib2
 add_dependencies(bld_testLib2 dep_testLib2)
 add_dependencies(exp_testLib2 dep_testLib2)
 
+#-----------------------------------------------------------------------------
 # Create a library to be linked by another directory in this project
 # to test transitive linking to otherwise invisible imported targets.
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
 add_library(imp_lib1 STATIC imp_lib1.c)
 target_link_libraries(imp_lib1 exp_testLib2)
 add_library(imp_lib1b STATIC imp_lib1.c)

+ 33 - 0
Tests/PerConfig/CMakeLists.txt

@@ -0,0 +1,33 @@
+project(PerConfig C)
+
+# Targets with per-configuration names.
+ADD_LIBRARY(pcStatic STATIC pcStatic.c)
+SET_PROPERTY(TARGET pcStatic PROPERTY RELEASE_POSTFIX -opt)
+SET_PROPERTY(TARGET pcStatic PROPERTY DEBUG_POSTFIX -dbg)
+ADD_LIBRARY(pcShared SHARED pcShared.c)
+SET_PROPERTY(TARGET pcShared PROPERTY RELEASE_POSTFIX -opt)
+SET_PROPERTY(TARGET pcShared PROPERTY DEBUG_POSTFIX -dbg)
+SET_PROPERTY(TARGET pcShared PROPERTY VERSION 1.2)
+SET_PROPERTY(TARGET pcShared PROPERTY SOVERSION 3)
+IF(UNIX AND NOT CYGWIN)
+  SET(soname_file -DpcShared_soname_file=$<TARGET_SONAME_FILE:pcShared>)
+ENDIF()
+ADD_EXECUTABLE(perconfig perconfig.c)
+TARGET_LINK_LIBRARIES(perconfig pcStatic pcShared)
+SET_PROPERTY(TARGET perconfig PROPERTY RELEASE_POSTFIX -opt)
+SET_PROPERTY(TARGET perconfig PROPERTY DEBUG_POSTFIX -dbg)
+
+SET(PerConfig_COMMAND
+  ${CMAKE_COMMAND}
+    -Dconfiguration=$<CONFIGURATION>
+    -Dperconfig_file_dir=$<TARGET_FILE_DIR:perconfig>
+    -Dperconfig_file_name=$<TARGET_FILE_NAME:perconfig>
+    -Dperconfig_file=$<TARGET_FILE:perconfig>
+    -DpcStatic_file=$<TARGET_FILE:pcStatic>
+    -DpcStatic_linker_file=$<TARGET_LINKER_FILE:pcStatic>
+    -DpcShared_file=$<TARGET_FILE:pcShared>
+    -DpcShared_linker_file=$<TARGET_LINKER_FILE:pcShared>
+    ${soname_file}
+    -P ${PerConfig_SOURCE_DIR}/perconfig.cmake
+    )
+SET(PerConfig_COMMAND "${PerConfig_COMMAND}" PARENT_SCOPE)

+ 0 - 0
Tests/Testing/pcShared.c → Tests/PerConfig/pcShared.c


+ 0 - 0
Tests/Testing/pcShared.h → Tests/PerConfig/pcShared.h


+ 0 - 0
Tests/Testing/pcStatic.c → Tests/PerConfig/pcStatic.c


+ 0 - 0
Tests/Testing/perconfig.c → Tests/PerConfig/perconfig.c


+ 0 - 0
Tests/Testing/driver.cmake → Tests/PerConfig/perconfig.cmake


+ 3 - 31
Tests/Testing/CMakeLists.txt

@@ -53,35 +53,7 @@ ADD_TEST(testing.1 ${Testing_BINARY_DIR}/bin/testing)
 #
 ADD_SUBDIRECTORY(Sub/Sub2)
 
-# Per-config target name test.
-ADD_LIBRARY(pcStatic STATIC pcStatic.c)
-SET_PROPERTY(TARGET pcStatic PROPERTY RELEASE_POSTFIX -opt)
-SET_PROPERTY(TARGET pcStatic PROPERTY DEBUG_POSTFIX -dbg)
-ADD_LIBRARY(pcShared SHARED pcShared.c)
-SET_PROPERTY(TARGET pcShared PROPERTY RELEASE_POSTFIX -opt)
-SET_PROPERTY(TARGET pcShared PROPERTY DEBUG_POSTFIX -dbg)
-SET_PROPERTY(TARGET pcShared PROPERTY VERSION 1.2)
-SET_PROPERTY(TARGET pcShared PROPERTY SOVERSION 3)
-IF(UNIX AND NOT CYGWIN)
-  SET(soname_file -DpcShared_soname_file=$<TARGET_SONAME_FILE:pcShared>)
-ENDIF()
-ADD_EXECUTABLE(perconfig perconfig.c)
-TARGET_LINK_LIBRARIES(perconfig pcStatic pcShared)
-SET_PROPERTY(TARGET perconfig PROPERTY RELEASE_POSTFIX -opt)
-SET_PROPERTY(TARGET perconfig PROPERTY DEBUG_POSTFIX -dbg)
+# Per-config target name and generator expressions.
+ADD_SUBDIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/../PerConfig PerConfig)
 ADD_TEST(NAME testing.perconfig COMMAND perconfig)
-
-# Test using a driver script with generator expressions.
-ADD_TEST(NAME testing.driver
-  COMMAND ${CMAKE_COMMAND}
-    -Dconfiguration=$<CONFIGURATION>
-    -Dperconfig_file_dir=$<TARGET_FILE_DIR:perconfig>
-    -Dperconfig_file_name=$<TARGET_FILE_NAME:perconfig>
-    -Dperconfig_file=$<TARGET_FILE:perconfig>
-    -DpcStatic_file=$<TARGET_FILE:pcStatic>
-    -DpcStatic_linker_file=$<TARGET_LINKER_FILE:pcStatic>
-    -DpcShared_file=$<TARGET_FILE:pcShared>
-    -DpcShared_linker_file=$<TARGET_LINKER_FILE:pcShared>
-    ${soname_file}
-    -P ${Testing_SOURCE_DIR}/driver.cmake
-  )
+ADD_TEST(NAME testing.driver COMMAND ${PerConfig_COMMAND})

+ 1 - 0
bootstrap

@@ -215,6 +215,7 @@ CMAKE_CXX_SOURCES="\
   cmTarget \
   cmTest \
   cmCustomCommand \
+  cmCustomCommandGenerator \
   cmDocumentVariables \
   cmCacheManager \
   cmListFileCache \