| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #if !defined(_WIN32) && !defined(__sun) && !defined(__OpenBSD__)
- // POSIX APIs are needed
- // NOLINTNEXTLINE(bugprone-reserved-identifier)
- # define _POSIX_C_SOURCE 200809L
- #endif
- #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__QNX__)
- // For isascii
- // NOLINTNEXTLINE(bugprone-reserved-identifier)
- # define _XOPEN_SOURCE 700
- #endif
- #include "cmTimestamp.h"
- #include <cstdlib>
- #include <cstring>
- #include <sstream>
- #include <utility>
- #ifdef __MINGW32__
- # include <libloaderapi.h>
- #endif
- #include <cm3p/uv.h>
- #include "cmStringAlgorithms.h"
- #include "cmSystemTools.h"
- std::string cmTimestamp::CurrentTime(const std::string& formatString,
- bool utcFlag) const
- {
- // get current time with microsecond resolution
- uv_timeval64_t timeval;
- uv_gettimeofday(&timeval);
- auto currentTimeT = static_cast<time_t>(timeval.tv_sec);
- auto microseconds = static_cast<uint32_t>(timeval.tv_usec);
- // check for override via SOURCE_DATE_EPOCH for reproducible builds
- std::string source_date_epoch;
- cmSystemTools::GetEnv("SOURCE_DATE_EPOCH", source_date_epoch);
- if (!source_date_epoch.empty()) {
- std::istringstream iss(source_date_epoch);
- iss >> currentTimeT;
- if (iss.fail() || !iss.eof()) {
- cmSystemTools::Error("Cannot parse SOURCE_DATE_EPOCH as integer");
- exit(27);
- }
- // SOURCE_DATE_EPOCH has only a resolution in the seconds range
- microseconds = 0;
- }
- if (currentTimeT == static_cast<time_t>(-1)) {
- return std::string();
- }
- return this->CreateTimestampFromTimeT(currentTimeT, microseconds,
- formatString, utcFlag);
- }
- std::string cmTimestamp::FileModificationTime(const char* path,
- const std::string& formatString,
- bool utcFlag) const
- {
- std::string real_path =
- cmSystemTools::GetRealPathResolvingWindowsSubst(path);
- if (!cmsys::SystemTools::FileExists(real_path)) {
- return std::string();
- }
- // use libuv's implementation of stat(2) to get the file information
- time_t mtime = 0;
- uint32_t microseconds = 0;
- uv_fs_t req;
- if (uv_fs_stat(nullptr, &req, real_path.c_str(), nullptr) == 0) {
- mtime = static_cast<time_t>(req.statbuf.st_mtim.tv_sec);
- // tv_nsec has nanosecond resolution, but we truncate it to microsecond
- // resolution in order to be consistent with cmTimestamp::CurrentTime()
- microseconds = static_cast<uint32_t>(req.statbuf.st_mtim.tv_nsec / 1000);
- }
- uv_fs_req_cleanup(&req);
- return this->CreateTimestampFromTimeT(mtime, microseconds, formatString,
- utcFlag);
- }
- std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT,
- std::string formatString,
- bool utcFlag) const
- {
- return this->CreateTimestampFromTimeT(timeT, 0, std::move(formatString),
- utcFlag);
- }
- std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT,
- const uint32_t microseconds,
- std::string formatString,
- bool utcFlag) const
- {
- if (formatString.empty()) {
- formatString = "%Y-%m-%dT%H:%M:%S";
- if (utcFlag) {
- formatString += "Z";
- }
- }
- struct tm timeStruct;
- memset(&timeStruct, 0, sizeof(timeStruct));
- struct tm* ptr = nullptr;
- if (utcFlag) {
- ptr = gmtime(&timeT);
- } else {
- ptr = localtime(&timeT);
- }
- if (!ptr) {
- return std::string();
- }
- timeStruct = *ptr;
- std::string result;
- for (std::string::size_type i = 0; i < formatString.size(); ++i) {
- char c1 = formatString[i];
- char c2 = (i + 1 < formatString.size()) ? formatString[i + 1]
- : static_cast<char>(0);
- if (c1 == '%' && c2 != 0) {
- result += this->AddTimestampComponent(c2, timeStruct, timeT, utcFlag,
- microseconds);
- ++i;
- } else {
- result += c1;
- }
- }
- return result;
- }
- time_t cmTimestamp::CreateUtcTimeTFromTm(struct tm& tm) const
- {
- #if defined(_MSC_VER) && _MSC_VER >= 1400
- return _mkgmtime(&tm);
- #else
- // From Linux timegm() manpage.
- std::string tz_old;
- bool const tz_was_set = cmSystemTools::GetEnv("TZ", tz_old);
- tz_old = "TZ=" + tz_old;
- // The standard says that "TZ=" or "TZ=[UNRECOGNIZED_TZ]" means UTC.
- // It seems that "TZ=" does NOT work, at least under Windows
- // with neither MSVC nor MinGW, so let's use explicit "TZ=UTC"
- cmSystemTools::PutEnv("TZ=UTC");
- tzset();
- time_t result = mktime(&tm);
- # ifndef CMAKE_BOOTSTRAP
- if (tz_was_set) {
- cmSystemTools::PutEnv(tz_old);
- } else {
- cmSystemTools::UnsetEnv("TZ");
- }
- # else
- // No UnsetEnv during bootstrap. This is good enough for CMake itself.
- cmSystemTools::PutEnv(tz_old);
- static_cast<void>(tz_was_set);
- # endif
- tzset();
- return result;
- #endif
- }
- std::string cmTimestamp::AddTimestampComponent(
- char flag, struct tm& timeStruct, const time_t timeT, const bool utcFlag,
- const uint32_t microseconds) const
- {
- std::string formatString = cmStrCat('%', flag);
- switch (flag) {
- case 'a':
- case 'A':
- case 'b':
- case 'B':
- case 'd':
- case 'H':
- case 'I':
- case 'j':
- case 'm':
- case 'M':
- case 'S':
- case 'U':
- case 'V':
- case 'w':
- case 'y':
- case 'Y':
- case '%':
- break;
- case 'Z':
- #if defined(__GLIBC__)
- // 'struct tm' has the time zone, so strftime can honor UTC.
- static_cast<void>(utcFlag);
- #else
- // 'struct tm' may not have the time zone, so strftime may
- // use local time. Hard-code the UTC result.
- if (utcFlag) {
- return std::string("GMT");
- }
- #endif
- break;
- case 'z': {
- #if defined(__GLIBC__)
- // 'struct tm' has the time zone, so strftime can honor UTC.
- static_cast<void>(utcFlag);
- #else
- // 'struct tm' may not have the time zone, so strftime may
- // use local time. Hard-code the UTC result.
- if (utcFlag) {
- return std::string("+0000");
- }
- #endif
- #ifndef _AIX
- break;
- #else
- std::string xpg_sus_old;
- bool const xpg_sus_was_set =
- cmSystemTools::GetEnv("XPG_SUS_ENV", xpg_sus_old);
- if (xpg_sus_was_set && xpg_sus_old == "ON") {
- break;
- }
- xpg_sus_old = "XPG_SUS_ENV=" + xpg_sus_old;
- // On AIX systems, %z requires XPG_SUS_ENV=ON to work as desired.
- cmSystemTools::PutEnv("XPG_SUS_ENV=ON");
- tzset();
- char buffer[16];
- size_t size = strftime(buffer, sizeof(buffer), "%z", &timeStruct);
- # ifndef CMAKE_BOOTSTRAP
- if (xpg_sus_was_set) {
- cmSystemTools::PutEnv(xpg_sus_old);
- } else {
- cmSystemTools::UnsetEnv("XPG_SUS_ENV");
- }
- # else
- // No UnsetEnv during bootstrap. This is good enough for CMake itself.
- cmSystemTools::PutEnv(xpg_sus_old);
- static_cast<void>(xpg_sus_was_set);
- # endif
- tzset();
- return std::string(buffer, size);
- #endif
- }
- case 's': // Seconds since UNIX epoch (midnight 1-jan-1970)
- {
- // Build a time_t for UNIX epoch and subtract from the input "timeT":
- struct tm tmUnixEpoch;
- memset(&tmUnixEpoch, 0, sizeof(tmUnixEpoch));
- tmUnixEpoch.tm_mday = 1;
- tmUnixEpoch.tm_year = 1970 - 1900;
- const time_t unixEpoch = this->CreateUtcTimeTFromTm(tmUnixEpoch);
- if (unixEpoch == -1) {
- cmSystemTools::Error(
- "Error generating UNIX epoch in string(TIMESTAMP ...) or "
- "file(TIMESTAMP ...). Please, file a bug report against CMake");
- return std::string();
- }
- return std::to_string(static_cast<long int>(difftime(timeT, unixEpoch)));
- }
- case 'f': // microseconds
- {
- // clip number to 6 digits and pad with leading zeros
- std::string microsecs = std::to_string(microseconds % 1000000);
- return std::string(6 - microsecs.length(), '0') + microsecs;
- }
- default: {
- return formatString;
- }
- }
- char buffer[16];
- #ifdef __MINGW32__
- /* See a bug in MinGW: https://sourceforge.net/p/mingw-w64/bugs/793/. A work
- * around is to try to use strftime() from ucrtbase.dll. */
- using T = size_t(__cdecl*)(char*, size_t, const char*, const struct tm*);
- auto loadUcrtStrftime = []() -> T {
- auto handle =
- LoadLibraryExA("ucrtbase.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
- if (handle) {
- # pragma GCC diagnostic push
- # pragma GCC diagnostic ignored "-Wcast-function-type"
- return reinterpret_cast<T>(GetProcAddress(handle, "strftime"));
- # pragma GCC diagnostic pop
- }
- return nullptr;
- };
- static T ucrtStrftime = loadUcrtStrftime();
- if (ucrtStrftime) {
- size_t size =
- ucrtStrftime(buffer, sizeof(buffer), formatString.c_str(), &timeStruct);
- return std::string(buffer, size);
- }
- #endif
- size_t size =
- strftime(buffer, sizeof(buffer), formatString.c_str(), &timeStruct);
- return std::string(buffer, size);
- }
|