[epiphany/wip/meson: 4/4] Port to meson build system



commit e737fe8718fec9715669f57dd0f6105e1b20992d
Author: Michael Catanzaro <mcatanzaro igalia com>
Date:   Wed Apr 26 16:42:13 2017 -0500

    Port to meson build system

 .gitmodules                                        |    7 +-
 Makefile.am                                        |   26 -
 autogen.sh                                         |   39 --
 configure.ac                                       |  252 -------
 data/Makefile.am                                   |   58 --
 data/icons/Makefile.am                             |   55 --
 .../16x16/apps/org.gnome.Epiphany.png}             |  Bin 1116 -> 1116 bytes
 .../24x24/apps/org.gnome.Epiphany.png}             |  Bin 1721 -> 1721 bytes
 .../32x32/apps/org.gnome.Epiphany.png}             |  Bin 2757 -> 2757 bytes
 .../48x48/apps/org.gnome.Epiphany.png}             |  Bin 4395 -> 4395 bytes
 .../512x512/apps/org.gnome.Epiphany.png}           |  Bin 137578 -> 137578 bytes
 .../symbolic/apps/org.gnome.Epiphany-symbolic.svg} |    2 +-
 data/meson.build                                   |   66 ++
 embed/Makefile.am                                  |  133 ----
 embed/ephy-embed-shell.c                           |    8 +-
 embed/ephy-embed-type-builtins.c.in                |   42 ++
 embed/ephy-embed-type-builtins.h.in                |   22 +
 embed/ephy-web-view.c                              |    2 +-
 embed/meson.build                                  |   71 ++
 embed/web-extension/Makefile.am                    |   46 --
 embed/web-extension/ephy-uri-tester.c              |   20 +-
 embed/web-extension/ephy-web-extension.c           |    2 +-
 embed/web-extension/meson.build                    |   25 +
 git.mk                                             |  350 ----------
 gvdb/Makefile.am                                   |   18 -
 gvdb/gvdb                                          |    1 -
 help/Makefile.am                                   |   39 --
 help/meson.build                                   |   46 ++
 lib/Makefile.am                                    |  161 -----
 lib/egg/Makefile.am                                |   13 -
 lib/ephy-lib-type-builtins.c.in                    |   42 ++
 lib/ephy-lib-type-builtins.h.in                    |   22 +
 lib/ephy-profile-utils.c                           |    4 +-
 lib/history/Makefile.am                            |   87 ---
 lib/history/ephy-history-service.c                 |    2 +-
 lib/meson.build                                    |   84 +++
 lib/widgets/Makefile.am                            |  120 ----
 lib/widgets/ephy-widgets-type-builtins.c.in        |   42 ++
 lib/widgets/ephy-widgets-type-builtins.h.in        |   22 +
 lib/widgets/meson.build                            |   58 ++
 libgd                                              |    1 -
 meson.build                                        |  101 +++
 meson_options.txt                                  |   29 +
 po/meson.build                                     |    1 +
 post_install.py                                    |   19 +
 release.sh                                         |   31 +
 src/Makefile.am                                    |  284 --------
 src/bookmarks/ephy-bookmark-properties-grid.c      |    6 +-
 src/bookmarks/ephy-bookmark.c                      |    6 +-
 src/bookmarks/ephy-bookmark.h                      |    2 +-
 src/ephy-main.c                                    |    4 +-
 src/ephy-shell.c                                   |   12 +-
 src/ephy-shell.h                                   |    4 +-
 src/ephy-type-builtins.c.in                        |   42 ++
 src/ephy-type-builtins.h.in                        |   22 +
 src/meson.build                                    |  134 ++++
 src/prefs-dialog.c                                 |   18 +-
 src/profile-migrator/Makefile.am                   |   46 --
 src/search-provider/Makefile.am                    |   74 --
 src/sync/ephy-sync-service.c                       |    2 +-
 subprojects/gvdb/README.epiphany                   |    3 +
 subprojects/gvdb/gvdb-builder.c                    |  523 ++++++++++++++
 subprojects/gvdb/gvdb-builder.h                    |   57 ++
 subprojects/gvdb/gvdb-format.h                     |   87 +++
 subprojects/gvdb/gvdb-reader.c                     |  720 ++++++++++++++++++++
 subprojects/gvdb/gvdb-reader.h                     |   65 ++
 subprojects/gvdb/meson.build                       |   22 +
 subprojects/libgd                                  |    1 +
 tests/Makefile.am                                  |  200 ------
 tests/data/Makefile.am                             |    3 -
 tests/ephy-file-helpers-test.c                     |    2 +-
 tests/meson.build                                  |  123 ++++
 72 files changed, 2572 insertions(+), 2059 deletions(-)
---
diff --git a/.gitmodules b/.gitmodules
index 894906c..c651969 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "libgd"]
-       path = libgd
+[submodule "subprojects/libgd"]
+       path = subprojects/libgd
        url = ../libgd
-[submodule "gvdb/gvdb"]
-       path = gvdb/gvdb
-       url = ../gvdb
diff --git a/data/icons/hicolor_apps_symbolic_org.gnome.Epiphany-symbolic.svg 
b/data/icons/hicolor/symbolic/apps/org.gnome.Epiphany-symbolic.svg
similarity index 99%
rename from data/icons/hicolor_apps_symbolic_org.gnome.Epiphany-symbolic.svg
rename to data/icons/hicolor/symbolic/apps/org.gnome.Epiphany-symbolic.svg
index ddc8e07..ef4e33d 100644
--- a/data/icons/hicolor_apps_symbolic_org.gnome.Epiphany-symbolic.svg
+++ b/data/icons/hicolor/symbolic/apps/org.gnome.Epiphany-symbolic.svg
@@ -19,7 +19,7 @@
   <g style='display:inline' inkscape:groupmode='layer' id='layer9' inkscape:label='status' 
transform='translate(-183,-529)'/>
   <g inkscape:groupmode='layer' id='layer10' inkscape:label='devices' transform='translate(-183,-529)'/>
   <g inkscape:groupmode='layer' id='layer11' inkscape:label='apps' transform='translate(-183,-529)'>
-    
+
     <path 
style='color:#000000;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:block;overflow:visible;enable-background:accumulate'
 sodipodi:nodetypes='cccccccc' id='path6242' inkscape:connector-curvature='0' d='m 191.0002,533.84553 
0,10.38049 -2.34399,-2.28818 -1.33941,2.73465 c -0.32808,0.73962 -2.03368,0.14492 -1.5487,-0.84412 l 
1.32547,-2.83928 -2.95789,0 6.86452,-7.14356 z'/>
     <path 
style='font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans'
 sodipodi:nodetypes='csccssccsssc' id='path23405' inkscape:connector-curvature='0' d='m 190.15645,530.0625 c 
-3.82659,0.46006 -6.57883,3.95775 -6.09375,7.78125 0.13127,1.03473 0.29377,1.38184 0.29377,1.38184 l 
1.67498,-1.63184 c -0.33104,-2.75343 1.62156,-5.23146 4.375,-5.5625 2.75344,-0.33104 5.23146,1.62156 
5.5625,4.375 0.31355,2.60795 -1.39127,5.02493 -3.96875,5.53125 l 0.0312,2 c 0,0 0.52086,-0.1059 
0.62354,-0.13097 3.41561,-0.83
 385 5.70627,-4.1273 5.28271,-7.65028 -0.46006,-3.8266 -3.95466,-6.55381 -7.78125,-6.09375 z'/>
     <path 
style='opacity:0.3;color:#000000;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate'
 id='path11289' inkscape:connector-curvature='0' d='m 187.11293,536.81497 0,-0.20574 -0.19826,0.0186 c 
0.0165,-0.13095 0.0329,-0.26167 0.0496,-0.3926 l -0.11578,0 -0.11556,0.14959 -0.11578,0.0559 -0.1653,-0.0932 
-0.0165,-0.20575 0.0331,-0.22438 0.24798,-0.18688 0.19826,0 0.0329,-0.11229 0.24786,0.0559 0.18183,0.2246 
0.0331,-0.37419 0.31401,-0.26167 0.11567,-0.28055 0.23133,-0.0934 0.1322,-0.18688 0.29738,-0.0564 
0.14885,-0.22415 c -0.14874,0 -0.29749,0 -0.44623,0 l 0.28094,-0.13095 0.19814,0 0.28106,-0.0937 
0.0331,-0.11186 -0.0992,-0.0937 -0.11567,-0.0375 0.0331,-0.11208 -0.0826,-0.16822 -0.19837,0.0746 
0.0331,-0.14947 -0.23134,-0.13096 -0.18171,0.3177 0.0165,0.11229 -0.18171,0.075 -0.11578,0.24302 
-0.0495,-0.22438 -0.31402,-0.13095 -0.0496,-0.16822 0.41315,-0.24325 0.18
 182,-0.16822 0.0165,-0.20563 -0.0991,-0.0562 -0.13219,-0.0188 -0.0826,0.20575 c 0,0 -0.1382,0.0271 
-0.17373,0.0358 -0.45378,0.41804 -1.37066,1.32044 -1.58368,3.02405 0.008,0.0395 0.15441,0.26854 
0.15441,0.26854 l 0.347,0.20552 0.347,0.0937 m 3.96609,-4.30034 -0.4298,-0.16833 -0.49552,0.0561 
-0.61161,0.16822 -0.11567,0.11229 0.38008,0.26167 0,0.14959 -0.14875,0.14959 0.19846,0.39294 0.13188,-0.075 
0.16561,-0.26168 c 0.2553,-0.0789 0.4842,-0.16833 0.72686,-0.28053 l 0.19846,-0.5048 m 2.52925,0.34192 
-0.375,0.0937 -0.21875,0.15625 0,0.125 -0.375,0.25 0.0937,0.34375 0.21875,-0.15625 0.125,0.15625 
0.15625,0.0937 0.0937,-0.28125 -0.0625,-0.15625 0.0625,-0.0937 0.21875,-0.1875 0.0937,0 -0.0937,0.21875 
0,0.1875 c 0.0892,-0.0242 0.1588,-0.051 0.25,-0.0625 l -0.25,0.1875 0,0.125 -0.3125,0.21875 -0.28125,-0.0625 
0,-0.15625 -0.125,0.0625 0.0625,0.15625 -0.21875,0 -0.125,0.21875 -0.15625,0.15625 -0.0937,0.0312 0,0.1875 
0.0312,0.15625 -0.0312,0 0,0.53125 0.0625,-0.0312 0.0937,-0.21875 0.1
 875,-0.125 0.0312,-0.0937 0.28125,-0.0625 0.15625,0.1875 0.1875,0.0937 -0.0937,0.1875 0.15625,-0.0312 
0.0625,-0.21875 -0.1875,-0.21875 0.0625,0 0.21875,0.15625 0.0312,0.21875 0.15625,0.21875 0.0625,-0.3125 
0.0937,-0.0312 c 0.0959,0.0996 0.1692,0.23163 0.25,0.34375 l 0.28125,0 0.1875,0.125 -0.0937,0.0937 
-0.15625,0.15625 -0.25,0 -0.34375,-0.0937 -0.1875,0 -0.125,0.15625 -0.34375,-0.375 -0.25,-0.0625 
-0.375,0.0625 -0.15625,0.0937 0,2.40625 0.0312,0.0312 0.25,-0.15625 0.0937,0.0937 0.28125,0 0.125,0.15625 
-0.0937,0.3125 0.1875,0.1875 0,0.375 0.125,0.25 -0.0937,0.25 c -0.009,0.16159 0,0.30714 0,0.46875 
0.0795,0.21894 0.14355,0.43575 0.21875,0.65625 l 0.0625,0.34375 0,0.1875 0.125,0 0.21875,-0.125 0.25,0 
0.375,-0.4375 -0.0312,-0.15625 0.25,-0.21875 -0.1875,-0.1875 0.21875,-0.1875 0.21875,-0.125 0.0937,-0.125 
-0.0625,-0.25 0,-0.59375 0.1875,-0.375 0.1875,-0.25 0.25,-0.5625 0,-0.15625 c -0.11654,0.0146 -0.22972,0.0231 
-0.34375,0.0312 -0.0722,0.005 -0.14446,0 -0.21875,0 -0.12359,-0.
 25961 -0.2183,-0.50966 -0.3125,-0.78125 l -0.15625,-0.1875 -0.0937,-0.3125 0.0625,-0.0625 0.21875,0.25 
