ENH: Add a depends check step to custom targets. Add support for the IMPLICIT_DEPENDS feature of custom commands when building in custom targets. Convert multiple-output pair checks to be per-target instead of global.

This commit is contained in:
Brad King 2007-12-21 12:22:12 -05:00
parent 6586149d64
commit d83b4cd255
16 changed files with 161 additions and 153 deletions

View File

@ -1680,13 +1680,6 @@ void cmGlobalGenerator::AppendDirectoryForConfig(const char*, const char*,
// configuration.
}
//----------------------------------------------------------------------------
void cmGlobalGenerator::CheckMultipleOutputs(cmMakefile*, bool)
{
// Only certain generators need this check. They define this
// method.
}
//----------------------------------------------------------------------------
std::vector<cmTarget *>& cmGlobalGenerator
::GetTargetDepends(cmTarget& target)

View File

@ -211,9 +211,6 @@ public:
void AddTarget(cmTargets::value_type &v);
/** Support for multiple custom command outputs. */
virtual void CheckMultipleOutputs(cmMakefile* mf, bool verbose);
virtual const char* GetAllTargetName() { return "ALL_BUILD"; }
virtual const char* GetInstallTargetName() { return "INSTALL"; }
virtual const char* GetInstallLocalTargetName() { return 0; }

View File

@ -132,16 +132,6 @@ void cmGlobalUnixMakefileGenerator3
"default make target. A \"make install\" target is also provided.";
}
//----------------------------------------------------------------------------
void
cmGlobalUnixMakefileGenerator3
::AddMultipleOutputPair(const char* depender, const char* dependee)
{
MultipleOutputPairsType::value_type p(depender, dependee);
this->MultipleOutputPairs.insert(p);
}
//----------------------------------------------------------------------------
void cmGlobalUnixMakefileGenerator3::Generate()
{
@ -373,62 +363,6 @@ void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile()
this->WriteMainCMakefileLanguageRules(cmakefileStream,
this->LocalGenerators);
if(!this->MultipleOutputPairs.empty())
{
cmakefileStream
<< "\n"
<< "SET(CMAKE_MULTIPLE_OUTPUT_PAIRS\n";
for(MultipleOutputPairsType::const_iterator pi =
this->MultipleOutputPairs.begin();
pi != this->MultipleOutputPairs.end(); ++pi)
{
cmakefileStream << " \"" << pi->first << "\" \""
<< pi->second << "\"\n";
}
cmakefileStream << " )\n\n";
}
}
//----------------------------------------------------------------------------
void cmGlobalUnixMakefileGenerator3::CheckMultipleOutputs(cmMakefile* mf,
bool verbose)
{
// Get the string listing the multiple output pairs.
const char* pairs_string = mf->GetDefinition("CMAKE_MULTIPLE_OUTPUT_PAIRS");
if(!pairs_string)
{
return;
}
// Convert the string to a list and preserve empty entries.
std::vector<std::string> pairs;
cmSystemTools::ExpandListArgument(pairs_string, pairs, true);
for(std::vector<std::string>::const_iterator i = pairs.begin();
i != pairs.end(); ++i)
{
const std::string& depender = *i;
if(++i != pairs.end())
{
const std::string& dependee = *i;
// If the depender is missing then delete the dependee to make
// sure both will be regenerated.
if(cmSystemTools::FileExists(dependee.c_str()) &&
!cmSystemTools::FileExists(depender.c_str()))
{
if(verbose)
{
cmOStringStream msg;
msg << "Deleting primary custom command output \"" << dependee
<< "\" because another output \""
<< depender << "\" does not exist." << std::endl;
cmSystemTools::Stdout(msg.str().c_str());
}
cmSystemTools::RemoveFile(dependee.c_str());
}
}
}
}
void cmGlobalUnixMakefileGenerator3
@ -763,21 +697,18 @@ cmGlobalUnixMakefileGenerator3
<< localName << "\n\n";
commands.clear();
if (t->second.GetType() != cmTarget::UTILITY)
makeTargetName = localName;
makeTargetName += "/depend";
commands.push_back(lg->GetRecursiveMakeCall
(makefileName.c_str(),makeTargetName.c_str()));
// add requires if we need it for this generator
if (needRequiresStep)
{
makeTargetName = localName;
makeTargetName += "/depend";
makeTargetName += "/requires";
commands.push_back(lg->GetRecursiveMakeCall
(makefileName.c_str(),makeTargetName.c_str()));
// add requires if we need it for this generator
if (needRequiresStep)
{
makeTargetName = localName;
makeTargetName += "/requires";
commands.push_back(lg->GetRecursiveMakeCall
(makefileName.c_str(),makeTargetName.c_str()));
}
(makefileName.c_str(),makeTargetName.c_str()));
}
makeTargetName = localName;
makeTargetName += "/build";

