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
This commit is contained in:
Kulla Christoph 2016-08-05 14:39:31 +02:00 committed by Brad King
parent 98caa14cc8
commit 048d1adb4e
18 changed files with 103 additions and 24 deletions

View File

@ -20,6 +20,7 @@ The first signature is for adding a custom command to produce an output::
[<lang2> depend2] ...] [<lang2> depend2] ...]
[WORKING_DIRECTORY dir] [WORKING_DIRECTORY dir]
[COMMENT comment] [COMMENT comment]
[DEPFILE depfile]
[VERBATIM] [APPEND] [USES_TERMINAL]) [VERBATIM] [APPEND] [USES_TERMINAL])
This defines a command to generate specified ``OUTPUT`` file(s). 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 If it is a relative path it will be interpreted relative to the
build tree directory corresponding to the current source directory. 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 Build Events
^^^^^^^^^^^^ ^^^^^^^^^^^^

View File

@ -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.

View File

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

View File

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

View File

@ -88,6 +88,10 @@ public:
bool GetUsesTerminal() const; bool GetUsesTerminal() const;
void SetUsesTerminal(bool b); 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: private:
std::vector<std::string> Outputs; std::vector<std::string> Outputs;
std::vector<std::string> Byproducts; std::vector<std::string> Byproducts;
@ -97,6 +101,7 @@ private:
ImplicitDependsList ImplicitDepends; ImplicitDependsList ImplicitDepends;
std::string Comment; std::string Comment;
std::string WorkingDirectory; std::string WorkingDirectory;
std::string Depfile;
bool HaveComment; bool HaveComment;
bool EscapeAllowMakeVars; bool EscapeAllowMakeVars;
bool EscapeOldStyle; bool EscapeOldStyle;

View File

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

View File

@ -113,7 +113,8 @@ public:
void WriteCustomCommandBuild(const std::string& command, void WriteCustomCommandBuild(const std::string& command,
const std::string& description, 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, bool restat, const cmNinjaDeps& outputs,
const cmNinjaDeps& deps = cmNinjaDeps(), const cmNinjaDeps& deps = cmNinjaDeps(),
const cmNinjaDeps& orderOnly = cmNinjaDeps()); const cmNinjaDeps& orderOnly = cmNinjaDeps());

View File

@ -412,7 +412,8 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
} else { } else {
this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild( this->GetGlobalNinjaGenerator()->WriteCustomCommandBuild(
this->BuildCommandLine(cmdLines), this->ConstructComment(ccg), 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, /*restat*/ !symbolic || !byproducts.empty(), ninjaOutputs, ninjaDeps,
orderOnlyDeps); orderOnlyDeps);
} }

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
1

View File

@ -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\)$

View File

@ -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"
)

View File

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

View File

@ -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()

View File

@ -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)

View File

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