]> git.draconx.ca Git - dxcommon.git/commitdiff
Add Bison-related configure tests.
authorNick Bowler <nbowler@draconx.ca>
Sat, 23 Jan 2021 22:23:29 +0000 (17:23 -0500)
committerNick Bowler <nbowler@draconx.ca>
Sat, 23 Jan 2021 22:53:18 +0000 (17:53 -0500)
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
m4/bison-compat.m4 [new file with mode: 0644]
m4/bison.m4 [new file with mode: 0644]
t/bison.sh [new file with mode: 0755]
tests/programs.at

index 86d6b842c0d6098fd29323945c7545ad2bacd4e5..42e836850eec96e46df21bf3363dda155a9ef1d6 100644 (file)
@@ -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 (file)
index 0000000..27993d6
--- /dev/null
@@ -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 (file)
index 0000000..18274aa
--- /dev/null
@@ -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 <https://www.gnu.org/software/bison/>.
+])])])
+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 (executable)
index 0000000..c21d1bf
--- /dev/null
@@ -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 <<EOF
+int yyparse(void)
+{
+  return 0;
+}
+EOF
+
+while read line <&3; do
+  case $line in
+  %%) break;
+  esac
+done
+
+cat <&3 >&4
index b99fa828f969736722d2d5596ccdff516bbb3f2a..1346edee73ec816e48590d6146f46c2a920e818f 100644 (file)
@@ -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])