[3.13] gh-113565: Improve and harden detection of curses dependencies (GH-119816) (#121202)

1. Use pkg-config to check for ncursesw/panelw. If that fails, use
   pkg-config to check for ncurses/panel.
2. Regardless of pkg-config output, search for curses/panel headers, so
   we're sure we have all defines in pyconfig.h.
3. Regardless of pkg-config output, check if libncurses or libncursesw
   contains the 'initscr' symbol; if it does _and_ pkg-config failed
   earlier, add the resulting -llib linker option to CURSES_LIBS.
   Ditto for 'update_panels' and PANEL_LIBS.
4. Wrap the rest of the checks with WITH_SAVE_ENV and make sure we're
   using updated LIBS and CPPFLAGS for those.

Add the PY_CHECK_CURSES convenience macro.
(cherry picked from commit f80376b129)

Co-authored-by: Erlend E. Aasland <erlend@python.org>
This commit is contained in:
Miss Islington (bot) 2024-07-01 10:35:38 +02:00 committed by GitHub
parent d481d4b767
commit 82777cd024
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 630 additions and 796 deletions

View File

@ -36,13 +36,21 @@
#define NCURSES_OPAQUE 0 #define NCURSES_OPAQUE 0
#endif #endif
#ifdef HAVE_NCURSES_H #if defined(HAVE_NCURSESW_NCURSES_H)
#include <ncurses.h> # include <ncursesw/ncurses.h>
#else #elif defined(HAVE_NCURSESW_CURSES_H)
#include <curses.h> # include <ncursesw/curses.h>
#elif defined(HAVE_NCURSES_NCURSES_H)
# include <ncurses/ncurses.h>
#elif defined(HAVE_NCURSES_CURSES_H)
# include <ncurses/curses.h>
#elif defined(HAVE_NCURSES_H)
# include <ncurses.h>
#elif defined(HAVE_CURSES_H)
# include <curses.h>
#endif #endif
#ifdef HAVE_NCURSES_H #ifdef NCURSES_VERSION
/* configure was checking <curses.h>, but we will /* configure was checking <curses.h>, but we will
use <ncurses.h>, which has some or all these features. */ use <ncurses.h>, which has some or all these features. */
#if !defined(WINDOW_HAS_FLAGS) && \ #if !defined(WINDOW_HAS_FLAGS) && \

View File

@ -0,0 +1,2 @@
Improve :mod:`curses` and :mod:`curses.panel` dependency checks in
:program:`configure`.

View File

@ -19,7 +19,13 @@ static const char PyCursesVersion[] = "2.1";
#include "py_curses.h" #include "py_curses.h"
#include <panel.h> #if defined(HAVE_NCURSESW_PANEL_H)
# include <ncursesw/panel.h>
#elif defined(HAVE_NCURSES_PANEL_H)
# include <ncurses/panel.h>
#elif defined(HAVE_PANEL_H)
# include <panel.h>
#endif
typedef struct { typedef struct {
PyObject *PyCursesError; PyObject *PyCursesError;

View File

@ -128,7 +128,7 @@ static const char PyCursesVersion[] = "2.2";
#include <langinfo.h> #include <langinfo.h>
#endif #endif
#if !defined(HAVE_NCURSES_H) && (defined(sgi) || defined(__sun) || defined(SCO5)) #if !defined(NCURSES_VERSION) && (defined(sgi) || defined(__sun) || defined(SCO5))
#define STRICT_SYSV_CURSES /* Don't use ncurses extensions */ #define STRICT_SYSV_CURSES /* Don't use ncurses extensions */
typedef chtype attr_t; /* No attr_t type is available */ typedef chtype attr_t; /* No attr_t type is available */
#endif #endif

1170
configure generated vendored

File diff suppressed because it is too large Load Diff

View File

@ -6653,55 +6653,88 @@ then
[Define if year with century should be normalized for strftime.]) [Define if year with century should be normalized for strftime.])
fi fi
dnl check for ncurses/ncursesw and panel/panelw dnl check for ncursesw/ncurses and panelw/panel
dnl NOTE: old curses is not detected. dnl NOTE: old curses is not detected.
dnl have_curses=[no, ncursesw, ncurses] dnl have_curses=[no, yes]
dnl have_panel=[no, panelw, panel] dnl have_panel=[no, yes]
have_curses=no have_curses=no
have_panel=no have_panel=no
AH_TEMPLATE([HAVE_NCURSESW], [Define to 1 if you have the `ncursesw' library.]) dnl PY_CHECK_CURSES(LIBCURSES, LIBPANEL)
AC_CHECK_HEADERS([curses.h ncurses.h]) dnl Sets 'have_curses' and 'have_panel'.
dnl For the PKG_CHECK_MODULES() calls, we can safely reuse the first variable
dnl here, since we're only calling the macro a second time if the first call
dnl fails.
AC_DEFUN([PY_CHECK_CURSES], [dnl
AS_VAR_PUSHDEF([curses_var], [m4_toupper([$1])])
AS_VAR_PUSHDEF([panel_var], [m4_toupper([$2])])
PKG_CHECK_MODULES([CURSES], [$1],
[AC_DEFINE([HAVE_]curses_var, [1], [Define if you have the '$1' library])
AS_VAR_SET([have_curses], [yes])
PKG_CHECK_MODULES([PANEL], [$2],
[AC_DEFINE([HAVE_]panel_var, [1], [Define if you have the '$2' library])
AS_VAR_SET([have_panel], [yes])],
[AS_VAR_SET([have_panel], [no])])],
[AS_VAR_SET([have_curses], [no])])
AS_VAR_POPDEF([curses_var])
AS_VAR_POPDEF([panel_var])])
AS_VAR_IF([ac_cv_header_ncurses_h], [yes], [ # Check for ncursesw/panelw first. If that fails, try ncurses/panel.
if test "$ac_sys_system" != "Darwin"; then PY_CHECK_CURSES([ncursesw], [panelw])
dnl On macOS, there is no separate /usr/lib/libncursesw nor libpanelw. AS_VAR_IF([have_curses], [no],
PKG_CHECK_MODULES([CURSES], [ncursesw], [ [PY_CHECK_CURSES([ncurses], [panel])])
AC_DEFINE([HAVE_NCURSESW], [1])
have_curses=ncursesw
], [
WITH_SAVE_ENV([
AC_CHECK_LIB([ncursesw], [initscr], [
AC_DEFINE([HAVE_NCURSESW], [1])
have_curses=ncursesw
CURSES_CFLAGS=${CURSES_CFLAGS-""}
CURSES_LIBS=${CURSES_LIBS-"-lncursesw"}
])
])
])
fi
AS_VAR_IF([have_curses], [no], [ WITH_SAVE_ENV([
PKG_CHECK_MODULES([CURSES], [ncurses], [ # Make sure we've got the header defines.
have_curses=ncurses AS_VAR_APPEND([CPPFLAGS], [" $CURSES_CFLAGS $PANEL_CFLAGS"])
], [ AC_CHECK_HEADERS(m4_normalize([
WITH_SAVE_ENV([ ncursesw/curses.h ncursesw/ncurses.h ncursesw/panel.h
AC_CHECK_LIB([ncurses], [initscr], [ ncurses/curses.h ncurses/ncurses.h ncurses/panel.h
have_curses=ncurses curses.h ncurses.h panel.h
CURSES_CFLAGS=${CURSES_CFLAGS-""} ]))
CURSES_LIBS=${CURSES_LIBS-"-lncurses"}
])
])
])
])
])dnl ac_cv_header_ncurses_h = yes # Check that we're able to link with crucial curses/panel functions. This
# also serves as a fallback in case pkg-config failed.
AS_VAR_APPEND([LIBS], [" $CURSES_LIBS $PANEL_LIBS"])
AC_SEARCH_LIBS([initscr], [ncursesw ncurses],
[AS_VAR_IF([have_curses], [no],
[AS_VAR_SET([have_curses], [yes])
CURSES_LIBS=${CURSES_LIBS-"$ac_cv_search_initscr"}])],
[AS_VAR_SET([have_curses], [no])])
AC_SEARCH_LIBS([update_panels], [panelw panel],
[AS_VAR_IF([have_panel], [no],
[AS_VAR_SET([have_panel], [yes])
PANEL_LIBS=${PANEL_LIBS-"$ac_cv_search_update_panels"}])],
[AS_VAR_SET([have_panel], [no])])
dnl Issue #25720: ncurses has introduced the NCURSES_OPAQUE symbol making opaque
dnl structs since version 5.7. If the macro is defined as zero before including
dnl [n]curses.h, ncurses will expose fields of the structs regardless of the
dnl configuration.
AC_DEFUN([_CURSES_INCLUDES],dnl
[
#define NCURSES_OPAQUE 0
#if defined(HAVE_NCURSESW_NCURSES_H)
# include <ncursesw/ncurses.h>
#elif defined(HAVE_NCURSESW_CURSES_H)
# include <ncursesw/curses.h>
#elif defined(HAVE_NCURSES_NCURSES_H)
# include <ncurses/ncurses.h>
#elif defined(HAVE_NCURSES_CURSES_H)
# include <ncurses/curses.h>
#elif defined(HAVE_NCURSES_H)
# include <ncurses.h>
#elif defined(HAVE_CURSES_H)
# include <curses.h>
#endif
])
AS_IF([test "have_curses" != "no"], [
dnl remove _XOPEN_SOURCE macro from curses cflags. pyconfig.h sets dnl remove _XOPEN_SOURCE macro from curses cflags. pyconfig.h sets
dnl the macro to 700. dnl the macro to 700.
CURSES_CFLAGS=$(echo $CURSES_CFLAGS | sed 's/-D_XOPEN_SOURCE=600//g') CURSES_CFLAGS=$(echo $CURSES_CFLAGS | sed 's/-D_XOPEN_SOURCE=600//g')
if test "$have_curses" != no -a "$ac_sys_system" = "Darwin"; then AS_VAR_IF([ac_sys_system], [Darwin], [
dnl On macOS, there is no separate /usr/lib/libncursesw nor libpanelw. dnl On macOS, there is no separate /usr/lib/libncursesw nor libpanelw.
dnl System-supplied ncurses combines libncurses/libpanel and supports wide dnl System-supplied ncurses combines libncurses/libpanel and supports wide
dnl characters, so we can use it like ncursesw. dnl characters, so we can use it like ncursesw.
@ -6711,82 +6744,17 @@ if test "$have_curses" != no -a "$ac_sys_system" = "Darwin"; then
dnl _XOPEN_SOURCE_EXTENDED here for ncurses wide char support. dnl _XOPEN_SOURCE_EXTENDED here for ncurses wide char support.
AS_VAR_APPEND([CURSES_CFLAGS], [" -D_XOPEN_SOURCE_EXTENDED=1"]) AS_VAR_APPEND([CURSES_CFLAGS], [" -D_XOPEN_SOURCE_EXTENDED=1"])
AC_DEFINE([HAVE_NCURSESW], [1])
fi
dnl TODO: detect "curses" and special cases tinfo, terminfo, or termcap
AC_MSG_CHECKING([curses module flags])
AS_VAR_IF([have_curses], [no], [
AC_MSG_RESULT([no])
], [
AC_MSG_RESULT([$have_curses (CFLAGS: $CURSES_CFLAGS, LIBS: $CURSES_LIBS)])
]) ])
dnl check for ncurses' panel/panelw library
AC_CHECK_HEADERS([panel.h])
AS_VAR_IF([ac_cv_header_panel_h], [yes], [
if test "$ac_sys_system" != "Darwin"; then
dnl On macOS, there is no separate /usr/lib/libncursesw nor libpanelw.
AS_VAR_IF([have_curses], [ncursesw], [
PKG_CHECK_MODULES([PANEL], [panelw], [
have_panel=panelw
], [
WITH_SAVE_ENV([
AC_CHECK_LIB([panelw], [update_panels], [
have_panel=panelw
PANEL_CFLAGS=${PANEL_CFLAGS-""}
PANEL_LIBS=${PANEL_LIBS-"-lpanelw"}
])
])
])
])
fi
AS_VAR_IF([have_curses], [ncurses], [
PKG_CHECK_MODULES([PANEL], [panel], [
have_panel=panel
], [
WITH_SAVE_ENV([
AC_CHECK_LIB([panel], [update_panels], [
have_panel=panel
PANEL_CFLAGS=${PANEL_CFLAGS-""}
PANEL_LIBS=${PANEL_LIBS-"-lpanel"}
])
])
])
])
])dnl ac_cv_header_panel_h = yes
dnl pyconfig.h defines _XOPEN_SOURCE=700 dnl pyconfig.h defines _XOPEN_SOURCE=700
PANEL_CFLAGS=$(echo $PANEL_CFLAGS | sed 's/-D_XOPEN_SOURCE=600//g') PANEL_CFLAGS=$(echo $PANEL_CFLAGS | sed 's/-D_XOPEN_SOURCE=600//g')
AC_MSG_CHECKING([panel flags])
AS_VAR_IF([have_panel], [no], [
AC_MSG_RESULT([no])
], [
AC_MSG_RESULT([$have_panel (CFLAGS: $PANEL_CFLAGS, LIBS: $PANEL_LIBS)])
])
# first curses header check
ac_save_cppflags="$CPPFLAGS"
if test "$cross_compiling" = no; then
CPPFLAGS="$CPPFLAGS -I/usr/include/ncursesw"
fi
# On Solaris, term.h requires curses.h # On Solaris, term.h requires curses.h
AC_CHECK_HEADERS([term.h], [], [], [ AC_CHECK_HEADERS([term.h], [], [], _CURSES_INCLUDES)
#ifdef HAVE_CURSES_H
#include <curses.h>
#endif
])
# On HP/UX 11.0, mvwdelch is a block with a return statement # On HP/UX 11.0, mvwdelch is a block with a return statement
AC_CACHE_CHECK([whether mvwdelch is an expression], [ac_cv_mvwdelch_is_expression], AC_CACHE_CHECK([whether mvwdelch is an expression], [ac_cv_mvwdelch_is_expression],
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <curses.h>]], [[ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(_CURSES_INCLUDES, [[
int rtn; int rtn;
rtn = mvwdelch(0,0,0); rtn = mvwdelch(0,0,0);
]])], ]])],
@ -6799,15 +6767,8 @@ then
[Define if mvwdelch in curses.h is an expression.]) [Define if mvwdelch in curses.h is an expression.])
fi fi
# Issue #25720: ncurses has introduced the NCURSES_OPAQUE symbol making opaque
# structs since version 5.7. If the macro is defined as zero before including
# [n]curses.h, ncurses will expose fields of the structs regardless of the
# configuration.
AC_CACHE_CHECK([whether WINDOW has _flags], [ac_cv_window_has_flags], AC_CACHE_CHECK([whether WINDOW has _flags], [ac_cv_window_has_flags],
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(_CURSES_INCLUDES, [[
#define NCURSES_OPAQUE 0
#include <curses.h>
]], [[
WINDOW *w; WINDOW *w;
w->_flags = 0; w->_flags = 0;
]])], ]])],
@ -6829,11 +6790,7 @@ AC_DEFUN([PY_CHECK_CURSES_FUNC],
[for curses function $1], [for curses function $1],
[py_var], [py_var],
[AC_COMPILE_IFELSE( [AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM( [AC_LANG_PROGRAM(_CURSES_INCLUDES, [
[
#define NCURSES_OPAQUE 0
#include <curses.h>
], [
#ifndef $1 #ifndef $1
void *x=$1 void *x=$1
#endif #endif
@ -6861,6 +6818,8 @@ PY_CHECK_CURSES_FUNC([has_key])
PY_CHECK_CURSES_FUNC([typeahead]) PY_CHECK_CURSES_FUNC([typeahead])
PY_CHECK_CURSES_FUNC([use_env]) PY_CHECK_CURSES_FUNC([use_env])
CPPFLAGS=$ac_save_cppflags CPPFLAGS=$ac_save_cppflags
])dnl have_curses != no
])dnl save env
AC_MSG_NOTICE([checking for device files]) AC_MSG_NOTICE([checking for device files])
@ -7778,11 +7737,11 @@ PY_STDLIB_MOD([_ctypes],
[], [test "$have_libffi" = yes], [], [test "$have_libffi" = yes],
[$NO_STRICT_OVERFLOW_CFLAGS $LIBFFI_CFLAGS], [$LIBFFI_LIBS]) [$NO_STRICT_OVERFLOW_CFLAGS $LIBFFI_CFLAGS], [$LIBFFI_LIBS])
PY_STDLIB_MOD([_curses], PY_STDLIB_MOD([_curses],
[], [test "$have_curses" != "no"], [], [test "$have_curses" = "yes"],
[$CURSES_CFLAGS], [$CURSES_LIBS] [$CURSES_CFLAGS], [$CURSES_LIBS]
) )
PY_STDLIB_MOD([_curses_panel], PY_STDLIB_MOD([_curses_panel],
[], [test "$have_panel" != "no"], [], [test "$have_curses" = "yes" && test "$have_panel" = "yes"],
[$PANEL_CFLAGS $CURSES_CFLAGS], [$PANEL_LIBS $CURSES_LIBS] [$PANEL_CFLAGS $CURSES_CFLAGS], [$PANEL_LIBS $CURSES_LIBS]
) )
PY_STDLIB_MOD([_decimal], PY_STDLIB_MOD([_decimal],

View File

@ -829,12 +829,33 @@
/* Define to 1 if you have the `nanosleep' function. */ /* Define to 1 if you have the `nanosleep' function. */
#undef HAVE_NANOSLEEP #undef HAVE_NANOSLEEP
/* Define to 1 if you have the `ncursesw' library. */ /* Define if you have the 'ncurses' library */
#undef HAVE_NCURSES
/* Define if you have the 'ncursesw' library */
#undef HAVE_NCURSESW #undef HAVE_NCURSESW
/* Define to 1 if you have the <ncursesw/curses.h> header file. */
#undef HAVE_NCURSESW_CURSES_H
/* Define to 1 if you have the <ncursesw/ncurses.h> header file. */
#undef HAVE_NCURSESW_NCURSES_H
/* Define to 1 if you have the <ncursesw/panel.h> header file. */
#undef HAVE_NCURSESW_PANEL_H
/* Define to 1 if you have the <ncurses/curses.h> header file. */
#undef HAVE_NCURSES_CURSES_H
/* Define to 1 if you have the <ncurses.h> header file. */ /* Define to 1 if you have the <ncurses.h> header file. */
#undef HAVE_NCURSES_H #undef HAVE_NCURSES_H
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
#undef HAVE_NCURSES_NCURSES_H
/* Define to 1 if you have the <ncurses/panel.h> header file. */
#undef HAVE_NCURSES_PANEL_H
/* Define to 1 if you have the <ndbm.h> header file. */ /* Define to 1 if you have the <ndbm.h> header file. */
#undef HAVE_NDBM_H #undef HAVE_NDBM_H
@ -878,6 +899,12 @@
/* Define to 1 if you have the `openpty' function. */ /* Define to 1 if you have the `openpty' function. */
#undef HAVE_OPENPTY #undef HAVE_OPENPTY
/* Define if you have the 'panel' library */
#undef HAVE_PANEL
/* Define if you have the 'panelw' library */
#undef HAVE_PANELW
/* Define to 1 if you have the <panel.h> header file. */ /* Define to 1 if you have the <panel.h> header file. */
#undef HAVE_PANEL_H #undef HAVE_PANEL_H