Merge branch 'fix-leaking-g_get_language_names_with_category' into 'master'

gcharset: fix leaking g_get_language_names_with_category

See merge request GNOME/glib!338
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0a3ed44..9dd25b7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: registry.gitlab.gnome.org/gnome/glib/master:v7
+image: registry.gitlab.gnome.org/gnome/glib/master:v8
 
 stages:
   - build
@@ -12,6 +12,7 @@
 variables:
   MESON_TEST_TIMEOUT_MULTIPLIER: 2
   G_MESSAGES_DEBUG: all
+  MESON_COMMON_OPTIONS: "--buildtype debug --fatal-meson-warnings"
 
 fedora-x86_64:
   stage: build
@@ -20,7 +21,7 @@
   variables:
     CFLAGS: "-coverage -ftest-coverage -fprofile-arcs"
   script:
-    - meson --buildtype debug --werror -Dsystemtap=true -Ddtrace=true -Dfam=true _build .
+    - meson ${MESON_COMMON_OPTIONS} --werror -Dsystemtap=true -Ddtrace=true -Dfam=true _build
     - ninja -C _build
     - mkdir -p _coverage
     - lcov --rc lcov_branch_coverage=1 --directory _build --capture --initial --output-file "_coverage/${CI_JOB_NAME}-baseline.lcov"
@@ -76,21 +77,21 @@
   <<: *cross-template
   script:
     # FIXME: add --werror
-    - meson --cross-file=/opt/cross_file_android_arm64_21.txt -Diconv=gnu -Dinternal_pcre=true --buildtype debug _build
+    - meson ${MESON_COMMON_OPTIONS} --cross-file=/opt/cross_file_android_arm64_21.txt -Diconv=gnu -Dinternal_pcre=true _build
     - ninja -C _build
 
 cross-android_api28_arm64:
   <<: *cross-template
   script:
     # FIXME: add --werror
-    - meson --cross-file=/opt/cross_file_android_arm64_28.txt -Dinternal_pcre=true --buildtype debug _build
+    - meson ${MESON_COMMON_OPTIONS} --cross-file=/opt/cross_file_android_arm64_28.txt -Dinternal_pcre=true _build
     - ninja -C _build
 
 cross-mingw64:
   <<: *cross-template
   script:
     # FIXME: Add --werror
-    - meson --cross-file=/opt/cross_file_mingw64.txt --buildtype debug _build
+    - meson ${MESON_COMMON_OPTIONS} --cross-file=/opt/cross_file_mingw64.txt _build
     - ninja -C _build
 
 msys2-mingw32:
@@ -127,7 +128,6 @@
       - _build/meson-logs
 
 freebsd-11-x86_64:
-  when: manual
   stage: build
   tags:
     # To run a FreeBSD builder, install gitlab-runner package and start both
@@ -150,18 +150,17 @@
     # FreeBSD iconv doesn't handle transliteration, so we use GNU libiconv here.
     # FreeBSD supports xattr, but its API is different from Linux xattr.
     # FIXME: extattr(2) support: https://gitlab.gnome.org/GNOME/glib/issues/1404
-    - meson -Db_lundef=false -Diconv=gnu -Dxattr=false --buildtype debug _build
+    - meson ${MESON_COMMON_OPTIONS} -Db_lundef=false -Diconv=gnu -Dxattr=false _build
     - ninja -C _build
     - meson test -C _build --timeout-multiplier "${MESON_TEST_TIMEOUT_MULTIPLIER}"
-  # FIXME: Remove this when we have a stable FreeBSD runner
-  # https://gitlab.gnome.org/Infrastructure/GitLab/issues/286
-  allow_failure: true
   except:
     - tags
   artifacts:
     name: "glib-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}"
     when: always
     paths:
+      - "_build/config.h"
+      - "_build/glib/glibconfig.h"
       - "_build/meson-logs"
 
 coverage:
@@ -191,7 +190,7 @@
   only:
     - tags
   script:
-    - meson --buildtype release -Dgtk_doc=true -Dman=true _build
+    - meson --buildtype release --fatal-meson-warnings -Dgtk_doc=true -Dman=true _build
     - cd _build
     - ninja dist
     - ninja glib-doc gobject-doc gio-doc
diff --git a/.gitlab-ci/Dockerfile b/.gitlab-ci/Dockerfile
index 4d7efa3..1a0dd11 100644
--- a/.gitlab-ci/Dockerfile
+++ b/.gitlab-ci/Dockerfile
@@ -52,7 +52,7 @@
 
 COPY cross_file_mingw64.txt /opt
 
-RUN pip3 install meson==0.47.0
+RUN pip3 install meson==0.48.0
 
 ARG HOST_USER_ID=5555
 ENV HOST_USER_ID ${HOST_USER_ID}
diff --git a/.gitlab-ci/run-docker.sh b/.gitlab-ci/run-docker.sh
index dc36d43..33df424 100755
--- a/.gitlab-ci/run-docker.sh
+++ b/.gitlab-ci/run-docker.sh
@@ -2,7 +2,7 @@
 
 set -e
 
-TAG="registry.gitlab.gnome.org/gnome/glib/master:v7"
+TAG="registry.gitlab.gnome.org/gnome/glib/master:v8"
 
 docker build --build-arg HOST_USER_ID="$UID" --tag "${TAG}" \
     --file "Dockerfile" .
diff --git a/.gitlab-ci/test-msvc.bat b/.gitlab-ci/test-msvc.bat
index 9b674f7..c3b19cd 100644
--- a/.gitlab-ci/test-msvc.bat
+++ b/.gitlab-ci/test-msvc.bat
@@ -5,7 +5,7 @@
 @echo on
 
 :: FIXME: make warnings fatal
-pip3 install --upgrade --user meson==0.47.0  || goto :error
+pip3 install --upgrade --user meson==0.48.0  || goto :error
 meson _build || goto :error
 ninja -C _build || goto :error
 
diff --git a/.gitlab-ci/test-msys2.sh b/.gitlab-ci/test-msys2.sh
index 48e18e9..0c375bb 100755
--- a/.gitlab-ci/test-msys2.sh
+++ b/.gitlab-ci/test-msys2.sh
@@ -33,7 +33,7 @@
 mkdir -p _ccache
 export CCACHE_BASEDIR="$(pwd)"
 export CCACHE_DIR="${CCACHE_BASEDIR}/_ccache"
-pip3 install --upgrade --user meson==0.47.0
+pip3 install --upgrade --user meson==0.48.0
 export PATH="$HOME/.local/bin:$PATH"
 export CFLAGS="-coverage -ftest-coverage -fprofile-arcs"
 DIR="$(pwd)"
diff --git a/configure.ac b/configure.ac
index fd748a4..b69b27c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -639,6 +639,7 @@
 AC_CHECK_HEADERS([linux/magic.h])
 AC_CHECK_HEADERS([termios.h])
 AC_CHECK_HEADERS([alloca.h])
+AC_CHECK_HEADERS([spawn.h])
 
 # Some versions of MSC lack these
 AC_CHECK_HEADERS([dirent.h sys/time.h])
@@ -1786,9 +1787,13 @@
 ])
 
 if test x$have_libmount = xyes; then
+  LIBMOUNT_REQUIRES="mount >= 2.23"
   AC_DEFINE(HAVE_LIBMOUNT, 1, [Define if libmount is available])
+else
+  LIBMOUNT_REQUIRES=""
 fi
 AM_CONDITIONAL(HAVE_LIBMOUNT, [test x$have_libmount = xyes])
+AC_SUBST([LIBMOUNT_REQUIRES])
 
 dnl ****************************************
 dnl *** platform dependent source checks ***
@@ -1853,7 +1858,7 @@
 
 AIX_COMPILE_INFO="AIX's C compiler needs to be called by a different name, when
 		linking threaded applications. As GLib cannot do that 
-		automatically, you will get an linkg error everytime you are 
+		automatically, you will get an linking error every time you are
 		not using the right compiler. In that case you have to relink 
 		with the right compiler. Ususally just '_r' is appended 
 		to the compiler name."
diff --git a/docs/reference/gio/gdbus-codegen.xml b/docs/reference/gio/gdbus-codegen.xml
index e419208..fdd367b 100644
--- a/docs/reference/gio/gdbus-codegen.xml
+++ b/docs/reference/gio/gdbus-codegen.xml
@@ -869,7 +869,7 @@
       subclass. For example, to handle
       <function>HelloWorld()</function> method invocations, set the
       vfunc for <function>handle_hello_hello_world()</function> in the
-      <type>MyAppFrobberIface</type> structure. Similary, to handle
+      <type>MyAppFrobberIface</type> structure. Similarly, to handle
       the <parameter>net.Corp.MyApp.Frobber:Verbose</parameter>
       property override the <parameter>:verbose</parameter> #GObject
       property from the subclass. To emit a signal, use
@@ -941,7 +941,7 @@
       received. The queue is drained in an idle handler (which is called from the
       <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
       of the thread where the skeleton object was
-      contructed) and will cause emissions of the <ulink
+      constructed) and will cause emissions of the <ulink
       url="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties">org.freedesktop.DBus.Properties::PropertiesChanged</ulink>
       signal with all the properties that have changed. Use
       g_dbus_interface_skeleton_flush() or
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 3b7cc28..f7efd2b 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1621,6 +1621,7 @@
 g_desktop_app_info_get_string
 g_desktop_app_info_get_locale_string
 g_desktop_app_info_get_boolean
+g_desktop_app_info_get_string_list
 g_desktop_app_info_has_key
 GDesktopAppLaunchCallback
 g_desktop_app_info_launch_uris_as_manager
diff --git a/docs/reference/gio/gio.xml b/docs/reference/gio/gio.xml
index f2635d0..c348c43 100644
--- a/docs/reference/gio/gio.xml
+++ b/docs/reference/gio/gio.xml
@@ -374,7 +374,7 @@
               </varlistentry>
               <varlistentry>
                 <term><option>-D</option>, <option>--direct=<replaceable>LOCATION</replaceable></option></term>
-                <listitem><para>Monitor the file directly. This allows to capture changes made via hardlinks.</para></listitem>
+                <listitem><para>Monitor the file directly. This allows changes made via hardlinks to be captured.</para></listitem>
               </varlistentry>
               <varlistentry>
                 <term><option>-s</option>, <option>--silent=<replaceable>LOCATION</replaceable></option></term>
@@ -599,7 +599,7 @@
           <arg choice="plain" rep="repeat"><replaceable>VALUE</replaceable></arg>
         </term>
         <listitem>
-          <para>Allows to set a file attribute on a file.</para>
+          <para>Sets a file attribute on a file.</para>
           <para>File attributes can be specified with their GIO name, e.g
             standard::icon. Note that not all GIO file attributes are writable.
             Use the --query-writable option of the info command to list
diff --git a/docs/reference/gio/meson.build b/docs/reference/gio/meson.build
index c7ca62a..f1880cc 100644
--- a/docs/reference/gio/meson.build
+++ b/docs/reference/gio/meson.build
@@ -221,8 +221,8 @@
     ],
     fixxref_args: [
       '--html-dir=' + docpath,
-      '--extra-dir=' + join_paths(meson.current_build_dir(), '../glib/html'),
-      '--extra-dir=' + join_paths(meson.current_build_dir(), '../gobject/html'),
+      '--extra-dir=' + join_paths('gio', '..', 'glib', 'html'),
+      '--extra-dir=' + join_paths('gio', '..', 'gobject', 'html'),
     ],
     install: true
   )
diff --git a/docs/reference/glib/meson.build b/docs/reference/glib/meson.build
index fbe3422..f14824b 100644
--- a/docs/reference/glib/meson.build
+++ b/docs/reference/glib/meson.build
@@ -140,7 +140,6 @@
                ],
                fixxref_args: [
                  '--html-dir=' + docpath,
-                 # TODO: gobject/gio extra dirs
                ],
                install: true)
 endif
