diff options
author | Brad King <brad.king@kitware.com> | 2008-06-02 20:44:58 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2008-06-02 20:44:58 (GMT) |
commit | 6be09c366774ed6d723a06f5f07ba5c09d8e4579 (patch) | |
tree | 55aef0c5ad0c7b582b76494f5db1b5e291c2e76a /Source/cmGlobalGenerator.cxx | |
parent | db59f49ecf6767e66d9edd12194bcd60804dc8a3 (diff) | |
download | CMake-6be09c366774ed6d723a06f5f07ba5c09d8e4579.zip CMake-6be09c366774ed6d723a06f5f07ba5c09d8e4579.tar.gz CMake-6be09c366774ed6d723a06f5f07ba5c09d8e4579.tar.bz2 |
ENH: Introduce "rule hashes" to help rebuild files when rules change.
- In CMake 2.4 custom commands would not rebuild when rules changed.
- In CMake 2.6.0 custom commands have a dependency on build.make
which causes them to rebuild when changed, but also when any
source is added or removed. This is too often.
- We cannot have a per-rule file because Windows filesystems
do not deal well with lots of small files.
- Instead we add a persistent CMakeFiles/CMakeRuleHashes.txt file
at the top of the build tree that is updated during each
CMake Generate step. It records a hash of the build rule for
each file to be built. When the hash changes the file is
removed so that it will be rebuilt.
Diffstat (limited to 'Source/cmGlobalGenerator.cxx')
-rw-r--r-- | Source/cmGlobalGenerator.cxx | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 33a7f20..b971298 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -27,9 +27,14 @@ #include "cmVersion.h" #include "cmExportInstallFileGenerator.h" #include "cmComputeTargetDepends.h" +#include "cmGeneratedFileStream.h" #include <cmsys/Directory.hxx> +#if defined(CMAKE_BUILD_WITH_CMAKE) +# include <cmsys/MD5.h> +#endif + #include <stdlib.h> // required for atof #include <assert.h> @@ -686,6 +691,7 @@ void cmGlobalGenerator::Configure() this->TotalTargets.clear(); this->LocalGeneratorToTargetMap.clear(); this->ProjectMap.clear(); + this->RuleHashes.clear(); // start with this directory cmLocalGenerator *lg = this->CreateLocalGenerator(); @@ -840,6 +846,9 @@ void cmGlobalGenerator::Generate() } this->SetCurrentLocalGenerator(0); + // Update rule hashes. + this->CheckRuleHashes(); + if (this->ExtraGenerator != 0) { this->ExtraGenerator->Generate(); @@ -1931,3 +1940,143 @@ cmGlobalGenerator::GetDirectoryContent(std::string const& dir, bool needDisk) return dc; } +//---------------------------------------------------------------------------- +void +cmGlobalGenerator::AddRuleHash(const std::vector<std::string>& outputs, + const std::vector<std::string>& depends, + const std::vector<std::string>& commands) +{ +#if defined(CMAKE_BUILD_WITH_CMAKE) + // Ignore if there are no outputs. + if(outputs.empty()) + { + return; + } + + // Compute a hash of the rule. + RuleHash hash; + { + unsigned char const* data; + int length; + cmsysMD5* sum = cmsysMD5_New(); + cmsysMD5_Initialize(sum); + for(std::vector<std::string>::const_iterator i = outputs.begin(); + i != outputs.end(); ++i) + { + data = reinterpret_cast<unsigned char const*>(i->c_str()); + length = static_cast<int>(i->length()); + cmsysMD5_Append(sum, data, length); + } + for(std::vector<std::string>::const_iterator i = depends.begin(); + i != depends.end(); ++i) + { + data = reinterpret_cast<unsigned char const*>(i->c_str()); + length = static_cast<int>(i->length()); + cmsysMD5_Append(sum, data, length); + } + for(std::vector<std::string>::const_iterator i = commands.begin(); + i != commands.end(); ++i) + { + data = reinterpret_cast<unsigned char const*>(i->c_str()); + length = static_cast<int>(i->length()); + cmsysMD5_Append(sum, data, length); + } + cmsysMD5_FinalizeHex(sum, hash.Data); + cmsysMD5_Delete(sum); + } + + // Shorten the output name (in expected use case). + cmLocalGenerator* lg = this->GetLocalGenerators()[0]; + std::string fname = lg->Convert(outputs[0].c_str(), + cmLocalGenerator::HOME_OUTPUT); + + // Associate the hash with this output. + this->RuleHashes[fname] = hash; +#else + (void)outputs; + (void)depends; + (void)commands; +#endif +} + +//---------------------------------------------------------------------------- +void cmGlobalGenerator::CheckRuleHashes() +{ +#if defined(CMAKE_BUILD_WITH_CMAKE) + std::string home = this->GetCMakeInstance()->GetHomeOutputDirectory(); + std::string pfile = home; + pfile += this->GetCMakeInstance()->GetCMakeFilesDirectory(); + pfile += "/CMakeRuleHashes.txt"; + +#if defined(_WIN32) || defined(__CYGWIN__) + std::ifstream fin(pfile.c_str(), std::ios::in | std::ios::binary); +#else + std::ifstream fin(pfile.c_str(), std::ios::in); +#endif + std::string line; + std::string fname; + while(cmSystemTools::GetLineFromStream(fin, line)) + { + // Line format is a 32-byte hex string followed by a space + // followed by a file name (with no escaping). + + // Skip blank and comment lines. + if(line.size() < 34 || line[0] == '#') + { + continue; + } + + // Get the filename. + fname = line.substr(33, line.npos); + + // Look for a hash for this file's rule. + std::map<cmStdString, RuleHash>::const_iterator rhi = + this->RuleHashes.find(fname); + if(rhi != this->RuleHashes.end()) + { + // Compare the rule hash in the file to that we were given. + if(strncmp(line.c_str(), rhi->second.Data, 32) != 0) + { + // The rule has changed. Delete the output so it will be + // built again. + fname = cmSystemTools::CollapseFullPath(fname.c_str(), home.c_str()); + cmSystemTools::RemoveFile(fname.c_str()); + } + } + else + { + // We have no hash for a rule previously listed. This may be a + // case where a user has turned off a build option and might + // want to turn it back on later, so do not delete the file. + // Instead, we keep the rule hash as long as the file exists so + // that if the feature is turned back on and the rule has + // changed the file is still rebuilt. + std::string fpath = + cmSystemTools::CollapseFullPath(fname.c_str(), home.c_str()); + if(cmSystemTools::FileExists(fpath.c_str())) + { + RuleHash hash; + strncpy(hash.Data, line.c_str(), 32); + this->RuleHashes[fname] = hash; + } + } + } + + // Now generate a new persistence file with the current hashes. + if(this->RuleHashes.empty()) + { + cmSystemTools::RemoveFile(pfile.c_str()); + } + else + { + cmGeneratedFileStream fout(pfile.c_str()); + fout << "# Hashes of file build rules.\n"; + for(std::map<cmStdString, RuleHash>::const_iterator + rhi = this->RuleHashes.begin(); rhi != this->RuleHashes.end(); ++rhi) + { + fout.write(rhi->second.Data, 32); + fout << " " << rhi->first << "\n"; + } + } +#endif +} |