Selaa lähdekoodia

Merge topic 'decompose-custom-command-creation'

5a06efda05 cmMakefile: Remove AddUtilityCommand overload without byproducts
ea1bed34b2 cmMakefile: Extract utilities used for creation of custom commands
91abf9f3c4 cmCustomCommand: Move custom commands
f151a57705 cmMakefile: Move enumerations into new header

Acked-by: Kitware Robot <[email protected]>
Merge-request: !3846
Brad King 6 vuotta sitten
vanhempi
sitoutus
f30f1625c2

+ 1 - 0
Source/CMakeLists.txt

@@ -195,6 +195,7 @@ set(SRCS
   cmCustomCommandGenerator.h
   cmCustomCommandLines.cxx
   cmCustomCommandLines.h
+  cmCustomCommandTypes.h
   cmDefinitions.cxx
   cmDefinitions.h
   cmDepends.cxx

+ 5 - 5
Source/cmAddCustomCommandCommand.cxx

@@ -8,6 +8,7 @@
 #include "cmCheckCustomOutputs.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -15,7 +16,6 @@
 #include "cmPolicies.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmTarget.h"
 
 bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
                                cmExecutionStatus& status)
@@ -55,7 +55,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
   // Save all command lines.
   cmCustomCommandLines commandLines;
 