0.25,0.5625 0.15625,0.15625 -0.0625,0.21875 0.15625,0.15625 0.25,-0.25 0.3125,-0.21875 0.15625,-0.1875 
0,-0.21875 c -0.0389,-0.0732 -0.0547,-0.14545 -0.0937,-0.21875 l -0.15625,0.1875 -0.125,-0.15625 
-0.1875,-0.125 0,-0.28125 0.21875,0.21875 0.21875,-0.0312 c 0.10166,0.0923 0.19205,0.20751 0.28125,0.3125 l 
0.15625,-0.1875 c 0,-0.17463 -0.19976,-1.02044 -0.625,-1.75 -0.42526,-0.72932 -1.15625,-1.40625 
-1.15625,-1.40625 l -0.0625,0.0937 -0.21875,0.21875 -0.25,-0.25 0.25,0 0.125,-0.125 -0.46875,-0.0937 
-0.25,-0.0937 z'/>
diff --git a/data/meson.build b/data/meson.build
new file mode 100644
index 0000000..855529a
--- /dev/null
+++ b/data/meson.build
@@ -0,0 +1,66 @@
+install_subdir('icons/hicolor',
+  install_dir: join_paths(datadir, 'icons')
+)
+
+i18n.merge_file('desktop-file',
+  input: 'org.gnome.Epiphany.desktop.in',
+  output: 'org.gnome.Epiphany.desktop',
+  install: true,
+  install_dir: join_paths(datadir, 'applications'),
+  po_dir: '../po',
+  type: 'desktop'
+)
+
+i18n.merge_file('appdata-file',
+  input: 'org.gnome.Epiphany.appdata.xml.in',
+  output: 'org.gnome.Epiphany.appdata.xml',
+  install: true,
+  install_dir: join_paths(datadir, 'appdata'),
+  po_dir: '../po'
+)
+
+# https://github.com/mesonbuild/meson/issues/1687
+custom_target('gsettings-enums',
+  input: '../lib/ephy-prefs.h',
+  output: 'org.gnome.Epiphany.enums.xml',
+  install: true,
+  install_dir: join_paths(datadir, 'glib-2.0', 'schemas'),
+  capture: true,
+  command: ['glib-mkenums',
+    '--comments', '<!-- @comment@ -->',
+    '--fhead', '<schemalist>',
+    '--vhead', '  <@type@ id="org.gnome.Epiphany.@EnumName@">',
+    '--vprod', '    <value nick="@valuenick@" value="@valuenum@"/>',
+    '--vtail', '  </@type@>',
+    '--ftail', '</schemalist>',
+    '@INPUT@'
+  ]
+)
+install_data('org.gnome.epiphany.gschema.xml',
+  install_dir: join_paths(datadir, 'glib-2.0', 'schemas')
+)
+
+serviceconf = configuration_data()
+serviceconf.set('libexecdir', libexecdir)
+configure_file(
+  input: 'org.gnome.EpiphanySearchProvider.service.in',
+  output: 'org.gnome.EpiphanySearchProvider.service',
+  configuration: serviceconf,
+  install: true,
+  install_dir: join_paths(datadir, 'dbus-1', 'services')
+)
+install_data('epiphany-search-provider.ini',
+  install_dir: join_paths(datadir, 'gnome-shell', 'search-providers')
+)
+
+bookmarksconf = configuration_data()
+bookmarksconf.set('pkgdatadir', pkgdatadir)
+configure_file(
+  input: 'default-bookmarks.rdf.in',
+  output: 'default-bookmarks.rdf',
+  configuration: bookmarksconf,
+  install: true,
+  install_dir: pkgdatadir
+)
+
+install_man('epiphany.1')
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index eb5f0a9..bb5572d 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -43,7 +43,7 @@
 #include <gtk/gtk.h>
 #include <stdlib.h>
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
 #include <httpseverywhere.h>
 #endif
 
@@ -821,7 +821,7 @@ ephy_embed_shell_create_web_context (EphyEmbedShell *shell)
   g_object_unref (manager);
 }
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
 static void
 https_everywhere_update_cb (HTTPSEverywhereUpdater *updater,
                             GAsyncResult           *result)
@@ -868,7 +868,7 @@ ephy_embed_shell_startup (GApplication *application)
   char *filename;
   char *cookie_policy;
   char *filters_dir;
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
   HTTPSEverywhereContext *context;
   HTTPSEverywhereUpdater *updater;
 #endif
