#include <cassert>
#include <cstddef>
#include <cstring>
+#include <ctime>
#include <fstream>
+#include <limits>
#include <memory>
#include <string>
#include <tuple>
Opts.addRemappedFile(Split.first, Split.second);
}
+ if (const char *Epoch = std::getenv("SOURCE_DATE_EPOCH")) {
+ // SOURCE_DATE_EPOCH, if specified, must be a non-negative decimal integer.
+ // On time64 systems, pick 253402300799 (the UNIX timestamp of
+ // 9999-12-31T23:59:59Z) as the upper bound.
+ const uint64_t MaxTimestamp =
+ std::min<uint64_t>(std::numeric_limits<time_t>::max(), 253402300799);
+ uint64_t V;
+ if (StringRef(Epoch).getAsInteger(10, V) || V > MaxTimestamp) {
+ Diags.Report(diag::err_fe_invalid_source_date_epoch)
+ << Epoch << MaxTimestamp;
+ } else {
+ Opts.SourceDateEpoch = V;
+ }
+ }
+
// Always avoid lexing editor placeholders when we're just running the
// preprocessor as we never want to emit the
// "editor placeholder in source file" error in PP only mode.
/// the identifier tokens inserted.
static void ComputeDATE_TIME(SourceLocation &DATELoc, SourceLocation &TIMELoc,
Preprocessor &PP) {
- time_t TT = time(nullptr);
- struct tm *TM = localtime(&TT);
+ time_t TT;
+ std::tm *TM;
+ if (PP.getPreprocessorOpts().SourceDateEpoch) {
+ TT = *PP.getPreprocessorOpts().SourceDateEpoch;
+ TM = std::gmtime(&TT);
+ } else {
+ TT = std::time(nullptr);
+ TM = std::localtime(&TT);
+ }
static const char * const Months[] = {
"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
{
SmallString<32> TmpBuffer;
llvm::raw_svector_ostream TmpStream(TmpBuffer);
- TmpStream << llvm::format("\"%s %2d %4d\"", Months[TM->tm_mon],
- TM->tm_mday, TM->tm_year + 1900);
+ if (TM)
+ TmpStream << llvm::format("\"%s %2d %4d\"", Months[TM->tm_mon],
+ TM->tm_mday, TM->tm_year + 1900);
+ else
+ TmpStream << "??? ?? ????";
Token TmpTok;
TmpTok.startToken();
PP.CreateString(TmpStream.str(), TmpTok);
{
SmallString<32> TmpBuffer;
llvm::raw_svector_ostream TmpStream(TmpBuffer);
- TmpStream << llvm::format("\"%02d:%02d:%02d\"",
- TM->tm_hour, TM->tm_min, TM->tm_sec);
+ if (TM)
+ TmpStream << llvm::format("\"%02d:%02d:%02d\"", TM->tm_hour, TM->tm_min,
+ TM->tm_sec);
+ else
+ TmpStream << "??:??:??";
Token TmpTok;
TmpTok.startToken();
PP.CreateString(TmpStream.str(), TmpTok);
Diag(Tok.getLocation(), diag::warn_pp_date_time);
// MSVC, ICC, GCC, VisualAge C++ extension. The generated string should be
// of the form "Ddd Mmm dd hh::mm::ss yyyy", which is returned by asctime.
-
- // Get the file that we are lexing out of. If we're currently lexing from
- // a macro, dig into the include stack.
- const FileEntry *CurFile = nullptr;
- PreprocessorLexer *TheLexer = getCurrentFileLexer();
-
- if (TheLexer)
- CurFile = SourceMgr.getFileEntryForID(TheLexer->getFileID());
-
const char *Result;
- if (CurFile) {
- time_t TT = CurFile->getModificationTime();
- struct tm *TM = localtime(&TT);
+ if (getPreprocessorOpts().SourceDateEpoch) {
+ time_t TT = *getPreprocessorOpts().SourceDateEpoch;
+ std::tm *TM = std::gmtime(&TT);
Result = asctime(TM);
} else {
- Result = "??? ??? ?? ??:??:?? ????\n";
+ // Get the file that we are lexing out of. If we're currently lexing from
+ // a macro, dig into the include stack.
+ const FileEntry *CurFile = nullptr;
+ if (PreprocessorLexer *TheLexer = getCurrentFileLexer())
+ CurFile = SourceMgr.getFileEntryForID(TheLexer->getFileID());
+ if (CurFile) {
+ time_t TT = CurFile->getModificationTime();
+ struct tm *TM = localtime(&TT);
+ Result = asctime(TM);
+ } else {
+ Result = "??? ??? ?? ??:??:?? ????\n";
+ }
}
// Surround the string with " and strip the trailing newline.
OS << '"' << StringRef(Result).drop_back() << '"';
--- /dev/null
+// RUN: env SOURCE_DATE_EPOCH=0 %clang_cc1 -E %s | FileCheck %s --check-prefix=19700101
+
+// 19700101: const char date[] = "Jan 1 1970";
+// 19700101-NEXT: const char time[] = "00:00:00";
+// 19700101-NEXT: const char timestamp[] = "Thu Jan 1 00:00:00 1970";
+
+// RUN: env SOURCE_DATE_EPOCH=2147483647 %clang_cc1 -E -Wdate-time %s 2>&1 | FileCheck %s --check-prefix=Y2038
+
+// Y2038: warning: expansion of date or time macro is not reproducible [-Wdate-time]
+// Y2038: const char date[] = "Jan 19 2038";
+// Y2038-NEXT: const char time[] = "03:14:07";
+// Y2038-NEXT: const char timestamp[] = "Tue Jan 19 03:14:07 2038";
+
+/// Test a large timestamp if the system uses 64-bit time_t and known to support large timestamps.
+// RUN: %if !system-windows && clang-target-64-bits %{ env SOURCE_DATE_EPOCH=253402300799 %clang_cc1 -E -Wdate-time %s 2>&1 | FileCheck %s --check-prefix=99991231 %}
+
+// 99991231: warning: expansion of date or time macro is not reproducible [-Wdate-time]
+// 99991231: const char date[] = "Dec 31 9999";
+// 99991231-NEXT: const char time[] = "23:59:59";
+// 99991231-NEXT: const char timestamp[] = "Fri Dec 31 23:59:59 9999";
+
+// RUN: env SOURCE_DATE_EPOCH=253402300800 not %clang_cc1 -E %s 2>&1 | FileCheck %s --check-prefix=TOOBIG
+
+// TOOBIG: error: environment variable 'SOURCE_DATE_EPOCH' ('253402300800') must be a non-negative decimal integer <= {{(2147483647|253402300799)}}
+
+// RUN: env SOURCE_DATE_EPOCH=0x0 not %clang_cc1 -E %s 2>&1 | FileCheck %s --check-prefix=NOTDECIMAL
+
+// NOTDECIMAL: error: environment variable 'SOURCE_DATE_EPOCH' ('0x0') must be a non-negative decimal integer <= {{(2147483647|253402300799)}}
+
+const char date[] = __DATE__;
+const char time[] = __TIME__;
+const char timestamp[] = __TIMESTAMP__;