From 048d1adb4ede50e49dce00873a5961e424e149f9 Mon Sep 17 00:00:00 2001 From: Kulla Christoph Date: Fri, 5 Aug 2016 14:39:31 +0200 Subject: [PATCH] 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 --- Help/command/add_custom_command.rst | 7 +++++++ .../dev/ninja-add_custom_command-depfile.rst | 6 ++++++ Source/cmAddCustomCommandCommand.cxx | 19 ++++++++++++++++--- Source/cmCustomCommand.cxx | 10 ++++++++++ Source/cmCustomCommand.h | 5 +++++ Source/cmGlobalNinjaGenerator.cxx | 10 ++++++---- Source/cmGlobalNinjaGenerator.h | 3 ++- Source/cmLocalNinjaGenerator.cxx | 3 ++- Source/cmMakefile.cxx | 10 ++++++---- Source/cmMakefile.h | 18 ++++++++---------- Source/cmNinjaUtilityTargetGenerator.cxx | 2 +- .../CustomCommandDepfile-ERROR-result.txt | 1 + .../CustomCommandDepfile-ERROR-stderr.txt | 5 +++++ .../Make/CustomCommandDepfile-ERROR.cmake | 8 ++++++++ Tests/RunCMake/Make/RunCMakeTest.cmake | 2 ++ .../Ninja/CustomCommandDepfile-check.cmake | 5 +++++ .../RunCMake/Ninja/CustomCommandDepfile.cmake | 11 +++++++++++ Tests/RunCMake/Ninja/RunCMakeTest.cmake | 2 ++ 18 files changed, 103 insertions(+), 24 deletions(-) create mode 100644 Help/release/dev/ninja-add_custom_command-depfile.rst create mode 100644 Tests/RunCMake/Make/CustomCommandDepfile-ERROR-result.txt create mode 100644 Tests/RunCMake/Make/CustomCommandDepfile-ERROR-stderr.txt create mode 100644 Tests/RunCMake/Make/CustomCommandDepfile-ERROR.cmake create mode 100644 Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake create mode 100644 Tests/RunCMake/Ninja/CustomCommandDepfile.cmake diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index d42136432..4ab4298f7 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -20,6 +20,7 @@ The first signature is for adding a custom command to produce an output:: [ 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 ^^^^^^^^^^^^ diff --git a/Help/release/dev/ninja-add_custom_command-depfile.rst b/Help/release/dev/ninja-add_custom_command-depfile.rst new file mode 100644 index 000000000..c8099fe8a --- /dev/null +++ b/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. diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx index 400be7712..2c4a4ca91 100644 --- a/Source/cmAddCustomCommandCommand.cxx +++ b/Source/cmAddCustomCommandCommand.cxx @@ -15,6 +15,8 @@ #include "cmSourceFile.h" +#include "cmGlobalGenerator.h" + // cmAddCustomCommandCommand bool cmAddCustomCommandCommand::InitialPass( std::vector 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 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 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()) { diff --git a/Source/cmCustomCommand.cxx b/Source/cmCustomCommand.cxx index 7533369fa..eaa49b04d 100644 --- a/Source/cmCustomCommand.cxx +++ b/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; +} diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h index c2b9738e5..34753f405 100644 --- a/Source/cmCustomCommand.h +++ b/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 Outputs; std::vector Byproducts; @@ -97,6 +101,7 @@ private: ImplicitDependsList ImplicitDepends; std::string Comment; std::string WorkingDirectory; + std::string Depfile; bool HaveComment; bool EscapeAllowMakeVars; bool EscapeOldStyle; diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 2b8347901..9bc8246b7 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/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); } } diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 52fa5c955..082ee3aba 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/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()); diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx index 46d7e18cf..4200e5d25 100644 --- a/Source/cmLocalNinjaGenerator.cxx +++ b/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); } diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 6e4779746..d1fddca27 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -701,7 +701,7 @@ void cmMakefile::AddCustomCommandToTarget( const std::vector& 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& 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& 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 outputs; outputs.push_back(output); std::vector 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( diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index b3587c5a7..4d137db67 100644 --- a/Source/cmMakefile.h +++ b/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& byproducts, - const std::vector& 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& byproducts, + const std::vector& 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& outputs, const std::vector& 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& 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& outputs, const std::vector& depends, diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx index 49836f27a..0664104a6 100644 --- a/Source/cmNinjaUtilityTargetGenerator.cxx +++ b/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( diff --git a/Tests/RunCMake/Make/CustomCommandDepfile-ERROR-result.txt b/Tests/RunCMake/Make/CustomCommandDepfile-ERROR-result.txt new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/Tests/RunCMake/Make/CustomCommandDepfile-ERROR-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/Make/CustomCommandDepfile-ERROR-stderr.txt b/Tests/RunCMake/Make/CustomCommandDepfile-ERROR-stderr.txt new file mode 100644 index 000000000..74d62a47d --- /dev/null +++ b/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\)$ diff --git a/Tests/RunCMake/Make/CustomCommandDepfile-ERROR.cmake b/Tests/RunCMake/Make/CustomCommandDepfile-ERROR.cmake new file mode 100644 index 000000000..bad795501 --- /dev/null +++ b/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" + ) diff --git a/Tests/RunCMake/Make/RunCMakeTest.cmake b/Tests/RunCMake/Make/RunCMakeTest.cmake index c6bbd03c0..869d11e5d 100644 --- a/Tests/RunCMake/Make/RunCMakeTest.cmake +++ b/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) diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfile-check.cmake new file mode 100644 index 000000000..189de6417 --- /dev/null +++ b/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() diff --git a/Tests/RunCMake/Ninja/CustomCommandDepfile.cmake b/Tests/RunCMake/Ninja/CustomCommandDepfile.cmake new file mode 100644 index 000000000..dbef2a535 --- /dev/null +++ b/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) diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake index 622c32760..778f2c111 100644 --- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake +++ b/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)