Переглянути джерело

add_custom_command: Add DEPFILE option for Ninja

Provide a way for custom commands to inform the ninja build tool about
their implicit dependencies.  For now simply make use of the option an
error on other generators.

Closes: #15479
Kulla Christoph 9 роки тому
батько
коміт
048d1adb4e

+ 7 - 0
Help/command/add_custom_command.rst

@@ -20,6 +20,7 @@ The first signature is for adding a custom command to produce an output::
                                       [<lang2> depend2] ...]
                      [WORKING_DIRECTORY dir]
                      [COMMENT comment]
+                     [DEPFILE depfile]
                      [VERBATIM] [APPEND] [USES_TERMINAL])
 
 This defines a command to generate specified ``OUTPUT`` file(s).
@@ -170,6 +171,12 @@ The options are:
   If it is a relative path it will be interpreted relative to the
   build tree directory corresponding to the current source directory.
 
+``DEPFILE``
+  Specify a ``.d`` depfile for the :generator:`Ninja` generator.
+  A ``.d`` file holds dependencies usually emitted by the custom
+  command itself.
+  Using ``DEPFILE`` with other generators than Ninja is an error.
+
 Build Events
 ^^^^^^^^^^^^
 

+ 6 - 0
Help/release/dev/ninja-add_custom_command-depfile.rst

@@ -0,0 +1,6 @@
+ninja-add_custom_command-depfile
+--------------------------------
+
+* The :command:`add_custom_command` command gained a new ``DEPFILE``
+  option that works with the :generator:`Ninja` generator to provide
+  implicit dependency information to the build tool.

+ 16 - 3
Source/cmAddCustomCommandCommand.cxx

@@ -15,6 +15,8 @@
 
 #include "cmSourceFile.h"
 
+#include "cmGlobalGenerator.h"
+
 // cmAddCustomCommandCommand
 bool cmAddCustomCommandCommand::InitialPass(
   std::vector<std::string> const& args, cmExecutionStatus&)
@@ -28,7 +30,7 @@ bool cmAddCustomCommandCommand::InitialPass(
     return false;
   }
 
-  std::string source, target, main_dependency, working;
+  std::string source, target, main_dependency, working, depfile;
   std::string comment_buffer;
   const char* comment = CM_NULLPTR;
   std::vector<std::string> depends, outputs, output, byproducts;
@@ -60,6 +62,7 @@ bool cmAddCustomCommandCommand::InitialPass(
     doing_byproducts,
     doing_comment,
     doing_working_directory,
+    doing_depfile,
     doing_nothing
   };
 
