From a525f18001c5d6339d125548c403df0bcd044f2b Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Sat, 23 Jan 2021 17:23:29 -0500 Subject: [PATCH] Add Bison-related configure tests. Recent versions of Bison have made a bunch of incompatible behaviour changes. Implement configure tests to identify some issues, so that build compatibility with multiple versions can be achieved. As part of this, we introduce a basic macro for finding Bison in the first place, which will verify support for "modern" features like %code blocks which are needed by all my Bison-using projects. --- m4/base.m4 | 11 +++++- m4/bison-compat.m4 | 94 ++++++++++++++++++++++++++++++++++++++++++++++ m4/bison.m4 | 67 +++++++++++++++++++++++++++++++++ t/bison.sh | 93 +++++++++++++++++++++++++++++++++++++++++++++ tests/programs.at | 6 ++- 5 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 m4/bison-compat.m4 create mode 100644 m4/bison.m4 create mode 100755 t/bison.sh diff --git a/m4/base.m4 b/m4/base.m4 index 86d6b84..42e8368 100644 --- a/m4/base.m4 +++ b/m4/base.m4 @@ -1,4 +1,4 @@ -dnl Copyright © 2012, 2014 Nick Bowler +dnl Copyright © 2012, 2014, 2021 Nick Bowler dnl dnl Base directory handling for dxcommon. dnl @@ -66,3 +66,12 @@ dnl DX_BASEDIR dnl dnl This macro expands to the dxcommon base directory, as a quoted string. AC_DEFUN([DX_BASEDIR], [m4_ignore(DX_INIT())m4_defn([_DX_BASEDIR])]) + +dnl DX_RUN_LOG(command) +dnl +dnl Run a command, logging all of the command, its output, and overall +dnl exit status to config.log. The expansion of this macro is a single +dnl complex shell command suitable for use in shell conditionals. +AC_DEFUN([DX_RUN_LOG], [{ (set -x; $1;) >&AS_MESSAGE_LOG_FD 2>&1 + dx_status=$?; AS_ECHO(["\$? = $dx_status"]) >&AS_MESSAGE_LOG_FD 2>&1 + test $dx_status = 0; }]) diff --git a/m4/bison-compat.m4 b/m4/bison-compat.m4 new file mode 100644 index 0000000..27993d6 --- /dev/null +++ b/m4/bison-compat.m4 @@ -0,0 +1,94 @@ +dnl Copyright © 2021 Nick Bowler +dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. +dnl This is free software: you are free to do what the fuck you want to. +dnl There is NO WARRANTY, to the extent permitted by law. + +dnl DX_BISON_COMPAT +dnl +dnl Checks $BISON for various options that may be required for compatibility +dnl with multiple versions. Currently tested features: +dnl +dnl If bison supports --define (as an alias for %define in the source file), +dnl then dx_cv_bison_define_cmdline will be set to "yes". Otherwise, it will +dnl be set to "no". +dnl +dnl If bison supports the --define=api.header.include feature (and thus +dnl #includes the header in the parser rather than embedding its contents), +dnl then dx_cv_bison_api_header_include will be set to "yes". Otherwise, it +dnl will be set to "no". +dnl +dnl If bison gives spews warnings for %error-verbose and -Wno-deprecated +dnl suppresses those warnings, then dx_cv_bison_warn_no_deprecated will be +dnl set to "yes". Otherwise, it will be set to "no". + +AC_DEFUN([DX_BISON_COMPAT], +[AC_REQUIRE([DX_PROG_BISON])dnl +AS_IF([test x"$dx_cv_bison_works" = x"yes"], +[# Older versions of bison accept --define as an abbreviation for --defines. +# This gives very bad results in the following configure test. +AC_CACHE_CHECK([whether $BISON supports --define], + [dx_cv_bison_define_cmdline], [dx_cv_bison_define_cmdline=no +cat >conftest.y <<'EOF' +%{ +int yylex(void) { return 0; } +void yyerror(const char *msg) { } +%} +%% +input: +%% +int main(void) { return yylval; } +EOF + +rm -f conftest.tab.c conftest.x +$BISON --define=conftest.x conftest.y >&AS_MESSAGE_LOG_FD 2>&1 +AS_IF([test ! -f conftest.x], +[AS_IF([$BISON conftest.y >&AS_MESSAGE_LOG_FD 2>&1], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include "conftest.tab.c"]])], + [AS_IF([$BISON --define=api.pure conftest.y >&AS_MESSAGE_LOG_FD 2>&1], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include "conftest.tab.c"]])], + [:], [dx_cv_bison_define_cmdline=yes])])])])]) +]) + +AS_IF([test x"$dx_cv_bison_define_cmdline" != x"yes"], + [dx_cv_bison_api_header_include=no]) + +AC_CACHE_CHECK([whether $BISON supports api.header.include], + [dx_cv_bison_api_header_include], [dx_cv_bison_api_header_include=no +cat >conftest.y <<'EOF' +%{ +int yylex(void) { return 0; } +void yyerror(const char *msg) { } +%} +%% +input: +EOF +rm -f conftest.[[123].[ch]] +for dx_pass in 1 2 3 +do + AS_CASE([$dx_pass], + [1], [$BISON -o conftest.1.c --defines=conftest.1.h conftest.y >&AS_MESSAGE_LOG_FD 2>&1|| break], + [2], [$BISON -o conftest.2.c --define=api.header.include='{"conftest.3.h"}' --defines=conftest.2.h conftest.y >&AS_MESSAGE_LOG_FD 2>&1 || break], + [3], [mv conftest.2.c conftest.3.c; mv conftest.2.h conftest.3.h]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include "conftest.$dx_pass.c"]])], + [test $dx_pass -ne 2 || break], [test $dx_pass -eq 2 || break]) + test $dx_pass -ne 3 || dx_cv_bison_api_header_include=yes +done +]) + +AC_CACHE_CHECK([whether $BISON wants -Wno-deprecated], + [dx_cv_bison_warn_no_deprecated], [dx_cv_bison_warn_no_deprecated=no +cat >conftest.y <<'EOF' +%error-verbose +%{ +int yylex(void) { return 0; } +void yyerror(const char *msg) { } +%} +%% +input: +EOF +AS_IF([$BISON -Werror conftest.y >&AS_MESSAGE_LOG_FD 2>&1], [], + [AS_IF([$BISON -Werror -Wno-deprecated conftest.y >&AS_MESSAGE_LOG_FD 2>&1], + [dx_cv_bison_warn_no_deprecated=yes])]) +])], [dx_cv_bison_define_cmdline=no +dx_cv_bison_api_header_include=no +dx_cv_bison_warn_no_deprecated=no])]) diff --git a/m4/bison.m4 b/m4/bison.m4 new file mode 100644 index 0000000..18274aa --- /dev/null +++ b/m4/bison.m4 @@ -0,0 +1,67 @@ +dnl Copyright © 2021 Nick Bowler +dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. +dnl This is free software, you are free to do what the fuck you want to. +dnl There is NO WARRANTY, to the extent permitted by law. + +dnl DX_PROG_BISON([min-version], [action-if-found], [action-if-not-found]) +dnl +dnl Search PATH for a reasonably modern GNU Bison of at least the given +dnl minimum version, which may be empty to accept any version. Here, +dnl "reasonably modern" means it supports the %code construct and (if +dnl a version is specified), the %require construct. +dnl +dnl If found, the BISON variable (which is substituted by AC_SUBST) is +dnl set with the result, and action-if-found is executed. Otherwise, +dnl action-if-not-found is executed if nonempty, else configure will +dnl exit with a fatal error. +AC_DEFUN([DX_PROG_BISON], +[AC_ARG_VAR([BISON], [GNU Bison command])dnl +AC_ARG_VAR([BISONFLAGS], [Additional options to pass to Bison])dnl +cat >conftest.y <<'EOF' +m4_ifnblank([$1], [%require "$1" +])%code top { +/* SUPPORTS_CODE_TOP_WITNESS */ +} +%code requires { +/* SUPPORTS_CODE_REQUIRES_WITNESS */ +} +%{ +int yylex(void) { return 0; } +void yyerror(const char *msg) { } +%} +%% +input: +%% +int main(void) { return yyparse(); } +EOF +AC_CACHE_CHECK([for bison], [ac_cv_path_BISON], +[dx_cv_bison_found=: +AC_PATH_PROGS_FEATURE_CHECK([BISON], [bison], +[_DX_BISON_DO_TEST([$ac_path_BISON], +[bison_relcmd=${ac_path_BISON##*/} +bison_bypath=`command -v "$bison_relcmd"` #'' +ac_cv_path_BISON=$ac_path_BISON +test x"$bison_bypath" = x"$ac_path_BISON" && ac_cv_path_BISON=$bison_relcmd +ac_path_BISON_found=: +dx_cv_bison_works=yes])], [ac_cv_path_BISON=no dx_cv_bison_found=false])]) +AS_IF([$dx_cv_bison_found], +[BISON=$ac_cv_path_BISON +AC_CACHE_CHECK([whether $BISON works and is m4_ifnblank([$1], + [at least version $1], [reasonably modern])], + [dx_cv_bison_works], + [_DX_BISON_DO_TEST([$BISON], [dx_cv_bison_works=yes], [dx_cv_bison_works=no])])]) +AS_IF([test x"$dx_cv_bison_works" = x"yes"], + [$2], [m4_default([$3], [AC_MSG_FAILURE( +[GNU bison m4_ifnblank([$1], [version at least $1 ])is required. + +The latest version may be found at . +])])]) +rm -f conftest.tab.[[ch]] conftest.y y.tab.[[ch]] +]) + +AC_DEFUN([_DX_BISON_DO_TEST], [rm -f conftest.tab.[[ch]] y.tab.[[ch]] +AS_IF([DX_RUN_LOG([$1 --file-prefix=conftest --defines conftest.y])], + [AC_LINK_IFELSE([AC_LANG_SOURCE([[#include "conftest.tab.c"]])], + [AS_IF([DX_RUN_LOG([grep SUPPORTS_CODE_TOP_WITNESS conftest.tab.c]) && + DX_RUN_LOG([grep SUPPORTS_CODE_REQUIRES_WITNESS conftest.tab.h])], + [$2], [$3])], [$3])], [$3])]) diff --git a/t/bison.sh b/t/bison.sh new file mode 100755 index 0000000..c21d1bf --- /dev/null +++ b/t/bison.sh @@ -0,0 +1,93 @@ +#!/bin/sh +# +# Copyright © 2021 Nick Bowler +# +# Fake bison program for testing the bison detection macro. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +: "${FAKE_BISON_VERSION=2.4.0}" + +argv0=$0 + +infile= +outheader= +use_header=false + +for arg +do + case $arg in + --defines) use_header=true ;; + --defines=*) use_header=true outheader=${arg%*=} ;; + *.y) infile=$arg ;; + esac +done + +if test x"$infile" = x""; then + printf '%s: error: no input file\n' "$argv0" 1>&2 + exit 1 +fi + +outfile=${infile%.y}.tab.c +if $use_header; then + test ${outheader:+y} || outheader=${outfile%.c}.h + exec 5>"$outheader" +fi +exec 3<"$infile" 4>"$outfile" + +copy_header=false copy_impl=false +while read line <&3; do + case $line in + %require*) + save_IFS=$IFS IFS=$IFS\". + set x $FAKE_BISON_VERSION 0 0 0; shift + fake_major=$1 fake_minor=$2 fake_revision=$3 + set x ${line#*\"} 0 0 0; shift + req_major=$1 req_minor=$2 req_revision=$3 + IFS=$save_IFS + set -x + test $fake_major -ge $req_major || exit 1 + if test $fake_major -eq $req_major; then + test $fake_minor -ge $req_minor || exit 1 + if test $fake_minor -eq $req_minor; then + test $fake_revision -ge $req_revision || exit 1 + fi + fi + set +x + ;; + %code*) + set x $line; shift + case $2 in + requires|provides) copy_header=true copy_impl=true ;; + top) copy_impl=true ;; + esac + ;; + [}]) + copy_header=false copy_impl=false + ;; + %%) + break + ;; + *) + $copy_header && printf '%s\n' "$line" >&5 + $copy_impl && printf '%s\n' "$line" >&4 + ;; + esac +done + +cat >&4 <&4 diff --git a/tests/programs.at b/tests/programs.at index b99fa82..1346ede 100644 --- a/tests/programs.at +++ b/tests/programs.at @@ -1,4 +1,4 @@ -dnl Copyright © 2020 Nick Bowler +dnl Copyright © 2020-2021 Nick Bowler dnl dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. dnl This is free software: you are free to do what the fuck you want to. @@ -11,6 +11,7 @@ m4_define([TEST_SIMPLE_PROGRAM_PROBES], m4_define([TEST_SIMPLE_PROGRAM_PROBES_], [AT_SETUP([$1 probes]) AT_KEYWORDS([DX_PROG_$2 program])dnl +AT_CAPTURE_FILE([config.log])dnl mkdir bin cp -P "$srcdir/t/$1.sh" bin/$1 @@ -27,7 +28,7 @@ AC_SUBST([HAVE_$2]) set x conftest*; shift if test -f $[]1; then - AC_MSG_ERROR([$[]1 left behind by [DX_PROG_$2]]) + AC_MSG_ERROR([$[]1 left behind by [D@@&t@&t@X_PROG_$2]]) fi AC_CONFIG_FILES([test]) @@ -94,3 +95,4 @@ AT_CLEANUP TEST_SIMPLE_PROGRAM_PROBES([gob2]) TEST_SIMPLE_PROGRAM_PROBES([flex]) +TEST_SIMPLE_PROGRAM_PROBES([bison]) -- 2.43.2