diff --git a/setup.cfg b/setup.cfg
index b41a238612ee6a7b5655e4f7eb228a47fd245641..14cb8b1bfeb7b81f2e60a2a9a55cdafb8310ffd3 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -53,6 +53,8 @@ setup_requires =
 [options.package_data]
 pyfastani = py.typed, *.pyi
 
+[publicize_headers]
+sources = vendor/FastANI/src
 
 # --- Python tools configuration --------------------------------------------
 
diff --git a/setup.py b/setup.py
index c8eccbb5b3e79bd4679cb41551d8315a316ab9e9..faf04cfe2d82a6be7315218e275bf17120746c99 100644
--- a/setup.py
+++ b/setup.py
@@ -57,6 +57,47 @@ class sdist(_sdist):
         _sdist.run(self)
 
 
+class publicize_headers(_build_ext):
+    """A custom command to make everything in C++ headers public.
+    """
+
+    user_options = _build_ext.user_options + [
+        ("sources=", "s", "the folder with the source headers to patch")
+    ]
+
+    def initialize_options(self):
+        _build_ext.initialize_options(self)
+        self.sources = None
+
+    def finalize_options(self):
+        _build_ext.finalize_options(self)
+        if self.sources is not None:
+            self.sources = _split_multiline(self.sources)
+
+    def run(self):
+        # patch sources
+        for source_dir in self.sources:
+            for dirpath, dirnames, filenames in os.walk(source_dir):
+                base_dirpath = os.path.relpath(dirpath, start=source_dir)
+                new_dirpath = os.path.join( self.build_temp, base_dirpath )
+                for filename in filenames:
+                    if filename.endswith((".h", ".hpp")):
+                        filepath = os.path.join(dirpath, filename)
+                        new_filepath = os.path.join( new_dirpath, filename )
+                        self.make_file([filepath], new_filepath, self.patch_header, (filepath, new_filepath))
+
+        # update the include dirs
+        for ext in self.extensions:
+            ext.include_dirs.append(self.build_temp)
+
+    def patch_header(self, old_path, new_path):
+        self.mkpath(os.path.dirname(new_path))
+        with open(old_path, "r") as src:
+            with open(new_path, "w") as dst:
+                for line in src:
+                    dst.write( line.replace("private:", "public:") )
+
+
 class build_ext(_build_ext):
     """A `build_ext` that disables optimizations if compiled in debug mode.
     """
@@ -68,6 +109,12 @@ class build_ext(_build_ext):
         self._clib_cmd.debug = self.debug
 
     def run(self):
+        # make sure sources have been patched to expose private fields
+        if not self.distribution.have_run.get("publicize_headers", False):
+            pub_cmd = self.get_finalized_command("publicize_headers")
+            pub_cmd.force = self.force
+            pub_cmd.run()
+
         # check `cythonize` is available
         if isinstance(cythonize, ImportError):
             raise RuntimeError("Cython is required to run `build_ext` command") from cythonize
@@ -97,14 +144,6 @@ class build_ext(_build_ext):
         for ext in self.extensions:
             ext._needs_stub = False
 
-        # # update the compiler include and link dirs to use the
-        # # temporary build folder so that the platform-specific headers
-        # # and static libs can be found
-
-        # check the libraries have been built already
-        if not self.distribution.have_run["build_clib"]:
-            self._clib_cmd.run()
-
         # build the extensions as normal
         _build_ext.run(self)
 
@@ -150,10 +189,10 @@ class clean(_clean):
 extensions = [
     Extension(
         "pyfastani._fastani",
-        [os.path.join("pyfastani", "_utils.cpp"), os.path.join("pyfastani", "_fastani.pyx")],
+        [os.path.join("pyfastani", x) for x in ("_utils.cpp", "_fastani.pyx")],
         language="c++",
         libraries=["gsl", "gslcblas", "stdc++", "z", "m"],
-        include_dirs=["include", "pyfastani", os.path.join("vendor", "FastANI", "src")],
+        include_dirs=["include", "pyfastani"],
         extra_compile_args=["-std=c++11"],
         extra_link_args=["-std=c++11"],
     )
@@ -163,9 +202,9 @@ extensions = [
 
 setuptools.setup(
     ext_modules=extensions,
-    # libraries=libraries,
     cmdclass=dict(
         build_ext=build_ext,
+        publicize_headers=publicize_headers,
         clean=clean,
         sdist=sdist,
     ),