View File

@ -98,19 +98,6 @@ public:
void WriteConvenienceRules(std::ostream& ruleFileStream,
std::set<cmStdString> &emitted);
/** In order to support parallel builds for custom commands with
multiple outputs the outputs are given a serial order, and only
the first output actually has the build rule. Other outputs
just depend on the first one. The check-build-system step must
remove a dependee if the depender is missing to make sure both
are regenerated properly. This method is used by the local
makefile generators to register such pairs. */
void AddMultipleOutputPair(const char* depender, const char* dependee);
/** Support for multiple custom command outputs. Called during
check-build-system step. */
virtual void CheckMultipleOutputs(cmMakefile* mf, bool verbose);
/** Get the command to use for a target that has no rule. This is
used for multiple output dependencies and for cmake_force. */
std::string GetEmptyRuleHackCommand() { return this->EmptyRuleHackCommand; }
@ -191,9 +178,6 @@ protected:
// in the rule to satisfy the make program.
std::string EmptyRuleHackCommand;
typedef std::map<cmStdString, cmStdString> MultipleOutputPairsType;
MultipleOutputPairsType MultipleOutputPairs;
std::map<cmStdString, int > TargetSourceFileCount;
bool ForceVerboseMakefiles;
};

View File

@ -1229,6 +1229,16 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(const char* tgtInfo,
bool verbose,
bool color)
{
// read in the target info file
if(!this->Makefile->ReadListFile(0, tgtInfo) ||
cmSystemTools::GetErrorOccuredFlag())
{
cmSystemTools::Error("Target DependInfo.cmake file not found");
}
// Check if any multiple output pairs have a missing file.
this->CheckMultipleOutputs(verbose);
std::string dir = cmSystemTools::GetFilenamePath(tgtInfo);
std::string internalDependFile = dir + "/depend.internal";
std::string dependFile = dir + "/depend.make";
@ -1257,7 +1267,7 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(const char* tgtInfo,
fprintf(stdout, "%s\n", message.c_str());
#endif
return this->ScanDependencies(tgtInfo);
return this->ScanDependencies(dir.c_str());
}
else
{
@ -1267,11 +1277,10 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(const char* tgtInfo,
}
//----------------------------------------------------------------------------
bool cmLocalUnixMakefileGenerator3::ScanDependencies(const char* tgtInfo)
bool
cmLocalUnixMakefileGenerator3
::ScanDependencies(const char* targetDir)
{
// The info file for this target
std::string infoFile = tgtInfo;
// Read the directory information file.
cmMakefile* mf = this->Makefile;
bool haveDirectoryInfo = false;
@ -1284,13 +1293,6 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies(const char* tgtInfo)
haveDirectoryInfo = true;
}
// read in the target info file
if(!mf->ReadListFile(0, infoFile.c_str()) ||
cmSystemTools::GetErrorOccuredFlag())
{
cmSystemTools::Error("Target DependInfo.cmake file not found");
}
// Lookup useful directory information.
if(haveDirectoryInfo)
{
@ -1322,7 +1324,7 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies(const char* tgtInfo)
}
// create the file stream for the depends file
std::string dir = cmSystemTools::GetFilenamePath(infoFile);
std::string dir = targetDir;
// Open the rule file. This should be copy-if-different because the
// rules may depend on this file itself.
@ -1449,6 +1451,48 @@ bool cmLocalUnixMakefileGenerator3::ScanDependencies(const char* tgtInfo)
return true;
}
//----------------------------------------------------------------------------
void cmLocalUnixMakefileGenerator3::CheckMultipleOutputs(bool verbose)
{
cmMakefile* mf = this->Makefile;
// Get the string listing the multiple output pairs.
const char* pairs_string = mf->GetDefinition("CMAKE_MULTIPLE_OUTPUT_PAIRS");
if(!pairs_string)
{
return;
}
// Convert the string to a list and preserve empty entries.
std::vector<std::string> pairs;
cmSystemTools::ExpandListArgument(pairs_string, pairs, true);
for(std::vector<std::string>::const_iterator i = pairs.begin();
i != pairs.end(); ++i)
{
const std::string& depender = *i;
if(++i != pairs.end())
{
const std::string& dependee = *i;
// If the depender is missing then delete the dependee to make
// sure both will be regenerated.
if(cmSystemTools::FileExists(dependee.c_str()) &&
!cmSystemTools::FileExists(depender.c_str()))
{
if(verbose)
{
cmOStringStream msg;
msg << "Deleting primary custom command output \"" << dependee
<< "\" because another output \""
<< depender << "\" does not exist." << std::endl;
cmSystemTools::Stdout(msg.str().c_str());
}
cmSystemTools::RemoveFile(dependee.c_str());
}
}
}
}
//----------------------------------------------------------------------------
void cmLocalUnixMakefileGenerator3
::WriteLocalAllRules(std::ostream& ruleFileStream)

View File

@ -203,9 +203,6 @@ public:
virtual bool UpdateDependencies(const char* tgtInfo,
bool verbose, bool color);
/** Called from command-line hook to scan dependencies. */
bool ScanDependencies(const char* tgtInfo);
/** Called from command-line hook to clear dependencies. */
virtual void ClearDependencies(cmMakefile* mf, bool verbose);
@ -325,6 +322,10 @@ protected:
std::map<cmStdString, std::vector<int> > ProgressFiles;
// Helper methods for dependeny updates.
bool ScanDependencies(const char* targetDir);
void CheckMultipleOutputs(bool verbose);
private:
friend class cmMakefileTargetGenerator;
friend class cmMakefileExecutableTargetGenerator;

View File

@ -45,9 +45,6 @@ void cmMakefileExecutableTargetGenerator::WriteRuleFiles()
// write the per-target per-language flags
this->WriteTargetLanguageFlags();
// Write the dependency generation rule.
this->WriteTargetDependRules();
// write the link rules
this->WriteExecutableRule(false);
if(this->Target->NeedRelinkBeforeInstall())
@ -62,6 +59,10 @@ void cmMakefileExecutableTargetGenerator::WriteRuleFiles()
// Write clean target
this->WriteTargetCleanRules();
// Write the dependency generation rule. This must be done last so
// that multiple output pair information is available.
this->WriteTargetDependRules();
// close the streams
this->CloseFileStreams();
}

