Commit | Line | Data |
---|---|---|
d58b8198 MW |
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 | ])]) | |
31ac64c4 MW |
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 | ])]) | |
d58b8198 MW |
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 | ||
31ac64c4 MW |
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 | ||
d58b8198 MW |
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 | ]) |