浏览代码

yikes added new custom command support

Ken Martin 22 年之前
父节点
当前提交
ba68f771b3

+ 6 - 0
Source/CMakeLists.txt

@@ -159,6 +159,12 @@ IF(BUILD_TESTING)
     ADD_TEST(DumpDocumentation ${EXECUTABLE_OUTPUT_PATH}/DumpDocumentation
       --all-for-coverage)
 
+    ADD_TEST(CustomCommand ${EXECUTABLE_OUTPUT_PATH}/cmaketest 
+      ${CMake_SOURCE_DIR}/Tests/CustomCommand
+      ${CMake_BINARY_DIR}/Tests/CustomCommand 
+      CustomCommand
+      ${CMake_BINARY_DIR}/Tests/CustomCommand/bin)
+
     ADD_TEST(SystemInformation ${EXECUTABLE_OUTPUT_PATH}/cmaketest 
       ${CMake_SOURCE_DIR}/Tests/SystemInformation
       ${CMake_BINARY_DIR}/Tests/SystemInformation 

+ 57 - 12
Source/cmAddCustomCommandCommand.cxx

@@ -15,14 +15,14 @@
 
 =========================================================================*/
 #include "cmAddCustomCommandCommand.h"
-
+#include "cmTarget.h"
 
 // cmAddCustomCommandCommand
 bool cmAddCustomCommandCommand::InitialPass(std::vector<std::string> const& args)
 {
   /* Let's complain at the end of this function about the lack of a particular
-     arg. For the moment, let's say that COMMAND, TARGET are always 
-     required.
+     arg. For the moment, let's say that COMMAND, and either TARGET or SOURCE
+     are required.
   */
   if (args.size() < 4)
     {
@@ -30,15 +30,19 @@ bool cmAddCustomCommandCommand::InitialPass(std::vector<std::string> const& args
       return false;
     }
 
-  std::string source, command, target, comment;
+  std::string source, command, target, comment, output, main_dependency;
   std::vector<std::string> command_args, depends, outputs;
 
+  cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD;
+  
   enum tdoing {
     doing_source,
     doing_command,
     doing_target,
     doing_args,
     doing_depends,
+    doing_main_dependency,
+    doing_output,
     doing_outputs,
     doing_comment,
     doing_nothing
@@ -58,6 +62,18 @@ bool cmAddCustomCommandCommand::InitialPass(std::vector<std::string> const& args
       {
       doing = doing_command;
       }
+    else if(copy == "PRE_BUILD")
+      {
+      cctype = cmTarget::PRE_BUILD;
+      }
+    else if(copy == "PRE_LINK")
+      {
+      cctype = cmTarget::PRE_LINK;
+      }
+    else if(copy == "POST_BUILD")
+      {
+      cctype = cmTarget::POST_BUILD;
+      }
     else if(copy == "TARGET")
       {
       doing = doing_target;
@@ -74,6 +90,14 @@ bool cmAddCustomCommandCommand::InitialPass(std::vector<std::string> const& args
       {
       doing = doing_outputs;
       }
+    else if (copy == "OUTPUT")
+      {
+      doing = doing_output;
+      }
+    else if (copy == "MAIN_DEPENDENCY")
+      {
+      doing = doing_main_dependency;
+      }
     else if (copy == "COMMENT")
       {
       doing = doing_comment;
@@ -85,6 +109,12 @@ bool cmAddCustomCommandCommand::InitialPass(std::vector<std::string> const& args
         case doing_source:
           source = copy;
           break;
+        case doing_output:
+          output = copy;
+          break;
+        case doing_main_dependency:
+          main_dependency = copy;
+          break;
         case doing_command:
           command = copy;
           break;
@@ -114,21 +144,36 @@ bool cmAddCustomCommandCommand::InitialPass(std::vector<std::string> const& args
      For the moment, let's say that COMMAND, TARGET are always 
      required.
   */
-  
-  if(target.empty())
+  if (output.empty() && target.empty())
     {
-    this->SetError("Wrong syntax. Empty TARGET.");
+    this->SetError("Wrong syntax. A TARGET or OUTPUT must be specified.");
     return false;
     }
+  
+  // If source is empty, use the target 
+  if(source.empty() && output.empty())
+    {
+    m_Makefile->AddCustomCommandToTarget(target.c_str(), 
+                                         command.c_str(), 
+                                         command_args, 
+                                         cctype,
+                                         comment.c_str());
+    return true;
+    }
 
-  // If source is empty, use target as source, so that this command
-  // can be used to just attach a commmand to a target
-
-  if(source.empty())
+  // If target is empty, use the output
+  if(target.empty())
     {
-    source = target;
+    m_Makefile->AddCustomCommandToOutput(output.c_str(), 
+                                         command.c_str(), 
+                                         command_args, 
+                                         main_dependency.c_str(),
+                                         depends, 
+                                         comment.c_str());
+    return true;
     }
 
+  // otherwise backwards compatiblity mode
   m_Makefile->AddCustomCommand(source.c_str(), 
                                command.c_str(), 
                                command_args, 

+ 52 - 25
Source/cmAddCustomCommandCommand.h

@@ -86,42 +86,69 @@ public:
   virtual const char* GetFullDocumentation()
     {
     return
-      "  ADD_CUSTOM_COMMAND(TARGET target\n"
-      "                     [SOURCE source]\n"
-      "                     [COMMAND command]\n"
+      "There are two main signatures for ADD_CUSTOM_COMMAND "
+      "The first signature is for adding a custom command "
+      "to a source file.\n" 
+      "  ADD_CUSTOM_COMMAND(OUTPUT result\n"
+      "                     COMMAND command\n"
       "                     [ARGS [args...]]\n"
+      "                     [MAIN_DEPENDENCY depend]\n"
       "                     [DEPENDS [depends...]]\n"
-      "                     [OUTPUTS [outputs...]]\n"
       "                     [COMMENT comment])\n"
       "This defines a new command that can be executed during the build "
       "process.  In makefile terms this creates a new target in the "
       "following form:\n"
-      "  OUTPUT1: SOURCE DEPENDS\n"
-      "           COMAND ARGS\n"
-      "  OUTPUT2: SOURCE DEPENDS\n"
-      "           COMAND ARGS\n"
-      "The TARGET must be specified, but it is not the make target of the "
-      "build rule.  It is the target (library, executable, or custom target) "
-      "that will use the output generated from this rule.  This is necessary "
-      "to choose a project file in which to generate the rule for Visual "
-      "Studio.\n"
-      "Example of usage:\n"
-      "  ADD_CUSTOM_COMMAND(\n"
-      "    TARGET tiff\n"
-      "    SOURCE ${TIFF_FAX_EXE}\n"
-      "    COMMAND ${TIFF_FAX_EXE}\n"
-      "    ARGS -c const ${TIFF_BINARY_DIR}/tif_fax3sm.c\n"
-      "    OUTPUTS ${TIFF_BINARY_DIR}/tif_fax3sm.c\n"
-      "  )\n"
-      "This will create custom target which will generate file tif_fax3sm.c "
-      "using command ${TIFF_FAX_EXE}.  The rule will be executed as part of "
-      "building the tiff library because it includes tif_fax3sm.c as a "
-      "source file with the GENERATED property.";
+      "  OUTPUT: MAIN_DEPENDENCY DEPENDS\n"
+      "          COMMAND ARGS\n"
+      "\n"
+      "The second signature adds a custom command to a target "
+      "such as a library or executable. This is useful for "
+      "performing an operation before or after building the target "
+      "\n"
+      "  ADD_CUSTOM_COMMAND(TARGET target\n"
+      "                     PRE_BUILD | PRE_LINK | POST_BUILD\n"
+      "                     COMMAND command\n"
+      "                     [ARGS [args...]]\n"
+      "                     [COMMENT comment])\n"
+      "This defines a new command that will be associated with "
+      "building the specified target. When the command will "
+      "happen is determined by whether you specify\n"
+      "PRE_BUILD - run before all other dependencies\n"
+      "PRE_LINK - run after other dependencies\n"
+      "POST_BUILD - run after the target has been built\n";
     }
   
   cmTypeMacro(cmAddCustomCommandCommand, cmCommand);
 };
 
 
+/*
+  
+target: normal depends
+  pre rules
+  normal rules
+  post rules
+
+output1: source other depends
+  rule
+  
+output2: source other dpeends
+  rule
+
+
+another option is
+
+output1: depends
+  rule
+      
+output2: depends
+  rule
+
+
+  
+use case1 - an executable that depending on args create diff output files
+
+*/
+
 
 #endif

+ 13 - 2
Source/cmAddCustomTargetCommand.cxx

@@ -45,14 +45,25 @@ bool cmAddCustomTargetCommand::InitialPass(std::vector<std::string> const& args)
     command = *s;
     ++s;
     }
-  for (;s != args.end(); ++s)
+  for (;s != args.end() && *s != "DEPENDS"; ++s)
     {
     arguments += cmSystemTools::EscapeSpaces(s->c_str());
     arguments += " ";
     }
+  std::vector<std::string> depends;
+  // skip depends keyword
+  if (s != args.end())
+    {
+    ++s;
+    }
+  while (s != args.end()) 
+    {
+    depends.push_back(*s);  
+    ++s;
+    }
   m_Makefile->AddUtilityCommand(args[0].c_str(), 
                                 command.c_str(),
-                                arguments.c_str(), all);
+                                arguments.c_str(), all, depends);
 
   return true;
 }

+ 2 - 1
Source/cmAddCustomTargetCommand.h

@@ -64,7 +64,8 @@ public:
   virtual const char* GetFullDocumentation()
     {
     return
-      "  ADD_CUSTOM_TARGET(Name [ALL] [ command arg arg arg ... ])\n"
+      "  ADD_CUSTOM_TARGET(Name [ALL] [ command arg arg arg ... ] "
+      "                    [DEPENDS depend depend depend ... ])\n"
       "Adds a target with the given name that executes the given command "
       "every time the target is built.  If the ALL option is specified "
       "it indicates that this target should be added to the default build "

+ 53 - 0
Source/cmCPluginAPI.cxx

@@ -239,6 +239,56 @@ void cmAddCustomCommand(void *arg, const char* source,
   mf->AddCustomCommand(source, command, args2, depends2, outputs2, target);
 }
 
+void cmAddCustomCommandToOutput(void *arg, const char* output,
+                                const char* command,
+                                int numArgs, const char **args,
+                                const char* main_dependency,
+                                int numDepends, const char **depends)
+{
+  cmMakefile *mf = static_cast<cmMakefile *>(arg);
+  int i;
+  std::vector<std::string> args2;
+  for (i = 0; i < numArgs; ++i)
+    {
+    args2.push_back(args[i]);
+    }
+  std::vector<std::string> depends2;
+  for (i = 0; i < numDepends; ++i)
+    {
+    depends2.push_back(depends[i]);
+    }
+  mf->AddCustomCommandToOutput(output, command, args2, main_dependency,
+                               depends2);
+}
+
+void cmAddCustomCommandToTarget(void *arg, const char* target,
+                                const char* command,
+                                int numArgs, const char **args,
+                                int commandType)
+{
+  cmMakefile *mf = static_cast<cmMakefile *>(arg);
+  int i;
+  std::vector<std::string> args2;
+  for (i = 0; i < numArgs; ++i)
+    {
+    args2.push_back(args[i]);
+    }
+  switch (commandType)
+    {
+    case CM_PRE_BUILD:
+      mf->AddCustomCommandToTarget(target, command, args2, 
+                                   cmTarget::PRE_BUILD);
+      break;
+    case CM_PRE_LINK:
+      mf->AddCustomCommandToTarget(target, command, args2, 
+                                   cmTarget::PRE_LINK);
+      break;
+    case CM_POST_BUILD:
+      mf->AddCustomCommandToTarget(target, command, args2, 
+                                   cmTarget::POST_BUILD);
+      break;
+    }
+}
 
 void cmAddLinkLibraryForTarget(void *arg, const char *tgt, const char*value, 
                                int libtype)
@@ -546,5 +596,8 @@ cmCAPI cmStaticCAPI =
   cmGetFilenamePath,
   cmRemoveFile,
   cmFree,
+
+  cmAddCustomCommandToOutput,
+  cmAddCustomCommandToTarget,
 };
 

+ 19 - 0
Source/cmCPluginAPI.h

@@ -145,6 +145,19 @@ typedef struct
   void   (*RemoveFile)(const char *f1);
   void   (*Free)(void *);
   
+  /*=========================================================================
+    The following are new functions added after 1.6
+  =========================================================================*/
+  void  (*AddCustomCommandToOutput) (void *mf, const char* output,
+                                     const char* command,
+                                     int numArgs, const char **args,
+                                     const char* main_dependency,
+                                     int numDepends, const char **depends);
+  void  (*AddCustomCommandToTarget) (void *mf, const char* target,
+                                     const char* command,
+                                     int numArgs, const char **args,
+                                     int commandType);
+  
   /* this is the end of the C function stub API structure */ 
 } cmCAPI;
 
@@ -176,6 +189,12 @@ define the different types of compiles a library may be
 #define CM_LIBRARY_DEBUG 1
 #define CM_LIBRARY_OPTIMIZED 2
 
+/*=========================================================================
+define the different types of custom commands for a target
+=========================================================================*/
+#define CM_PRE_BUILD  0
+#define CM_PRE_LINK   1
+#define CM_POST_BUILD 2
   
 /*=========================================================================
 Finally we define the key data structures and function prototypes

+ 15 - 14
Source/cmCustomCommand.cxx

@@ -20,47 +20,48 @@
 /**
  * The constructor
  */
-cmCustomCommand::cmCustomCommand(const char *src, const char *command,
+cmCustomCommand::cmCustomCommand(const char *command,
                                  const char* arguments,
                                  std::vector<std::string> dep,
-                                 std::vector<std::string> out):
-  m_Source(src),
+                                 const char *out):
   m_Command(command),
   m_Arguments(arguments),
-  m_Depends(dep),
-  m_Outputs(out)
+  m_Depends(dep)
 {
+  if (out)
+    {
+    m_Output = out;
+    }
 }
 
+cmCustomCommand::cmCustomCommand(const char *command,
+                                 const char* arguments):
+  m_Command(command),
+  m_Arguments(arguments)
+{
+}
 
 /**
  * Copy constructor.
  */
 cmCustomCommand::cmCustomCommand(const cmCustomCommand& r):
-  m_Source(r.m_Source),
   m_Command(r.m_Command),
   m_Arguments(r.m_Arguments),
   m_Comment(r.m_Comment),
   m_Depends(r.m_Depends),
-  m_Outputs(r.m_Outputs)
+  m_Output(r.m_Output)
 {
 }
 
 void cmCustomCommand::ExpandVariables(const cmMakefile &mf)
 {
-  mf.ExpandVariablesInString(m_Source);
   mf.ExpandVariablesInString(m_Command);
   mf.ExpandVariablesInString(m_Arguments);
+  mf.ExpandVariablesInString(m_Output);
 
   for (std::vector<std::string>::iterator i = m_Depends.begin();
        i != m_Depends.end(); ++i)
     {
     mf.ExpandVariablesInString(*i);
     }
-  for (std::vector<std::string>::iterator i = m_Outputs.begin();
-       i != m_Outputs.end(); ++i)
-    {
-    mf.ExpandVariablesInString(*i);
-    }  
 }
-

+ 11 - 17
Source/cmCustomCommand.h

@@ -28,10 +28,13 @@ class cmMakefile;
 class cmCustomCommand
 {
 public:
-  cmCustomCommand(const char *src, const char *command,
+  cmCustomCommand(const char *command,
                   const char* arguments,
                   std::vector<std::string> dep,
-                  std::vector<std::string> out);
+                  const char *out);
+  cmCustomCommand(const char *command,
+                  const char* arguments);
+  cmCustomCommand() {};
   cmCustomCommand(const cmCustomCommand& r);
   
   /**
@@ -40,12 +43,6 @@ public:
    */
   void ExpandVariables(const cmMakefile &);
 
-  /**
-   * Return the name of the source file. I'm not sure if this is a full path or not.
-   */
-  std::string GetSourceName() const {return m_Source;}
-  void SetSourceName(const char *name) {m_Source = name;}
-
   ///! Return the command to execute with arguments
   std::string GetCommandAndArguments() const
     {return m_Command + " " + m_Arguments;}
@@ -54,7 +51,11 @@ public:
   std::string GetCommand() const {return m_Command;}
   void SetCommand(const char *cmd) {m_Command = cmd;}
 
-  ///! Return the command to execute
+  ///! Return the output
+  std::string GetOutput() const {return m_Output;}
+  void SetOutput(const char *cm) {m_Output = cm;}
+
+  ///! Return the comment
   std::string GetComment() const {return m_Comment;}
   void SetComment(const char *cm) {m_Comment = cm;}
 
@@ -68,19 +69,12 @@ public:
   const std::vector<std::string> &GetDepends() const {return m_Depends;}
   std::vector<std::string> &GetDepends() {return m_Depends;}
   
-  /**
-   * Return the vector that holds the list of outputs of this command
-   */
-  const std::vector<std::string> &GetOutputs() const {return m_Outputs;}
-  std::vector<std::string> &GetOutputs() {return m_Outputs;}
-  
 private:
-  std::string m_Source;
   std::string m_Command;
   std::string m_Arguments;
   std::string m_Comment;
+  std::string m_Output;
   std::vector<std::string> m_Depends;
-  std::vector<std::string> m_Outputs;
 };
 
 

+ 7 - 4
Source/cmGlobalVisualStudio6Generator.cxx

@@ -146,8 +146,9 @@ void cmGlobalVisualStudio6Generator::Generate()
 {
   // add a special target that depends on ALL projects for easy build
   // of Debug only
+  std::vector<std::string> srcs;
   m_LocalGenerators[0]->GetMakefile()->
-    AddUtilityCommand("ALL_BUILD", "echo","\"Build all projects\"",false);
+    AddUtilityCommand("ALL_BUILD", "echo","\"Build all projects\"",false,srcs);
 
   // add the Run Tests command
   this->SetupTests();
@@ -235,8 +236,9 @@ void cmGlobalVisualStudio6Generator::SetupTests()
     // If the file doesn't exist, then ENABLE_TESTING hasn't been run
     if (cmSystemTools::FileExists(fname.c_str()))
       {
+      std::vector<std::string> srcs;
       m_LocalGenerators[0]->GetMakefile()->
-        AddUtilityCommand("RUN_TESTS", ctest.c_str(), "-D $(IntDir)",false);
+        AddUtilityCommand("RUN_TESTS", ctest.c_str(), "-D $(IntDir)",false,srcs);
       }
     }
 }
@@ -310,12 +312,13 @@ void cmGlobalVisualStudio6Generator::WriteDSWFile(std::ostream& fout)
       // Write the project into the DSW file
       if (strncmp(l->first.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
         {
-        cmCustomCommand cc = l->second.GetCustomCommands()[0];
+        cmCustomCommand cc = l->second.GetPreLinkCommands()[0];
         
         // dodgy use of the cmCustomCommand's members to store the 
         // arguments from the INCLUDE_EXTERNAL_MSPROJECT command
         std::vector<std::string> stuff = cc.GetDepends();
-        std::vector<std::string> depends = cc.GetOutputs();
+        std::vector<std::string> depends;
+        depends.push_back(cc.GetOutput());
         this->WriteExternalProject(fout, stuff[0].c_str(), stuff[1].c_str(), depends);
         ++si;
         }

+ 3 - 2
Source/cmGlobalVisualStudio71Generator.cxx

@@ -109,12 +109,13 @@ void cmGlobalVisualStudio71Generator::WriteSLNFile(std::ostream& fout)
       // Write the project into the SLN file
       if (strncmp(l->first.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
         {
-        cmCustomCommand cc = l->second.GetCustomCommands()[0];
+        cmCustomCommand cc = l->second.GetPreLinkCommands()[0];
         
         // dodgy use of the cmCustomCommand's members to store the 
         // arguments from the INCLUDE_EXTERNAL_MSPROJECT command
         std::vector<std::string> stuff = cc.GetDepends();
-        std::vector<std::string> depends = cc.GetOutputs();
+        std::vector<std::string> depends;
+        depends.push_back(cc.GetOutput());
         this->WriteExternalProject(fout, stuff[0].c_str(), 
                                    stuff[1].c_str(), depends);
         ++si;

+ 7 - 4
Source/cmGlobalVisualStudio7Generator.cxx

@@ -145,8 +145,9 @@ void cmGlobalVisualStudio7Generator::SetupTests()
     // If the file doesn't exist, then ENABLE_TESTING hasn't been run
     if (cmSystemTools::FileExists(fname.c_str()))
       {
+      std::vector<std::string> srcs;
       m_LocalGenerators[0]->GetMakefile()->
-        AddUtilityCommand("RUN_TESTS", ctest.c_str(), "-D $(IntDir)",false);
+        AddUtilityCommand("RUN_TESTS", ctest.c_str(), "-D $(IntDir)",false, srcs);
       }
     }
 }
@@ -223,8 +224,9 @@ void cmGlobalVisualStudio7Generator::Generate()
 {
   // add a special target that depends on ALL projects for easy build
   // of Debug only
+  std::vector<std::string> srcs;
   m_LocalGenerators[0]->GetMakefile()->
-    AddUtilityCommand("ALL_BUILD", "echo","\"Build all projects\"",false);
+    AddUtilityCommand("ALL_BUILD", "echo","\"Build all projects\"",false, srcs);
   
   // add the Run Tests command
   this->SetupTests();
@@ -340,12 +342,13 @@ void cmGlobalVisualStudio7Generator::WriteSLNFile(std::ostream& fout)
       // Write the project into the SLN file
       if (strncmp(l->first.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
         {
-        cmCustomCommand cc = l->second.GetCustomCommands()[0];
+        cmCustomCommand cc = l->second.GetPreLinkCommands()[0];
         
         // dodgy use of the cmCustomCommand's members to store the 
         // arguments from the INCLUDE_EXTERNAL_MSPROJECT command
         std::vector<std::string> stuff = cc.GetDepends();
-        std::vector<std::string> depends = cc.GetOutputs();
+        std::vector<std::string> depends;
+        depends.push_back(cc.GetOutput());
         this->WriteExternalProject(fout, stuff[0].c_str(), 
                                    stuff[1].c_str(), depends);
         ++si;

+ 186 - 142
Source/cmLocalUnixMakefileGenerator.cxx

@@ -422,7 +422,8 @@ void cmLocalUnixMakefileGenerator::OutputTargetRules(std::ostream& fout)
       for(std::vector<cmSourceFile*>::iterator i = classes.begin(); 
           i != classes.end(); i++)
         {
-        if(!(*i)->GetPropertyAsBool("HEADER_FILE_ONLY"))
+        if(!(*i)->GetPropertyAsBool("HEADER_FILE_ONLY") && 
+           !(*i)->GetCustomCommand())
           {
           std::string outExt(
             this->GetOutputExtension((*i)->GetSourceExtension().c_str()));
@@ -439,7 +440,8 @@ void cmLocalUnixMakefileGenerator::OutputTargetRules(std::ostream& fout)
       for(std::vector<cmSourceFile*>::iterator i = classes.begin(); 
           i != classes.end(); i++)
         {
-        if(!(*i)->GetPropertyAsBool("HEADER_FILE_ONLY"))
+        if(!(*i)->GetPropertyAsBool("HEADER_FILE_ONLY") &&
+           !(*i)->GetCustomCommand())
           {
           std::string outExt(this->GetOutputExtension((*i)->GetSourceExtension().c_str()));
           if(outExt.size())
@@ -676,31 +678,77 @@ void cmLocalUnixMakefileGenerator::OutputLinkLibraries(std::ostream& fout,
     }
 }
 
+std::string cmLocalUnixMakefileGenerator::CreatePreBuildRules(
+  const cmTarget &target, const char* targetName)
+{
+  std::string customRuleCode = "";
+  bool initNext = false;
+  for (std::vector<cmCustomCommand>::const_iterator cr = 
+         target.GetPreBuildCommands().begin(); 
+       cr != target.GetPreBuildCommands().end(); ++cr)
+    {
+    cmCustomCommand cc(*cr);
+    cc.ExpandVariables(*m_Makefile);
+    if(initNext)
+      {
+      customRuleCode += "\n\t";
+      }
+    else
+      {
+      initNext = true;
+      }
+    std::string command = cmSystemTools::ConvertToOutputPath(cc.GetCommand().c_str());
+    customRuleCode += command + " " + cc.GetArguments();
+    }
+  return customRuleCode;
+}
 
-std::string cmLocalUnixMakefileGenerator::CreateTargetRules(const cmTarget &target,
-                                                       const char* targetName)
+std::string cmLocalUnixMakefileGenerator::CreatePreLinkRules(
+  const cmTarget &target, const char* targetName)
 {
   std::string customRuleCode = "";
   bool initNext = false;
   for (std::vector<cmCustomCommand>::const_iterator cr = 
-         target.GetCustomCommands().begin(); 
-       cr != target.GetCustomCommands().end(); ++cr)
+         target.GetPreLinkCommands().begin(); 
+       cr != target.GetPreLinkCommands().end(); ++cr)
     {
     cmCustomCommand cc(*cr);
     cc.ExpandVariables(*m_Makefile);
-    if (cc.GetSourceName() == targetName)
+    if(initNext)
       {
-      if(initNext)
-        {
-        customRuleCode += "\n\t";
-        }
-      else
-        {
-        initNext = true;
-        }
-      std::string command = cmSystemTools::ConvertToOutputPath(cc.GetCommand().c_str());
-      customRuleCode += command + " " + cc.GetArguments();
+      customRuleCode += "\n\t";
+      }
+    else
+      {
+      initNext = true;
       }
+    std::string command = cmSystemTools::ConvertToOutputPath(cc.GetCommand().c_str());
+    customRuleCode += command + " " + cc.GetArguments();
+    }
+  return customRuleCode;
+}
+
+std::string cmLocalUnixMakefileGenerator::CreatePostBuildRules(
+  const cmTarget &target, const char* targetName)
+{
+  std::string customRuleCode = "";
+  bool initNext = false;
+  for (std::vector<cmCustomCommand>::const_iterator cr = 
+         target.GetPostBuildCommands().begin(); 
+       cr != target.GetPostBuildCommands().end(); ++cr)
+    {
+    cmCustomCommand cc(*cr);
+    cc.ExpandVariables(*m_Makefile);
+    if(initNext)
+      {
+      customRuleCode += "\n\t";
+      }
+    else
+      {
+      initNext = true;
+      }
+    std::string command = cmSystemTools::ConvertToOutputPath(cc.GetCommand().c_str());
+    customRuleCode += command + " " + cc.GetArguments();
     }
   return customRuleCode;
 }
@@ -848,9 +896,21 @@ void cmLocalUnixMakefileGenerator::OutputLibraryRule(std::ostream& fout,
   // expand multi-command semi-colon separated lists
   // of commands into separate commands
   std::vector<std::string> commands;
+  // collect custom commands for this target and add them to the list
+  std::string customCommands = this->CreatePreBuildRules(t, name);
+  if(customCommands.size() > 0)
+    {
+    commands.push_back(customCommands);
+    }
+  // collect custom commands for this target and add them to the list
+  customCommands = this->CreatePreLinkRules(t, name);
+  if(customCommands.size() > 0)
+    {
+    commands.push_back(customCommands);
+    }
   cmSystemTools::ExpandList(rules, commands);
   // collect custom commands for this target and add them to the list
-  std::string customCommands = this->CreateTargetRules(t, name);
+  customCommands = this->CreatePostBuildRules(t, name);
   if(customCommands.size() > 0)
     {
     commands.push_back(customCommands);
@@ -1072,8 +1132,18 @@ void cmLocalUnixMakefileGenerator::OutputExecutableRule(std::ostream& fout,
   std::string comment = "executable";
   
   std::vector<std::string> commands;
+  std::string customCommands = this->CreatePreBuildRules(t, name);
+  if(customCommands.size() > 0)
+    {
+    commands.push_back(customCommands.c_str());
+    }
+  customCommands = this->CreatePreLinkRules(t, name);
+  if(customCommands.size() > 0)
+    {
+    commands.push_back(customCommands.c_str());
+    }
   cmSystemTools::ExpandList(rules, commands);
-  std::string customCommands = this->CreateTargetRules(t, name);
+  customCommands = this->CreatePostBuildRules(t, name);
   if(customCommands.size() > 0)
     {
     commands.push_back(customCommands.c_str());
@@ -1127,8 +1197,18 @@ void cmLocalUnixMakefileGenerator::OutputUtilityRule(std::ostream& fout,
                                                 const char* name,
                                                 const cmTarget &t)
 {
-  std::string customCommands = this->CreateTargetRules(t, name);
   const char* cc = 0;
+  std::string customCommands = this->CreatePreBuildRules(t, name);
+  std::string customCommands2 = this->CreatePreLinkRules(t, name);
+  if(customCommands2.size() > 0)
+    {
+    customCommands += customCommands2;
+    }
+  customCommands2 = this->CreatePostBuildRules(t, name);
+  if(customCommands2.size() > 0)
+    {
+    customCommands += customCommands2;
+    }
   if(customCommands.size() > 0)
     {
     cc = customCommands.c_str();
@@ -1136,7 +1216,7 @@ void cmLocalUnixMakefileGenerator::OutputUtilityRule(std::ostream& fout,
   std::string comment = "Utility";
   std::string depends;
   std::string replaceVars;
-  const std::vector<cmCustomCommand> &ccs = t.GetCustomCommands();
+  const std::vector<cmCustomCommand> &ccs = t.GetPostBuildCommands();
   for(std::vector<cmCustomCommand>::const_iterator i = ccs.begin();
       i != ccs.end(); ++i)
     {
@@ -1513,6 +1593,17 @@ void cmLocalUnixMakefileGenerator::OutputExeDepend(std::ostream& fout,
     exepath += cmSystemTools::GetExecutableExtension();
     fout << cmSystemTools::ConvertToOutputPath(exepath.c_str()) << " ";
     }
+  // if it isn't in the cache, it might still be a utility target
+  // so check for that
+  else
+    {
+    std::map<cmStdString, cmTarget>& targets = m_Makefile->GetTargets();
+    if (targets.find(name) != targets.end())
+      {
+      fout << name << " ";
+      }
+    }
+  
 }
 
 
@@ -1707,9 +1798,6 @@ void cmLocalUnixMakefileGenerator::OutputSubDirectoryRules(std::ostream& fout)
                                SubDirectories);
 }
 
-
-
-
 // Output the depend information for all the classes 
 // in the makefile.  These would have been generated
 // by the class cmMakeDepend GenerateMakefile
@@ -1821,126 +1909,76 @@ void cmLocalUnixMakefileGenerator::OutputCheckDepends(std::ostream& fout)
 //   (tab)   command...
 void cmLocalUnixMakefileGenerator::OutputCustomRules(std::ostream& fout)
 {
-  // We may be modifying the source groups temporarily, so make a copy.
-  std::vector<cmSourceGroup> sourceGroups = m_Makefile->GetSourceGroups();
+  // we cannot provide multiple rules for a single output
+  // so we will keep track of outputs to make sure we don't write
+  // two rules. First found wins
+  std::set<std::string> processedOutputs;
   
-  const cmTargets &tgts = m_Makefile->GetTargets();
-  for(cmTargets::const_iterator tgt = tgts.begin(); 
-      tgt != tgts.end(); ++tgt)
-    {
-    // add any custom rules to the source groups
-    for (std::vector<cmCustomCommand>::const_iterator cr = 
-           tgt->second.GetCustomCommands().begin(); 
-         cr != tgt->second.GetCustomCommands().end(); ++cr)
-      {
-      // if the source for the custom command is the same name
-      // as the target, then to not create a rule in the makefile for
-      // the custom command, as the command will be fired when the other target 
-      // is built.  
-      if ( cr->GetSourceName().compare(tgt->first) !=0)
-        {
-        cmSourceGroup& sourceGroup = 
-          m_Makefile->FindSourceGroup(cr->GetSourceName().c_str(),
-                                      sourceGroups);
-        cmCustomCommand cc(*cr);
-        cc.ExpandVariables(*m_Makefile);
-        sourceGroup.AddCustomCommand(cc);
-        }
-      }
-    }
-
-  // Loop through every source group.
-  for(std::vector<cmSourceGroup>::const_iterator sg =
-        sourceGroups.begin(); sg != sourceGroups.end(); ++sg)
+  // first output all custom rules
+  const std::vector<cmSourceFile*>& sources = m_Makefile->GetSourceFiles();
+  for(std::vector<cmSourceFile*>::const_iterator i = sources.begin();
+      i != sources.end(); ++i)
     {
-    const cmSourceGroup::BuildRules& buildRules = sg->GetBuildRules();
-    if(buildRules.empty())
-      { continue; }
-    
-    std::string name = sg->GetName();
-    if(name != "")
+    if ((*i)->GetCustomCommand())
       {
-      fout << "# Start of source group \"" << name.c_str() << "\"\n";
-      }
-    
-    // Loop through each source in the source group.
-    for(cmSourceGroup::BuildRules::const_iterator cc =
-          buildRules.begin(); cc != buildRules.end(); ++ cc)
-      {
-      std::string source = cc->first;
-      const cmSourceGroup::Commands& commands = cc->second.m_Commands;
-      // Loop through every command generating code from the current source.
-      for(cmSourceGroup::Commands::const_iterator c = commands.begin();
-          c != commands.end(); ++c)
+      cmCustomCommand *c = (*i)->GetCustomCommand();
+      // escape spaces and convert to native slashes path for
+      // the command
+      const char* comment = c->GetComment().c_str();
+      std::string command = c->GetCommand();
+      cmSystemTools::ReplaceString(command, "/./", "/");
+      command = cmSystemTools::ConvertToOutputPath(command.c_str());
+      command += " ";
+      // now add the arguments
+      command += c->GetArguments();
+      std::string depends;
+      // Collect out all the dependencies for this rule.
+      for(std::vector<std::string>::const_iterator d =
+            c->GetDepends().begin();
+          d != c->GetDepends().end(); ++d)
         {
-        // escape spaces and convert to native slashes path for
-        // the command
-        const char* comment = c->second.m_Comment.c_str();
-        std::string command = c->second.m_Command;
-        cmSystemTools::ReplaceString(command, "/./", "/");
-        command = cmSystemTools::ConvertToOutputPath(command.c_str());
-        command += " ";
-        // now add the arguments
-        command += c->second.m_Arguments;
-        const cmSourceGroup::CommandFiles& commandFiles = c->second;
-        // if the command has no outputs, then it is a utility command
-        // with no outputs
-        if(commandFiles.m_Outputs.size() == 0)
-          {
-          std::string depends;
-          // collect out all the dependencies for this rule.
-          for(std::set<std::string>::const_iterator d =
-                commandFiles.m_Depends.begin();
-              d != commandFiles.m_Depends.end(); ++d)
-            { 
-            std::string dep = *d;
-            cmSystemTools::ReplaceString(dep, "/./", "/");
-            cmSystemTools::ReplaceString(dep, "/$(IntDir)/", "/");
-            dep = cmSystemTools::ConvertToOutputPath(dep.c_str());
-            depends += " ";
-            depends += dep;
-            }
-          // output rule
-          this->OutputMakeRule(fout,
-                               (*comment?comment:"Custom command"),
-                               source.c_str(),
-                               depends.c_str(),
-                               command.c_str());
-          }
-        // Write a rule for every output generated by this command.
-        for(std::set<std::string>::const_iterator output =
-              commandFiles.m_Outputs.begin();
-            output != commandFiles.m_Outputs.end(); ++output)
+        std::string dep = *d;
+        m_Makefile->ExpandVariablesInString(dep);
+
+        // watch for target dependencies,
+        std::string libPath = dep + "_CMAKE_PATH";
+        const char* cacheValue = m_Makefile->GetDefinition(libPath.c_str());
+        if (cacheValue)
           {
-          std::string src = cmSystemTools::ConvertToOutputPath(source.c_str());
-          std::string depends;
-          depends +=  src;
-          // Collect out all the dependencies for this rule.
-          for(std::set<std::string>::const_iterator d =
-                commandFiles.m_Depends.begin();
-              d != commandFiles.m_Depends.end(); ++d)
+          libPath = cacheValue;
+          if (m_Makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH") && 
+              m_Makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH")[0] != '\0')
             {
-            std::string dep = *d;
-            cmSystemTools::ReplaceString(dep, "/./", "/");
-            cmSystemTools::ReplaceString(dep, "/$(IntDir)/", "/");
-            dep = cmSystemTools::ConvertToOutputPath(dep.c_str());
-            depends += " ";
-            depends += dep;
-            } 
-          // output rule
-          this->OutputMakeRule(fout,
-                               (*comment?comment:"Custom command"),
-                               output->c_str(),
-                               depends.c_str(),
-                               command.c_str());
+            libPath = m_Makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH");
+            }
+          libPath += "/";
+          libPath += dep;
+          libPath += cmSystemTools::GetExecutableExtension();
+          dep = libPath;
           }
+        cmSystemTools::ReplaceString(dep, "/./", "/");
+        cmSystemTools::ReplaceString(dep, "/$(IntDir)/", "/");
+        dep = cmSystemTools::ConvertToOutputPath(dep.c_str());
+        depends += " ";
+        depends += dep;
+        } 
+      // output rule
+      if (processedOutputs.find(c->GetOutput()) == processedOutputs.end())
+        {
+        this->OutputMakeRule(fout,
+                             (*comment?comment:"Custom command"),
+                             c->GetOutput().c_str(),
+                             depends.c_str(),
+                             command.c_str());
+        processedOutputs.insert(c->GetOutput());
+        }
+      else
+        {
+        cmSystemTools::Error("An output was found with multiple rules on how to build it for output: ",
+                             c->GetOutput().c_str());
         }
       }
-    if(name != "")
-      {
-      fout << "# End of source group \"" << name.c_str() << "\"\n\n";
-      }
-    }  
+    }
 }
 
 std::string 
@@ -2185,7 +2223,7 @@ void cmLocalUnixMakefileGenerator::OutputMakeRules(std::ostream& fout)
   // collect up all the sources
   std::string allsources;
   std::map<cmStdString, cmTarget>& targets = m_Makefile->GetTargets();
-  for(std::map<cmStdString, cmTarget>::const_iterator target = targets.begin(); 
+  for(std::map<cmStdString,cmTarget>::const_iterator target = targets.begin(); 
       target != targets.end(); ++target)
     {
     // Iterate over every source for this target.
@@ -2501,11 +2539,13 @@ void cmLocalUnixMakefileGenerator::OutputSourceObjectBuildRules(std::ostream& fo
       exportsDef = "-D"+ export_symbol;
       }
     // Iterate over every source for this target.
-    const std::vector<cmSourceFile*>& sources = target->second.GetSourceFiles();
+    const std::vector<cmSourceFile*>& sources = 
+      target->second.GetSourceFiles();
     for(std::vector<cmSourceFile*>::const_iterator source = sources.begin(); 
         source != sources.end(); ++source)
       {
-      if(!(*source)->GetPropertyAsBool("HEADER_FILE_ONLY"))
+      if(!(*source)->GetPropertyAsBool("HEADER_FILE_ONLY") && 
+         !(*source)->GetCustomCommand())
         {
         std::string shortName;
         std::string sourceName;
@@ -2513,11 +2553,15 @@ void cmLocalUnixMakefileGenerator::OutputSourceObjectBuildRules(std::ostream& fo
         // directory, we want to use the relative path for the
         // filename of the object file.  Otherwise, we will use just
         // the filename portion.
-        if((cmSystemTools::GetFilenamePath((*source)->GetFullPath()).find(m_Makefile->GetCurrentDirectory()) == 0)
-           || (cmSystemTools::GetFilenamePath((*source)->GetFullPath()).find(m_Makefile->
-                                                                          GetCurrentOutputDirectory()) == 0))
+        if((cmSystemTools::GetFilenamePath(
+              (*source)->GetFullPath()).find(
+                m_Makefile->GetCurrentDirectory()) == 0)
+           || (cmSystemTools::GetFilenamePath(
+                 (*source)->GetFullPath()).find(
+                   m_Makefile->GetCurrentOutputDirectory()) == 0))
           {
-          sourceName = (*source)->GetSourceName()+"."+(*source)->GetSourceExtension();
+          sourceName = (*source)->GetSourceName()+"."+
+            (*source)->GetSourceExtension();
           shortName = (*source)->GetSourceName();
           
           // The path may be relative.  See if a directory needs to be

+ 6 - 2
Source/cmLocalUnixMakefileGenerator.h

@@ -187,8 +187,12 @@ protected:
   ///! return true if the two paths are the same
   virtual bool SamePath(const char* path1, const char* path2);
   virtual std::string GetOutputExtension(const char* sourceExtension);
-  std::string CreateTargetRules(const cmTarget &target,
-                                const char* targetName);
+  std::string CreatePreBuildRules(const cmTarget &target,
+                                  const char* targetName);
+  std::string CreatePreLinkRules(const cmTarget &target,
+                                 const char* targetName);
+  std::string CreatePostBuildRules(const cmTarget &target,
+                                   const char* targetName);
   virtual std::string CreateMakeVariable(const char* s, const char* s2);
   
   ///! if the OS is case insensitive then return a lower case of the path.

+ 211 - 156
Source/cmLocalVisualStudio6Generator.cxx

@@ -20,6 +20,7 @@
 #include "cmSystemTools.h"
 #include "cmSourceFile.h"
 #include "cmCacheManager.h"
+#include <queue>
 
 cmLocalVisualStudio6Generator::cmLocalVisualStudio6Generator()
 {
@@ -74,6 +75,9 @@ void cmLocalVisualStudio6Generator::OutputDSPFile()
   // clear project names
   m_CreatedProjectNames.clear();
 
+  // expand vars for custom commands
+  m_Makefile->ExpandVariablesInCustomCommands();
+
   // build any targets
   cmTargets &tgts = m_Makefile->GetTargets();
   for(cmTargets::iterator l = tgts.begin(); 
@@ -154,7 +158,7 @@ void cmLocalVisualStudio6Generator::CreateSingleDSP(const char *lname, cmTarget
 }
 
 
-void cmLocalVisualStudio6Generator::AddDSPBuildRule(cmSourceGroup& sourceGroup)
+void cmLocalVisualStudio6Generator::AddDSPBuildRule()
 {
   std::string dspname = *(m_CreatedProjectNames.end()-1);
   if(dspname == "ALL_BUILD")
@@ -169,20 +173,28 @@ void cmLocalVisualStudio6Generator::AddDSPBuildRule(cmSourceGroup& sourceGroup)
   std::string dsprule = "${CMAKE_COMMAND}";
   m_Makefile->ExpandVariablesInString(dsprule);
   dsprule = cmSystemTools::ConvertToOutputPath(dsprule.c_str());
-  std::string args = makefileIn;
-  args += " -H";
+  std::vector<std::string> argv;
+  argv.push_back(makefileIn);
+  makefileIn = m_Makefile->GetStartDirectory();
+  makefileIn += "/";
+  makefileIn += "CMakeLists.txt";
+  std::string args;
+  args = "-H";
   args +=
     cmSystemTools::ConvertToOutputPath(m_Makefile->GetHomeDirectory());
-  args += " -S";
+  argv.push_back(args);
+  args = "-S";
   args +=
     cmSystemTools::ConvertToOutputPath(m_Makefile->GetStartDirectory());
-  args += " -O";
+  argv.push_back(args);
+  args = "-O";
   args += 
     cmSystemTools::ConvertToOutputPath(m_Makefile->GetStartOutputDirectory());
-  args += " -B";
+  argv.push_back(args);
+  args = "-B";
   args += 
     cmSystemTools::ConvertToOutputPath(m_Makefile->GetHomeOutputDirectory());
-  m_Makefile->ExpandVariablesInString(args);
+  argv.push_back(args);
 
   std::string configFile = 
     m_Makefile->GetDefinition("CMAKE_ROOT");
@@ -201,14 +213,9 @@ void cmLocalVisualStudio6Generator::AddDSPBuildRule(cmSourceGroup& sourceGroup)
     {
     listFiles.push_back(configFile);
     }
-  
-  std::vector<std::string> outputs;
-  outputs.push_back(dspname);
-  cmCustomCommand cc(makefileIn.c_str(), dsprule.c_str(),
-                     args.c_str(),
-                     listFiles, 
-                     outputs);
-  sourceGroup.AddCustomCommand(cc);
+  m_Makefile->AddCustomCommandToOutput(dspname.c_str(), dsprule.c_str(), 
+                                       argv, makefileIn.c_str(), listFiles,
+                                       NULL, true);
 }
 
 
@@ -219,8 +226,87 @@ void cmLocalVisualStudio6Generator::WriteDSPFile(std::ostream& fout,
   // We may be modifying the source groups temporarily, so make a copy.
   std::vector<cmSourceGroup> sourceGroups = m_Makefile->GetSourceGroups();
   
+  // if we should add regen rule then...
+  const char *suppRegenRule = 
+    m_Makefile->GetDefinition("CMAKE_SUPPRESS_REGENERATION");
+  if (!cmSystemTools::IsOn(suppRegenRule))
+    {
+    this->AddDSPBuildRule();
+    }
+
   // get the classes from the source lists then add them to the groups
-  std::vector<cmSourceFile*> classes = target.GetSourceFiles();
+  std::vector<cmSourceFile*> & classes = target.GetSourceFiles();
+  // use a deck to keep track of processed source files
+  std::queue<std::string> srcFilesToProcess;
+  std::string name;
+  for(std::vector<cmSourceFile*>::const_iterator i = classes.begin(); 
+      i != classes.end(); ++i)
+    {
+    name = (*i)->GetSourceName();
+    if ((*i)->GetSourceExtension() != "rule")
+      {
+      name += ".";
+      name += (*i)->GetSourceExtension();
+      }
+    srcFilesToProcess.push(name);
+    }
+  name = libName;
+  name += ".dsp.cmake";
+  srcFilesToProcess.push(name);
+  // add in the library depends for cusotm targets
+  if (target.GetType() == cmTarget::UTILITY)
+    {
+    cmCustomCommand &c = target.GetPostBuildCommands()[0];
+    for (std::vector<std::string>::iterator i = c.GetDepends().begin();
+         i != c.GetDepends().end(); ++i)
+      {
+      srcFilesToProcess.push(*i);
+      }
+    }
+  while (!srcFilesToProcess.empty())
+    {
+    // is this source the output of a custom command
+    cmSourceFile* outsf = 
+      m_Makefile->GetSourceFileWithOutput(srcFilesToProcess.front().c_str());
+    if (outsf)
+      {
+      // is it not already in the target?
+      if (std::find(classes.begin(),classes.end(),outsf) == classes.end())
+        {
+        // then add the source to this target and add it to the queue
+        classes.push_back(outsf);
+        std::string name = outsf->GetSourceName();
+        if (outsf->GetSourceExtension() != "rule")
+          {
+          name += ".";
+          name += outsf->GetSourceExtension();
+          }
+        srcFilesToProcess.push(name);
+        }
+      // add its dependencies to the list to check
+      unsigned int i;
+      for (i = 0; i < outsf->GetCustomCommand()->GetDepends().size(); ++i)
+        {
+        std::string dep = cmSystemTools::GetFilenameName(
+          outsf->GetCustomCommand()->GetDepends()[i]);
+        // watch for target dependencies,
+        std::string libPath = dep + "_CMAKE_PATH";
+        const char* cacheValue = m_Makefile->GetDefinition(libPath.c_str());
+        if (cacheValue)
+          {
+          // add the depend as a utility on the target
+          target.AddUtility(dep.c_str());
+          }
+        else
+          {
+          srcFilesToProcess.push(dep);
+          }
+        }
+      }
+    // finished with this SF move to the next
+    srcFilesToProcess.pop();
+    }
+
   for(std::vector<cmSourceFile*>::iterator i = classes.begin(); 
       i != classes.end(); i++)
     {
@@ -231,49 +317,20 @@ void cmLocalVisualStudio6Generator::WriteDSPFile(std::ostream& fout,
     sourceGroup.AddSource(source.c_str(), *i);
     }
   
-  // add any custom rules to the source groups
-  for (std::vector<cmCustomCommand>::const_iterator cr = 
-         target.GetCustomCommands().begin(); 
-       cr != target.GetCustomCommands().end(); ++cr)
-    {
-    cmSourceGroup& sourceGroup = 
-      m_Makefile->FindSourceGroup(cr->GetSourceName().c_str(),
-                                  sourceGroups);
-    cmCustomCommand cc(*cr);
-    cc.ExpandVariables(*m_Makefile);
-    sourceGroup.AddCustomCommand(cc);
-    }
-  
   // Write the DSP file's header.
   this->WriteDSPHeader(fout, libName, target, sourceGroups);
   
-  // if we should add regen rule then...
-  const char *suppRegenRule = 
-    m_Makefile->GetDefinition("CMAKE_SUPPRESS_REGENERATION");
-  
-  // Find the group in which the CMakeLists.txt source belongs, and add
-  // the rule to generate this DSP file.
-  if (!cmSystemTools::IsOn(suppRegenRule))
-    {
-    for(std::vector<cmSourceGroup>::reverse_iterator sg = sourceGroups.rbegin();
-        sg != sourceGroups.rend(); ++sg)
-      {
-      if(sg->Matches("CMakeLists.txt"))
-        {
-        this->AddDSPBuildRule(*sg);
-        break;
-        }    
-      }
-    }
-  
   // Loop through every source group.
   for(std::vector<cmSourceGroup>::const_iterator sg = sourceGroups.begin();
       sg != sourceGroups.end(); ++sg)
     {
-    const cmSourceGroup::BuildRules& buildRules = sg->GetBuildRules();
+    const std::vector<const cmSourceFile *> &sourceFiles = 
+      sg->GetSourceFiles();
     // If the group is empty, don't write it at all.
-    if(buildRules.empty())
-      { continue; }
+    if(sourceFiles.empty())
+      { 
+      continue; 
+      }
     
     // If the group has a name, write the header.
     std::string name = sg->GetName();
@@ -282,37 +339,32 @@ void cmLocalVisualStudio6Generator::WriteDSPFile(std::ostream& fout,
       this->WriteDSPBeginGroup(fout, name.c_str(), "");
       }
     
-    // Loop through each build rule in the source group.
-    for(cmSourceGroup::BuildRules::const_iterator cc =
-          buildRules.begin(); cc != buildRules.end(); ++ cc)
+    // Loop through each source in the source group.
+    for(std::vector<const cmSourceFile *>::const_iterator sf =
+          sourceFiles.begin(); sf != sourceFiles.end(); ++sf)
       {
-      std::string source = cc->first;
-      const cmSourceGroup::Commands& commands = cc->second.m_Commands;
-      std::vector<std::string> depends;
+      std::string source = (*sf)->GetFullPath();
+      const cmCustomCommand *command = 
+        (*sf)->GetCustomCommand();
       std::string compileFlags;
-      if(cc->second.m_SourceFile)
+      std::vector<std::string> depends;
+      const char* cflags = (*sf)->GetProperty("COMPILE_FLAGS");
+      if(cflags)
         {
-        // Check for extra compiler flags.
-        const char* cflags = cc->second.m_SourceFile->GetProperty("COMPILE_FLAGS");
-        if(cflags)
-          {
-          compileFlags = cflags;
-          }
-        if(cmSystemTools::GetFileFormat(
-             cc->second.m_SourceFile->GetSourceExtension().c_str())
-           == cmSystemTools::CXX_FILE_FORMAT)
-          {
-          // force a C++ file type
-          compileFlags += " /TP ";
-          }
-        
-        // Check for extra object-file dependencies.
-        const char* dependsValue =
-          cc->second.m_SourceFile->GetProperty("OBJECT_DEPENDS");
-        if(dependsValue)
-          {
-          cmSystemTools::ExpandListArgument(dependsValue, depends);
-          }
+        compileFlags = cflags;
+        }
+      if(cmSystemTools::GetFileFormat((*sf)->GetSourceExtension().c_str())
+         == cmSystemTools::CXX_FILE_FORMAT)
+        {
+        // force a C++ file type
+        compileFlags += " /TP ";
+        }
+      
+      // Check for extra object-file dependencies.
+      const char* dependsValue = (*sf)->GetProperty("OBJECT_DEPENDS");
+      if(dependsValue)
+        {
+        cmSystemTools::ExpandListArgument(dependsValue, depends);
         }
       if (source != libName || target.GetType() == cmTarget::UTILITY)
         {
@@ -334,18 +386,20 @@ void cmLocalVisualStudio6Generator::WriteDSPFile(std::ostream& fout,
             }
           fout << "\n";
           }
-        if (!commands.empty())
+        if (command)
           {
-          cmSourceGroup::CommandFiles totalCommand;
           std::string totalCommandStr;
-          totalCommandStr = this->CombineCommands(commands, totalCommand,
-                                                  source.c_str());
-          const char* comment = totalCommand.m_Comment.c_str(); 
+          totalCommandStr = 
+            cmSystemTools::ConvertToOutputPath(command->GetCommand().c_str()); 
+          totalCommandStr += " ";
+          totalCommandStr += command->GetArguments();
+          totalCommandStr += "\n";
+          const char* comment = command->GetComment().c_str();
           const char* flags = compileFlags.size() ? compileFlags.c_str(): 0;
           this->WriteCustomRule(fout, source.c_str(), totalCommandStr.c_str(), 
                                 (*comment?comment:"Custom Rule"),
-                                totalCommand.m_Depends, 
-                                totalCommand.m_Outputs, flags);
+                                command->GetDepends(), 
+                                command->GetOutput().c_str(), flags);
           }
         else if(compileFlags.size())
           {
@@ -384,8 +438,8 @@ void cmLocalVisualStudio6Generator::WriteCustomRule(std::ostream& fout,
                                   const char* source,
                                   const char* command,
                                   const char* comment,
-                                  const std::set<std::string>& depends,
-                                  const std::set<std::string>& outputs,
+                                  const std::vector<std::string>& depends,
+                                  const char *output,
                                   const char* flags
                                   )
 {
@@ -406,7 +460,7 @@ void cmLocalVisualStudio6Generator::WriteCustomRule(std::ostream& fout,
       }
     // Write out the dependencies for the rule.
     fout << "USERDEP__HACK=";
-    for(std::set<std::string>::const_iterator d = depends.begin();
+    for(std::vector<std::string>::const_iterator d = depends.begin();
         d != depends.end(); ++d)
       {
       fout << "\\\n\t" << 
@@ -417,21 +471,16 @@ void cmLocalVisualStudio6Generator::WriteCustomRule(std::ostream& fout,
     fout << "# PROP Ignore_Default_Tool 1\n";
     fout << "# Begin Custom Build - Building " << comment 
          << " $(InputPath)\n\n";
-    if(outputs.size() == 0)
+    if(output == 0)
       {
       fout << source << "_force :  \"$(SOURCE)\" \"$(INTDIR)\" \"$(OUTDIR)\"";
       fout << command << "\n\n";
       }
     
     // Write a rule for every output generated by this command.
-    for(std::set<std::string>::const_iterator output = outputs.begin();
-        output != outputs.end(); ++output)
-      {
-      fout << cmSystemTools::ConvertToOutputPath(output->c_str())
-           << " :  \"$(SOURCE)\" \"$(INTDIR)\" \"$(OUTDIR)\"";
-      fout << command << "\n\n";
-      }
-    
+    fout << cmSystemTools::ConvertToOutputPath(output)
+         << " :  \"$(SOURCE)\" \"$(INTDIR)\" \"$(OUTDIR)\"";
+    fout << command << "\n\n";
     fout << "# End Custom Build\n\n";
     }
   
@@ -544,46 +593,6 @@ void cmLocalVisualStudio6Generator::SetBuildType(BuildType b,
     }
 }
 
-std::string
-cmLocalVisualStudio6Generator::CombineCommands(const cmSourceGroup::Commands &commands,
-                             cmSourceGroup::CommandFiles &totalCommand,
-                             const char *source)
-  
-{
-  // Loop through every custom command generating code from the
-  // current source.
-  // build up the depends and outputs and commands 
-  std::string totalCommandStr = "";
-  std::string temp;
-  for(cmSourceGroup::Commands::const_iterator c = commands.begin();
-      c != commands.end(); ++c)
-    {
-    totalCommandStr += "\n\t";
-    temp= c->second.m_Command; 
-    temp = cmSystemTools::ConvertToOutputPath(temp.c_str());
-    totalCommandStr += temp;
-    totalCommandStr += " ";
-    totalCommandStr += c->second.m_Arguments;
-    totalCommand.Merge(c->second);
-    totalCommand.m_Comment = c->second.m_Comment.c_str();
-    }      
-  // Create a dummy file with the name of the source if it does
-  // not exist
-  if(totalCommand.m_Outputs.empty())
-    { 
-    std::string dummyFile = m_Makefile->GetStartOutputDirectory();
-    dummyFile += "/";
-    dummyFile += source;
-    if(!cmSystemTools::FileExists(dummyFile.c_str()))
-      {
-      std::ofstream fout(dummyFile.c_str());
-      fout << "Dummy file created by cmake as unused source for utility command.\n";
-      }
-    }
-  return totalCommandStr;
-}
-
-
 // look for custom rules on a target and collect them together
 std::string 
 cmLocalVisualStudio6Generator::CreateTargetRules(const cmTarget &target, 
@@ -591,39 +600,85 @@ cmLocalVisualStudio6Generator::CreateTargetRules(const cmTarget &target,
 {
   std::string customRuleCode = "";
 
-  if (target.GetType() >= cmTarget::UTILITY)
+  if (target.GetType() > cmTarget::UTILITY)
     {
     return customRuleCode;
     }
+
+  // are there any rules?
+  if (target.GetPreBuildCommands().size() + 
+      target.GetPreLinkCommands().size() + 
+      target.GetPostBuildCommands().size() == 0)
+    {
+    return customRuleCode;
+    }
+    
+  customRuleCode = "# Begin Special Build Tool\n";
   
-  // Find the group in which the lix exe custom rules belong
+  // Do the PreBuild and PreLink (VS6 does not support both)
   bool init = false;
   for (std::vector<cmCustomCommand>::const_iterator cr = 
-         target.GetCustomCommands().begin(); 
-       cr != target.GetCustomCommands().end(); ++cr)
+         target.GetPreBuildCommands().begin(); 
+       cr != target.GetPreBuildCommands().end(); ++cr)
     {
     cmCustomCommand cc(*cr);
     cc.ExpandVariables(*m_Makefile);
-    if (cc.GetSourceName() == libName)
+    if (!init)
       {
-      if (!init)
-        {
-        // header stuff
-        customRuleCode = "# Begin Special Build Tool\nPostBuild_Cmds=";
-        init = true;
-        }
-      else
-        {
-        customRuleCode += "\t";
-        }
-      customRuleCode += cmSystemTools::ConvertToOutputPath(cc.GetCommand().c_str()) + " " + cc.GetArguments();
+      // header stuff
+      customRuleCode = "PreLink_Cmds=";
+      init = true;
+      }
+    else
+      {
+      customRuleCode += "\t";
+      }
+    customRuleCode += cmSystemTools::ConvertToOutputPath(cc.GetCommand().c_str()) + " " + cc.GetArguments();
+    }
+
+  for (std::vector<cmCustomCommand>::const_iterator cr = 
+         target.GetPreLinkCommands().begin(); 
+       cr != target.GetPreLinkCommands().end(); ++cr)
+    {
+    cmCustomCommand cc(*cr);
+    cc.ExpandVariables(*m_Makefile);
+    if (!init)
+      {
+      // header stuff
+      customRuleCode = "PreLink_Cmds=";
+      init = true;
+      }
+    else
+      {
+      customRuleCode += "\t";
       }
+    customRuleCode += cmSystemTools::ConvertToOutputPath(cc.GetCommand().c_str()) + " " + cc.GetArguments();
     }
 
-  if (init)
+  // do the post build rules
+  init = false;
+  for (std::vector<cmCustomCommand>::const_iterator cr = 
+         target.GetPostBuildCommands().begin(); 
+       cr != target.GetPostBuildCommands().end(); ++cr)
     {
-    customRuleCode += "\n# End Special Build Tool\n";
+    cmCustomCommand cc(*cr);
+    cc.ExpandVariables(*m_Makefile);
+    if (!init)
+      {
+      // header stuff
+      customRuleCode = "PostBuild_Cmds=";
+      init = true;
+      }
+    else
+      {
+      customRuleCode += "\t";
+      }
+    customRuleCode += 
+      cmSystemTools::ConvertToOutputPath(cc.GetCommand().c_str()) + 
+      " " + cc.GetArguments();
     }
+
+  customRuleCode += "\n# End Special Build Tool\n";
   return customRuleCode;
 }
 

+ 5 - 10
Source/cmLocalVisualStudio6Generator.h

@@ -22,9 +22,8 @@
 class cmMakeDepend;
 class cmTarget;
 class cmSourceFile;
-
-// please remove me.... Yuck
-#include "cmSourceGroup.h"
+class cmSourceGroup;
+class cmCustomCommand;
 
 /** \class cmLocalVisualStudio6Generator
  * \brief Write a LocalUnix makefiles.
@@ -84,21 +83,17 @@ private:
                       const cmTarget &tgt, std::vector<cmSourceGroup> &sgs);
 
   void WriteDSPFooter(std::ostream& fout);
-  void AddDSPBuildRule(cmSourceGroup&);
+  void AddDSPBuildRule();
   void WriteCustomRule(std::ostream& fout,
                        const char* source,
                        const char* command,
                        const char* comment,
-                       const std::set<std::string>& depends,
-                       const std::set<std::string>& outputs,
+                       const std::vector<std::string>& depends,
+                       const char* output,
                        const char* flags);
 
   std::string CreateTargetRules(const cmTarget &target, 
                                 const char *libName);
-  std::string CombineCommands(const cmSourceGroup::Commands &commands,
-                              cmSourceGroup::CommandFiles &totalCommand,
-                              const char *source);
-
   std::string m_IncludeOptions;
   std::vector<std::string> m_Configurations;
 };

+ 237 - 190
Source/cmLocalVisualStudio7Generator.cxx

@@ -21,6 +21,7 @@
 #include "cmSourceFile.h"
 #include "cmCacheManager.h"
 #include "cmake.h"
+#include <queue>
 
 cmLocalVisualStudio7Generator::cmLocalVisualStudio7Generator()
 {
@@ -85,6 +86,9 @@ void cmLocalVisualStudio7Generator::OutputVCProjFile()
   // clear project names
   m_CreatedProjectNames.clear();
 
+  // expand vars for custom commands
+  m_Makefile->ExpandVariablesInCustomCommands();  
+
   // build any targets
   cmTargets &tgts = m_Makefile->GetTargets();
   for(cmTargets::iterator l = tgts.begin(); 
@@ -127,7 +131,7 @@ void cmLocalVisualStudio7Generator::CreateSingleVCProj(const char *lname, cmTarg
 }
 
 
-void cmLocalVisualStudio7Generator::AddVCProjBuildRule(cmSourceGroup& sourceGroup)
+void cmLocalVisualStudio7Generator::AddVCProjBuildRule()
 {
   std::string dspname = *(m_CreatedProjectNames.end()-1);
   if(dspname == "ALL_BUILD")
@@ -142,22 +146,29 @@ void cmLocalVisualStudio7Generator::AddVCProjBuildRule(cmSourceGroup& sourceGrou
   std::string dsprule = "${CMAKE_COMMAND}";
   m_Makefile->ExpandVariablesInString(dsprule);
   dsprule = cmSystemTools::ConvertToOutputPath(dsprule.c_str());
-  std::string args = makefileIn;
-  args += " -H";
+  std::vector<std::string> argv;
+  argv.push_back(makefileIn);
+  makefileIn = m_Makefile->GetStartDirectory();
+  makefileIn += "/";
+  makefileIn += "CMakeLists.txt";
+  std::string args;
+  args = "-H";
   args +=
     cmSystemTools::ConvertToOutputPath(m_Makefile->GetHomeDirectory());
-  args += " -S";
-  args += 
+  argv.push_back(args);
+  args = "-S";
+  args +=
     cmSystemTools::ConvertToOutputPath(m_Makefile->GetStartDirectory());
-  args += " -O";
+  argv.push_back(args);
+  args = "-O";
   args += 
     cmSystemTools::ConvertToOutputPath(m_Makefile->GetStartOutputDirectory());
-  args += " -B";
+  argv.push_back(args);
+  args = "-B";
   args += 
     cmSystemTools::ConvertToOutputPath(m_Makefile->GetHomeOutputDirectory());
-  args += "";
-  m_Makefile->ExpandVariablesInString(args);
-
+  argv.push_back(args);
+  
   std::string configFile = 
     m_Makefile->GetDefinition("CMAKE_ROOT");
   configFile += "/Templates/CMakeWindowsSystemConfig.cmake";
@@ -175,14 +186,9 @@ void cmLocalVisualStudio7Generator::AddVCProjBuildRule(cmSourceGroup& sourceGrou
     {
     listFiles.push_back(configFile);
     }
-  
-  std::vector<std::string> outputs;
-  outputs.push_back(dspname);
-  cmCustomCommand cc(makefileIn.c_str(), dsprule.c_str(),
-                     args.c_str(),
-                     listFiles, 
-                     outputs);
-  sourceGroup.AddCustomCommand(cc);
+  m_Makefile->AddCustomCommandToOutput(dspname.c_str(), dsprule.c_str(), 
+                                       argv, makefileIn.c_str(), listFiles,
+                                       NULL, true);
 }
 
 
@@ -359,10 +365,7 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout,
     }
   fout << "\"\n/>\n";
   fout << "\t\t\t<Tool\n\t\t\t\tName=\"VCMIDLTool\"/>\n";
-  fout << "\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"";
   this->OutputTargetRules(fout, target, libName);
-  fout << "/>\n";
-  fout << "\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"/>\n";
   this->OutputBuildTool(fout, configName, libName, target);
   fout << "\t\t</Configuration>\n";
 }
@@ -635,18 +638,104 @@ void cmLocalVisualStudio7Generator::OutputDefineFlags(std::ostream& fout)
 }
 
 void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout, 
-                                 const char *libName,
-                                 cmTarget &target)
+                                                    const char *libName,
+                                                    cmTarget &target)
 {
   // get the configurations
   std::vector<std::string> *configs = 
-    static_cast<cmGlobalVisualStudio7Generator *>(m_GlobalGenerator)->GetConfigurations();
+    static_cast<cmGlobalVisualStudio7Generator *>
+    (m_GlobalGenerator)->GetConfigurations();
   
+  // if we should add regen rule then...
+  const char *suppRegenRule = 
+    m_Makefile->GetDefinition("CMAKE_SUPPRESS_REGENERATION");
+  if (!cmSystemTools::IsOn(suppRegenRule))
+    {
+    this->AddVCProjBuildRule();
+    }
+
   // We may be modifying the source groups temporarily, so make a copy.
   std::vector<cmSourceGroup> sourceGroups = m_Makefile->GetSourceGroups();
   
   // get the classes from the source lists then add them to the groups
-  std::vector<cmSourceFile*> const& classes = target.GetSourceFiles();
+  std::vector<cmSourceFile*> & classes = target.GetSourceFiles();
+  // use a deck to keep track of processed source files
+  std::queue<std::string> srcFilesToProcess;
+  std::string name;
+  for(std::vector<cmSourceFile*>::const_iterator i = classes.begin(); 
+      i != classes.end(); ++i)
+    {
+    std::string name = (*i)->GetSourceName();
+    if ((*i)->GetSourceExtension() != "rule")
+      {
+      name += ".";
+      name += (*i)->GetSourceExtension();
+      }
+    srcFilesToProcess.push(name);
+    }
+  // add in the project file itself
+  name = libName;
+  name += ".vcproj.cmake";
+  srcFilesToProcess.push(name);
+  // add in the library depends for cusotm targets
+  if (target.GetType() == cmTarget::UTILITY)
+    {
+    cmCustomCommand &c = target.GetPostBuildCommands()[0];
+    for (std::vector<std::string>::iterator i = c.GetDepends().begin();
+         i != c.GetDepends().end(); ++i)
+      {
+      srcFilesToProcess.push(*i);
+      }
+    }
+  while (!srcFilesToProcess.empty())
+    {
+    // is this source the output of a custom command
+    cmSourceFile* outsf = 
+      m_Makefile->GetSourceFileWithOutput(srcFilesToProcess.front().c_str());
+    if (outsf)
+      {
+      // is it not already in the target?
+      if (std::find(classes.begin(),classes.end(),outsf) == classes.end())
+        {
+        // then add the source to this target and add it to the queue
+        classes.push_back(outsf);
+        std::string name = outsf->GetSourceName();
+        if (outsf->GetSourceExtension() != "rule")
+          {
+          name += ".";
+          name += outsf->GetSourceExtension();
+          }
+        srcFilesToProcess.push(name);
+        }
+      // add its dependencies to the list to check
+      unsigned int i;
+      for (i = 0; i < outsf->GetCustomCommand()->GetDepends().size(); ++i)
+        {
+        std::string dep = cmSystemTools::GetFilenameName(
+          outsf->GetCustomCommand()->GetDepends()[i]);
+        if (cmSystemTools::GetFilenameLastExtension(dep) == ".exe")
+          {
+          dep = cmSystemTools::GetFilenameWithoutLastExtension(dep);
+          }
+        // watch for target dependencies,
+        std::string libPath = dep + "_CMAKE_PATH";
+        const char* cacheValue = m_Makefile->GetDefinition(libPath.c_str());
+        if (cacheValue)
+          {
+          // add the depend as a utility on the target
+          target.AddUtility(dep.c_str());
+          }
+        else
+          {
+          srcFilesToProcess.push(dep);
+          }
+        }
+      }
+    // finished with this SF move to the next
+    srcFilesToProcess.pop();
+    }
+
+  // get the classes from the source lists then add them to the groups
   for(std::vector<cmSourceFile*>::const_iterator i = classes.begin(); 
       i != classes.end(); i++)
     {
@@ -656,23 +745,9 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout,
       {
       m_ModuleDefinitionFile = (*i)->GetFullPath();
       }
-    
-    cmSourceGroup& sourceGroup = m_Makefile->FindSourceGroup(source.c_str(),
-                                                             sourceGroups);
-    sourceGroup.AddSource(source.c_str(), *i);
-    }
-  
-  // add any custom rules to the source groups
-  for (std::vector<cmCustomCommand>::const_iterator cr = 
-         target.GetCustomCommands().begin(); 
-       cr != target.GetCustomCommands().end(); ++cr)
-    {
     cmSourceGroup& sourceGroup = 
-      m_Makefile->FindSourceGroup(cr->GetSourceName().c_str(),
-                                  sourceGroups);
-    cmCustomCommand cc(*cr);
-    cc.ExpandVariables(*m_Makefile);
-    sourceGroup.AddCustomCommand(cc);
+      m_Makefile->FindSourceGroup(source.c_str(), sourceGroups);
+    sourceGroup.AddSource(source.c_str(), *i);
     }
   
   // open the project
@@ -682,34 +757,18 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout,
 
   fout << "\t<Files>\n";
 
-  // if we should add regen rule then...
-  const char *suppRegenRule = 
-    m_Makefile->GetDefinition("CMAKE_SUPPRESS_REGENERATION");
-
-  // Find the group in which the CMakeLists.txt source belongs, and add
-  // the rule to generate this VCProj file.
-  if (!cmSystemTools::IsOn(suppRegenRule))
-    {
-    for(std::vector<cmSourceGroup>::reverse_iterator sg = 
-          sourceGroups.rbegin();
-        sg != sourceGroups.rend(); ++sg)
-      {
-      if(sg->Matches("CMakeLists.txt"))
-        {
-        this->AddVCProjBuildRule(*sg);
-        break;
-        }    
-      }
-    }
 
   // Loop through every source group.
   for(std::vector<cmSourceGroup>::const_iterator sg = sourceGroups.begin();
       sg != sourceGroups.end(); ++sg)
     {
-    const cmSourceGroup::BuildRules& buildRules = sg->GetBuildRules();
+    const std::vector<const cmSourceFile *> &sourceFiles = 
+      sg->GetSourceFiles();
     // If the group is empty, don't write it at all.
-    if(buildRules.empty())
-      { continue; }
+    if(sourceFiles.empty())
+      { 
+      continue; 
+      }
     
     // If the group has a name, write the header.
     std::string name = sg->GetName();
@@ -718,45 +777,41 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout,
       this->WriteVCProjBeginGroup(fout, name.c_str(), "");
       }
     
-    // Loop through each build rule in the source group.
-    for(cmSourceGroup::BuildRules::const_iterator cc =
-          buildRules.begin(); cc != buildRules.end(); ++ cc)
+    // Loop through each source in the source group.
+    for(std::vector<const cmSourceFile *>::const_iterator sf =
+          sourceFiles.begin(); sf != sourceFiles.end(); ++sf)
       {
-      std::string source = cc->first;
-      const cmSourceGroup::Commands& commands = cc->second.m_Commands;
+      std::string source = (*sf)->GetFullPath();
+      const cmCustomCommand *command = (*sf)->GetCustomCommand();
       std::string compileFlags;
       std::string additionalDeps;
-      if(cc->second.m_SourceFile)
+
+      // Check for extra compiler flags.
+      const char* cflags = (*sf)->GetProperty("COMPILE_FLAGS");
+      if(cflags)
         {
-        // Check for extra compiler flags.
-        const char* cflags = cc->second.m_SourceFile->GetProperty("COMPILE_FLAGS");
-        if(cflags)
-          {
-          compileFlags = cc->second.m_SourceFile->GetProperty("COMPILE_FLAGS");
-          }
-        if(cmSystemTools::GetFileFormat(
-             cc->second.m_SourceFile->GetSourceExtension().c_str())
-           == cmSystemTools::CXX_FILE_FORMAT)
-          {
-          // force a C++ file type
-          compileFlags += " /TP ";
-          }
-        // Check for extra object-file dependencies.
-        const char* deps =
-          cc->second.m_SourceFile->GetProperty("OBJECT_DEPENDS");
-        if(deps)
+        compileFlags = cflags;
+        }
+      if(cmSystemTools::GetFileFormat((*sf)->GetSourceExtension().c_str())
+         == cmSystemTools::CXX_FILE_FORMAT)
+        {
+        // force a C++ file type
+        compileFlags += " /TP ";
+        }
+      // Check for extra object-file dependencies.
+      const char* deps = (*sf)->GetProperty("OBJECT_DEPENDS");
+      if(deps)
+        {
+        std::vector<std::string> depends;
+        cmSystemTools::ExpandListArgument(deps, depends);
+        if(!depends.empty())
           {
-          std::vector<std::string> depends;
-          cmSystemTools::ExpandListArgument(deps, depends);
-          if(!depends.empty())
+          std::vector<std::string>::iterator i = depends.begin();
+          additionalDeps = this->ConvertToXMLOutputPath(i->c_str());
+          for(++i;i != depends.end(); ++i)
             {
-            std::vector<std::string>::iterator i = depends.begin();
-            additionalDeps = this->ConvertToXMLOutputPath(i->c_str());
-            for(++i;i != depends.end(); ++i)
-              {
-              additionalDeps += ";";
-              additionalDeps += this->ConvertToXMLOutputPath(i->c_str());
-              }
+            additionalDeps += ";";
+            additionalDeps += this->ConvertToXMLOutputPath(i->c_str());
             }
           }
         }
@@ -767,18 +822,20 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout,
         // Tell MS-Dev what the source is.  If the compiler knows how to
         // build it, then it will.
         fout << "\t\t\t\tRelativePath=\"" << d << "\">\n";
-        if (!commands.empty())
+        if (command)
           {
-          cmSourceGroup::CommandFiles totalCommand;
           std::string totalCommandStr;
-          totalCommandStr = this->CombineCommands(commands, totalCommand,
-                                                  source.c_str());
-          const char* comment = totalCommand.m_Comment.c_str();
+          totalCommandStr = 
+            cmSystemTools::ConvertToOutputPath(command->GetCommand().c_str()); 
+          totalCommandStr += " ";
+          totalCommandStr += command->GetArguments();
+          totalCommandStr += "\n";
+          const char* comment = command->GetComment().c_str();
           const char* flags = compileFlags.size() ? compileFlags.c_str(): 0;
           this->WriteCustomRule(fout, source.c_str(), totalCommandStr.c_str(), 
                                 (*comment?comment:"Custom Rule"),
-                                totalCommand.m_Depends,
-                                totalCommand.m_Outputs, flags);
+                                command->GetDepends(),
+                                command->GetOutput().c_str(), flags);
           }
         else if(compileFlags.size() || additionalDeps.length())
           {
@@ -820,13 +877,14 @@ void cmLocalVisualStudio7Generator::WriteVCProjFile(std::ostream& fout,
 }
 
 
-void cmLocalVisualStudio7Generator::WriteCustomRule(std::ostream& fout,
-                                          const char* source,
-                                          const char* command,
-                                          const char* comment,
-                                          const std::set<std::string>& depends,
-                                          const std::set<std::string>& outputs,
-                                          const char* compileFlags)
+void cmLocalVisualStudio7Generator::
+WriteCustomRule(std::ostream& fout,
+                const char* source,
+                const char* command,
+                const char* comment,
+                const std::vector<std::string>& depends,
+                const char *output,
+                const char* compileFlags)
 {
   std::string cmd = command;
   cmSystemTools::ReplaceString(cmd, "\"", "&quot;");
@@ -847,17 +905,13 @@ void cmLocalVisualStudio7Generator::WriteCustomRule(std::ostream& fout,
     fout << "\t\t\t\t\t<Tool\n"
          << "\t\t\t\t\tName=\"VCCustomBuildTool\"\n"
          << "\t\t\t\t\tDescription=\"Building " << comment;
-    std::set<std::string>::const_iterator it;
-    for ( it = outputs.begin(); it != outputs.end(); it ++ )
-      {
-      fout << " " << *it;
-      }
+    fout << " " << output;
     fout << "\"\n"
          << "\t\t\t\t\tCommandLine=\"" << cmd << "\n\"\n"
          << "\t\t\t\t\tAdditionalDependencies=\"";
     // Write out the dependencies for the rule.
     std::string temp;
-    for(std::set<std::string>::const_iterator d = depends.begin();
+    for(std::vector<std::string>::const_iterator d = depends.begin();
         d != depends.end(); ++d)
       {
       fout << this->ConvertToXMLOutputPath(d->c_str())
@@ -865,26 +919,14 @@ void cmLocalVisualStudio7Generator::WriteCustomRule(std::ostream& fout,
       }
     fout << "\"\n";
     fout << "\t\t\t\t\tOutputs=\"";
-    if(outputs.size() == 0)
+    if(output == 0)
       {
       fout << source << "_force";
       }
     
     bool first = true;
-    // Write a rule for every output generated by this command.
-    for(std::set<std::string>::const_iterator output = outputs.begin();
-        output != outputs.end(); ++output)
-      {
-      if(!first)
-        {
-        fout << ";";
-        }
-      else
-        {
-        first = false;
-        }
-      fout << this->ConvertToXMLOutputPathSingle(output->c_str());
-      }
+    // Write a rule for the output generated by this command.
+    fout << this->ConvertToXMLOutputPathSingle(output);
     fout << "\"/>\n";
     fout << "\t\t\t\t</FileConfiguration>\n";
     }
@@ -907,85 +949,90 @@ void cmLocalVisualStudio7Generator::WriteVCProjEndGroup(std::ostream& fout)
 }
 
 
-
-
-
-std::string
-cmLocalVisualStudio7Generator::CombineCommands(
-  const cmSourceGroup::Commands &commands,
-  cmSourceGroup::CommandFiles &totalCommand,
-  const char *source)
-  
+// look for custom rules on a target and collect them together
+void cmLocalVisualStudio7Generator::OutputTargetRules(std::ostream& fout,
+                                                      const cmTarget &target, 
+                                                      const char *libName)
 {
-  // Loop through every custom command generating code from the
-  // current source.
-  // build up the depends and outputs and commands 
-  std::string totalCommandStr = "";
-  std::string temp;
-  for(cmSourceGroup::Commands::const_iterator c = commands.begin();
-      c != commands.end(); ++c)
+  if (target.GetType() > cmTarget::UTILITY)
     {
-    temp= 
-      cmSystemTools::ConvertToOutputPath(c->second.m_Command.c_str()); 
-    totalCommandStr += temp;
-    totalCommandStr += " ";
-    totalCommandStr += c->second.m_Arguments;
-    totalCommandStr += "\n";
-    totalCommand.Merge(c->second);
-    totalCommand.m_Comment = c->second.m_Comment.c_str();
-    }      
-  // Create a dummy file with the name of the source if it does
-  // not exist
-  if(totalCommand.m_Outputs.empty())
-    { 
-    std::string dummyFile = m_Makefile->GetStartOutputDirectory();
-    dummyFile += "/";
-    dummyFile += source;
-    if(!cmSystemTools::FileExists(dummyFile.c_str()))
+    return;
+    }
+  
+  // add the pre build rules
+  fout << "\t\t\t<Tool\n\t\t\t\tName=\"VCPreBuildEventTool\"";
+  bool init = false;
+  for (std::vector<cmCustomCommand>::const_iterator cr = 
+         target.GetPreBuildCommands().begin(); 
+       cr != target.GetPreBuildCommands().end(); ++cr)
+    {
+    cmCustomCommand cc(*cr);
+    cc.ExpandVariables(*m_Makefile);
+    if(!init)
       {
-      std::ofstream fout(dummyFile.c_str());
-      fout << "Dummy file created by cmake as unused source for utility command.\n";
+      fout << "\nCommandLine=\"";
+      init = true;
       }
+    std::string args = cc.GetArguments();
+    cmSystemTools::ReplaceString(args, "\"", "&quot;");
+    fout << this->ConvertToXMLOutputPath(cc.GetCommand().c_str()) << " " << 
+      args << "\n";
     }
-  return totalCommandStr;
-}
-
-
-// look for custom rules on a target and collect them together
+  if (init)
+    {
+    fout << "\"";
+    }
+  fout << "/>\n";
 
-void cmLocalVisualStudio7Generator::OutputTargetRules(std::ostream& fout,
-                                            const cmTarget &target, 
-                                            const char *libName)
-{
-  if (target.GetType() >= cmTarget::UTILITY)
+  // add the pre Link rules
+  fout << "\t\t\t<Tool\n\t\t\t\tName=\"VCPreLinkEventTool\"";
+  init = false;
+  for (std::vector<cmCustomCommand>::const_iterator cr = 
+         target.GetPreLinkCommands().begin(); 
+       cr != target.GetPreLinkCommands().end(); ++cr)
     {
-    return;
+    cmCustomCommand cc(*cr);
+    cc.ExpandVariables(*m_Makefile);
+    if(!init)
+      {
+      fout << "\nCommandLine=\"";
+      init = true;
+      }
+    std::string args = cc.GetArguments();
+    cmSystemTools::ReplaceString(args, "\"", "&quot;");
+    fout << this->ConvertToXMLOutputPath(cc.GetCommand().c_str()) << " " << 
+      args << "\n";
+    }
+  if (init)
+    {
+    fout << "\"";
     }
+  fout << "/>\n";
   
-  // Find the group in which the lix exe custom rules belong
-  bool init = false;
+  // add the PostBuild rules
+  fout << "\t\t\t<Tool\n\t\t\t\tName=\"VCPostBuildEventTool\"";
+  init = false;
   for (std::vector<cmCustomCommand>::const_iterator cr = 
-         target.GetCustomCommands().begin(); 
-       cr != target.GetCustomCommands().end(); ++cr)
+         target.GetPostBuildCommands().begin(); 
+       cr != target.GetPostBuildCommands().end(); ++cr)
     {
     cmCustomCommand cc(*cr);
     cc.ExpandVariables(*m_Makefile);
-    if (cc.GetSourceName() == libName)
+    if(!init)
       {
-      if(!init)
-        {
-        fout << "\nCommandLine=\"";
-        init = true;
-        }
-      std::string args = cc.GetArguments();
-      cmSystemTools::ReplaceString(args, "\"", "&quot;");
-      fout << this->ConvertToXMLOutputPath(cc.GetCommand().c_str()) << " " << args << "\n";
+      fout << "\nCommandLine=\"";
+      init = true;
       }
+    std::string args = cc.GetArguments();
+    cmSystemTools::ReplaceString(args, "\"", "&quot;");
+    fout << this->ConvertToXMLOutputPath(cc.GetCommand().c_str()) << " " << 
+      args << "\n";
     }
   if (init)
     {
     fout << "\"";
     }
+  fout << "/>\n";
 }
 
 void 

+ 5 - 9
Source/cmLocalVisualStudio7Generator.h

@@ -22,9 +22,8 @@
 class cmMakeDepend;
 class cmTarget;
 class cmSourceFile;
-
-// please remove me.... Yuck
-#include "cmSourceGroup.h"
+class cmCustomCommand;
+class cmSourceGroup;
 
 /** \class cmLocalVisualStudio7Generator
  * \brief Write a LocalUnix makefiles.
@@ -74,7 +73,7 @@ private:
   void CreateSingleVCProj(const char *lname, cmTarget &tgt);
   void WriteVCProjFile(std::ostream& fout, const char *libName, 
                        cmTarget &tgt);
-  void AddVCProjBuildRule(cmSourceGroup&);
+  void AddVCProjBuildRule();
   void WriteConfigurations(std::ostream& fout,
                            const char *libName,
                            const cmTarget &tgt);
@@ -107,15 +106,12 @@ private:
                           const char* group,
                           const char* filter);
   void WriteVCProjEndGroup(std::ostream& fout);
-  std::string CombineCommands(const cmSourceGroup::Commands &commands,
-                              cmSourceGroup::CommandFiles &totalCommand,
-                              const char *source);
   void WriteCustomRule(std::ostream& fout,
                        const char* source,
                        const char* command,
                        const char* comment,
-                       const std::set<std::string>& depends,
-                       const std::set<std::string>& outputs,
+                       const std::vector<std::string>& depends,
+                       const char* output,
                        const char* extraFlags);
 
   std::vector<std::string> m_CreatedProjectNames;

+ 271 - 26
Source/cmMakefile.cxx

@@ -65,6 +65,7 @@ cmMakefile::cmMakefile()
   this->AddSourceGroup("Source Files", 
                        "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|m|mm|rc|def|r|odl|idl|hpj|bat)$");
   this->AddSourceGroup("Header Files", "\\.(h|h\\+\\+|hm|hpp|hxx|in|txx|inl)$");
+  this->AddSourceGroup("CMake Rules", "\\.rule$");
   this->AddDefaultDefinitions();
 }
 
@@ -169,7 +170,7 @@ void cmMakefile::Print() const
   for( std::vector<cmSourceGroup>::const_iterator i = m_SourceGroups.begin();
        i != m_SourceGroups.end(); ++i)
     {
-    i->Print();
+    std::cout << "Source Group: " << i->GetName() << std::endl;
     }
 }
 
@@ -441,6 +442,7 @@ void cmMakefile::ConfigureFinalPass()
 }
 
 
+// this is the old style signature, we convert to new style
 void cmMakefile::AddCustomCommand(const char* source,
                                   const char* command,
                                   const std::vector<std::string>& commandArgs,
@@ -448,6 +450,130 @@ void cmMakefile::AddCustomCommand(const char* source,
                                   const std::vector<std::string>& outputs,
                                   const char *target,
                                   const char *comment) 
+{
+  if (strcmp(source,target))
+    {
+    // what a pain, for backwards compatibility we will try to
+    // convert this to an output based rule... so for each output..
+    for(std::vector<std::string>::const_iterator d = outputs.begin();
+        d != outputs.end(); ++d)
+      {
+      this->AddCustomCommandToOutput(d->c_str(), command, commandArgs, 
+                                     source, depends, comment);
+      // add the output to the target?
+      std::string sname = *d;
+      sname += ".rule";
+      if (!this->GetSource(sname.c_str()))
+        {
+        m_Targets[target].GetSourceLists().push_back(source);
+        }
+      }
+    }
+  else
+    {
+    this->AddCustomCommandToTarget(target, command, commandArgs, 
+                                   cmTarget::POST_BUILD,
+                                   comment);
+    }
+}
+
+void cmMakefile::AddCustomCommand(const char* source,
+                                  const char* command,
+                                  const std::vector<std::string>& commandArgs,
+                                  const std::vector<std::string>& depends,
+                                  const char* output, 
+                                  const char *target) 
+{
+  std::vector<std::string> outputs;
+  outputs.push_back(output);
+  this->AddCustomCommand(source, command, commandArgs, depends, 
+                         outputs, target);
+}
+
+void cmMakefile::
+AddCustomCommandToOutput(const char* output,
+                         const char* command,
+                         const std::vector<std::string>& commandArgs,
+                         const char *main_dependency,
+                         const std::vector<std::string>& depends,
+                         const char *comment,
+                         bool replace)
+{
+  cmSourceFile *file = 0;
+  std::string outName = output;
+  outName += ".rule";
+  
+  // OK this rule will be placed on a generated output file unless the main
+  // depednency was specified.
+  if (main_dependency && main_dependency[0] != '\0')
+    {
+    file = this->GetSource(main_dependency);
+    if (file && file->GetCustomCommand() && !replace)
+      {
+      // generate a source instead
+      file = 0;
+      }
+    else
+      {
+      file = this->GetOrCreateSource(main_dependency);
+      }
+    }
+
+  if (!file)
+    {
+    file = this->GetSource(outName.c_str());
+    if (file && file->GetCustomCommand() && !replace)
+      {
+      cmSystemTools::Error("Attempt to add a custom rule to an output that already has a custom rule. For output: ",  output);
+      return;
+      }
+    // create a cmSourceFile for the output
+    file = this->GetOrCreateSource(outName.c_str(), true);
+    // always mark as generated
+    file->SetProperty("GENERATED","1");
+    }
+  
+  // always create the output and mark it generated
+  cmSourceFile *out = this->GetOrCreateSource(output, true);
+  out->SetProperty("GENERATED","1");
+  
+  // process the command
+  std::string expandC = command;
+  this->ExpandVariablesInString(expandC);
+  std::string c = cmSystemTools::EscapeSpaces(expandC.c_str());
+  
+  std::string combinedArgs;
+  unsigned int i;
+  for (i = 0; i < commandArgs.size(); ++i)
+    {
+    expandC = commandArgs[i].c_str();
+    this->ExpandVariablesInString(expandC);
+    combinedArgs += cmSystemTools::EscapeSpaces(expandC.c_str());
+    combinedArgs += " ";
+    }
+  std::vector<std::string> depends2(depends);
+  if (main_dependency && main_dependency[0] != '\0')
+    {
+    depends2.push_back(main_dependency);
+    }
+  cmCustomCommand *cc = 
+    new cmCustomCommand(c.c_str(),combinedArgs.c_str(),depends2, output);
+  if ( comment && comment[0] )
+    {
+    cc->SetComment(comment);
+    }
+  if (file->GetCustomCommand())
+    {
+    delete file->GetCustomCommand();
+    }
+  file->SetCustomCommand(cc);
+}
+
+void cmMakefile::
+AddCustomCommandToTarget(const char* target, const char* command,
+                         const std::vector<std::string>& commandArgs,
+                         cmTarget::CustomCommandType type,
+                         const char *comment) 
 {
   // find the target, 
   if (m_Targets.find(target) != m_Targets.end())
@@ -455,7 +581,7 @@ void cmMakefile::AddCustomCommand(const char* source,
     std::string expandC = command;
     this->ExpandVariablesInString(expandC);
     std::string c = cmSystemTools::EscapeSpaces(expandC.c_str());
-
+    
     std::string combinedArgs;
     unsigned int i;
     
@@ -467,12 +593,23 @@ void cmMakefile::AddCustomCommand(const char* source,
       combinedArgs += " ";
       }
     
-    cmCustomCommand cc(source,c.c_str(),combinedArgs.c_str(),depends,outputs);
+    cmCustomCommand cc(c.c_str(),combinedArgs.c_str());
     if ( comment && comment[0] )
       {
       cc.SetComment(comment);
       }
-    m_Targets[target].GetCustomCommands().push_back(cc);
+    switch (type)
+      {
+      case cmTarget::PRE_BUILD:
+        m_Targets[target].GetPreBuildCommands().push_back(cc);
+        break;
+      case cmTarget::PRE_LINK:
+        m_Targets[target].GetPreLinkCommands().push_back(cc);
+        break;
+      case cmTarget::POST_BUILD:
+        m_Targets[target].GetPostBuildCommands().push_back(cc);
+        break;
+      }
     std::string cacheCommand = command;
     this->ExpandVariablesInString(cacheCommand);
     if(this->GetCacheManager()->GetCacheValue(cacheCommand.c_str()))
@@ -483,18 +620,6 @@ void cmMakefile::AddCustomCommand(const char* source,
     }
 }
 
-void cmMakefile::AddCustomCommand(const char* source,
-                                  const char* command,
-                                  const std::vector<std::string>& commandArgs,
-                                  const std::vector<std::string>& depends,
-                                  const char* output, 
-                                  const char *target) 
-{
-  std::vector<std::string> outputs;
-  outputs.push_back(output);
-  this->AddCustomCommand(source, command, commandArgs, depends, outputs, target);
-}
-
 void cmMakefile::AddDefineFlag(const char* flag)
 {
   m_DefineFlags += " ";
@@ -799,11 +924,12 @@ void cmMakefile::AddExecutable(const char *exeName,
 void cmMakefile::AddUtilityCommand(const char* utilityName,
                                    const char* command,
                                    const char* arguments,
-                                   bool all)
+                                   bool all,
+                                   const std::vector<std::string> &depends)
 {
   std::vector<std::string> empty;
   this->AddUtilityCommand(utilityName,command,arguments,all,
-                          empty,empty);
+                          depends, empty);
 }
 
 void cmMakefile::AddUtilityCommand(const char* utilityName,
@@ -816,11 +942,54 @@ void cmMakefile::AddUtilityCommand(const char* utilityName,
   cmTarget target;
   target.SetType(cmTarget::UTILITY);
   target.SetInAll(all);
-  cmCustomCommand cc(utilityName, command, arguments, dep, out);
-  target.GetCustomCommands().push_back(cc);
+  if (out.size() > 1)
+    {
+    cmSystemTools::Error(
+      "Utility targets can only have one output. For utilityNamed: ",
+      utilityName);
+    return;
+    }
+  if (out.size())
+    {
+    cmCustomCommand cc(command, arguments, dep, out[0].c_str());
+    target.GetPostBuildCommands().push_back(cc);
+    }
+  else
+    {
+    cmCustomCommand cc(command, arguments, dep, NULL);
+    target.GetPostBuildCommands().push_back(cc);
+    }
   m_Targets.insert(cmTargets::value_type(utilityName,target));
 }
 
+cmSourceFile *cmMakefile::GetSourceFileWithOutput(const char *cname)
+{
+  std::string name = cname;
+  
+  // look through all the source files that have custom commands
+  // and see if the custom command has the passed source file as an output
+  // keep in mind the possible .rule extension that may be tacked on
+  for(std::vector<cmSourceFile*>::const_iterator i = m_SourceFiles.begin(); 
+      i != m_SourceFiles.end(); ++i)
+    {
+    // does this source file have a custom command?
+    if ((*i)->GetCustomCommand())
+      {
+      // is the output of the custom command match the source files name
+      std::string out = (*i)->GetCustomCommand()->GetOutput();
+      if (out.rfind(name) != out.npos &&
+          out.rfind(name) == out.size() - name.size())
+        {
+        return *i;
+        }
+      }
+    }
+  
+  // otherwise return NULL
+  return NULL;
+}
+
+
 cmSourceGroup* cmMakefile::GetSourceGroup(const char* name)
 {
   // First see if the group exists.  If so, replace its regular expression.
@@ -946,6 +1115,19 @@ void cmMakefile::ExpandVariables()
     }
 }
 
+void cmMakefile::ExpandVariablesInCustomCommands()
+{
+  for(std::vector<cmSourceFile*>::iterator i = m_SourceFiles.begin();
+      i != m_SourceFiles.end(); ++i)
+    {
+    cmCustomCommand *cc = (*i)->GetCustomCommand();
+    if (cc)
+      {
+      cc->ExpandVariables(*this);
+      }
+    }
+}
+
 bool cmMakefile::IsOn(const char* name) const
 {
   const char* value = this->GetDefinition(name);
@@ -1254,7 +1436,7 @@ cmMakefile::FindSourceGroup(const char* source,
   std::string::size_type pos = file.rfind('/');
   if(pos != std::string::npos)
     {
-    file = file.substr(pos, file.length()-pos);
+    file = file.substr(pos+1);
     }
 
   for(std::vector<cmSourceGroup>::reverse_iterator sg = groups.rbegin();
@@ -1402,11 +1584,11 @@ cmSourceFile* cmMakefile::GetSource(const char* sourceName) const
 {
   std::string s = cmSystemTools::GetFilenameName(sourceName);
   std::string ext;
-  std::string::size_type pos = s.rfind('.');
-  if(pos != std::string::npos)
+  ext = cmSystemTools::GetFilenameLastExtension(s);
+  s = s.substr(0, s.length()-ext.length());
+  if ( ext.length() && ext[0] == '.' )
     {
-    ext = s.substr(pos+1, s.size() - pos-1);
-    s = s.substr(0, pos);
+    ext = ext.substr(1);
     }
   for(std::vector<cmSourceFile*>::const_iterator i = m_SourceFiles.begin();
       i != m_SourceFiles.end(); ++i)
@@ -1422,8 +1604,71 @@ cmSourceFile* cmMakefile::GetSource(const char* sourceName) const
   return 0;
 }
 
+cmSourceFile* cmMakefile::GetOrCreateSource(const char* sourceName, 
+                                            bool generated)
+{
+  // check to see if it exists
+  cmSourceFile* ret = this->GetSource(sourceName);
+  if (ret)
+    {
+    return ret;
+    }
+  
+  // we must create one
+  std::string newfile = sourceName;
+  cmSourceFile file; 
+  std::string path = cmSystemTools::GetFilenamePath(newfile);
+  if(generated)
+    {
+    std::string ext = cmSystemTools::GetFilenameLastExtension(newfile);
+    std::string name_no_ext = cmSystemTools::GetFilenameName(newfile.c_str());
+    name_no_ext = name_no_ext.substr(0, name_no_ext.length()-ext.length());
+    if ( ext.length() && ext[0] == '.' )
+      {
+      ext = ext.substr(1);
+      }
+    if((path.size() && path[0] == '/') ||
+       (path.size() > 1 && path[1] == ':'))
+      {
+      file.SetName(name_no_ext.c_str(), path.c_str(), ext.c_str(), false);
+      }
+    else
+      {
+      file.SetName(name_no_ext.c_str(), this->GetCurrentOutputDirectory(), 
+                   ext.c_str(), false);
+      }
+    }
+  else
+    {
+    // if this is a full path then 
+    if((path.size() && path[0] == '/') ||
+       (path.size() > 1 && path[1] == ':'))
+      {
+      file.SetName(cmSystemTools::GetFilenameName(newfile.c_str()).c_str(), 
+                   path.c_str(),
+                   this->GetSourceExtensions(),
+                   this->GetHeaderExtensions());
+      }
+    else
+      {
+      file.SetName(newfile.c_str(), this->GetCurrentDirectory(), 
+                   this->GetSourceExtensions(),
+                   this->GetHeaderExtensions());
+      }    
+    }
+  // add the source file to the makefile
+  this->AddSource(file);
+  ret = this->GetSource(sourceName);
+  if (!ret)
+    {
+    cmSystemTools::Error(
+      "CMake failed to properly look up cmSourceFile: ", sourceName);
+    int i = *(int *)0x0;
+    }
+  return ret;
+}
 
-
+  
 cmSourceFile* cmMakefile::AddSource(cmSourceFile const&sf)
 {
   // check to see if it exists

+ 41 - 2
Source/cmMakefile.h

@@ -118,6 +118,22 @@ public:
    */
   void Print() const;
   
+  /**
+   * Add a custom command to the build.
+   */
+  void AddCustomCommandToOutput(const char* output,
+                                const char* command,
+                                const std::vector<std::string>& commandArgs,
+                                const char *main_dependency,
+                                const std::vector<std::string>& depends,
+                                const char *comment = 0,
+                                bool replace = false);
+  void AddCustomCommandToTarget(const char* target,
+                                const char* command,
+                                const std::vector<std::string>& commandArgs,
+                                cmTarget::CustomCommandType type,
+                                const char *comment = 0);
+  
   /**
    * Add a custom command to the build.
    */
@@ -156,7 +172,8 @@ public:
   void AddUtilityCommand(const char* utilityName,
                          const char* command,
                          const char* arguments,
-                         bool all);
+                         bool all,
+                         const std::vector<std::string> &depends);
   void AddUtilityCommand(const char* utilityName,
                          const char* command,
                          const char* arguments,
@@ -417,6 +434,14 @@ public:
   cmSourceFile* GetSource(const char* sourceName) const;
   ///! Add a new cmSourceFile to the list of sources for this makefile.
   cmSourceFile* AddSource(cmSourceFile const&);
+
+  /** Get a cmSourceFile pointer for a given source name, if the name is
+   *  not found, then create the source file and return it. generated 
+   * indicates if it is a generated file, this is used in determining
+   * how to create the source file instance e.g. name
+   */
+  cmSourceFile* GetOrCreateSource(const char* sourceName, 
+                                  bool generated = false);
   
   /**
    * Obtain a list of auxiliary source directories.
@@ -508,7 +533,8 @@ public:
    * Expand variables in the makefiles ivars such as link directories etc
    */
   void ExpandVariables();  
-
+  void ExpandVariablesInCustomCommands();
+  
   /**
    * find what source group this source is in
    */
@@ -559,6 +585,19 @@ public:
    */ 
   cmake *GetCMakeInstance() const;
 
+  /**
+   * Get all the source files this makefile knows about
+   */
+  const std::vector<cmSourceFile*> &GetSourceFiles() const 
+    {return m_SourceFiles;}
+  std::vector<cmSourceFile*> &GetSourceFiles() {return m_SourceFiles;}
+
+  /**
+   * Is there a source file that has the provided source file as an output?
+   * if so then return it
+   */
+  cmSourceFile *GetSourceFileWithOutput(const char *outName);
+  
 protected:
   // add link libraries and directories to the target
   void AddGlobalLinkInformation(const char* name, cmTarget& target);

+ 20 - 61
Source/cmSetSourceFilesPropertiesCommand.cxx

@@ -29,6 +29,7 @@ bool cmSetSourceFilesPropertiesCommand::InitialPass(
   // first collect up the list of files
   std::vector<std::string> propertyPairs;
   bool doingFiles = true;
+  bool generated = false;
   int numFiles = 0;
   std::vector<std::string>::const_iterator j;
   for(j= args.begin(); j != args.end();++j)
@@ -49,6 +50,7 @@ bool cmSetSourceFilesPropertiesCommand::InitialPass(
     else if(*j == "GENERATED")
       {
       doingFiles = false;
+      generated = true;
       propertyPairs.push_back("GENERATED");
       propertyPairs.push_back("1");
       }
@@ -84,7 +86,18 @@ bool cmSetSourceFilesPropertiesCommand::InitialPass(
       while (j != args.end())
         {
         propertyPairs.push_back(*j);
-        ++j;
+        if(*j == "GENERATED")
+          {
+          ++j;
+          if(j != args.end() && cmSystemTools::IsOn(j->c_str()))
+            {
+            generated = true;
+            }
+          }
+        else
+          {
+          ++j;
+          }
         if(j == args.end())
           {
           this->SetError("called with incorrect number of arguments.");
@@ -112,67 +125,13 @@ bool cmSetSourceFilesPropertiesCommand::InitialPass(
   unsigned int k;
   for(i = 0; i < numFiles; ++i)
     {   
-    // if the file is already in the makefile just set properites on it
-    cmSourceFile* sf = m_Makefile->GetSource(args[i].c_str());
-    if(sf)
+    // get the source file
+    cmSourceFile* sf =
+      m_Makefile->GetOrCreateSource(args[i].c_str(), generated);
+    // now loop through all the props and set them
+    for (k = 0; k < propertyPairs.size(); k = k + 2)
       {
-      // now loop through all the props and set them
-      for (k = 0; k < propertyPairs.size(); k = k + 2)
-        {
-        sf->SetProperty(propertyPairs[k].c_str(),propertyPairs[k+1].c_str());
-        }
-      }
-    // if file is not already in the makefile, then add it
-    else
-      { 
-      std::string newfile = args[i];
-      cmSourceFile file; 
-      std::string path = cmSystemTools::GetFilenamePath(newfile);
-      // now loop through all the props and set them
-      for (k = 0; k < propertyPairs.size(); k = k + 2)
-        {
-        file.SetProperty(propertyPairs[k].c_str(),propertyPairs[k+1].c_str());
-        }
-      if(file.GetPropertyAsBool("GENERATED"))
-        {
-        std::string ext = cmSystemTools::GetFilenameExtension(newfile);
-        std::string name_no_ext = cmSystemTools::GetFilenameName(newfile.c_str());
-        name_no_ext = name_no_ext.substr(0, name_no_ext.length()-ext.length());
-        if ( ext.length() && ext[0] == '.' )
-          {
-          ext = ext.substr(1);
-          }
-        if((path.size() && path[0] == '/') ||
-           (path.size() > 1 && path[1] == ':'))
-          {
-          file.SetName(name_no_ext.c_str(), path.c_str(), ext.c_str(), false);
-          }
-        else
-          {
-          file.SetName(name_no_ext.c_str(), m_Makefile->GetCurrentOutputDirectory(), 
-                       ext.c_str(), false);
-          }
-        }
-      else
-        {
-        // if this is a full path then 
-        if((path.size() && path[0] == '/') ||
-           (path.size() > 1 && path[1] == ':'))
-          {
-          file.SetName(cmSystemTools::GetFilenameName(newfile.c_str()).c_str(), 
-                       path.c_str(),
-                       m_Makefile->GetSourceExtensions(),
-                       m_Makefile->GetHeaderExtensions());
-          }
-        else
-          {
-          file.SetName(newfile.c_str(), m_Makefile->GetCurrentDirectory(),
-                       m_Makefile->GetSourceExtensions(),
-                       m_Makefile->GetHeaderExtensions());
-          }    
-        }
-      // add the source file to the makefile
-      m_Makefile->AddSource(file);
+      sf->SetProperty(propertyPairs[k].c_str(),propertyPairs[k+1].c_str());
       }
     }
   return true;

+ 17 - 3
Source/cmSourceFile.h

@@ -18,6 +18,7 @@
 #define cmSourceFile_h
 
 #include "cmStandardIncludes.h"
+#include "cmCustomCommand.h"
 
 /** \class cmSourceFile
  * \brief Represent a class loaded from a makefile.
@@ -34,6 +35,11 @@ public:
    */
   cmSourceFile()
     {
+      m_CustomCommand = 0;
+    }
+  ~cmSourceFile()
+    {
+      if (m_CustomCommand) { delete m_CustomCommand; }
     }
   
   /**
@@ -45,6 +51,15 @@ public:
                const std::vector<std::string>& sourceExts,
                const std::vector<std::string>& headerExts);
 
+  /**
+   * Get the list of the custom commands for this source file
+   */
+  const cmCustomCommand *GetCustomCommand() const 
+    {return m_CustomCommand;}
+  cmCustomCommand *GetCustomCommand() {return m_CustomCommand;}
+  void SetCustomCommand(cmCustomCommand *cc) 
+    { m_CustomCommand = cc;}
+    
   /**
    * Set the name of the file, given the directory the file should be in.  IN
    * this version the extension is provided in the call. This is useful for
@@ -77,8 +92,7 @@ public:
   void SetSourceName(const char *name) {m_SourceName = name;}
 
   /**
-   * The file name associated with stripped off directory and extension.
-   * (In most cases this is the name of the class.)
+   * The file extension associated with source file
    */
   const std::string &GetSourceExtension() const {return m_SourceExtension;}
   void SetSourceExtension(const char *name) {m_SourceExtension = name;}
@@ -90,8 +104,8 @@ public:
   std::vector<std::string> &GetDepends() {return m_Depends;}
 
 private:
-
   std::map<cmStdString,cmStdString> m_Properties;
+  cmCustomCommand *m_CustomCommand;
   std::string m_FullPath;
   std::string m_SourceName;
   std::string m_SourceExtension;

+ 2 - 96
Source/cmSourceGroup.cxx

@@ -33,7 +33,7 @@ cmSourceGroup::cmSourceGroup(const char* name, const char* regex):
 cmSourceGroup::cmSourceGroup(const cmSourceGroup& r):
   m_Name(r.m_Name),
   m_GroupRegex(r.m_GroupRegex),
-  m_BuildRules(r.m_BuildRules)
+  m_SourceFiles(r.m_SourceFiles)
 {
 }
 
@@ -52,100 +52,6 @@ bool cmSourceGroup::Matches(const char* name)
  */
 void cmSourceGroup::AddSource(const char* name, const cmSourceFile* sf)
 {
-  BuildRules::iterator s = m_BuildRules.find(name);
-  if(s == m_BuildRules.end())
-    {
-    SourceAndCommands sc;
-    sc.m_SourceFile = sf;
-    // The source was not found.  Add it with no commands.
-    m_BuildRules[name] = sc;
-    return;
-    }
+  m_SourceFiles.push_back(sf);
 }
 
-
-/**
- * Add a source and corresponding custom command to the group.  If the
- * source already exists, the command will be added to its set of commands.
- * If the command also already exists, the given dependencies and outputs
- * are added to it.
- */
-void cmSourceGroup::AddCustomCommand(const cmCustomCommand &cmd)
-{
-  std::string commandAndArgs = cmd.GetCommandAndArguments();
-  BuildRules::iterator s = m_BuildRules.find(cmd.GetSourceName());
-  if(s == m_BuildRules.end())
-    {
-    // The source was not found.  Add it with this command.
-    CommandFiles& cmdFiles = 
-      m_BuildRules[cmd.GetSourceName()].m_Commands[commandAndArgs];
-    cmdFiles.m_Command = cmd.GetCommand();
-    cmdFiles.m_Comment = cmd.GetComment();
-    cmdFiles.m_Arguments = cmd.GetArguments();
-    cmdFiles.m_Depends.insert(cmd.GetDepends().begin(),cmd.GetDepends().end());
-    cmdFiles.m_Outputs.insert(cmd.GetOutputs().begin(),cmd.GetOutputs().end());
-    return;
-    }
-  
-  // The source already exists.  See if the command exists.
-  Commands& commands = s->second.m_Commands;
-  Commands::iterator c = commands.find(commandAndArgs);
-  if(c == commands.end())
-    {
-    // The command did not exist.  Add it.
-    commands[commandAndArgs].m_Command = cmd.GetCommand();
-    commands[commandAndArgs].m_Comment = cmd.GetComment();
-    commands[commandAndArgs].m_Arguments = cmd.GetArguments();
-    commands[commandAndArgs].m_Depends.insert(cmd.GetDepends().begin(),
-                                              cmd.GetDepends().end());
-    commands[commandAndArgs].m_Outputs.insert(cmd.GetOutputs().begin(),
-                                              cmd.GetOutputs().end());
-    return;
-    }
-  
-  // The command already exists for this source.  Merge the sets.
-  CommandFiles& commandFiles = c->second;
-  commandFiles.m_Depends.insert(cmd.GetDepends().begin(), 
-                                cmd.GetDepends().end());
-  commandFiles.m_Outputs.insert(cmd.GetOutputs().begin(),
-                                cmd.GetOutputs().end());
-}
-
-void cmSourceGroup::Print() const
-{
-  std::cout << "cmSourceGroup: " << m_Name.c_str() << "\n";
-  for(BuildRules::const_iterator i = m_BuildRules.begin();
-      i != m_BuildRules.end(); ++i)
-    {
-    std::cout << "BuildRule: " << i->first.c_str() << "\n";
-    for(Commands::const_iterator j = i->second.m_Commands.begin();
-        j != i->second.m_Commands.end(); ++j)
-      {
-      std::cout << "FullCommand: " << j->first.c_str() << "\n";
-      std::cout << "Command: " << j->second.m_Command.c_str() << "\n";
-      std::cout << "Arguments: " << j->second.m_Arguments.c_str() << "\n";
-      std::cout << "Command Outputs " 
-                  << static_cast<int>(j->second.m_Outputs.size()) << "\n";
-      std::cout << "Command Depends " 
-                  << static_cast<int>(j->second.m_Depends.size()) << "\n";
-      }
-    }
-}
-
-
-void cmSourceGroup::CommandFiles::Merge(const CommandFiles &r)
-{
-  std::set<std::string>::const_iterator dep = r.m_Depends.begin();
-  std::set<std::string>::const_iterator out = r.m_Outputs.begin();
-  for (;dep != r.m_Depends.end(); ++dep)
-    {
-    this->m_Depends.insert(*dep);
-    }
-  for (;out != r.m_Outputs.end(); ++out)
-    {
-    this->m_Outputs.insert(*out);
-    }
-}
-
-
-

+ 11 - 41
Source/cmSourceGroup.h

@@ -19,7 +19,6 @@
 
 #include "cmStandardIncludes.h"
 #include "cmRegularExpression.h"
-#include "cmCustomCommand.h"
 class cmSourceFile;
 
 /** \class cmSourceGroup
@@ -35,47 +34,20 @@ public:
   cmSourceGroup(const cmSourceGroup&);
   ~cmSourceGroup() {}
   
-  struct CommandFiles
-  {
-    CommandFiles() {}
-    CommandFiles(const CommandFiles& r):
-      m_Comment(r.m_Comment), m_Outputs(r.m_Outputs), m_Depends(r.m_Depends) {}
-    
-    void Merge(const CommandFiles &r);
-    
-    std::string m_Command;
-    std::string m_Arguments;
-    std::string m_Comment;
-    std::set<std::string> m_Outputs;
-    std::set<std::string> m_Depends;
-  };
-  
-  /**
-   * Map from command to its output/depends sets.
-   */
-  typedef std::map<cmStdString, CommandFiles> Commands;
-
-  struct SourceAndCommands
-  {
-    SourceAndCommands(): m_SourceFile(0) {}
-    const cmSourceFile* m_SourceFile;
-    Commands m_Commands;
-  };
-  /**
-   * Map from source to command map.
-   */
-  typedef std::map<cmStdString, SourceAndCommands>  BuildRules;
-
-  bool Matches(const char* name);
   void SetGroupRegex(const char* regex)
     { m_GroupRegex.compile(regex); }
   void AddSource(const char* name, const cmSourceFile*);
-  void AddCustomCommand(const cmCustomCommand &cmd);
   const char* GetName() const
     { return m_Name.c_str(); }
-  const BuildRules& GetBuildRules() const
-    { return m_BuildRules; }
-  void Print() const;
+  bool Matches(const char *);
+
+  /**
+   * Get the list of the source files used by this target
+   */
+  const std::vector<const cmSourceFile*> &GetSourceFiles() const 
+    {return m_SourceFiles;}
+  std::vector<const cmSourceFile*> &GetSourceFiles() {return m_SourceFiles;}
+  
 private:
   /**
    * The name of the source group.
@@ -88,11 +60,9 @@ private:
   cmRegularExpression m_GroupRegex;
   
   /**
-   * Map from source name to the commands to build from the source.
-   * Some commands may build from files that the compiler also knows how to
-   * build.
+   * vector of all source files in this source group
    */
-  BuildRules m_BuildRules;  
+  std::vector<const cmSourceFile*> m_SourceFiles;
 };
 
 #endif

+ 19 - 0
Source/cmSystemTools.cxx

@@ -1932,6 +1932,25 @@ std::string cmSystemTools::GetFilenameExtension(const std::string& filename)
     }
 }
 
+/**
+ * Return file extension of a full filename (dot included).
+ * Warning: this is the shortest extension (for example: .tar.gz)
+ */
+std::string 
+cmSystemTools::GetFilenameLastExtension(const std::string& filename)
+{
+  std::string name = cmSystemTools::GetFilenameName(filename);
+  std::string::size_type dot_pos = name.rfind(".");
+  if(dot_pos != std::string::npos)
+    {
+    return name.substr(dot_pos);
+    }
+  else
+    {
+    return "";
+    }
+}
+
 
 /**
  * Return file name without extension of a full filename (i.e. without path).

+ 3 - 0
Source/cmSystemTools.h

@@ -284,6 +284,9 @@ public:
   ///! return file extension of a full filename (dot included).
   static std::string GetFilenameExtension(const std::string&);
   
+  ///! return file extension of a full filename (dot included).
+  static std::string GetFilenameLastExtension(const std::string&);
+
   ///! return file name without extension of a full filename.
   static std::string GetFilenameWithoutExtension(const std::string&);
   

+ 17 - 3
Source/cmTarget.h

@@ -34,6 +34,8 @@ public:
                     SHARED_LIBRARY, MODULE_LIBRARY, UTILITY, INSTALL_FILES, 
                     INSTALL_PROGRAMS };
 
+  enum CustomCommandType { PRE_BUILD, PRE_LINK, POST_BUILD };
+
   /**
    * Return the type of target.
    */
@@ -57,8 +59,18 @@ public:
   /**
    * Get the list of the custom commands for this target
    */
-  const std::vector<cmCustomCommand> &GetCustomCommands() const {return m_CustomCommands;}
-  std::vector<cmCustomCommand> &GetCustomCommands() {return m_CustomCommands;}
+  const std::vector<cmCustomCommand> &GetPreBuildCommands() const 
+    {return m_PreBuildCommands;}
+  std::vector<cmCustomCommand> &GetPreBuildCommands() 
+    {return m_PreBuildCommands;}
+  const std::vector<cmCustomCommand> &GetPreLinkCommands() const 
+    {return m_PreLinkCommands;}
+  std::vector<cmCustomCommand> &GetPreLinkCommands() 
+    {return m_PreLinkCommands;}
+  const std::vector<cmCustomCommand> &GetPostBuildCommands() const 
+    {return m_PostBuildCommands;}
+  std::vector<cmCustomCommand> &GetPostBuildCommands() 
+    {return m_PostBuildCommands;}
 
   /**
    * Get the list of the source lists used by this target
@@ -186,7 +198,9 @@ private:
 
 
 private:
-  std::vector<cmCustomCommand> m_CustomCommands;
+  std::vector<cmCustomCommand> m_PreBuildCommands;
+  std::vector<cmCustomCommand> m_PreLinkCommands;
+  std::vector<cmCustomCommand> m_PostBuildCommands;
   std::vector<std::string> m_SourceLists;
   TargetType m_TargetType;
   std::vector<cmSourceFile*> m_SourceFiles;