View File

@ -47,9 +47,6 @@ void cmMakefileLibraryTargetGenerator::WriteRuleFiles()
// write the per-target per-language flags
this->WriteTargetLanguageFlags();
// Write the dependency generation rule.
this->WriteTargetDependRules();
// write the link rules
// Write the rule for this target type.
switch(this->Target->GetType())
@ -85,6 +82,10 @@ void cmMakefileLibraryTargetGenerator::WriteRuleFiles()
// Write clean target
this->WriteTargetCleanRules();
// Write the dependency generation rule. This must be done last so
// that multiple output pair information is available.
this->WriteTargetDependRules();
// close the streams
this->CloseFileStreams();
}

View File

@ -775,6 +775,23 @@ 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 << " \"" << pi->first << "\" \""
<< pi->second << "\"\n";
}
*this->InfoFileStream << " )\n\n";
}
// and now write the rule to use it
std::vector<std::string> depends;
std::vector<std::string> commands;
@ -993,7 +1010,7 @@ cmMakefileTargetGenerator
// the check-build-system step will remove the primary output if any
// extra outputs are missing. This forces the rule to regenerate
// all outputs.
this->GlobalGenerator->AddMultipleOutputPair(out, in);
this->AddMultipleOutputPair(out, in);
}
//----------------------------------------------------------------------------
@ -1334,3 +1351,12 @@ void cmMakefileTargetGenerator::WriteProgressVariables(unsigned long total,
current += this->NumberOfProgressActions;
delete progressFileStream;
}
//----------------------------------------------------------------------------
void
cmMakefileTargetGenerator
::AddMultipleOutputPair(const char* depender, const char* dependee)
{
MultipleOutputPairsType::value_type p(depender, dependee);
this->MultipleOutputPairs.insert(p);
}

