[tests] Port check scripts to python
diff --git a/src/Makefile.am b/src/Makefile.am
index c841c8d..e11150e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -424,18 +424,18 @@
 test_bimap_LDADD = $(COMPILED_TESTS_LDADD)
 
 dist_check_SCRIPTS = \
-	check-c-linkage-decls.sh \
-	check-externs.sh \
-	check-header-guards.sh \
-	check-includes.sh \
-	check-static-inits.sh \
-	check-symbols.sh \
+	check-c-linkage-decls.py \
+	check-externs.py \
+	check-header-guards.py \
+	check-includes.py \
+	check-static-inits.py \
+	check-symbols.py \
 	$(NULL)
 TESTS += $(dist_check_SCRIPTS)
 
 if !WITH_LIBSTDCXX
 dist_check_SCRIPTS += \
-	check-libstdc++.sh \
+	check-libstdc++.py \
 	$(NULL)
 endif
 
diff --git a/src/check-c-linkage-decls.py b/src/check-c-linkage-decls.py
new file mode 100755
index 0000000..41b9d02
--- /dev/null
+++ b/src/check-c-linkage-decls.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+
+import sys, os
+
+os.chdir (os.environ.get ('srcdir', os.path.dirname (__file__)))
+
+HBHEADERS = os.environ.get ('HBHEADERS', '').split () or \
+	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')]
+HBSOURCES = os.environ.get ('HBSOURCES', '').split () or \
+	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh'))]
+
+stat = 0
+
+for x in HBHEADERS:
+	with open (x, 'r', encoding='utf-8') as f: content = f.read ()
+	if ('HB_BEGIN_DECLS' not in content) or ('HB_END_DECLS' not in content):
+		print ('Ouch, file %s does not have HB_BEGIN_DECLS / HB_END_DECLS, but it should' % x)
+		stat = 1
+
+for x in HBSOURCES:
+	with open (x, 'r', encoding='utf-8') as f: content = f.read ()
+	if ('HB_BEGIN_DECLS' in content) or ('HB_END_DECLS' in content):
+		print ('Ouch, file %s has HB_BEGIN_DECLS / HB_END_DECLS, but it shouldn\'t' % x)
+		stat = 1
+
+sys.exit (stat)
diff --git a/src/check-c-linkage-decls.sh b/src/check-c-linkage-decls.sh
deleted file mode 100755
index 8234abc..0000000
--- a/src/check-c-linkage-decls.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-stat=0
-
-test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
-test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.cc'`
-
-for x in $HBHEADERS; do
-	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
-	if ! grep -q HB_BEGIN_DECLS "$x" || ! grep -q HB_END_DECLS "$x"; then
-		echo "Ouch, file $x does not have HB_BEGIN_DECLS / HB_END_DECLS, but it should"
-		stat=1
-	fi
-done
-for x in $HBSOURCES; do
-	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
-	if grep -q HB_BEGIN_DECLS "$x" || grep -q HB_END_DECLS "$x"; then
-		echo "Ouch, file $x has HB_BEGIN_DECLS / HB_END_DECLS, but it shouldn't"
-		stat=1
-	fi
-done
-
-exit $stat
diff --git a/src/check-externs.py b/src/check-externs.py
new file mode 100755
index 0000000..13aedf8
--- /dev/null
+++ b/src/check-externs.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+import sys, os, re
+
+os.chdir (os.environ.get ('srcdir', os.path.dirname (__file__)))
+
+HBHEADERS = os.environ.get ('HBHEADERS', '').split () or \
+	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')]
+
+stat = 0
+
+print ('Checking that all public symbols are exported with HB_EXTERN')
+for x in HBHEADERS:
+	with open (x, 'r', encoding='utf-8') as f: content = f.read ()
+	for s in re.findall (r'\n.+\nhb_.+\n', content):
+		if not s.startswith ('\nHB_EXTERN '):
+			print ('failure on:', s)
+			stat = 1
+
+sys.exit (stat)
diff --git a/src/check-externs.sh b/src/check-externs.sh
deleted file mode 100755
index a6de375..0000000
--- a/src/check-externs.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-stat=0
-
-test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
-test "x$EGREP" = x && EGREP='grep -E'
-
-
-echo 'Checking that all public symbols are exported with HB_EXTERN'
-
-for x in $HBHEADERS; do
-	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
-	$EGREP -B1 -n '^hb_' /dev/null "$x" |
-	$EGREP -v '(^--|:hb_|-HB_EXTERN )' -A1
-done |
-grep . >&2 && stat=1
-
-exit $stat
diff --git a/src/check-header-guards.py b/src/check-header-guards.py
new file mode 100755
index 0000000..e6c9077
--- /dev/null
+++ b/src/check-header-guards.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python3
+
+import sys, os, re
+
+os.chdir (os.environ.get ('srcdir', os.path.dirname (__file__)))
+
+HBHEADERS = os.environ.get ('HBHEADERS', '').split () or \
+	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')]
+HBSOURCES = os.environ.get ('HBSOURCES', '').split () or \
+	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh'))]
+
+stat = 0
+
+for x in HBHEADERS + HBSOURCES:
+	if not x.endswith ('h') or x == 'hb-gobject-structs.h': continue
+	tag = x.upper ().replace ('.', '_').replace ('-', '_')
+	with open (x, 'r', encoding='utf-8') as f: content = f.read ()
+	if len (re.findall (tag + r'\b', content)) != 3:
+		print ('Ouch, header file %s does not have correct preprocessor guards' % x)
+		stat = 1
+
+sys.exit (stat)
diff --git a/src/check-header-guards.sh b/src/check-header-guards.sh
deleted file mode 100755
index b67640f..0000000
--- a/src/check-header-guards.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-stat=0
-
-test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h' ! -name 'hb-gobject-structs.h'`
-test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`
-
-for x in $HBHEADERS $HBSOURCES; do
-	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
-	echo "$x" | grep -q '[^h]$' && continue;
-	xx=`echo "$x" | sed 's@.*/@@'`
-	tag=`echo "$xx" | tr 'a-z.-' 'A-Z_'`
-	lines=`grep -w "$tag" "$x" | wc -l | sed 's/[ 	]*//g'`
-	if test "x$lines" != x3; then
-		echo "Ouch, header file $x does not have correct preprocessor guards"
-		stat=1
-	fi
-done
-
-exit $stat
diff --git a/src/check-includes.py b/src/check-includes.py
new file mode 100755
index 0000000..b278b3e
--- /dev/null
+++ b/src/check-includes.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+
+import sys, os, re
+
+os.chdir (os.environ.get ('srcdir', os.path.dirname (__file__)))
+
+HBHEADERS = os.environ.get ('HBHEADERS', '').split () or \
+	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')]
+HBSOURCES = os.environ.get ('HBSOURCES', '').split () or \
+	[x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh'))]
+
+stat = 0
+
+print ('Checking that public header files #include "hb-common.h" or "hb.h" first (or none)')
+for x in HBHEADERS:
+	if x == 'hb.h' or x == 'hb-common.h': continue
+	with open (x, 'r', encoding='utf-8') as f: content = f.read ()
+	first = re.findall (r'#.*include.*', content)[0]
+	if first not in ['#include "hb.h"', '#include "hb-common.h"']:
+		print ('failure on %s' % x)
+		stat = 1
+
+print ('Checking that source files #include a private header first (or none)')
+for x in HBSOURCES:
+	with open (x, 'r', encoding='utf-8') as f: content = f.read ()
+	includes = re.findall (r'#.*include.*', content)
+	if includes:
+		if not len (re.findall (r'"hb.*\.hh"', includes[0])):
+			print ('failure on %s' % x)
+			stat = 1
+
+print ('Checking that there is no #include <hb-*.h>')
+for x in HBHEADERS + HBSOURCES:
+	with open (x, 'r', encoding='utf-8') as f: content = f.read ()
+	if re.findall ('#.*include.*<.*hb', content):
+		print ('failure on %s' % x)
+		stat = 1
+
+sys.exit (stat)
diff --git a/src/check-includes.sh b/src/check-includes.sh
deleted file mode 100755
index f938f70..0000000
--- a/src/check-includes.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-stat=0
-
-test "x$HBHEADERS" = x && HBHEADERS=`cd "$srcdir"; find . -maxdepth 1 -name 'hb*.h'`
-test "x$HBSOURCES" = x && HBSOURCES=`cd "$srcdir"; find . -maxdepth 1 -name 'hb-*.cc' -or -name 'hb-*.hh'`
-
-
-echo 'Checking that public header files #include "hb-common.h" or "hb.h" first (or none)'
-
-for x in $HBHEADERS; do
-	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
-	grep '#.*\<include\>' "$x" /dev/null | head -n 1
-done |
-grep -v '"hb-common[.]h"' |
-grep -v '"hb[.]h"' |
-grep -v 'hb-common[.]h:' |
-grep -v 'hb[.]h:' |
-grep . >&2 && stat=1
-
-
-echo 'Checking that source files #include a private header first (or none)'
-
-for x in $HBSOURCES; do
-	test -f "$srcdir/$x" -a ! -f "$x" && x="$srcdir/$x"
-	grep '#.*\<include\>' "$x" /dev/null | head -n 1
-done |
-grep -v '"hb-.*[.]hh"' |
-grep -v 'hb[.]hh' |
-grep . >&2 && stat=1
-
-
-echo 'Checking that there is no #include <hb-*.h>'
-for x in $HBHEADERS $HBSOURCES; do
-	test -f "$srcdir/$x" && x="$srcdir/$x"
-	grep '#.*\<include\>.*<.*hb' "$x" /dev/null >&2 && stat=1
-done
-
-
-exit $stat
diff --git a/src/check-libstdc++.py b/src/check-libstdc++.py
new file mode 100755
index 0000000..142785c
--- /dev/null
+++ b/src/check-libstdc++.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+
+import sys, os, shutil, subprocess
+
+os.chdir (os.environ.get ('srcdir', os.path.dirname (__file__)))
+
+libs = os.environ.get ('libs', '.libs')
+
+ldd = shutil.which ('ldd')
+if ldd:
+	ldd = [ldd]
+else:
+	ldd = shutil.which ('otool')
+	if ldd:
+		ldd = [ldd, '-L'] # otool -L
+	else:
+		print ('check-libstdc++.py: \'ldd\' not found; skipping test')
+		sys.exit (77)
+
+stat = 0
+tested = False
+
+# harfbuzz-icu links to libstdc++ because icu does.
+for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-gobject']:
+	for suffix in ['so', 'dylib']:
+		so = os.path.join (libs, 'lib%s.%s' % (soname, suffix))
+		if not os.path.exists (so): continue
+
+		print ('Checking that we are not linking to libstdc++ or libc++ in %s' % so)
+		ldd_result = subprocess.check_output (ldd + [so])
+		if (b'libstdc++' in ldd_result) or (b'libc++' in ldd_result):
+			print ('Ouch, %s is linked to libstdc++ or libc++' % so)
+			stat = 1
+
+		tested = True
+
+if not tested:
+	print ('check-libstdc++.py: libharfbuzz shared library not found; skipping test')
+	sys.exit (77)
+
+sys.exit (stat)
diff --git a/src/check-libstdc++.sh b/src/check-libstdc++.sh
deleted file mode 100755
index a7b4c49..0000000
--- a/src/check-libstdc++.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-test -z "$libs" && libs=.libs
-stat=0
-
-
-if which ldd 2>/dev/null >/dev/null; then
-	LDD=ldd
-else
-	# macOS specific tool
-	if which otool 2>/dev/null >/dev/null; then
-		LDD="otool -L"
-	else
-		echo "check-libstdc++.sh: 'ldd' not found; skipping test"
-		exit 77
-	fi
-fi
-
-tested=false
-# harfbuzz-icu links to libstdc++ because icu does.
-for soname in harfbuzz harfbuzz-subset harfbuzz-gobject; do
-	for suffix in so dylib; do
-		so=$libs/lib$soname.$suffix
-		if ! test -f "$so"; then continue; fi
-
-		echo "Checking that we are not linking to libstdc++ or libc++ in $so"
-		if $LDD $so | grep 'libstdc[+][+]\|libc[+][+]'; then
-			echo "Ouch, linked to libstdc++ or libc++"
-			stat=1
-		fi
-		tested=true
-	done
-done
-if ! $tested; then
-	echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test"
-	exit 77
-fi
-
-exit $stat
diff --git a/src/check-static-inits.py b/src/check-static-inits.py
new file mode 100755
index 0000000..9e43b81
--- /dev/null
+++ b/src/check-static-inits.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+
+import sys, os, shutil, subprocess, glob, re
+
+builddir = os.environ.get ('builddir', os.path.dirname (__file__))
+libs = os.environ.get ('libs', '.libs')
+
+objdump = shutil.which ('objdump')
+if not objdump:
+	print ('check-static-inits.py: \'ldd\' not found; skipping test')
+	sys.exit (77)
+
+if sys.version_info < (3, 5):
+	print ('check-static-inits.py: needs python 3.5 for recursive support in glob')
+	sys.exit (77)
+
+OBJS = glob.glob (os.path.join (builddir, libs, '**', '*.o'), recursive=True)
+if not OBJS:
+	print ('check-static-inits.py: object files not found; skipping test')
+	sys.exit (77)
+
+stat = 0
+
+for obj in OBJS:
+	result = subprocess.check_output ([objdump, '-t', obj]).decode ('utf-8')
+
+	# Checking that no object file has static initializers
+	for l in re.findall (r'^.*\.[cd]tors.*$', result, re.MULTILINE):
+		if not re.match (r'.*\b0+\b', l):
+			print ('Ouch, %s has static initializers/finalizers' % obj)
+			stat = 1
+
+	# Checking that no object file has lazy static C++ constructors/destructors or other such stuff
+	if ('__cxa_' in result) and ('__ubsan_handle' not in result):
+		print ('Ouch, %s has lazy static C++ constructors/destructors or other such stuff' % obj)
+		stat = 1
+
+sys.exit (stat)
diff --git a/src/check-static-inits.sh b/src/check-static-inits.sh
deleted file mode 100755
index cdfdee8..0000000
--- a/src/check-static-inits.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-test -z "$builddir" && builddir=.
-stat=0
-
-if which objdump 2>/dev/null >/dev/null; then
-	:
-else
-	echo "check-static-inits.sh: 'objdump' not found; skipping test"
-	exit 77
-fi
-
-OBJS=$(find $builddir/ -name '*.o')
-if test "x`echo $OBJS`" = "x$OBJS" 2>/dev/null >/dev/null; then
-	echo "check-static-inits.sh: object files not found; skipping test"
-	exit 77
-fi
-
-tested=false
-
-echo "Checking that no object file has static initializers"
-for obj in $OBJS; do
-	if objdump -t "$obj" | grep '[.][cd]tors' | grep -v '\<00*\>'; then
-		echo "Ouch, $obj has static initializers/finalizers"
-		stat=1
-	fi
-	tested=true
-done
-
-echo "Checking that no object file has lazy static C++ constructors/destructors or other such stuff"
-for obj in $OBJS; do
-	if objdump -t "$obj" | grep -q '__cxa_' && ! objdump -t "$obj" | grep -q __ubsan_handle; then
-		objdump -t "$obj" | grep '__cxa_'
-		echo "Ouch, $obj has lazy static C++ constructors/destructors or other such stuff"
-		stat=1
-	fi
-	tested=true
-done
-
-if ! $tested; then
-	echo "check-static-inits.sh: no objects found; skipping test"
-	exit 77
-fi
-
-exit $stat
diff --git a/src/check-symbols.py b/src/check-symbols.py
new file mode 100755
index 0000000..92f44cd
--- /dev/null
+++ b/src/check-symbols.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python3
+
+import sys, os, shutil, subprocess, re, difflib
+
+os.environ['LC_ALL'] = 'C' # otherwise 'nm' prints in wrong order
+
+builddir = os.environ.get ('builddir', os.path.dirname (__file__))
+libs = os.environ.get ('libs', '.libs')
+
+IGNORED_SYMBOLS = '|'.join(['_fini', '_init', '_fdata', '_ftext', '_fbss',
+	'__bss_start', '__bss_start__', '__bss_end__', '_edata', '_end', '_bss_end__',
+	'__end__', '__gcov_.*', 'llvm_.*', 'flush_fn_list', 'writeout_fn_list'])
+
+nm = shutil.which ('nm')
+if not nm:
+	print ('check-symbols.py: \'nm\' not found; skipping test')
+	sys.exit (77)
+
+cxxflit = shutil.which ('c++filt')
+
+tested = False
+stat = 0
+
+for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-icu', 'harfbuzz-gobject']:
+	for suffix in ['so', 'dylib']:
+		so = os.path.join (builddir, libs, 'lib%s.%s' % (soname, suffix))
+		if not os.path.exists (so): continue
+
+		# On macOS, C symbols are prefixed with _
+		symprefix = '_' if suffix == 'dylib' else ''
+
+		EXPORTED_SYMBOLS = [s.split ()[2]
+							for s in re.findall (r'^.+ [BCDGIRST] .+$', subprocess.check_output ([nm, so]).decode ('utf-8'), re.MULTILINE)
+							if not re.match (r'.* %s(%s)\b' % (symprefix, IGNORED_SYMBOLS), s)]
+
+		# run again c++flit also if is available
+		if cxxflit:
+			EXPORTED_SYMBOLS = subprocess.check_output (
+				[cxxflit], input='\n'.join (EXPORTED_SYMBOLS).encode ()
+			).decode ('utf-8').splitlines ()
+
+		prefix = (symprefix + os.path.basename (so)).replace ('libharfbuzz', 'hb').replace ('-', '_').split ('.')[0]
+
+		print ('Checking that %s does not expose internal symbols' % so)
+		suspicious_symbols = [x for x in EXPORTED_SYMBOLS if not re.match (r'^%s(_|$)' % prefix, x)]
+		if suspicious_symbols:
+			print ('Ouch, internal symbols exposed:', suspicious_symbols)
+			stat = 1
+
+		def_path = os.path.join (builddir, soname + '.def')
+		if not os.path.exists (def_path):
+			print ('\'%s\' not found; skipping' % def_path)
+		else:
+			print ('Checking that %s has the same symbol list as %s' % (so, def_path))
+			with open (def_path, 'r', encoding='utf-8') as f: def_file = f.read ()
+			diff_result = list (difflib.context_diff (
+				def_file.splitlines (),
+				['EXPORTS'] + [re.sub ('^%shb' % symprefix, 'hb', x) for x in EXPORTED_SYMBOLS] +
+					# cheat: copy the last line from the def file!
+					[def_file.splitlines ()[-1]]
+			))
+
+			if diff_result:
+				print ('\n'.join (diff_result))
+				stat = 1
+
+			tested = True
+
+if not tested:
+	print ('check-symbols.sh: no shared libraries found; skipping test')
+	sys.exit (77)
+
+sys.exit (stat)
diff --git a/src/check-symbols.sh b/src/check-symbols.sh
deleted file mode 100755
index a6ca1c2..0000000
--- a/src/check-symbols.sh
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-test -z "$builddir" && builddir=.
-test -z "$libs" && libs=.libs
-stat=0
-
-IGNORED_SYMBOLS='_fini\|_init\|_fdata\|_ftext\|_fbss\|__bss_start\|__bss_start__\|__bss_end__\|_edata\|_end\|_bss_end__\|__end__\|__gcov_.*\|llvm_.*\|flush_fn_list\|writeout_fn_list'
-
-if which nm 2>/dev/null >/dev/null; then
-	:
-else
-	echo "check-symbols.sh: 'nm' not found; skipping test"
-	exit 77
-fi
-
-tested=false
-for soname in harfbuzz harfbuzz-subset harfbuzz-icu harfbuzz-gobject; do
-	for suffix in so dylib; do
-		so=$libs/lib$soname.$suffix
-		if ! test -f "$so"; then continue; fi
-
-		# On macOS, C symbols are prefixed with _
-		symprefix=
-		if test $suffix = dylib; then symprefix=_; fi
-
-		EXPORTED_SYMBOLS=`nm "$so" | grep ' [BCDGIRST] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`
-
-		prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
-
-		echo "Checking that $so does not expose internal symbols"
-		if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}\(_\|$\)"; then
-			echo "Ouch, internal symbols exposed"
-			stat=1
-		fi
-
-		def=$builddir/$soname.def
-		if ! test -f "$def"; then
-			echo "'$def' not found; skipping"
-		else
-			echo "Checking that $so has the same symbol list as $def"
-			{
-				echo EXPORTS
-				echo "$EXPORTED_SYMBOLS" | sed -e "s/^${symprefix}hb/hb/g"
-				# cheat: copy the last line from the def file!
-				tail -n1 "$def"
-			} | c++filt | diff "$def" - >&2 || stat=1
-
-			tested=true
-		fi
-	done
-done
-if ! $tested; then
-	echo "check-symbols.sh: no shared libraries found; skipping test"
-	exit 77
-fi
-
-exit $stat
diff --git a/src/meson.build b/src/meson.build
index 698f006..98d2e92 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -494,35 +494,6 @@
       install: false,
     ))
   endforeach
