summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2014-11-13 23:54:52 (GMT)
committerBrad King <brad.king@kitware.com>2014-11-14 21:16:00 (GMT)
commite15a7075b58aef6fe7b6eb56f810d0f33bc31feb (patch)
treeffd22a2e663b4d0c0511e90f0718dce2cda51356 /Source
parent67bd514adce689f4c7f537cdc446c01f7373f5a5 (diff)
downloadCMake-e15a7075b58aef6fe7b6eb56f810d0f33bc31feb.zip
CMake-e15a7075b58aef6fe7b6eb56f810d0f33bc31feb.tar.gz
CMake-e15a7075b58aef6fe7b6eb56f810d0f33bc31feb.tar.bz2
Add an option for explicit BYPRODUCTS of custom commands (#14963)
A common idiom in CMake-based build systems is to have custom commands that generate files not listed explicitly as outputs so that these files do not have to be newer than the inputs. The file modification times of such "byproducts" are updated only when their content changes. Then other build rules can depend on the byproducts explicitly so that their dependents rebuild when the content of the original byproducts really does change. This "undeclared byproduct" approach is necessary for Makefile, VS, and Xcode build tools because if a byproduct were listed as an output of a rule then the rule would always rerun when the input is newer than the byproduct but the byproduct may never be updated. Ninja solves this problem by offering a 'restat' feature to check whether an output was really modified after running a rule and tracking the fact that it is up to date separately from its timestamp. However, Ninja also stats all dependencies up front and will only restat files that are listed as outputs of rules with the 'restat' option enabled. Therefore an undeclared byproduct that does not exist at the start of the build will be considered missing and the build will fail even if other dependencies would cause the byproduct to be available before its dependents build. CMake works around this limitation by adding 'phony' build rules for custom command dependencies in the build tree that do not have any explicit specification of what produces them. This is not optimal because it prevents Ninja from reporting an error when an input to a rule really is missing. A better approach is to allow projects to explicitly specify the byproducts of their custom commands so that no phony rules are needed for them. In order to work with the non-Ninja generators, the byproducts must be known separately from the outputs. Add a new "BYPRODUCTS" option to the add_custom_command and add_custom_target commands to specify byproducts explicitly. Teach the Ninja generator to specify byproducts as outputs of the custom commands. In the case of POST_BUILD, PRE_LINK, and PRE_BUILD events on targets that link, the byproducts must be specified as outputs of the link rule that runs the commands. Activate 'restat' for such rules so that Ninja knows it needs to check the byproducts, but not for link rules that have no byproducts.
Diffstat (limited to 'Source')
-rw-r--r--Source/cmAddCustomCommandCommand.cxx26
-rw-r--r--Source/cmAddCustomTargetCommand.cxx29
-rw-r--r--Source/cmCPluginAPI.cxx3
-rw-r--r--Source/cmCustomCommand.cxx10
-rw-r--r--Source/cmCustomCommand.h5
-rw-r--r--Source/cmCustomCommandGenerator.cxx6
-rw-r--r--Source/cmCustomCommandGenerator.h1
-rw-r--r--Source/cmGlobalGenerator.cxx5
-rw-r--r--Source/cmGlobalVisualStudio8Generator.cxx3
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx3
-rw-r--r--Source/cmLocalNinjaGenerator.cxx10
-rw-r--r--Source/cmLocalVisualStudio6Generator.cxx4
-rw-r--r--Source/cmLocalVisualStudioGenerator.cxx4
-rw-r--r--Source/cmMakefile.cxx70
-rw-r--r--Source/cmMakefile.h13
-rw-r--r--Source/cmNinjaNormalTargetGenerator.cxx17
-rw-r--r--Source/cmNinjaTargetGenerator.cxx3
-rw-r--r--Source/cmNinjaUtilityTargetGenerator.cxx21
-rw-r--r--Source/cmQtAutoGenerators.cxx7
19 files changed, 210 insertions, 30 deletions
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index 410f978..818b910 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -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");
diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx
index fc4f8f1..09c8af5 100644
--- a/Source/cmAddCustomTargetCommand.cxx
+++ b/Source/cmAddCustomTargetCommand.cxx
@@ -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);
diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx
index dd2a1b8..b304f28 100644
--- a/Source/cmCPluginAPI.cxx
+++ b/Source/cmCPluginAPI.cxx
@@ -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);
}
diff --git a/Source/cmCustomCommand.cxx b/Source/cmCustomCommand.cxx
index 45369cc..2afb029 100644
--- a/Source/cmCustomCommand.cxx
+++ b/Source/cmCustomCommand.cxx
@@ -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),
@@ -100,6 +104,12 @@ const std::vector<std::string>& cmCustomCommand::GetOutputs() const
}
//----------------------------------------------------------------------------
+const std::vector<std::string>& cmCustomCommand::GetByproducts() const
+{
+ return this->Byproducts;
+}
+
+//----------------------------------------------------------------------------
const std::vector<std::string>& cmCustomCommand::GetDepends() const
{
return this->Depends;
diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h
index 283a0e4..0bfaef2 100644
--- a/Source/cmCustomCommand.h
+++ b/Source/cmCustomCommand.h
@@ -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;
diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx
index 1bca6e6..162d7a1 100644
--- a/Source/cmCustomCommandGenerator.cxx
+++ b/Source/cmCustomCommandGenerator.cxx
@@ -91,6 +91,12 @@ std::vector<std::string> const& cmCustomCommandGenerator::GetOutputs() const
}
//----------------------------------------------------------------------------
+std::vector<std::string> const& cmCustomCommandGenerator::GetByproducts() const
+{
+ return this->CC.GetByproducts();
+}
+
+//----------------------------------------------------------------------------
std::vector<std::string> const& cmCustomCommandGenerator::GetDepends() const
{
if (!this->DependsDone)
diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h
index 0d8a0a4..b4ae014 100644
--- a/Source/cmCustomCommandGenerator.h
+++ b/Source/cmCustomCommandGenerator.h
@@ -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;
};
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 4d49fe3..b9c9b3b 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -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);
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx
index 745515b..e6ce45d 100644
--- a/Source/cmGlobalVisualStudio8Generator.cxx
+++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -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))
{
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 5e7a898..b9f64e2 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -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,
@@ -1368,6 +1370,7 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(cmXCodeObject* buildPhases,
cmCustomCommand command(this->CurrentMakefile,
std::vector<std::string>(),
std::vector<std::string>(),
+ std::vector<std::string>(),
cmd,
"Creating symlinks",
"");
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index 3c39b62..0b0d971 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -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();
diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx
index c14fb2b..b9a5074 100644
--- a/Source/cmLocalVisualStudio6Generator.cxx
+++ b/Source/cmLocalVisualStudio6Generator.cxx
@@ -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;
diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx
index 9680d43..f01aa6b 100644
--- a/Source/cmLocalVisualStudioGenerator.cxx
+++ b/Source/cmLocalVisualStudioGenerator.cxx
@@ -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;
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 7e5e4e7..61807b2 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -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;
}
@@ -1251,6 +1276,23 @@ cmMakefile::AddUtilityCommand(const std::string& utilityName,
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);
if (excludeFromAll)
@@ -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;
}
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 73c299e..0458d54 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -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.
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 48c4a2d..25931f3 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -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(),
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 57fbcd3..7967762 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -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())
diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx
index c0a14ec..42d6b46 100644
--- a/Source/cmNinjaUtilityTargetGenerator.cxx
+++ b/Source/cmNinjaUtilityTargetGenerator.cxx
@@ -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(),
diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx
index 929cbc0..8f9c091 100644
--- a/Source/cmQtAutoGenerators.cxx
+++ b/Source/cmQtAutoGenerators.cxx
@@ -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);