resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmTimestamp.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3
4 #if !defined(_WIN32) && !defined(__sun) && !defined(__OpenBSD__)
5 // POSIX APIs are needed
6 // NOLINTNEXTLINE(bugprone-reserved-identifier)
7 #  define _POSIX_C_SOURCE 200809L
8 #endif
9 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__QNX__)
10 // For isascii
11 // NOLINTNEXTLINE(bugprone-reserved-identifier)
12 #  define _XOPEN_SOURCE 700
13 #endif
14
15 #include "cmTimestamp.h"
16
17 #include <cstdlib>
18 #include <cstring>
19 #include <sstream>
20 #include <utility>
21
22 #ifdef __MINGW32__
23 #  include <libloaderapi.h>
24 #endif
25
26 #include <cm3p/uv.h>
27
28 #include "cmStringAlgorithms.h"
29 #include "cmSystemTools.h"
30
31 std::string cmTimestamp::CurrentTime(const std::string& formatString,
32                                      bool utcFlag) const
33 {
34   // get current time with microsecond resolution
35   uv_timeval64_t timeval;
36   uv_gettimeofday(&timeval);
37   auto currentTimeT = static_cast<time_t>(timeval.tv_sec);
38   auto microseconds = static_cast<uint32_t>(timeval.tv_usec);
39
40   // check for override via SOURCE_DATE_EPOCH for reproducible builds
41   std::string source_date_epoch;
42   cmSystemTools::GetEnv("SOURCE_DATE_EPOCH", source_date_epoch);
43   if (!source_date_epoch.empty()) {
44     std::istringstream iss(source_date_epoch);
45     iss >> currentTimeT;
46     if (iss.fail() || !iss.eof()) {
47       cmSystemTools::Error("Cannot parse SOURCE_DATE_EPOCH as integer");
48       exit(27);
49     }
50     // SOURCE_DATE_EPOCH has only a resolution in the seconds range
51     microseconds = 0;
52   }
53   if (currentTimeT == static_cast<time_t>(-1)) {
54     return std::string();
55   }
56
57   return this->CreateTimestampFromTimeT(currentTimeT, microseconds,
58                                         formatString, utcFlag);
59 }
60
61 std::string cmTimestamp::FileModificationTime(const char* path,
62                                               const std::string& formatString,
63                                               bool utcFlag) const
64 {
65   std::string real_path =
66     cmSystemTools::GetRealPathResolvingWindowsSubst(path);
67
68   if (!cmsys::SystemTools::FileExists(real_path)) {
69     return std::string();
70   }
71
72   // use libuv's implementation of stat(2) to get the file information
73   time_t mtime = 0;
74   uint32_t microseconds = 0;
75   uv_fs_t req;
76   if (uv_fs_stat(nullptr, &req, real_path.c_str(), nullptr) == 0) {
77     mtime = static_cast<time_t>(req.statbuf.st_mtim.tv_sec);
78     // tv_nsec has nanosecond resolution, but we truncate it to microsecond
79     // resolution in order to be consistent with cmTimestamp::CurrentTime()
80     microseconds = static_cast<uint32_t>(req.statbuf.st_mtim.tv_nsec / 1000);
81   }
82   uv_fs_req_cleanup(&req);
83
84   return this->CreateTimestampFromTimeT(mtime, microseconds, formatString,
85                                         utcFlag);
86 }
87
88 std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT,
89                                                   std::string formatString,
90                                                   bool utcFlag) const
91 {
92   return this->CreateTimestampFromTimeT(timeT, 0, std::move(formatString),
93                                         utcFlag);
94 }
95
96 std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT,
97                                                   const uint32_t microseconds,
98                                                   std::string formatString,
99                                                   bool utcFlag) const
100 {
101   if (formatString.empty()) {
102     formatString = "%Y-%m-%dT%H:%M:%S";
103     if (utcFlag) {
104       formatString += "Z";
105     }
106   }
107
108   struct tm timeStruct;
109   memset(&timeStruct, 0, sizeof(timeStruct));
110
111   struct tm* ptr = nullptr;
112   if (utcFlag) {
113     ptr = gmtime(&timeT);
114   } else {
115     ptr = localtime(&timeT);
116   }
117
118   if (ptr == nullptr) {
119     return std::string();
120   }
121
122   timeStruct = *ptr;
123
124   std::string result;
125   for (std::string::size_type i = 0; i < formatString.size(); ++i) {
126     char c1 = formatString[i];
127     char c2 = (i + 1 < formatString.size()) ? formatString[i + 1]
128                                             : static_cast<char>(0);
129
130     if (c1 == '%' && c2 != 0) {
131       result +=
132         this->AddTimestampComponent(c2, timeStruct, timeT, microseconds);
133       ++i;
134     } else {
135       result += c1;
136     }
137   }
138
139   return result;
140 }
141
142 time_t cmTimestamp::CreateUtcTimeTFromTm(struct tm& tm) const
143 {
144 #if defined(_MSC_VER) && _MSC_VER >= 1400
145   return _mkgmtime(&tm);
146 #else
147   // From Linux timegm() manpage.
148
149   std::string tz_old;
150   bool const tz_was_set = cmSystemTools::GetEnv("TZ", tz_old);
151   tz_old = "TZ=" + tz_old;
152
153   // The standard says that "TZ=" or "TZ=[UNRECOGNIZED_TZ]" means UTC.
154   // It seems that "TZ=" does NOT work, at least under Windows
155   // with neither MSVC nor MinGW, so let's use explicit "TZ=UTC"
156
157   cmSystemTools::PutEnv("TZ=UTC");
158
159   tzset();
160
161   time_t result = mktime(&tm);
162
163 #  ifndef CMAKE_BOOTSTRAP
164   if (tz_was_set) {
165     cmSystemTools::PutEnv(tz_old);
166   } else {
167     cmSystemTools::UnsetEnv("TZ");
168   }
169 #  else
170   // No UnsetEnv during bootstrap.  This is good enough for CMake itself.
171   cmSystemTools::PutEnv(tz_old);
172   static_cast<void>(tz_was_set);
173 #  endif
174
175   tzset();
176
177   return result;
178 #endif
179 }
180
181 std::string cmTimestamp::AddTimestampComponent(
182   char flag, struct tm& timeStruct, const time_t timeT,
183   const uint32_t microseconds) const
184 {
185   std::string formatString = cmStrCat('%', flag);
186
187   switch (flag) {
188     case 'a':
189     case 'A':
190     case 'b':
191     case 'B':
192     case 'd':
193     case 'H':
194     case 'I':
195     case 'j':
196     case 'm':
197     case 'M':
198     case 'S':
199     case 'U':
200     case 'V':
201     case 'w':
202     case 'y':
203     case 'Y':
204     case '%':
205       break;
206     case 's': // Seconds since UNIX epoch (midnight 1-jan-1970)
207     {
208       // Build a time_t for UNIX epoch and subtract from the input "timeT":
209       struct tm tmUnixEpoch;
210       memset(&tmUnixEpoch, 0, sizeof(tmUnixEpoch));
211       tmUnixEpoch.tm_mday = 1;
212       tmUnixEpoch.tm_year = 1970 - 1900;
213
214       const time_t unixEpoch = this->CreateUtcTimeTFromTm(tmUnixEpoch);
215       if (unixEpoch == -1) {
216         cmSystemTools::Error(
217           "Error generating UNIX epoch in string(TIMESTAMP ...) or "
218           "file(TIMESTAMP ...). Please, file a bug report against CMake");
219         return std::string();
220       }
221
222       return std::to_string(static_cast<long int>(difftime(timeT, unixEpoch)));
223     }
224     case 'f': // microseconds
225     {
226       // clip number to 6 digits and pad with leading zeros
227       std::string microsecs = std::to_string(microseconds % 1000000);
228       return std::string(6 - microsecs.length(), '0') + microsecs;
229     }
230     default: {
231       return formatString;
232     }
233   }
234
235   char buffer[16];
236
237 #ifdef __MINGW32__
238   /* See a bug in MinGW: https://sourceforge.net/p/mingw-w64/bugs/793/. A work
239    * around is to try to use strftime() from ucrtbase.dll. */
240   using T = size_t(__cdecl*)(char*, size_t, const char*, const struct tm*);
241   auto loadUcrtStrftime = []() -> T {
242     auto handle =
243       LoadLibraryExA("ucrtbase.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
244     if (handle) {
245 #  pragma GCC diagnostic push
246 #  pragma GCC diagnostic ignored "-Wcast-function-type"
247       return reinterpret_cast<T>(GetProcAddress(handle, "strftime"));
248 #  pragma GCC diagnostic pop
249     }
250     return nullptr;
251   };
252   static T ucrtStrftime = loadUcrtStrftime();
253
254   if (ucrtStrftime) {
255     size_t size =
256       ucrtStrftime(buffer, sizeof(buffer), formatString.c_str(), &timeStruct);
257     return std::string(buffer, size);
258   }
259 #endif
260
261   size_t size =
262     strftime(buffer, sizeof(buffer), formatString.c_str(), &timeStruct);
263
264   return std::string(buffer, size);
265 }