gh-113565: Improve and harden detection of curses dependencies (#119816)

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.
This commit is contained in:
Erlend E. Aasland 2024-07-01 10:10:03 +02:00 committed by GitHub
parent bd473aa598
commit f80376b129
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
#endif
#ifdef HAVE_NCURSES_H
#include <ncurses.h>
#else
#include <curses.h>
#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
#ifdef HAVE_NCURSES_H
#ifdef NCURSES_VERSION
/* configure was checking <curses.h>, but we will
use <ncurses.h>, which has some or all these features. */
#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 <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 {
PyObject *PyCursesError;

View File

@ -128,7 +128,7 @@ static const char PyCursesVersion[] = "2.2";
#include <langinfo.h>
#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 */
typedef chtype attr_t; /* No attr_t type is available */
#endif

1170
configure generated vendored

File diff suppressed because it is too large Load Diff

View File

@ -6663,55 +6663,88 @@ then
[Define if year with century should be normalized for strftime.])
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 have_curses=[no, ncursesw, ncurses]
dnl have_panel=[no, panelw, panel]
dnl have_curses=[no, yes]
dnl have_panel=[no, yes]
have_curses=no
have_panel=no
AH_TEMPLATE([HAVE_NCURSESW], [Define to 1 if you have the `ncursesw' library.])
AC_CHECK_HEADERS([curses.h ncurses.h])
dnl PY_CHECK_CURSES(LIBCURSES, LIBPANEL)
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], [
if test "$ac_sys_system" != "Darwin"; then
dnl On macOS, there is no separate /usr/lib/libncursesw nor libpanelw.
PKG_CHECK_MODULES([CURSES], [ncursesw], [
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
# Check for ncursesw/panelw first. If that fails, try ncurses/panel.
PY_CHECK_CURSES([ncursesw], [panelw])
AS_VAR_IF([have_curses], [no],
[PY_CHECK_CURSES([ncurses], [panel])])
AS_VAR_IF([have_curses], [no], [
PKG_CHECK_MODULES([CURSES], [ncurses], [
have_curses=ncurses
], [
WITH_SAVE_ENV([
AC_CHECK_LIB([ncurses], [initscr], [
have_curses=ncurses
CURSES_CFLAGS=${CURSES_CFLAGS-""}
CURSES_LIBS=${CURSES_LIBS-"-lncurses"}
])
])
])
])
WITH_SAVE_ENV([
# Make sure we've got the header defines.
AS_VAR_APPEND([CPPFLAGS], [" $CURSES_CFLAGS $PANEL_CFLAGS"])
AC_CHECK_HEADERS(m4_normalize([
ncursesw/curses.h ncursesw/ncurses.h ncursesw/panel.h
ncurses/curses.h ncurses/ncurses.h ncurses/panel.h
curses.h ncurses.h panel.h
]))
])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 the macro to 700.
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 System-supplied ncurses combines libncurses/libpanel and supports wide
dnl characters, so we can use it like ncursesw.
@ -6721,82 +6754,17 @@ if test "$have_curses" != no -a "$ac_sys_system" = "Darwin"; then
dnl _XOPEN_SOURCE_EXTENDED here for ncurses wide char support.
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
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
AC_CHECK_HEADERS([term.h], [], [], [
#ifdef HAVE_CURSES_H
#include <curses.h>
#endif
])
AC_CHECK_HEADERS([term.h], [], [], _CURSES_INCLUDES)
# 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_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <curses.h>]], [[
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(_CURSES_INCLUDES, [[
int rtn;
rtn = mvwdelch(0,0,0);
]])],
@ -6809,15 +6777,8 @@ then
[Define if mvwdelch in curses.h is an expression.])
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_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#define NCURSES_OPAQUE 0
#include <curses.h>
]], [[
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(_CURSES_INCLUDES, [[
WINDOW *w;
w->_flags = 0;
]])],
@ -6839,11 +6800,7 @@ AC_DEFUN([PY_CHECK_CURSES_FUNC],
[for curses function $1],
[py_var],
[AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM(
[
#define NCURSES_OPAQUE 0
#include <curses.h>
], [
[AC_LANG_PROGRAM(_CURSES_INCLUDES, [
#ifndef $1
void *x=$1
#endif
@ -6871,6 +6828,8 @@ PY_CHECK_CURSES_FUNC([has_key])
PY_CHECK_CURSES_FUNC([typeahead])
PY_CHECK_CURSES_FUNC([use_env])
CPPFLAGS=$ac_save_cppflags
])dnl have_curses != no
])dnl save env
AC_MSG_NOTICE([checking for device files])
@ -7788,11 +7747,11 @@ PY_STDLIB_MOD([_ctypes],
[], [test "$have_libffi" = yes],
[$NO_STRICT_OVERFLOW_CFLAGS $LIBFFI_CFLAGS], [$LIBFFI_LIBS])
PY_STDLIB_MOD([_curses],
[], [test "$have_curses" != "no"],
[], [test "$have_curses" = "yes"],
[$CURSES_CFLAGS], [$CURSES_LIBS]
)
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]
)
PY_STDLIB_MOD([_decimal],

View File

@ -829,12 +829,33 @@
/* Define to 1 if you have the `nanosleep' function. */
#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
/* 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. */
#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. */
#undef HAVE_NDBM_H
@ -878,6 +899,12 @@
/* Define to 1 if you have the `openpty' function. */
#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. */
#undef HAVE_PANEL_H