all.sh: list components automatically

Extract the list of available components by looking for definitions of
functions called component_xxx. The previous code explicitly listed
all components in run_all_components, which opened the risk of
forgetting to list a component there.

Add a conditional execution facility: if a function support_xxx exists
and returns false then component_xxx is not executed (except when the
command line lists an explicit set of components to execute).
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index 5fdb141..41a6f88 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -62,6 +62,8 @@
 #      * component_check_XXX: quick tests that aren't worth parallelizing
 #      * component_build_XXX: build things but don't run them
 #      * component_test_XXX: build and test
+#  * support_XXX: if support_XXX exists and returns false then
+#    component_XXX is not run by default.
 #  * post_XXX: things to do after running the tests.
 #  * other: miscellaneous support functions.
 #
@@ -105,7 +107,6 @@
     ALL_EXCEPT=0
     MEMORY=0
     FORCE=0
-    INTROSPECTION_MODE=
     KEEP_GOING=0
     RUN_ARMCC=1
 
@@ -125,12 +126,29 @@
     if [ -z "${MAKEFLAGS+set}" ]; then
         export MAKEFLAGS="-j"
     fi
+
+    # Gather the list of available components. These are the functions
+    # defined in this script whose name starts with "component_".
+    # Parse the script with sed, because in sh there is no way to list
+    # defined functions.
+    ALL_COMPONENTS=$(sed -n 's/^ *component_\([0-9A-Z_a-z]*\) *().*/\1/p' <"$0")
+
+    # Exclude components that are not supported on this platform.
+    SUPPORTED_COMPONENTS=
+    for component in $ALL_COMPONENTS; do
+        case $(type "support_$component" 2>&1) in
+            *' function'*)
+                if ! support_$component; then continue; fi;;
+        esac
+        SUPPORTED_COMPONENTS="$SUPPORTED_COMPONENTS $component"
+    done
 }
 
-# Test whether $1 is excluded via $COMPONENTS (a space-separated list of
-# wildcard patterns).
-component_is_excluded()
+# Test whether $1 is excluded via the command line.
+is_component_excluded()
 {
+    # Is $1 excluded via $COMPONENTS (a space-separated list of wildcard
+    # patterns)?
     set -f
     for pattern in $COMPONENTS; do
         set +f
@@ -149,7 +167,8 @@
 
 Special options:
   -h|--help             Print this help and exit.
-  --list-components     List available test components and exit.
+  --list-all-components List all available test components and exit.
+  --list-components     List components supported on this platform and exit.
 
 General options:
   -f|--force            Force the tests to overwrite any modified files.
@@ -285,7 +304,8 @@
             --gnutls-serv) shift; GNUTLS_SERV="$1";;
             --help|-h) usage; exit;;
             --keep-going|-k) KEEP_GOING=1;;
-            --list-components) INTROSPECTION_MODE=list_components;;
+            --list-all-components) printf '%s\n' $ALL_COMPONENTS; exit;;
+            --list-components) printf '%s\n' $SUPPORTED_COMPONENTS; exit;;
             --memory|-m) MEMORY=1;;
             --no-armcc) RUN_ARMCC=0;;
             --no-force) FORCE=0;;
@@ -854,6 +874,12 @@
     msg "test: i386, make, gcc -O0 (ASan build)"
     make test
 }
+support_test_m32_o0 () {
+    case $(uname -m) in
+        *64*) true;;
+        *) false;;
+    esac
+}
 
 component_test_m32_o1 () {
     # Build again with -O1, to compile in the i386 specific inline assembly
@@ -864,6 +890,9 @@
     msg "test: i386, make, gcc -O1 (ASan build)"
     make test
 }
+support_test_m32_o1 () {
+    support_test_m32_o0 "$@"
+}
 
 component_test_mx32 () {
     msg "build: 64-bit ILP32, make, gcc" # ~ 30s
@@ -873,6 +902,12 @@
     msg "test: 64-bit ILP32, make, gcc"
     make test
 }