diff --git a/docs/reference/gobject/glib-mkenums.xml b/docs/reference/gobject/glib-mkenums.xml
index a59cacf..2573bb3 100644
--- a/docs/reference/gobject/glib-mkenums.xml
+++ b/docs/reference/gobject/glib-mkenums.xml
@@ -253,7 +253,7 @@
 <varlistentry>
 <term><option>--eprod</option> <replaceable>TEXT</replaceable></term>
 <listitem><para>
-Emits <replaceable>TEXT</replaceable> everytime an enum is encountered
+Emits <replaceable>TEXT</replaceable> every time an enum is encountered
 in the input files.
 </para></listitem>
 </varlistentry>
@@ -333,7 +333,7 @@
 <varlistentry>
 <term><option>--identifier-prefix</option> <replaceable>PREFIX</replaceable></term>
 <listitem><para>
-Indicates what portion of the enum name should be intepreted as the
+Indicates what portion of the enum name should be interpreted as the
 prefix (eg, the "<literal>Gtk</literal>" in
 "<literal>GtkDirectionType</literal>"). Normally this will be figured
 out automatically, but you may need to override the default if your
diff --git a/docs/reference/gobject/meson.build b/docs/reference/gobject/meson.build
index ac2da57..014fe54 100644
--- a/docs/reference/gobject/meson.build
+++ b/docs/reference/gobject/meson.build
@@ -103,7 +103,7 @@
     ],
     fixxref_args: [
       '--html-dir=' + docpath,
-      '--extra-dir=' + join_paths(meson.current_build_dir(), '../glib/html'),
+      '--extra-dir=' + join_paths('gobject', '..', 'glib', 'html'),
     ],
     install: true
   )
diff --git a/gio-2.0.pc.in b/gio-2.0.pc.in
index afd3a40..d67e808 100644
--- a/gio-2.0.pc.in
+++ b/gio-2.0.pc.in
@@ -15,7 +15,7 @@
 Description: glib I/O library
 Version: @VERSION@
 Requires: glib-2.0 gobject-2.0
-Requires.private: gmodule-no-export-2.0
+Requires.private: gmodule-no-export-2.0 @LIBMOUNT_REQUIRES@
 Libs: -L${libdir} -lgio-2.0
-Libs.private: @ZLIB_LIBS@ @NETWORK_LIBS@ @SELINUX_LIBS@ @COCOA_LIBS@ @CARBON_LIBS@ @LIBMOUNT_LIBS@
+Libs.private: @ZLIB_LIBS@ @NETWORK_LIBS@ @SELINUX_LIBS@ @COCOA_LIBS@ @CARBON_LIBS@
 Cflags:
diff --git a/gio/Makefile.am b/gio/Makefile.am
index e14cad2..fc0b918 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -990,6 +990,7 @@
 	$(NULL)
 gio_LDADD = libgio-2.0.la 				\
 	$(top_builddir)/gobject/libgobject-2.0.la	\
+	$(top_builddir)/gmodule/libgmodule-2.0.la	\
 	$(top_builddir)/glib/libglib-2.0.la		\
 	$(NULL)
 
diff --git a/gio/completion/gio b/gio/completion/gio
old mode 100755
new mode 100644
diff --git a/gio/gdbus-2.0/codegen/codegen_main.py b/gio/gdbus-2.0/codegen/codegen_main.py
old mode 100755
new mode 100644
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 543ca0b..7270365 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -3088,7 +3088,7 @@
  * dispatched anywhere else - not even the standard dispatch machinery
  * (that API such as g_dbus_connection_signal_subscribe() and
  * g_dbus_connection_send_message_with_reply() relies on) will see the
- * message. Similary, if a filter consumes an outgoing message, the
+ * message. Similarly, if a filter consumes an outgoing message, the
  * message will not be sent to the other peer.
  *
  * If @user_data_free_func is non-%NULL, it will be called (in the
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
index d0ffbac..c55a0ab 100644
--- a/gio/gdesktopappinfo.c
+++ b/gio/gdesktopappinfo.c
@@ -4535,6 +4535,33 @@
 }
 
 /**
+ * g_desktop_app_info_get_string_list:
+ * @info: a #GDesktopAppInfo
+ * @key: the key to look up
+ * @length: (out) (optional): return location for the number of returned strings, or %NULL
+ *
+ * Looks up a string list value in the keyfile backing @info.
+ *
+ * The @key is looked up in the "Desktop Entry" group.
+ *
+ * Returns: (array zero-terminated=1 length=length) (element-type utf8) (transfer full):
+ *  a %NULL-terminated string array or %NULL if the specified
+ *  key cannot be found. The array should be freed with g_strfreev().
+ *
+ * Since: 2.59.0
+ */
+gchar **
+g_desktop_app_info_get_string_list (GDesktopAppInfo *info,
+                                    const char      *key,
+                                    gsize           *length)
+{
+  g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
+
+  return g_key_file_get_string_list (info->keyfile,
+                                     G_KEY_FILE_DESKTOP_GROUP, key, length, NULL);
+}
+
+/**
  * g_desktop_app_info_has_key:
  * @info: a #GDesktopAppInfo
  * @key: the key to look up
diff --git a/gio/gdesktopappinfo.h b/gio/gdesktopappinfo.h
index 86a3caa..1254038 100644
--- a/gio/gdesktopappinfo.h
+++ b/gio/gdesktopappinfo.h
@@ -89,6 +89,11 @@
 gboolean         g_desktop_app_info_get_boolean       (GDesktopAppInfo *info,
                                                        const char      *key);
 
+GLIB_AVAILABLE_IN_2_60
+gchar **         g_desktop_app_info_get_string_list (GDesktopAppInfo *info,
+                                                     const char      *key,
+                                                     gsize           *length);
+
 GLIB_AVAILABLE_IN_2_38
 const gchar * const *   g_desktop_app_info_list_actions                 (GDesktopAppInfo   *info);
 
diff --git a/gio/gio-tool.c b/gio/gio-tool.c
index 1820001..847a218 100644
--- a/gio/gio-tool.c
+++ b/gio/gio-tool.c
@@ -115,7 +115,7 @@
     case G_FILE_ATTRIBUTE_TYPE_OBJECT:
       return "object";
     default:
-      return "uknown type";
+      return "unknown type";
     }
 }
 
diff --git a/gio/gtask.c b/gio/gtask.c
index 4087543..a40bc01 100644
--- a/gio/gtask.c
+++ b/gio/gtask.c
@@ -551,20 +551,26 @@
   gint64 creation_time;
   gint priority;
   GCancellable *cancellable;
-  gboolean check_cancellable;
 
   GAsyncReadyCallback callback;
   gpointer callback_data;
-  gboolean completed;
 
   GTaskThreadFunc task_func;
   GMutex lock;
   GCond cond;
-  gboolean return_on_cancel;
+
+  /* This can’t be in the bit field because we access it from TRACE(). */
   gboolean thread_cancelled;
-  gboolean synchronous;
-  gboolean thread_complete;
-  gboolean blocking_other_task;
+
+  gboolean check_cancellable : 1;
+  gboolean completed : 1;
+  gboolean return_on_cancel : 1;
+  gboolean synchronous : 1;
+  gboolean thread_complete : 1;
+  gboolean blocking_other_task : 1;
+  gboolean had_error : 1;
+  gboolean result_set : 1;
+  gboolean ever_returned : 1;
 
   GError *error;
   union {
@@ -573,8 +579,6 @@
     gboolean boolean;
   } result;
   GDestroyNotify result_destroy;
-  gboolean had_error;
-  gboolean result_set;
 };
 
 #define G_TASK_IS_THREADED(task) ((task)->task_func != NULL)
@@ -1176,6 +1180,9 @@
 {
   GSource *source;
 
+  if (type != G_TASK_RETURN_FROM_THREAD)
+    task->ever_returned = TRUE;
+
   if (type == G_TASK_RETURN_SUCCESS)
     task->result_set = TRUE;
 
@@ -1596,7 +1603,7 @@
                        GDestroyNotify  result_destroy)
 {
   g_return_if_fail (G_IS_TASK (task));
-  g_return_if_fail (task->result_set == FALSE);
+  g_return_if_fail (!task->ever_returned);
 
   task->result.pointer = result;
   task->result_destroy = result_destroy;
@@ -1631,7 +1638,7 @@
   if (g_task_propagate_error (task, error))
     return NULL;
 
-  g_return_val_if_fail (task->result_set == TRUE, NULL);
+  g_return_val_if_fail (task->result_set, NULL);
 
   task->result_destroy = NULL;
   task->result_set = FALSE;
@@ -1654,7 +1661,7 @@
                    gssize  result)
 {
   g_return_if_fail (G_IS_TASK (task));
-  g_return_if_fail (task->result_set == FALSE);
+  g_return_if_fail (!task->ever_returned);
 
   task->result.size = result;
 
@@ -1687,7 +1694,7 @@
   if (g_task_propagate_error (task, error))
     return -1;
 
-  g_return_val_if_fail (task->result_set == TRUE, -1);
+  g_return_val_if_fail (task->result_set, -1);
 
   task->result_set = FALSE;
   return task->result.size;
@@ -1709,7 +1716,7 @@
                        gboolean  result)
 {
   g_return_if_fail (G_IS_TASK (task));
-  g_return_if_fail (task->result_set == FALSE);
+  g_return_if_fail (!task->ever_returned);
 
   task->result.boolean = result;
 
@@ -1742,7 +1749,7 @@
   if (g_task_propagate_error (task, error))
     return FALSE;
 
-  g_return_val_if_fail (task->result_set == TRUE, FALSE);
+  g_return_val_if_fail (task->result_set, FALSE);
 
   task->result_set = FALSE;
   return task->result.boolean;
@@ -1772,7 +1779,7 @@
                      GError *error)
 {
   g_return_if_fail (G_IS_TASK (task));
-  g_return_if_fail (task->result_set == FALSE);
+  g_return_if_fail (!task->ever_returned);
   g_return_if_fail (error != NULL);
 
   task->error = error;
@@ -1833,7 +1840,7 @@
   GError *error = NULL;
 
   g_return_val_if_fail (G_IS_TASK (task), FALSE);
-  g_return_val_if_fail (task->result_set == FALSE, FALSE);
+  g_return_val_if_fail (!task->ever_returned, FALSE);
 
   if (g_cancellable_set_error_if_cancelled (task->cancellable, &error))
     {
diff --git a/gio/gunionvolumemonitor.c b/gio/gunionvolumemonitor.c
index 8e58d18..eeb4c5f 100644
--- a/gio/gunionvolumemonitor.c
+++ b/gio/gunionvolumemonitor.c
@@ -621,7 +621,7 @@
  * also listen for the "removed" signal on the returned object
  * and give up its reference when handling that signal
  * 
- * Similary, if implementing g_volume_monitor_adopt_orphan_mount(),
+ * Similarly, if implementing g_volume_monitor_adopt_orphan_mount(),
  * the implementor must take a reference to @mount and return it in
  * its g_volume_get_mount() implemented. Also, the implementor must
  * listen for the "unmounted" signal on @mount and give up its
diff --git a/gio/meson.build b/gio/meson.build
index 04581b1..7f2c08e 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -351,6 +351,12 @@
 
 platform_deps = []
 internal_deps = []
+# TODO: internal_objects is a workaround for
+# <https://github.com/mesonbuild/meson/issues/3934> and
+# <https://github.com/mesonbuild/meson/issues/3937>. When we can depend
+# on a meson version where those are fixed, revert the commit that
+# introduced this workaround.
+internal_objects = []
 appinfo_sources = []
 contenttype_sources = []
 portal_sources = []
@@ -424,6 +430,7 @@
 
   subdir('xdgmime')
   internal_deps += [xdgmime_lib]
+  internal_objects += [xdgmime_lib.extract_all_objects()]
 
   install_headers(gio_unix_include_headers, subdir : 'gio-unix-2.0/gio')
 
@@ -749,17 +756,20 @@
 if glib_conf.has('HAVE_SYS_INOTIFY_H') and have_func_inotify_init1
   subdir('inotify')
   internal_deps += [ inotify_lib ]
+  internal_objects += [inotify_lib.extract_all_objects()]
 endif
 
 # kevent
 if have_func_kqueue and have_func_kevent
   subdir('kqueue')
   internal_deps += [ kqueue_lib ]
+  internal_objects += [kqueue_lib.extract_all_objects()]
 endif
 
 if host_system == 'windows'
   subdir('win32')
   internal_deps += [ giowin32_lib ]
+  internal_objects += [giowin32_lib.extract_all_objects()]
 endif
 
 if have_bash
@@ -784,11 +794,11 @@
 libgio = library('gio-2.0',
   gioenumtypes_h, gioenumtypes_c, gnetworking_h, gio_sources,
   gio_dtrace_hdr, gio_dtrace_obj,
+  objects : internal_objects,
   version : library_version,
   soversion : soversion,
   install : true,
   include_directories : [configinc, gioinc],
-  link_with : internal_deps,
   #  '$(gio_win32_res_ldflag)',
   dependencies : [libz_dep, libdl_dep, libmount_dep, libglib_dep,
                   libgobject_dep, libgmodule_dep, selinux_dep, xattr_dep,
diff --git a/gio/tests/appinfo.c b/gio/tests/appinfo.c
index a52bc70..e75c4ab 100644
--- a/gio/tests/appinfo.c
+++ b/gio/tests/appinfo.c
@@ -512,6 +512,8 @@
   GKeyFile *kf;
   GError *error = NULL;
   const gchar *categories;
+  gchar **categories_list;
+  gsize categories_count;
   gchar **keywords;
   const gchar *file;
   const gchar *name;
@@ -522,7 +524,7 @@
   g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, &error);
   g_assert_no_error (error);
   info = g_desktop_app_info_new_from_keyfile (kf);
-  g_key_file_free (kf);
+  g_key_file_unref (kf);
   g_assert (info != NULL);
 
   g_object_get (info, "filename", &file, NULL);
@@ -532,6 +534,11 @@
   g_assert (file == NULL);
   categories = g_desktop_app_info_get_categories (info);
   g_assert_cmpstr (categories, ==, "GNOME;GTK;");
+  categories_list = g_desktop_app_info_get_string_list (info, "Categories", &categories_count);
+  g_assert_cmpint (categories_count, ==, 2);
+  g_assert_cmpint (g_strv_length (categories_list), ==, 2);
+  g_assert_cmpstr (categories_list[0], ==, "GNOME");
+  g_assert_cmpstr (categories_list[1], ==, "GTK");
   keywords = (gchar **)g_desktop_app_info_get_keywords (info);
   g_assert_cmpint (g_strv_length (keywords), ==, 2);
   g_assert_cmpstr (keywords[0], ==, "keyword1");
@@ -540,6 +547,7 @@
   g_assert_cmpstr (name, ==, "generic-appinfo-test");
   g_assert (!g_desktop_app_info_get_nodisplay (info));
 
+  g_strfreev (categories_list);
   g_object_unref (info);
 }
 
diff --git a/gio/tests/desktop-files/usr/applications/org.gnome.clocks.desktop b/gio/tests/desktop-files/usr/applications/org.gnome.clocks.desktop
old mode 100755
new mode 100644
diff --git a/gio/tests/gdbus-object-manager-example/meson.build b/gio/tests/gdbus-object-manager-example/meson.build
index 90dcc1f..6b27576 100644
--- a/gio/tests/gdbus-object-manager-example/meson.build
+++ b/gio/tests/gdbus-object-manager-example/meson.build
@@ -14,11 +14,12 @@
              '--generate-docbook', 'gdbus-example-objectmanager-generated',
              '@INPUT@'])
 
