From 4b5f564a5d958295d51a1a7ff825896a89f22b75 Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Fri, 6 Nov 2020 08:53:31 -0800 Subject: [PATCH] libcpp: Provide date routine Joseph pointed me at cb_get_source_date_epoch, which allows repeatable builds and solves a FIXME I had on the modules branch. Unfortunately it's used exclusively to generate __DATE__ and __TIME__ values, which fallback to using a time(2) call. It'd be nicer if the preprocessor made whatever time value it determined available to the rest of the compiler. So this patch adds a new cpp_get_date function, which abstracts the call to the get_source_date_epoch hook, or uses time directly. The value is cached. Thus the timestamp I end up putting on CMI files matches __DATE__ and __TIME__ expansions. That seems worthwhile. libcpp/ * include/cpplib.h (enum class CPP_time_kind): New. (cpp_get_date): Declare. * internal.h (struct cpp_reader): Replace source_date_epoch with time_stamp and time_stamp_kind. * init.c (cpp_create_reader): Initialize them. * macro.c (_cpp_builtin_macro_text): Use cpp_get_date. (cpp_get_date): Broken out from _cpp_builtin_macro_text and genericized. --- libcpp/include/cpplib.h | 9 +++++ libcpp/init.c | 5 ++- libcpp/internal.h | 7 ++-- libcpp/macro.c | 83 +++++++++++++++++++++++++++-------------- 4 files changed, 71 insertions(+), 33 deletions(-) diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index 8e398863cf6..c4d7cc520d1 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -1040,6 +1040,15 @@ inline location_t cpp_macro_definition_location (cpp_hashnode *node) { return node->value.macro->line; } +/* Return an idempotent time stamp (possibly from SOURCE_DATE_EPOCH). */ +enum class CPP_time_kind +{ + FIXED = -1, /* Fixed time via source epoch. */ + DYNAMIC = -2, /* Dynamic via time(2). */ + UNKNOWN = -3 /* Wibbly wobbly, timey wimey. */ +}; +extern CPP_time_kind cpp_get_date (cpp_reader *, time_t *); + extern void _cpp_backup_tokens (cpp_reader *, unsigned int); extern const cpp_token *cpp_peek_token (cpp_reader *, int); diff --git a/libcpp/init.c b/libcpp/init.c index 6c52f50de39..dcf1d4be587 100644 --- a/libcpp/init.c +++ b/libcpp/init.c @@ -273,8 +273,9 @@ cpp_create_reader (enum c_lang lang, cpp_hash_table *table, /* Do not force token locations by default. */ pfile->forced_token_location = 0; - /* Initialize source_date_epoch to -2 (not yet set). */ - pfile->source_date_epoch = (time_t) -2; + /* Note the timestamp is unset. */ + pfile->time_stamp = time_t (-1); + pfile->time_stamp_kind = 0; /* The expression parser stack. */ _cpp_expand_op_stack (pfile); diff --git a/libcpp/internal.h b/libcpp/internal.h index 4759961a33a..d7780e49d27 100644 --- a/libcpp/internal.h +++ b/libcpp/internal.h @@ -512,10 +512,9 @@ struct cpp_reader const unsigned char *date; const unsigned char *time; - /* Externally set timestamp to replace current date and time useful for - reproducibility. It should be initialized to -2 (not yet set) and - set to -1 to disable it or to a non-negative value to enable it. */ - time_t source_date_epoch; + /* Time stamp, set idempotently lazily. */ + time_t time_stamp; + int time_stamp_kind; /* Or errno. */ /* A token forcing paste avoidance, and one demarking macro arguments. */ cpp_token avoid_paste; diff --git a/libcpp/macro.c b/libcpp/macro.c index e304f67c2e0..e2cb89e4c43 100644 --- a/libcpp/macro.c +++ b/libcpp/macro.c @@ -606,29 +606,21 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node, at init time, because time() and localtime() are very slow on some systems. */ time_t tt; - struct tm *tb = NULL; + auto kind = cpp_get_date (pfile, &tt); - /* 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) - tb = gmtime (&pfile->source_date_epoch); - else + if (kind == CPP_time_kind::UNKNOWN) { - /* (time_t) -1 is a legitimate value for "number of seconds - since the Epoch", so we have to do a little dance to - distinguish that from a genuine error. */ - errno = 0; - tt = time (NULL); - if (tt != (time_t)-1 || errno == 0) - tb = localtime (&tt); + cpp_errno (pfile, CPP_DL_WARNING, + "could not determine date and time"); + + pfile->date = UC"\"??? ?? ????\""; + pfile->time = UC"\"??:??:??\""; } - - if (tb) + else { + struct tm *tb = (kind == CPP_time_kind::FIXED + ? gmtime : localtime) (&tt); + pfile->date = _cpp_unaligned_alloc (pfile, sizeof ("\"Oct 11 1347\"")); sprintf ((char *) pfile->date, "\"%s %2d %4d\"", @@ -640,14 +632,6 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node, sprintf ((char *) pfile->time, "\"%02d:%02d:%02d\"", tb->tm_hour, tb->tm_min, tb->tm_sec); } - else - { - cpp_errno (pfile, CPP_DL_WARNING, - "could not determine date and time"); - - pfile->date = UC"\"??? ?? ????\""; - pfile->time = UC"\"??:??:??\""; - } } if (node->value.builtin == BT_DATE) @@ -688,6 +672,51 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node, return result; } +/* Get an idempotent date. Either the cached value, the value from + source epoch, or failing that, the value from time(2). Use this + during compilation so that every time stamp is the same. */ +CPP_time_kind +cpp_get_date (cpp_reader *pfile, time_t *result) +{ + if (!pfile->time_stamp_kind) + { + int kind = 0; + if (pfile->cb.get_source_date_epoch) + { + /* Try reading the fixed epoch. */ + pfile->time_stamp = pfile->cb.get_source_date_epoch (pfile); + if (pfile->time_stamp != time_t (-1)) + kind = int (CPP_time_kind::FIXED); + } + + if (!kind) + { + /* Pedantically time_t (-1) is a legitimate value for + "number of seconds since the Epoch". It is a silly + time. */ + errno = 0; + pfile->time_stamp = time (nullptr); + /* Annoyingly a library could legally set errno and return a + valid time! Bad library! */ + if (pfile->time_stamp == time_t (-1) && errno) + kind = errno; + else + kind = int (CPP_time_kind::DYNAMIC); + } + + pfile->time_stamp_kind = kind; + } + + *result = pfile->time_stamp; + if (pfile->time_stamp_kind >= 0) + { + errno = pfile->time_stamp_kind; + return CPP_time_kind::UNKNOWN; + } + + return CPP_time_kind (pfile->time_stamp_kind); +} + /* Convert builtin macros like __FILE__ to a token and push it on the context stack. Also handles _Pragma, for which a new token may not be created. Returns 1 if it generates a new token context, 0 to -- 2.30.2