-  cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD;
+  cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD;
 
   enum tdoing
   {
@@ -139,11 +139,11 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
           currentLine.clear();
         }
       } else if (copy == keyPRE_BUILD) {
-        cctype = cmTarget::PRE_BUILD;
+        cctype = cmCustomCommandType::PRE_BUILD;
       } else if (copy == keyPRE_LINK) {
-        cctype = cmTarget::PRE_LINK;
+        cctype = cmCustomCommandType::PRE_LINK;
       } else if (copy == keyPOST_BUILD) {
-        cctype = cmTarget::POST_BUILD;
+        cctype = cmCustomCommandType::POST_BUILD;
       } else if (copy == keyVERBATIM) {
         verbatim = true;
       } else if (copy == keyAPPEND) {

+ 2 - 1
Source/cmAddCustomTargetCommand.cxx

@@ -6,6 +6,7 @@
 
 #include "cmCheckCustomOutputs.h"
 #include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
 #include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
@@ -214,7 +215,7 @@ bool cmAddCustomTargetCommand(std::vector<std::string> const& args,
   // Add the utility target to the makefile.
   bool escapeOldStyle = !verbatim;
   cmTarget* target = mf.AddUtilityCommand(
-    targetName, cmMakefile::TargetOrigin::Project, excludeFromAll,
+    targetName, cmCommandOrigin::Project, excludeFromAll,
     working_directory.c_str(), byproducts, depends, commandLines,
     escapeOldStyle, comment, uses_terminal, command_expand_lists, job_pool);
 

+ 8 - 6
Source/cmCPluginAPI.cxx

@@ -220,8 +220,10 @@ void CCONV cmAddUtilityCommand(void* arg, const char* utilityName,
   }
 
   // Pass the call to the makefile instance.
-  mf->AddUtilityCommand(utilityName, cmMakefile::TargetOrigin::Project,
-                        (all ? false : true), nullptr, depends2, commandLines);
+  std::vector<std::string> no_byproducts;
+  mf->AddUtilityCommand(utilityName, cmCommandOrigin::Project,
+                        (all ? false : true), nullptr, no_byproducts, depends2,
+                        commandLines);
 }
 void CCONV cmAddCustomCommand(void* arg, const char* source,
                               const char* command, int numArgs,
@@ -319,16 +321,16 @@ void CCONV cmAddCustomCommandToTarget(void* arg, const char* target,
   commandLines.push_back(commandLine);
 
   // Select the command type.
-  cmTarget::CustomCommandType cctype = cmTarget::POST_BUILD;
+  cmCustomCommandType cctype = cmCustomCommandType::POST_BUILD;
   switch (commandType) {
     case CM_PRE_BUILD:
-      cctype = cmTarget::PRE_BUILD;
+      cctype = cmCustomCommandType::PRE_BUILD;
       break;
     case CM_PRE_LINK:
-      cctype = cmTarget::PRE_LINK;
+      cctype = cmCustomCommandType::PRE_LINK;
       break;
     case CM_POST_BUILD:
-      cctype = cmTarget::POST_BUILD;
+      cctype = cmCustomCommandType::POST_BUILD;
       break;
   }
 

+ 39 - 0
Source/cmCustomCommandTypes.h

@@ -0,0 +1,39 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmCustomCommandTypes_h
+#define cmCustomCommandTypes_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+
+/** Target custom command type */
+enum class cmCustomCommandType
+{
+  PRE_BUILD,
+  PRE_LINK,
+  POST_BUILD
+};
+
+/** Where the command originated from. */
+enum class cmCommandOrigin
+{
+  Project,
+  Generator
+};
+
+/** How to handle custom commands for object libraries */
+enum class cmObjectLibraryCommands
+{
+  Reject,
+  Accept
+};
+
+/** Utility target output source file name.  */
+struct cmUtilityOutput
+{
+  std::string Name;
+  std::string NameCMP0049;
+};
+
+#endif

+ 1 - 1
Source/cmGlobalGenerator.cxx

@@ -2582,7 +2582,7 @@ cmTarget cmGlobalGenerator::CreateGlobalTarget(GlobalTargetInfo const& gti,
   cmCustomCommand cc(nullptr, no_outputs, no_byproducts, no_depends,
                      gti.CommandLines, nullptr, gti.WorkingDir.c_str());
   cc.SetUsesTerminal(gti.UsesTerminal);
-  target.AddPostBuildCommand(cc);
+  target.AddPostBuildCommand(std::move(cc));
   if (!gti.Message.empty()) {
     target.SetProperty("EchoString", gti.Message.c_str());
   }

+ 10 - 10
Source/cmGlobalVisualStudio8Generator.cxx

@@ -96,17 +96,18 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget()
     return false;
   }
 
-  const char* no_working_directory = nullptr;
-  std::vector<std::string> no_depends;
   std::vector<cmLocalGenerator*> const& generators = this->LocalGenerators;
   cmLocalVisualStudio7Generator* lg =
     static_cast<cmLocalVisualStudio7Generator*>(generators[0]);
   cmMakefile* mf = lg->GetMakefile();
 
-  cmCustomCommandLines noCommandLines;
+  const char* no_working_directory = nullptr;
+  std::vector<std::string> no_byproducts;
+  std::vector<std::string> no_depends;
+  cmCustomCommandLines no_commands;
   cmTarget* tgt = mf->AddUtilityCommand(
-    CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator,
-    false, no_working_directory, no_depends, noCommandLines);
+    CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmCommandOrigin::Generator, false,
+    no_working_directory, no_byproducts, no_depends, no_commands);
 
   cmGeneratorTarget* gt = new cmGeneratorTarget(tgt, lg);
   lg->AddGeneratorTarget(gt);
@@ -152,10 +153,10 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget()
       std::vector<std::string> byproducts;
       byproducts.push_back(cm->GetGlobVerifyStamp());
 
-      mf->AddCustomCommandToTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET, byproducts,
-                                   no_depends, verifyCommandLines,
-                                   cmTarget::PRE_BUILD, "Checking File Globs",
-                                   no_working_directory, false);
+      mf->AddCustomCommandToTarget(
+        CMAKE_CHECK_BUILD_SYSTEM_TARGET, byproducts, no_depends,
+        verifyCommandLines, cmCustomCommandType::PRE_BUILD,
+        "Checking File Globs", no_working_directory, false);
 
       // Ensure ZERO_CHECK always runs in Visual Studio using MSBuild,
       // otherwise the prebuild command will not be run.
@@ -182,7 +183,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget()
     // file as the main dependency because it would get
     // overwritten by the CreateVCProjBuildRule.
     // (this could be avoided with per-target source files)
-    std::vector<std::string> no_byproducts;
     std::string no_main_dependency;
     cmImplicitDependsList no_implicit_depends;
     if (cmSourceFile* file = mf->AddCustomCommandToOutput(

+ 4 - 3
Source/cmGlobalVisualStudioGenerator.cxx

@@ -183,7 +183,8 @@ void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
 {
   // Add a special target that depends on ALL projects for easy build
   // of one configuration only.
-  const char* no_working_dir = 0;
+  const char* no_working_dir = nullptr;
+  std::vector<std::string> no_byproducts;
   std::vector<std::string> no_depends;
   cmCustomCommandLines no_commands;
   for (auto const& it : this->ProjectMap) {
@@ -193,8 +194,8 @@ void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
       // Use no actual command lines so that the target itself is not
       // considered always out of date.
       cmTarget* allBuild = gen[0]->GetMakefile()->AddUtilityCommand(
-        "ALL_BUILD", cmMakefile::TargetOrigin::Generator, true, no_working_dir,
-        no_depends, no_commands, false, "Build all projects");
+        "ALL_BUILD", cmCommandOrigin::Generator, true, no_working_dir,
+        no_byproducts, no_depends, no_commands, false, "Build all projects");
 
       cmGeneratorTarget* gt = new cmGeneratorTarget(allBuild, gen[0]);
       gen[0]->AddGeneratorTarget(gt);

+ 12 - 11
Source/cmGlobalXCodeGenerator.cxx

@@ -497,12 +497,14 @@ void cmGlobalXCodeGenerator::AddExtraTargets(
 {
   cmMakefile* mf = root->GetMakefile();
 
-  // Add ALL_BUILD
   const char* no_working_directory = nullptr;
+  std::vector<std::string> no_byproducts;
   std::vector<std::string> no_depends;
+
+  // Add ALL_BUILD
   cmTarget* allbuild = mf->AddUtilityCommand(
-    "ALL_BUILD", cmMakefile::TargetOrigin::Generator, true,
-    no_working_directory, no_depends,
+    "ALL_BUILD", cmCommandOrigin::Generator, true, no_working_directory,
+    no_byproducts, no_depends,
     cmMakeSingleCommandLine({ "echo", "Build all projects" }));
 
   cmGeneratorTarget* allBuildGt = new cmGeneratorTarget(allbuild, root);
@@ -526,8 +528,8 @@ void cmGlobalXCodeGenerator::AddExtraTargets(
       this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile);
     cmSystemTools::ReplaceString(file, "\\ ", " ");
     cmTarget* check = mf->AddUtilityCommand(
-      CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmMakefile::TargetOrigin::Generator,
-      true, no_working_directory, no_depends,
+      CMAKE_CHECK_BUILD_SYSTEM_TARGET, cmCommandOrigin::Generator, true,
+      no_working_directory, no_byproducts, no_depends,
       cmMakeSingleCommandLine({ "make", "-f", file }));
 
     cmGeneratorTarget* checkGt = new cmGeneratorTarget(check, root);
@@ -542,9 +544,8 @@ void cmGlobalXCodeGenerator::AddExtraTargets(
         continue;
       }
 
-      std::string targetName = target->GetName();
-
-      if (regenerate && (targetName != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) {
+      if (regenerate &&
+          (target->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET)) {
         target->Target->AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET);
       }
 
@@ -555,11 +556,11 @@ void cmGlobalXCodeGenerator::AddExtraTargets(
       if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
         commandLines.front().back() = // fill placeholder
           this->PostBuildMakeTarget(target->GetName(), "$(CONFIGURATION)");
-        std::vector<std::string> no_byproducts;
         gen->GetMakefile()->AddCustomCommandToTarget(
           target->GetName(), no_byproducts, no_depends, commandLines,
-          cmTarget::POST_BUILD, "Depend check for xcode", dir.c_str(), true,
-          false, "", "", false, cmMakefile::AcceptObjectLibraryCommands);
+          cmCustomCommandType::POST_BUILD, "Depend check for xcode",
+          dir.c_str(), true, false, "", "", false,
+          cmObjectLibraryCommands::Accept);
       }
 
       if (!this->IsExcluded(target)) {

+ 2 - 1
Source/cmLocalGenerator.cxx

@@ -7,6 +7,7 @@
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpressionEvaluationFile.h"
@@ -2356,7 +2357,7 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
           if (this->GetGlobalGenerator()->IsMultiConfig()) {
             this->Makefile->AddCustomCommandToTarget(
               target->GetName(), outputs, no_deps, commandLines,
-              cmTarget::PRE_BUILD, no_message, no_current_dir);
+              cmCustomCommandType::PRE_BUILD, no_message, no_current_dir);
           } else {
             cmImplicitDependsList no_implicit_depends;
             cmSourceFile* copy_rule = this->Makefile->AddCustomCommandToOutput(

+ 156 - 142
Source/cmMakefile.cxx

@@ -39,6 +39,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
+#include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
 #include "cmTest.h"
 #include "cmTestGenerator.h" // IWYU pragma: keep
@@ -837,13 +838,8 @@ bool cmMakefile::ValidateCustomCommand(
   return true;
 }
 
-cmTarget* cmMakefile::AddCustomCommandToTarget(
-  const std::string& target, const std::vector<std::string>& byproducts,
-  const std::vector<std::string>& depends,
-  const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
-  const char* comment, const char* workingDir, bool escapeOldStyle,
-  bool uses_terminal, const std::string& depfile, const std::string& job_pool,
-  bool command_expand_lists, ObjectLibraryCommands objLibraryCommands)
+cmTarget* cmMakefile::GetCustomCommandTarget(
+  const std::string& target, cmObjectLibraryCommands objLibCommands) const
 {
   // Find the target to which to add the custom command.
   auto ti = this->Targets.find(target);
@@ -884,7 +880,7 @@ cmTarget* cmMakefile::AddCustomCommandToTarget(
   }
 
   cmTarget* t = &ti->second;
-  if (objLibraryCommands == RejectObjectLibraryCommands &&
+  if (objLibCommands == cmObjectLibraryCommands::Reject &&
       t->GetType() == cmStateEnums::OBJECT_LIBRARY) {
     std::ostringstream e;
     e << "Target \"" << target
@@ -902,8 +898,21 @@ cmTarget* cmMakefile::AddCustomCommandToTarget(
     return nullptr;
   }
 
+  return t;
+}
+
+cmTarget* cmMakefile::AddCustomCommandToTarget(
+  const std::string& target, const std::vector<std::string>& byproducts,
+  const std::vector<std::string>& depends,
+  const cmCustomCommandLines& commandLines, cmCustomCommandType type,
+  const char* comment, const char* workingDir, bool escapeOldStyle,
+  bool uses_terminal, const std::string& depfile, const std::string& job_pool,
+  bool command_expand_lists, cmObjectLibraryCommands objLibCommands)
+{
+  cmTarget* t = this->GetCustomCommandTarget(target, objLibCommands);
+
   // Validate custom commands.
-  if (!this->ValidateCustomCommand(commandLines)) {
+  if (!t || !this->ValidateCustomCommand(commandLines)) {
     return t;
   }
 
@@ -920,7 +929,7 @@ cmTarget* cmMakefile::AddCustomCommandToTarget(
 void cmMakefile::CommitCustomCommandToTarget(
   cmTarget* target, const std::vector<std::string>& byproducts,
   const std::vector<std::string>& depends,
-  const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
+  const cmCustomCommandLines& commandLines, cmCustomCommandType type,
   const char* comment, const char* workingDir, bool escapeOldStyle,
   bool uses_terminal, const std::string& depfile, const std::string& job_pool,
   bool command_expand_lists)
@@ -936,47 +945,18 @@ void cmMakefile::CommitCustomCommandToTarget(
   cc.SetDepfile(depfile);
   cc.SetJobPool(job_pool);
   switch (type) {
-    case cmTarget::PRE_BUILD:
-      target->AddPreBuildCommand(cc);
+    case cmCustomCommandType::PRE_BUILD:
+      target->AddPreBuildCommand(std::move(cc));
       break;
-    case cmTarget::PRE_LINK:
-      target->AddPreLinkCommand(cc);
+    case cmCustomCommandType::PRE_LINK:
+      target->AddPreLinkCommand(std::move(cc));
       break;
-    case cmTarget::POST_BUILD:
-      target->AddPostBuildCommand(cc);
+    case cmCustomCommandType::POST_BUILD:
+      target->AddPostBuildCommand(std::move(cc));
       break;
   }
-  this->UpdateOutputToSourceMap(byproducts, target);
-}
-
-void cmMakefile::UpdateOutputToSourceMap(
-  std::vector<std::string> const& byproducts, cmTarget* target)
-{
-  for (std::string const& o : byproducts) {
-    this->UpdateOutputToSourceMap(o, target);
-  }
-}
-
-void cmMakefile::UpdateOutputToSourceMap(std::string const& byproduct,
-                                         cmTarget* target)
-{
-  SourceEntry entry;
-  entry.Sources.Target = target;
 
-  auto pr = this->OutputToSource.emplace(byproduct, entry);
-  if (!pr.second) {
-    SourceEntry& current = pr.first->second;
-    // Has the target already been set?
-    if (!current.Sources.Target) {
-      current.Sources.Target = target;
-    } else {
-      // Multiple custom commands/targets produce the same output (source file
-      // or target).  See also comment in other UpdateOutputToSourceMap
-      // overload.
-      //
-      // TODO: Warn the user about this case.
-    }
-  }
+  this->AddTargetByproducts(target, byproducts);
 }
 
 cmSourceFile* cmMakefile::AddCustomCommandToOutput(
@@ -1102,47 +1082,10 @@ cmSourceFile* cmMakefile::CommitCustomCommandToOutput(
     cc->SetDepfile(depfile);
     cc->SetJobPool(job_pool);
     file->SetCustomCommand(std::move(cc));
-    this->UpdateOutputToSourceMap(outputs, file, false);
-    this->UpdateOutputToSourceMap(byproducts, file, true);
-  }
-  return file;
-}
-
-void cmMakefile::UpdateOutputToSourceMap(
-  std::vector<std::string> const& outputs, cmSourceFile* source,
-  bool byproduct)
-{
-  for (std::string const& o : outputs) {
-    this->UpdateOutputToSourceMap(o, source, byproduct);
-  }
-}
-
-void cmMakefile::UpdateOutputToSourceMap(std::string const& output,
-                                         cmSourceFile* source, bool byproduct)
-{
-  SourceEntry entry;
-  entry.Sources.Source = source;
-  entry.Sources.SourceIsByproduct = byproduct;
 
-  auto pr = this->OutputToSource.emplace(output, entry);
-  if (!pr.second) {
-    SourceEntry& current = pr.first->second;
-    // Outputs take precedence over byproducts
-    if (!current.Sources.Source ||
-        (current.Sources.SourceIsByproduct && !byproduct)) {
-      current.Sources.Source = source;
-      current.Sources.SourceIsByproduct = false;
-    } else {
-      // Multiple custom commands produce the same output but may
-      // be attached to a different source file (MAIN_DEPENDENCY).
-      // LinearGetSourceFileWithOutput would return the first one,
-      // so keep the mapping for the first one.
-      //
-      // TODO: Warn the user about this case.  However, the VS 8 generator
-      // triggers it for separate generate.stamp rules in ZERO_CHECK and
-      // individual targets.
-    }
+    this->AddSourceOutputs(file, outputs, byproducts);
   }
+  return file;
 }
 
 void cmMakefile::AddCustomCommandOldStyle(
@@ -1157,9 +1100,9 @@ void cmMakefile::AddCustomCommandOldStyle(
     // same then it added a post-build rule to the target.  Preserve
     // this behavior.
     std::vector<std::string> no_byproducts;
-    this->AddCustomCommandToTarget(target, no_byproducts, depends,
-                                   commandLines, cmTarget::POST_BUILD, comment,
-                                   nullptr);
+    this->AddCustomCommandToTarget(
+      target, no_byproducts, depends, commandLines,
+      cmCustomCommandType::POST_BUILD, comment, nullptr);
     return;
   }
 
@@ -1247,85 +1190,73 @@ void cmMakefile::CommitAppendCustomCommandToOutput(
   }
 }
 
-cmTarget* cmMakefile::AddUtilityCommand(
-  const std::string& utilityName, TargetOrigin origin, bool excludeFromAll,
-  const char* workingDirectory, const std::vector<std::string>& depends,
-  const cmCustomCommandLines& commandLines, bool escapeOldStyle,
-  const char* comment, bool uses_terminal, bool command_expand_lists,
-  const std::string& job_pool)
+cmUtilityOutput cmMakefile::GetUtilityOutput(cmTarget* target)
 {
-  std::vector<std::string> no_byproducts;
-  return this->AddUtilityCommand(
-    utilityName, origin, excludeFromAll, workingDirectory, no_byproducts,
-    depends, commandLines, escapeOldStyle, comment, uses_terminal,
-    command_expand_lists, job_pool);
+  std::string force = cmStrCat(this->GetCurrentBinaryDirectory(),
+                               "/CMakeFiles/", target->GetName());
+  std::string forceCMP0049 = target->GetSourceCMP0049(force);
+  {
+    cmSourceFile* sf = nullptr;
+    if (!forceCMP0049.empty()) {
+      sf = this->GetOrCreateSource(forceCMP0049, false,
+                                   cmSourceFileLocationKind::Known);
+    }
+    // The output is not actually created so mark it symbolic.
+    if (sf) {
+      sf->SetProperty("SYMBOLIC", "1");
+    } else {
+      cmSystemTools::Error("Could not get source file entry for " + force);
+    }
+  }
+  return { std::move(force), std::move(forceCMP0049) };
 }
 
 cmTarget* cmMakefile::AddUtilityCommand(
-  const std::string& utilityName, TargetOrigin origin, bool excludeFromAll,
+  const std::string& utilityName, cmCommandOrigin origin, bool excludeFromAll,
   const char* workingDirectory, const std::vector<std::string>& byproducts,
   const std::vector<std::string>& depends,
   const cmCustomCommandLines& commandLines, bool escapeOldStyle,
   const char* comment, bool uses_terminal, bool command_expand_lists,
   const std::string& job_pool)
 {
-  // Create a target instance for this utility.
-  cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName);
-  target->SetIsGeneratorProvided(origin == TargetOrigin::Generator);
-  if (excludeFromAll || this->GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
-    target->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
-  }
+  cmTarget* target =
+    this->AddNewUtilityTarget(utilityName, origin, excludeFromAll);
 
   // Validate custom commands.
-  if (!this->ValidateCustomCommand(commandLines) ||
-      (commandLines.empty() && depends.empty())) {
+  if ((commandLines.empty() && depends.empty()) ||
+      !this->ValidateCustomCommand(commandLines)) {
     return target;
   }
 
+  // Get the output name of the utility target and mark it generated.
+  cmUtilityOutput force = this->GetUtilityOutput(target);
+  this->GetOrCreateGeneratedSource(force.Name);
+
   // Always create the byproduct sources and mark them generated.
   this->CreateGeneratedSources(byproducts);
 
-  std::string force =
-    cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/", utilityName);
-  this->CreateGeneratedSource(force);
-  std::string forceCMP0049 = target->GetSourceCMP0049(force);
-  {
-    cmSourceFile* sf = nullptr;
-    if (!forceCMP0049.empty()) {
-      sf = this->GetOrCreateSource(forceCMP0049, false,
-                                   cmSourceFileLocationKind::Known);
-    }
-    // The output is not actually created so mark it symbolic.
-    if (sf) {
-      sf->SetProperty("SYMBOLIC", "1");
-    } else {
-      cmSystemTools::Error("Could not get source file entry for " + force);
-    }
-  }
-
   if (!comment) {
     // Use an empty comment to avoid generation of default comment.
     comment = "";
   }
 
-  this->CommitUtilityCommand(target, force, forceCMP0049, workingDirectory,
-                             byproducts, depends, commandLines, escapeOldStyle,
-                             comment, uses_terminal, command_expand_lists,
-                             job_pool);
+  this->CommitUtilityCommand(target, force, workingDirectory, byproducts,
+                             depends, commandLines, escapeOldStyle, comment,
+                             uses_terminal, command_expand_lists, job_pool);
 
   return target;
 }
 
 void cmMakefile::CommitUtilityCommand(
-  cmTarget* target, const std::string& force, const std::string& forceCMP0049,
-  const char* workingDirectory, const std::vector<std::string>& byproducts,
+  cmTarget* target, const cmUtilityOutput& force, const char* workingDirectory,
+  const std::vector<std::string>& byproducts,
   const std::vector<std::string>& depends,
   const cmCustomCommandLines& commandLines, bool escapeOldStyle,
   const char* comment, bool uses_terminal, bool command_expand_lists,
   const std::string& job_pool)
 {
   std::vector<std::string> forced;
-  forced.push_back(force);
+  forced.push_back(force.Name);
   std::string no_main_dependency;
   cmImplicitDependsList no_implicit_depends;
   bool no_replace = false;
@@ -1333,11 +1264,11 @@ void cmMakefile::CommitUtilityCommand(
     forced, byproducts, depends, no_main_dependency, no_implicit_depends,
     commandLines, comment, workingDirectory, no_replace, escapeOldStyle,
     uses_terminal, command_expand_lists, /*depfile=*/"", job_pool);
-  if (!forceCMP0049.empty()) {
-    target->AddSource(forceCMP0049);
+  if (!force.NameCMP0049.empty()) {
+    target->AddSource(force.NameCMP0049);
   }
   if (sf) {
-    this->UpdateOutputToSourceMap(byproducts, target);
+    this->AddTargetByproducts(target, byproducts);
   }
 }
 
@@ -2156,6 +2087,18 @@ cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type,
   return &it->second;
 }
 
+cmTarget* cmMakefile::AddNewUtilityTarget(const std::string& utilityName,
+                                          cmCommandOrigin origin,
+                                          bool excludeFromAll)
+{
+  cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName);
+  target->SetIsGeneratorProvided(origin == cmCommandOrigin::Generator);
+  if (excludeFromAll || this->GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
+    target->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
+  }
+  return target;
+}
+
 namespace {
 bool AnyOutputMatches(const std::string& name,
                       const std::vector<std::string>& outputs)
@@ -2286,6 +2229,76 @@ bool cmMakefile::MightHaveCustomCommand(const std::string& name) const
   return false;
 }
 
+void cmMakefile::AddTargetByproducts(
+  cmTarget* target, const std::vector<std::string>& byproducts)
+{
+  for (std::string const& o : byproducts) {
+    this->UpdateOutputToSourceMap(o, target);
+  }
+}
+
+void cmMakefile::AddSourceOutputs(cmSourceFile* source,
+                                  const std::vector<std::string>& outputs,
+                                  const std::vector<std::string>& byproducts)
+{
+  for (std::string const& o : outputs) {
+    this->UpdateOutputToSourceMap(o, source, false);
+  }
+  for (std::string const& o : byproducts) {
+    this->UpdateOutputToSourceMap(o, source, true);
+  }
+}
+
+void cmMakefile::UpdateOutputToSourceMap(std::string const& byproduct,
+                                         cmTarget* target)
+{
+  SourceEntry entry;
+  entry.Sources.Target = target;
+
+  auto pr = this->OutputToSource.emplace(byproduct, entry);
+  if (!pr.second) {
+    SourceEntry& current = pr.first->second;
+    // Has the target already been set?
+    if (!current.Sources.Target) {
+      current.Sources.Target = target;
+    } else {
+      // Multiple custom commands/targets produce the same output (source file
+      // or target).  See also comment in other UpdateOutputToSourceMap
+      // overload.
+      //
+      // TODO: Warn the user about this case.
+    }
+  }
+}
+
+void cmMakefile::UpdateOutputToSourceMap(std::string const& output,
+                                         cmSourceFile* source, bool byproduct)
+{
+  SourceEntry entry;
+  entry.Sources.Source = source;
+  entry.Sources.SourceIsByproduct = byproduct;
+
+  auto pr = this->OutputToSource.emplace(output, entry);
+  if (!pr.second) {
+    SourceEntry& current = pr.first->second;
+    // Outputs take precedence over byproducts
+    if (!current.Sources.Source ||
+        (current.Sources.SourceIsByproduct && !byproduct)) {
+      current.Sources.Source = source;
+      current.Sources.SourceIsByproduct = false;
+    } else {
+      // Multiple custom commands produce the same output but may
+      // be attached to a different source file (MAIN_DEPENDENCY).
+      // LinearGetSourceFileWithOutput would return the first one,
+      // so keep the mapping for the first one.
+      //
+      // TODO: Warn the user about this case.  However, the VS 8 generator
+      // triggers it for separate generate.stamp rules in ZERO_CHECK and
+      // individual targets.
+    }
+  }
+}
+
 #if !defined(CMAKE_BOOTSTRAP)
 cmSourceGroup* cmMakefile::GetSourceGroup(
   const std::vector<std::string>& name) const
@@ -3530,19 +3543,20 @@ cmSourceFile* cmMakefile::GetOrCreateSource(const std::string& sourceName,
   return this->CreateSource(sourceName, generated, kind);
 }
 
-void cmMakefile::CreateGeneratedSource(const std::string& output)
+cmSourceFile* cmMakefile::GetOrCreateGeneratedSource(
+  const std::string& sourceName)
 {
-  if (cmSourceFile* out = this->GetOrCreateSource(
-        output, true, cmSourceFileLocationKind::Known)) {
-    out->SetProperty("GENERATED", "1");
-  }
+  cmSourceFile* sf =
+    this->GetOrCreateSource(sourceName, true, cmSourceFileLocationKind::Known);
+  sf->SetProperty("GENERATED", "1");
+  return sf;
 }
 
 void cmMakefile::CreateGeneratedSources(
   const std::vector<std::string>& outputs)
 {
   for (std::string const& output : outputs) {
-    this->CreateGeneratedSource(output);
+    this->GetOrCreateGeneratedSource(output);
   }
 }
 

+ 48 - 37
Source/cmMakefile.h

@@ -20,6 +20,7 @@
 #include <cm/string_view>
 
 #include "cmAlgorithms.h"
+#include "cmCustomCommandTypes.h"
 #include "cmListFileCache.h"
 #include "cmMessageType.h"
 #include "cmNewLineStyle.h"
@@ -28,7 +29,10 @@
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
-#include "cmTarget.h"
+
+// IWYU does not see that 'std::unordered_map<std::string, cmTarget>'
+// will not compile without the complete type.
+#include "cmTarget.h" // IWYU pragma: keep
 
 #if !defined(CMAKE_BOOTSTRAP)
 #  include "cmSourceGroup.h"
@@ -164,22 +168,21 @@ public:
    */
   void FinalPass();
 
-  /** How to handle custom commands for object libraries */
-  enum ObjectLibraryCommands
-  {
-    RejectObjectLibraryCommands,
-    AcceptObjectLibraryCommands
-  };
+  /**
+   * Get the target for PRE_BUILD, PRE_LINK, or POST_BUILD commands.
+   */
+  cmTarget* GetCustomCommandTarget(
+    const std::string& target, cmObjectLibraryCommands objLibCommands) const;
 
   /** Add a custom command to the build.  */
   cmTarget* AddCustomCommandToTarget(
     const std::string& target, const std::vector<std::string>& byproducts,
     const std::vector<std::string>& depends,
-    const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
+    const cmCustomCommandLines& commandLines, cmCustomCommandType type,
     const char* comment, const char* workingDir, bool escapeOldStyle = true,
     bool uses_terminal = false, const std::string& depfile = "",
     const std::string& job_pool = "", bool command_expand_lists = false,
-    ObjectLibraryCommands objLibraryCommands = RejectObjectLibraryCommands);
+    cmObjectLibraryCommands objLibCommands = cmObjectLibraryCommands::Reject);
   cmSourceFile* AddCustomCommandToOutput(
     const std::string& output, const std::vector<std::string>& depends,
     const std::string& main_dependency,
@@ -208,6 +211,19 @@ public:
     const cmImplicitDependsList& implicit_depends,
     const cmCustomCommandLines& commandLines);
 
+  /**
+   * Add target byproducts.
+   */
+  void AddTargetByproducts(cmTarget* target,
+                           const std::vector<std::string>& byproducts);
+
+  /**
+   * Add source file outputs.
+   */
+  void AddSourceOutputs(cmSourceFile* source,
+                        const std::vector<std::string>& outputs,
+                        const std::vector<std::string>& byproducts);
+
   /**
    * Add a define flag to the build.
    */
@@ -225,6 +241,10 @@ public:
   cmTarget* AddNewTarget(cmStateEnums::TargetType type,
                          const std::string& name);
 
+  /** Create a target instance for the utility.  */
+  cmTarget* AddNewUtilityTarget(const std::string& utilityName,
+                                cmCommandOrigin origin, bool excludeFromAll);
+
   /**
    * Add an executable to the build.
    */
@@ -232,26 +252,19 @@ public:
                           const std::vector<std::string>& srcs,
                           bool excludeFromAll = false);
 
-  /** Where the target originated from. */
-  enum class TargetOrigin
-  {
-    Project,
-    Generator
-  };
+  /**
+   * Return the utility target output source file name and the CMP0049 name.
+   */
+  cmUtilityOutput GetUtilityOutput(cmTarget* target);
 
   /**
    * Add a utility to the build.  A utility target is a command that
    * is run every time the target is built.
    */
   cmTarget* AddUtilityCommand(
-    const std::string& utilityName, TargetOrigin origin, bool excludeFromAll,
-    const char* workingDirectory, const std::vector<std::string>& depends,
-    const cmCustomCommandLines& commandLines, bool escapeOldStyle = true,
-    const char* comment = nullptr, bool uses_terminal = false,
-    bool command_expand_lists = false, const std::string& job_pool = "");
-  cmTarget* AddUtilityCommand(
-    const std::string& utilityName, TargetOrigin origin, bool excludeFromAll,
-    const char* workingDirectory, const std::vector<std::string>& byproducts,
+    const std::string& utilityName, cmCommandOrigin origin,
+    bool excludeFromAll, const char* workingDirectory,
+    const std::vector<std::string>& byproducts,
     const std::vector<std::string>& depends,
     const cmCustomCommandLines& commandLines, bool escapeOldStyle = true,
     const char* comment = nullptr, bool uses_terminal = false,
@@ -455,6 +468,12 @@ public:
     const std::string& sourceName, bool generated = false,
     cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous);
 
+  /** Get a cmSourceFile pointer for a given source name and always mark the
+   * file as generated, if the name is not found, then create the source file
+   * and return it.
+   */
+  cmSourceFile* GetOrCreateGeneratedSource(const std::string& sourceName);
+
   void AddTargetObject(std::string const& tgtName, std::string const& objFile);
 
   /**
@@ -1036,13 +1055,12 @@ private:
   friend bool cmCMakePolicyCommand(std::vector<std::string> const& args,
                                    cmExecutionStatus& status);
   class IncludeScope;
-
   friend class IncludeScope;
-  class ListFileScope;
 
+  class ListFileScope;
   friend class ListFileScope;
-  class BuildsystemFileScope;
 
+  class BuildsystemFileScope;
   friend class BuildsystemFileScope;
 
   // CMP0053 == old
@@ -1061,10 +1079,12 @@ private:
 
   bool ValidateCustomCommand(const cmCustomCommandLines& commandLines) const;
 
+  void CreateGeneratedSources(const std::vector<std::string>& outputs);
+
   void CommitCustomCommandToTarget(
     cmTarget* target, const std::vector<std::string>& byproducts,
     const std::vector<std::string>& depends,
-    const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
+    const cmCustomCommandLines& commandLines, cmCustomCommandType type,
     const char* comment, const char* workingDir, bool escapeOldStyle,
     bool uses_terminal, const std::string& depfile,
     const std::string& job_pool, bool command_expand_lists);
@@ -1083,8 +1103,7 @@ private:
     const cmImplicitDependsList& implicit_depends,
     const cmCustomCommandLines& commandLines);
 
-  void CommitUtilityCommand(cmTarget* target, const std::string& force,
-                            const std::string& forceCMP0049,
+  void CommitUtilityCommand(cmTarget* target, const cmUtilityOutput& force,
                             const char* workingDirectory,
                             const std::vector<std::string>& byproducts,
                             const std::vector<std::string>& depends,
@@ -1093,9 +1112,6 @@ private:
                             bool uses_terminal, bool command_expand_lists,
                             const std::string& job_pool);
 
-  void CreateGeneratedSource(const std::string& output);
-  void CreateGeneratedSources(const std::vector<std::string>& outputs);
-
   /**
    * See LinearGetSourceFileWithOutput for background information
    */
@@ -1120,12 +1136,7 @@ private:
   using OutputToSourceMap = std::unordered_map<std::string, SourceEntry>;
   OutputToSourceMap OutputToSource;
 
-  void UpdateOutputToSourceMap(std::vector<std::string> const& byproducts,
-                               cmTarget* target);
   void UpdateOutputToSourceMap(std::string const& byproduct, cmTarget* target);
-
-  void UpdateOutputToSourceMap(std::vector<std::string> const& outputs,
-                               cmSourceFile* source, bool byproduct);
   void UpdateOutputToSourceMap(std::string const& output, cmSourceFile* source,
                                bool byproduct);
 

+ 2 - 1
Source/cmQtAutoGenGlobalInitializer.cxx

@@ -3,6 +3,7 @@
 #include "cmQtAutoGenGlobalInitializer.h"
 
 #include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
 #include "cmDuration.h"
 #include "cmGeneratorTarget.h"
 #include "cmLocalGenerator.h"
@@ -154,7 +155,7 @@ void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget(
 
     // Create utility target
     cmTarget* target = makefile->AddUtilityCommand(
-      name, cmMakefile::TargetOrigin::Generator, true,
+      name, cmCommandOrigin::Generator, true,
       makefile->GetHomeOutputDirectory().c_str() /*work dir*/,
       std::vector<std::string>() /*output*/,
       std::vector<std::string>() /*depends*/, cmCustomCommandLines(), false,

+ 5 - 5
Source/cmQtAutoGenInitializer.cxx

@@ -8,6 +8,7 @@
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -1085,7 +1086,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
                        this->Dir.Work.c_str());
     cc.SetEscapeOldStyle(false);
     cc.SetEscapeAllowMakeVars(true);
-    this->GenTarget->Target->AddPreBuildCommand(cc);
+    this->GenTarget->Target->AddPreBuildCommand(std::move(cc));
   } else {
 
     // Add link library target dependencies to the autogen target
@@ -1117,7 +1118,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
 
     // Create autogen target
     cmTarget* autogenTarget = this->Makefile->AddUtilityCommand(
-      this->AutogenTarget.Name, cmMakefile::TargetOrigin::Generator, true,
+      this->AutogenTarget.Name, cmCommandOrigin::Generator, true,
       this->Dir.Work.c_str(), /*byproducts=*/autogenProvides,
       std::vector<std::string>(this->AutogenTarget.DependFiles.begin(),
                                this->AutogenTarget.DependFiles.end()),
@@ -1199,9 +1200,8 @@ bool cmQtAutoGenInitializer::InitRccTargets()
         }
 
         cmTarget* autoRccTarget = this->Makefile->AddUtilityCommand(
-          ccName, cmMakefile::TargetOrigin::Generator, true,
-          this->Dir.Work.c_str(), ccOutput, ccDepends, commandLines, false,
-          ccComment.c_str());
+          ccName, cmCommandOrigin::Generator, true, this->Dir.Work.c_str(),
+          ccOutput, ccDepends, commandLines, false, ccComment.c_str());
 
         // Create autogen generator target
         this->LocalGen->AddGeneratorTarget(

+ 15 - 0
Source/cmTarget.cxx

@@ -604,6 +604,11 @@ void cmTarget::AddPreBuildCommand(cmCustomCommand const& cmd)
   impl->PreBuildCommands.push_back(cmd);
 }
 
+void cmTarget::AddPreBuildCommand(cmCustomCommand&& cmd)
+{
+  impl->PreBuildCommands.push_back(std::move(cmd));
+}
+
 std::vector<cmCustomCommand> const& cmTarget::GetPreLinkCommands() const
 {
   return impl->PreLinkCommands;
@@ -614,6 +619,11 @@ void cmTarget::AddPreLinkCommand(cmCustomCommand const& cmd)
   impl->PreLinkCommands.push_back(cmd);
 }
 
+void cmTarget::AddPreLinkCommand(cmCustomCommand&& cmd)
+{
+  impl->PreLinkCommands.push_back(std::move(cmd));
+}
+
 std::vector<cmCustomCommand> const& cmTarget::GetPostBuildCommands() const
 {
   return impl->PostBuildCommands;
@@ -624,6 +634,11 @@ void cmTarget::AddPostBuildCommand(cmCustomCommand const& cmd)
   impl->PostBuildCommands.push_back(cmd);
 }
 
+void cmTarget::AddPostBuildCommand(cmCustomCommand&& cmd)
+{
+  impl->PostBuildCommands.push_back(std::move(cmd));
+}
+
 void cmTarget::AddTracedSources(std::vector<std::string> const& srcs)
 {
   if (!srcs.empty()) {

+ 3 - 7
Source/cmTarget.h

@@ -43,13 +43,6 @@ public:
     VisibilityImportedGlobally
   };
 
-  enum CustomCommandType
-  {
-    PRE_BUILD,
-    PRE_LINK,
-    POST_BUILD
-  };
-
   cmTarget(std::string const& name, cmStateEnums::TargetType type,
            Visibility vis, cmMakefile* mf);
 
@@ -91,14 +84,17 @@ public:
   //! Get the list of the PRE_BUILD custom commands for this target
   std::vector<cmCustomCommand> const& GetPreBuildCommands() const;
   void AddPreBuildCommand(cmCustomCommand const& cmd);
+  void AddPreBuildCommand(cmCustomCommand&& cmd);
 
   //! Get the list of the PRE_LINK custom commands for this target
   std::vector<cmCustomCommand> const& GetPreLinkCommands() const;
   void AddPreLinkCommand(cmCustomCommand const& cmd);
+  void AddPreLinkCommand(cmCustomCommand&& cmd);
 
   //! Get the list of the POST_BUILD custom commands for this target
   std::vector<cmCustomCommand> const& GetPostBuildCommands() const;
   void AddPostBuildCommand(cmCustomCommand const& cmd);
+  void AddPostBuildCommand(cmCustomCommand&& cmd);
 
   //! Add sources to the target.
   void AddSources(std::vector<std::string> const& srcs);