chiark / gitweb /
Add half-hearted support for Clang, because its `blocks' are deficient.
[finally] / m4 / finally.m4
1 dnl -*-autoconf-*-
2
3 ### SYNOPSIS
4 ###
5 ###   FINALLY([IF-SUCCEEDED], [IF-FAILED])
6 ###
7 ### DESCRIPTION
8 ###
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.
14 ###
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'.
21 ###
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.
26 ###
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.
30 ###
31 ### LICENSE
32 ###
33 ###   Copyright (c) 2023 Mark Wooding <mdw@distorted.org.uk>
34 ###
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.
39 ###
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.
44 ###
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/>.
47 ###
48 ###   In particular, no exception to the GPL is granted regarding generated
49 ###   `configure' scripts which are the output of Autoconf.
50
51 AC_DEFUN([FINALLY_GCC_NESTED_FUNCTIONS_TEST_PROGRAM], [AC_LANG_PROGRAM([], [
52   __extension__ __inline__ void nested(void) { ; }
53   nested();
54 ])])
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);
58 ], [
59   __attribute__((cleanup(cleanup_fn))) int x = 0;
60   bamboozle(&x);
61 ])])
62 AC_DEFUN([FINALLY_GCC_REALLY_CLANG_TEST_PROGRAM], [AC_LANG_PROGRAM([], [
63 #ifndef __clang__
64   choke me
65 #endif
66 ])])
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)(); }
70 ], [
71   unsigned x = 1; /* closed over */
72   __attribute__((__unused__, __cleanup__(runblk)))
73     void (^f)(void) = ^{ srand(x); };
74   srand(0);
75 ])])
76
77 dnl Decide whether we can define a plausible `FINALLY' macro.
78 AC_DEFUN([FINALLY_CHECK],
79 [finally_flavour=undecided finally_result="not supported"
80
81 dnl We're going to want to test C code.
82 AC_LANG_PUSH([C])
83
84 case $finally_flavour,$GCC in
85   undecided,yes)
86     dnl Our GCC-ish strategies have a common factor: they depend on
87     dnl `__attribute__((cleanup(...)))' working.  So let's check for that.
88
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 ;;
96     esac
97     ;;
98 esac
99
100 case $finally_flavour,$GCC in
101   undecided,yes)
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.
108
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
115       yes)
116         finally_flavour=GCC_NESTED_FUNCTIONS
117         finally_result="GCC nested functions"
118         ;;
119     esac
120     ;;
121 esac
122
123 case $finally_flavour,$GCC in
124   undecided,yes)
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.
127
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
134     ;;
135   *)
136     finally_clang_p=no
137     ;;
138 esac
139
140 case $finally_flavour,$finally_clang_p in
141   undecided,yes)
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.
147
148     AC_CACHE_CHECK([which hacks are needed to persuade Clang to work with simple blocks],
149                    [finally_cv_clang_blocks_hacks], [
150
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
154
155       dnl Maintain a list of things that we did.  This is the thing
156       dnl we'll cache.
157       unset hacks; win=t
158
159       case $win in
160         t)
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.
166
167           for pass in nil -fblocks; do
168             case $pass in -*) CFLAGS="$CFLAGS -fblocks" ;; esac
169             AC_COMPILE_IFELSE([FINALLY_CLANG_BLOCKS_TEST_PROGRAM],
170                               [win=t], [win=nil])
171             case $win in t) break ;; esac
172           done
173           case $win,$pass in
174             *,nil | nil,*) ;;
175             *) hacks=${hacks+$hacks }$pass
176           esac
177           ;;
178       esac
179
180       case $win in
181         t)
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.
187
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],
191                            [win=t], [win=nil])
192             case $win in t) break ;; esac
193           done
194           case $win,$pass in
195             *,nil | nil,*) ;;
196             *) hacks=${hacks+$hacks }$pass
197           esac
198           ;;
199       esac
200
201       dnl We've finished probing, and it's time to report our findings.
202       case $win in
203         t) finally_cv_clang_blocks_hacks=${hacks-none} ;;
204         *) finally_cv_clang_blocks_hacks=failed ;;
205       esac
206
207       dnl Oh!  And don't forget to undo our fiddling with the compiler and
208       dnl linker settings.
209       CFLAGS=$finally_original_CFLAGS LIBS=$finally_original_LIBS])
210
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
214       failed)
215         finally_flavour=NIL
216         ;;
217       *)
218         finally_flavour=CLANG_BLOCKS FINALLY_CFLAGS= FINALLY_LIBS=
219         finally_result="Clang blocks"
220         for hack in $finally_cv_clang_blocks_hacks; do
221           case $hack in
222             none) ;;
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]) ;;
226           esac
227         done
228         ;;
229     esac
230     ;;
231 esac
232
233 case $finally_flavour in
234   undecided)
235     dnl We've got this far and we've drawn a blank.  Give up.
236     finally_flavour=NIL
237     ;;
238 esac
239
240 AC_LANG_POP([C])
241
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],
245                    [$finally_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])
249
250 dnl Invoke the caller's shell fragments according to our findings.
251 case $finally_flavour in
252   nil)
253     $2
254     ;;
255   *)
256     $1
257     ;;
258 esac
259 ])