This brings the behaviour in line with the __DATE__ and __TIME__ macros. Note though the minor difference: __TIMESTAMP__ is defined as the file-modification date and not the "current" date or time like the other two macros. Therefore, we do a clamping behaviour (similar to tar --clamp-mtime).
Acknowledgements ---------------- Reiner Herrmann suggested the clamping behaviour. ChangeLogs ---------- libcpp/ChangeLog: 2016-11-01 Ximin Luo <infini...@pwned.gg> * macro.c (_cpp_builtin_macro_text): Use SOURCE_DATE_EPOCH in place of __TIMESTAMP__ if the latter is newer than the former. gcc/ChangeLog: 2016-11-01 Ximin Luo <infini...@pwned.gg> * doc/cppenv.texi (Environment Variables): Update SOURCE_DATE_EPOCH to describe the new effect on __TIMESTAMP__. gcc/testsuite/ChangeLog: 2016-11-01 Ximin Luo <infini...@pwned.gg> * gcc.dg/cpp/source_date_epoch-4.c: New test. * gcc.dg/cpp/source_date_epoch-5.c: New test. Index: gcc-7-20161030/libcpp/macro.c =================================================================== --- gcc-7-20161030.orig/libcpp/macro.c +++ gcc-7-20161030/libcpp/macro.c @@ -264,7 +264,30 @@ _cpp_builtin_macro_text (cpp_reader *pfi struct tm *tb = NULL; struct stat *st = _cpp_get_file_stat (file); if (st) - tb = localtime (&st->st_mtime); + { + /* Set a reproducible timestamp for __DATE__ and __TIME__ macro + if SOURCE_DATE_EPOCH is defined. */ + if (pfile->source_date_epoch == (time_t) -2 + && pfile->cb.get_source_date_epoch != NULL) + pfile->source_date_epoch = pfile->cb.get_source_date_epoch (pfile); + + if (pfile->source_date_epoch >= (time_t) 0) + { + /* If SOURCE_DATE_EPOCH is set, we want reproducible + timestamps so use gmtime not localtime. */ + if (st->st_mtime >= pfile->source_date_epoch) + tb = gmtime (&pfile->source_date_epoch); + else + /* Don't use SOURCE_DATE_EPOCH if the timestamp is + older, since that means it was probably already + set in a reproducible way (e.g. via source tarball + extraction or some other method). */ + tb = gmtime (&st->st_mtime); + } + else + tb = localtime (&st->st_mtime); + } + if (tb) { char *str = asctime (tb); Index: gcc-7-20161030/gcc/doc/cppenv.texi =================================================================== --- gcc-7-20161030.orig/gcc/doc/cppenv.texi +++ gcc-7-20161030/gcc/doc/cppenv.texi @@ -83,8 +83,9 @@ main input file is omitted. @item SOURCE_DATE_EPOCH If this variable is set, its value specifies a UNIX timestamp to be used in replacement of the current date and time in the @code{__DATE__} -and @code{__TIME__} macros, so that the embedded timestamps become -reproducible. +and @code{__TIME__} macros, and in replacement of the file modification +date in the @code{__TIMESTAMP__} macro if the latter is newer than the +former, so that the embedded timestamps become reproducible. The value of @env{SOURCE_DATE_EPOCH} must be a UNIX timestamp, defined as the number of seconds (excluding leap seconds) since Index: gcc-7-20161030/gcc/testsuite/gcc.dg/cpp/source_date_epoch-4.c =================================================================== --- /dev/null +++ gcc-7-20161030/gcc/testsuite/gcc.dg/cpp/source_date_epoch-4.c @@ -0,0 +1,13 @@ +/* __TIMESTAMP__ should use SOURCE_DATE_EPOCH if the latter is older. */ +/* { dg-do run } */ +/* { dg-set-compiler-env-var TZ "UTC" } */ +/* { dg-set-compiler-env-var LC_ALL "C" } */ +/* { dg-set-compiler-env-var SOURCE_DATE_EPOCH "0" } */ + +int +main () +{ + if (__builtin_strcmp (__TIMESTAMP__, "Thu Jan 1 00:00:00 1970") != 0) + __builtin_abort (); + return 0; +} Index: gcc-7-20161030/gcc/testsuite/gcc.dg/cpp/source_date_epoch-5.c =================================================================== --- /dev/null +++ gcc-7-20161030/gcc/testsuite/gcc.dg/cpp/source_date_epoch-5.c @@ -0,0 +1,13 @@ +/* __TIMESTAMP__ should not use SOURCE_DATE_EPOCH if the latter is newer. */ +/* { dg-do run } */ +/* { dg-set-compiler-env-var TZ "UTC" } */ +/* { dg-set-compiler-env-var LC_ALL "C" } */ +/* { dg-set-compiler-env-var SOURCE_DATE_EPOCH "253402300799" } */ + +int +main () +{ + if (__builtin_strcmp (__TIMESTAMP__, "Fri Dec 31 23:59:59 UTC 9999") == 0) + __builtin_abort (); + return 0; +}