Browse Source

BUG: Target names in the COMMAND part of a custom command should not create a file-level dependency that forces the command to rerun when the executable target rebuilds, but the target-level dependency should still be created. Target names in a DEPENDS should do both a target-level and file-level dependency. Updated the BuildDepends test to check that this works.

Brad King 18 years ago
parent
commit
c288da754a

+ 17 - 1
Source/cmAddCustomCommandCommand.h

@@ -124,7 +124,23 @@ public:
       "it is an option is to preserve compatibility with older CMake code.\n"
       "If the output of the custom command is not actually "
       "created as a file on disk it should be marked as SYMBOLIC with "
-      "SET_SOURCE_FILES_PROPERTIES.";
+      "SET_SOURCE_FILES_PROPERTIES.\n"
+
+      "If COMMAND specifies an executable target (created by "
+      "ADD_EXECUTABLE) it will automatically be replaced by the location "
+      "of the executable created at build time.  Additionally a "
+      "target-level dependency will be added so that the executable target "
+      "will be built before any target using this custom command.  However "
+      "this does NOT add a file-level dependency that would cause the "
+      "custom command to re-run whenever the executable is recompiled.\n"
+
+      "If DEPENDS specifies any target (created by an ADD_* command) "
+      "a target-level dependency is created to make sure the target is "
+      "built before any target using this custom command.  Additionally, "
+      "if the target is an executable or library a file-level dependency "
+      "is created to cause the custom command to re-run whenever the target "
+      "is recompiled.\n"
+      ;
     }
   
   cmTypeMacro(cmAddCustomCommandCommand, cmCommand);

+ 32 - 38
Source/cmTarget.cxx

@@ -409,7 +409,29 @@ void cmTarget::SetMakefile(cmMakefile* mf)
 }
 
 
-void 
+void cmTarget::CheckForTargetsAsCommand(const cmCustomCommand& cc)
+{
+  for(cmCustomCommandLines::const_iterator cit = cc.GetCommandLines().begin();
+      cit != cc.GetCommandLines().end(); ++cit )
+    {
+    std::string const& command = *cit->begin();
+    // Look for a non-imported target with this name.
+    if(cmTarget* t = this->Makefile->GetLocalGenerator()->
+       GetGlobalGenerator()->FindTarget(0, command.c_str(), false))
+      {
+      if(t->GetType() == cmTarget::EXECUTABLE)
+        {
+        // The command refers to an executable target built in
+        // this project.  Add the target-level dependency to make
+        // sure the executable is up to date before this custom
+        // command possibly runs.
+        this->AddUtility(command.c_str());
+        }
+      }
+    }
+}
+
+void
 cmTarget
 ::CheckForTargetsAsCommand(const std::vector<cmCustomCommand>& commands)
 {
@@ -417,20 +439,7 @@ cmTarget
         cli != commands.end();
         ++cli )
     {
-    for(cmCustomCommandLines::const_iterator cit = 
-          cli->GetCommandLines().begin();
-        cit!=cli->GetCommandLines().end();
-        ++cit )
-      {
-      std::string command = *cit->begin();
-      // see if we can find a target with this name
-      cmTarget* t = this->Makefile->GetLocalGenerator()->
-                    GetGlobalGenerator()->FindTarget(0, command.c_str(), false);
-      if ( ( t ) && ( t->GetType() ==cmTarget::EXECUTABLE ) )
-        {
-        this->AddUtility ( command.c_str() );
-        }
-      }
+    this->CheckForTargetsAsCommand(*cli);
     }
 }
 
@@ -566,24 +575,8 @@ void cmTarget::TraceVSDependencies(std::string projFile,
           }
         }
 