-libgdbus_example_objectmanager = library('libgdbus-example-objectmanager',
+libgdbus_example_objectmanager = library('gdbus-example-objectmanager',
     gdbus_example_objectmanager_generated,
     c_args : gio_c_args,
     dependencies : [libglib_dep, libgmodule_dep, libgobject_dep, libgio_dep],
-    install : false)
+    install : installed_tests_enabled,
+    install_dir : installed_tests_execdir)
 
 libgdbus_example_objectmanager_dep = declare_dependency(sources : gdbus_example_objectmanager_generated,
   link_with : libgdbus_example_objectmanager)
diff --git a/gio/tests/meson.build b/gio/tests/meson.build
index e16f534..334fd00 100644
--- a/gio/tests/meson.build
+++ b/gio/tests/meson.build
@@ -19,9 +19,7 @@
   command: [gengiotypefuncs_prog, '@OUTPUT@', '@INPUT@'])
 
 #  Test programs buildable on all platforms
-# FIXME: We are using list of dictionnaries until we can depend on Meson 0.48.0
-# that supports '+=' operator on dictionnaries.
-gio_tests = [{
+gio_tests = {
   'appmonitor' : {},
   'async-close-output-stream' : {},
   'async-splice-output-stream' : {},
@@ -73,14 +71,13 @@
   'tls-certificate' : {'extra_sources' : ['gtesttlsbackend.c']},
   'tls-interaction' : {'extra_sources' : ['gtesttlsbackend.c']},
   'tls-database' : {'extra_sources' : ['gtesttlsbackend.c']},
-}]
+}
 
-# FIXME: We are using list of dictionnaries until we can depend on Meson 0.48.0
-# that supports '+=' operator on dictionnaries.
-test_extra_programs = [{
+test_extra_programs = {
   'gdbus-connection-flush-helper' : {},
   'gdbus-testserver' : {},
-}]
+  'gsubprocess-testprog' : {},
+}
 
 test_env = environment()
 test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
@@ -113,17 +110,17 @@
 if dbus1_dep.found()
   glib_conf.set('HAVE_DBUS1', 1)
 
-  gio_tests += [{
+  gio_tests += {
     'gdbus-serialization' : {
       'extra_sources' : ['gdbus-tests.c'],
       'dependencies' : [dbus1_dep],
     }
-  }]
+  }
 endif
 
 #  Test programs buildable on UNIX only
 if host_machine.system() != 'windows'
-  gio_tests += [{
+  gio_tests += {
     'file' : {},
     'gdbus-peer' : {'dependencies' : [libgdbus_example_objectmanager_dep]},
     'gdbus-peer-object-manager' : {},
@@ -136,34 +133,36 @@
     'g-file-info-filesystem-readonly' : {},
     'gschema-compile' : {'install' : false},
     'trash' : {},
-  }]
+  }
 
   # Uninstalled because of the check-for-executable logic in DesktopAppInfo
   # unable to find the installed executable
   if not glib_have_cocoa
-    gio_tests += [{
+    gio_tests += {
       'appinfo' : {
         'install' : false,
+        'is_parallel' : false,
       },
       'desktop-app-info' : {
         'install' : false,
+        'is_parallel' : false,
       },
-    }]
+    }
   endif
 
-  test_extra_programs += [{
+  test_extra_programs += {
     'basic-application' : {},
     'dbus-launch' : {},
     'appinfo-test' : {},
-  }]
+  }
 
   if not glib_have_cocoa
-    test_extra_programs += [{
+    test_extra_programs += {
       'apps' : {},
-    }]
-    gio_tests += [{
+    }
+    gio_tests += {
       'mimeapps' : {},
-    }]
+    }
   endif
 
   #  Test programs that need to bring up a session bus (requires dbus-daemon)
@@ -194,7 +193,7 @@
 
     extra_sources = ['gdbus-sessionbus.c', 'gdbus-tests.c']
 
-    gio_tests += [{
+    gio_tests += {
       'actions' : {
         'extra_sources' : extra_sources,
         'suite' : ['slow'],
@@ -226,7 +225,10 @@
         'extra_sources' : extra_sources,
         'suite' : ['slow'],
       },
-      'gmenumodel' : {'extra_sources' : extra_sources},
+      'gmenumodel' : {
+        'extra_sources' : extra_sources,
+        'suite' : ['slow'],
+      },
       'gnotification' : {
         'extra_sources' : [extra_sources, 'gnotification-server.c'],
       },
@@ -238,61 +240,62 @@
       },
       'gapplication' : {'extra_sources' : extra_sources},
       'gdbus-unix-addresses' : {},
-    }]
+    }
 
     if not glib_have_cocoa
-      gio_tests += [{
+      gio_tests += {
         'dbus-appinfo' : {
           'extra_sources' : extra_sources,
         },
-      }]
+      }
     endif
   endif # have_dbus_daemon
 
   # This test is currently unreliable
   executable('gdbus-overflow', 'gdbus-overflow.c',
-      install : false,
       c_args : test_c_args,
-      dependencies : common_gio_tests_deps)
+      dependencies : common_gio_tests_deps,
+      install_dir : installed_tests_execdir,
+      install : installed_tests_enabled)
 
-  gio_tests += [{
+  gio_tests += {
     'gdbus-connection-flush' : {
       'extra_sources' : ['test-io-stream.c', 'test-pipe-unix.c'],
     },
     'gdbus-non-socket' : {
       'extra_sources' : ['gdbus-tests.c', 'test-io-stream.c', 'test-pipe-unix.c'],
     },
-  }]
+  }
 
   # Generate test.mo from de.po using msgfmt
   msgfmt = find_program('msgfmt', required : false)
   if msgfmt.found()
     subdir('de/LC_MESSAGES')
-    gio_tests += [{
+    gio_tests += {
       'gsettings' : {
         'extra_sources' : [test_mo],
         'c_args' : ['-DSRCDIR="@0@"'.format(meson.current_source_dir()),
                     '-DTEST_LOCALE_PATH="@0@"'.format(test_mo_dir)],
         'install' : false,
       },
-    }]
+    }
   endif
 endif # unix
 
 #  Test programs buildable on Windows only
 if host_machine.system() == 'windows'
-  gio_tests += [{'win32-streams' : {}}]
+  gio_tests += {'win32-streams' : {}}
 endif
 
 if cc.get_id() != 'msvc'
-  gio_tests += [{
+  gio_tests += {
     'autoptr-gio' : {
       'source' : 'autoptr.c',
     },
-  }]
+  }
 endif
 
-test_extra_programs += [{
+test_extra_programs += {
   'gio-du' : {'install' : false},
   'echo-server' : {'install' : false},
   'filter-cat' : {'install' : false},
@@ -315,7 +318,6 @@
   'gdbus-example-subtree' : {'install' : false},
   'gdbus-example-watch-name' : {'install' : false},
   'gdbus-example-watch-proxy' : {'install' : false},
-  'gsubprocess-testprog' : {'install' : false},
   'httpd' : {'install' : false},
   'proxy' : {'install' : false},
   'resolver' : {'install' : false},
@@ -325,10 +327,10 @@
     'extra_sources' : ['gtlsconsoleinteraction.c'],
     'install' : false,
   },
-}]
+}
 
 if cc.get_id() != 'msvc'
-  test_extra_programs += [{
+  test_extra_programs += {
     # These three are manual-run tests because they need a session bus but don't bring one up themselves
     # FIXME: these build but don't seem to work!
     'gdbus-example-objectmanager-client' : {
@@ -343,15 +345,34 @@
       'dependencies' : [libgdbus_example_objectmanager_dep],
       'install' : false,
     },
-  }]
+  }
 endif
 
 if host_machine.system() != 'windows'
