Makefile: Fix multiple custom command outputs regression (#15116)
In commit v3.2.0-rc1~272^2~2 (Makefile: Fix rebuild with multiple custom command outputs, 2014-12-05) we changed the generated makefile pattern for multiple outputs from out1: depends... commands... out2: out1 to out1 out2: depends... commands... This was based on the incorrect assumption that make tools would treat this as a combined output rule and run the command(s) exactly once for them. It turns out that instead this new pattern is equivalent to out1: depends... commands... out2: depends... commands... so the commands may be run more than once. Some documents suggest using a "dedicated witness" stamp file: stamp: depends... rm -f stamp touch stamp.tmp commands... mv stamp.tmp stamp out1 out2: stamp However, if the commands fail the error message will refer to the stamp instead of any of the real outputs, which may be confusing to readers. Also, this approach seems to have the same behavior of the original approach that motiviated the above commit: multiple invocations are needed to bring consumers of the outputs up to date. Instead we can return to the original approach but add an explicit touch to each extra output rule: out1: depends... commands... out2: out1 touch -c out2 This causes make tools to recognize that all outputs have changed and therefore to execute any commands that consume them.
This commit is contained in:
parent
eb3bced50f
commit
66a9c90c4b
|
@ -49,7 +49,6 @@ cmLocalGenerator *cmGlobalBorlandMakefileGenerator::CreateLocalGenerator()
|
|||
lg->SetUnixCD(false);
|
||||
lg->SetMakeCommandEscapeTargetTwice(true);
|
||||
lg->SetBorlandMakeCurlyHack(true);
|
||||
lg->SetNoMultiOutputMultiDepRules(true);
|
||||
return lg;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,6 @@ cmLocalUnixMakefileGenerator3::cmLocalUnixMakefileGenerator3()
|
|||
this->SkipAssemblySourceRules = false;
|
||||
this->MakeCommandEscapeTargetTwice = false;
|
||||
this->BorlandMakeCurlyHack = false;
|
||||
this->NoMultiOutputMultiDepRules = false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -600,7 +599,6 @@ const std::string &cmLocalUnixMakefileGenerator3::GetHomeRelativeOutputPath()
|
|||
return this->HomeRelativeOutputPath;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void
|
||||
cmLocalUnixMakefileGenerator3
|
||||
|
@ -619,30 +617,6 @@ cmLocalUnixMakefileGenerator3
|
|||
comment);
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> outputs(1, target);
|
||||
this->WriteMakeRule(os, comment,
|
||||
outputs, depends, commands,
|
||||
symbolic, in_help);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void
|
||||
cmLocalUnixMakefileGenerator3
|
||||
::WriteMakeRule(std::ostream& os,
|
||||
const char* comment,
|
||||
const std::vector<std::string>& outputs,
|
||||
const std::vector<std::string>& depends,
|
||||
const std::vector<std::string>& commands,
|
||||
bool symbolic,
|
||||
bool in_help)
|
||||
{
|
||||
// Make sure there is an output.
|
||||
if(outputs.empty())
|
||||
{
|
||||
cmSystemTools::Error("No outputs for WriteMakeRule! called with comment: ",
|
||||
comment);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string replace;
|
||||
|
||||
|
@ -661,17 +635,7 @@ cmLocalUnixMakefileGenerator3
|
|||
}
|
||||
|
||||
// Construct the left hand side of the rule.
|
||||
std::string tgt;
|
||||
{
|
||||
const char* sep = "";
|
||||
for (std::vector<std::string>::const_iterator i = outputs.begin();
|
||||
i != outputs.end(); ++i)
|
||||
{
|
||||
tgt += sep;
|
||||
tgt += this->Convert(*i,HOME_OUTPUT,MAKERULE);
|
||||
sep = " ";
|
||||
}
|
||||
}
|
||||
std::string tgt = this->Convert(target, HOME_OUTPUT, MAKERULE);
|
||||
|
||||
const char* space = "";
|
||||
if(tgt.size() == 1)
|
||||
|
@ -697,19 +661,6 @@ cmLocalUnixMakefileGenerator3
|
|||
// No dependencies. The commands will always run.
|
||||
os << cmMakeSafe(tgt) << space << ":\n";
|
||||
}
|
||||
else if(this->NoMultiOutputMultiDepRules && outputs.size() >= 2)
|
||||
{
|
||||
// Borland make does not understand multiple dependency rules when
|
||||
// there are multiple outputs, so write them all on one line.
|
||||
os << cmMakeSafe(tgt) << space << ":";
|
||||
for(std::vector<std::string>::const_iterator dep = depends.begin();
|
||||
dep != depends.end(); ++dep)
|
||||
{
|
||||
replace = this->Convert(*dep, HOME_OUTPUT, MAKERULE);
|
||||
os << " " << cmMakeSafe(replace);
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Split dependencies into multiple rule lines. This allows for
|
||||
|
@ -738,8 +689,7 @@ cmLocalUnixMakefileGenerator3
|
|||
// Add the output to the local help if requested.
|
||||
if(in_help)
|
||||
{
|
||||
this->LocalHelp.insert(this->LocalHelp.end(),
|
||||
outputs.begin(), outputs.end());
|
||||
this->LocalHelp.push_back(target);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1758,8 +1708,6 @@ cmLocalUnixMakefileGenerator3
|
|||
//----------------------------------------------------------------------------
|
||||
void cmLocalUnixMakefileGenerator3::CheckMultipleOutputs(bool verbose)
|
||||
{
|
||||
// Nothing populates multiple output pairs anymore, but we need to
|
||||
// honor it when working in a build tree generated by an older CMake.
|
||||
cmMakefile* mf = this->Makefile;
|
||||
|
||||
// Get the string listing the multiple output pairs.
|
||||
|
|
|
@ -61,13 +61,6 @@ public:
|
|||
const std::vector<std::string>& commands,
|
||||
bool symbolic,
|
||||
bool in_help = false);
|
||||
void WriteMakeRule(std::ostream& os,
|
||||
const char* comment,
|
||||
const std::vector<std::string>& outputs,
|
||||
const std::vector<std::string>& depends,
|
||||
const std::vector<std::string>& commands,
|
||||
bool symbolic,
|
||||
bool in_help = false);
|
||||
|
||||
// write the main variables used by the makefiles
|
||||
void WriteMakeVariables(std::ostream& makefileStream);
|
||||
|
@ -161,9 +154,6 @@ public:
|
|||
void SetBorlandMakeCurlyHack(bool b)
|
||||
{ this->BorlandMakeCurlyHack = b; }
|
||||
|
||||
void SetNoMultiOutputMultiDepRules(bool b)
|
||||
{ this->NoMultiOutputMultiDepRules = b; }
|
||||
|
||||
// used in writing out Cmake files such as WriteDirectoryInformation
|
||||
static void WriteCMakeArgument(std::ostream& os, const char* s);
|
||||
|
||||
|
@ -348,7 +338,6 @@ private:
|
|||
bool PassMakeflags;
|
||||
bool MakeCommandEscapeTargetTwice;
|
||||
bool BorlandMakeCurlyHack;
|
||||
bool NoMultiOutputMultiDepRules;
|
||||
//==========================================================================
|
||||
|
||||
std::string HomeRelativeOutputPath;
|
||||
|
|
|
@ -765,9 +765,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
|
|||
}
|
||||
|
||||
// Write the build rule.
|
||||
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
||||
outputs, depends, commands, false);
|
||||
|
||||
this->WriteMakeRule(*this->BuildFileStream, 0, outputs,
|
||||
depends, commands, false);
|
||||
|
||||
// Write the main driver rule to build everything in this target.
|
||||
this->WriteTargetDriverRule(targetFullPath, relink);
|
||||
|
|
|
@ -766,8 +766,8 @@ cmMakefileTargetGenerator
|
|||
}
|
||||
|
||||
// Write the rule.
|
||||
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
||||
outputs, depends, commands, false);
|
||||
this->WriteMakeRule(*this->BuildFileStream, 0, outputs,
|
||||
depends, commands, false);
|
||||
|
||||
bool do_preprocess_rules = lang_has_preprocessor &&
|
||||
this->LocalGenerator->GetCreatePreprocessedSourceRules();
|
||||
|
@ -988,6 +988,57 @@ void cmMakefileTargetGenerator::WriteTargetCleanRules()
|
|||
depends, commands, true);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void cmMakefileTargetGenerator::WriteMakeRule(
|
||||
std::ostream& os,
|
||||
const char* comment,
|
||||
const std::vector<std::string>& outputs,
|
||||
const std::vector<std::string>& depends,
|
||||
const std::vector<std::string>& commands,
|
||||
bool symbolic,
|
||||
bool in_help)
|
||||
{
|
||||
if (outputs.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We always attach the actual commands to the first output.
|
||||
this->LocalGenerator->WriteMakeRule(os, comment, outputs[0], depends,
|
||||
commands, symbolic, in_help);
|
||||
|
||||
// For single outputs, we are done.
|
||||
if (outputs.size() == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// For multiple outputs, make the extra ones depend on the first one.
|
||||
std::vector<std::string> const output_depends(1, outputs[0]);
|
||||
for (std::vector<std::string>::const_iterator o = outputs.begin()+1;
|
||||
o != outputs.end(); ++o)
|
||||
{
|
||||
// Touch the extra output so "make" knows that it was updated,
|
||||
// but only if the output was acually created.
|
||||
std::string const out = this->Convert(*o, cmLocalGenerator::HOME_OUTPUT,
|
||||
cmLocalGenerator::SHELL);
|
||||
std::vector<std::string> output_commands;
|
||||
if (!symbolic)
|
||||
{
|
||||
output_commands.push_back("@$(CMAKE_COMMAND) -E touch_nocreate " + out);
|
||||
}
|
||||
this->LocalGenerator->WriteMakeRule(os, 0, *o, output_depends,
|
||||
output_commands, symbolic, in_help);
|
||||
|
||||
if (!symbolic)
|
||||
{
|
||||
// At build time, remove the first output if this one does not exist
|
||||
// so that "make" will rerun the real commands that create this one.
|
||||
MultipleOutputPairsType::value_type p(*o, outputs[0]);
|
||||
this->MultipleOutputPairs.insert(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void cmMakefileTargetGenerator::WriteTargetDependRules()
|
||||
|
@ -1008,6 +1059,25 @@ void cmMakefileTargetGenerator::WriteTargetDependRules()
|
|||
this->LocalGenerator->
|
||||
WriteDependLanguageInfo(*this->InfoFileStream,*this->Target);
|
||||
|
||||
// Store multiple output pairs in the depend info file.
|
||||
if(!this->MultipleOutputPairs.empty())
|
||||
{
|
||||
*this->InfoFileStream
|
||||
<< "\n"
|
||||
<< "# Pairs of files generated by the same build rule.\n"
|
||||
<< "set(CMAKE_MULTIPLE_OUTPUT_PAIRS\n";
|
||||
for(MultipleOutputPairsType::const_iterator pi =
|
||||
this->MultipleOutputPairs.begin();
|
||||
pi != this->MultipleOutputPairs.end(); ++pi)
|
||||
{
|
||||
*this->InfoFileStream
|
||||
<< " " << this->LocalGenerator->EscapeForCMake(pi->first)
|
||||
<< " " << this->LocalGenerator->EscapeForCMake(pi->second)
|
||||
<< "\n";
|
||||
}
|
||||
*this->InfoFileStream << " )\n\n";
|
||||
}
|
||||
|
||||
// Store list of targets linked directly or transitively.
|
||||
{
|
||||
*this->InfoFileStream
|
||||
|
@ -1234,9 +1304,8 @@ void cmMakefileTargetGenerator
|
|||
symbolic = sf->GetPropertyAsBool("SYMBOLIC");
|
||||
}
|
||||
}
|
||||
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0,
|
||||
outputs, depends, commands,
|
||||
symbolic);
|
||||
this->WriteMakeRule(*this->BuildFileStream, 0, outputs,
|
||||
depends, commands, symbolic);
|
||||
|
||||
// If the rule has changed make sure the output is rebuilt.
|
||||
if(!symbolic)
|
||||
|
|
|
@ -222,6 +222,16 @@ protected:
|
|||
// Set of extra output files to be driven by the build.
|
||||
std::set<std::string> ExtraFiles;
|
||||
|
||||
typedef std::map<std::string, std::string> MultipleOutputPairsType;
|
||||
MultipleOutputPairsType MultipleOutputPairs;
|
||||
void WriteMakeRule(std::ostream& os,
|
||||
const char* comment,
|
||||
const std::vector<std::string>& outputs,
|
||||
const std::vector<std::string>& depends,
|
||||
const std::vector<std::string>& commands,
|
||||
bool symbolic,
|
||||
bool in_help = false);
|
||||
|
||||
// Target name info.
|
||||
std::string TargetNameOut;
|
||||
std::string TargetNameSO;
|
||||
|
|
Loading…
Reference in New Issue