View File

@ -119,6 +119,15 @@ protected:
// append intertarget dependencies
void AppendTargetDepends(std::vector<std::string>& depends);
/** In order to support parallel builds for custom commands with
multiple outputs the outputs are given a serial order, and only
the first output actually has the build rule. Other outputs
just depend on the first one. The check-build-system step must
remove a dependee if the depender is missing to make sure both
are regenerated properly. This method is used by the local
makefile generators to register such pairs. */
void AddMultipleOutputPair(const char* depender, const char* dependee);
virtual void CloseFileStreams();
void RemoveForbiddenFlags(const char* flagVar, const char* linkLang,
std::string& linkFlags);
@ -166,6 +175,8 @@ protected:
// Set of object file names that will be built in this directory.
std::set<cmStdString> ObjectFiles;
typedef std::map<cmStdString, cmStdString> MultipleOutputPairsType;
MultipleOutputPairsType MultipleOutputPairs;
//==================================================================
// Convenience routines that do nothing more than forward to

View File

@ -89,6 +89,10 @@ void cmMakefileUtilityTargetGenerator::WriteRuleFiles()
// Write clean target
this->WriteTargetCleanRules();
// Write the dependency generation rule. This must be done last so
// that multiple output pair information is available.
this->WriteTargetDependRules();
// close the streams
this->CloseFileStreams();
}

View File

@ -2547,29 +2547,24 @@ int cmake::CheckBuildSystem()
return 1;
}
// Now that we know the generator used to build the project, use it
// to check the dependency integrity.
const char* genName = mf->GetDefinition("CMAKE_DEPENDS_GENERATOR");
if (!genName || genName[0] == '\0')
if(this->ClearBuildSystem)
{
genName = "Unix Makefiles";
}
// this global generator is never set to the cmake object so it is never
// deleted, so make it an auto_ptr
std::auto_ptr<cmGlobalGenerator> ggd(this->CreateGlobalGenerator(genName));
if (ggd.get())
{
// Check the dependencies in case source files were removed.
std::auto_ptr<cmLocalGenerator> lgd(ggd->CreateLocalGenerator());
lgd->SetGlobalGenerator(ggd.get());
if(this->ClearBuildSystem)
// Get the generator used for this build system.
const char* genName = mf->GetDefinition("CMAKE_DEPENDS_GENERATOR");
if(!genName || genName[0] == '\0')
{
lgd->ClearDependencies(mf, verbose);
genName = "Unix Makefiles";
}
// Check for multiple output pairs.
ggd->CheckMultipleOutputs(mf, verbose);
// Create the generator and use it to clear the dependencies.
std::auto_ptr<cmGlobalGenerator>
ggd(this->CreateGlobalGenerator(genName));
if(ggd.get())
{
std::auto_ptr<cmLocalGenerator> lgd(ggd->CreateLocalGenerator());
lgd->SetGlobalGenerator(ggd.get());
lgd->ClearDependencies(mf, verbose);
}
}
// Get the set of dependencies and outputs.

View File

