chiark / gitweb /
Add half-hearted support for Clang, because its `blocks' are deficient.
[finally] / m4 / finally.m4
index 70c2d83dfee362bf0c49f1d6ba1ee25df0186b76..d714f2c243862eb9728b772969b6ba224a74a867 100644 (file)
@@ -59,6 +59,20 @@ AC_DEFUN([FINALLY_GCC_ATTRIBUTE_CLEANUP_TEST_PROGRAM], [AC_LANG_PROGRAM([
   __attribute__((cleanup(cleanup_fn))) int x = 0;
   bamboozle(&x);
 ])])
+AC_DEFUN([FINALLY_GCC_REALLY_CLANG_TEST_PROGRAM], [AC_LANG_PROGRAM([], [
+#ifndef __clang__
+  choke me
+#endif
+])])
+AC_DEFUN([FINALLY_CLANG_BLOCKS_TEST_PROGRAM], [AC_LANG_PROGRAM([
+  extern void srand(unsigned); /* might throw? */
+  static __inline__ void runblk(void (^*f)(void)) { (*f)(); }
+], [
+  unsigned x = 1; /* closed over */
+  __attribute__((__unused__, __cleanup__(runblk)))
+    void (^f)(void) = ^{ srand(x); };
+  srand(0);
+])])
 
 dnl Decide whether we can define a plausible `FINALLY' macro.
 AC_DEFUN([FINALLY_CHECK],
@@ -106,6 +120,116 @@ case $finally_flavour,$GCC in
     ;;
 esac
 
+case $finally_flavour,$GCC in
+  undecided,yes)
+    dnl That didn't work.  I guess it's not really GCC after all.  Maybe it's
+    dnl Clang wearing a false moustache.
+
+    AC_CACHE_CHECK([whether the impostor GNU C compiler is really Clang],
+                  [finally_cv_gcc_really_clang_p], [
+      AC_COMPILE_IFELSE([FINALLY_GCC_REALLY_CLANG_TEST_PROGRAM],
+                       [finally_cv_gcc_really_clang_p=yes],
+                       [finally_cv_gcc_really_clang_p=no])])
+    finally_clang_p=$finally_cv_gcc_really_clang_p
+    ;;
+  *)
+    finally_clang_p=no
+    ;;
+esac
+
+case $finally_flavour,$finally_clang_p in
+  undecided,yes)
+    dnl Yup.  Not a particularly convincing disguise, really.  Well, at least
+    dnl Clang has a thing which looks a bit like nested functions, and a bit
+    dnl like closures, only with a terrible syntax, which it calls `blocks'.
+    dnl Unfortunately, we may or may not require varying levels of ceremony
+    dnl to make this work.
+
+    AC_CACHE_CHECK([which hacks are needed to persuade Clang to work with simple blocks],
+                  [finally_cv_clang_blocks_hacks], [
+
+      dnl We'll need to mess with the compiler flags and libraries,
+      dnl so make sure we can put them back again afterwards.
+      finally_original_CFLAGS=$CFLAGS finally_original_LIBS=$LIBS
+
+      dnl Maintain a list of things that we did.  This is the thing
+      dnl we'll cache.
+      unset hacks; win=t
+
+      case $win in
+       t)
+         dnl OK.  First thing, we need to get the compiler proper to accept
+         dnl the `blocks' syntax.  I guess the syntax is so hideous that
+         dnl Clang is sometimes ashamed to admit to parsing it unless we
+         dnl twist its arm.  Apparently `-fblocks' is unnecessary on some
+         dnl targets, so let's see if we can manage without.
+
+         for pass in nil -fblocks; do
+           case $pass in -*) CFLAGS="$CFLAGS -fblocks" ;; esac
+           AC_COMPILE_IFELSE([FINALLY_CLANG_BLOCKS_TEST_PROGRAM],
+                             [win=t], [win=nil])
+           case $win in t) break ;; esac
+         done
+         case $win,$pass in
+           *,nil | nil,*) ;;
+           *) hacks=${hacks+$hacks }$pass
+         esac
+         ;;
+      esac
+
+      case $win in
+       t)
+         dnl We got the compiler to accept the unpleasant syntax.  The next
+         dnl problem is that, technically, the generated code depends on a
+         dnl runtime support library; only our use for these things is so
+         dnl simple that, at reasonable optimization settings, we can do
+         dnl without.  So let's see if we're in that situation.
+
+         for pass in nil -lBlocksRuntime; do
+           case $pass in -l*) LIBS="$LIBS $pass" ;; esac
+           AC_LINK_IFELSE([FINALLY_CLANG_BLOCKS_TEST_PROGRAM],
+                          [win=t], [win=nil])
+           case $win in t) break ;; esac
+         done
+         case $win,$pass in
+           *,nil | nil,*) ;;
+           *) hacks=${hacks+$hacks }$pass
+         esac
+         ;;
+      esac
+
+      dnl We've finished probing, and it's time to report our findings.
+      case $win in
+       t) finally_cv_clang_blocks_hacks=${hacks-none} ;;
+       *) finally_cv_clang_blocks_hacks=failed ;;
+      esac
+
+      dnl Oh!  And don't forget to undo our fiddling with the compiler and
+      dnl linker settings.
+      CFLAGS=$finally_original_CFLAGS LIBS=$finally_original_LIBS])
+
+    dnl That was fun.  Now we know how to persuade Clang to support these
+    dnl block thingummies (or not).  Report our findings.
+    case $finally_cv_clang_blocks_hacks in
+      failed)
+       finally_flavour=NIL
+       ;;
+      *)
+       finally_flavour=CLANG_BLOCKS FINALLY_CFLAGS= FINALLY_LIBS=
+       finally_result="Clang blocks"
+       for hack in $finally_cv_clang_blocks_hacks; do
+         case $hack in
+           none) ;;
+           -l* | -L*) FINALLY_LIBS=${FINALLY_LIBS:+ $FINALLY_LIBS}$hack ;;
+           -*) FINALLY_CFLAGS=${FINALLY_CFLAGS:+ $FINALLY_CFLAGS}$hack ;;
+           *) AC_MSG_ERROR([confused by unexpected hack $hack]) ;;
+         esac
+       done
+       ;;
+    esac
+    ;;
+esac
+
 case $finally_flavour in
   undecided)
     dnl We've got this far and we've drawn a blank.  Give up.