-  test_extra_programs += [{
+  test_extra_programs += {
     'gdbus-example-unix-fd-client' : {
       'install' : false,
     },
-  }]
+  }
+endif
+
+if installed_tests_enabled
+  install_data(
+    'contexts.c',
+    'g-icon.c',
+    'appinfo-test-actions.desktop',
+    'appinfo-test-gnome.desktop',
+    'appinfo-test-notgnome.desktop',
+    'appinfo-test.desktop',
+    'appinfo-test2.desktop',
+    'file.c',
+    'org.gtk.test.dbusappinfo.desktop',
+    install_dir : installed_tests_execdir,
+  )
+  install_subdir('x-content', install_dir : installed_tests_execdir)
+  install_subdir('desktop-files', install_dir : installed_tests_execdir)
+  install_subdir('thumbnails', install_dir : installed_tests_execdir)
+  install_subdir('cert-tests', install_dir : installed_tests_execdir)
 endif
 
 if not meson.is_cross_build() or meson.has_exe_wrapper()
@@ -366,9 +387,12 @@
                '--c-name', '_g_plugin',
                '@INPUT@'])
 
-  shared_module ('resourceplugin', 'resourceplugin.c', plugin_resources_c,
+  shared_module('resourceplugin', 'resourceplugin.c', plugin_resources_c,
     link_args : export_dynamic_ldflags,
-    dependencies : common_gio_tests_deps)
+    dependencies : common_gio_tests_deps,
+    install_dir : installed_tests_execdir,
+    install : installed_tests_enabled
+  )
 
   test_gresource = custom_target('test.gresource',
     input : 'test.gresource.xml',
@@ -377,7 +401,9 @@
                '--target=@OUTPUT@',
                '--sourcedir=' + meson.current_source_dir(),
                '--sourcedir=' + meson.current_build_dir(),
-               '@INPUT@'])
+               '@INPUT@'],
+    install_dir : installed_tests_execdir,
+    install : installed_tests_enabled)
 
   test_resources2_c = custom_target('test_resources2.c',
     input : 'test3.gresource.xml',
@@ -417,57 +443,59 @@
     copy : true,
     install : false)
 
-  gio_tests += [{
+  gio_tests += {
     'resources' : {
       'extra_sources' : [test_gresource, test_resources_c, test_resources2_c,
                          test_resources2_h],
     },
-  }]
+  }
 endif
 
-foreach test_dict : gio_tests
-  foreach test_name, extra_args : test_dict
-    source = extra_args.get('source', test_name + '.c')
-    extra_sources = extra_args.get('extra_sources', [])
-    install = installed_tests_enabled and extra_args.get('install', true)
+foreach test_name, extra_args : gio_tests
+  source = extra_args.get('source', test_name + '.c')
+  extra_sources = extra_args.get('extra_sources', [])
+  install = installed_tests_enabled and extra_args.get('install', true)
 
-    if install
-      test_conf = configuration_data()
-      test_conf.set('installed_tests_dir', installed_tests_execdir)
-      test_conf.set('program', test_name)
-      configure_file(
-        input: installed_tests_template,
-        output: test_name + '.test',
-        install_dir: installed_tests_metadir,
-        configuration: test_conf
-      )
-    endif
-
-    exe = executable(test_name, [source, extra_sources],
-      c_args : test_c_args + extra_args.get('c_args', []),
-      dependencies : common_gio_tests_deps + extra_args.get('dependencies', []),
-      install_dir: installed_tests_execdir,
-      install: install,
+  if install
+    test_conf = configuration_data()
+    test_conf.set('installed_tests_dir', installed_tests_execdir)
+    test_conf.set('program', test_name)
+    configure_file(
+      input: installed_tests_template_tap,
+      output: test_name + '.test',
+      install_dir: installed_tests_metadir,
+      configuration: test_conf
     )
+  endif
 
-    suite = ['gio'] + extra_args.get('suite', [])
-    timeout = suite.contains('slow') ? 120 : 30
-    test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
-  endforeach
+  exe = executable(test_name, [source, extra_sources],
+    c_args : test_c_args + extra_args.get('c_args', []),
+    dependencies : common_gio_tests_deps + extra_args.get('dependencies', []),
+    install_dir: installed_tests_execdir,
+    install: install,
+  )
+
+  suite = ['gio'] + extra_args.get('suite', [])
+  timeout = suite.contains('slow') ? 120 : 30
+  test(test_name, exe,
+    env : test_env,
+    timeout : timeout,
+    suite : suite,
+    args : ['--tap'],
+    is_parallel : extra_args.get('is_parallel', true),
+  )
 endforeach
 
-foreach program_dict : test_extra_programs
-  foreach program_name, extra_args : program_dict
-    source = extra_args.get('source', program_name + '.c')
-    extra_sources = extra_args.get('extra_sources', [])
-    install = installed_tests_enabled and extra_args.get('install', true)
-    executable(program_name, [source, extra_sources],
-        c_args : test_c_args,
-        dependencies : common_gio_tests_deps + extra_args.get('dependencies', []),
-        install_dir : installed_tests_execdir,
-        install : install,
-    )
-  endforeach
+foreach program_name, extra_args : test_extra_programs
+  source = extra_args.get('source', program_name + '.c')
+  extra_sources = extra_args.get('extra_sources', [])
+  install = installed_tests_enabled and extra_args.get('install', true)
+  executable(program_name, [source, extra_sources],
+      c_args : test_c_args,
+      dependencies : common_gio_tests_deps + extra_args.get('dependencies', []),
+      install_dir : installed_tests_execdir,
+      install : install,
+  )
 endforeach
 
 # FIXME: subdir('services')
diff --git a/gio/tests/modules/meson.build b/gio/tests/modules/meson.build
index 5ae131a..fa981c4 100644
--- a/gio/tests/modules/meson.build
+++ b/gio/tests/modules/meson.build
@@ -1,11 +1,13 @@
-libtestmodulea = library('testmodulea', 'test-module-a.c',
-  install : false,
+libtestmodulea = shared_module('testmodulea', 'test-module-a.c',
   dependencies : [libglib_dep, libgobject_dep, libgmodule_dep, libgio_dep],
-  c_args : [ ]
+  c_args : [ ],
+  install : installed_tests_enabled,
+  install_dir : join_paths(installed_tests_execdir, 'modules'),
 )
 
-libtestmoduleb = library('testmoduleb', 'test-module-b.c',
-  install : false,
+libtestmoduleb = shared_module('testmoduleb', 'test-module-b.c',
   dependencies : [libglib_dep, libgobject_dep, libgmodule_dep, libgio_dep],
-  c_args : [ ]
+  c_args : [ ],
+  install : installed_tests_enabled,
+  install_dir : join_paths(installed_tests_execdir, 'modules'),
 )
diff --git a/gio/tests/task.c b/gio/tests/task.c
index 934262e..7f69830 100644
--- a/gio/tests/task.c
+++ b/gio/tests/task.c
@@ -1689,7 +1689,6 @@
 
   g_task_set_task_data (task, (gpointer)&state, NULL);
   g_task_run_in_thread (task, return_on_cancel_atomic_thread);
-  g_object_unref (task);
 
   g_assert_cmpint (state, ==, 0);
 
@@ -1724,6 +1723,7 @@
   g_object_unref (cancellable);
   g_mutex_unlock (&roca_mutex_1);
   g_mutex_unlock (&roca_mutex_2);
+  g_object_unref (task);
 }
 
 /* test_return_pointer: memory management of pointer returns */
@@ -1987,6 +1987,136 @@
   g_assert (simple == NULL);
 }
 
+/* Various helper functions for the return tests below. */
+static void
+task_complete_cb (GObject *source,
+                  GAsyncResult *result,
+                  gpointer user_data)
+{
+  GTask *task = G_TASK (result);
+  guint *calls = user_data;
+
+  g_assert_cmpint (++*calls, <=, 1);
+
+  /* Propagate the result, so it’s removed from the task’s internal state. */
+  g_task_propagate_boolean (task, NULL);
+}
+
+static void
+return_twice (GTask *task)
+{
+  gboolean error_first = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+  if (error_first)
+    {
+      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_UNKNOWN, "oh no");
+      g_task_return_boolean (task, TRUE);
+    }
+  else
+    {
+      g_task_return_boolean (task, TRUE);
+      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_UNKNOWN, "oh no");
+    }
+}
+
+static gboolean
+idle_cb (gpointer user_data)
+{
+  GTask *task = user_data;
+  return_twice (task);
+  g_object_unref (task);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+test_return_permutation (gboolean error_first,
+                         gboolean return_in_idle)
+{
+  guint calls = 0;
+  GTask *task = NULL;
+
+  g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1525");
+
+  task = g_task_new (NULL, NULL, task_complete_cb, &calls);
+  g_task_set_task_data (task, GUINT_TO_POINTER (error_first), NULL);
+
+  if (return_in_idle)
+    g_idle_add (idle_cb, g_object_ref (task));
+  else
+    return_twice (task);
+
+  while (calls == 0)
+    g_main_context_iteration (NULL, TRUE);
+
+  g_assert_cmpint (calls, ==, 1);
+
+  g_object_unref (task);
+}
+
+/* Test that calling g_task_return_boolean() after g_task_return_error(), when
+ * returning in an idle callback, correctly results in a critical warning. */
+static void
+test_return_in_idle_error_first (void)
+{
+  if (g_test_subprocess ())
+    {
+      test_return_permutation (TRUE, TRUE);
+      return;
+    }
+
+  g_test_trap_subprocess (NULL, 0, 0);
+  g_test_trap_assert_failed ();
+  g_test_trap_assert_stderr ("*CRITICAL*assertion '!task->ever_returned' failed*");
+}
+
+/* Test that calling g_task_return_error() after g_task_return_boolean(), when
+ * returning in an idle callback, correctly results in a critical warning. */
+static void
+test_return_in_idle_value_first (void)
+{
+  if (g_test_subprocess ())
+    {
+      test_return_permutation (FALSE, TRUE);
+      return;
+    }
+
+  g_test_trap_subprocess (NULL, 0, 0);
+  g_test_trap_assert_failed ();
+  g_test_trap_assert_stderr ("*CRITICAL*assertion '!task->ever_returned' failed*");
+}
+
+/* Test that calling g_task_return_boolean() after g_task_return_error(), when
+ * returning synchronously, correctly results in a critical warning. */
+static void
+test_return_error_first (void)
+{
+  if (g_test_subprocess ())
+    {
+      test_return_permutation (TRUE, FALSE);
+      return;
+    }
+
+  g_test_trap_subprocess (NULL, 0, 0);
+  g_test_trap_assert_failed ();
+  g_test_trap_assert_stderr ("*CRITICAL*assertion '!task->ever_returned' failed*");
+}
+
+/* Test that calling g_task_return_error() after g_task_return_boolean(), when
+ * returning synchronously, correctly results in a critical warning. */
+static void
+test_return_value_first (void)
+{
+  if (g_test_subprocess ())
+    {
+      test_return_permutation (FALSE, FALSE);
+      return;
+    }
+
+  g_test_trap_subprocess (NULL, 0, 0);
+  g_test_trap_assert_failed ();
+  g_test_trap_assert_stderr ("*CRITICAL*assertion '!task->ever_returned' failed*");
+}
 
 int
 main (int argc, char **argv)
@@ -1994,6 +2124,7 @@
   int ret;
 
   g_test_init (&argc, &argv, NULL);
+  g_test_bug_base ("");
 
   loop = g_main_loop_new (NULL, FALSE);
   main_thread = g_thread_self ();
@@ -2021,6 +2152,10 @@
   g_test_add_func ("/gtask/return-pointer", test_return_pointer);
   g_test_add_func ("/gtask/object-keepalive", test_object_keepalive);
   g_test_add_func ("/gtask/legacy-error", test_legacy_error);
+  g_test_add_func ("/gtask/return/in-idle/error-first", test_return_in_idle_error_first);
+  g_test_add_func ("/gtask/return/in-idle/value-first", test_return_in_idle_value_first);
+  g_test_add_func ("/gtask/return/error-first", test_return_error_first);
+  g_test_add_func ("/gtask/return/value-first", test_return_value_first);
 
   ret = g_test_run();
 
diff --git a/glib.mk b/glib.mk
index 09aae0e..f9d09ff 100644
--- a/glib.mk
+++ b/glib.mk
@@ -3,7 +3,6 @@
 #GTESTER = gtester 			# for non-GLIB packages
 #GTESTER_REPORT = gtester-report        # for non-GLIB packages
 GTESTER = $(top_builddir)/glib/gtester			# for the GLIB package
-GTESTER_REPORT = $(top_builddir)/glib/gtester-report	# for the GLIB package
 NULL =
 
 # initialize variables for unconditional += appending
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 8f6536c..90d33d0 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -454,24 +454,17 @@
 gtester_SOURCES	 = gtester.c
 gtester_LDADD	 = libglib-2.0.la 
 
-auto_config_binscripts = gtester-report
-bin_SCRIPTS = ${auto_config_binscripts}
-EXTRA_DIST += ${auto_config_binscripts}
+bin_SCRIPTS = gtester-report
+EXTRA_DIST += gtester-report.in
 
-CONFIGVARS = \
-	"bindir"    	: "${bindir}",		\
-	"glib-version"  : "${GLIB_VERSION}"
-
-install-exec-hook:
-	for sf in ${auto_config_binscripts} ; do \
-	  mv -f "$(DESTDIR)$(bindir)/$$sf" "$(DESTDIR)$(bindir)/$$sf".tmp \
-	  && sed < "$(DESTDIR)$(bindir)/$$sf".tmp > "$(DESTDIR)$(bindir)/$$sf" \
-	    -e '1,24s|^ *#@PKGINSTALL_CONFIGVARS_IN24LINES@|  ${CONFIGVARS}|' \
-	    -e '1,1s|#! /usr/bin/env python.*|#!${PYTHON}|' \
+gtester-report: gtester-report.in Makefile
+	$(AM_V_GEN) set -e ; \
+	sed < $< > $@.tmp \
+	    -e '1,1s|#!.*|#!${PYTHON}|' \
+	    -e 's|[@]GLIB_VERSION[@]|${GLIB_VERSION}|' \
 	  || exit $$? ; \
-	  chmod a+x $(DESTDIR)$(bindir)/$$sf ; \
-	  rm -f "$(DESTDIR)$(bindir)/$$sf".tmp ; \
-	done
+	  chmod +x $@.tmp ; \
+	  mv $@.tmp $@
 
 endif
 
diff --git a/glib/garray.c b/glib/garray.c
index a6cbd57..3ef15cd 100644
--- a/glib/garray.c
+++ b/glib/garray.c
@@ -318,15 +318,15 @@
  * @free_segment: if %TRUE the actual element data is freed as well
  *
  * Frees the memory allocated for the #GArray. If @free_segment is
- * %TRUE it frees the memory block holding the elements as well and
- * also each element if @array has a @element_free_func set. Pass
+ * %TRUE it frees the memory block holding the elements as well. Pass
  * %FALSE if you want to free the #GArray wrapper but preserve the
- * underlying array for use elsewhere. If the reference count of @array
- * is greater than one, the #GArray wrapper is preserved but the size
- * of @array will be set to zero.
+ * underlying array for use elsewhere. If the reference count of
+ * @array is greater than one, the #GArray wrapper is preserved but
+ * the size of  @array will be set to zero.
  *
- * If array elements contain dynamically-allocated memory, they should
- * be freed separately.
+ * If array contents point to dynamically-allocated memory, they should
+ * be freed separately if @free_seg is %TRUE and no @clear_func
+ * function has been set for @array.
  *
  * This function is not thread-safe. If using a #GArray from multiple
  * threads, use only the atomic g_array_ref() and g_array_unref()
diff --git a/glib/gbookmarkfile.c b/glib/gbookmarkfile.c
index 31706ba..ffd7ea0 100644
--- a/glib/gbookmarkfile.c
+++ b/glib/gbookmarkfile.c
@@ -3360,8 +3360,8 @@
  * @stamp: (out) (optional): return location for the last registration time, or %NULL
  * @error: return location for a #GError, or %NULL
  *
- * Gets the registration informations of @app_name for the bookmark for
- * @uri.  See g_bookmark_file_set_app_info() for more informations about
+ * Gets the registration information of @app_name for the bookmark for
+ * @uri.  See g_bookmark_file_set_app_info() for more information about
  * the returned data.
  *
  * The string returned in @app_exec must be freed.
diff --git a/glib/gkeyfile.c b/glib/gkeyfile.c
index ae3bbbc..4e9b535 100644
--- a/glib/gkeyfile.c
+++ b/glib/gkeyfile.c
@@ -1191,7 +1191,11 @@
   g_return_if_fail (key_file != NULL);
 
   g_key_file_clear (key_file);
-  g_key_file_unref (key_file);
+
+  if (g_atomic_int_dec_and_test (&key_file->ref_count))
+    g_slice_free (GKeyFile, key_file);
+  else
+    g_key_file_init (key_file);
 }
 
 /**
diff --git a/glib/gspawn.c b/glib/gspawn.c
index 5d0c29d..23ade06 100644
--- a/glib/gspawn.c
+++ b/glib/gspawn.c
@@ -30,7 +30,10 @@
 #include <string.h>
 #include <stdlib.h>   /* for fdwalk */
 #include <dirent.h>
