diff --git a/src/tools/pginclude/Makefile b/src/tools/pginclude/Makefile
new file mode 100644
index 0000000..b7e5e7e
--- /dev/null
+++ b/src/tools/pginclude/Makefile
@@ -0,0 +1,15 @@
+subdir = src/tools/pginclude
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+include $(top_srcdir)/src/backend/common.mk
+
+test-compile-include:
+	rm -rf compile-includes
+	cd $(top_builddir) && $(subdir)/gen_compile_include.pl
+	$(MAKE) -C $(top_builddir)/src/backend generated-headers
+	$(MAKE) -C compile-includes
+	rm -rf compile-includes
+
+clean:
+	rm -rf compile-includes
diff --git a/src/tools/pginclude/gen_compile_include.pl b/src/tools/pginclude/gen_compile_include.pl
new file mode 100755
index 0000000..24f858d
--- /dev/null
+++ b/src/tools/pginclude/gen_compile_include.pl
@@ -0,0 +1,163 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my $include_prefix = 'src/include';
+my $output_dir = 'src/tools/pginclude/compile-includes';
+
+#
+# Some include files need to be excluded from the "does it compile separately?"
+# test, either because they are platform-dependent stubs or because they aren't
+# actually intended to compile separately.
+#
+my %exclude_include = map { $_ => 1 } qw(
+	access/rmgrlist.h
+	parser/gram.h
+	parser/kwlist.h
+	port/atomics
+	port/cygwin.h
+	port/win32.h
+	port/win32
+	port/win32_msvc
+	regex/regerrs.h
+    storage/checksum_impl.h
+	rusagestub.h
+);
+
+my $top_builddir = $output_dir;
+$top_builddir =~ s@[^/]+@..@g;
+
+my @include_dir = ('');
+my @include_file;
+my @frontend_obj;
+my @backend_obj;
+
+#
+# Recurse through src/include and gather a list of all include files, excluding
+# those mentioned in the exclusion list, above.
+#
+while (@include_dir)
+{
+	my $include_dir = shift @include_dir;
+	my $include_path = $include_prefix . ($include_dir eq '' ? ''
+		: '/' . $include_dir);
+
+	opendir(my $directory, $include_path)
+		or die "could not opendir($include_path): $!";
+
+	while (my $entry = readdir($directory))
+	{
+		next if ($entry eq '.' || $entry eq '..');
+
+		$entry = $include_dir eq '' ? $entry : $include_dir . '/' . $entry;
+		next if exists $exclude_include{$entry};
+		my $path = $include_prefix . '/' . $entry;
+		if (-f $path)
+		{
+			push @include_file, $entry;
+		}
+		elsif (-d $path)
+		{
+			push @include_dir, $entry;
+		}
+	}
+}
+
+#
+# Create output directories.
+#
+mkdir($output_dir) or die "mkdir($output_dir): $!";
+mkdir($output_dir . '/backend') or die "mkdir($output_dir . '/backend'): $!";
+mkdir($output_dir . '/frontend') or die "mkdir($output_dir . '/frontend'): $!";
+
+#
+# For each include file, create a C file which includes either postgres.h
+# (for frontend includes) or postgres_fe.h (for backend includes), the
+# relevant header, and nothing else.
+#
+for my $include_file (@include_file)
+{
+	my $dotc_file = $include_file;
+	$dotc_file =~ s/\.h$/.c/;
+	$dotc_file =~ s@/@-@g;
+	my $is_frontend = ($include_file =~ m@^fe_utils/@);
+
+	my $dotc_path = $output_dir . ($is_frontend ? '/frontend/' : '/backend/')
+		. $dotc_file;
+	my $base_include = $is_frontend ? 'postgres_fe.h' : 'postgres.h';
+	$base_include = "postgres-fe.h" if $include_file =~ m@^fe_utils/@;
+
+	write_file($dotc_path, <<EOM);
+#include "postgres.h"
+
+#include "$include_file"
+EOM
+
+	# Record the name of the expected object file in the appropriate list.
+	my $object_file = $dotc_file;
+	$object_file =~ s/\.c$/.o/;
+	if ($is_frontend)
+	{
+		push @frontend_obj, $object_file;
+	}
+	else
+	{
+		push @backend_obj, $object_file;
+	}
+}
+
+#
+# Write a Makefile for the backend subdirectory.
+#
+my $backend_object_list = join(' ', @backend_obj);
+write_file($output_dir . '/backend/Makefile', <<EOM);
+subdir = $output_dir/backend
+top_builddir = $top_builddir/..
+include \$(top_builddir)/src/Makefile.global
+
+OBJS = $backend_object_list
+
+include \$(top_srcdir)/src/backend/common.mk
+
+EOM
+
+#
+# Write a Makefile for the frontend subdirectory.
+#
+my $frontend_object_list = join(' ', @frontend_obj);
+write_file($output_dir . '/frontend/Makefile', <<EOM);
+subdir = $output_dir/frontend
+top_builddir = $top_builddir/..
+include \$(top_builddir)/src/Makefile.global
+
+override CPPFLAGS := -DFRONTEND -I\$(libpq_srcdir) \$(CPPFLAGS)
+
+OBJS = $frontend_object_list
+
+include \$(top_srcdir)/src/backend/common.mk
+
+EOM
+
+#
+# Write a Makefile for the main output directory.
+#
+write_file($output_dir . '/Makefile', <<EOM);
+subdir = $output_dir
+top_builddir = $top_builddir
+include \$(top_builddir)/src/Makefile.global
+
+SUBDIRS = frontend backend
+
+include \$(top_srcdir)/src/backend/common.mk
+
+EOM
+
+sub write_file
+{
+	my ($path, $content) = @_;
+
+	open(my $file, '>', $path) or die "open($path): $!";
+	print $file $content;
+	close($file);
+}
