From 0d17190cc821ee5ce1c1bf6ddafaeeebb2c3f463 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 3 Jan 2025 13:02:20 +0200 Subject: [PATCH 1/2] gh-123681: Check the strftime() behavior at runtime instead of at the compile time It is needed to support cross-compiling. Remove macros Py_NORMALIZE_CENTURY and Py_STRFTIME_C99_SUPPORT. --- ...-01-03-13-02-06.gh-issue-123681.gQ67nK.rst | 3 + Modules/_datetimemodule.c | 50 +++++++-- configure | 104 ------------------ configure.ac | 56 ---------- pyconfig.h.in | 6 - 5 files changed, 42 insertions(+), 177 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst diff --git a/Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst b/Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst new file mode 100644 index 00000000000000..8f4f1fb4fabbe5 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2025-01-03-13-02-06.gh-issue-123681.gQ67nK.rst @@ -0,0 +1,3 @@ +Check the ``strftime()`` behavior at runtime instead of at the compile time +to support cross-compiling. Remove macros Py_NORMALIZE_CENTURY and +Py_STRFTIME_C99_SUPPORT. diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index b1102984cb5e9e..04b256edd88498 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1740,6 +1740,42 @@ format_utcoffset(char *buf, size_t buflen, const char *sep, return 0; } +/* Check whether year with century should be normalized for strftime. */ +inline static int +normalize_century(void) +{ + static int _normalize_century = -1; + if (_normalize_century < 0) { + char year[5]; + struct tm date = { + .tm_year = -1801, + .tm_mon = 0, + .tm_mday = 1 + }; + _normalize_century = (strftime(year, sizeof(year), "%Y", &date) && + strcmp(year, "0099") != 0); + } + return _normalize_century; +} + +/* Check whether C99-specific strftime specifiers are supported. */ +inline static int +strftime_c99_support(void) +{ + static int _strftime_c99_support = -1; + if (_strftime_c99_support < 0) { + char full_date[11]; + struct tm date = { + .tm_year = 0, + .tm_mon = 0, + .tm_mday = 1 + }; + _strftime_c99_support = (strftime(full_date, sizeof(full_date), "%F", &date) && + strcmp(full_date, "1900-01-01") == 0); + } + return _strftime_c99_support; +} + static PyObject * make_somezreplacement(PyObject *object, char *sep, PyObject *tzinfoarg) { @@ -1910,12 +1946,9 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } replacement = freplacement; } -#ifdef Py_NORMALIZE_CENTURY - else if (ch == 'Y' || ch == 'G' -#ifdef Py_STRFTIME_C99_SUPPORT - || ch == 'F' || ch == 'C' -#endif - ) { + else if (normalize_century() && (ch == 'Y' || ch == 'G' || + (strftime_c99_support() && (ch == 'F' || ch == 'C')))) + { /* 0-pad year with century as necessary */ PyObject *item = PySequence_GetItem(timetuple, 0); if (item == NULL) { @@ -1952,15 +1985,11 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, * +6 to accommodate dashes, 2-digit month and day for %F. */ char buf[SIZEOF_LONG * 5 / 2 + 2 + 6]; Py_ssize_t n = PyOS_snprintf(buf, sizeof(buf), -#ifdef Py_STRFTIME_C99_SUPPORT ch == 'F' ? "%04ld-%%m-%%d" : -#endif "%04ld", year_long); -#ifdef Py_STRFTIME_C99_SUPPORT if (ch == 'C') { n -= 2; } -#endif if (_PyUnicodeWriter_WriteSubstring(&writer, format, start, end) < 0) { goto Error; } @@ -1970,7 +1999,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, } continue; } -#endif else { /* percent followed by something else */ continue; diff --git a/configure b/configure index 3d2c60213db591..9f67c9ead085dd 100755 --- a/configure +++ b/configure @@ -26384,110 +26384,6 @@ printf "%s\n" "#define HAVE_STAT_TV_NSEC2 1" >>confdefs.h fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether year with century should be normalized for strftime" >&5 -printf %s "checking whether year with century should be normalized for strftime... " >&6; } -if test ${ac_cv_normalize_century+y} -then : - printf %s "(cached) " >&6 -else $as_nop - -if test "$cross_compiling" = yes -then : - ac_cv_normalize_century=yes -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_normalize_century=yes -else $as_nop - ac_cv_normalize_century=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_normalize_century" >&5 -printf "%s\n" "$ac_cv_normalize_century" >&6; } -if test "$ac_cv_normalize_century" = yes -then - -printf "%s\n" "#define Py_NORMALIZE_CENTURY 1" >>confdefs.h - -fi - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C99-specific strftime specifiers are supported" >&5 -printf %s "checking whether C99-specific strftime specifiers are supported... " >&6; } -if test ${ac_cv_strftime_c99_support+y} -then : - printf %s "(cached) " >&6 -else $as_nop - -if test "$cross_compiling" = yes -then : - ac_cv_strftime_c99_support=no -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include -#include - -int main(void) -{ - char full_date[11]; - struct tm date = { - .tm_year = 0, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) { - return 0; - } - return 1; -} - -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_strftime_c99_support=yes -else $as_nop - ac_cv_strftime_c99_support=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_strftime_c99_support" >&5 -printf "%s\n" "$ac_cv_strftime_c99_support" >&6; } -if test "$ac_cv_strftime_c99_support" = yes -then - -printf "%s\n" "#define Py_STRFTIME_C99_SUPPORT 1" >>confdefs.h - -fi - have_curses=no have_panel=no diff --git a/configure.ac b/configure.ac index ee034e5a9621df..7edc7caa0e3d4c 100644 --- a/configure.ac +++ b/configure.ac @@ -6644,62 +6644,6 @@ then [Define if you have struct stat.st_mtimensec]) fi -AC_CACHE_CHECK([whether year with century should be normalized for strftime], [ac_cv_normalize_century], [ -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include - -int main(void) -{ - char year[5]; - struct tm date = { - .tm_year = -1801, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { - return 1; - } - return 0; -} -]])], -[ac_cv_normalize_century=yes], -[ac_cv_normalize_century=no], -[ac_cv_normalize_century=yes])]) -if test "$ac_cv_normalize_century" = yes -then - AC_DEFINE([Py_NORMALIZE_CENTURY], [1], - [Define if year with century should be normalized for strftime.]) -fi - -AC_CACHE_CHECK([whether C99-specific strftime specifiers are supported], [ac_cv_strftime_c99_support], [ -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include - -int main(void) -{ - char full_date[11]; - struct tm date = { - .tm_year = 0, - .tm_mon = 0, - .tm_mday = 1 - }; - if (strftime(full_date, sizeof(full_date), "%F", &date) && !strcmp(full_date, "1900-01-01")) { - return 0; - } - return 1; -} -]])], -[ac_cv_strftime_c99_support=yes], -[ac_cv_strftime_c99_support=no], -[ac_cv_strftime_c99_support=no])]) -if test "$ac_cv_strftime_c99_support" = yes -then - AC_DEFINE([Py_STRFTIME_C99_SUPPORT], [1], - [Define if C99-specific strftime specifiers are supported.]) -fi - dnl check for ncursesw/ncurses and panelw/panel dnl NOTE: old curses is not detected. dnl have_curses=[no, yes] diff --git a/pyconfig.h.in b/pyconfig.h.in index 1ca83fd2f2ca1b..6edd4a5678484f 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1706,18 +1706,12 @@ /* Defined if _Complex C type is available. */ #undef Py_HAVE_C_COMPLEX -/* Define if year with century should be normalized for strftime. */ -#undef Py_NORMALIZE_CENTURY - /* Define if rl_startup_hook takes arguments */ #undef Py_RL_STARTUP_HOOK_TAKES_ARGS /* Define if you want to enable internal statistics gathering. */ #undef Py_STATS -/* Define if C99-specific strftime specifiers are supported. */ -#undef Py_STRFTIME_C99_SUPPORT - /* The version of SunOS/Solaris as reported by `uname -r' without the dot. */ #undef Py_SUNOS_VERSION From 26441f90997b6442d7a9325cc2987161a83b753c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 8 Apr 2025 15:14:42 +0300 Subject: [PATCH 2/2] Add static variables to ignored.tsv. --- Modules/_datetimemodule.c | 20 ++++++++++---------- Tools/c-analyzer/cpython/ignored.tsv | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index bef98d484c72c1..c808d36b791d59 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1761,36 +1761,36 @@ format_utcoffset(char *buf, size_t buflen, const char *sep, inline static int normalize_century(void) { - static int _normalize_century = -1; - if (_normalize_century < 0) { + static int cache = -1; + if (cache < 0) { char year[5]; struct tm date = { .tm_year = -1801, .tm_mon = 0, .tm_mday = 1 }; - _normalize_century = (strftime(year, sizeof(year), "%Y", &date) && - strcmp(year, "0099") != 0); + cache = (strftime(year, sizeof(year), "%Y", &date) && + strcmp(year, "0099") != 0); } - return _normalize_century; + return cache; } /* Check whether C99-specific strftime specifiers are supported. */ inline static int strftime_c99_support(void) { - static int _strftime_c99_support = -1; - if (_strftime_c99_support < 0) { + static int cache = -1; + if (cache < 0) { char full_date[11]; struct tm date = { .tm_year = 0, .tm_mon = 0, .tm_mday = 1 }; - _strftime_c99_support = (strftime(full_date, sizeof(full_date), "%F", &date) && - strcmp(full_date, "1900-01-01") == 0); + cache = (strftime(full_date, sizeof(full_date), "%F", &date) && + strcmp(full_date, "1900-01-01") == 0); } - return _strftime_c99_support; + return cache; } static PyObject * diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 14dc5007b65861..28a83c8c41fe45 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -224,6 +224,8 @@ Modules/_datetimemodule.c datetime_isoformat specs - Modules/_datetimemodule.c parse_hh_mm_ss_ff correction - Modules/_datetimemodule.c time_isoformat specs - Modules/_datetimemodule.c - capi_types - +Modules/_datetimemodule.c normalize_century cache - +Modules/_datetimemodule.c strftime_c99_support cache - Modules/_decimal/_decimal.c - cond_map_template - Modules/_decimal/_decimal.c - dec_signal_string - Modules/_decimal/_decimal.c - dflt_ctx -