From d4b010a6dc3c999c9d3488a453ab2be1af18b6c9 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Sat, 22 Nov 2014 13:38:20 -0500 Subject: [PATCH] Fix DX_BASEDIR hoisting problems in base.m4. Currently, DX_INIT expands to a bunch of text, mostly whitespace. This is not normally a serious problem, except when DX_BASEDIR is called at the top level without previously expanding DX_INIT. Since DX_BASEDIR is intended to be expanded during argument collection, the result is that DX_INIT's output ends up as part of the argument where DX_BASEDIR appears. Fixing this turns out to be somewhat non-trivial. Both DX_INIT and DX_BASEDIR need to be changed. - First, avoid using AC_REQUIRE in DX_BASEDIR. This relies on diversions which do not work correctly during argument collection. - Rewrite DX_INIT so that it expands to no text whatsoever. - Finally, the AC_DEFUN_ONCE mechanism appears to insert newlines where the macros are called for the first time. Use m4_ignore to eat that inside DX_BASEDIR. --- m4/base.m4 | 95 +++++++++++++++++++++++++++---------------------- m4/dx-stamp.m4 | 2 +- tests/macros.at | 40 +++++++++++++++++++-- 3 files changed, 91 insertions(+), 46 deletions(-) diff --git a/m4/base.m4 b/m4/base.m4 index aebf6cd..86d6b84 100644 --- a/m4/base.m4 +++ b/m4/base.m4 @@ -1,57 +1,68 @@ -dnl Copyright © 2012 Nick Bowler +dnl Copyright © 2012, 2014 Nick Bowler dnl -dnl Base definitions for dxcommon. +dnl Base directory handling for dxcommon. 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. dnl There is NO WARRANTY, to the extent permitted by law. -m4_pattern_forbid([^_?DX_]) +AC_PREREQ([2.64]) -dnl DX_INIT([directory]) -dnl -dnl Sets the directory of the dxcommon checkout, which is used by other macros -dnl to find any source files that they may need. If this file was included -dnl from its original location using m4_include, which normally happens when -dnl using aclocal, then the directory will be determined automatically (and -dnl this macro does not need to be called in configure.ac at all). Otherwise, -dnl this should be called prior to any other dxcommon macro. +m4_pattern_forbid([^_?DX_]) +dnl _DX_BASE_FILE +dnl +dnl This macro identifies the filename of this file, used to automatically +dnl determine the dxcommon base directory. It is a literal string, thus it +dnl should not be expanded directly; use m4_defn([_DX_BASE_FILE]) instead. m4_define([_DX_BASE_FILE], __file__) -AC_DEFUN_ONCE([DX_INIT], [dnl -dnl Care must be taken to avoid spurious expansions of things that look like -dnl macros in the filename; this is made especially difficult since -dnl m4_bpatsubst does not expand to a quoted string. -m4_define([_DX_BASEDIR], m4_if([$1], [], - [m4_bpatsubst(m4_dquote(m4_defn([_DX_BASE_FILE])), - [/m4/[^/]*\.m4\(.\)$], [\1])], - [[$1]])) +dnl _DX_SET_BASEDIR([directory]) +dnl +dnl Defines the macro _DX_BASEDIR to the dxcommon base directory. If the +dnl argument is empty, the base directory will be determined automatically. +dnl Otherwise, the base directory is set to the argument (which is not +dnl subject to macro expansion). +dnl +dnl Quoting here is tricky as we must avoid macro expansion of _DX_BASE_FILE, +dnl but m4_bpatsubst does not quote its result. The resulting _DX_BASEDIR +dnl macro is a literal string, thus it should not be expanded directly; use +dnl m4_defn([_DX_BASEDIR]) instead. +m4_define([_DX_SET_BASEDIR], [m4_do( + [m4_define([_DX_BASEDIR], m4_ifval([$1], [[$1]], + [m4_bpatsubst(m4_dquote(m4_defn([_DX_BASE_FILE])), + [/m4/[^/]*\.m4\(.\)$], [\1])]))], + [_DX_STAMP])]) -dnl Include the stamp file, which will cause a failure at autoconf time -dnl if it does not exist in the checkout. We circumvent m4_include to -dnl avoid warnings about multiple inclusions. -m4_pushdef([m4_include], [m4_builtin([include], $][@)]) -m4_include(m4_defn([_DX_BASEDIR])[/m4/dx-stamp.m4]) -m4_popdef([m4_include]) -_DX_STAMP_DUMMY +dnl Include the stamp file to force failure at autoconf time if it does +dnl not exist in the checkout. We circumvent m4_include to avoid warnings +dnl about multiple inclusions. +m4_define([_DX_STAMP], [m4_do( + [m4_pushdef([m4_include], [m4_builtin([include], $][@)])], + [m4_include(m4_defn([_DX_BASEDIR])[/m4/dx-stamp.m4])], + [m4_popdef([m4_include])])]) -dnl Autoconf scripts should use the DX_BASEDIR m4 macro instead of the -dnl AC_SUBSTed shell variable, but we use the same name for the macro since -dnl it means the same thing as the substituted variable. +dnl DX_INIT(directory) +dnl +dnl Sets the dxcommon base directory, which is used by other macros to +dnl find any source files that they may need. It is not normally necessary +dnl to use this macro. If it used, it must be expanded prior to any other +dnl dxcommon macro. Otherwise, the base directory will be determined +dnl automatically. +dnl +dnl The directory argument is a literal string, not subject to macro expansion. dnl dnl Autoconf versions <= 2.69 have a bug which causes the first argument of -dnl AC_SUBST to be expanded as a macro when using the two-argument form. So -dnl until we require a newer version of autoconf, we must use the one-argument -dnl form of AC_SUBST. +dnl AC_SUBST to be expanded as a macro when using the two-argument form. This +dnl bug has been fixed upstream but as of September, 2014 there is no release +dnl which includes the fix. +AC_DEFUN_ONCE([DX_INIT], [m4_do( + [_DX_SET_BASEDIR([$1])], + [AC_SUBST([DX_BASEDIR])], + [AC_CONFIG_COMMANDS_PRE( + [[DX_BASEDIR]="AS_ESCAPE(m4_dquote(m4_defn([_DX_BASEDIR])))"])])]) -[DX_BASEDIR]="AS_ESCAPE(m4_dquote(m4_defn([_DX_BASEDIR])))" -m4_pushdef([m4_pattern_allow]) -AC_SUBST([DX_BASEDIR]) -m4_popdef([m4_pattern_allow]) -]) - -AC_DEFUN([DX_BASEDIR], [dnl -AC_REQUIRE([DX_INIT])dnl -m4_defn([_DX_BASEDIR])dnl -]) +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])]) diff --git a/m4/dx-stamp.m4 b/m4/dx-stamp.m4 index 5b64a7c..f078480 100644 --- a/m4/dx-stamp.m4 +++ b/m4/dx-stamp.m4 @@ -1,4 +1,4 @@ dnl This m4 snippet does nothing; its existence is used to identify the dnl dxcommon checkout location. We define a dummy macro so that aclocal dnl can pick it up. -AC_DEFUN([_DX_STAMP_DUMMY]) +AC_DEFUN([_DX_STAMP_DUMMY])dnl diff --git a/tests/macros.at b/tests/macros.at index 9986d75..f57ecae 100644 --- a/tests/macros.at +++ b/tests/macros.at @@ -26,14 +26,12 @@ dnl Verify that the DX_BASEDIR macro expands correctly during argument dnl collection. Crucially, the output must not contain any other text. AT_SETUP([DX_BASEDIR during argument collection]) -AT_XFAIL_IF([:]) - AT_DATA([test.in], [[@TEST@ ]]) TEST_CONFIGURE_AC( -[[AC@&t@_SUBST([TEST], 'm4@&t@_dquote(DX_BASEDIR)') +[[AC_SUBST([TEST], 'm4@&t@_dquote(DX_BASEDIR)') AC_CONFIG_FILES([test]) ]]) TEST_AUTORECONF @@ -44,3 +42,39 @@ printf '%s\n' "$srcdir" | sed -e 's:/\./:/:g' -e 's:/\.$::g' \ AT_CHECK([cat test], [0], [expout]) AT_CLEANUP + +dnl Verify that DX_BASEDIR is correctly AC_SUBSTed +AT_SETUP([DX_BASEDIR output substitution]) + +AT_DATA([test.in], +[[@DX_BASEDIR@ +]]) + +TEST_CONFIGURE_AC( +[[DX_INIT +AC_CONFIG_FILES([test]) +]]) +TEST_AUTORECONF +TEST_CONFIGURE + +printf '%s\n' "$srcdir" | sed -e 's:/\./:/:g' -e 's:/\.$::g' \ + -e 's:\(.\)//*:\1/:g' >expout +AT_CHECK([cat test], [0], [expout]) + +AT_CLEANUP + +dnl Verify that DX_BASEDIR is set correctly implicitly +AT_SETUP([DX_BASEDIR implicit definition]) + +AT_DATA([test.in], +[[@DX_BASEDIR@ +]]) + +TEST_CONFIGURE_AC( +[[DX_EXPORTED_SH +AC_CONFIG_FILES([test]) +]]) +TEST_AUTORECONF +TEST_CONFIGURE + +AT_CLEANUP -- 2.43.0