+support_test_mx32 () {
+    case $(uname -m) in
+        amd64|x86_64) true;;
+        *) false;;
+    esac
+}
 
 component_test_have_int32 () {
     msg "build: gcc, force 32-bit bignum limbs"
@@ -1164,79 +1199,8 @@
 #### Run all the things
 ################################################################
 
-run_all_components () {
-    # Small things
-    run_component component_check_recursion
-    run_component component_check_generated_files
-    run_component component_check_doxy_blocks
-    run_component component_check_files
-    run_component component_check_names
-    run_component component_check_doxygen_warnings
-
-    # Test many different configurations
-    run_component component_test_default_cmake_gcc_asan
-    run_component component_test_ref_configs
-    run_component component_test_sslv3
-    run_component component_test_no_renegotiation
-    run_component component_test_rsa_no_crt
-    run_component component_test_small_ssl_out_content_len
-    run_component component_test_small_ssl_in_content_len
-    run_component component_test_small_ssl_dtls_max_buffering
-    run_component component_test_small_mbedtls_ssl_dtls_max_buffering
-    run_component component_test_full_cmake_clang
-    run_component component_build_deprecated
-    run_component component_test_depends_curves
-    run_component component_test_depends_hashes
-    run_component component_test_depends_pkalgs
-    run_component component_build_key_exchanges
-    run_component component_build_default_make_gcc_and_cxx
-    run_component component_test_check_params_without_platform
-    run_component component_test_check_params_silent
-    run_component component_test_no_platform
-    run_component component_build_no_std_function
-    run_component component_build_no_ssl_srv
-    run_component component_build_no_ssl_cli
-    run_component component_build_no_sockets
-    run_component component_test_no_max_fragment_length
-    run_component component_test_no_max_fragment_length_small_ssl_out_content_len
-    run_component component_test_null_entropy
-    run_component component_test_platform_calloc_macro
-    run_component component_test_aes_fewer_tables
-    run_component component_test_aes_rom_tables
-    run_component component_test_aes_fewer_tables_and_rom_tables
-    run_component component_test_make_shared
-    case $(uname -m) in
-        amd64|x86_64)
-            run_component component_test_m32_o0
-            run_component component_test_m32_o1
-            run_component component_test_mx32
-            ;;
-    esac
-    run_component component_test_have_int32
-    run_component component_test_have_int64
-    run_component component_test_no_udbl_division
-    run_component component_test_no_64bit_multiplication
-    run_component component_build_arm_none_eabi_gcc
-    run_component component_build_arm_none_eabi_gcc_no_udbl_division
-    run_component component_build_arm_none_eabi_gcc_no_64bit_multiplication
-    run_component component_build_armcc
-    run_component component_test_allow_sha1
-    run_component component_build_mingw
-    run_component component_test_memsan
-    run_component component_test_memcheck
-    run_component component_test_cmake_out_of_source
-
-    # More small things
-    run_component component_test_zeroize
-    run_component component_check_python_files
-    run_component component_check_generate_test_code
-}
-
 # Run one component and clean up afterwards.
 run_component () {
-    if [ $ALL_EXCEPT -ne 0 ] && component_is_excluded "$1"; then
-        return
-    fi
     # Back up the configuration in case the component modifies it.
     # The cleanup function will restore it.
     cp -p "$CONFIG_H" "$CONFIG_BAK"
@@ -1250,47 +1214,33 @@
 pre_initialize_variables
 pre_parse_command_line "$@"
 
-case "$INTROSPECTION_MODE" in
-    list_components)
-        components=
-        newline='
-'
-        run_component () {
-            components="${components}${newline}${1#component_}"
-        }
-        ;;
-
-    *)
-        pre_check_git
-        build_status=0
-        if [ $KEEP_GOING -eq 1 ]; then
-            pre_setup_keep_going
-        else
-            record_status () {
-                "$@"
-            }
-        fi
-        pre_print_configuration
-        pre_check_tools
-        pre_print_tools
-        cleanup
-        ;;
-esac
+pre_check_git
+build_status=0
+if [ $KEEP_GOING -eq 1 ]; then
+    pre_setup_keep_going
+else
+    record_status () {
+        "$@"
+    }
+fi
+pre_print_configuration
+pre_check_tools
+pre_print_tools
+cleanup
 
 if [ -n "$COMPONENTS" ] && [ $ALL_EXCEPT -eq 0 ]; then
+    # Run the components passed on the command line.
     for component in $COMPONENTS; do
-          run_component "component_$component"
+        run_component "component_$component"
     done
 else
-    run_all_components
+    # Run all components except those excluded on the command line.
+    for component in $SUPPORTED_COMPONENTS; do
+        if ! is_component_excluded "$component"; then
+            run_component "component_$component"
+        fi
+    done
 fi
 
 # We're done.
-case "$INTROSPECTION_MODE" in
-    list_components)
-        echo "$components" | sort
-        ;;
-    *)
-        post_report
-        ;;
-esac
+post_report