Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -785,6 +785,9 @@ def fexec_charset_EQ : Joined<["-"], "fexec-charset=">, Group; def finstrument_functions : Flag<["-"], "finstrument-functions">, Group, Flags<[CC1Option]>, HelpText<"Generate calls to instrument function entry and exit">; +def ffixed_date_time_EQ : Joined<["-"], "ffixed-date-time=">, Group, + Flags<[CC1Option, CoreOption]>, + HelpText<"Set a fixed value for __DATE__, __TIME__, __TIMESTAMP__, formatted as 1969-12-31T23:59:59 (RFC3339 without fractional seconds and timezone)">; def fxray_instrument : Flag<["-"], "fxray-instrument">, Group, Flags<[CC1Option]>, Index: include/clang/Lex/PreprocessorOptions.h =================================================================== --- include/clang/Lex/PreprocessorOptions.h +++ include/clang/Lex/PreprocessorOptions.h @@ -12,11 +12,13 @@ #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include #include #include +#include #include #include @@ -138,6 +140,9 @@ /// build it again. IntrusiveRefCntPtr FailedModules; + /// If set, a fixed time that __DATE__, __TIME__, __TIMESTAMP__ expand to. + llvm::Optional FixedDateTime; + public: PreprocessorOptions() : UsePredefines(true), DetailedRecord(false), DisablePCHValidation(false), Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -4681,6 +4681,11 @@ CmdArgs.push_back("-generate-type-units"); } + if (Arg *A = Args.getLastArg(options::OPT_ffixed_date_time_EQ)) { + StringRef DateTime = A->getValue(); + CmdArgs.push_back(Args.MakeArgString("-ffixed-date-time=" + DateTime)); + } + // CloudABI and WebAssembly use -ffunction-sections and -fdata-sections by // default. bool UseSeparateSections = Triple.getOS() == llvm::Triple::CloudABI || Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -44,6 +44,7 @@ #include "llvm/Target/TargetOptions.h" #include "llvm/Support/ScopedPrinter.h" #include +#include #include #include #include @@ -2237,6 +2238,21 @@ else Opts.ObjCXXARCStandardLibrary = (ObjCXXARCStandardLibraryKind)Library; } + + if (Arg *A = Args.getLastArg(OPT_ffixed_date_time_EQ)) { + StringRef DateTime = A->getValue(); + tm TM = {}; + char *Rest = strptime(DateTime.str().c_str(), "%Y-%m-%dT%T", &TM); + if (!Rest || *Rest) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) + << DateTime; + else { + // Fill in tm_wday. + TM.tm_isdst = -1; + mktime(&TM); + Opts.FixedDateTime = TM; + } + } } static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -21,6 +21,7 @@ #include "clang/Lex/MacroArgs.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -1012,8 +1013,14 @@ /// the identifier tokens inserted. static void ComputeDATE_TIME(SourceLocation &DATELoc, SourceLocation &TIMELoc, Preprocessor &PP) { - time_t TT = time(nullptr); - struct tm *TM = localtime(&TT); + struct tm *TM; + PreprocessorOptions &PO = PP.getPreprocessorOpts(); + if (PO.FixedDateTime.hasValue()) + TM = PO.FixedDateTime.getPointer(); + else { + time_t TT = time(nullptr); + TM = localtime(&TT); + } static const char * const Months[] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" @@ -1644,21 +1651,26 @@ // 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); - Result = asctime(TM); + PreprocessorOptions &PO = getPreprocessorOpts(); + if (PO.FixedDateTime.hasValue()) { + Result = asctime(PO.FixedDateTime.getPointer()); } 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; + PreprocessorLexer *TheLexer = getCurrentFileLexer(); + + if (TheLexer) + 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() << '"'; Index: test/Preprocessor/fixed-date-time.cc =================================================================== --- test/Preprocessor/fixed-date-time.cc +++ test/Preprocessor/fixed-date-time.cc @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -ffixed-date-time=2008-09-02T14:30:27 -std=c++11 %s -verify +// RUN: not %clang_cc1 -ffixed-date-time=2008-09-02T14:30:27asdf -std=c++11 %s 2>&1 | FileCheck %s +// expected-no-diagnostics + +// CHECK: error: invalid value '2008-09-02T14:30:27asdf' in '-ffixed-date-time=2008-09-02T14:30:27asdf' + +constexpr int constexpr_strcmp(const char *p, const char *q) { + return *p != *q ? *p - *q : !*p ? 0 : constexpr_strcmp(p + 1, q + 1); +} + +static_assert(!constexpr_strcmp(__DATE__, "Sep 2 2008"), ""); +static_assert(!constexpr_strcmp(__TIME__, "14:30:27"), ""); +static_assert(!constexpr_strcmp(__TIMESTAMP__, "Tue Sep 2 14:30:27 2008"), "");