cmMakefile: support "after generator target" generator actions

These actions may require additional information gathered during
generation. Run them at the appropriate time.
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 309a140..aeebc6f 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -1627,6 +1627,13 @@
     return false;
   }
 
+  // Perform after-generator-target generator actions. These involve collecting
+  // information gathered during the construction of generator targets.
+  for (unsigned int i = 0; i < this->Makefiles.size(); ++i) {
+    this->Makefiles[i]->GenerateAfterGeneratorTargets(
+      *this->LocalGenerators[i]);
+  }
+
   // Add generator specific helper commands
   for (const auto& localGen : this->LocalGenerators) {
     localGen->AddHelperCommands();
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 2803279..8ffd855 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -1055,8 +1055,13 @@
 }
 
 void cmMakefile::GeneratorAction::operator()(cmLocalGenerator& lg,
-                                             const cmListFileBacktrace& lfbt)
+                                             const cmListFileBacktrace& lfbt,
+                                             GeneratorActionWhen when)
 {
+  if (this->When != when) {
+    return;
+  }
+
   if (cc) {
     CCAction(lg, lfbt, std::move(cc));
   } else {
@@ -1073,7 +1078,7 @@
   // give all the commands a chance to do something
   // after the file has been parsed before generation
   for (auto& action : this->GeneratorActions) {
-    action.Value(lg, action.Backtrace);
+    action.Value(lg, action.Backtrace, GeneratorActionWhen::AfterConfigure);
   }
   this->GeneratorActionsInvoked = true;
 
@@ -1107,6 +1112,14 @@
   }
 }
 
+void cmMakefile::GenerateAfterGeneratorTargets(cmLocalGenerator& lg)
+{
+  for (auto& action : this->GeneratorActions) {
+    action.Value(lg, action.Backtrace,
+                 GeneratorActionWhen::AfterGeneratorTargets);
+  }
+}
+
 namespace {
 // There are still too many implicit backtraces through cmMakefile.  As a
 // workaround we reset the backtrace temporarily.
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index da49af1..6e1739e 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -143,6 +143,14 @@
   bool EnforceUniqueName(std::string const& name, std::string& msg,
                          bool isCustom = false) const;
 
+  enum class GeneratorActionWhen
+  {
+    // Run after all CMake code has been parsed.
+    AfterConfigure,
+    // Run after generator targets have been constructed.
+    AfterGeneratorTargets,
+  };
+
   class GeneratorAction
   {
     using ActionT =
@@ -152,20 +160,29 @@
                          std::unique_ptr<cmCustomCommand> cc)>;
 
   public:
-    GeneratorAction(ActionT&& action)
-      : Action(std::move(action))
+    GeneratorAction(
+      ActionT&& action,
+      GeneratorActionWhen when = GeneratorActionWhen::AfterConfigure)
+      : When(when)
+      , Action(std::move(action))
     {
     }
 
-    GeneratorAction(std::unique_ptr<cmCustomCommand> tcc, CCActionT&& action)
-      : CCAction(std::move(action))
+    GeneratorAction(
+      std::unique_ptr<cmCustomCommand> tcc, CCActionT&& action,
+      GeneratorActionWhen when = GeneratorActionWhen::AfterConfigure)
+      : When(when)
+      , CCAction(std::move(action))
       , cc(std::move(tcc))
     {
     }
 
-    void operator()(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt);
+    void operator()(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
+                    GeneratorActionWhen when);
 
   private:
+    GeneratorActionWhen When;
+
     ActionT Action;
 
     // FIXME: Use std::variant
@@ -190,6 +207,7 @@
    * the makefile.
    */
   void Generate(cmLocalGenerator& lg);
+  void GenerateAfterGeneratorTargets(cmLocalGenerator& lg);
 
   /**
    * Get the target for PRE_BUILD, PRE_LINK, or POST_BUILD commands.