diff options
author | Peter Würth <wuerth.peter@freenet.de> | 2022-01-27 14:20:15 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2022-01-28 11:23:57 (GMT) |
commit | c050d6a01e14500903619589e69a973f1215d109 (patch) | |
tree | d78eb4dec319610788d4518d2fe3225e889b9b7c /Source/cmTimestamp.cxx | |
parent | 44939f01e761568ab9fe511d2ddf81a48c7c54b9 (diff) | |
download | CMake-c050d6a01e14500903619589e69a973f1215d109.zip CMake-c050d6a01e14500903619589e69a973f1215d109.tar.gz CMake-c050d6a01e14500903619589e69a973f1215d109.tar.bz2 |
string(TIMESTAMP): add %f specifier for microseconds
The %f specified extends the string(TIMESTAMP) and file(TIMESTAMP)
commands to output the timestamp with a microsecond resolution.
This convention is offered by python's datetime module.
Before, the precision was limited to seconds.
The implementation is done by extending existing cmTimestamp methods
with a `microseconds` parameter. This parameter is optional in order to
be backwards compatible. The timestamps are now received in a
cross-platform manner using libuv, since the standard C functions like
time() don't allow for sub-second precision.
This requires libuv 1.28 or higher. We already require higher than
that on Windows, so update the required version for other platforms.
Implements: #19335
Diffstat (limited to 'Source/cmTimestamp.cxx')
-rw-r--r-- | Source/cmTimestamp.cxx | 60 |
1 files changed, 50 insertions, 10 deletions
diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx index 3826577..e2b6c20 100644 --- a/Source/cmTimestamp.cxx +++ b/Source/cmTimestamp.cxx @@ -17,18 +17,27 @@ #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 { - time_t currentTimeT = time(nullptr); + // 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()) { @@ -38,12 +47,15 @@ std::string cmTimestamp::CurrentTime(const std::string& formatString, 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 == time_t(-1)) { return std::string(); } - return this->CreateTimestampFromTimeT(currentTimeT, formatString, utcFlag); + return this->CreateTimestampFromTimeT(currentTimeT, microseconds, + formatString, utcFlag); } std::string cmTimestamp::FileModificationTime(const char* path, @@ -57,11 +69,32 @@ std::string cmTimestamp::FileModificationTime(const char* path, return std::string(); } - time_t mtime = cmsys::SystemTools::ModifiedTime(real_path); - return this->CreateTimestampFromTimeT(mtime, formatString, utcFlag); + // 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 { @@ -95,7 +128,8 @@ std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT, : static_cast<char>(0); if (c1 == '%' && c2 != 0) { - result += this->AddTimestampComponent(c2, timeStruct, timeT); + result += + this->AddTimestampComponent(c2, timeStruct, timeT, microseconds); ++i; } else { result += c1; @@ -144,9 +178,9 @@ time_t cmTimestamp::CreateUtcTimeTFromTm(struct tm& tm) const #endif } -std::string cmTimestamp::AddTimestampComponent(char flag, - struct tm& timeStruct, - const time_t timeT) const +std::string cmTimestamp::AddTimestampComponent( + char flag, struct tm& timeStruct, const time_t timeT, + const uint32_t microseconds) const { std::string formatString = cmStrCat('%', flag); @@ -180,13 +214,19 @@ std::string cmTimestamp::AddTimestampComponent(char flag, const time_t unixEpoch = this->CreateUtcTimeTFromTm(tmUnixEpoch); if (unixEpoch == -1) { cmSystemTools::Error( - "Error generating UNIX epoch in " - "STRING(TIMESTAMP ...). Please, file a bug report against CMake"); + "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; } |