-      // check if commands for this custom commands are names of targets and
-      // if that's the case add these targets as dependencies
-      std::vector<std::string> automaticTargetDepends;
-      for(cmCustomCommandLines::const_iterator it=
-          outsf->GetCustomCommand()->GetCommandLines().begin();
-          it!=outsf->GetCustomCommand()->GetCommandLines().end();
-          ++it)
-        {
-        const std::string& currentCommand = (*it)[0];
-        // see if we can find a target with this name
-        cmTarget* t =  this->Makefile->GetLocalGenerator()->
-            GetGlobalGenerator()->FindTarget(0, currentCommand.c_str(), false);
-        if (( t) && (t->GetType()==cmTarget::EXECUTABLE))
-          {
-          automaticTargetDepends.push_back(currentCommand);
-          }
-        }
-        outsf->GetCustomCommand()->AppendDepends(automaticTargetDepends);
+      // Add target-level dependencies for the commands.
+      this->CheckForTargetsAsCommand(*outsf->GetCustomCommand());
 
       // add its dependencies to the list to check
       for (unsigned int i = 0; 
@@ -598,10 +591,9 @@ void cmTarget::TraceVSDependencies(std::string projFile,
           dep = cmSystemTools::GetFilenameWithoutLastExtension(dep);
           }
         bool isUtility = false;
-        // see if we can find a target with this name
-        cmTarget* t =  this->Makefile->GetLocalGenerator()->
-          GetGlobalGenerator()->FindTarget(0, dep.c_str(), false);
-        if(t)
+        // Check for a non-imported target with this name.
+        if(cmTarget* t =  this->Makefile->GetLocalGenerator()->
+           GetGlobalGenerator()->FindTarget(0, dep.c_str(), false))
           {
           // if we find the target and the dep was given as a full
           // path, then make sure it was not a full path to something
@@ -629,7 +621,9 @@ void cmTarget::TraceVSDependencies(std::string projFile,
           }
         if(isUtility)
           {
-          // add the depend as a utility on the target
+          // The dependency refers to a target built in this project.
+          // Add the target-level dependency to make sure the target
+          // is up to date before this custom command possibly runs.
           this->AddUtility(dep.c_str());
           }
         else

+ 3 - 2
Source/cmTarget.h

@@ -296,9 +296,10 @@ private:
                          const LibraryID& dep);
 
   /*
-   * Checks the prebuild, prelink and postbuild custom commands for known
-   * targets and adds them to the dependencies.
+   * Check custom commands for known targets and add a target-level
+   * dependency.
    */
+  void CheckForTargetsAsCommand(const cmCustomCommand& cc);
   void CheckForTargetsAsCommand(const std::vector<cmCustomCommand>& commands);
   
   

+ 1 - 1
Tests/BuildDepends/CMakeLists.txt

@@ -60,5 +60,5 @@ message("${out}")
 if("${out}" STREQUAL "foo changed ")
   message("Worked!")
 else("${out}" STREQUAL "foo changed ")
-  message(SEND_ERROR "Program did not rebuild with changed file")
+  message(SEND_ERROR "Project did not rebuild properly!")
 endif("${out}" STREQUAL "foo changed ")

+ 27 - 1
Tests/BuildDepends/Project/CMakeLists.txt

@@ -1,4 +1,30 @@
 project(testRebuild)
 add_library(foo STATIC ${testRebuild_BINARY_DIR}/foo.cxx)
-add_executable(bar bar.cxx)
+
+# Add a generated header that regenerates when the generator is
+# rebuilt.
+add_custom_command(
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/regen.h
+  COMMAND generator ${CMAKE_CURRENT_BINARY_DIR}/regen.h regen
+  DEPENDS generator # adds file-level dependency to re-run rule
+  )
+
+# Add a generated header that does NOT regenerate when the generator
+# is rebuilt.
+add_custom_command(
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/noregen.h
+  COMMAND generator ${CMAKE_CURRENT_BINARY_DIR}/noregen.h noregen
+  )
+
+# Test that the generator rebuilds when the static library source file
+# changes.  This should cause regen.h to be recreated also.
+add_executable(generator generator.cxx)
+target_link_libraries(generator foo)
+
+# Build an executable to drive the build and rebuild.
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+add_executable(bar bar.cxx
+  ${CMAKE_CURRENT_BINARY_DIR}/regen.h
+  ${CMAKE_CURRENT_BINARY_DIR}/noregen.h
+  )
 target_link_libraries(bar foo)

+ 13 - 4
Tests/BuildDepends/Project/bar.cxx

@@ -1,10 +1,19 @@
-#include "stdio.h"
+#include <stdio.h>
+#include <string.h>
+#include <regen.h>
+#include <noregen.h>
 
-const char* foo();
 int main()
 {
-  int i;
-  printf("%s\n", foo());
+  /* Make sure the noregen header was not regenerated.  */
+  if(strcmp("foo", noregen_string) != 0)
+    {
+    printf("FAILED: noregen.h was regenerated!\n");
+    return 1;
+    }
+
+  /* Print out the string that should have been regenerated.  */
+  printf("%s\n", regen_string);
   fflush(stdout);
   for(;;);
   return 0;