+
+#ifdef HAVE_SPAWN_H
 #include <spawn.h>
+#endif /* HAVE_SPAWN_H */
 
 #ifdef HAVE_CRT_EXTERNS_H
 #include <crt_externs.h> /* for _NSGetEnviron */
diff --git a/glib/gstdio.c b/glib/gstdio.c
index 1a56b19..4243fd5 100644
--- a/glib/gstdio.c
+++ b/glib/gstdio.c
@@ -36,6 +36,7 @@
 #include <direct.h>
 #include <io.h>
 #include <sys/utime.h>
+#include <stdlib.h> /* for MB_CUR_MAX */
 #else
 #include <utime.h>
 #include <errno.h>
@@ -123,6 +124,140 @@
 
 #include "gstdio-private.c"
 
+/* From
+ * https://support.microsoft.com/en-ca/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime
+ * FT = UT * 10000000 + 116444736000000000.
+ * Therefore:
+ * UT = (FT - 116444736000000000) / 10000000.
+ */
+static gint64
+_g_win32_filetime_to_unix_time (FILETIME *ft)
+{
+  gint64 result;
+  /* 1 unit of FILETIME is 100ns */
+  const gint64 hundreds_of_usec_per_sec = 10000000;
+  /* The difference between January 1, 1601 UTC (FILETIME epoch) and UNIX epoch
+   * in hundreds of nanoseconds.
+   */
+  const gint64 filetime_unix_epoch_offset = 116444736000000000;
+
+  result = ((gint64) ft->dwLowDateTime) | (((gint64) ft->dwHighDateTime) << 32);
+  return (result - filetime_unix_epoch_offset) / hundreds_of_usec_per_sec;
+}
+
+#  ifdef _MSC_VER
+#    ifndef S_IXUSR
+#      define _S_IRUSR _S_IREAD
+#      define _S_IWUSR _S_IWRITE
+#      define _S_IXUSR _S_IEXEC
+#      define S_IRUSR _S_IRUSR
+#      define S_IWUSR _S_IWUSR
+#      define S_IXUSR _S_IXUSR
+#      define S_IRGRP (S_IRUSR >> 3)
+#      define S_IWGRP (S_IWUSR >> 3)
+#      define S_IXGRP (S_IXUSR >> 3)
+#      define S_IROTH (S_IRGRP >> 3)
+#      define S_IWOTH (S_IWGRP >> 3)
+#      define S_IXOTH (S_IXGRP >> 3)
+#    endif
+#    ifndef S_ISDIR
+#      define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+#    endif
+#  endif
+
+static int
+_g_win32_fill_statbuf_from_handle_info (const wchar_t              *filename,
+                                        const wchar_t              *filename_target,
+                                        BY_HANDLE_FILE_INFORMATION *handle_info,
+                                        struct __stat64            *statbuf)
+{
+  wchar_t drive_letter_w = 0;
+  size_t drive_letter_size = MB_CUR_MAX;
+  char *drive_letter = _alloca (drive_letter_size);
+
+  /* If filename (target or link) is absolute,
+   * then use the drive letter from it as-is.
+   */
+  if (filename_target != NULL &&
+      filename_target[0] != L'\0' &&
+      filename_target[1] == L':')
+    drive_letter_w = filename_target[0];
+  else if (filename[0] != L'\0' &&
+           filename[1] == L':')
+    drive_letter_w = filename[0];
+
+  if (drive_letter_w > 0 &&
+      iswalpha (drive_letter_w) &&
+      iswascii (drive_letter_w) &&
+      wctomb (drive_letter, drive_letter_w) == 1)
+    statbuf->st_dev = toupper (drive_letter[0]) - 'A'; /* 0 means A: drive */
+  else
+    /* Otherwise use the PWD drive.
+     * Return value of 0 gives us 0 - 1 = -1,
+     * which is the "no idea" value for st_dev.
+     */
+    statbuf->st_dev = _getdrive () - 1;
+
+  statbuf->st_rdev = statbuf->st_dev;
+  /* Theoretically, it's possible to set it for ext-FS. No idea how.
+   * Meaningless for all filesystems that Windows normally uses.
+   */
+  statbuf->st_ino = 0;
+  statbuf->st_mode = 0;
+
+  if ((handle_info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
+    statbuf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+  else
+    statbuf->st_mode |= S_IFREG;
+  /* No idea what S_IFCHR means here. */
+  /* S_IFIFO is not even mentioned in MSDN */
+  /* S_IFBLK is also not mentioned */
+
+  /* The aim here is to reproduce MS stat() behaviour,
+   * even if it's braindead.
+   */
+  statbuf->st_mode |= S_IRUSR | S_IRGRP | S_IROTH;
+  if ((handle_info->dwFileAttributes & FILE_ATTRIBUTE_READONLY) != FILE_ATTRIBUTE_READONLY)
+    statbuf->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+
+  if (!S_ISDIR (statbuf->st_mode))
+    {
+      const wchar_t *name;
+      const wchar_t *dot = NULL;
+
+      if (filename_target != NULL)
+        name = filename_target;
+      else
+        name = filename;
+
+      do
+        {
+          wchar_t *last_dot = wcschr (name, L'.');
+          if (last_dot == NULL)
+            break;
+          dot = last_dot;
+          name = &last_dot[1];
+        }
+      while (TRUE);
+
+      if ((dot != NULL &&
+          (wcsicmp (dot, L".exe") == 0 ||
+           wcsicmp (dot, L".com") == 0 ||
+           wcsicmp (dot, L".bat") == 0 ||
+           wcsicmp (dot, L".cmd") == 0)))
+        statbuf->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+    }
+
+  statbuf->st_nlink = handle_info->nNumberOfLinks;
+  statbuf->st_uid = statbuf->st_gid = 0;
+  statbuf->st_size = (((guint64) handle_info->nFileSizeHigh) << 32) | handle_info->nFileSizeLow;
+  statbuf->st_ctime = _g_win32_filetime_to_unix_time (&handle_info->ftCreationTime);
+  statbuf->st_mtime = _g_win32_filetime_to_unix_time (&handle_info->ftLastWriteTime);
+  statbuf->st_atime = _g_win32_filetime_to_unix_time (&handle_info->ftLastAccessTime);
+
+  return 0;
+}
+
 static int
 _g_win32_stat_utf16_no_trailing_slashes (const gunichar2    *filename,
                                          int                 fd,
@@ -232,11 +367,9 @@
 
       if (is_symlink && !for_symlink)
         {
-          /* If filename is a symlink, _wstat64 obtains information about
-           * the symlink (except that st_size will be 0).
+          /* If filename is a symlink, but we need the target.
            * To get information about the target we need to resolve
-           * the symlink first. And we need _wstat64() to get st_dev,
-           * it's a bother to try finding it ourselves.
+           * the symlink first.
            */
           DWORD filename_target_len;
           DWORD new_len;
@@ -326,8 +459,18 @@
       return -1;
     }
 
+  /*
+   * We can't use _wstat64() here, because with UCRT it now gives
+   * information about the target, even if we want information about
+   * the link itself (unlike MSVCRT, which gave information about
+   * the link, and if we needed information about the target we were
+   * able to resolve it by ourselves prior to calling _wstat64()).
+   */
   if (fd < 0)
-    result = _wstat64 (filename_target != NULL ? filename_target : filename, &statbuf);
+    result = _g_win32_fill_statbuf_from_handle_info (filename,
+                                                     filename_target,
+                                                     &handle_info,
+                                                     &statbuf);
   else
     result = _fstat64 (fd, &statbuf);
 
diff --git a/glib/gtester-report b/glib/gtester-report.in
old mode 100755
new mode 100644
similarity index 98%
rename from glib/gtester-report
rename to glib/gtester-report.in
index c4790ad..01f6033
--- a/glib/gtester-report
+++ b/glib/gtester-report.in
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env @PYTHON@
 # GLib Testing Framework Utility			-*- Mode: python; -*-
 # Copyright (C) 2007 Imendio AB
 # Authors: Tim Janik
@@ -28,10 +28,6 @@
     subunit = None
 
 
-pkginstall_configvars = {
-  #@PKGINSTALL_CONFIGVARS_IN24LINES@ # configvars are substituted upon script installation
-}
-
 # xml utilities
 def find_child (node, child_name):
   for child in node.childNodes:
@@ -454,7 +450,7 @@
     :return: An options object and the program arguments.
     """
     parser = optparse.OptionParser()
-    parser.version =  pkginstall_configvars.get ('glib-version', '0.0-uninstalled')
+    parser.version = '@GLIB_VERSION@'
     parser.usage = "%prog [OPTIONS] <gtester-log.xml>"
     parser.description = "Generate HTML reports from the XML log files generated by gtester."
     parser.epilog = "gtester-report (GLib utils) version %s."% (parser.version,)
diff --git a/glib/gtester.c b/glib/gtester.c
index 41bf877..c54221a 100644
--- a/glib/gtester.c
+++ b/glib/gtester.c
@@ -500,7 +500,7 @@
   g_print ("Help Options:\n");
   g_print ("  -h, --help                    Show this help message\n\n");
   g_print ("Utility Options:\n");
-  g_print ("  -v, --version                 Print version informations\n");
+  g_print ("  -v, --version                 Print version information\n");
   g_print ("  --g-fatal-warnings            Make warnings fatal (abort)\n");
   g_print ("  -k, --keep-going              Continue running after tests failed\n");
   g_print ("  -l                            List paths of available test cases\n");
diff --git a/glib/meson.build b/glib/meson.build
index c05c694..6fc56da 100644
--- a/glib/meson.build
+++ b/glib/meson.build
@@ -7,10 +7,18 @@
 if not use_system_pcre
   subdir('pcre')
 endif
+
+# TODO: gnulib_objects, pcre_objects and pcre_deps are a workaround for
+# <https://github.com/mesonbuild/meson/issues/3934> and
+# <https://github.com/mesonbuild/meson/issues/3937>. When we can depend
+# on a meson version where those are fixed, revert the commit that
+# introduced this workaround.
 if have_good_vsnprintf and have_good_snprintf
   gnulib_lib = []
+  gnulib_objects = []
 else
   subdir('gnulib')
+  gnulib_objects = [gnulib_lib.extract_all_objects()]
 endif
 
 glib_headers = files(
@@ -234,17 +242,25 @@
   pcre_static_args = ['-DPCRE_STATIC']
 endif
 
+if use_system_pcre
+  pcre_deps = [pcre]
+  pcre_objects = []
+else
+  pcre_deps = []
+  pcre_objects = [libpcre.extract_all_objects()]
+endif
+
 libglib = library('glib-2.0',
   glib_dtrace_obj, glib_dtrace_hdr,
   sources : [deprecated_sources, glib_sources],
+  objects : [charset_lib.extract_all_objects()] + gnulib_objects + pcre_objects,
   version : library_version,
   soversion : soversion,
   install : true,
   # intl.lib is not compatible with SAFESEH
   link_args : [noseh_link_args, glib_link_flags, win32_ldflags],
   include_directories : configinc,
-  link_with : [charset_lib, gnulib_lib],
-  dependencies : [pcre, thread_dep, libintl, librt] + libiconv + platform_deps,
+  dependencies : pcre_deps + [thread_dep, libintl, librt] + libiconv + platform_deps,
   c_args : ['-DG_LOG_DOMAIN="GLib"', '-DGLIB_COMPILATION'] + pcre_static_args + glib_hidden_visibility_args
 )
 
@@ -303,7 +319,16 @@
     dependencies : [libglib_dep])
 endif
 
-install_data('gtester-report', install_dir : get_option('bindir'))
+report_conf = configuration_data()
+report_conf.set('GLIB_VERSION', glib_version)
+report_conf.set('PYTHON', python_name)
+configure_file(
+  input: 'gtester-report.in',
+  output: 'gtester-report',
+  install_dir: get_option('bindir'),
+  configuration: report_conf,
+  install_mode: 'rwxr-xr-x'
+)
 
 install_data('glib_gdb.py', install_dir : join_paths(glib_pkgdatadir, 'gdb'))
 
diff --git a/glib/tests/fileutils.c b/glib/tests/fileutils.c
index c2a553b..3ef6615 100644
--- a/glib/tests/fileutils.c
+++ b/glib/tests/fileutils.c
@@ -913,9 +913,12 @@
   cwd = g_get_current_dir ();
   path = g_build_filename (cwd, "mkdir-test", NULL);
   g_free (cwd);
+#ifndef G_OS_WIN32
+  /* 0666 on directories means nothing to Windows, it only obeys ACLs */
   ret = g_chdir (path);
   g_assert_cmpint (errno, ==, EACCES);
   g_assert_cmpint (ret, ==, -1);
+#endif
   ret = g_chmod (path, 0777);
   g_assert_cmpint (ret, ==, 0);
   ret = g_chdir (path);
@@ -945,6 +948,15 @@
   g_close (ret, &error);
   g_assert_no_error (error);
 
+#ifdef G_OS_WIN32
+  /* On Windows the 5 permission bit results in a read-only file
+   * that cannot be modified in any way (attribute changes included).
+   * Remove the read-only attribute via chmod().
+   */
+  ret = g_chmod ("test-create", 0666);
+  g_assert_cmpint (ret, ==, 0);
+#endif
+
   ut.actime = ut.modtime = (time_t)0;
   ret = g_utime ("test-create", &ut);
   g_assert_cmpint (ret, ==, 0);
diff --git a/glib/tests/keyfile.c b/glib/tests/keyfile.c
index 16f3b78..9ee7d02 100644
--- a/glib/tests/keyfile.c
+++ b/glib/tests/keyfile.c
@@ -1726,6 +1726,42 @@
   g_key_file_free (kf);
 }
 
+static void
+test_free_when_not_last_ref (void)
+{
+  GKeyFile *kf;
+  GError *error = NULL;
+  const gchar *data =
+    "[Group]\n"
+    "Key=Value\n";
+
+  kf = load_data (data, G_KEY_FILE_NONE);
+  /* Add a second ref */
+  g_key_file_ref (kf);
+
+  /* Quick coherence check */
+  g_assert_true (g_key_file_has_group (kf, "Group"));
+  g_assert_true (g_key_file_has_key (kf, "Group", "Key", &error));
+  g_assert_no_error (error);
+
+  /* Should clear all keys and groups, and remove one ref */
+  g_key_file_free (kf);
+
+  /* kf should still work */
+  g_assert_false (g_key_file_has_group (kf, "Group"));
+  g_assert_false (g_key_file_has_key (kf, "Group", "Key", &error));
+  check_error (&error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND);
+  g_clear_error (&error);
+
+  g_key_file_load_from_data (kf, data, -1, G_KEY_FILE_NONE, &error);
+  g_assert_no_error (error);
+
+  g_assert_true (g_key_file_has_group (kf, "Group"));
+  g_assert_true (g_key_file_has_key (kf, "Group", "Key", &error));
+
+  g_key_file_unref (kf);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -1771,6 +1807,7 @@
   g_test_add_func ("/keyfile/roundtrip", test_roundtrip);
   g_test_add_func ("/keyfile/bytes", test_bytes);
   g_test_add_func ("/keyfile/get-locale", test_get_locale);
+  g_test_add_func ("/keyfile/free-when-not-last-ref", test_free_when_not_last_ref);
 
   return g_test_run ();
 }
diff --git a/glib/tests/meson.build b/glib/tests/meson.build
index 0af71b0..ad0139d 100644
--- a/glib/tests/meson.build
+++ b/glib/tests/meson.build
@@ -129,6 +129,21 @@
   },
 }
 
+if installed_tests_enabled
+  install_data(
+    'keyfiletest.ini',
+    'pages.ini',
+    'keyfile.c',
+    'empty',
+    '4096-random-bytes',
+    'echo-script',
+    'echo-script.bat',
+    install_dir : installed_tests_execdir,
+  )
+  install_subdir('bookmarks', install_dir : installed_tests_execdir)
+  install_subdir('markups', install_dir : installed_tests_execdir)
+endif
+
 # Not entirely random of course, but at least it changes over time
 random_number = minor_version + meson.version().split('.').get(1).to_int()
 
@@ -155,7 +170,7 @@
       test_conf.set('installed_tests_dir', installed_tests_execdir)
       test_conf.set('program', test_name)
       configure_file(
-        input: installed_tests_template,
+        input: installed_tests_template_tap,
         output: test_name + '.test',
         install_dir: installed_tests_metadir,
         configuration: test_conf
@@ -171,7 +186,8 @@
 
     suite = ['glib'] + extra_args.get('suite', [])
     timeout = suite.contains('slow') ? 120 : 30
-    test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
+    test(test_name, exe, env : test_env, timeout : timeout, suite : suite,
+      args : ['--tap'])
   endif
 endforeach
 
diff --git a/gobject/gobject-query.c b/gobject/gobject-query.c
index 2992567..fd45fba 100644
--- a/gobject/gobject-query.c
+++ b/gobject/gobject-query.c
@@ -99,7 +99,7 @@
 help (gchar *arg)
 {
   g_fprintf (stderr, "usage: gobject-query <qualifier> [-r <type>] [-{i|b} \"\"] [-s #] [-{h|x|y}]\n");
-  g_fprintf (stderr, "       -r       specifiy root type\n");
+  g_fprintf (stderr, "       -r       specify root type\n");
   g_fprintf (stderr, "       -n       don't descend type tree\n");
   g_fprintf (stderr, "       -h       guess what ;)\n");
   g_fprintf (stderr, "       -b       specify indent string\n");
diff --git a/gobject/tests/meson.build b/gobject/tests/meson.build
index cc83c5f..74521ab 100644
--- a/gobject/tests/meson.build
+++ b/gobject/tests/meson.build
@@ -73,7 +73,7 @@
     test_conf.set('installed_tests_dir', installed_tests_execdir)
     test_conf.set('program', test_name)
     configure_file(
-      input: installed_tests_template,
+      input: installed_tests_template_tap,
       output: test_name + '.test',
       install_dir: installed_tests_metadir,
       configuration: test_conf
@@ -89,7 +89,8 @@
 
   suite = ['gobject'] + extra_args.get('suite', [])
   timeout = suite.contains('slow') ? 120 : 30
-  test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
+  test(test_name, exe, env : test_env, timeout : timeout, suite : suite,
+    args: ['--tap'])
 endforeach
 
 test(
diff --git a/gthread/meson.build b/gthread/meson.build
index 6b0748f..515479b 100644
--- a/gthread/meson.build
+++ b/gthread/meson.build
@@ -30,3 +30,5 @@
   name : 'GThread',
   description : 'Thread support for GLib',
 )
+
+libgthread_dep = declare_dependency(link_with : libgthread)
diff --git a/meson.build b/meson.build
index 7c59300..dd5bd8f 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
 project('glib', 'c', 'cpp',
   version : '2.59.0',
-  meson_version : '>= 0.47.0',
+  meson_version : '>= 0.48.0',
   default_options : [
     'buildtype=debugoptimized',
     'warning_level=1',
@@ -71,6 +71,7 @@
 installed_tests_execdir = join_paths(glib_libexecdir, 'installed-tests', meson.project_name())
 installed_tests_enabled = get_option('installed_tests')
 installed_tests_template = files('template.test.in')
+installed_tests_template_tap = files('template-tap.test.in')
 
 add_project_arguments('-D_GNU_SOURCE', language: 'c')
 
@@ -226,6 +227,7 @@
   'poll.h',
   'pwd.h',
   'sched.h',
+  'spawn.h',
   'stdint.h',
   'stdlib.h',
   'string.h',
@@ -1661,7 +1663,7 @@
   growing_stack = meson.get_cross_property('growing_stack', false)
 endif
 
-glibconfig_conf.set('G_HAVE_GROWING_STACK', growing_stack)
+glibconfig_conf.set10('G_HAVE_GROWING_STACK', growing_stack)
 
 # Tests for iconv
 #
diff --git a/template-tap.test.in b/template-tap.test.in
new file mode 100644
index 0000000..6adf73f
--- /dev/null
+++ b/template-tap.test.in
@@ -0,0 +1,4 @@
+[Test]
+Type=session
+Exec=@installed_tests_dir@/@program@ --tap
+Output=TAP
diff --git a/tests/Makefile.am b/tests/Makefile.am
index fba1865..9682352 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -123,7 +123,7 @@
 
 if HAVE_CXX
 installed_test_programs += cxx-test
-cxx_test_SOURCES = cxx-test.C
+cxx_test_SOURCES = cxx-test.cpp
 endif
 
 if ENABLE_TIMELOOP
diff --git a/tests/cxx-test.C b/tests/cxx-test.cpp
similarity index 100%
rename from tests/cxx-test.C
rename to tests/cxx-test.cpp
diff --git a/tests/gio-test.c b/tests/gio-test.c
index 22ae77c..d9bd7e3 100644
--- a/tests/gio-test.c
+++ b/tests/gio-test.c
@@ -214,11 +214,11 @@
 {
   GIOError error;
   MSG msg;
-  guint nb;
+  gsize nb;
   
   while (1)
     {
-      error = g_io_channel_read (channel, &msg, sizeof (MSG), &nb);
+      error = g_io_channel_read (channel, (gchar *) &msg, sizeof (MSG), &nb);
       
       if (error != G_IO_ERROR_NONE)
 	{
@@ -232,20 +232,25 @@
       break;
     }
 
-  g_print ("gio-test: ...Windows message for %#x: %d,%d,%d\n",
-	   msg.hwnd, msg.message, msg.wParam, msg.lParam);
+  g_print ("gio-test: ...Windows message for 0x%p: %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT "\n",
+	   msg.hwnd, msg.message, msg.wParam, (gintptr)msg.lParam);
 
   return TRUE;
 }
 
+LRESULT CALLBACK window_procedure (HWND   hwnd,
+                                   UINT   message,
+                                   WPARAM wparam,
+                                   LPARAM lparam);
+
 LRESULT CALLBACK 
 window_procedure (HWND hwnd,
 		  UINT message,
 		  WPARAM wparam,
 		  LPARAM lparam)
 {
-  g_print ("gio-test: window_procedure for %#x: %d,%d,%d\n",
-	   hwnd, message, wparam, lparam);
+  g_print ("gio-test: window_procedure for 0x%p: %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT "\n",
+	   hwnd, message, wparam, (gintptr)lparam);
   return DefWindowProc (hwnd, message, wparam, lparam);
 }
 
@@ -331,7 +336,7 @@
 	  nrunning++;
 	  
 #ifdef G_OS_WIN32
-	  cmdline = g_strdup_printf ("%d:%d:%d",
+	  cmdline = g_strdup_printf ("%d:%d:0x%p",
 				     pipe_to_sub[0],
 				     pipe_from_sub[1],
 				     hwnd);
@@ -388,7 +393,7 @@
       sscanf (argv[2], "%d:%d%n", &readfd, &writefd, &n);
 
 #ifdef G_OS_WIN32
-      sscanf (argv[2] + n, ":%d", &hwnd);
+      sscanf (argv[2] + n, ":0x%p", &hwnd);
 #endif
       
       srand (tv.tv_sec ^ (tv.tv_usec / 1000) ^ readfd ^ (writefd << 4));
@@ -413,8 +418,8 @@
 	      int msg = WM_USER + (rand() % 100);
 	      WPARAM wparam = rand ();
 	      LPARAM lparam = rand ();
-	      g_print ("gio-test: child posting message %d,%d,%d to %#x\n",
-		       msg, wparam, lparam, hwnd);
+	      g_print ("gio-test: child posting message %d,%" G_GUINTPTR_FORMAT ",%" G_GINTPTR_FORMAT " to 0x%p\n",
+		       msg, wparam, (gintptr)lparam, hwnd);
 	      PostMessage (hwnd, msg, wparam, lparam);
 	    }
 #endif
diff --git a/tests/gobject/Makefile.am b/tests/gobject/Makefile.am
index 0e60987..1bcefbe 100644
--- a/tests/gobject/Makefile.am
+++ b/tests/gobject/Makefile.am
@@ -51,24 +51,20 @@
 installed_test_programs += timeloop-closure
 endif
 
-# The marshal test requires running a binary, which means we cannot
-# build it when cross-compiling
-if !CROSS_COMPILING
 glib_genmarshal=$(top_builddir)/gobject/glib-genmarshal
 
 testmarshal.h: stamp-testmarshal.h
 	@true
 stamp-testmarshal.h: testmarshal.list $(glib_genmarshal)
-	$(AM_V_GEN) $(glib_genmarshal) --prefix=test_marshal $(srcdir)/testmarshal.list --header >> xgen-gmh \
+	$(AM_V_GEN) $(PYTHON) $(glib_genmarshal) --prefix=test_marshal $(srcdir)/testmarshal.list --header >> xgen-gmh \
 	&& (cmp -s xgen-gmh testmarshal.h 2>/dev/null || cp xgen-gmh testmarshal.h) \
 	&& rm -f xgen-gmh xgen-gmh~ \
 	&& echo timestamp > $@
 testmarshal.c: testmarshal.h testmarshal.list $(glib_genmarshal)
-	$(AM_V_GEN) (echo "#include \"testmarshal.h\""; $(glib_genmarshal) --prefix=test_marshal $(srcdir)/testmarshal.list --body) >> xgen-gmc \
+	$(AM_V_GEN) (echo "#include \"testmarshal.h\""; $(PYTHON) $(glib_genmarshal) --prefix=test_marshal $(srcdir)/testmarshal.list --body) >> xgen-gmc \
 	&& cp xgen-gmc testmarshal.c \
 	&& rm -f xgen-gmc xgen-gmc~
 
 BUILT_SOURCES += testmarshal.h testmarshal.c
 CLEANFILES += stamp-testmarshal.h testmarshal.h testmarshal.c
 EXTRA_DIST += testcommon.h testmarshal.list
-endif # !CROSS_COMPILING
\ No newline at end of file
diff --git a/tests/gobject/meson.build b/tests/gobject/meson.build
index 4944599..a79a9f7 100644
--- a/tests/gobject/meson.build
+++ b/tests/gobject/meson.build
@@ -1,16 +1,3 @@
-gobject_tests = [
-  ['gvalue-test'],
-  ['paramspec-test'],
-  ['deftype'],
-  ['defaultiface', ['defaultiface.c', 'testmodule.c']],
-  ['dynamictype', ['dynamictype.c', 'testmodule.c']],
-  ['override'],
-  ['signals'],
-  ['singleton'],
-  ['references'],
-  ['testgobject'],
-]
-
 # We cannot use gnome.genmarshal() here
 testmarshal_h = custom_target('testmarshal_h',
   output : 'testmarshal.h',
@@ -39,41 +26,76 @@
   ],
 )
 
-gobject_tests += [
-  ['accumulator', ['accumulator.c', testmarshal_c, testmarshal_h]],
-]
+gobject_tests = {
+  'gvalue-test' : {'tap' : true},
+  'paramspec-test' : {'tap' : true},
+  'deftype' : {},
+  'defaultiface' : {
+    'extra_sources' : ['testmodule.c'],
+  },
+  'dynamictype' : {
+    'extra_sources' : ['testmodule.c'],
+  },
+  'override' : {},
+  'signals' : {},
+  'singleton' : {},
+  'references' : {},
+  'testgobject' : {},
+  'accumulator' : {
+    'extra_sources' : [testmarshal_c, testmarshal_h],
+  },
+}
 
-foreach t : gobject_tests
-  test_name = t.get(0)
-  test_src = t.get(1, test_name + '.c')
-  test_extra_cargs = t.get(2, [])
-  test_timeout = t.get(3, 30)
-  test_suite = test_timeout == 30 ? ['gobject'] : ['gobject', 'slow']
+if host_system != 'windows'
+  gobject_tests += {
+    'timeloop-closure' : {},
+  }
+endif
+
+common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS']
+common_deps = [libm, thread_dep, libglib_dep, libgobject_dep]
+
+foreach test_name, extra_args : gobject_tests
+  source = extra_args.get('source', test_name + '.c')
+  extra_sources = extra_args.get('extra_sources', [])
+  install = installed_tests_enabled and extra_args.get('install', true)
+  template = extra_args.get('tap', false) ? installed_tests_template_tap : installed_tests_template
+  test_command_args = extra_args.get('tap', false) ? ['--tap'] : []
+
+  if install
+    test_conf = configuration_data()
+    test_conf.set('installed_tests_dir', installed_tests_execdir)
+    test_conf.set('program', test_name)
+    configure_file(
+      input: template,
+      output: test_name + '.test',
+      install_dir: installed_tests_metadir,
+      configuration: test_conf
+    )
+  endif
 
   # FIXME? $(GLIB_DEBUG_FLAGS)
-  exe = executable(test_name + '-gobject', test_src,
-    c_args : test_cargs + test_extra_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS'],
-    dependencies : [libm, thread_dep, libglib_dep, libgobject_dep],
-    install : false,
+  exe = executable(test_name, [source, extra_sources],
+    c_args : common_c_args + extra_args.get('c_args', []),
+    dependencies : common_deps + extra_args.get('dependencies', []),
+    install_dir: installed_tests_execdir,
+    install: install,
   )
+
+  suite = ['gobject'] + extra_args.get('suite', [])
+  timeout = suite.contains('slow') ? 120 : 30
   # FIXME? TESTS_ENVIRONMENT = LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset
-  test(test_name, exe, env : test_env, timeout : test_timeout, suite : test_suite)
+  test(test_name, exe, env : test_env, timeout : timeout, suite : suite,
+    args : test_command_args)
 endforeach
 
 # Don't install these ones, and keep them out of 'make check' because they take too long...
 executable('performance', 'performance.c',
-    c_args : test_cargs + test_extra_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS'],
-    dependencies : [libm, thread_dep, libglib_dep, libgobject_dep],
+    c_args : common_c_args,
+    dependencies : common_deps,
     install : false)
 
 executable('performance-threaded', 'performance-threaded.c',
-    c_args : test_cargs + test_extra_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS'],
-    dependencies : [libm, thread_dep, libglib_dep, libgobject_dep],
+    c_args : common_c_args,
+    dependencies : common_deps,
     install : false)
-
-if host_system != 'windows' and host_system != 'minix'
-  executable('timeloop-closure', 'timeloop-closure.c',
-      c_args : test_cargs + test_extra_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS'],
-      dependencies : [libm, thread_dep, libglib_dep, libgobject_dep],
-      install : false)
-endif
diff --git a/tests/mapping-test.c b/tests/mapping-test.c
index 3eb90fa..ad776fa 100644
--- a/tests/mapping-test.c
+++ b/tests/mapping-test.c
@@ -25,6 +25,9 @@
 #ifdef G_OS_UNIX
 #include <unistd.h>
 #endif
+#ifdef G_OS_WIN32
+#include <process.h>
+#endif
 
 static gchar *dir, *filename, *displayname, *childname;
 
@@ -188,7 +191,9 @@
   gsize len;
   gchar *child_argv[4];
   GPid  child_pid;
+#ifndef G_OS_WIN32
   GMainLoop *loop;
+#endif
   gchar pid[100];
   
 #ifdef G_OS_WIN32
diff --git a/tests/meson.build b/tests/meson.build
index 3beb4c6..68e0431 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,12 +1,143 @@
 # tests
 
+# Not entirely random of course, but at least it changes over time
+random_number = minor_version + meson.version().split('.').get(1).to_int()
+
 test_env = environment()
 test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
 test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
 test_env.set('G_DEBUG', 'gc-friendly')
 test_env.set('MALLOC_CHECK_', '2')
+test_env.set('MALLOC_PERTURB_', '@0@'.format(random_number % 256))
 
 test_cargs = ['-DG_LOG_DOMAIN="GLib"']
 
 subdir('gobject')
 subdir('refcount')
+
+tests = {
+  'testglib' : {'tap' : true},
+  'testgdate' : {},
+  'datetime' : {},
+  'atomic-test' : {},
+  'bit-test' : {},
+  'child-test' : {},
+  'completion-test' : {},
+  'dirname-test' : {},
+  'file-test' : {},
+  'env-test' : {},
+  'gio-test' : {},
+  'mainloop-test' : {},
+  'mapping-test' : {},
+  'onceinit' : {},
+  'asyncqueue-test' : {},
+  'qsort-test' : {},
+  'relation-test' : {},
+  'slice-concurrent' : {},
+  'slice-threadinit' : {
+    'dependencies' : [libgthread_dep],
+  },
+  'sources' : {},
+  'thread-test' : {},
+  'threadpool-test' : {'suite' : ['slow']},
+  'type-test' : {},
+  'unicode-caseconv' : {},
+  'unicode-encoding' : {},
+  'module-test' : {
+    'dependencies' : [libgmodule_dep],
+    'export_dynamic' : true,
+  },
+  'cxx-test' : {
+    'source' : 'cxx-test.cpp',
+    'include_directories' : gmoduleinc,
+    'dependencies' : [libgio_dep],
+  },
+}
+
+test_extra_programs = {
+  'slice-test' : {
+    'extra_sources' : ['memchunks.c'],
+  },
+  'slice-color' : {
+    'extra_sources' : ['memchunks.c'],
+  },
+  'assert-msg-test' : {},
+  'unicode-collate' : {},
+}
+
+if host_machine.system() != 'windows'
+  tests += {
+    'timeloop' : {},
+    'spawn-test' : {},
+    'iochannel-test' : {},
+  }
+endif
+
+if installed_tests_enabled
+  install_data(
+    'iochannel-test-infile',
+    'casemap.txt',
+    'casefold.txt',
+    'utf8.txt',
+    install_dir : installed_tests_execdir,
+  )
+endif
+
+foreach module : ['moduletestplugin_a', 'moduletestplugin_b']
+  shared_module(module, 'lib@0@.c'.format(module),
+    dependencies : [libglib_dep, libgmodule_dep],
+    install_dir : installed_tests_execdir,
+    install : installed_tests_enabled
+  )
+endforeach
+
+common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS']
+common_deps = [libm, thread_dep, libglib_dep]
+
+foreach test_name, extra_args : tests
+  source = extra_args.get('source', test_name + '.c')
+  extra_sources = extra_args.get('extra_sources', [])
+  install = installed_tests_enabled and extra_args.get('install', true)
+  template = extra_args.get('tap', false) ? installed_tests_template_tap : installed_tests_template
+  test_command_args = extra_args.get('tap', false) ? ['--tap'] : []
+
+  if install
+    test_conf = configuration_data()
+    test_conf.set('installed_tests_dir', installed_tests_execdir)
+    test_conf.set('program', test_name)
+    configure_file(
+      input: template,
+      output: test_name + '.test',
+      install_dir: installed_tests_metadir,
+      configuration: test_conf
+    )
+  endif
+
+  # FIXME? $(GLIB_DEBUG_FLAGS)
+  exe = executable(test_name, [source, extra_sources],
+    c_args : common_c_args + extra_args.get('c_args', []),
+    dependencies : common_deps + extra_args.get('dependencies', []),
+    export_dynamic : extra_args.get('export_dynamic', false),
+    include_directories : extra_args.get('include_directories', []),
+    install_dir: installed_tests_execdir,
+    install: install,
+  )
+
+  suite = ['glib'] + extra_args.get('suite', [])
+  timeout = suite.contains('slow') ? 120 : 30
+  # FIXME? TESTS_ENVIRONMENT = LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset
+  test(test_name, exe, env : test_env, timeout : timeout, suite : suite,
+    args : test_command_args)
+endforeach
+
+foreach program_name, extra_args : test_extra_programs
+  source = extra_args.get('source', program_name + '.c')
+  extra_sources = extra_args.get('extra_sources', [])
+  install = installed_tests_enabled and extra_args.get('install', true)
+  executable(program_name, [source, extra_sources],
+      c_args : common_c_args,
+      dependencies : common_deps + extra_args.get('dependencies', []),
+      install_dir : installed_tests_execdir,
+      install : install,
+  )
+endforeach
diff --git a/tests/refcount/meson.build b/tests/refcount/meson.build
index 9046bf7..3a2072d 100644
--- a/tests/refcount/meson.build
+++ b/tests/refcount/meson.build
@@ -1,30 +1,59 @@
-refcount_tests = [
- ['closures', 'closures.c', []],
- ['objects', 'objects.c', []],
- ['objects2', 'objects2.c', [], 90],
- ['properties', 'properties.c', []],
- ['properties2', 'properties2.c', [], 90],
- ['properties3', 'properties3.c', [], 90], # extra long timeout
- ['properties4', 'properties4.c', []],
- ['signal1', 'signals.c', ['-DTESTNUM=1']],
- ['signal2', 'signals.c', ['-DTESTNUM=2']],
- ['signal3', 'signals.c', ['-DTESTNUM=3']],
- ['signal4', 'signals.c', ['-DTESTNUM=4']],
-]
+refcount_tests = {
+  'closures' : {'suite' : ['slow']},
+  'objects' : {},
+  'objects2' : {'suite' : ['slow']},
+  'properties' : {},
+  'properties2' : {'suite' : ['slow']},
+  'properties3' : {'suite' : ['slow']},
+  'properties4' : {},
+  'signal1' : {
+    'source' : 'signals.c',
+    'c_args' : ['-DTESTNUM=1'],
+  },
+  'signal2' : {
+    'source' : 'signals.c',
+    'c_args' : ['-DTESTNUM=2'],
+  },
+  'signal3' : {
+    'source' : 'signals.c',
+    'c_args' : ['-DTESTNUM=3'],
+  },
+  'signal4' : {
+    'source' : 'signals.c',
+    'c_args' : ['-DTESTNUM=4'],
+  },
+}
 
-foreach t : refcount_tests
-  test_name = t.get(0)
-  test_src = t.get(1)
-  test_extra_cargs = t.get(2)
-  test_timeout = t.get(3, 30)
-  test_suite = test_timeout == 30 ? ['refcount'] : ['refcount', 'slow']
+common_c_args = test_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS']
+common_deps = [libm, thread_dep, libglib_dep, libgobject_dep]
+
+foreach test_name, extra_args : refcount_tests
+  source = extra_args.get('source', test_name + '.c')
+  extra_sources = extra_args.get('extra_sources', [])
+  install = installed_tests_enabled and extra_args.get('install', true)
+
+  if install
+    test_conf = configuration_data()
+    test_conf.set('installed_tests_dir', installed_tests_execdir)
+    test_conf.set('program', test_name)
+    configure_file(
+      input: installed_tests_template,
+      output: test_name + '.test',
+      install_dir: installed_tests_metadir,
+      configuration: test_conf
+    )
+  endif
 
   # FIXME? $(GLIB_DEBUG_FLAGS)
-  exe = executable(test_name + '-test', test_src,
-    c_args : test_cargs + test_extra_cargs + ['-DGLIB_DISABLE_DEPRECATION_WARNINGS'],
-    dependencies : [libm, thread_dep, libglib_dep, libgobject_dep],
-    install : false,
+  exe = executable(test_name, [source, extra_sources],
+    c_args : common_c_args + extra_args.get('c_args', []),
+    dependencies : common_deps + extra_args.get('dependencies', []),
+    install_dir: installed_tests_execdir,
+    install: install,
   )
+
+  suite = ['refcount'] + extra_args.get('suite', [])
+  timeout = suite.contains('slow') ? 120 : 30
   # FIXME? TESTS_ENVIRONMENT = LIBCHARSET_ALIAS_DIR=$(top_builddir)/glib/libcharset
-  test(test_name, exe, env : test_env, timeout : test_timeout, suite : test_suite)
+  test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
 endforeach
diff --git a/tests/type-test.c b/tests/type-test.c
index ed7cc44..43da394 100644
--- a/tests/type-test.c
+++ b/tests/type-test.c
@@ -120,8 +120,13 @@
   gu64t1 = G_GINT64_CONSTANT (0xFAFAFAFAFAFAFAFA); 
 
 #define FORMAT64 "%" G_GINT64_FORMAT " %" G_GUINT64_FORMAT "\n"
+#ifndef G_OS_WIN32
+#  define SCAN_FORMAT64 FORMAT64
+#else
+#  define SCAN_FORMAT64 "%I64d %I64u\n"
+#endif
   string = g_strdup_printf (FORMAT64, gi64t1, gu64t1);
-  sscanf (string, FORMAT64, &gi64t2, &gu64t2);
+  sscanf (string, SCAN_FORMAT64, &gi64t2, &gu64t2);
   g_free (string);
   g_assert (gi64t1 == gi64t2);
   g_assert (gu64t1 == gu64t2);
@@ -130,8 +135,13 @@
   gst1 = 0xFAFAFAFA; 
 
 #define FORMATSIZE "%" G_GSSIZE_FORMAT " %" G_GSIZE_FORMAT "\n"
+#ifndef G_OS_WIN32
+#  define SCAN_FORMATSIZE FORMATSIZE
+#else
+#  define SCAN_FORMATSIZE "%Id %Iu\n"
+#endif
   string = g_strdup_printf (FORMATSIZE, gsst1, gst1);
-  sscanf (string, FORMATSIZE, &gsst2, &gst2);
+  sscanf (string, SCAN_FORMATSIZE, &gsst2, &gst2);
   g_free (string);
   g_assert (gsst1 == gsst2);
   g_assert (gst1 == gst2);
diff --git a/tests/unicode-encoding.c b/tests/unicode-encoding.c
index 8ac4f95..c729b28 100644
--- a/tests/unicode-encoding.c
+++ b/tests/unicode-encoding.c
@@ -9,6 +9,7 @@
 
 static gint exit_status = 0;
 
+G_GNUC_PRINTF (1, 2)
 static void
 croak (char *format, ...)
 {
@@ -21,6 +22,7 @@
   exit (1);
 }
 
+G_GNUC_PRINTF (1, 2)
 static void
 fail (char *format, ...)
 {