cmTimestamp.cxx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #if !defined(_WIN32) && !defined(__sun) && !defined(__OpenBSD__)
  4. // POSIX APIs are needed
  5. // NOLINTNEXTLINE(bugprone-reserved-identifier)
  6. # define _POSIX_C_SOURCE 200809L
  7. #endif
  8. #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__QNX__)
  9. // For isascii
  10. // NOLINTNEXTLINE(bugprone-reserved-identifier)
  11. # define _XOPEN_SOURCE 700
  12. #endif
  13. #include "cmTimestamp.h"
  14. #include <cstdlib>
  15. #include <cstring>
  16. #include <sstream>
  17. #ifdef __MINGW32__
  18. # include <libloaderapi.h>
  19. #endif
  20. #include "cmStringAlgorithms.h"
  21. #include "cmSystemTools.h"
  22. std::string cmTimestamp::CurrentTime(const std::string& formatString,
  23. bool utcFlag) const
  24. {
  25. time_t currentTimeT = time(nullptr);
  26. std::string source_date_epoch;
  27. cmSystemTools::GetEnv("SOURCE_DATE_EPOCH", source_date_epoch);
  28. if (!source_date_epoch.empty()) {
  29. std::istringstream iss(source_date_epoch);
  30. iss >> currentTimeT;
  31. if (iss.fail() || !iss.eof()) {
  32. cmSystemTools::Error("Cannot parse SOURCE_DATE_EPOCH as integer");
  33. exit(27);
  34. }
  35. }
  36. if (currentTimeT == time_t(-1)) {
  37. return std::string();
  38. }
  39. return this->CreateTimestampFromTimeT(currentTimeT, formatString, utcFlag);
  40. }
  41. std::string cmTimestamp::FileModificationTime(const char* path,
  42. const std::string& formatString,
  43. bool utcFlag) const
  44. {
  45. std::string real_path =
  46. cmSystemTools::GetRealPathResolvingWindowsSubst(path);
  47. if (!cmsys::SystemTools::FileExists(real_path)) {
  48. return std::string();
  49. }
  50. time_t mtime = cmsys::SystemTools::ModifiedTime(real_path);
  51. return this->CreateTimestampFromTimeT(mtime, formatString, utcFlag);
  52. }
  53. std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT,
  54. std::string formatString,
  55. bool utcFlag) const
  56. {
  57. if (formatString.empty()) {
  58. formatString = "%Y-%m-%dT%H:%M:%S";
  59. if (utcFlag) {
  60. formatString += "Z";
  61. }
  62. }
  63. struct tm timeStruct;
  64. memset(&timeStruct, 0, sizeof(timeStruct));
  65. struct tm* ptr = nullptr;
  66. if (utcFlag) {
  67. ptr = gmtime(&timeT);
  68. } else {
  69. ptr = localtime(&timeT);
  70. }
  71. if (ptr == nullptr) {
  72. return std::string();
  73. }
  74. timeStruct = *ptr;
  75. std::string result;
  76. for (std::string::size_type i = 0; i < formatString.size(); ++i) {
  77. char c1 = formatString[i];
  78. char c2 = (i + 1 < formatString.size()) ? formatString[i + 1]
  79. : static_cast<char>(0);
  80. if (c1 == '%' && c2 != 0) {
  81. result += this->AddTimestampComponent(c2, timeStruct, timeT);
  82. ++i;
  83. } else {
  84. result += c1;
  85. }
  86. }
  87. return result;
  88. }
  89. time_t cmTimestamp::CreateUtcTimeTFromTm(struct tm& tm) const
  90. {
  91. #if defined(_MSC_VER) && _MSC_VER >= 1400
  92. return _mkgmtime(&tm);
  93. #else
  94. // From Linux timegm() manpage.
  95. std::string tz_old;
  96. bool const tz_was_set = cmSystemTools::GetEnv("TZ", tz_old);
  97. tz_old = "TZ=" + tz_old;
  98. // The standard says that "TZ=" or "TZ=[UNRECOGNIZED_TZ]" means UTC.
  99. // It seems that "TZ=" does NOT work, at least under Windows
  100. // with neither MSVC nor MinGW, so let's use explicit "TZ=UTC"
  101. cmSystemTools::PutEnv("TZ=UTC");
  102. tzset();
  103. time_t result = mktime(&tm);
  104. # ifndef CMAKE_BOOTSTRAP
  105. if (tz_was_set) {
  106. cmSystemTools::PutEnv(tz_old);
  107. } else {
  108. cmSystemTools::UnsetEnv("TZ");
  109. }
  110. # else
  111. // No UnsetEnv during bootstrap. This is good enough for CMake itself.
  112. cmSystemTools::PutEnv(tz_old);
  113. static_cast<void>(tz_was_set);
  114. # endif
  115. tzset();
  116. return result;
  117. #endif
  118. }
  119. std::string cmTimestamp::AddTimestampComponent(char flag,
  120. struct tm& timeStruct,
  121. const time_t timeT) const
  122. {
  123. std::string formatString = cmStrCat('%', flag);
  124. switch (flag) {
  125. case 'a':
  126. case 'A':
  127. case 'b':
  128. case 'B':
  129. case 'd':
  130. case 'H':
  131. case 'I':
  132. case 'j':
  133. case 'm':
  134. case 'M':
  135. case 'S':
  136. case 'U':
  137. case 'V':
  138. case 'w':
  139. case 'y':
  140. case 'Y':
  141. case '%':
  142. break;
  143. case 's': // Seconds since UNIX epoch (midnight 1-jan-1970)
  144. {
  145. // Build a time_t for UNIX epoch and subtract from the input "timeT":
  146. struct tm tmUnixEpoch;
  147. memset(&tmUnixEpoch, 0, sizeof(tmUnixEpoch));
  148. tmUnixEpoch.tm_mday = 1;
  149. tmUnixEpoch.tm_year = 1970 - 1900;
  150. const time_t unixEpoch = this->CreateUtcTimeTFromTm(tmUnixEpoch);
  151. if (unixEpoch == -1) {
  152. cmSystemTools::Error(
  153. "Error generating UNIX epoch in "
  154. "STRING(TIMESTAMP ...). Please, file a bug report against CMake");
  155. return std::string();
  156. }
  157. return std::to_string(static_cast<long int>(difftime(timeT, unixEpoch)));
  158. }
  159. default: {
  160. return formatString;
  161. }
  162. }
  163. #ifdef __MINGW32__
  164. /* See a bug in MinGW: https://sourceforge.net/p/mingw-w64/bugs/793/. A work
  165. * around is to try to use strftime() from ucrtbase.dll. */
  166. using T = size_t(WINAPI*)(char*, size_t, const char*, const struct tm*);
  167. auto loadStrftime = [] {
  168. auto handle =
  169. LoadLibraryExA("ucrtbase.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
  170. if (handle) {
  171. # pragma GCC diagnostic push
  172. # pragma GCC diagnostic ignored "-Wcast-function-type"
  173. return reinterpret_cast<T>(GetProcAddress(handle, "strftime"));
  174. # pragma GCC diagnostic pop
  175. }
  176. return strftime;
  177. };
  178. static T strftime = loadStrftime();
  179. #endif
  180. char buffer[16];
  181. size_t size =
  182. strftime(buffer, sizeof(buffer), formatString.c_str(), &timeStruct);
  183. return std::string(buffer, size);
  184. }