@@ -110,6 +113,13 @@ bool cmAddCustomCommandCommand::InitialPass(
       doing = doing_implicit_depends_lang;
     } else if (copy == "COMMENT") {
       doing = doing_comment;
+    } else if (copy == "DEPFILE") {
+      doing = doing_depfile;
+      if (this->Makefile->GetGlobalGenerator()->GetName() != "Ninja") {
+        this->SetError("Option DEPFILE not supported by " +
+                       this->Makefile->GetGlobalGenerator()->GetName());
+        return false;
+      }
     } else {
       std::string filename;
       switch (doing) {
@@ -147,6 +157,9 @@ bool cmAddCustomCommandCommand::InitialPass(
         filename = cmSystemTools::CollapseFullPath(filename);
       }
       switch (doing) {
+        case doing_depfile:
+          depfile = copy;
+          break;
         case doing_working_directory:
           working = copy;
           break;
@@ -269,12 +282,12 @@ bool cmAddCustomCommandCommand::InitialPass(
     std::vector<std::string> no_depends;
     this->Makefile->AddCustomCommandToTarget(
       target, byproducts, no_depends, commandLines, cctype, comment,
-      working.c_str(), escapeOldStyle, uses_terminal);
+      working.c_str(), escapeOldStyle, uses_terminal, depfile);
   } else if (target.empty()) {
     // Target is empty, use the output.
     this->Makefile->AddCustomCommandToOutput(
       output, byproducts, depends, main_dependency, commandLines, comment,
-      working.c_str(), false, escapeOldStyle, uses_terminal);
+      working.c_str(), false, escapeOldStyle, uses_terminal, depfile);
 
     // Add implicit dependency scanning requests if any were given.
     if (!implicit_depends.empty()) {

+ 10 - 0
Source/cmCustomCommand.cxx

@@ -135,3 +135,13 @@ void cmCustomCommand::SetUsesTerminal(bool b)
 {
   this->UsesTerminal = b;
 }
+
+const std::string& cmCustomCommand::GetDepfile() const
+{
+  return this->Depfile;
+}
+
+void cmCustomCommand::SetDepfile(const std::string& depfile)
+{
+  this->Depfile = depfile;
+}

+ 5 - 0
Source/cmCustomCommand.h

@@ -88,6 +88,10 @@ public:
   bool GetUsesTerminal() const;
   void SetUsesTerminal(bool b);
 
+  /** Set/Get the depfile (used by the Ninja generator) */
+  const std::string& GetDepfile() const;
+  void SetDepfile(const std::string& depfile);
+
 private:
   std::vector<std::string> Outputs;
   std::vector<std::string> Byproducts;
@@ -97,6 +101,7 @@ private:
   ImplicitDependsList ImplicitDepends;
   std::string Comment;
   std::string WorkingDirectory;
+  std::string Depfile;
   bool HaveComment;
   bool EscapeAllowMakeVars;
   bool EscapeOldStyle;

+ 6 - 4
Source/cmGlobalNinjaGenerator.cxx

@@ -251,8 +251,8 @@ void cmGlobalNinjaGenerator::AddCustomCommandRule()
 
 void cmGlobalNinjaGenerator::WriteCustomCommandBuild(
   const std::string& command, const std::string& description,
-  const std::string& comment, bool uses_terminal, bool restat,
-  const cmNinjaDeps& outputs, const cmNinjaDeps& deps,
+  const std::string& comment, const std::string& depfile, bool uses_terminal,
+  bool restat, const cmNinjaDeps& outputs, const cmNinjaDeps& deps,
   const cmNinjaDeps& orderOnly)
 {
   std::string cmd = command;
@@ -273,7 +273,9 @@ void cmGlobalNinjaGenerator::WriteCustomCommandBuild(
   if (uses_terminal && SupportsConsolePool()) {
     vars["pool"] = "console";
   }
-
+  if (!depfile.empty()) {
+    vars["depfile"] = depfile;
+  }
   this->WriteBuild(*this->BuildFileStream, comment, "CUSTOM_COMMAND", outputs,
                    deps, cmNinjaDeps(), orderOnly, vars);
 
@@ -839,7 +841,7 @@ void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
     std::copy(i->second.begin(), i->second.end(), std::back_inserter(deps));
     WriteCustomCommandBuild(/*command=*/"", /*description=*/"",
                             "Assume dependencies for generated source file.",
-                            /*uses_terminal*/ false,
+                            /*depfile*/ "", /*uses_terminal*/ false,
                             /*restat*/ true, cmNinjaDeps(1, i->first), deps);
   }
 }

+ 2 - 1
Source/cmGlobalNinjaGenerator.h

@@ -113,7 +113,8 @@ public:
 
   void WriteCustomCommandBuild(const std::string& command,
                                const std::string& description,
-                               const std::string& comment, bool uses_terminal,
+                               const std::string& comment,
+                               const std::string& depfile, bool uses_terminal,
                                bool restat, const cmNinjaDeps& outputs,
                                const cmNinjaDeps& deps = cmNinjaDeps(),
                                const cmNinjaDeps& orderOnly = cmNinjaDeps());

+ 2 - 1
Source/cmLocalNinjaGenerator.cxx

@@ -412,7 +412,8 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
   } else {
     this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild(
       this->BuildCommandLine(cmdLines), this->ConstructComment(ccg),
-      "Custom command for " + ninjaOutputs[0], cc->GetUsesTerminal(),
+      "Custom command for " + ninjaOutputs[0], cc->GetDepfile(),
+      cc->GetUsesTerminal(),
       /*restat*/ !symbolic || !byproducts.empty(), ninjaOutputs, ninjaDeps,
       orderOnlyDeps);
   }

+ 6 - 4
Source/cmMakefile.cxx

@@ -701,7 +701,7 @@ void cmMakefile::AddCustomCommandToTarget(
   const std::vector<std::string>& depends,
   const cmCustomCommandLines& commandLines, cmTarget::CustomCommandType type,
   const char* comment, const char* workingDir, bool escapeOldStyle,
-  bool uses_terminal)
+  bool uses_terminal, const std::string& depfile)
 {
   // Find the target to which to add the custom command.
   cmTargets::iterator ti = this->Targets.find(target);
@@ -773,6 +773,7 @@ void cmMakefile::AddCustomCommandToTarget(
   cc.SetEscapeOldStyle(escapeOldStyle);
   cc.SetEscapeAllowMakeVars(true);
   cc.SetUsesTerminal(uses_terminal);
+  cc.SetDepfile(depfile);
   switch (type) {
     case cmTarget::PRE_BUILD:
       ti->second.AddPreBuildCommand(cc);
@@ -792,7 +793,7 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput(
   const std::vector<std::string>& depends, const std::string& main_dependency,
   const cmCustomCommandLines& commandLines, const char* comment,
   const char* workingDir, bool replace, bool escapeOldStyle,
-  bool uses_terminal)
+  bool uses_terminal, const std::string& depfile)
 {
   // Make sure there is at least one output.
   if (outputs.empty()) {
@@ -886,6 +887,7 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput(
     cc->SetEscapeOldStyle(escapeOldStyle);
     cc->SetEscapeAllowMakeVars(true);
     cc->SetUsesTerminal(uses_terminal);
+    cc->SetDepfile(depfile);
     file->SetCustomCommand(cc);
     this->UpdateOutputToSourceMap(outputs, file);
   }
@@ -923,14 +925,14 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput(
   const std::string& output, const std::vector<std::string>& depends,
   const std::string& main_dependency, const cmCustomCommandLines& commandLines,
   const char* comment, const char* workingDir, bool replace,
-  bool escapeOldStyle, bool uses_terminal)
+  bool escapeOldStyle, bool uses_terminal, const std::string& depfile)
 {
   std::vector<std::string> outputs;
   outputs.push_back(output);
   std::vector<std::string> no_byproducts;
   return this->AddCustomCommandToOutput(
     outputs, no_byproducts, depends, main_dependency, commandLines, comment,
-    workingDir, replace, escapeOldStyle, uses_terminal);
+    workingDir, replace, escapeOldStyle, uses_terminal, depfile);
 }
 
 void cmMakefile::AddCustomCommandOldStyle(

+ 8 - 10
Source/cmMakefile.h

@@ -135,14 +135,12 @@ public:
   void FinalPass();
 
   /** Add a custom command to the build.  */
-  void 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 = true,
-                                bool uses_terminal = false);
+  void 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 = true,
+    bool uses_terminal = false, const std::string& depfile = "");
   cmSourceFile* AddCustomCommandToOutput(
     const std::vector<std::string>& outputs,
     const std::vector<std::string>& byproducts,
@@ -150,13 +148,13 @@ public:
     const std::string& main_dependency,
     const cmCustomCommandLines& commandLines, const char* comment,
     const char* workingDir, bool replace = false, bool escapeOldStyle = true,
-    bool uses_terminal = false);
+    bool uses_terminal = false, const std::string& depfile = "");
   cmSourceFile* AddCustomCommandToOutput(
     const std::string& output, const std::vector<std::string>& depends,
     const std::string& main_dependency,
     const cmCustomCommandLines& commandLines, const char* comment,
     const char* workingDir, bool replace = false, bool escapeOldStyle = true,
-    bool uses_terminal = false);
+    bool uses_terminal = false, const std::string& depfile = "");
   void AddCustomCommandOldStyle(const std::string& target,
                                 const std::vector<std::string>& outputs,
                                 const std::vector<std::string>& depends,

+ 1 - 1
Source/cmNinjaUtilityTargetGenerator.cxx

@@ -150,7 +150,7 @@ void cmNinjaUtilityTargetGenerator::Generate()
 
     this->GetGlobalGenerator()->WriteCustomCommandBuild(
       command, desc, "Utility command for " + this->GetTargetName(),
-      uses_terminal,
+      /*depfile*/ "", uses_terminal,
       /*restat*/ true, util_outputs, deps);
 
     this->GetGlobalGenerator()->WritePhonyBuild(

+ 1 - 0
Tests/RunCMake/Make/CustomCommandDepfile-ERROR-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/Make/CustomCommandDepfile-ERROR-stderr.txt

@@ -0,0 +1,5 @@
+^CMake Error at CustomCommandDepfile-ERROR.cmake:1 \(add_custom_command\):
+  add_custom_command Option DEPFILE not supported by [^
+]+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$

+ 8 - 0
Tests/RunCMake/Make/CustomCommandDepfile-ERROR.cmake

@@ -0,0 +1,8 @@
+add_custom_command(
+  OUTPUT hello.copy.c
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
+          hello.copy.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test.d"
+  )

+ 2 - 0
Tests/RunCMake/Make/RunCMakeTest.cmake

@@ -15,3 +15,5 @@ run_TargetMessages(OFF)
 
 run_TargetMessages(VAR-ON -DCMAKE_TARGET_MESSAGES=ON)
 run_TargetMessages(VAR-OFF -DCMAKE_TARGET_MESSAGES=OFF)
+
+run_cmake(CustomCommandDepfile-ERROR)

+ 5 - 0
Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake

@@ -0,0 +1,5 @@
+set(log "${RunCMake_BINARY_DIR}/CustomCommandDepfile-build/build.ninja")
+file(READ "${log}" build_file)
+if(NOT "${build_file}" MATCHES "depfile = test\\.d")
+  set(RunCMake_TEST_FAILED "Log file:\n ${log}\ndoes not have expected line: depfile = test.d")
+endif()

+ 11 - 0
Tests/RunCMake/Ninja/CustomCommandDepfile.cmake

@@ -0,0 +1,11 @@
+add_custom_command(
+  OUTPUT hello.copy.c
+  COMMAND "${CMAKE_COMMAND}" -E copy
+          "${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
+          hello.copy.c
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+  DEPFILE "test.d"
+  )
+add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/hello.copy.c")
+
+include(CheckNoPrefixSubDir.cmake)

+ 2 - 0
Tests/RunCMake/Ninja/RunCMakeTest.cmake

@@ -32,6 +32,8 @@ run_CMP0058(WARN-by)
 run_CMP0058(NEW-no)
 run_CMP0058(NEW-by)
 
+run_cmake(CustomCommandDepfile)
+
 function(run_SubDir)
   # Use a single build tree for a few tests without cleaning.
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/SubDir-build)