-
-  if host_machine.system() != 'windows' and not meson.is_cross_build()
-    # Some of them should be ported to python
-    dist_check_script = [
-      'check-c-linkage-decls.sh',
-      'check-externs.sh',
-      'check-header-guards.sh',
-      'check-static-inits.sh',
-    ]
-    if not get_option('amalgam')
-      dist_check_script += 'check-includes.sh'
-    endif
-    if false and not get_option('with_libstdcxx')
-      # enable this once https://github.com/mesonbuild/meson/pull/6838 hits a release
-      # and make that version (i.e. 0.55) our minimum build requirement
-      dist_check_script += 'check-libstdc++.sh' # See https://github.com/harfbuzz/harfbuzz/issues/2276
-    endif
-
-    env = environment()
-    env.set('srcdir', meson.current_source_dir())
-    env.set('builddir', meson.current_build_dir())
-    env.set('libs', meson.current_build_dir()) # TODO: Merge this with builddir after autotools removal
-    env.set('HBSOURCES', ' '.join(hb_sources))
-    env.set('HBHEADERS', ' '.join(hb_headers))
-
-    foreach name : dist_check_script
-      test(name, find_program(name), env: env)
-    endforeach
-  endif
 endif
 
 pkgmod.generate(libharfbuzz,
@@ -689,10 +660,34 @@
   libharfbuzz_gobject_dep = dependency('', required: false)
 endif
 
-if get_option('tests').enabled() and host_machine.system() != 'windows' and not meson.is_cross_build()
-  test('check-symbols.sh', find_program('check-symbols.sh'),
-    depends: defs_list,
-    env: env)
+if get_option('tests').enabled()
+  dist_check_script = [
+    'check-c-linkage-decls',
+    'check-externs',
+    'check-header-guards',
+    'check-includes',
+  ]
+
+  env = environment()
+  env.set('srcdir', meson.current_source_dir())
+  env.set('builddir', meson.current_build_dir())
+  env.set('libs', meson.current_build_dir()) # TODO: Merge this with builddir after autotools removal
+  if not get_option('amalgam')
+    env.set('HBSOURCES', ' '.join(hb_sources))
+  endif
+  env.set('HBHEADERS', ' '.join(hb_headers))
+
+  if cpp.get_id() != 'msvc' and not meson.is_cross_build() # ensure the local tools are usable
+    # See https://github.com/mesonbuild/meson/pull/6838
+    if meson.version().version_compare('>=0.54.999') and not get_option('with_libstdcxx')
+      dist_check_script += 'check-libstdc++'
+    endif
+    dist_check_script += ['check-static-inits', 'check-symbols']
+  endif
+
+  foreach name : dist_check_script
+    test(name, find_program(name + '.py'), env: env, depends: defs_list)
+  endforeach
 endif
 
 install_headers(hb_headers + hb_subset_headers, subdir: meson.project_name())