Merge topic 'custom-command-byproducts'

557aef0b ExternalProject: Add options to specify BYPRODUCTS (#14963)
e15a7075 Add an option for explicit BYPRODUCTS of custom commands (#14963)
This commit is contained in:
Brad King 2014-11-25 10:18:16 -05:00 committed by CMake Topic Stage
commit 0f19208076
46 changed files with 469 additions and 30 deletions

View File

@ -15,6 +15,7 @@ The first signature is for adding a custom command to produce an output::
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCY depend]
[DEPENDS [depends...]]
[BYPRODUCTS [files...]]
[IMPLICIT_DEPENDS <lang1> depend1
[<lang2> depend2] ...]
[WORKING_DIRECTORY dir]
@ -44,6 +45,27 @@ The options are:
options are currently ignored when APPEND is given, but may be
used in the future.
``BYPRODUCTS``
Specify the files the command is expected to produce but whose
modification time may or may not be newer than the dependencies.
If a byproduct name is a relative path it will be interpreted
relative to the build tree directory corresponding to the
current source directory.
Each byproduct file will be marked with the :prop_sf:`GENERATED`
source file property automatically.
Explicit specification of byproducts is supported by the
:generator:`Ninja` generator to tell the ``ninja`` build tool
how to regenerate byproducts when they are missing. It is
also useful when other build rules (e.g. custom commands)
depend on the byproducts. Ninja requires a build rule for any
generated file on which another rule depends even if there are
order-only dependencies to ensure the byproducts will be
available before their dependents build.
The ``BYPRODUCTS`` option is ignored on non-Ninja generators
except to mark byproducts ``GENERATED``.
``COMMAND``
Specify the command-line(s) to execute at build time.
If more than one ``COMMAND`` is specified they will be executed in order,
@ -156,6 +178,7 @@ target is already built, the command will not execute.
PRE_BUILD | PRE_LINK | POST_BUILD
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL])

View File

@ -8,6 +8,7 @@ Add a target with no output so it will always be built.
add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL]
@ -28,6 +29,27 @@ The options are:
target so that it will be run every time (the command cannot be
called ``ALL``).
``BYPRODUCTS``
Specify the files the command is expected to produce but whose
modification time may or may not be updated on subsequent builds.
If a byproduct name is a relative path it will be interpreted
relative to the build tree directory corresponding to the
current source directory.
Each byproduct file will be marked with the :prop_sf:`GENERATED`
source file property automatically.
Explicit specification of byproducts is supported by the
:generator:`Ninja` generator to tell the ``ninja`` build tool
how to regenerate byproducts when they are missing. It is
also useful when other build rules (e.g. custom commands)
depend on the byproducts. Ninja requires a build rule for any
generated file on which another rule depends even if there are
order-only dependencies to ensure the byproducts will be
available before their dependents build.
The ``BYPRODUCTS`` option is ignored on non-Ninja generators
except to mark byproducts ``GENERATED``.
``COMMAND``
Specify the command-line(s) to execute at build time.
If more than one ``COMMAND`` is specified they will be executed in order,

View File

