5 ### FINALLY([IF-SUCCEEDED], [IF-FAILED])
9 ### Probe at the C compiler to determine how, if at all, to implement the
10 ### `FINALLY' macro, which arranges to run some code when control leaves a
11 ### given scope. This isn't at all a standard C feature, so we need to use
12 ### compiler-specific hacks, and this is the main machinery for deciding
13 ### which hacks to deploy.
15 ### On exit, the shell variable `finally_flavour' is set to an uppercase
16 ### word naming the chosen implementation strategy: it will be `NIL' if the
17 ### macro failed and no strategy could be found. The preprocessor define
18 ### `FINALLY_CONFIG_FLAVOUR' is set to `FINALLY_CONFIG_FLAVOUR_...'
19 ### followed by the same word: this is the main input to the selection
20 ### machinery in `finally.h'.
22 ### The substitution variables `FINALLY_CFLAGS' and `FINALLY_LIBS' are set
23 ### to any additional compiler flags or libraries needed to support the
24 ### `FINALLY' macro. They can be set per-target in the `Makefile', or
25 ### stuffed into the global variables by the `configure' script.
27 ### If the macro managed to find a workable strategy, then the shell
28 ### fragment IF-SUCCEEDED is run; otherwise, (if `finally_flavour' is
29 ### `NIL'), the shell fragment IF-FAILED is run.
33 ### Copyright (c) 2023 Mark Wooding <mdw@distorted.org.uk>
35 ### This program is free software: you can redistribute it and/or modify it
36 ### under the terms of the GNU General Public License as published by the
37 ### Free Software Foundation, either version 2 of the License, or (at your
38 ### option) any later version.
40 ### This program is distributed in the hope that it will be useful, but
41 ### WITHOUT ANY WARRANTY; without even the implied warranty of
42 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
43 ### General Public License for more details.
45 ### You should have received a copy of the GNU General Public License along
46 ### with this program. If not, see <http://www.gnu.org/licenses/>.
48 ### In particular, no exception to the GPL is granted regarding generated
49 ### `configure' scripts which are the output of Autoconf.
51 AC_DEFUN([FINALLY_GCC_NESTED_FUNCTIONS_TEST_PROGRAM], [AC_LANG_PROGRAM([], [
52 __extension__ __inline__ void nested(void) { ; }
55 AC_DEFUN([FINALLY_GCC_ATTRIBUTE_CLEANUP_TEST_PROGRAM], [AC_LANG_PROGRAM([
56 extern void cleanup_fn(const int *x);
57 extern void bamboozle(int *x_inout);
59 __attribute__((cleanup(cleanup_fn))) int x = 0;
62 AC_DEFUN([FINALLY_GCC_REALLY_CLANG_TEST_PROGRAM], [AC_LANG_PROGRAM([], [
67 AC_DEFUN([FINALLY_CLANG_BLOCKS_TEST_PROGRAM], [AC_LANG_PROGRAM([
68 extern void srand(unsigned); /* might throw? */
69 static __inline__ void runblk(void (^*f)(void)) { (*f)(); }
71 unsigned x = 1; /* closed over */
72 __attribute__((__unused__, __cleanup__(runblk)))
73 void (^f)(void) = ^{ srand(x); };
77 dnl Decide whether we can define a plausible `FINALLY' macro.
78 AC_DEFUN([FINALLY_CHECK],
79 [finally_flavour=undecided finally_result="not supported"
81 dnl We're going to want to test C code.
84 case $finally_flavour,$GCC in
86 dnl Our GCC-ish strategies have a common factor: they depend on
87 dnl `__attribute__((cleanup(...)))' working. So let's check for that.
89 AC_CACHE_CHECK([whether the alleged GNU C compiler supports \`__attribute__((cleanup(...)))'],
90 [finally_cv_gcc_attribute_cleanup_p], [
91 AC_COMPILE_IFELSE([FINALLY_GCC_ATTRIBUTE_CLEANUP_TEST_PROGRAM],
92 [finally_cv_gcc_attribute_cleanup_p=yes],
93 [finally_cv_gcc_attribute_cleanup_p=no])])
94 case $finally_cv_gcc_attribute_cleanup_p in
95 no) finally_flavour=NIL ;;
100 case $finally_flavour,$GCC in
102 dnl Autoconf has decided that the compiler smells a bit like GCC, and it
103 dnl certainly seems to support a GCC extension. But many compilers
104 dnl impersonate GCC, in more or less convincing ways. Our GCC-flavoured
105 dnl `FINALLY' code depends on nested functions, which GCC has supported
106 dnl pretty much forever, but other compilers don't even though they lie
107 dnl about being compatible.
109 AC_CACHE_CHECK([whether the alleged GNU C compiler supports nested functions],
110 [finally_cv_gcc_nested_functions_p], [
111 AC_COMPILE_IFELSE([FINALLY_GCC_NESTED_FUNCTIONS_TEST_PROGRAM],
112 [finally_cv_gcc_nested_functions_p=yes],
113 [finally_cv_gcc_nested_functions_p=no])])
114 case $finally_cv_gcc_nested_functions_p in
116 finally_flavour=GCC_NESTED_FUNCTIONS
117 finally_result="GCC nested functions"
123 case $finally_flavour,$GCC in
125 dnl That didn't work. I guess it's not really GCC after all. Maybe it's
126 dnl Clang wearing a false moustache.
128 AC_CACHE_CHECK([whether the impostor GNU C compiler is really Clang],
129 [finally_cv_gcc_really_clang_p], [
130 AC_COMPILE_IFELSE([FINALLY_GCC_REALLY_CLANG_TEST_PROGRAM],
131 [finally_cv_gcc_really_clang_p=yes],
132 [finally_cv_gcc_really_clang_p=no])])
133 finally_clang_p=$finally_cv_gcc_really_clang_p
140 case $finally_flavour,$finally_clang_p in
142 dnl Yup. Not a particularly convincing disguise, really. Well, at least
143 dnl Clang has a thing which looks a bit like nested functions, and a bit
144 dnl like closures, only with a terrible syntax, which it calls `blocks'.
145 dnl Unfortunately, we may or may not require varying levels of ceremony
146 dnl to make this work.
148 AC_CACHE_CHECK([which hacks are needed to persuade Clang to work with simple blocks],
149 [finally_cv_clang_blocks_hacks], [
151 dnl We'll need to mess with the compiler flags and libraries,
152 dnl so make sure we can put them back again afterwards.
153 finally_original_CFLAGS=$CFLAGS finally_original_LIBS=$LIBS
155 dnl Maintain a list of things that we did. This is the thing
161 dnl OK. First thing, we need to get the compiler proper to accept
162 dnl the `blocks' syntax. I guess the syntax is so hideous that
163 dnl Clang is sometimes ashamed to admit to parsing it unless we
164 dnl twist its arm. Apparently `-fblocks' is unnecessary on some
165 dnl targets, so let's see if we can manage without.
167 for pass in nil -fblocks; do
168 case $pass in -*) CFLAGS="$CFLAGS -fblocks" ;; esac
169 AC_COMPILE_IFELSE([FINALLY_CLANG_BLOCKS_TEST_PROGRAM],
171 case $win in t) break ;; esac
175 *) hacks=${hacks+$hacks }$pass
182 dnl We got the compiler to accept the unpleasant syntax. The next
183 dnl problem is that, technically, the generated code depends on a
184 dnl runtime support library; only our use for these things is so
185 dnl simple that, at reasonable optimization settings, we can do
186 dnl without. So let's see if we're in that situation.
188 for pass in nil -lBlocksRuntime; do
189 case $pass in -l*) LIBS="$LIBS $pass" ;; esac
190 AC_LINK_IFELSE([FINALLY_CLANG_BLOCKS_TEST_PROGRAM],
192 case $win in t) break ;; esac
196 *) hacks=${hacks+$hacks }$pass
201 dnl We've finished probing, and it's time to report our findings.
203 t) finally_cv_clang_blocks_hacks=${hacks-none} ;;
204 *) finally_cv_clang_blocks_hacks=failed ;;
207 dnl Oh! And don't forget to undo our fiddling with the compiler and
209 CFLAGS=$finally_original_CFLAGS LIBS=$finally_original_LIBS])
211 dnl That was fun. Now we know how to persuade Clang to support these
212 dnl block thingummies (or not). Report our findings.
213 case $finally_cv_clang_blocks_hacks in
218 finally_flavour=CLANG_BLOCKS FINALLY_CFLAGS= FINALLY_LIBS=
219 finally_result="Clang blocks"
220 for hack in $finally_cv_clang_blocks_hacks; do
223 -l* | -L*) FINALLY_LIBS=${FINALLY_LIBS:+ $FINALLY_LIBS}$hack ;;
224 -*) FINALLY_CFLAGS=${FINALLY_CFLAGS:+ $FINALLY_CFLAGS}$hack ;;
225 *) AC_MSG_ERROR([confused by unexpected hack $hack]) ;;
233 case $finally_flavour in
235 dnl We've got this far and we've drawn a blank. Give up.
242 dnl Pass the results on to the implementation machinery.
243 AC_MSG_CHECKING([how to implement deferred cleanup code])
244 AC_DEFINE_UNQUOTED([FINALLY_CONFIG_FLAVOUR],
246 [Select one of the implementation strategies for the `FINALLY' macro.])
247 AC_SUBST(FINALLY_CFLAGS) AC_SUBST(FINALLY_LIBS)
248 AC_MSG_RESULT([$finally_result])
250 dnl Invoke the caller's shell fragments according to our findings.
251 case $finally_flavour in