cmFileTimeComparison.cxx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmFileTimeComparison.h"
  11. #include <cmConfigure.h>
  12. #include <string>
  13. #include <time.h>
  14. #include <utility>
  15. // Use a hash table to avoid duplicate file time checks from disk.
  16. #if defined(CMAKE_BUILD_WITH_CMAKE)
  17. #ifdef CMake_HAVE_CXX_UNORDERED_MAP
  18. #include <unordered_map>
  19. #else
  20. #include <cmsys/hash_map.hxx>
  21. #endif
  22. #endif
  23. // Use a platform-specific API to get file times efficiently.
  24. #if !defined(_WIN32) || defined(__CYGWIN__)
  25. #include <sys/stat.h>
  26. #define cmFileTimeComparison_Type struct stat
  27. #else
  28. #include <cmsys/Encoding.hxx>
  29. #include <windows.h>
  30. #define cmFileTimeComparison_Type FILETIME
  31. #endif
  32. class cmFileTimeComparisonInternal
  33. {
  34. public:
  35. // Internal comparison method.
  36. inline bool FileTimeCompare(const char* f1, const char* f2, int* result);
  37. bool FileTimesDiffer(const char* f1, const char* f2);
  38. private:
  39. #if defined(CMAKE_BUILD_WITH_CMAKE)
  40. // Use a hash table to efficiently map from file name to modification time.
  41. class HashString
  42. {
  43. public:
  44. size_t operator()(const std::string& s) const { return h(s.c_str()); }
  45. #ifdef CMake_HAVE_CXX_UNORDERED_MAP
  46. std::hash<const char*> h;
  47. #else
  48. cmsys::hash<const char*> h;
  49. #endif
  50. };
  51. #ifdef CMake_HAVE_CXX_UNORDERED_MAP
  52. typedef std::unordered_map<std::string,
  53. #else
  54. typedef cmsys::hash_map<std::string,
  55. #endif
  56. cmFileTimeComparison_Type, HashString>
  57. FileStatsMap;
  58. FileStatsMap Files;
  59. #endif
  60. // Internal methods to lookup and compare modification times.
  61. inline bool Stat(const char* fname, cmFileTimeComparison_Type* st);
  62. inline int Compare(cmFileTimeComparison_Type* st1,
  63. cmFileTimeComparison_Type* st2);
  64. inline bool TimesDiffer(cmFileTimeComparison_Type* st1,
  65. cmFileTimeComparison_Type* st2);
  66. };
  67. bool cmFileTimeComparisonInternal::Stat(const char* fname,
  68. cmFileTimeComparison_Type* st)
  69. {
  70. #if defined(CMAKE_BUILD_WITH_CMAKE)
  71. // Use the stored time if available.
  72. cmFileTimeComparisonInternal::FileStatsMap::iterator fit =
  73. this->Files.find(fname);
  74. if (fit != this->Files.end()) {
  75. *st = fit->second;
  76. return true;
  77. }
  78. #endif
  79. #if !defined(_WIN32) || defined(__CYGWIN__)
  80. // POSIX version. Use the stat function.
  81. int res = ::stat(fname, st);
  82. if (res != 0) {
  83. return false;
  84. }
  85. #else
  86. // Windows version. Get the modification time from extended file
  87. // attributes.
  88. WIN32_FILE_ATTRIBUTE_DATA fdata;
  89. if (!GetFileAttributesExW(cmsys::Encoding::ToWide(fname).c_str(),
  90. GetFileExInfoStandard, &fdata)) {
  91. return false;
  92. }
  93. // Copy the file time to the output location.
  94. *st = fdata.ftLastWriteTime;
  95. #endif
  96. #if defined(CMAKE_BUILD_WITH_CMAKE)
  97. // Store the time for future use.
  98. this->Files[fname] = *st;
  99. #endif
  100. return true;
  101. }
  102. cmFileTimeComparison::cmFileTimeComparison()
  103. {
  104. this->Internals = new cmFileTimeComparisonInternal;
  105. }
  106. cmFileTimeComparison::~cmFileTimeComparison()
  107. {
  108. delete this->Internals;
  109. }
  110. bool cmFileTimeComparison::FileTimeCompare(const char* f1, const char* f2,
  111. int* result)
  112. {
  113. return this->Internals->FileTimeCompare(f1, f2, result);
  114. }
  115. bool cmFileTimeComparison::FileTimesDiffer(const char* f1, const char* f2)
  116. {
  117. return this->Internals->FileTimesDiffer(f1, f2);
  118. }
  119. int cmFileTimeComparisonInternal::Compare(cmFileTimeComparison_Type* s1,
  120. cmFileTimeComparison_Type* s2)
  121. {
  122. #if !defined(_WIN32) || defined(__CYGWIN__)
  123. #if CMake_STAT_HAS_ST_MTIM
  124. // Compare using nanosecond resolution.
  125. if (s1->st_mtim.tv_sec < s2->st_mtim.tv_sec) {
  126. return -1;
  127. }
  128. if (s1->st_mtim.tv_sec > s2->st_mtim.tv_sec) {
  129. return 1;
  130. }
  131. if (s1->st_mtim.tv_nsec < s2->st_mtim.tv_nsec) {
  132. return -1;
  133. }
  134. if (s1->st_mtim.tv_nsec > s2->st_mtim.tv_nsec) {
  135. return 1;
  136. }
  137. #elif CMake_STAT_HAS_ST_MTIMESPEC
  138. // Compare using nanosecond resolution.
  139. if (s1->st_mtimespec.tv_sec < s2->st_mtimespec.tv_sec) {
  140. return -1;
  141. } else if (s1->st_mtimespec.tv_sec > s2->st_mtimespec.tv_sec) {
  142. return 1;
  143. } else if (s1->st_mtimespec.tv_nsec < s2->st_mtimespec.tv_nsec) {
  144. return -1;
  145. } else if (s1->st_mtimespec.tv_nsec > s2->st_mtimespec.tv_nsec) {
  146. return 1;
  147. }
  148. #else
  149. // Compare using 1 second resolution.
  150. if (s1->st_mtime < s2->st_mtime) {
  151. return -1;
  152. } else if (s1->st_mtime > s2->st_mtime) {
  153. return 1;
  154. }
  155. #endif
  156. // Files have the same time.
  157. return 0;
  158. #else
  159. // Compare using system-provided function.
  160. return (int)CompareFileTime(s1, s2);
  161. #endif
  162. }
  163. bool cmFileTimeComparisonInternal::TimesDiffer(cmFileTimeComparison_Type* s1,
  164. cmFileTimeComparison_Type* s2)
  165. {
  166. #if !defined(_WIN32) || defined(__CYGWIN__)
  167. #if CMake_STAT_HAS_ST_MTIM
  168. // Times are integers in units of 1ns.
  169. long long bil = 1000000000;
  170. long long t1 = s1->st_mtim.tv_sec * bil + s1->st_mtim.tv_nsec;
  171. long long t2 = s2->st_mtim.tv_sec * bil + s2->st_mtim.tv_nsec;
  172. if (t1 < t2) {
  173. return (t2 - t1) >= bil;
  174. }
  175. if (t2 < t1) {
  176. return (t1 - t2) >= bil;
  177. }
  178. return false;
  179. #elif CMake_STAT_HAS_ST_MTIMESPEC
  180. // Times are integers in units of 1ns.
  181. long long bil = 1000000000;
  182. long long t1 = s1->st_mtimespec.tv_sec * bil + s1->st_mtimespec.tv_nsec;
  183. long long t2 = s2->st_mtimespec.tv_sec * bil + s2->st_mtimespec.tv_nsec;
  184. if (t1 < t2) {
  185. return (t2 - t1) >= bil;
  186. } else if (t2 < t1) {
  187. return (t1 - t2) >= bil;
  188. } else {
  189. return false;
  190. }
  191. #else
  192. // Times are integers in units of 1s.
  193. if (s1->st_mtime < s2->st_mtime) {
  194. return (s2->st_mtime - s1->st_mtime) >= 1;
  195. } else if (s1->st_mtime > s2->st_mtime) {
  196. return (s1->st_mtime - s2->st_mtime) >= 1;
  197. } else {
  198. return false;
  199. }
  200. #endif
  201. #else
  202. // Times are integers in units of 100ns.
  203. LARGE_INTEGER t1;
  204. LARGE_INTEGER t2;
  205. t1.LowPart = s1->dwLowDateTime;
  206. t1.HighPart = s1->dwHighDateTime;
  207. t2.LowPart = s2->dwLowDateTime;
  208. t2.HighPart = s2->dwHighDateTime;
  209. if (t1.QuadPart < t2.QuadPart) {
  210. return (t2.QuadPart - t1.QuadPart) >= static_cast<LONGLONG>(10000000);
  211. } else if (t2.QuadPart < t1.QuadPart) {
  212. return (t1.QuadPart - t2.QuadPart) >= static_cast<LONGLONG>(10000000);
  213. } else {
  214. return false;
  215. }
  216. #endif
  217. }
  218. bool cmFileTimeComparisonInternal::FileTimeCompare(const char* f1,
  219. const char* f2, int* result)
  220. {
  221. // Get the modification time for each file.
  222. cmFileTimeComparison_Type s1;
  223. cmFileTimeComparison_Type s2;
  224. if (this->Stat(f1, &s1) && this->Stat(f2, &s2)) {
  225. // Compare the two modification times.
  226. *result = this->Compare(&s1, &s2);
  227. return true;
  228. }
  229. // No comparison available. Default to the same time.
  230. *result = 0;
  231. return false;
  232. }
  233. bool cmFileTimeComparisonInternal::FileTimesDiffer(const char* f1,
  234. const char* f2)
  235. {
  236. // Get the modification time for each file.
  237. cmFileTimeComparison_Type s1;
  238. cmFileTimeComparison_Type s2;
  239. if (this->Stat(f1, &s1) && this->Stat(f2, &s2)) {
  240. // Compare the two modification times.
  241. return this->TimesDiffer(&s1, &s2);
  242. }
  243. // No comparison available. Default to different times.
  244. return true;
  245. }