@ -132,6 +132,9 @@ Create custom targets to build projects in external trees
Use source dir for build dir
``BUILD_ALWAYS 1``
No stamp file, build step always runs
``BUILD_BYPRODUCTS <file>...``
Files that will be generated by the build command but may or may
not have their modification time updated by subsequent builds.
Install step options are:
@ -234,6 +237,9 @@ Create custom targets to build projects in external trees
Steps that depend on this step
``DEPENDS <file>...``
Files on which this step depends
``BYPRODUCTS <file>...``
Files that will be generated by this step but may or may not
have their modification time updated by subsequent builds.
``ALWAYS 1``
No stamp file, step always runs
``EXCLUDE_FROM_MAIN 1``
@ -1409,6 +1415,9 @@ function(ExternalProject_Add_Step name step)
# Dependencies on files.
get_property(depends TARGET ${name} PROPERTY _EP_${step}_DEPENDS)
# Byproducts of the step.
get_property(byproducts TARGET ${name} PROPERTY _EP_${step}_BYPRODUCTS)
# Dependencies on steps.
get_property(dependees TARGET ${name} PROPERTY _EP_${step}_DEPENDEES)
foreach(dependee IN LISTS dependees)
@ -1466,6 +1475,7 @@ function(ExternalProject_Add_Step name step)
add_custom_command(
OUTPUT ${stamp_file}
BYPRODUCTS ${byproducts}
COMMENT ${comment}
COMMAND ${command}
COMMAND ${touch}
@ -2139,8 +2149,11 @@ function(_ep_add_build_command name)
set(always 0)
endif()
get_property(build_byproducts TARGET ${name} PROPERTY _EP_BUILD_BYPRODUCTS)
ExternalProject_Add_Step(${name} build
COMMAND ${cmd}
BYPRODUCTS ${build_byproducts}
WORKING_DIRECTORY ${binary_dir}
DEPENDEES configure
ALWAYS ${always}

View File

@ -32,7 +32,7 @@ bool cmAddCustomCommandCommand
std::string source, target, main_dependency, working;
std::string comment_buffer;
const char* comment = 0;
std::vector<std::string> depends, outputs, output;
std::vector<std::string> depends, outputs, output, byproducts;
bool verbatim = false;
bool append = false;
bool uses_terminal = false;
@ -57,6 +57,7 @@ bool cmAddCustomCommandCommand
doing_main_dependency,
doing_output,
doing_outputs,
doing_byproducts,
doing_comment,
doing_working_directory,
doing_nothing
@ -127,6 +128,10 @@ bool cmAddCustomCommandCommand
{
doing = doing_output;
}
else if (copy == "BYPRODUCTS")
{
doing = doing_byproducts;
}
else if (copy == "WORKING_DIRECTORY")
{
doing = doing_working_directory;
@ -150,6 +155,7 @@ bool cmAddCustomCommandCommand
{
case doing_output:
case doing_outputs:
case doing_byproducts:
if (!cmSystemTools::FileIsFullPath(copy.c_str()))
{
// This is an output to be generated, so it should be
@ -233,6 +239,9 @@ bool cmAddCustomCommandCommand
case doing_outputs:
outputs.push_back(filename);
break;
case doing_byproducts:
byproducts.push_back(filename);
break;
case doing_comment:
comment_buffer = copy;
comment = comment_buffer.c_str();
@ -272,7 +281,9 @@ bool cmAddCustomCommandCommand
}
// Make sure the output names and locations are safe.
if(!this->CheckOutputs(output) || !this->CheckOutputs(outputs))
if(!this->CheckOutputs(output) ||
!this->CheckOutputs(outputs) ||
!this->CheckOutputs(byproducts))
{
return false;
}
@ -314,7 +325,7 @@ bool cmAddCustomCommandCommand
{
// Source is empty, use the target.
std::vector<std::string> no_depends;
this->Makefile->AddCustomCommandToTarget(target, no_depends,
this->Makefile->AddCustomCommandToTarget(target, byproducts, no_depends,
commandLines, cctype,
comment, working.c_str(),
escapeOldStyle, uses_terminal);
@ -322,8 +333,8 @@ bool cmAddCustomCommandCommand
else if(target.empty())
{
// Target is empty, use the output.
this->Makefile->AddCustomCommandToOutput(output, depends,
main_dependency,
this->Makefile->AddCustomCommandToOutput(output, byproducts,
depends, main_dependency,
commandLines, comment,
working.c_str(), false,
escapeOldStyle, uses_terminal);
@ -351,6 +362,11 @@ bool cmAddCustomCommandCommand
}
}
}
else if (!byproducts.empty())
{
this->SetError("BYPRODUCTS may not be specified with SOURCE signatures");
return false;
}
else if (uses_terminal)
{
this->SetError("USES_TERMINAL may not be used with SOURCE signatures");

View File

@ -45,7 +45,7 @@ bool cmAddCustomTargetCommand
cmCustomCommandLines commandLines;
// Accumulate dependencies.
std::vector<std::string> depends;
std::vector<std::string> depends, byproducts;
std::string working_directory;
bool verbatim = false;
bool uses_terminal = false;
@ -57,6 +57,7 @@ bool cmAddCustomTargetCommand
enum tdoing {
doing_command,
doing_depends,
doing_byproducts,
doing_working_directory,
doing_comment,
doing_source,
@ -85,6 +86,10 @@ bool cmAddCustomTargetCommand
{
doing = doing_depends;
}
else if(copy == "BYPRODUCTS")
{
doing = doing_byproducts;
}
else if(copy == "WORKING_DIRECTORY")
{
doing = doing_working_directory;
@ -128,6 +133,19 @@ bool cmAddCustomTargetCommand
case doing_command:
currentLine.push_back(copy);
break;
case doing_byproducts:
{
std::string filename;
if (!cmSystemTools::FileIsFullPath(copy.c_str()))
{
filename = this->Makefile->GetCurrentOutputDirectory();
filename += "/";
}
filename += copy;
cmSystemTools::ConvertToUnixSlashes(filename);
byproducts.push_back(filename);
}
break;
case doing_depends:
{
std::string dep = copy;
@ -227,6 +245,12 @@ bool cmAddCustomTargetCommand
cmSystemTools::CollapseFullPath(working_directory, build_dir);
}
if (commandLines.empty() && !byproducts.empty())
{
this->Makefile->IssueMessage(cmake::FATAL_ERROR,
"BYPRODUCTS may not be specified without any COMMAND");
return true;
}
if (commandLines.empty() && uses_terminal)
{
this->Makefile->IssueMessage(cmake::FATAL_ERROR,
@ -238,7 +262,8 @@ bool cmAddCustomTargetCommand
bool escapeOldStyle = !verbatim;
cmTarget* target =
this->Makefile->AddUtilityCommand(targetName, excludeFromAll,
working_directory.c_str(), depends,
working_directory.c_str(),
byproducts, depends,
commandLines, escapeOldStyle, comment,
uses_terminal);

View File

@ -353,10 +353,11 @@ void CCONV cmAddCustomCommandToTarget(void *arg, const char* target,
}
// Pass the call to the makefile instance.
std::vector<std::string> no_byproducts;
std::vector<std::string> no_depends;
const char* no_comment = 0;
const char* no_working_dir = 0;
mf->AddCustomCommandToTarget(target, no_depends, commandLines,
mf->AddCustomCommandToTarget(target, no_byproducts, no_depends, commandLines,
cctype, no_comment, no_working_dir);
}

View File

@ -28,6 +28,7 @@ cmCustomCommand::cmCustomCommand()
//----------------------------------------------------------------------------
cmCustomCommand::cmCustomCommand(const cmCustomCommand& r):
Outputs(r.Outputs),
Byproducts(r.Byproducts),
Depends(r.Depends),
CommandLines(r.CommandLines),
HaveComment(r.HaveComment),
@ -49,6 +50,7 @@ cmCustomCommand& cmCustomCommand::operator=(cmCustomCommand const& r)
}
this->Outputs = r.Outputs;
this->Byproducts= r.Byproducts;
this->Depends = r.Depends;
this->CommandLines = r.CommandLines;
this->HaveComment = r.HaveComment;
@ -66,11 +68,13 @@ cmCustomCommand& cmCustomCommand::operator=(cmCustomCommand const& r)
//----------------------------------------------------------------------------
cmCustomCommand::cmCustomCommand(cmMakefile const* mf,
const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines,
const char* comment,
const char* workingDirectory):
Outputs(outputs),
Byproducts(byproducts),
Depends(depends),
CommandLines(commandLines),
HaveComment(comment?true:false),
@ -99,6 +103,12 @@ const std::vector<std::string>& cmCustomCommand::GetOutputs() const
return this->Outputs;
}
//----------------------------------------------------------------------------
const std::vector<std::string>& cmCustomCommand::GetByproducts() const
{
return this->Byproducts;
}
//----------------------------------------------------------------------------
const std::vector<std::string>& cmCustomCommand::GetDepends() const
{

View File

@ -32,6 +32,7 @@ public:
/** Main constructor specifies all information for the command. */
cmCustomCommand(cmMakefile const* mf,
const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const cmCustomCommandLines& commandLines,
const char* comment,
@ -42,6 +43,9 @@ public:
/** Get the output file produced by the command. */
const std::vector<std::string>& GetOutputs() const;
/** Get the extra files produced by the command. */
const std::vector<std::string>& GetByproducts() const;
/** Get the vector that holds the list of dependencies. */
const std::vector<std::string>& GetDepends() const;
@ -86,6 +90,7 @@ public:
private:
std::vector<std::string> Outputs;
std::vector<std::string> Byproducts;
std::vector<std::string> Depends;
cmCustomCommandLines CommandLines;
bool HaveComment;

View File

@ -90,6 +90,12 @@ std::vector<std::string> const& cmCustomCommandGenerator::GetOutputs() const
return this->CC.GetOutputs();
}
//----------------------------------------------------------------------------
std::vector<std::string> const& cmCustomCommandGenerator::GetByproducts() const
{
return this->CC.GetByproducts();
}
//----------------------------------------------------------------------------
std::vector<std::string> const& cmCustomCommandGenerator::GetDepends() const
{

View File

@ -42,6 +42,7 @@ public:
const char* GetComment() const;
std::string GetWorkingDirectory() const;
std::vector<std::string> const& GetOutputs() const;
std::vector<std::string> const& GetByproducts() const;
std::vector<std::string> const& GetDepends() const;
};

View File

@ -2510,10 +2510,11 @@ cmTarget cmGlobalGenerator::CreateGlobalTarget(
target.SetProperty("EXCLUDE_FROM_ALL","TRUE");
std::vector<std::string> no_outputs;
std::vector<std::string> no_byproducts;
std::vector<std::string> no_depends;
// Store the custom command in the target.
cmCustomCommand cc(0, no_outputs, no_depends, *commandLines, 0,
workingDirectory);
cmCustomCommand cc(0, no_outputs, no_byproducts, no_depends,
*commandLines, 0, workingDirectory);
cc.SetUsesTerminal(uses_terminal);
target.AddPostBuildCommand(cc);
target.SetProperty("EchoString", message);

View File

@ -341,9 +341,10 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget()
// overwritten by the CreateVCProjBuildRule.
// (this could be avoided with per-target source files)
std::string no_main_dependency = "";
std::vector<std::string> no_byproducts;
if(cmSourceFile* file =
mf->AddCustomCommandToOutput(
stamps, listFiles,
stamps, no_byproducts, listFiles,
no_main_dependency, commandLines, "Checking Build System",
no_working_directory, true))
{

View File

@ -477,7 +477,9 @@ cmGlobalXCodeGenerator::AddExtraTargets(cmLocalGenerator* root,
this->PostBuildMakeTarget(target.GetName(), "$(CONFIGURATION)");
cmCustomCommandLines commandLines;
commandLines.push_back(makeHelper);
std::vector<std::string> no_byproducts;
lg->GetMakefile()->AddCustomCommandToTarget(target.GetName(),
no_byproducts,
no_depends,
commandLines,
cmTarget::POST_BUILD,
@ -1366,6 +1368,7 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(cmXCodeObject* buildPhases,
cmd[0].push_back(str_link_file);
cmCustomCommand command(this->CurrentMakefile,
std::vector<std::string>(),
std::vector<std::string>(),
std::vector<std::string>(),
cmd,

View File

@ -440,10 +440,18 @@ cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
cmCustomCommandGenerator ccg(*cc, this->GetConfigName(), this->Makefile);
const std::vector<std::string> &outputs = ccg.GetOutputs();
cmNinjaDeps ninjaOutputs(outputs.size()), ninjaDeps;
const std::vector<std::string> &byproducts = ccg.GetByproducts();
cmNinjaDeps ninjaOutputs(outputs.size()+byproducts.size()), ninjaDeps;
#if 0
#error TODO: Once CC in an ExternalProject target must provide the \
file of each imported target that has an add_dependencies pointing \
at us. How to know which ExternalProject step actually provides it?
#endif
std::transform(outputs.begin(), outputs.end(),
ninjaOutputs.begin(), MapToNinjaPath());
std::transform(byproducts.begin(), byproducts.end(),
ninjaOutputs.begin() + outputs.size(), MapToNinjaPath());
this->AppendCustomCommandDeps(ccg, ninjaDeps);
for (cmNinjaDeps::iterator i = ninjaOutputs.begin(); i != ninjaOutputs.end();

View File

@ -821,10 +821,12 @@ cmLocalVisualStudio6Generator::MaybeCreateOutputDir(cmTarget& target,
command.push_back("make_directory");
command.push_back(outDir);
std::vector<std::string> no_output;
std::vector<std::string> no_byproducts;
std::vector<std::string> no_depends;
cmCustomCommandLines commands;
commands.push_back(command);
pcc.reset(new cmCustomCommand(0, no_output, no_depends, commands, 0, 0));
pcc.reset(new cmCustomCommand(0, no_output, no_byproducts,
no_depends, commands, 0, 0));
pcc->SetEscapeOldStyle(false);
pcc->SetEscapeAllowMakeVars(true);
return pcc;

View File

@ -95,10 +95,12 @@ cmLocalVisualStudioGenerator::MaybeCreateImplibDir(cmTarget& target,
command.push_back("make_directory");
command.push_back(impDir);
std::vector<std::string> no_output;
std::vector<std::string> no_byproducts;
std::vector<std::string> no_depends;
cmCustomCommandLines commands;
commands.push_back(command);
pcc.reset(new cmCustomCommand(0, no_output, no_depends, commands, 0, 0));
pcc.reset(new cmCustomCommand(0, no_output, no_byproducts,
no_depends, commands, 0, 0));
pcc->SetEscapeOldStyle(false);
pcc->SetEscapeAllowMakeVars(true);
return pcc;

View File

@ -880,13 +880,14 @@ void cmMakefile::ConfigureFinalPass()
//----------------------------------------------------------------------------
void
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
bool uses_terminal)
{
// Find the target to which to add the custom command.
cmTargets::iterator ti = this->Targets.find(target);
@ -936,9 +937,20 @@ cmMakefile::AddCustomCommandToTarget(const std::string& target,
this->IssueMessage(cmake::FATAL_ERROR, e.str());
return;
}
// Always create the byproduct sources and mark them generated.
for(std::vector<std::string>::const_iterator o = byproducts.begin();
o != byproducts.end(); ++o)
{
if(cmSourceFile* out = this->GetOrCreateSource(*o, true))
{
out->SetProperty("GENERATED", "1");
}
}
// Add the command to the appropriate build step for the target.
std::vector<std::string> no_output;
cmCustomCommand cc(this, no_output, depends,
cmCustomCommand cc(this, no_output, byproducts, depends,
commandLines, comment, workingDir);
cc.SetEscapeOldStyle(escapeOldStyle);
cc.SetEscapeAllowMakeVars(true);
@ -960,6 +972,7 @@ cmMakefile::AddCustomCommandToTarget(const std::string& target,
//----------------------------------------------------------------------------
cmSourceFile*
cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const std::string& main_dependency,
const cmCustomCommandLines& commandLines,
@ -1058,6 +1071,14 @@ cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs,
out->SetProperty("GENERATED", "1");
}
}
for(std::vector<std::string>::const_iterator o = byproducts.begin();
o != byproducts.end(); ++o)
{
if(cmSourceFile* out = this->GetOrCreateSource(*o, true))
{
out->SetProperty("GENERATED", "1");
}
}
// Attach the custom command to the file.
if(file)
@ -1070,8 +1091,8 @@ cmMakefile::AddCustomCommandToOutput(const std::vector<std::string>& outputs,
}
cmCustomCommand* cc =
new cmCustomCommand(this, outputs, depends2, commandLines,
comment, workingDir);
new cmCustomCommand(this, outputs, byproducts, depends2,
commandLines, comment, workingDir);
cc->SetEscapeOldStyle(escapeOldStyle);
cc->SetEscapeAllowMakeVars(true);
cc->SetUsesTerminal(uses_terminal);
@ -1128,7 +1149,9 @@ cmMakefile::AddCustomCommandToOutput(const std::string& output,
{
std::vector<std::string> outputs;
outputs.push_back(output);
return this->AddCustomCommandToOutput(outputs, depends, main_dependency,
std::vector<std::string> no_byproducts;
return this->AddCustomCommandToOutput(outputs, no_byproducts,
depends, main_dependency,
commandLines, comment, workingDir,
replace, escapeOldStyle,
uses_terminal);
@ -1150,7 +1173,9 @@ cmMakefile::AddCustomCommandOldStyle(const std::string& target,
// In the old-style signature if the source and target were the
// same then it added a post-build rule to the target. Preserve
// this behavior.
this->AddCustomCommandToTarget(target, depends, commandLines,
std::vector<std::string> no_byproducts;
this->AddCustomCommandToTarget(target, no_byproducts,
depends, commandLines,
cmTarget::POST_BUILD, comment, 0);
return;
}
@ -1250,6 +1275,23 @@ cmMakefile::AddUtilityCommand(const std::string& utilityName,
const cmCustomCommandLines& commandLines,
bool escapeOldStyle, const char* comment,
bool uses_terminal)
{
std::vector<std::string> no_byproducts;
return this->AddUtilityCommand(utilityName, excludeFromAll, workingDirectory,
no_byproducts, depends, commandLines,
escapeOldStyle, comment, uses_terminal);
}
//----------------------------------------------------------------------------
cmTarget*
cmMakefile::AddUtilityCommand(const std::string& utilityName,
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)
{
// Create a target instance for this utility.
cmTarget* target = this->AddNewTarget(cmTarget::UTILITY, utilityName);
@ -1270,10 +1312,12 @@ cmMakefile::AddUtilityCommand(const std::string& utilityName,
force += cmake::GetCMakeFilesDirectory();
force += "/";
force += utilityName;
std::vector<std::string> forced;
forced.push_back(force);
std::string no_main_dependency = "";
bool no_replace = false;
this->AddCustomCommandToOutput(force, depends,
no_main_dependency,
this->AddCustomCommandToOutput(forced, byproducts,
depends, no_main_dependency,
commandLines, comment,
workingDirectory, no_replace,
escapeOldStyle, uses_terminal);
@ -1289,6 +1333,16 @@ cmMakefile::AddUtilityCommand(const std::string& utilityName,
cmSystemTools::Error("Could not get source file entry for ",
force.c_str());
}
// Always create the byproduct sources and mark them generated.
for(std::vector<std::string>::const_iterator o = byproducts.begin();
o != byproducts.end(); ++o)
{
if(cmSourceFile* out = this->GetOrCreateSource(*o, true))
{
out->SetProperty("GENERATED", "1");
}
}
}
return target;
}

View File

@ -170,14 +170,16 @@ public:
/** 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) const;
bool uses_terminal = false);
cmSourceFile* AddCustomCommandToOutput(
const std::vector<std::string>& outputs,
const std::vector<std::string>& byproducts,
const std::vector<std::string>& depends,
const std::string& main_dependency,
const cmCustomCommandLines& commandLines,
@ -242,6 +244,15 @@ public:
bool escapeOldStyle = true,
const char* comment = 0,
bool uses_terminal = false);
cmTarget* AddUtilityCommand(const std::string& utilityName,
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 = 0,
bool uses_terminal = false);
/**
* Add a link library to the build.

View File

@ -256,7 +256,7 @@ cmNinjaNormalTargetGenerator
/*deptype*/ "",
rspfile,
rspcontent,
/*restat*/ "",
/*restat*/ "$RESTAT",
/*generator*/ false);
}
@ -556,6 +556,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
&postBuildCmdLines
};
cmNinjaDeps byproducts;
for (unsigned i = 0; i != 3; ++i)
{
for (std::vector<cmCustomCommand>::const_iterator
@ -564,6 +565,9 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
{
cmCustomCommandGenerator ccg(*ci, cfgName, mf);
localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]);
std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
std::transform(ccByproducts.begin(), ccByproducts.end(),
std::back_inserter(byproducts), MapToNinjaPath());
}
}
@ -611,6 +615,17 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
this->GetLocalGenerator()->AppendTargetDepends(this->GetTarget(),
orderOnlyDeps);
// Ninja should restat after linking if and only if there are byproducts.
vars["RESTAT"] = byproducts.empty()? "" : "1";
for (cmNinjaDeps::const_iterator oi = byproducts.begin(),
oe = byproducts.end();
oi != oe; ++oi)
{
this->GetGlobalGenerator()->SeenCustomCommandOutput(*oi);
outputs.push_back(*oi);
}
// Write the build statement for this target.
globalGen.WriteBuild(this->GetBuildFileStream(),
comment.str(),

View File

@ -538,8 +538,11 @@ cmNinjaTargetGenerator
cmCustomCommandGenerator ccg(*cc, this->GetConfigName(),
this->GetMakefile());
const std::vector<std::string>& ccoutputs = ccg.GetOutputs();
const std::vector<std::string>& ccbyproducts= ccg.GetByproducts();
std::transform(ccoutputs.begin(), ccoutputs.end(),
std::back_inserter(orderOnlyDeps), MapToNinjaPath());
std::transform(ccbyproducts.begin(), ccbyproducts.end(),
std::back_inserter(orderOnlyDeps), MapToNinjaPath());
}
if (!orderOnlyDeps.empty())

View File

@ -27,8 +27,11 @@ cmNinjaUtilityTargetGenerator::~cmNinjaUtilityTargetGenerator() {}
void cmNinjaUtilityTargetGenerator::Generate()
{
std::string utilCommandName = cmake::GetCMakeFilesDirectoryPostSlash();
utilCommandName += this->GetTargetName() + ".util";
std::vector<std::string> commands;
cmNinjaDeps deps, outputs;
cmNinjaDeps deps, outputs, util_outputs(1, utilCommandName);
const std::vector<cmCustomCommand> *cmdLists[2] = {
&this->GetTarget()->GetPreBuildCommands(),
@ -44,6 +47,9 @@ void cmNinjaUtilityTargetGenerator::Generate()
this->GetMakefile());
this->GetLocalGenerator()->AppendCustomCommandDeps(ccg, deps);
this->GetLocalGenerator()->AppendCustomCommandLines(ccg, commands);
std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
std::transform(ccByproducts.begin(), ccByproducts.end(),
std::back_inserter(util_outputs), MapToNinjaPath());
if (ci->GetUsesTerminal())
uses_terminal = true;
}
@ -64,8 +70,11 @@ void cmNinjaUtilityTargetGenerator::Generate()
// Depend on all custom command outputs.
const std::vector<std::string>& ccOutputs = ccg.GetOutputs();
const std::vector<std::string>& ccByproducts = ccg.GetByproducts();
std::transform(ccOutputs.begin(), ccOutputs.end(),
std::back_inserter(deps), MapToNinjaPath());
std::transform(ccByproducts.begin(), ccByproducts.end(),
std::back_inserter(deps), MapToNinjaPath());
}
}
@ -107,15 +116,19 @@ void cmNinjaUtilityTargetGenerator::Generate()
if (command.find('$') != std::string::npos)
return;
std::string utilCommandName = cmake::GetCMakeFilesDirectoryPostSlash();
utilCommandName += this->GetTargetName() + ".util";
for (cmNinjaDeps::const_iterator
oi = util_outputs.begin(), oe = util_outputs.end();
oi != oe; ++oi)
{
this->GetGlobalGenerator()->SeenCustomCommandOutput(*oi);
}
this->GetGlobalGenerator()->WriteCustomCommandBuild(
command,
desc,
"Utility command for " + this->GetTargetName(),
uses_terminal,
cmNinjaDeps(1, utilCommandName),
util_outputs,
deps);
this->GetGlobalGenerator()->WritePhonyBuild(this->GetBuildFileStream(),

View File

@ -438,7 +438,8 @@ bool cmQtAutoGenerators::InitializeAutogenTarget(cmTarget* target)
// rejection in cmMakefile::AddCustomCommandToTarget because we know
// PRE_BUILD will work for an OBJECT_LIBRARY in this specific case.
std::vector<std::string> no_output;
cmCustomCommand cc(makefile, no_output, depends,
std::vector<std::string> no_byproducts;
cmCustomCommand cc(makefile, no_output, no_byproducts, depends,
commandLines, autogenComment.c_str(),
workingDirectory.c_str());
cc.SetEscapeOldStyle(false);
@ -451,7 +452,9 @@ bool cmQtAutoGenerators::InitializeAutogenTarget(cmTarget* target)
cmTarget* autogenTarget = 0;
if (!rcc_output.empty())
{
makefile->AddCustomCommandToOutput(rcc_output, depends, "",
std::vector<std::string> no_byproducts;
makefile->AddCustomCommandToOutput(rcc_output, no_byproducts,
depends, "",
commandLines, 0,
workingDirectory.c_str(),
false, false);

View File

@ -716,6 +716,8 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release
)
list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/CustomCommand")
ADD_TEST_MACRO(CustomCommandByproducts CustomCommandByproducts)
ADD_TEST_MACRO(EmptyDepends ${CMAKE_CTEST_COMMAND})
add_test(CustomCommandWorkingDirectory ${CMAKE_CTEST_COMMAND}

View File

@ -117,6 +117,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E echo " Copying doc1pre.txt to doc2post.txt."
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/doc1pre.txt
${PROJECT_BINARY_DIR}/doc2post.txt
BYPRODUCTS ${PROJECT_BINARY_DIR}/doc2post.txt
COMMENT "Running TDocument post-build commands"
)

View File

@ -0,0 +1,127 @@
cmake_minimum_required(VERSION 3.1)
project(CustomCommandByproducts C)
# Generate a byproduct in a rule that runs in the target consuming it.
add_custom_command(
OUTPUT timestamp1.txt
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/byproduct1.c.in byproduct1.c
BYPRODUCTS byproduct1.c
COMMAND ${CMAKE_COMMAND} -E touch timestamp1.txt
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/byproduct1.c.in
)
# Generate a byproduct in a rule that runs in a dependency of the consumer.
add_custom_command(
OUTPUT timestamp2.txt
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/byproduct2.c.in byproduct2.c
BYPRODUCTS byproduct2.c
COMMAND ${CMAKE_COMMAND} -E touch timestamp2.txt
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/byproduct2.c.in
)
add_custom_target(Producer2 DEPENDS timestamp2.txt)
# Generate a byproduct in a custom target.
add_custom_target(Producer3_4
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/byproduct3.c.in byproduct3.c
BYPRODUCTS byproduct3.c
)
# Generate a byproduct in a custom target POST_BUILD command.
add_custom_command(
TARGET Producer3_4 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/byproduct4.c.in byproduct4.c
BYPRODUCTS byproduct4.c
)
add_executable(ProducerExe ProducerExe.c)
# Generate a byproduct in an executable POST_BUILD command.
add_custom_command(
TARGET ProducerExe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/byproduct5.c.in byproduct5.c
BYPRODUCTS byproduct5.c
)
# Generate a byproduct in an executable PRE_LINK command.
add_custom_command(
TARGET ProducerExe PRE_LINK
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/byproduct6.c.in byproduct6.c
BYPRODUCTS byproduct6.c
)
# Generate a byproduct in an executable PRE_BUILD command.
add_custom_command(
TARGET ProducerExe PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/byproduct7.c.in byproduct7.c
BYPRODUCTS byproduct7.c
)
# Generate a byproduct in a custom command that consumes other byproducts.
add_custom_command(OUTPUT timestamp8.txt
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/byproduct8.c.in byproduct8.c
COMMAND ${CMAKE_COMMAND} -E touch timestamp8.txt
BYPRODUCTS byproduct8.c
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/byproduct2.c
${CMAKE_CURRENT_BINARY_DIR}/byproduct3.c
${CMAKE_CURRENT_BINARY_DIR}/byproduct4.c
${CMAKE_CURRENT_BINARY_DIR}/byproduct5.c
${CMAKE_CURRENT_BINARY_DIR}/byproduct6.c
${CMAKE_CURRENT_BINARY_DIR}/byproduct7.c
${CMAKE_CURRENT_SOURCE_DIR}/byproduct8.c.in
)
# Generate the library file of an imported target as a byproduct
# of an external project.
if(CMAKE_CONFIGURATION_TYPES)
set(cfg /${CMAKE_CFG_INTDIR})
else()
set(cfg)
endif()
set(ExternalLibrary_LIBRARY
${CMAKE_CURRENT_BINARY_DIR}/External-build${cfg}/${CMAKE_STATIC_LIBRARY_PREFIX}ExternalLibrary${CMAKE_STATIC_LIBRARY_SUFFIX}
)
include(ExternalProject)
ExternalProject_Add(ExternalTarget
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/External"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/External-build"
PREFIX "${CMAKE_CURRENT_BINARY_DIR}/External-build/root"
DOWNLOAD_COMMAND ""
INSTALL_COMMAND ""
BUILD_BYPRODUCTS "${ExternalLibrary_LIBRARY}"
)
add_library(ExternalLibrary STATIC IMPORTED)
set_property(TARGET ExternalLibrary PROPERTY IMPORTED_LOCATION ${ExternalLibrary_LIBRARY})
add_dependencies(ExternalLibrary ExternalTarget)
# Add an executable consuming all the byproducts.
add_executable(CustomCommandByproducts
CustomCommandByproducts.c
byproduct1.c timestamp1.txt
byproduct2.c
byproduct3.c
byproduct4.c
byproduct5.c
byproduct6.c
byproduct7.c
byproduct8.c timestamp8.txt
)
add_dependencies(CustomCommandByproducts Producer2)
add_dependencies(CustomCommandByproducts Producer3_4)
add_dependencies(CustomCommandByproducts ProducerExe)
target_link_libraries(CustomCommandByproducts ExternalLibrary)
if(CMAKE_GENERATOR STREQUAL "Ninja")
add_custom_target(CheckNinja ALL
COMMENT "Checking build.ninja"
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/ninja-check.cmake
)
endif()

View File

@ -0,0 +1,23 @@
extern int byproduct1(void);
extern int byproduct2(void);
extern int byproduct3(void);
extern int byproduct4(void);
extern int byproduct5(void);
extern int byproduct6(void);
extern int byproduct7(void);
extern int byproduct8(void);
extern int ExternalLibrary(void);
int main(void)
{
return (
byproduct1() +
byproduct2() +
byproduct3() +
byproduct4() +
byproduct5() +
byproduct6() +
byproduct7() +
byproduct8() +
ExternalLibrary() +
0);
}

View File

@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.1)
project(External C)
add_library(ExternalLibrary STATIC ExternalLibrary.c)

View File

@ -0,0 +1 @@
int ExternalLibrary(void) { return 0; }

View File

@ -0,0 +1 @@
int main(void) { return 0; }

View File

@ -0,0 +1 @@
int byproduct1(void) { return 0; }

View File

@ -0,0 +1 @@
int byproduct2(void) { return 0; }

View File

@ -0,0 +1 @@
int byproduct3(void) { return 0; }

View File

@ -0,0 +1 @@
int byproduct4(void) { return 0; }

View File

@ -0,0 +1 @@
int byproduct5(void) { return 0; }

View File

@ -0,0 +1 @@
int byproduct6(void) { return 0; }

View File

@ -0,0 +1 @@
int byproduct7(void) { return 0; }

View File

@ -0,0 +1 @@
int byproduct8(void) { return 0; }

View File

@ -0,0 +1,20 @@
file(READ build.ninja build_ninja)
if("${build_ninja}" MATCHES [====[
# Unknown Build Time Dependencies.
# Tell Ninja that they may appear as side effects of build rules
# otherwise ordered by order-only dependencies.
((build [^:]*: phony [^\n]*
)*)# ========]====])
set(phony "${CMAKE_MATCH_1}")
if(NOT phony)
message(STATUS "build.ninja correctly does not have extra phony rules")
else()
string(REGEX REPLACE "\n+$" "" phony "${phony}")
string(REGEX REPLACE "\n" "\n " phony " ${phony}")
message(FATAL_ERROR "build.ninja incorrectly has extra phony rules:\n"
"${phony}")
endif()
else()
message(FATAL_ERROR "build.ninja is incorrectly missing expected block")
endif()

View File

@ -6,4 +6,5 @@ run_cmake(BadArgument)
run_cmake(NoArguments)
run_cmake(NoOutputOrTarget)
run_cmake(OutputAndTarget)
run_cmake(SourceByproducts)
run_cmake(SourceUsesTerminal)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at SourceByproducts.cmake:1 \(add_custom_command\):
add_custom_command BYPRODUCTS may not be specified with SOURCE signatures
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1 @@
add_custom_command(SOURCE t TARGET t BYPRODUCTS b)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at ByproductsNoCommand.cmake:1 \(add_custom_target\):
BYPRODUCTS may not be specified without any COMMAND
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1 @@
add_custom_target(MyTarget BYPRODUCTS a b c d)

View File

@ -2,4 +2,5 @@ include(RunCMake)
run_cmake(NoArguments)
run_cmake(BadTargetName)
run_cmake(ByproductsNoCommand)
run_cmake(UsesTerminalNoCommand)