@ -13,6 +13,8 @@ write_file(${BuildDepends_BINARY_DIR}/Project/foo.cxx
file(WRITE ${BuildDepends_BINARY_DIR}/Project/zot.hxx.in
"static const char* zot = \"zot\";\n")
file(WRITE ${BuildDepends_BINARY_DIR}/Project/zot_custom.hxx.in
"static const char* zot_custom = \"zot_custom\";\n")
message("Building project first time")
try_compile(RESULT
@ -70,11 +72,11 @@ execute_process(COMMAND ${zot} OUTPUT_VARIABLE out RESULT_VARIABLE runResult)
string(REGEX REPLACE "[\r\n]" " " out "${out}")
message("Run result: ${runResult} Output: \"${out}\"")
if("${out}" STREQUAL "zot ")
if("${out}" STREQUAL "[zot] [zot_custom] ")
message("Worked!")
else("${out}" STREQUAL "zot ")
else("${out}" STREQUAL "[zot] [zot_custom] ")
message(SEND_ERROR "Project did not initially build properly: ${out}")
endif("${out}" STREQUAL "zot ")
endif("${out}" STREQUAL "[zot] [zot_custom] ")
message("Waiting 3 seconds...")
# any additional argument will cause ${bar} to wait forever
@ -85,6 +87,8 @@ write_file(${BuildDepends_BINARY_DIR}/Project/foo.cxx
"const char* foo() { return \"foo changed\";}" )
file(WRITE ${BuildDepends_BINARY_DIR}/Project/zot.hxx.in
"static const char* zot = \"zot changed\";\n")
file(WRITE ${BuildDepends_BINARY_DIR}/Project/zot_custom.hxx.in
"static const char* zot_custom = \"zot_custom changed\";\n")
message("Building project second time")
try_compile(RESULT
@ -137,8 +141,8 @@ execute_process(COMMAND ${zot} OUTPUT_VARIABLE out RESULT_VARIABLE runResult)
string(REGEX REPLACE "[\r\n]" " " out "${out}")
message("Run result: ${runResult} Output: \"${out}\"")
if("${out}" STREQUAL "zot changed ")
if("${out}" STREQUAL "[zot changed] [zot_custom changed] ")
message("Worked!")
else("${out}" STREQUAL "zot changed ")
else("${out}" STREQUAL "[zot changed] [zot_custom changed] ")
message(SEND_ERROR "Project did not rebuild properly!")
endif("${out}" STREQUAL "zot changed ")
endif("${out}" STREQUAL "[zot changed] [zot_custom changed] ")

View File

@ -32,9 +32,12 @@ add_executable(bar bar.cxx
IF("${CMAKE_GENERATOR}" MATCHES "Make")
# Test the IMPLICIT_DEPENDS feature.
SET(ZOT_DEPENDS IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep.cxx)
SET(ZOT_CUSTOM_DEP
IMPLICIT_DEPENDS CXX ${CMAKE_CURRENT_SOURCE_DIR}/dep_custom.cxx)
ELSE("${CMAKE_GENERATOR}" MATCHES "Make")
# No IMPLICIT_DEPENDS...just depend directly.
SET(ZOT_DEPENDS DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/zot.hxx.in)
SET(ZOT_CUSTOM_DEP DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/zot_custom.hxx.in)
ENDIF("${CMAKE_GENERATOR}" MATCHES "Make")
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/zot.hxx
@ -44,4 +47,15 @@ add_custom_command(
${ZOT_DEPENDS}
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/zot_custom.hxx
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_BINARY_DIR}/zot_custom.hxx.in
${CMAKE_CURRENT_BINARY_DIR}/zot_custom.hxx
${ZOT_CUSTOM_DEP}
)
add_custom_target(zot_custom ALL DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/zot_custom.hxx)
add_executable(zot zot.cxx ${CMAKE_CURRENT_BINARY_DIR}/zot.hxx)
add_dependencies(zot zot_custom)

View File

@ -0,0 +1 @@
#include <zot_custom.hxx.in>

View File

@ -1,9 +1,10 @@
#include <zot.hxx>
#include <zot_custom.hxx>
#include <stdio.h>
int main()
{
printf("%s\n", zot);
printf("[%s] [%s]\n", zot, zot_custom);
fflush(stdout);
return 0;
}