@@ -973,7 +973,7 @@ ephy_embed_shell_startup (GApplication *application)
   ephy_embed_prefs_set_cookie_accept_policy (cookie_manager, cookie_policy);
   g_free (cookie_policy);
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
     /* We might want to be smarter about this in the future. For now,
      * trigger an update of the rulesets once each time Epiphany is started.
      * Note that the updated rules will not be used until the next time Epiphany
diff --git a/embed/ephy-embed-type-builtins.c.in b/embed/ephy-embed-type-builtins.c.in
new file mode 100644
index 0000000..ed7feca
--- /dev/null
+++ b/embed/ephy-embed-type-builtins.c.in
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+
+#include <config.h>
+#include "ephy-embed-type-builtins.h"
+
+/*** END file-header ***/
+/*** BEGIN file-production ***/
+
+/* enumerations from "@basename@" */
+#include "@basename@"
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type(void) {
+    static volatile gsize g_define_type_id__volatile = 0;
+
+    if(g_once_init_enter(&g_define_type_id__volatile)) {
+        static const G@Type@Value values [] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+            { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+            { 0, NULL, NULL }
+        };
+
+        GType g_define_type_id =
+            g_@type@_register_static(g_intern_static_string("@EnumName@"), values);
+        g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+    }
+
+    return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/embed/ephy-embed-type-builtins.h.in b/embed/ephy-embed-type-builtins.h.in
new file mode 100644
index 0000000..f367945
--- /dev/null
+++ b/embed/ephy-embed-type-builtins.h.in
@@ -0,0 +1,22 @@
+/*** BEGIN file-header ***/
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@basename@" */
+/*** END file-production ***/
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+
+G_END_DECLS
+
+/*** END file-tail ***/
diff --git a/embed/ephy-web-view.c b/embed/ephy-web-view.c
index 8c490f6..4915238 100644
--- a/embed/ephy-web-view.c
+++ b/embed/ephy-web-view.c
@@ -1994,7 +1994,7 @@ format_crash_error_page (const char  *uri,
                                      formatted_uri);
 
   formatted_distributor = g_strdup_printf ("<strong>%s</strong>",
-                                           LSB_DISTRIBUTOR);
+                                           DISTRIBUTOR_NAME);
   /* Further error details when a site cannot be loaded due to a page crash error. */
   second_paragraph = g_strdup_printf (_("If this happens again, please report "
                                         "the problem to the %s developers."),
diff --git a/embed/meson.build b/embed/meson.build
new file mode 100644
index 0000000..b391dd9
--- /dev/null
+++ b/embed/meson.build
@@ -0,0 +1,71 @@
+subdir('web-extension')
+
+types_headers = [
+  'ephy-download.h',
+  'ephy-embed-shell.h',
+  'ephy-web-view.h'
+]
+
+enums = gnome.mkenums('ephy-embed',
+  sources: types_headers,
+  h_template: 'ephy-embed-type-builtins.h.in',
+  c_template: 'ephy-embed-type-builtins.c.in'
+)
+
+libephyembed_sources = [
+  'ephy-about-handler.c',
+  'ephy-downloads-manager.c',
+  'ephy-download.c',
+  'ephy-embed.c',
+  'ephy-embed-container.c',
+  'ephy-embed-event.c',
+  'ephy-embed-prefs.c',
+  'ephy-embed-shell.c',
+  'ephy-embed-utils.c',
+  'ephy-encoding.c',
+  'ephy-encodings.c',
+  'ephy-file-monitor.c',
+  'ephy-find-toolbar.c',
+  'ephy-notification-container.c',
+  'ephy-web-view.c',
+  'ephy-web-extension-proxy.c',
+  enums
+]
+
+libephyembed_deps = [
+  gio_dep,
+  glib_dep,
+  gtk_dep,
+  icu_uc_dep,
+  libgd_dep,
+  libsecret_dep,
+  libsoup_dep,
+  m_dep,
+  webkit2gtk_dep
+]
+
+if get_option('enable_https_everywhere')
+  libephyembed_deps += httpseverywhere_dep
+endif
+
+libephyembed_includes = include_directories(
+  '.',
+  '..',
+  '../lib',
+  '../lib/egg',
+  '../lib/history',
+  '../lib/widgets',
+  '../subprojects/libgd'
+)
+
+libephyembed = static_library('ephyembed',
+  libephyembed_sources,
+  dependencies: libephyembed_deps,
+  include_directories: libephyembed_includes,
+)
+
+ephyembed_dep = declare_dependency(
+  link_with: libephyembed,
+  include_directories: libephyembed_includes,
+  dependencies: libephyembed_deps
+)
diff --git a/embed/web-extension/ephy-uri-tester.c b/embed/web-extension/ephy-uri-tester.c
index 5415d51..4f23792 100644
--- a/embed/web-extension/ephy-uri-tester.c
+++ b/embed/web-extension/ephy-uri-tester.c
@@ -37,7 +37,7 @@
 #include <libsoup/soup.h>
 #include <string.h>
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
 #include <httpseverywhere.h>
 #endif
 
@@ -69,7 +69,7 @@ struct _EphyUriTester {
   GMainLoop *load_loop;
   int adblock_filters_to_load;
   gboolean adblock_loaded;
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
   gboolean https_everywhere_loaded;
 
   HTTPSEverywhereContext *https_everywhere_context;
@@ -538,7 +538,7 @@ ephy_uri_tester_adblock_loaded (EphyUriTester *tester)
 {
   if (g_atomic_int_dec_and_test (&tester->adblock_filters_to_load)) {
     tester->adblock_loaded = TRUE;
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
     if (tester->https_everywhere_loaded)
       g_main_loop_quit (tester->load_loop);
 #else
@@ -547,7 +547,7 @@ ephy_uri_tester_adblock_loaded (EphyUriTester *tester)
   }
 }
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
 static void
 ephy_uri_tester_https_everywhere_loaded (EphyUriTester *tester)
 {
@@ -634,7 +634,7 @@ ephy_uri_tester_rewrite_uri (EphyUriTester    *tester,
     return NULL;
   }
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
   if ((flags & EPHY_URI_TEST_HTTPS_EVERYWHERE) && tester->https_everywhere_context != NULL)
     return https_everywhere_context_rewrite (tester->https_everywhere_context, request_uri);
 #endif
@@ -642,7 +642,7 @@ ephy_uri_tester_rewrite_uri (EphyUriTester    *tester,
   return g_strdup (request_uri);
 }
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
 static void
 https_everywhere_context_init_cb (HTTPSEverywhereContext *context,
                                   GAsyncResult           *res,
@@ -723,7 +723,7 @@ ephy_uri_tester_load_sync (GTask         *task,
   g_main_context_push_thread_default (context);
   tester->load_loop = g_main_loop_new (context, FALSE);
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
   if (!tester->https_everywhere_loaded) {
     g_assert (tester->https_everywhere_context == NULL);
     tester->https_everywhere_context = https_everywhere_context_new ();
@@ -819,13 +819,13 @@ ephy_uri_tester_set_property (GObject      *object,
 static void
 ephy_uri_tester_dispose (GObject *object)
 {
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
   EphyUriTester *tester = EPHY_URI_TESTER (object);
 #endif
 
   LOG ("EphyUriTester disposing %p", object);
 
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
   g_clear_object (&tester->https_everywhere_context);
 #endif
 
@@ -932,7 +932,7 @@ ephy_uri_tester_load (EphyUriTester *tester)
     tester->adblock_loaded = TRUE;
 
   if (tester->adblock_loaded
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
       && tester->https_everywhere_loaded
 #endif
      )
diff --git a/embed/web-extension/ephy-web-extension.c b/embed/web-extension/ephy-web-extension.c
index 9133081..edde1f3 100644
--- a/embed/web-extension/ephy-web-extension.c
+++ b/embed/web-extension/ephy-web-extension.c
@@ -109,7 +109,7 @@ static gboolean
 should_use_https_everywhere (const char *request_uri,
                              const char *redirected_uri)
 {
-#ifdef HAVE_LIBHTTPSEVERYWHERE
+#if ENABLE_HTTPS_EVERYWHERE
   SoupURI *request_soup_uri;
   SoupURI *redirected_soup_uri;
   gboolean result = TRUE;
diff --git a/embed/web-extension/meson.build b/embed/web-extension/meson.build
new file mode 100644
index 0000000..f48fdf3
--- /dev/null
+++ b/embed/web-extension/meson.build
@@ -0,0 +1,25 @@
+web_extension_sources = [
+  'ephy-embed-form-auth.c',
+  'ephy-uri-tester.c',
+  'ephy-web-dom-utils.c',
+  'ephy-web-extension.c',
+  'ephy-web-extension-main.c',
+  'ephy-web-overview.c',
+  'ephy-web-overview-model.c'
+]
+
+web_extension_deps = [
+  ephymisc_dep,
+  webkit2gtk_web_extension_dep
+]
+
+if get_option('enable_https_everywhere')
+  web_extension_deps += httpseverywhere_dep
+endif
+
+shared_module('ephywebextension',
+  web_extension_sources,
+  dependencies: web_extension_deps,
+  install: true,
+  install_dir: webextensionsdir
+)
diff --git a/help/meson.build b/help/meson.build
new file mode 100644
index 0000000..ab54aa2
--- /dev/null
+++ b/help/meson.build
@@ -0,0 +1,46 @@
+help_files = [
+  'index.page',
+  'introduction.page',
+  'legal.xml',
+  'browse-local.page',
+  'browse-private.page',
+  'browse-tab.page',
+  'browse-web.page',
+  'browse-webapps.page',
+  'browse-webapps-del.page',
+  'cert.page',
+  'cookies.xml',
+  'data-cookies.page',
+  'data-passwords.page',
+  'history.page',
+  'history-delete.page',
+  'keyboard-shortcut.page',
+  'pref.page',
+  'pref-cookies.page',
+  'pref-css.page',
+  'pref-downloads.page',
+  'pref-do-not-track.page',
+  'pref-font.page',
+  'pref-passwords.page',
+  'prob-restore-closed-page.page',
+  'proxy.page'
+]
+
+help_media = [
+  'media/epiphany-3-12.png',
+  'media/private-browsing-3-12.png',
+  'media/web-browser.png'
+]
+
+help_linguas = [
+  'cs',
+  'de',
+  'el',
+  'es',
+  'fr',
+  'hu',
+  'ko',
+  'pt_BR',
+  'ru',
+  'sv'
+]
diff --git a/lib/ephy-lib-type-builtins.c.in b/lib/ephy-lib-type-builtins.c.in
new file mode 100644
index 0000000..83532e8
--- /dev/null
+++ b/lib/ephy-lib-type-builtins.c.in
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+
+#include <config.h>
+#include "ephy-lib-type-builtins.h"
+
+/*** END file-header ***/
+/*** BEGIN file-production ***/
+
+/* enumerations from "@basename@" */
+#include "@basename@"
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type(void) {
+    static volatile gsize g_define_type_id__volatile = 0;
+
+    if(g_once_init_enter(&g_define_type_id__volatile)) {
+        static const G@Type@Value values [] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+            { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+            { 0, NULL, NULL }
+        };
+
+        GType g_define_type_id =
+            g_@type@_register_static(g_intern_static_string("@EnumName@"), values);
+        g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+    }
+
+    return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/lib/ephy-lib-type-builtins.h.in b/lib/ephy-lib-type-builtins.h.in
new file mode 100644
index 0000000..f367945
--- /dev/null
+++ b/lib/ephy-lib-type-builtins.h.in
@@ -0,0 +1,22 @@
+/*** BEGIN file-header ***/
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@basename@" */
+/*** END file-production ***/
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+
+G_END_DECLS
+
+/*** END file-tail ***/
diff --git a/lib/ephy-profile-utils.c b/lib/ephy-profile-utils.c
index b6c5020..9bfd848 100644
--- a/lib/ephy-profile-utils.c
+++ b/lib/ephy-profile-utils.c
@@ -136,7 +136,7 @@ ephy_profile_utils_do_migration (const char *profile_directory, int test_to_run,
   argv[i++] = NULL;
 
   if (debug)
-    argv[0] = ABS_TOP_BUILD_DIR "/lib/"EPHY_PROFILE_MIGRATOR;
+    argv[0] = BUILD_ROOT "/src/" EPHY_PROFILE_MIGRATOR;
 
   ret = g_spawn_sync (NULL, (char **)argv, envp, G_SPAWN_SEARCH_PATH,
                       NULL, NULL, NULL, NULL,
@@ -146,7 +146,7 @@ ephy_profile_utils_do_migration (const char *profile_directory, int test_to_run,
   g_strfreev (envp);
 
   if (error) {
-    LOG ("Failed to run migrator: %s", error->message);
+    g_warning ("Failed to run migrator: %s", error->message);
     g_error_free (error);
   }
 
diff --git a/lib/history/ephy-history-service.c b/lib/history/ephy-history-service.c
index ca003fe..57eff46 100644
--- a/lib/history/ephy-history-service.c
+++ b/lib/history/ephy-history-service.c
@@ -23,7 +23,7 @@
 
 #include "ephy-history-service-private.h"
 #include "ephy-history-types.h"
-#include "ephy-history-type-builtins.h"
+#include "ephy-lib-type-builtins.h"
 #include "ephy-sqlite-connection.h"
 
 #include <errno.h>
diff --git a/lib/meson.build b/lib/meson.build
new file mode 100644
index 0000000..0fa1932
--- /dev/null
+++ b/lib/meson.build
@@ -0,0 +1,84 @@
+types_headers = [
+  'ephy-security-levels.h',
+  'ephy-sqlite-connection.h',
+  'history/ephy-history-types.h'
+]
+
+enums = gnome.mkenums('ephy-lib',
+  sources: types_headers,
+  h_template: 'ephy-lib-type-builtins.h.in',
+  c_template: 'ephy-lib-type-builtins.c.in'
+)
+
+libephymisc_sources = [
+  'egg/eggtreemultidnd.c',
+  'ephy-dbus-util.c',
+  'ephy-debug.c',
+  'ephy-dnd.c',
+  'ephy-favicon-helpers.c',
+  'ephy-file-helpers.c',
+  'ephy-filters-manager.c',
+  'ephy-form-auth-data.c',
+  'ephy-gui.c',
+  'ephy-langs.c',
+  'ephy-permissions-manager.c',
+  'ephy-profile-utils.c',
+  'ephy-search-engine-manager.c',
+  'ephy-security-levels.c',
+  'ephy-settings.c',
+  'ephy-signal-accumulator.c',
+  'ephy-smaps.c',
+  'ephy-snapshot-service.c',
+  'ephy-sqlite-connection.c',
+  'ephy-sqlite-statement.c',
+  'ephy-string.c',
+  'ephy-time-helpers.c',
+  'ephy-uri-helpers.c',
+  'ephy-uri-tester-shared.c',
+  'ephy-web-app-utils.c',
+  'ephy-zoom.c',
+  'history/ephy-history-service.c',
+  'history/ephy-history-service-hosts-table.c',
+  'history/ephy-history-service-urls-table.c',
+  'history/ephy-history-service-visits-table.c',
+  'history/ephy-history-types.c',
+  enums
+]
+
+libephymisc_deps = [
+  cairo_dep,
+  gdk_pixbuf_dep,
+  gio_dep,
+  gio_unix_dep,
+  glib_dep,
+  gnome_desktop_dep,
+  gtk_dep,
+  icu_uc_dep,
+  libsecret_dep,
+  libsoup_dep,
+  libxml_dep,
+  m_dep,
+  sqlite3_dep,
+  webkit2gtk_dep
+]
+
+libephymisc_includes = include_directories(
+  '.',
+  '..',
+  'egg',
+  'history'
+)
+
+libephymisc = static_library('ephymisc',
+  libephymisc_sources,
+  dependencies: libephymisc_deps,
+  include_directories: libephymisc_includes
+)
+
+ephymisc_dep = declare_dependency(
+  link_with: libephymisc,
+  include_directories: libephymisc_includes,
+  dependencies: libephymisc_deps
+)
+
+subdir('widgets')
diff --git a/lib/widgets/ephy-widgets-type-builtins.c.in b/lib/widgets/ephy-widgets-type-builtins.c.in
new file mode 100644
index 0000000..5d06fd8
--- /dev/null
+++ b/lib/widgets/ephy-widgets-type-builtins.c.in
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+
+#include <config.h>
+#include "ephy-widgets-type-builtins.h"
+
+/*** END file-header ***/
+/*** BEGIN file-production ***/
+
+/* enumerations from "@basename@" */
+#include "@basename@"
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type(void) {
+    static volatile gsize g_define_type_id__volatile = 0;
+
+    if(g_once_init_enter(&g_define_type_id__volatile)) {
+        static const G@Type@Value values [] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+            { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+            { 0, NULL, NULL }
+        };
+
+        GType g_define_type_id =
+            g_@type@_register_static(g_intern_static_string("@EnumName@"), values);
+        g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+    }
+
+    return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/lib/widgets/ephy-widgets-type-builtins.h.in b/lib/widgets/ephy-widgets-type-builtins.h.in
new file mode 100644
index 0000000..f367945
--- /dev/null
+++ b/lib/widgets/ephy-widgets-type-builtins.h.in
@@ -0,0 +1,22 @@
+/*** BEGIN file-header ***/
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@basename@" */
+/*** END file-production ***/
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+
+G_END_DECLS
+
+/*** END file-tail ***/
diff --git a/lib/widgets/meson.build b/lib/widgets/meson.build
new file mode 100644
index 0000000..2de9d3d
--- /dev/null
+++ b/lib/widgets/meson.build
@@ -0,0 +1,58 @@
+types_headers = [
+  'ephy-location-entry.h'
+]
+
+enums = gnome.mkenums('ephy-widgets',
+  sources: types_headers,
+  h_template: 'ephy-widgets-type-builtins.h.in',
+  c_template: 'ephy-widgets-type-builtins.c.in'
+)
+
+libephywidgets_sources = [
+  'ephy-certificate-dialog.c',
+  'ephy-downloads-popover.c',
+  'ephy-downloads-progress-icon.c',
+  'ephy-download-widget.c',
+  'ephy-file-chooser.c',
+  'ephy-location-entry.c',
+  'ephy-middle-clickable-button.c',
+  'ephy-notification.c',
+  'ephy-security-popover.c',
+  'ephy-title-box.c',
+  'ephy-title-widget.c',
+  'nautilus-floating-bar.c',
+  enums
+]
+
+libephywidgets_deps = [
+  gcr_dep,
+  gdk_dep,
+  gdk_pixbuf_dep,
+  gio_dep,
+  glib_dep,
+  gtk_dep,
+  libgd_dep,
+  libsoup_dep,
+  webkit2gtk_dep
+]
+
+libephywidgets_includes = include_directories(
+  '.',
+  '..',
+  '../egg',
+  '../history',
+  '../..',
+  '../../embed'
+)
+
+libephywidgets = static_library('ephywidgets',
+  libephywidgets_sources,
+  dependencies: libephywidgets_deps,
+  include_directories: libephywidgets_includes,
+)
+
+ephywidgets_dep = declare_dependency(
+  link_with: libephywidgets,
+  include_directories: libephywidgets_includes,
+  dependencies: libephywidgets_deps
+)
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..9cc338a
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,101 @@
+project('epiphany', 'c',
+  license: 'GPL3+',
+  version: '3.24.1',
+  meson_version: '>= 0.40.0',
+  default_options: ['c_std=gnu11']
+)
+
+gnome = import('gnome')
+i18n = import('i18n')
+
+prefix = get_option('prefix')
+datadir = join_paths(prefix, get_option('datadir'))
+libdir = join_paths(prefix, get_option('libdir'))
+libexecdir = join_paths(prefix, get_option('libexecdir'))
+localedir = join_paths(prefix, get_option('localedir'))
+pkgdatadir = join_paths(datadir, 'epiphany')
+pkglibdir = join_paths(libdir, 'epiphany')
+pkglibexecdir = join_paths(libexecdir, 'epiphany')
+webextensionsdir = join_paths(pkglibdir, 'web-extensions')
+
+conf = configuration_data()
+conf.set_quoted('BUILD_ROOT', meson.build_root())
+conf.set_quoted('DISTRIBUTOR_NAME', get_option('distributor_name'))
+conf.set_quoted('EPHY_WEB_EXTENSIONS_DIR', webextensionsdir)
+conf.set_quoted('GETTEXT_PACKAGE', meson.project_name())
+conf.set_quoted('ISO_CODES_PREFIX', get_option('iso_codes_prefix'))
+conf.set_quoted('LOCALEDIR', localedir)
+conf.set_quoted('PKGDATADIR', pkgdatadir)
+conf.set_quoted('PKGLIBEXECDIR', pkglibexecdir)
+conf.set_quoted('SOURCE_ROOT', meson.source_root())
+conf.set_quoted('VERSION', meson.project_version())
+
+conf.set10('ENABLE_NLS', true)
+conf.set10('ENABLE_FIREFOX_SYNC', get_option('enable_firefox_sync'))
+conf.set10('ENABLE_HTTPS_EVERYWHERE', get_option('enable_https_everywhere'))
+
+configure_file(
+  output: 'config.h',
+  configuration: conf
+)
+
+gvdb = subproject('gvdb')
+gvdb_dep = gvdb.get_variable('gvdb_dep')
+
+libgd = subproject('libgd',
+  default_options: [
+    'with-main-view=true',
+    'with-notification=true'
+  ]
+)
+libgd_dep = libgd.get_variable('libgd_dep')
+
+glib_requirement = '>= 2.46.0'
+gtk_requirement = '>= 3.22.0'
+webkitgtk_requirement = '>= 2.16.0'
+
+cairo_dep = dependency('cairo', version: '>= 1.2')
+gcr_dep = dependency('gcr-3', version: '>= 3.5.5')
+gdk_dep = dependency('gdk-3.0', version: gtk_requirement)
+gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0', version: '>= 2.36.5')
+gio_dep = dependency('gio-2.0', version: glib_requirement)
+gio_unix_dep = dependency('gio-unix-2.0', version: glib_requirement)
+glib_dep = dependency('glib-2.0', version: glib_requirement)
+gnome_desktop_dep = dependency('gnome-desktop-3.0', version: '>= 2.91.2')
+gtk_dep = dependency('gtk+-3.0', version: gtk_requirement)
+gtk_unix_print_dep = dependency('gtk+-unix-print-3.0', version: gtk_requirement)
+icu_uc_dep = dependency('icu-uc', version: '>= 4.6')
+iso_codes_dep = dependency('iso-codes', version: '>= 0.35')
+json_glib_dep = dependency('json-glib-1.0', version: '>= 1.2.0')
+libnotify_dep = dependency('libnotify', version: '>= 0.5.1')
+libsecret_dep = dependency('libsecret-1', version: '>= 0.14')
+libsoup_dep = dependency('libsoup-2.4', version: '>= 2.48.0')
+libxml_dep = dependency('libxml-2.0', version: '>= 2.6.12')
+libxslt_dep = dependency('libxslt', version: '>= 1.1.7')
+sqlite3_dep = dependency('sqlite3', version: '>= 3.0')
+webkit2gtk_dep = dependency('webkit2gtk-4.0', version: webkitgtk_requirement)
+webkit2gtk_web_extension_dep = dependency('webkit2gtk-web-extension-4.0', version: webkitgtk_requirement)
+
+cc = meson.get_compiler('c')
+m_dep = cc.find_library('m')
+
+if get_option('enable_firefox_sync')
+  nettle_requirement = '>= 3.2'
+  hogweed_dep = dependency('hogweed', version: nettle_requirement)
+  nettle_dep = dependency('nettle', version: nettle_requirement)
+  gmp_dep = cc.find_library('gmp')
+endif
+
+if get_option('enable_https_everywhere')
+  httpseverywhere_dep = dependency('httpseverywhere-0.2', version: '>= 0.2.2')
+endif
+
+subdir('data')
+subdir('help')
+subdir('po')
+subdir('lib')
+subdir('embed')
+subdir('src')
+subdir('tests')
+
+meson.add_install_script('post_install.py')
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 0000000..64efbdf
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,29 @@
+option('distributor_name',
+  type: 'string',
+  value: 'GNOME Web',
+  description: 'Distributor name displayed on process crash page'
+)
+
+option('enable_firefox_sync',
+  type: 'boolean',
+  value: false,
+  description: 'Enable experimental Firefox Sync support'
+)
+
+option('enable_https_everywhere',
+  type: 'boolean',
+  value: false,
+  description: 'Enable experimental HTTPS Everywhere support'
+)
+
+option('enable_unit_tests',
+  type: 'boolean',
+  value: true,
+  description: 'Enable unit tests'
+)
+
+option('iso_codes_prefix',
+  type: 'string',
+  value: '/usr',
+  description: 'Prefix for installed ISO codes'
+)
diff --git a/po/meson.build b/po/meson.build
new file mode 100644
index 0000000..e9b77d7
--- /dev/null
+++ b/po/meson.build
@@ -0,0 +1 @@
+i18n.gettext(meson.project_name(), preset: 'glib')
diff --git a/post_install.py b/post_install.py
new file mode 100644
index 0000000..c1faf09
--- /dev/null
+++ b/post_install.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python3
+
+import os
+import pathlib
+import subprocess
+
+prefix = pathlib.Path(os.environ.get('MESON_INSTALL_PREFIX', '/usr/local'))
+datadir = prefix / 'share'
+destdir = os.environ.get('DESTDIR', '')
+
+if not destdir:
+    print('Compiling gsettings schemas...')
+    subprocess.call(['glib-compile-schemas', str(datadir / 'glib-2.0' / 'schemas')])
+
+    print('Updating icon cache...')
+    subprocess.call(['gtk-update-icon-cache', '-qtf', str(datadir / 'icons' / 'hicolor')])
+
+    print('Updating desktop database...')
+    subprocess.call(['update-desktop-database', '-q', str(datadir / 'applications')])
diff --git a/release.sh b/release.sh
new file mode 100644
index 0000000..cc9962e
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+test -n "$srcdir" || srcdir=$1
+test -n "$srcdir" || srcdir=.
+
+cd $srcdir
+
+VERSION=$(git describe --abbrev=0)
+NAME="epiphany-$VERSION"
+
+echo "Updating submodules…"
+git submodule update --init
+
+echo "Creating git tree archive…"
+git archive --prefix="${NAME}/" --format=tar HEAD > epiphany.tar
+
+cd libgd
+
+git archive --prefix="${NAME}/libgd/" --format=tar HEAD > libgd.tar
+
+cd ..
+
+rm -f "${NAME}.tar"
+
+tar -Af "${NAME}.tar" epiphany.tar
+tar -Af "${NAME}.tar" libgd/libgd.tar
+
+rm -f epiphany.tar
+rm -f libgd/libgd.tar
+
+echo "Compressing archive…"
+xz -f "${NAME}.tar"
diff --git a/src/bookmarks/ephy-bookmark-properties-grid.c b/src/bookmarks/ephy-bookmark-properties-grid.c
index a7b5c7a..5b8d22c 100644
--- a/src/bookmarks/ephy-bookmark-properties-grid.c
+++ b/src/bookmarks/ephy-bookmark-properties-grid.c
@@ -32,7 +32,7 @@
 #include <libsoup/soup.h>
 #include <string.h>
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 #include "ephy-sync-service.h"
 #endif
 
@@ -245,14 +245,14 @@ ephy_bookmarks_properties_grid_actions_remove_bookmark (GSimpleAction *action,
                                                         GVariant      *value,
                                                         gpointer       user_data)
 {
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
   EphySyncService *service;
 #endif
   EphyBookmarkPropertiesGrid *self = user_data;
 
   g_assert (EPHY_IS_BOOKMARK_PROPERTIES_GRID (self));
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
   service = ephy_shell_get_sync_service (ephy_shell_get_default ());
   ephy_sync_service_delete_bookmark (service, self->bookmark, FALSE);
 #endif
diff --git a/src/bookmarks/ephy-bookmark.c b/src/bookmarks/ephy-bookmark.c
index ff0239b..adc5b25 100644
--- a/src/bookmarks/ephy-bookmark.c
+++ b/src/bookmarks/ephy-bookmark.c
@@ -26,7 +26,7 @@
 
 #include <string.h>
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 #include "ephy-sync-crypto.h"
 #include "ephy-sync-utils.h"
 #endif
@@ -204,7 +204,7 @@ ephy_bookmark_class_init (EphyBookmarkClass *klass)
 static void
 ephy_bookmark_init (EphyBookmark *self)
 {
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
   self->id = g_malloc0 (ID_LEN + 1);
   ephy_sync_crypto_random_hex_gen (NULL, ID_LEN, (guint8 *)self->id);
 #else
@@ -542,7 +542,7 @@ ephy_bookmark_tags_compare (const char *tag1, const char *tag2)
   return result;
 }
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 char *
 ephy_bookmark_to_bso (EphyBookmark *self)
 {
diff --git a/src/bookmarks/ephy-bookmark.h b/src/bookmarks/ephy-bookmark.h
index 1f49f04..6811c46 100644
--- a/src/bookmarks/ephy-bookmark.h
+++ b/src/bookmarks/ephy-bookmark.h
@@ -70,7 +70,7 @@ int                  ephy_bookmark_bookmarks_compare_func   (EphyBookmark *bookm
 int                  ephy_bookmark_tags_compare          (const char *tag1,
                                                           const char *tag2);
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 char                *ephy_bookmark_to_bso                (EphyBookmark *self);
 EphyBookmark        *ephy_bookmark_from_bso              (JsonObject *bso);
 #endif
diff --git a/src/ephy-main.c b/src/ephy-main.c
index 2d5f843..9c4687e 100644
--- a/src/ephy-main.c
+++ b/src/ephy-main.c
@@ -206,7 +206,7 @@ main (int   argc,
    */
   LIBXML_TEST_VERSION;
 
-  notify_init (PACKAGE);
+  notify_init ("epiphany");
 
   /* If we're given -remote arguments, translate them */
   if (argc >= 2 && strcmp (argv[1], "-remote") == 0) {
@@ -346,7 +346,7 @@ main (int   argc,
   if (!(private_instance && profile_directory == NULL) && incognito_mode == FALSE) {
     /* If the migration fails we don't really want to continue. */
     if (!ephy_profile_utils_do_migration ((const char *)profile_directory, -1, FALSE)) {
-      g_print ("Failed to run the migrator process, Web will now abort.");
+      g_print ("Failed to run the migrator process, Web will now abort.\n");
       exit (1);
     }
   }
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index decc08b..ace9ac1 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -47,7 +47,7 @@
 #include <gdk/gdkx.h>
 #include <gtk/gtk.h>
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 #include "ephy-notification.h"
 #endif
 
@@ -55,7 +55,7 @@ struct _EphyShell {
   EphyEmbedShell parent_instance;
 
   EphySession *session;
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
   EphySyncService *sync_service;
 #endif
   GList *windows;
@@ -312,7 +312,7 @@ download_started_cb (WebKitWebContext *web_context,
   g_object_unref (ephy_download);
 }
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 static void
 sync_tokens_load_finished_cb (EphySyncService *service,
                               GError          *error,
@@ -373,7 +373,7 @@ ephy_shell_startup (GApplication *application)
                               G_BINDING_SYNC_CREATE);
     }
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
     /* Create the sync service. */
     ephy_shell->sync_service = ephy_sync_service_new ();
     g_signal_connect (ephy_shell->sync_service,
@@ -647,7 +647,7 @@ ephy_shell_dispose (GObject *object)
   g_clear_pointer (&shell->history_dialog, gtk_widget_destroy);
   g_clear_object (&shell->prefs_dialog);
   g_clear_object (&shell->network_monitor);
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
   g_clear_object (&shell->sync_service);
 #endif
   g_clear_object (&shell->bookmarks_manager);
@@ -802,7 +802,7 @@ ephy_shell_get_session (EphyShell *shell)
   return shell->session;
 }
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 /**
  * ephy_shell_get_sync_service:
  * @shell: the #EphyShell
diff --git a/src/ephy-shell.h b/src/ephy-shell.h
index 9e86fa6..e81c074 100644
--- a/src/ephy-shell.h
+++ b/src/ephy-shell.h
@@ -32,7 +32,7 @@
 #include <glib-object.h>
 #include <glib.h>
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 #include "ephy-sync-service.h"
 #endif
 
@@ -105,7 +105,7 @@ GNetworkMonitor *ephy_shell_get_net_monitor              (EphyShell *shell);
 
 EphyBookmarksManager *ephy_shell_get_bookmarks_manager   (EphyShell *shell);
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 EphySyncService *ephy_shell_get_sync_service             (EphyShell *shell);
 #endif
 
diff --git a/src/ephy-type-builtins.c.in b/src/ephy-type-builtins.c.in
new file mode 100644
index 0000000..f5929d4
--- /dev/null
+++ b/src/ephy-type-builtins.c.in
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+
+#include <config.h>
+#include "ephy-type-builtins.h"
+
+/*** END file-header ***/
+/*** BEGIN file-production ***/
+
+/* enumerations from "@basename@" */
+#include "@basename@"
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type(void) {
+    static volatile gsize g_define_type_id__volatile = 0;
+
+    if(g_once_init_enter(&g_define_type_id__volatile)) {
+        static const G@Type@Value values [] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+            { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+            { 0, NULL, NULL }
+        };
+
+        GType g_define_type_id =
+            g_@type@_register_static(g_intern_static_string("@EnumName@"), values);
+        g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+    }
+
+    return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/src/ephy-type-builtins.h.in b/src/ephy-type-builtins.h.in
new file mode 100644
index 0000000..f367945
--- /dev/null
+++ b/src/ephy-type-builtins.h.in
@@ -0,0 +1,22 @@
+/*** BEGIN file-header ***/
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@basename@" */
+/*** END file-production ***/
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+
+G_END_DECLS
+
+/*** END file-tail ***/
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..ba5361e
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,134 @@
+types_headers = [
+  'bookmarks/ephy-bookmark-properties-grid.h',
+  'ephy-link.h',
+  'ephy-session.h',
+  'ephy-shell.h',
+  'ephy-window.h'
+]
+
+enums = gnome.mkenums('ephy',
+  sources: types_headers,
+  h_template: 'ephy-type-builtins.h.in',
+  c_template: 'ephy-type-builtins.c.in'
+)
+
+libephymain_sources = [
+  'bookmarks/ephy-add-bookmark-popover.c',
+  'bookmarks/ephy-bookmark.c',
+  'bookmarks/ephy-bookmark-properties-grid.c',
+  'bookmarks/ephy-bookmark-row.c',
+  'bookmarks/ephy-bookmarks-export.c',
+  'bookmarks/ephy-bookmarks-import.c',
+  'bookmarks/ephy-bookmarks-manager.c',
+  'bookmarks/ephy-bookmarks-popover.c',
+  'clear-data-dialog.c',
+  'cookies-dialog.c',
+  'ephy-action-helper.c',
+  'ephy-completion-model.c',
+  'ephy-encoding-dialog.c',
+  'ephy-encoding-row.c',
+  'ephy-header-bar.c',
+  'ephy-history-dialog.c',
+  'ephy-link.c',
+  'ephy-location-controller.c',
+  'ephy-lockdown.c',
+  'ephy-notebook.c',
+  'ephy-search-engine-dialog.c',
+  'ephy-session.c',
+  'ephy-shell.c',
+  'ephy-window.c',
+  'passwords-dialog.c',
+  'popup-commands.c',
+  'prefs-dialog.c',
+  'window-commands.c',
+  enums
+]
+
+libephymain_deps = [
+  ephyembed_dep,
+  ephymisc_dep,
+  ephywidgets_dep,
+  gvdb_dep,
+  json_glib_dep,
+  libnotify_dep
+]
+
+libephymain_includes = include_directories(
+  '.',
+  'bookmarks',
+  'sync'
+)
+
+if get_option('enable_firefox_sync')
+  libephymain_sources += [
+    'sync/ephy-sync-crypto.c',
+    'sync/ephy-sync-secret.c',
+    'sync/ephy-sync-service.c',
+    'sync/ephy-sync-utils.c'
+  ]
+
+  libephymain_deps += [
+    gmp_dep,
+    hogweed_dep,
+    nettle_dep
+  ]
+endif
+
+libephymain = static_library('ephymain',
+  libephymain_sources,
+  dependencies: libephymain_deps,
+  include_directories: libephymain_includes,
+)
+
+ephymain_dep = declare_dependency(
+  link_with: libephymain,
+  include_directories: libephymain_includes,
+  dependencies: libephymain_deps
+)
+
+
+executable('ephy-profile-migrator',
+  'profile-migrator/ephy-profile-migrator.c',
+  dependencies: ephymain_dep,
+  install: true,
+  install_dir: pkglibexecdir,
+)
+
+
+codegen = gnome.gdbus_codegen('ephy-shell-search-provider-generated',
+  'search-provider/org.gnome.ShellSearchProvider2.xml',
+  interface_prefix: 'org.gnome',
+  namespace: 'Ephy'
+)
+
+search_provider_sources = [
+  'search-provider/ephy-search-provider.c',
+  'search-provider/ephy-search-provider-main.c',
+  codegen
+]
+
+executable('epiphany-search-provider',
+  search_provider_sources,
+  dependencies: ephymain_dep,
+  install: true,
+  install_dir: pkglibexecdir
+)
+
+
+resource_files = files('resources/epiphany.gresource.xml')
+resources = gnome.compile_resources('epiphany-resources',
+    resource_files,
+    c_name: 'epiphany',
+    source_dir: 'resources'
+)
+
+epiphany_sources = [
+  'ephy-main.c',
+  resources
+]
+
+executable('epiphany',
+  epiphany_sources,
+  dependencies: ephymain_dep,
+  install: true
+)
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index 6151d70..c72f023 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -50,7 +50,7 @@
 #include <json-glib/json-glib.h>
 #include <string.h>
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 #include "ephy-sync-crypto.h"
 #include "ephy-sync-secret.h"
 #include "ephy-sync-service.h"
@@ -115,7 +115,7 @@ struct _PrefsDialog {
   GHashTable *iso_639_table;
   GHashTable *iso_3166_table;
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
   /* sync */
   GtkWidget *sync_authenticate_box;
   GtkWidget *sync_sign_in_box;
@@ -132,7 +132,7 @@ struct _PrefsDialog {
   GtkWidget *notebook;
 };
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 typedef struct {
   PrefsDialog *dialog;
   char        *email;
@@ -155,7 +155,7 @@ enum {
 
 G_DEFINE_TYPE (PrefsDialog, prefs_dialog, GTK_TYPE_DIALOG)
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 static FxACallbackData *
 fxa_callback_data_new (PrefsDialog *dialog,
                        const char  *email,
@@ -224,7 +224,7 @@ prefs_dialog_finalize (GObject *object)
   g_hash_table_destroy (dialog->iso_639_table);
   g_hash_table_destroy (dialog->iso_3166_table);
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
   if (dialog->fxa_web_view != NULL) {
     webkit_user_content_manager_unregister_script_message_handler (dialog->fxa_manager,
                                                                    "accountsCommandHandler");
@@ -241,7 +241,7 @@ prefs_dialog_finalize (GObject *object)
   G_OBJECT_CLASS (prefs_dialog_parent_class)->finalize (object);
 }
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 static void
 hide_fxa_iframe (PrefsDialog *dialog,
                  const char  *email)
@@ -638,7 +638,7 @@ prefs_dialog_class_init (PrefsDialogClass *klass)
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, lang_down_button);
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, enable_spell_checking_checkbutton);
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
   /* sync */
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_authenticate_box);
   gtk_widget_class_bind_template_child (widget_class, PrefsDialog, sync_sign_in_box);
@@ -1632,7 +1632,7 @@ setup_language_page (PrefsDialog *dialog)
   create_language_section (dialog);
 }
 
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
 static void
 setup_sync_page (PrefsDialog *dialog)
 {
@@ -1688,7 +1688,7 @@ prefs_dialog_init (PrefsDialog *dialog)
   setup_fonts_page (dialog);
   setup_stored_data_page (dialog);
   setup_language_page (dialog);
-#ifdef ENABLE_SYNC
+#if ENABLE_FIREFOX_SYNC
   if (mode != EPHY_EMBED_SHELL_MODE_APPLICATION) {
     setup_sync_page (dialog);
 
diff --git a/src/sync/ephy-sync-service.c b/src/sync/ephy-sync-service.c
index 13d2eff..99d512d 100644
--- a/src/sync/ephy-sync-service.c
+++ b/src/sync/ephy-sync-service.c
@@ -853,7 +853,7 @@ ephy_sync_service_start_sign_in (EphySyncService  *self,
                                  guint8           *tokenID,
                                  guint8           *reqHMACkey)
 {
-  JsonNode *node;
+  JsonNode *node = NULL;
   JsonObject *json;
   char *tokenID_hex;
   char *bundle = NULL;
diff --git a/subprojects/gvdb/README.epiphany b/subprojects/gvdb/README.epiphany
new file mode 100644
index 0000000..6c6a808
--- /dev/null
+++ b/subprojects/gvdb/README.epiphany
@@ -0,0 +1,3 @@
+GVariant Database
+
+Copied from https://git.gnome.org/browse/gvdb
diff --git a/subprojects/gvdb/gvdb-builder.c b/subprojects/gvdb/gvdb-builder.c
new file mode 100644
index 0000000..16a5283
--- /dev/null
+++ b/subprojects/gvdb/gvdb-builder.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "gvdb-builder.h"
+#include "gvdb-format.h"
+
+#include <glib.h>
+#include <fcntl.h>
+#if !defined(G_OS_WIN32) || !defined(_MSC_VER)
+#include <unistd.h>
+#endif
+#include <string.h>
+
+
+struct _GvdbItem
+{
+  gchar *key;
+  guint32 hash_value;
+  guint32_le assigned_index;
+  GvdbItem *parent;
+  GvdbItem *sibling;
+  GvdbItem *next;
+
+  /* one of:
+   * this:
+   */
+  GVariant *value;
+
+  /* this: */
+  GHashTable *table;
+
+  /* or this: */
+  GvdbItem *child;
+};
+
+static void
+gvdb_item_free (gpointer data)
+{
+  GvdbItem *item = data;
+
+  g_free (item->key);
+
+  if (item->value)
+    g_variant_unref (item->value);
+
+  if (item->table)
+    g_hash_table_unref (item->table);
+
+  g_slice_free (GvdbItem, item);
+}
+
+GHashTable *
+gvdb_hash_table_new (GHashTable  *parent,
+                     const gchar *name_in_parent)
+{
+  GHashTable *table;
+
+  table = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                 g_free, gvdb_item_free);
+
+  if (parent)
+    {
+      GvdbItem *item;
+
+      item = gvdb_hash_table_insert (parent, name_in_parent);
+      gvdb_item_set_hash_table (item, table);
+    }
+
+  return table;
+}
+
+static guint32
+djb_hash (const gchar *key)
+{
+  guint32 hash_value = 5381;
+
+  while (*key)
+    hash_value = hash_value * 33 + *(signed char *)key++;
+
+  return hash_value;
+}
+
+GvdbItem *
+gvdb_hash_table_insert (GHashTable  *table,
+                        const gchar *key)
+{
+  GvdbItem *item;
+
+  item = g_slice_new0 (GvdbItem);
+  item->key = g_strdup (key);
+  item->hash_value = djb_hash (key);
+
+  g_hash_table_insert (table, g_strdup (key), item);
+
+  return item;
+}
+
+void
+gvdb_hash_table_insert_string (GHashTable  *table,
+                               const gchar *key,
+                               const gchar *value)
+{
+  GvdbItem *item;
+
+  item = gvdb_hash_table_insert (table, key);
+  gvdb_item_set_value (item, g_variant_new_string (value));
+}
+
+void
+gvdb_item_set_value (GvdbItem *item,
+                     GVariant *value)
+{
+  g_return_if_fail (!item->value && !item->table && !item->child);
+
+  item->value = g_variant_ref_sink (value);
+}
+
+void
+gvdb_item_set_hash_table (GvdbItem   *item,
+                          GHashTable *table)
+{
+  g_return_if_fail (!item->value && !item->table && !item->child);
+
+  item->table = g_hash_table_ref (table);
+}
+
+void
+gvdb_item_set_parent (GvdbItem *item,
+                      GvdbItem *parent)
+{
+  GvdbItem **node;
+
+  g_return_if_fail (g_str_has_prefix (item->key, parent->key));
+  g_return_if_fail (!parent->value && !parent->table);
+  g_return_if_fail (!item->parent && !item->sibling);
+
+  for (node = &parent->child; *node; node = &(*node)->sibling)
+    if (strcmp ((*node)->key, item->key) > 0)
+      break;
+
+  item->parent = parent;
+  item->sibling = *node;
+  *node = item;
+}
+
+typedef struct
+{
+  GvdbItem **buckets;
+  gint n_buckets;
+} HashTable;
+
+static HashTable *
+hash_table_new (gint n_buckets)
+{
+  HashTable *table;
+
+  table = g_slice_new (HashTable);
+  table->buckets = g_new0 (GvdbItem *, n_buckets);
+  table->n_buckets = n_buckets;
+
+  return table;
+}
+
+static void
+hash_table_free (HashTable *table)
+{
+  g_free (table->buckets);
+
+  g_slice_free (HashTable, table);
+}
+
+static void
+hash_table_insert (gpointer key,
+                   gpointer value,
+                   gpointer data)
+{
+  guint32 hash_value, bucket;
+  HashTable *table = data;
+  GvdbItem *item = value;
+
+  hash_value = djb_hash (key);
+  bucket = hash_value % table->n_buckets;
+  item->next = table->buckets[bucket];
+  table->buckets[bucket] = item;
+}
+
+static guint32_le
+item_to_index (GvdbItem *item)
+{
+  if (item != NULL)
+    return item->assigned_index;
+
+  return guint32_to_le (-1u);
+}
+
+typedef struct
+{
+  GQueue *chunks;
+  guint64 offset;
+  gboolean byteswap;
+} FileBuilder;
+
+typedef struct
+{
+  gsize offset;
+  gsize size;
+  gpointer data;
+} FileChunk;
+
+static gpointer
+file_builder_allocate (FileBuilder         *fb,
+                       guint                alignment,
+                       gsize                size,
+                       struct gvdb_pointer *pointer)
+{
+  FileChunk *chunk;
+
+  if (size == 0)
+    return NULL;
+
+  fb->offset += (-fb->offset) & (alignment - 1);
+  chunk = g_slice_new (FileChunk);
+  chunk->offset = fb->offset;
+  chunk->size = size;
+  chunk->data = g_malloc (size);
+
+  pointer->start = guint32_to_le (fb->offset);
+  fb->offset += size;
+  pointer->end = guint32_to_le (fb->offset);
+
+  g_queue_push_tail (fb->chunks, chunk);
+
+  return chunk->data;
+}
+
+static void
+file_builder_add_value (FileBuilder         *fb,
+                        GVariant            *value,
+                        struct gvdb_pointer *pointer)
+{
+  GVariant *variant, *normal;
+  gpointer data;
+  gsize size;
+
+  if (fb->byteswap)
+    {
+      value = g_variant_byteswap (value);
+      variant = g_variant_new_variant (value);
+      g_variant_unref (value);
+    }
+  else
+    variant = g_variant_new_variant (value);
+
+  normal = g_variant_get_normal_form (variant);
+  g_variant_unref (variant);
+
+  size = g_variant_get_size (normal);
+  data = file_builder_allocate (fb, 8, size, pointer);
+  g_variant_store (normal, data);
+  g_variant_unref (normal);
+}
+
+static void
+file_builder_add_string (FileBuilder *fb,
+                         const gchar *string,
+                         guint32_le  *start,
+                         guint16_le  *size)
+{
+  FileChunk *chunk;
+  gsize length;
+
+  length = strlen (string);
+
+  chunk = g_slice_new (FileChunk);
+  chunk->offset = fb->offset;
+  chunk->size = length;
+  chunk->data = g_malloc (length);
+  memcpy (chunk->data, string, length);
+
+  *start = guint32_to_le (fb->offset);
+  *size = guint16_to_le (length);
+  fb->offset += length;
+
+  g_queue_push_tail (fb->chunks, chunk);
+}
+
+static void
+file_builder_allocate_for_hash (FileBuilder            *fb,
+                                gsize                   n_buckets,
+                                gsize                   n_items,
+                                guint                   bloom_shift,
+                                gsize                   n_bloom_words,
+                                guint32_le            **bloom_filter,
+                                guint32_le            **hash_buckets,
+                                struct gvdb_hash_item **hash_items,
+                                struct gvdb_pointer    *pointer)
+{
+  guint32_le bloom_hdr, table_hdr;
+  guchar *data;
+  gsize size;
+
+  g_assert (n_bloom_words < (1u << 27));
+
+  bloom_hdr = guint32_to_le (bloom_shift << 27 | n_bloom_words);
+  table_hdr = guint32_to_le (n_buckets);
+
+  size = sizeof bloom_hdr + sizeof table_hdr +
+         n_bloom_words * sizeof (guint32_le) +
+         n_buckets     * sizeof (guint32_le) +
+         n_items       * sizeof (struct gvdb_hash_item);
+
+  data = file_builder_allocate (fb, 4, size, pointer);
+
+#define chunk(s) (size -= (s), data += (s), data - (s))
+  memcpy (chunk (sizeof bloom_hdr), &bloom_hdr, sizeof bloom_hdr);
+  memcpy (chunk (sizeof table_hdr), &table_hdr, sizeof table_hdr);
+  *bloom_filter = (guint32_le *) chunk (n_bloom_words * sizeof (guint32_le));
+  *hash_buckets = (guint32_le *) chunk (n_buckets * sizeof (guint32_le));
+  *hash_items = (struct gvdb_hash_item *) chunk (n_items *
+                  sizeof (struct gvdb_hash_item));
+  g_assert (size == 0);
+#undef chunk
+
+  memset (*bloom_filter, 0, n_bloom_words * sizeof (guint32_le));
+
+  /* NOTE - the code to actually fill in the bloom filter here is missing.
+   * Patches welcome!
+   *
+   * http://en.wikipedia.org/wiki/Bloom_filter
+   * http://0pointer.de/blog/projects/bloom.html
+   */
+}
+
+static void
+file_builder_add_hash (FileBuilder         *fb,
+                       GHashTable          *table,
+                       struct gvdb_pointer *pointer)
+{
+  guint32_le *buckets, *bloom_filter;
+  struct gvdb_hash_item *items;
+  HashTable *mytable;
+  GvdbItem *item;
+  guint32 index;
+  gint bucket;
+
+  mytable = hash_table_new (g_hash_table_size (table));
+  g_hash_table_foreach (table, hash_table_insert, mytable);
+  index = 0;
+
+  for (bucket = 0; bucket < mytable->n_buckets; bucket++)
+    for (item = mytable->buckets[bucket]; item; item = item->next)
+      item->assigned_index = guint32_to_le (index++);
+
+  file_builder_allocate_for_hash (fb, mytable->n_buckets, index, 5, 0,
+                                  &bloom_filter, &buckets, &items, pointer);
+
+  index = 0;
+  for (bucket = 0; bucket < mytable->n_buckets; bucket++)
+    {
+      buckets[bucket] = guint32_to_le (index);
+
+      for (item = mytable->buckets[bucket]; item; item = item->next)
+        {
+          struct gvdb_hash_item *entry = items++;
+          const gchar *basename;
+
+          g_assert (index == guint32_from_le (item->assigned_index));
+          entry->hash_value = guint32_to_le (item->hash_value);
+          entry->parent = item_to_index (item->parent);
+          entry->unused = 0;
+
+          if (item->parent != NULL)
+            basename = item->key + strlen (item->parent->key);
+          else
+            basename = item->key;
+
+          file_builder_add_string (fb, basename,
+                                   &entry->key_start,
+                                   &entry->key_size);
+
+          if (item->value != NULL)
+            {
+              g_assert (item->child == NULL && item->table == NULL);
+
+              file_builder_add_value (fb, item->value, &entry->value.pointer);
+              entry->type = 'v';
+            }
+
+          if (item->child != NULL)
+            {
+              guint32 children = 0, i = 0;
+              guint32_le *offsets;
+              GvdbItem *child;
+
+              g_assert (item->table == NULL);
+
+              for (child = item->child; child; child = child->sibling)
+                children++;
+
+              offsets = file_builder_allocate (fb, 4, 4 * children,
+                                               &entry->value.pointer);
+              entry->type = 'L';
+
+              for (child = item->child; child; child = child->sibling)
+                offsets[i++] = child->assigned_index;
+
+              g_assert (children == i);
+            }
+
+          if (item->table != NULL)
+            {
+              entry->type = 'H';
+              file_builder_add_hash (fb, item->table, &entry->value.pointer);
+            }
+
+          index++;
+        }
+    }
+
+  hash_table_free (mytable);
+}
+
+static FileBuilder *
+file_builder_new (gboolean byteswap)
+{
+  FileBuilder *builder;
+
+  builder = g_slice_new (FileBuilder);
+  builder->chunks = g_queue_new ();
+  builder->offset = sizeof (struct gvdb_header);
+  builder->byteswap = byteswap;
+
+  return builder;
+}
+
+static GString *
+file_builder_serialise (FileBuilder          *fb,
+                        struct gvdb_pointer   root)
+{
+  struct gvdb_header header = { { 0, }, };
+  GString *result;
+
+  if (fb->byteswap)
+    {
+      header.signature[0] = GVDB_SWAPPED_SIGNATURE0;
+      header.signature[1] = GVDB_SWAPPED_SIGNATURE1;
+    }
+  else
+    {
+      header.signature[0] = GVDB_SIGNATURE0;
+      header.signature[1] = GVDB_SIGNATURE1;
+    }
+
+  result = g_string_new (NULL);
+
+  header.root = root;
+  g_string_append_len (result, (gpointer) &header, sizeof header);
+
+  while (!g_queue_is_empty (fb->chunks))
+    {
+      FileChunk *chunk = g_queue_pop_head (fb->chunks);
+
+      if (result->len != chunk->offset)
+        {
+          gchar zero[8] = { 0, };
+
+          g_assert (chunk->offset > result->len);
+          g_assert (chunk->offset - result->len < 8);
+
+          g_string_append_len (result, zero, chunk->offset - result->len);
+          g_assert (result->len == chunk->offset);
+        }
+
+      g_string_append_len (result, chunk->data, chunk->size);
+      g_free (chunk->data);
+
+      g_slice_free (FileChunk, chunk);
+    }
+
+  g_queue_free (fb->chunks);
+  g_slice_free (FileBuilder, fb);
+
+  return result;
+}
+
+gboolean
+gvdb_table_write_contents (GHashTable   *table,
+                           const gchar  *filename,
+                           gboolean      byteswap,
+                           GError      **error)
+{
+  struct gvdb_pointer root;
+  gboolean status;
+  FileBuilder *fb;
+  GString *str;
+
+  fb = file_builder_new (byteswap);
+  file_builder_add_hash (fb, table, &root);
+  str = file_builder_serialise (fb, root);
+
+  status = g_file_set_contents (filename, str->str, str->len, error);
+  g_string_free (str, TRUE);
+
+  return status;
+}
diff --git a/subprojects/gvdb/gvdb-builder.h b/subprojects/gvdb/gvdb-builder.h
new file mode 100644
index 0000000..797626e
--- /dev/null
+++ b/subprojects/gvdb/gvdb-builder.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __gvdb_builder_h__
+#define __gvdb_builder_h__
+
+#include <gio/gio.h>
+
+typedef struct _GvdbItem GvdbItem;
+
+G_GNUC_INTERNAL
+GHashTable *            gvdb_hash_table_new                             (GHashTable    *parent,
+                                                                         const gchar   *key);
+
+G_GNUC_INTERNAL
+GvdbItem *              gvdb_hash_table_insert                          (GHashTable    *table,
+                                                                         const gchar   *key);
+G_GNUC_INTERNAL
+void                    gvdb_hash_table_insert_string                   (GHashTable    *table,
+                                                                         const gchar   *key,
+                                                                         const gchar   *value);
+
+G_GNUC_INTERNAL
+void                    gvdb_item_set_value                             (GvdbItem      *item,
+                                                                         GVariant      *value);
+G_GNUC_INTERNAL
+void                    gvdb_item_set_hash_table                        (GvdbItem      *item,
+                                                                         GHashTable    *table);
+G_GNUC_INTERNAL
+void                    gvdb_item_set_parent                            (GvdbItem      *item,
+                                                                         GvdbItem      *parent);
+
+G_GNUC_INTERNAL
+gboolean                gvdb_table_write_contents                       (GHashTable     *table,
+                                                                         const gchar    *filename,
+                                                                         gboolean        byteswap,
+                                                                         GError        **error);
+
+#endif /* __gvdb_builder_h__ */
diff --git a/subprojects/gvdb/gvdb-format.h b/subprojects/gvdb/gvdb-format.h
new file mode 100644
index 0000000..886aa56
--- /dev/null
+++ b/subprojects/gvdb/gvdb-format.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __gvdb_format_h__
+#define __gvdb_format_h__
+
+#include <glib.h>
+
+typedef struct { guint16 value; } guint16_le;
+typedef struct { guint32 value; } guint32_le;
+
+struct gvdb_pointer {
+  guint32_le start;
+  guint32_le end;
+};
+
+struct gvdb_hash_header {
+  guint32_le n_bloom_words;
+  guint32_le n_buckets;
+};
+
+struct gvdb_hash_item {
+  guint32_le hash_value;
+  guint32_le parent;
+
+  guint32_le key_start;
+  guint16_le key_size;
+  gchar type;
+  gchar unused;
+
+  union
+  {
+    struct gvdb_pointer pointer;
+    gchar direct[8];
+  } value;
+};
+
+struct gvdb_header {
+  guint32 signature[2];
+  guint32_le version;
+  guint32_le options;
+
+  struct gvdb_pointer root;
+};
+
+static inline guint32_le guint32_to_le (guint32 value) {
+  guint32_le result = { GUINT32_TO_LE (value) };
+  return result;
+}
+
+static inline guint32 guint32_from_le (guint32_le value) {
+  return GUINT32_FROM_LE (value.value);
+}
+
+static inline guint16_le guint16_to_le (guint16 value) {
+  guint16_le result = { GUINT16_TO_LE (value) };
+  return result;
+}
+
+static inline guint16 guint16_from_le (guint16_le value) {
+  return GUINT16_FROM_LE (value.value);
+}
+
+#define GVDB_SIGNATURE0 1918981703
+#define GVDB_SIGNATURE1 1953390953
+#define GVDB_SWAPPED_SIGNATURE0 GUINT32_SWAP_LE_BE (GVDB_SIGNATURE0)
+#define GVDB_SWAPPED_SIGNATURE1 GUINT32_SWAP_LE_BE (GVDB_SIGNATURE1)
+
+#endif /* __gvdb_format_h__ */
diff --git a/subprojects/gvdb/gvdb-reader.c b/subprojects/gvdb/gvdb-reader.c
new file mode 100644
index 0000000..8ccfc8f
--- /dev/null
+++ b/subprojects/gvdb/gvdb-reader.c
@@ -0,0 +1,720 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "gvdb-reader.h"
+#include "gvdb-format.h"
+
+#include <string.h>
+
+struct _GvdbTable {
+  GBytes *bytes;
+
+  const gchar *data;
+  gsize size;
+
+  gboolean byteswapped;
+  gboolean trusted;
+
+  const guint32_le *bloom_words;
+  guint32 n_bloom_words;
+  guint bloom_shift;
+
+  const guint32_le *hash_buckets;
+  guint32 n_buckets;
+
+  struct gvdb_hash_item *hash_items;
+  guint32 n_hash_items;
+};
+
+static const gchar *
+gvdb_table_item_get_key (GvdbTable                   *file,
+                         const struct gvdb_hash_item *item,
+                         gsize                       *size)
+{
+  guint32 start, end;
+
+  start = guint32_from_le (item->key_start);
+  *size = guint16_from_le (item->key_size);
+  end = start + *size;
+
+  if G_UNLIKELY (start > end || end > file->size)
+    return NULL;
+
+  return file->data + start;
+}
+
+static gconstpointer
+gvdb_table_dereference (GvdbTable                 *file,
+                        const struct gvdb_pointer *pointer,
+                        gint                       alignment,
+                        gsize                     *size)
+{
+  guint32 start, end;
+
+  start = guint32_from_le (pointer->start);
+  end = guint32_from_le (pointer->end);
+
+  if G_UNLIKELY (start > end || end > file->size || start & (alignment - 1))
+    return NULL;
+
+  *size = end - start;
+
+  return file->data + start;
+}
+
+static void
+gvdb_table_setup_root (GvdbTable                 *file,
+                       const struct gvdb_pointer *pointer)
+{
+  const struct gvdb_hash_header *header;
+  guint32 n_bloom_words;
+  guint32 n_buckets;
+  gsize size;
+
+  header = gvdb_table_dereference (file, pointer, 4, &size);
+
+  if G_UNLIKELY (header == NULL || size < sizeof *header)
+    return;
+
+  size -= sizeof *header;
+
+  n_bloom_words = guint32_from_le (header->n_bloom_words);
+  n_buckets = guint32_from_le (header->n_buckets);
+  n_bloom_words &= (1u << 27) - 1;
+
+  if G_UNLIKELY (n_bloom_words * sizeof (guint32_le) > size)
+    return;
+
+  file->bloom_words = (gpointer) (header + 1);
+  size -= n_bloom_words * sizeof (guint32_le);
+  file->n_bloom_words = n_bloom_words;
+
+  if G_UNLIKELY (n_buckets > G_MAXUINT / sizeof (guint32_le) ||
+                 n_buckets * sizeof (guint32_le) > size)
+    return;
+
+  file->hash_buckets = file->bloom_words + file->n_bloom_words;
+  size -= n_buckets * sizeof (guint32_le);
+  file->n_buckets = n_buckets;
+
+  if G_UNLIKELY (size % sizeof (struct gvdb_hash_item))
+    return;
+
+  file->hash_items = (gpointer) (file->hash_buckets + n_buckets);
+  file->n_hash_items = size / sizeof (struct gvdb_hash_item);
+}
+
+/**
+ * gvdb_table_new_from_bytes:
+ * @bytes: the #GBytes with the data
+ * @trusted: if the contents of @bytes are trusted
+ * @error: %NULL, or a pointer to a %NULL #GError
+ * @returns: a new #GvdbTable
+ *
+ * Creates a new #GvdbTable from the contents of @bytes.
+ *
+ * This call can fail if the header contained in @bytes is invalid.
+ *
+ * You should call gvdb_table_free() on the return result when you no
+ * longer require it.
+ **/
+GvdbTable *
+gvdb_table_new_from_bytes (GBytes    *bytes,
+                           gboolean   trusted,
+                           GError   **error)
+{
+  const struct gvdb_header *header;
+  GvdbTable *file;
+
+  file = g_slice_new0 (GvdbTable);
+  file->bytes = g_bytes_ref (bytes);
+  file->data = g_bytes_get_data (bytes, &file->size);
+  file->trusted = trusted;
+
+  if (file->size < sizeof (struct gvdb_header))
+    goto invalid;
+
+  header = (gpointer) file->data;
+
+  if (header->signature[0] == GVDB_SIGNATURE0 &&
+      header->signature[1] == GVDB_SIGNATURE1 &&
+      guint32_from_le (header->version) == 0)
+    file->byteswapped = FALSE;
+
+  else if (header->signature[0] == GVDB_SWAPPED_SIGNATURE0 &&
+           header->signature[1] == GVDB_SWAPPED_SIGNATURE1 &&
+           guint32_from_le (header->version) == 0)
+    file->byteswapped = TRUE;
+
+  else
+    goto invalid;
+
+  gvdb_table_setup_root (file, &header->root);
+
+  return file;
+
+invalid:
+  g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, "invalid gvdb header");
+
+  g_bytes_unref (file->bytes);
+
+  g_slice_free (GvdbTable, file);
+
+  return NULL;
+}
+
+/**
+ * gvdb_table_new:
+ * @filename: a filename
+ * @trusted: if the contents of @bytes are trusted
+ * @error: %NULL, or a pointer to a %NULL #GError
+ * @returns: a new #GvdbTable
+ *
+ * Creates a new #GvdbTable using the #GMappedFile for @filename as the
+ * #GBytes.
+ **/
+GvdbTable *
+gvdb_table_new (const gchar  *filename,
+                gboolean      trusted,
+                GError      **error)
+{
+  GMappedFile *mapped;
+  GvdbTable *table;
+  GBytes *bytes;
+
+  mapped = g_mapped_file_new (filename, FALSE, error);
+  if (!mapped)
+    return NULL;
+
+  bytes = g_mapped_file_get_bytes (mapped);
+  table = gvdb_table_new_from_bytes (bytes, trusted, error);
+  g_mapped_file_unref (mapped);
+  g_bytes_unref (bytes);
+
+  g_prefix_error (error, "%s: ", filename);
+
+  return table;
+}
+
+static gboolean
+gvdb_table_bloom_filter (GvdbTable *file,
+                          guint32    hash_value)
+{
+  guint32 word, mask;
+
+  if (file->n_bloom_words == 0)
+    return TRUE;
+
+  word = (hash_value / 32) % file->n_bloom_words;
+  mask = 1 << (hash_value & 31);
+  mask |= 1 << ((hash_value >> file->bloom_shift) & 31);
+
+  return (guint32_from_le (file->bloom_words[word]) & mask) == mask;
+}
+
+static gboolean
+gvdb_table_check_name (GvdbTable             *file,
+                       struct gvdb_hash_item *item,
+                       const gchar           *key,
+                       guint                  key_length)
+{
+  const gchar *this_key;
+  gsize this_size;
+  guint32 parent;
+
+  this_key = gvdb_table_item_get_key (file, item, &this_size);
+
+  if G_UNLIKELY (this_key == NULL || this_size > key_length)
+    return FALSE;
+
+  key_length -= this_size;
+
+  if G_UNLIKELY (memcmp (this_key, key + key_length, this_size) != 0)
+    return FALSE;
+
+  parent = guint32_from_le (item->parent);
+  if (key_length == 0 && parent == 0xffffffffu)
+    return TRUE;
+
+  if G_LIKELY (parent < file->n_hash_items && this_size > 0)
+    return gvdb_table_check_name (file,
+                                   &file->hash_items[parent],
+                                   key, key_length);
+
+  return FALSE;
+}
+
+static const struct gvdb_hash_item *
+gvdb_table_lookup (GvdbTable   *file,
+                   const gchar *key,
+                   gchar        type)
+{
+  guint32 hash_value = 5381;
+  guint key_length;
+  guint32 bucket;
+  guint32 lastno;
+  guint32 itemno;
+
+  if G_UNLIKELY (file->n_buckets == 0 || file->n_hash_items == 0)
+    return NULL;
+
+  for (key_length = 0; key[key_length]; key_length++)
+    hash_value = (hash_value * 33) + ((signed char *) key)[key_length];
+
+  if (!gvdb_table_bloom_filter (file, hash_value))
+    return NULL;
+
+  bucket = hash_value % file->n_buckets;
+  itemno = guint32_from_le (file->hash_buckets[bucket]);
+
+  if (bucket == file->n_buckets - 1 ||
+      (lastno = guint32_from_le(file->hash_buckets[bucket + 1])) > file->n_hash_items)
+    lastno = file->n_hash_items;
+
+  while G_LIKELY (itemno < lastno)
+    {
+      struct gvdb_hash_item *item = &file->hash_items[itemno];
+
+      if (hash_value == guint32_from_le (item->hash_value))
+        if G_LIKELY (gvdb_table_check_name (file, item, key, key_length))
+          if G_LIKELY (item->type == type)
+            return item;
+
+      itemno++;
+    }
+
+  return NULL;
+}
+
+static gboolean
+gvdb_table_list_from_item (GvdbTable                    *table,
+                           const struct gvdb_hash_item  *item,
+                           const guint32_le            **list,
+                           guint                        *length)
+{
+  gsize size;
+
+  *list = gvdb_table_dereference (table, &item->value.pointer, 4, &size);
+
+  if G_LIKELY (*list == NULL || size % 4)
+    return FALSE;
+
+  *length = size / 4;
+
+  return TRUE;
+}
+
+/**
+ * gvdb_table_get_names:
+ * @table: a #GvdbTable
+ * @length: the number of items returned, or %NULL
+ *
+ * Gets a list of all names contained in @table.
+ *
+ * No call to gvdb_table_get_table(), gvdb_table_list() or
+ * gvdb_table_get_value() will succeed unless it is for one of the
+ * names returned by this function.
+ *
+ * Note that some names that are returned may still fail for all of the
+ * above calls in the case of the corrupted file.  Note also that the
+ * returned strings may not be utf8.
+ *
+ * Returns: a %NULL-terminated list of strings, of length @length
+ **/
+gchar **
+gvdb_table_get_names (GvdbTable *table,
+                      gint      *length)
+{
+  gchar **names;
+  gint n_names;
+  gint filled;
+  gint total;
+  gint i;
+
+  /* We generally proceed by iterating over the list of items in the
+   * hash table (in order of appearance) recording them into an array.
+   *
+   * Each item has a parent item (except root items).  The parent item
+   * forms part of the name of the item.  We could go fetching the
+   * parent item chain at the point that we encounter each item but then
+   * we would need to implement some sort of recursion along with checks
+   * for self-referential items.
+   *
+   * Instead, we do a number of passes.  Each pass will build up one
+   * level of names (starting from the root).  We continue to do passes
+   * until no more items are left.  The first pass will only add root
+   * items and each further pass will only add items whose direct parent
+   * is an item added in the immediately previous pass.  It's also
+   * possible that items get filled if they follow their parent within a
+   * particular pass.
+   *
+   * At most we will have a number of passes equal to the depth of the
+   * tree.  Self-referential items will never be filled in (since their
+   * parent will have never been filled in).  We continue until we have
+   * a pass that fills in no additional items.
+   *
+   * This takes an O(n) algorithm and turns it into O(n*m) where m is
+   * the depth of the tree, but in all sane cases the tree won't be very
+   * deep and the constant factor of this algorithm is lower (and the
+   * complexity of coding it, as well).
+   */
+
+  n_names = table->n_hash_items;
+  names = g_new0 (gchar *, n_names + 1);
+
+  /* 'names' starts out all-NULL.  On each pass we record the number
+   * of items changed from NULL to non-NULL in 'filled' so we know if we
+   * should repeat the loop.  'total' counts the total number of items
+   * filled.  If 'total' ends up equal to 'n_names' then we know that
+   * 'names' has been completely filled.
+   */
+
+  total = 0;
+  do
+    {
+      /* Loop until we have filled no more entries */
+      filled = 0;
+
+      for (i = 0; i < n_names; i++)
+        {
+          const struct gvdb_hash_item *item = &table->hash_items[i];
+          const gchar *name;
+          gsize name_length;
+          guint32 parent;
+
+          /* already got it on a previous pass */
+          if (names[i] != NULL)
+            continue;
+
+          parent = guint32_from_le (item->parent);
+
+          if (parent == 0xffffffffu)
+            {
+              /* it's a root item */
+              name = gvdb_table_item_get_key (table, item, &name_length);
+
+              if (name != NULL)
+                {
+                  names[i] = g_strndup (name, name_length);
+                  filled++;
+                }
+            }
+
+          else if (parent < n_names && names[parent] != NULL)
+            {
+              /* It's a non-root item whose parent was filled in already.
+               *
+               * Calculate the name of this item by combining it with
+               * its parent name.
+               */
+              name = gvdb_table_item_get_key (table, item, &name_length);
+
+              if (name != NULL)
+                {
+                  const gchar *parent_name = names[parent];
+                  gsize parent_length;
+                  gchar *fullname;
+
+                  parent_length = strlen (parent_name);
+                  fullname = g_malloc (parent_length + name_length + 1);
+                  memcpy (fullname, parent_name, parent_length);
+                  memcpy (fullname + parent_length, name, name_length);
+                  fullname[parent_length + name_length] = '\0';
+                  names[i] = fullname;
+                  filled++;
+                }
+            }
+        }
+
+      total += filled;
+    }
+  while (filled && total < n_names);
+
+  /* If the table was corrupted then 'names' may have holes in it.
+   * Collapse those.
+   */
+  if G_UNLIKELY (total != n_names)
+    {
+      GPtrArray *fixed_names;
+
+      fixed_names = g_ptr_array_new ();
+      for (i = 0; i < n_names; i++)
+        if (names[i] != NULL)
+          g_ptr_array_add (fixed_names, names[i]);
+
+      g_free (names);
+      n_names = fixed_names->len;
+      g_ptr_array_add (fixed_names, NULL);
+      names = (gchar **) g_ptr_array_free (fixed_names, FALSE);
+    }
+
+  if (length)
+    *length = n_names;
+
+  return names;
+}
+
+/**
+ * gvdb_table_list:
+ * @file: a #GvdbTable
+ * @key: a string
+ * @returns: a %NULL-terminated string array
+ *
+ * List all of the keys that appear below @key.  The nesting of keys
+ * within the hash file is defined by the program that created the hash
+ * file.  One thing is constant: each item in the returned array can be
+ * concatenated to @key to obtain the full name of that key.
+ *
+ * It is not possible to tell from this function if a given key is
+ * itself a path, a value, or another hash table; you are expected to
+ * know this for yourself.
+ *
+ * You should call g_strfreev() on the return result when you no longer
+ * require it.
+ **/
+gchar **
+gvdb_table_list (GvdbTable   *file,
+                 const gchar *key)
+{
+  const struct gvdb_hash_item *item;
+  const guint32_le *list;
+  gchar **strv;
+  guint length;
+  guint i;
+
+  if ((item = gvdb_table_lookup (file, key, 'L')) == NULL)
+    return NULL;
+
+  if (!gvdb_table_list_from_item (file, item, &list, &length))
+    return NULL;
+
+  strv = g_new (gchar *, length + 1);
+  for (i = 0; i < length; i++)
+    {
+      guint32 itemno = guint32_from_le (list[i]);
+
+      if (itemno < file->n_hash_items)
+        {
+          const struct gvdb_hash_item *item;
+          const gchar *string;
+          gsize strsize;
+
+          item = file->hash_items + itemno;
+
+          string = gvdb_table_item_get_key (file, item, &strsize);
+
+          if (string != NULL)
+            strv[i] = g_strndup (string, strsize);
+          else
+            strv[i] = g_malloc0 (1);
+        }
+      else
+        strv[i] = g_malloc0 (1);
+    }
+
+  strv[i] = NULL;
+
+  return strv;
+}
+
+/**
+ * gvdb_table_has_value:
+ * @file: a #GvdbTable
+ * @key: a string
+ * @returns: %TRUE if @key is in the table
+ *
+ * Checks for a value named @key in @file.
+ *
+ * Note: this function does not consider non-value nodes (other hash
+ * tables, for example).
+ **/
+gboolean
+gvdb_table_has_value (GvdbTable    *file,
+                      const gchar  *key)
+{
+  static const struct gvdb_hash_item *item;
+  gsize size;
+
+  item = gvdb_table_lookup (file, key, 'v');
+
+  if (item == NULL)
+    return FALSE;
+
+  return gvdb_table_dereference (file, &item->value.pointer, 8, &size) != NULL;
+}
+
+static GVariant *
+gvdb_table_value_from_item (GvdbTable                   *table,
+                            const struct gvdb_hash_item *item)
+{
+  GVariant *variant, *value;
+  gconstpointer data;
+  GBytes *bytes;
+  gsize size;
+
+  data = gvdb_table_dereference (table, &item->value.pointer, 8, &size);
+
+  if G_UNLIKELY (data == NULL)
+    return NULL;
+
+  bytes = g_bytes_new_from_bytes (table->bytes, ((gchar *) data) - table->data, size);
+  variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT, bytes, table->trusted);
+  value = g_variant_get_variant (variant);
+  g_variant_unref (variant);
+  g_bytes_unref (bytes);
+
+  return value;
+}
+
+/**
+ * gvdb_table_get_value:
+ * @file: a #GvdbTable
+ * @key: a string
+ * @returns: a #GVariant, or %NULL
+ *
+ * Looks up a value named @key in @file.
+ *
+ * If the value is not found then %NULL is returned.  Otherwise, a new
+ * #GVariant instance is returned.  The #GVariant does not depend on the
+ * continued existence of @file.
+ *
+ * You should call g_variant_unref() on the return result when you no
+ * longer require it.
+ **/
+GVariant *
+gvdb_table_get_value (GvdbTable    *file,
+                      const gchar  *key)
+{
+  const struct gvdb_hash_item *item;
+  GVariant *value;
+
+  if ((item = gvdb_table_lookup (file, key, 'v')) == NULL)
+    return NULL;
+
+  value = gvdb_table_value_from_item (file, item);
+
+  if (value && file->byteswapped)
+    {
+      GVariant *tmp;
+
+      tmp = g_variant_byteswap (value);
+      g_variant_unref (value);
+      value = tmp;
+    }
+
+  return value;
+}
+
+/**
+ * gvdb_table_get_raw_value:
+ * @table: a #GvdbTable
+ * @key: a string
+ * @returns: a #GVariant, or %NULL
+ *
+ * Looks up a value named @key in @file.
+ *
+ * This call is equivalent to gvdb_table_get_value() except that it
+ * never byteswaps the value.
+ **/
+GVariant *
+gvdb_table_get_raw_value (GvdbTable   *table,
+                          const gchar *key)
+{
+  const struct gvdb_hash_item *item;
+
+  if ((item = gvdb_table_lookup (table, key, 'v')) == NULL)
+    return NULL;
+
+  return gvdb_table_value_from_item (table, item);
+}
+
+/**
+ * gvdb_table_get_table:
+ * @file: a #GvdbTable
+ * @key: a string
+ * @returns: a new #GvdbTable, or %NULL
+ *
+ * Looks up the hash table named @key in @file.
+ *
+ * The toplevel hash table in a #GvdbTable can contain reference to
+ * child hash tables (and those can contain further references...).
+ *
+ * If @key is not found in @file then %NULL is returned.  Otherwise, a
+ * new #GvdbTable is returned, referring to the child hashtable as
+ * contained in the file.  This newly-created #GvdbTable does not depend
+ * on the continued existence of @file.
+ *
+ * You should call gvdb_table_free() on the return result when you no
+ * longer require it.
+ **/
+GvdbTable *
+gvdb_table_get_table (GvdbTable   *file,
+                      const gchar *key)
+{
+  const struct gvdb_hash_item *item;
+  GvdbTable *new;
+
+  item = gvdb_table_lookup (file, key, 'H');
+
+  if (item == NULL)
+    return NULL;
+
+  new = g_slice_new0 (GvdbTable);
+  new->bytes = g_bytes_ref (file->bytes);
+  new->byteswapped = file->byteswapped;
+  new->trusted = file->trusted;
+  new->data = file->data;
+  new->size = file->size;
+
+  gvdb_table_setup_root (new, &item->value.pointer);
+
+  return new;
+}
+
+/**
+ * gvdb_table_free:
+ * @file: a #GvdbTable
+ *
+ * Frees @file.
+ **/
+void
+gvdb_table_free (GvdbTable *file)
+{
+  g_bytes_unref (file->bytes);
+  g_slice_free (GvdbTable, file);
+}
+
+/**
+ * gvdb_table_is_valid:
+ * @table: a #GvdbTable
+ * @returns: %TRUE if @table is still valid
+ *
+ * Checks if the table is still valid.
+ *
+ * An on-disk GVDB can be marked as invalid.  This happens when the file
+ * has been replaced.  The appropriate action is typically to reopen the
+ * file.
+ **/
+gboolean
+gvdb_table_is_valid (GvdbTable *table)
+{
+  return !!*table->data;
+}
diff --git a/subprojects/gvdb/gvdb-reader.h b/subprojects/gvdb/gvdb-reader.h
new file mode 100644
index 0000000..5980925
--- /dev/null
+++ b/subprojects/gvdb/gvdb-reader.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __gvdb_reader_h__
+#define __gvdb_reader_h__
+
+#include <glib.h>
+
+typedef struct _GvdbTable GvdbTable;
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL
+GvdbTable *             gvdb_table_new_from_bytes                       (GBytes       *bytes,
+                                                                         gboolean      trusted,
+                                                                         GError      **error);
+G_GNUC_INTERNAL
+GvdbTable *             gvdb_table_new                                  (const gchar  *filename,
+                                                                         gboolean      trusted,
+                                                                         GError      **error);
+G_GNUC_INTERNAL
+void                    gvdb_table_free                                 (GvdbTable    *table);
+G_GNUC_INTERNAL
+gchar **                gvdb_table_get_names                            (GvdbTable    *table,
+                                                                         gint         *length);
+G_GNUC_INTERNAL
+gchar **                gvdb_table_list                                 (GvdbTable    *table,
+                                                                         const gchar  *key);
+G_GNUC_INTERNAL
+GvdbTable *             gvdb_table_get_table                            (GvdbTable    *table,
+                                                                         const gchar  *key);
+G_GNUC_INTERNAL
+GVariant *              gvdb_table_get_raw_value                        (GvdbTable    *table,
+                                                                         const gchar  *key);
+G_GNUC_INTERNAL
+GVariant *              gvdb_table_get_value                            (GvdbTable    *table,
+                                                                         const gchar  *key);
+
+G_GNUC_INTERNAL
+gboolean                gvdb_table_has_value                            (GvdbTable    *table,
+                                                                         const gchar  *key);
+G_GNUC_INTERNAL
+gboolean                gvdb_table_is_valid                             (GvdbTable    *table);
+
+G_END_DECLS
+
+#endif /* __gvdb_reader_h__ */
diff --git a/subprojects/gvdb/meson.build b/subprojects/gvdb/meson.build
new file mode 100644
index 0000000..a21ac74
--- /dev/null
+++ b/subprojects/gvdb/meson.build
@@ -0,0 +1,22 @@
+project('gvdb', 'c',
+  meson_version: '>= 0.40.0',
+  default_options: ['static=true'],
+)
+
+gvdb_sources = [
+  'gvdb-builder.c',
+  'gvdb-reader.c'
+]
+
+glib_dep = dependency('glib-2.0')
+
+gvdb_lib = static_library('gvdb',
+  gvdb_sources,
+  dependencies: glib_dep
+)
+
+gvdb_dep = declare_dependency(
+  link_with: gvdb_lib,
+  include_directories: include_directories('.'),
+  dependencies: glib_dep
+)
diff --git a/subprojects/libgd b/subprojects/libgd
new file mode 160000
index 0000000..a4928d0
--- /dev/null
+++ b/subprojects/libgd
@@ -0,0 +1 @@
+Subproject commit a4928d0f5e7aebe7d0670fb65d5b6c1bcbe5dd60
diff --git a/tests/ephy-file-helpers-test.c b/tests/ephy-file-helpers-test.c
index 6b8c3d3..41f4a10 100644
--- a/tests/ephy-file-helpers-test.c
+++ b/tests/ephy-file-helpers-test.c
@@ -323,7 +323,7 @@ main (int argc, char *argv[])
   /* Set our custom user-dirs.dirs, to control the output of
    * g_get_user_special_dir. The values there are the ones we should
    * check for in the test. */
-  g_setenv ("XDG_CONFIG_HOME", TOP_SRC_DIR "/tests/data/", TRUE);
+  g_setenv ("XDG_CONFIG_HOME", SOURCE_ROOT "/tests/data/", TRUE);
 
   gtk_test_init (&argc, &argv);
 
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644
index 0000000..8fa8161
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1,123 @@
+if get_option('enable_unit_tests')
+  # FIXME: The tests that need ephy-test-utils are all disabled....
+  #
+  # libephytestutils = static_library('ephytestutils',
+  #   'ephy-test-utils.c',
+  #   dependencies: ephymain_dep
+  # )
+  #
+  # ephytestutils_dep = declare_dependency(
+  #   link_with: libephytestutils
+  # )
+
+  completion_model_test = executable('test-ephy-completion-model',
+    'ephy-completion-model-test.c',
+    dependencies: ephymain_dep
+  )
+  test('Completion model test', completion_model_test)
+
+  # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=778153
+  # download_test = executable('test-ephy-download',
+  #   'ephy-download-test.c',
+  #   dependencies: ephymain_dep
+  # )
+  # test('Download test', download_test)
+
+  # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=773448
+  # embed_shell_test = executable('test-ephy-embed-shell',
+  #   'ephy-embed-shell-test.c',
+  #   dependencies: ephymain_dep
+  # )
+  # test('Embed shell test', embed_shell_test)
+
+  embed_utils_test = executable('test-ephy-embed-utils',
+    'ephy-embed-utils-test.c',
+    dependencies: ephymain_dep
+  )
+  test('Embed utils test', embed_utils_test)
+
+  encodings_test = executable('test-ephy-encodings',
+    'ephy-encodings-test.c',
+    dependencies: ephymain_dep
+  )
+  test('Encodings test', encodings_test)
+
+  file_helpers_test = executable('test-ephy-file-helpers',
+    'ephy-file-helpers-test.c',
+    dependencies: ephymain_dep
+  )
+  test('File helpers test', file_helpers_test)
+
+  history_test = executable('test-ephy-history',
+    'ephy-history-test.c',
+    dependencies: ephymain_dep
+  )
+  test('History test', history_test)
+
+  location_entry_test = executable('test-location-entry',
+    'ephy-location-entry-test.c',
+    dependencies: ephymain_dep
+  )
+  test('Location entry test', location_entry_test)
+
+  migration_test = executable('test-ephy-migration',
+    'ephy-migration-test.c',
+    dependencies: ephymain_dep
+  )
+  test('Migration test', migration_test)
+
+  # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=707220
+  # session_test = executable('test-ephy-session',
+  #   'ephy-session-test.c',
+  #   dependencies: ephytestutils_dep
+  # )
+  # test('Session test', session_test)
+
+  # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=693369
+  # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=695703
+  # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=707217
+  # shell_test = executable('test-ephy-shell',
+  #   'ephy-shell-test.c',
+  #   dependencies: ephytestutils_dep
+  # )
+  # test('Shell test', shell_test)
+
+  # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=762753
+  # snapshot_service_test = executable('test-snapshot-service',
+  #   'ephy-snapshot-service-test.c',
+  #   dependencies: ephymain_dep
+  # )
+  # test('Snapshot service test', snapshot_service_test)
+
+  sqlite_test = executable('test-ephy-sqlite',
+    'ephy-sqlite-test.c',
+    dependencies: ephymain_dep
+  )
+  test('SQLite test', sqlite_test)
+
+  string_test = executable('test-ephy-string',
+    'ephy-string-test.c',
+    dependencies: ephymain_dep
+  )
+  test('String test', string_test)
+
+  uri_helpers_test = executable('test-ephy-uri-helpers',
+    'ephy-uri-helpers-test.c',
+    dependencies: ephymain_dep
+  )
+  test('URI helpers test', uri_helpers_test)
+
+  # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=759256
+  # web_app_utils_test = executable('test-ephy-web-app-utils',
+  #   'ephy-web-app-utils-test.c',
+  #   dependencies: ephymain_dep
+  # )
+  # test('Web app utils test', web_app_utils_test)
+
+  # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=780280
+  # web_view_test = executable('test-ephy-web-view',
+  #   'ephy-web-view-test.c',
+  #   dependencies: ephymain_dep
+  # )
+  # test('Web view test', web_view_test)
+endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]