meson's in-tree libpq header search order vs -Dextra_include_dirs

Started by Thomas Munro11 months ago16 messages
#1Thomas Munro
thomas.munro@gmail.com
1 attachment(s)

When I use configure/make and --with-includes=/usr/local/include, I
see compile lines like this:

... -I../../src/interfaces/libpq -I../../src/include -I/usr/local/include ...

That's important, because if I happen to have libpq headers installed
on the system I don't want to be confused by them. Yet meson's
-Dextra_include_dirs=/usr/local/include compiles like this:

... -I../src/include -I/usr/local/include -Isrc/interfaces/libpq ...

... which gives me errors due to the v16 headers I happen to have installed:

../src/fe_utils/connect_utils.c:164:3: error: unknown type name
'PGcancelConn'; did you mean 'PGcancel'?
164 | PGcancelConn *cancelConn = PQcancelCreate(conn);

I guess this affects Macs, BSDs and a few lesser used Linux distros
that put optional packages outside the base system and default search
paths. Perhaps you can see this on everything-goes-into-base-system
distros if you redundantly say -Dextra_include_dirs=/usr/include; I
didn't check if that is true, it wouldn't be if meson is opinionated
enough to remove it.

Reordering obvious mentions of libpq, as in the attached, gets past
most of them but I couldn't immediately figure out how to reorder
src/test/isolation/meson.build and gave up and uninstalled the
interfering libpq package for now. Dumping these observations here as
this is as far as I got with this impediment today.

Attachments:

wip-reorder-include.diffapplication/octet-stream; name=wip-reorder-include.diffDownload
diff --git a/contrib/dblink/meson.build b/contrib/dblink/meson.build
index 3ab78668288..e2be066cd70 100644
--- a/contrib/dblink/meson.build
+++ b/contrib/dblink/meson.build
@@ -13,7 +13,7 @@ endif
 dblink = shared_module('dblink',
   dblink_sources,
   kwargs: contrib_mod_args + {
-    'dependencies': contrib_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + contrib_mod_args['dependencies'],
   },
 )
 contrib_targets += dblink
diff --git a/contrib/postgres_fdw/meson.build b/contrib/postgres_fdw/meson.build
index 8b29be24dee..4138fbe61ae 100644
--- a/contrib/postgres_fdw/meson.build
+++ b/contrib/postgres_fdw/meson.build
@@ -17,7 +17,7 @@ endif
 postgres_fdw = shared_module('postgres_fdw',
   postgres_fdw_sources,
   kwargs: contrib_mod_args + {
-    'dependencies': contrib_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + contrib_mod_args['dependencies'],
   },
 )
 contrib_targets += postgres_fdw
diff --git a/src/backend/replication/libpqwalreceiver/meson.build b/src/backend/replication/libpqwalreceiver/meson.build
index 2150f31cfa3..5318a76e29d 100644
--- a/src/backend/replication/libpqwalreceiver/meson.build
+++ b/src/backend/replication/libpqwalreceiver/meson.build
@@ -14,7 +14,7 @@ libpqwalreceiver = shared_module('pqwalreceiver',
   libpqwalreceiver_sources,
   kwargs: pg_mod_args + {
     'name_prefix': 'lib',
-    'dependencies': pg_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + pg_mod_args['dependencies'],
   }
 )
 
diff --git a/src/bin/pg_amcheck/meson.build b/src/bin/pg_amcheck/meson.build
index 316ea0d40b8..c8f792ff971 100644
--- a/src/bin/pg_amcheck/meson.build
+++ b/src/bin/pg_amcheck/meson.build
@@ -12,7 +12,7 @@ endif
 
 pg_amcheck = executable('pg_amcheck',
   pg_amcheck_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_amcheck
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index f795ff28271..4fe711177af 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -58,7 +58,7 @@ psql = executable('psql',
   psql_sources,
   c_pch: pch_postgres_fe_h,
   include_directories: include_directories('.'),
-  dependencies: [frontend_code, libpq, readline],
+  dependencies: [libpq, frontend_code, readline],
   kwargs: default_bin_args,
 )
 bin_targets += psql
diff --git a/src/fe_utils/meson.build b/src/fe_utils/meson.build
index a18cbc939e4..7487869f1cf 100644
--- a/src/fe_utils/meson.build
+++ b/src/fe_utils/meson.build
@@ -31,7 +31,7 @@ fe_utils_sources += psqlscan
 fe_utils = static_library('libpgfeutils',
   fe_utils_sources + generated_headers,
   c_pch: pch_postgres_fe_h,
-  include_directories: [postgres_inc, libpq_inc],
+  include_directories: [libpq_inc, postgres_inc],
   c_args: host_system == 'windows' ? ['-DFD_SETSIZE=1024'] : [],
   dependencies: frontend_common_code,
   kwargs: default_lib_args,
diff --git a/src/test/modules/libpq_pipeline/meson.build b/src/test/modules/libpq_pipeline/meson.build
index d9f26f84f69..a6dc3a1b717 100644
--- a/src/test/modules/libpq_pipeline/meson.build
+++ b/src/test/modules/libpq_pipeline/meson.build
@@ -12,7 +12,7 @@ endif
 
 libpq_pipeline = executable('libpq_pipeline',
   libpq_pipeline_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   },
#2Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#1)
1 attachment(s)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

I took another look and got it working, after something else I'm using
insisted on installing libpq. It's mostly no-brainer hunks like this:

-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],

For the new src/test/modules/test_oat_hooks/meson.build, I copied what
I'd seen in contrib modules that use libpq:

-  kwargs: pg_test_mod_args,
+  kwargs: pg_test_mod_args + {
+       'dependencies': [libpq] + pg_test_mod_args['dependencies'],
+  }

(I'm still green enough with Meson that I had to look that syntax up:
it's how you replace the values of common keys with the values from
the right hand dict[1]https://mesonbuild.com/Reference-manual_elementary_dict.html, which ain't Pythonesque AFAIK.)

For ecpg and pg_regress, where I got stuck last time, I am still a bit
confused about whether it should go here:

-ecpg_inc = include_directories('.')
+ecpg_inc = [libpq_inc, include_directories('.')]

... or perhaps in intermediate ecpgXXX_inc variables or the final
include_directories directives. In this version I just did that easy
thing and it works for me. But is it the right place?

Likewise for pg_regress_inc, also used by ECPG and isolation tests:

-pg_regress_inc = include_directories('.')
+pg_regress_inc = [libpq_inc, include_directories('.')]

[1]: https://mesonbuild.com/Reference-manual_elementary_dict.html

Attachments:

0001-meson-Fix-libpq-header-include-order.patchtext/x-patch; charset=US-ASCII; name=0001-meson-Fix-libpq-header-include-order.patchDownload
From 0ba525115e6c6b7b9cadd73b3db9e26c77b8c1a2 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 14 Jul 2025 11:54:00 +1200
Subject: [PATCH] meson: Fix libpq header include order.

On systems using -Dextra_include_dir that happen to have a copy of libpq
installed there, it's important to put the libpq header search path
ahead of -Dextra_include_dir.  Reorder the dependencies to achieve that.
This matches the order used by configure.

Back-patch to 16, where meson arrived.

Reviewed-by:
Discussion: https://postgr.es/m/CA+hUKGKispvxLyrBn3=3mp0BB1N+RBYR5eE2guCOksnwEoOcPQ@mail.gmail.com
---
 contrib/dblink/meson.build                           | 2 +-
 contrib/oid2name/meson.build                         | 2 +-
 contrib/postgres_fdw/meson.build                     | 2 +-
 contrib/vacuumlo/meson.build                         | 2 +-
 src/backend/replication/libpqwalreceiver/meson.build | 2 +-
 src/bin/initdb/meson.build                           | 2 +-
 src/bin/pg_amcheck/meson.build                       | 2 +-
 src/bin/pg_basebackup/meson.build                    | 4 ++--
 src/bin/pg_combinebackup/meson.build                 | 2 +-
 src/bin/pg_dump/meson.build                          | 8 ++++----
 src/bin/pg_rewind/meson.build                        | 2 +-
 src/bin/pg_upgrade/meson.build                       | 2 +-
 src/bin/pgbench/meson.build                          | 2 +-
 src/bin/psql/meson.build                             | 2 +-
 src/bin/scripts/meson.build                          | 4 ++--
 src/fe_utils/meson.build                             | 2 +-
 src/interfaces/ecpg/ecpglib/meson.build              | 6 +++---
 src/interfaces/ecpg/include/meson.build              | 2 +-
 src/interfaces/libpq/test/meson.build                | 4 ++--
 src/test/isolation/meson.build                       | 4 ++--
 src/test/modules/libpq_pipeline/meson.build          | 2 +-
 src/test/modules/oauth_validator/meson.build         | 2 +-
 src/test/modules/test_escape/meson.build             | 2 +-
 src/test/modules/test_oat_hooks/meson.build          | 4 +++-
 src/test/regress/meson.build                         | 4 ++--
 25 files changed, 37 insertions(+), 35 deletions(-)

diff --git a/contrib/dblink/meson.build b/contrib/dblink/meson.build
index a19ce6cf4b9..28da26d3d45 100644
--- a/contrib/dblink/meson.build
+++ b/contrib/dblink/meson.build
@@ -13,7 +13,7 @@ endif
 dblink = shared_module('dblink',
   dblink_sources,
   kwargs: contrib_mod_args + {
-    'dependencies': contrib_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + contrib_mod_args['dependencies'],
   },
 )
 contrib_targets += dblink
diff --git a/contrib/oid2name/meson.build b/contrib/oid2name/meson.build
index 074f16acd72..dd958f00bdf 100644
--- a/contrib/oid2name/meson.build
+++ b/contrib/oid2name/meson.build
@@ -12,7 +12,7 @@ endif
 
 oid2name = executable('oid2name',
   oid2name_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 contrib_targets += oid2name
diff --git a/contrib/postgres_fdw/meson.build b/contrib/postgres_fdw/meson.build
index 5c11bc6496f..49012db1ba0 100644
--- a/contrib/postgres_fdw/meson.build
+++ b/contrib/postgres_fdw/meson.build
@@ -17,7 +17,7 @@ endif
 postgres_fdw = shared_module('postgres_fdw',
   postgres_fdw_sources,
   kwargs: contrib_mod_args + {
-    'dependencies': contrib_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + contrib_mod_args['dependencies'],
   },
 )
 contrib_targets += postgres_fdw
diff --git a/contrib/vacuumlo/meson.build b/contrib/vacuumlo/meson.build
index deee1d2832d..51e8fe9a325 100644
--- a/contrib/vacuumlo/meson.build
+++ b/contrib/vacuumlo/meson.build
@@ -12,7 +12,7 @@ endif
 
 vacuumlo = executable('vacuumlo',
   vacuumlo_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 contrib_targets += vacuumlo
diff --git a/src/backend/replication/libpqwalreceiver/meson.build b/src/backend/replication/libpqwalreceiver/meson.build
index 2150f31cfa3..5318a76e29d 100644
--- a/src/backend/replication/libpqwalreceiver/meson.build
+++ b/src/backend/replication/libpqwalreceiver/meson.build
@@ -14,7 +14,7 @@ libpqwalreceiver = shared_module('pqwalreceiver',
   libpqwalreceiver_sources,
   kwargs: pg_mod_args + {
     'name_prefix': 'lib',
-    'dependencies': pg_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + pg_mod_args['dependencies'],
   }
 )
 
diff --git a/src/bin/initdb/meson.build b/src/bin/initdb/meson.build
index 06958e370f3..b8584fe56de 100644
--- a/src/bin/initdb/meson.build
+++ b/src/bin/initdb/meson.build
@@ -21,7 +21,7 @@ initdb = executable('initdb',
   # shared library from a different PG version.  Define
   # USE_PRIVATE_ENCODING_FUNCS to ensure that that happens.
   c_args: ['-DUSE_PRIVATE_ENCODING_FUNCS'],
-  dependencies: [frontend_code, libpq, icu, icu_i18n],
+  dependencies: [libpq, frontend_code, icu, icu_i18n],
   kwargs: default_bin_args,
 )
 bin_targets += initdb
diff --git a/src/bin/pg_amcheck/meson.build b/src/bin/pg_amcheck/meson.build
index 316ea0d40b8..c8f792ff971 100644
--- a/src/bin/pg_amcheck/meson.build
+++ b/src/bin/pg_amcheck/meson.build
@@ -12,7 +12,7 @@ endif
 
 pg_amcheck = executable('pg_amcheck',
   pg_amcheck_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_amcheck
diff --git a/src/bin/pg_basebackup/meson.build b/src/bin/pg_basebackup/meson.build
index 3a7fc10eab0..b85ba465d47 100644
--- a/src/bin/pg_basebackup/meson.build
+++ b/src/bin/pg_basebackup/meson.build
@@ -7,7 +7,7 @@ common_sources = files(
   'walmethods.c',
 )
 
-pg_basebackup_deps = [frontend_code, libpq, lz4, zlib, zstd]
+pg_basebackup_deps = [libpq, frontend_code, lz4, zlib, zstd]
 pg_basebackup_common = static_library('libpg_basebackup_common',
   common_sources,
   dependencies: pg_basebackup_deps,
@@ -45,7 +45,7 @@ endif
 
 pg_createsubscriber = executable('pg_createsubscriber',
   pg_createsubscriber_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_createsubscriber
diff --git a/src/bin/pg_combinebackup/meson.build b/src/bin/pg_combinebackup/meson.build
index e80a4756a7f..04e09162d9b 100644
--- a/src/bin/pg_combinebackup/meson.build
+++ b/src/bin/pg_combinebackup/meson.build
@@ -17,7 +17,7 @@ endif
 
 pg_combinebackup = executable('pg_combinebackup',
   pg_combinebackup_sources,
-  dependencies: [frontend_code],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_combinebackup
diff --git a/src/bin/pg_dump/meson.build b/src/bin/pg_dump/meson.build
index 4a4ebbd8ec9..a079b6dfa92 100644
--- a/src/bin/pg_dump/meson.build
+++ b/src/bin/pg_dump/meson.build
@@ -22,7 +22,7 @@ pg_dump_common_sources = files(
 pg_dump_common = static_library('libpgdump_common',
   pg_dump_common_sources,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_code, libpq, lz4, zlib, zstd],
+  dependencies: [libpq, frontend_code, lz4, zlib, zstd],
   kwargs: internal_lib_args,
 )
 
@@ -42,7 +42,7 @@ endif
 pg_dump = executable('pg_dump',
   pg_dump_sources,
   link_with: [pg_dump_common],
-  dependencies: [frontend_code, libpq, zlib],
+  dependencies: [libpq, frontend_code, zlib],
   kwargs: default_bin_args,
 )
 bin_targets += pg_dump
@@ -61,7 +61,7 @@ endif
 pg_dumpall = executable('pg_dumpall',
   pg_dumpall_sources,
   link_with: [pg_dump_common],
-  dependencies: [frontend_code, libpq, zlib],
+  dependencies: [libpq, frontend_code, zlib],
   kwargs: default_bin_args,
 )
 bin_targets += pg_dumpall
@@ -80,7 +80,7 @@ endif
 pg_restore = executable('pg_restore',
   pg_restore_sources,
   link_with: [pg_dump_common],
-  dependencies: [frontend_code, libpq, zlib],
+  dependencies: [libpq, frontend_code, zlib],
   kwargs: default_bin_args,
 )
 bin_targets += pg_restore
diff --git a/src/bin/pg_rewind/meson.build b/src/bin/pg_rewind/meson.build
index 36171600cca..f2790246b23 100644
--- a/src/bin/pg_rewind/meson.build
+++ b/src/bin/pg_rewind/meson.build
@@ -21,7 +21,7 @@ endif
 
 pg_rewind = executable('pg_rewind',
   pg_rewind_sources,
-  dependencies: [frontend_code, libpq, lz4, zstd],
+  dependencies: [libpq, frontend_code, lz4, zstd],
   c_args: ['-DFRONTEND'], # needed for xlogreader et al
   kwargs: default_bin_args,
 )
diff --git a/src/bin/pg_upgrade/meson.build b/src/bin/pg_upgrade/meson.build
index ac992f0d14b..8aefaeea91f 100644
--- a/src/bin/pg_upgrade/meson.build
+++ b/src/bin/pg_upgrade/meson.build
@@ -28,7 +28,7 @@ endif
 pg_upgrade = executable('pg_upgrade',
   pg_upgrade_sources,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_upgrade
diff --git a/src/bin/pgbench/meson.build b/src/bin/pgbench/meson.build
index a2a909a8442..de013cc9827 100644
--- a/src/bin/pgbench/meson.build
+++ b/src/bin/pgbench/meson.build
@@ -27,7 +27,7 @@ endif
 
 pgbench = executable('pgbench',
   pgbench_sources,
-  dependencies: [frontend_code, libpq, thread_dep],
+  dependencies: [libpq, frontend_code, thread_dep],
   include_directories: include_directories('.'),
   c_pch: pch_postgres_fe_h,
   c_args: host_system == 'windows' ? ['-DFD_SETSIZE=1024'] : [],
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index f795ff28271..4fe711177af 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -58,7 +58,7 @@ psql = executable('psql',
   psql_sources,
   c_pch: pch_postgres_fe_h,
   include_directories: include_directories('.'),
-  dependencies: [frontend_code, libpq, readline],
+  dependencies: [libpq, frontend_code, readline],
   kwargs: default_bin_args,
 )
 bin_targets += psql
diff --git a/src/bin/scripts/meson.build b/src/bin/scripts/meson.build
index 80df7c33257..90517dd9a10 100644
--- a/src/bin/scripts/meson.build
+++ b/src/bin/scripts/meson.build
@@ -2,7 +2,7 @@
 
 scripts_common = static_library('libscripts_common',
   files('common.c'),
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: internal_lib_args,
 )
 
@@ -29,7 +29,7 @@ foreach binary : binaries
   binary = executable(binary,
     binary_sources,
     link_with: [scripts_common],
-    dependencies: [frontend_code, libpq],
+    dependencies: [libpq, frontend_code],
     kwargs: default_bin_args,
   )
   bin_targets += binary
diff --git a/src/fe_utils/meson.build b/src/fe_utils/meson.build
index a18cbc939e4..7487869f1cf 100644
--- a/src/fe_utils/meson.build
+++ b/src/fe_utils/meson.build
@@ -31,7 +31,7 @@ fe_utils_sources += psqlscan
 fe_utils = static_library('libpgfeutils',
   fe_utils_sources + generated_headers,
   c_pch: pch_postgres_fe_h,
-  include_directories: [postgres_inc, libpq_inc],
+  include_directories: [libpq_inc, postgres_inc],
   c_args: host_system == 'windows' ? ['-DFD_SETSIZE=1024'] : [],
   dependencies: frontend_common_code,
   kwargs: default_lib_args,
diff --git a/src/interfaces/ecpg/ecpglib/meson.build b/src/interfaces/ecpg/ecpglib/meson.build
index 8f478c6a73e..fb7d33d2a0e 100644
--- a/src/interfaces/ecpg/ecpglib/meson.build
+++ b/src/interfaces/ecpg/ecpglib/meson.build
@@ -30,7 +30,7 @@ ecpglib_st = static_library('libecpg',
   include_directories: ecpglib_inc,
   c_args: ecpglib_c_args,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_stlib_code, thread_dep, libpq],
+  dependencies: [libpq, frontend_stlib_code, thread_dep],
   link_with: [ecpg_pgtypes_st],
   kwargs: default_lib_args,
 )
@@ -41,7 +41,7 @@ ecpglib_so = shared_library('libecpg',
   include_directories: ecpglib_inc,
   c_args: ecpglib_c_args,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_shlib_code, libpq, thread_dep],
+  dependencies: [libpq, frontend_shlib_code, thread_dep],
   link_with: ecpg_pgtypes_so,
   soversion: host_system != 'windows' ? '6' : '',
   darwin_versions: ['6', '6.' + pg_version_major.to_string()],
@@ -58,7 +58,7 @@ pkgconfig.generate(
   url: pg_url,
   libraries: ecpglib_so,
   libraries_private: [frontend_stlib_code, thread_dep],
-  requires_private: ['libpgtypes', 'libpq'],
+  requires_private: ['libpq', 'libpgtypes'],
 )
 
 subdir('po', if_found: libintl)
diff --git a/src/interfaces/ecpg/include/meson.build b/src/interfaces/ecpg/include/meson.build
index a6541e1a686..133ebe2f11f 100644
--- a/src/interfaces/ecpg/include/meson.build
+++ b/src/interfaces/ecpg/include/meson.build
@@ -1,6 +1,6 @@
 # Copyright (c) 2022-2025, PostgreSQL Global Development Group
 
-ecpg_inc = include_directories('.')
+ecpg_inc = [libpq_inc, include_directories('.')]
 
 ecpg_conf_keys = [
   'SIZEOF_LONG',
diff --git a/src/interfaces/libpq/test/meson.build b/src/interfaces/libpq/test/meson.build
index 07a5facc321..75d2012ba17 100644
--- a/src/interfaces/libpq/test/meson.build
+++ b/src/interfaces/libpq/test/meson.build
@@ -14,7 +14,7 @@ endif
 
 libpq_test_deps += executable('libpq_uri_regress',
   libpq_uri_regress_sources,
-  dependencies: [frontend_no_fe_utils_code, libpq],
+  dependencies: [libpq, frontend_no_fe_utils_code],
   kwargs: default_bin_args + {
     'install': false,
   }
@@ -33,7 +33,7 @@ endif
 
 libpq_test_deps += executable('libpq_testclient',
   libpq_testclient_sources,
-  dependencies: [frontend_no_fe_utils_code, libpq],
+  dependencies: [libpq, frontend_no_fe_utils_code],
   kwargs: default_bin_args + {
     'install': false,
   }
diff --git a/src/test/isolation/meson.build b/src/test/isolation/meson.build
index a180e4e2741..660b11eff8b 100644
--- a/src/test/isolation/meson.build
+++ b/src/test/isolation/meson.build
@@ -35,7 +35,7 @@ pg_isolation_regress = executable('pg_isolation_regress',
   isolation_sources,
   c_args: pg_regress_cflags,
   include_directories: pg_regress_inc,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install_dir': dir_pgxs / 'src/test/isolation',
   },
@@ -52,7 +52,7 @@ endif
 isolationtester = executable('isolationtester',
   isolationtester_sources,
   include_directories: include_directories('.'),
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install_dir': dir_pgxs / 'src/test/isolation',
   },
diff --git a/src/test/modules/libpq_pipeline/meson.build b/src/test/modules/libpq_pipeline/meson.build
index 3fd70a04a38..83201111ca3 100644
--- a/src/test/modules/libpq_pipeline/meson.build
+++ b/src/test/modules/libpq_pipeline/meson.build
@@ -12,7 +12,7 @@ endif
 
 libpq_pipeline = executable('libpq_pipeline',
   libpq_pipeline_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   },
diff --git a/src/test/modules/oauth_validator/meson.build b/src/test/modules/oauth_validator/meson.build
index a6f937fd7d7..2cd5c4cd537 100644
--- a/src/test/modules/oauth_validator/meson.build
+++ b/src/test/modules/oauth_validator/meson.build
@@ -60,7 +60,7 @@ endif
 
 oauth_hook_client = executable('oauth_hook_client',
   oauth_hook_client_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   },
diff --git a/src/test/modules/test_escape/meson.build b/src/test/modules/test_escape/meson.build
index a21341d5067..04fd0be0889 100644
--- a/src/test/modules/test_escape/meson.build
+++ b/src/test/modules/test_escape/meson.build
@@ -10,7 +10,7 @@ endif
 
 test_escape = executable('test_escape',
   test_escape_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   }
diff --git a/src/test/modules/test_oat_hooks/meson.build b/src/test/modules/test_oat_hooks/meson.build
index 1e600b0f4c7..6f0c4afd433 100644
--- a/src/test/modules/test_oat_hooks/meson.build
+++ b/src/test/modules/test_oat_hooks/meson.build
@@ -12,7 +12,9 @@ endif
 
 test_oat_hooks = shared_module('test_oat_hooks',
   test_oat_hooks_sources,
-  kwargs: pg_test_mod_args,
+  kwargs: pg_test_mod_args + {
+	'dependencies': [libpq] + pg_test_mod_args['dependencies'],
+  }
 )
 test_install_libs += test_oat_hooks
 
diff --git a/src/test/regress/meson.build b/src/test/regress/meson.build
index 1da9e9462a9..99f402f49a8 100644
--- a/src/test/regress/meson.build
+++ b/src/test/regress/meson.build
@@ -2,7 +2,7 @@
 
 # also used by isolationtester and ecpg tests
 pg_regress_c = files('pg_regress.c')
-pg_regress_inc = include_directories('.')
+pg_regress_inc = [libpq_inc, include_directories('.')]
 
 regress_sources = pg_regress_c + files(
   'pg_regress_main.c'
@@ -30,7 +30,7 @@ endif
 pg_regress = executable('pg_regress',
   regress_sources,
   c_args: pg_regress_cflags,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install_dir': dir_pgxs / 'src/test/regress',
   },
-- 
2.50.1

#3Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#2)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

Added to commitfest: https://commitfest.postgresql.org/patch/6056/

I'm having to carry this patch in all my development branches for now
or I can't build on one of my regular dev machines, so I'm quite keen
to get this into the tree soon and would back-patch to 16.

I gather no one else is affected yet, but I assume you can see the
problem on a Mac by installing PostgreSQL with Homebrew/MacPorts, or
on CI if you do this to .cirrus.tasks.yml:

   setup_additional_packages_script: |
-    #pkg install -y ...
+    pkg install -y postgresql17-client
#4Mario González Troncoso
gonzalemario@gmail.com
In reply to: Thomas Munro (#3)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On Thu, 9 Oct 2025 at 16:06, Thomas Munro <thomas.munro@gmail.com> wrote:

Added to commitfest: https://commitfest.postgresql.org/patch/6056/

I'm having to carry this patch in all my development branches for now
or I can't build on one of my regular dev machines, so I'm quite keen
to get this into the tree soon and would back-patch to 16.

Can you confirm you still have this problem on current master? I tried
to reproduce it in debian 12 by installing `postgresql-server-dev-15`
and adding the -Dextra_include_dirs

I gather no one else is affected yet, but I assume you can see the
problem on a Mac by installing PostgreSQL with Homebrew/MacPorts, or
on CI if you do this to .cirrus.tasks.yml:

setup_additional_packages_script: |
-    #pkg install -y ...
+    pkg install -y postgresql17-client

--
https://www.linkedin.com/in/gonzalemario

#5Thomas Munro
thomas.munro@gmail.com
In reply to: Mario González Troncoso (#4)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On Fri, Oct 10, 2025 at 8:09 AM Mario González Troncoso
<gonzalemario@gmail.com> wrote:

Can you confirm you still have this problem on current master? I tried

Thanks for looking! Yes.

to reproduce it in debian 12 by installing `postgresql-server-dev-15`
and adding the -Dextra_include_dirs

The problem is with libpq headers, not server headers.

Thinking about how to show this on Debian... Also it'll fail to fail
if your libpq headers are new enough to be compatible, eg 18. I guess
if you find an older libpq-dev .deb file, v16 is what my system is
clashing with, and unpack it into a temporary directory (something
like ar x XXX.deb then tar xf data.tar.XXX), and then point to the
headers in -Dextra_include_dirs, you should see it?

Or maybe just make a temporary file somewhere called libpq-fe.h that contains:

#error "wrong header included"

... and point to its parent with -Dextra_include_dirs. The goal is
for the in-tree libpq-fe.h to be found sooner in the search path (as
it is with configure), completely hiding that poisoned file.

#6Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Thomas Munro (#3)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On 14/09/2025 06:23, Thomas Munro wrote:

Added to commitfest: https://commitfest.postgresql.org/patch/6056/

I'm having to carry this patch in all my development branches for now
or I can't build on one of my regular dev machines, so I'm quite keen
to get this into the tree soon and would back-patch to 16.

I gather no one else is affected yet, but I assume you can see the
problem on a Mac by installing PostgreSQL with Homebrew/MacPorts, or
on CI if you do this to .cirrus.tasks.yml:

setup_additional_packages_script: |
-    #pkg install -y ...
+    pkg install -y postgresql17-client

The patch seems harmless enough to me, no objections to committing it.
However, I'm worried that we'll soon break it again. The new rule is
apparently "include libpq first", but we have no way of enforcing it.

- Heikki

#7Thomas Munro
thomas.munro@gmail.com
In reply to: Heikki Linnakangas (#6)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On Sat, Nov 1, 2025 at 7:14 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

The patch seems harmless enough to me, no objections to committing it.

Thanks!

However, I'm worried that we'll soon break it again. The new rule is
apparently "include libpq first", but we have no way of enforcing it.

Hmm, my meson-fu is weak, but there must surely be some way to declare
that you want libpq and have it automatically put things in the right
order, will look into that...

#8Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#7)
1 attachment(s)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On Sat, Nov 1, 2025 at 10:13 AM Thomas Munro <thomas.munro@gmail.com> wrote:

On Sat, Nov 1, 2025 at 7:14 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

However, I'm worried that we'll soon break it again. The new rule is
apparently "include libpq first", but we have no way of enforcing it.

Hmm, my meson-fu is weak, but there must surely be some way to declare
that you want libpq and have it automatically put things in the right
order, will look into that...

If we really don't want every site that depends on both libpq and
frontend_code to have to remember to respect that order when declaring
dependencies, then we could instead change frontend_code to force
libpq_inc to appear in its include_directories before postgres_inc (=
where extra_include_dirs comes from). This one-liner fixes the build
on my system:

 # for frontend binaries
 frontend_code = declare_dependency(
-  include_directories: [postgres_inc],
+  include_directories: [libpq_inc, postgres_inc],

That feels a little odd, because libpq is not really a dependency of
frontend_code, and not all frontend_code users also use libpq (though
almost all do). Does this have any unwanted side-effects? Is there a
better way to do this in a central place?

There are two other places that already have those two in
include_directories already, so their order should surely be flipped
to match, they just didn't happen to break on my system (I guess by
luck, ie not accessing APIs that changed incompatibly since the
version in my system-installed libpq headers).

Attachments:

v2-0001-meson-Fix-libpq-header-inclusion-order.patchtext/x-patch; charset=US-ASCII; name=v2-0001-meson-Fix-libpq-header-inclusion-order.patchDownload
From 25d830c8dab734349758286605bd2d1de643ed86 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sat, 1 Nov 2025 17:10:11 +1300
Subject: [PATCH v2] meson: Fix libpq header inclusion order.

Make sure that libpq_inc always comes before postgres_inc, since the
latter includes -Dextra_include_dirs.  We want to find the in-tree libpq
headers before any copy that might happen to be installed on the system
in that location.

Discussion: https://postgr.es/m/CA%2BhUKGJU9kJiSTwxvrDxqrDFw8VvsX9ZbZRL%3DqP%3DKXuFX_XH2A%40mail.gmail.com
---
 meson.build                             | 7 ++++++-
 src/fe_utils/meson.build                | 2 +-
 src/interfaces/ecpg/preproc/meson.build | 2 +-
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/meson.build b/meson.build
index 0f61ff6a700..2017b76a836 100644
--- a/meson.build
+++ b/meson.build
@@ -3301,7 +3301,12 @@ subdir('src/interfaces/libpq-oauth')
 
 # for frontend binaries
 frontend_code = declare_dependency(
-  include_directories: [postgres_inc],
+  # Not all code that depends on frontend_code also depends on libpq, but by
+  # pulling in its headers before postgres_inc we avoid exposing headers from a
+  # system-installed copy of libpq found in -Dextra_include_dirs, without
+  # requiring every frontend binary that depends on both to declare them in the
+  # right order.
+  include_directories: [libpq_inc, postgres_inc],
   link_with: [fe_utils, common_static, pgport_static],
   sources: generated_headers_stamp,
   dependencies: [os_deps, libintl],
diff --git a/src/fe_utils/meson.build b/src/fe_utils/meson.build
index ddac3c3a658..0a65b973779 100644
--- a/src/fe_utils/meson.build
+++ b/src/fe_utils/meson.build
@@ -32,7 +32,7 @@ fe_utils_sources += psqlscan
 fe_utils = static_library('libpgfeutils',
   fe_utils_sources,
   c_pch: pch_postgres_fe_h,
-  include_directories: [postgres_inc, libpq_inc],
+  include_directories: [libpq_inc, postgres_inc],
   c_args: host_system == 'windows' ? ['-DFD_SETSIZE=1024'] : [],
   dependencies: frontend_common_code,
   kwargs: default_lib_args,
diff --git a/src/interfaces/ecpg/preproc/meson.build b/src/interfaces/ecpg/preproc/meson.build
index aa948efc0dc..c754d885b7b 100644
--- a/src/interfaces/ecpg/preproc/meson.build
+++ b/src/interfaces/ecpg/preproc/meson.build
@@ -78,7 +78,7 @@ endif
 
 ecpg_exe = executable('ecpg',
   ecpg_sources,
-  include_directories: ['.', ecpg_inc, postgres_inc, libpq_inc],
+  include_directories: ['.', ecpg_inc, libpq_inc, postgres_inc],
   c_pch: pch_postgres_fe_h,
   dependencies: [frontend_code],
   kwargs: default_bin_args,
-- 
2.51.1

#9Thomas Munro
thomas.munro@gmail.com
In reply to: Heikki Linnakangas (#6)
2 attachment(s)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On Sat, Nov 1, 2025 at 7:14 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

The patch seems harmless enough to me, no objections to committing it.
However, I'm worried that we'll soon break it again. The new rule is
apparently "include libpq first", but we have no way of enforcing it.

Here's a new version of the original approach with fixes for some
copy-and-pastes I'd missed, and a new patch to check the search order
in CI. It adds a "poisoned" libpq-fe.h header into the search path to
break the build step if you get it wrong in meson or configure (cf
commit 4300d8b6 which fixed a case of this there). It fails like
this:

[01:45:21.825] In file included from ../src/include/fe_utils/connect_utils.h:15,
[01:45:21.825] from ../src/bin/scripts/common.h:13,
[01:45:21.825] from ../src/bin/scripts/vacuumdb.c:17:
[01:45:21.825] /tmp/poisoned_headers/libpq-fe.h:1:2: error: #error
"external header hides in-tree header"
[01:45:21.825] 1 | #error "external header hides in-tree header"
[01:45:21.825] | ^~~~~

There must surely be a more mesonic way to do this with declarations
rather than the order in a flimsy list that is copied all over the
place (I guess that is probably supposed to be thought of as an
unordered set...?), but I'm not sure I was on the right path with my
v2 as mentioned and it didn't survive this test. This approach is OK
for now, I think... if someone ever shows up with a patch to tackle it
more fundamentally, the order will presumably become more flexible, so
the new order will still be valid but unimportant.

Will wait another day for a better idea or objection to show up, and
otherwise commit these to 16+.

Attachments:

v3-0001-meson-Fix-libpq-header-include-order.patchtext/x-patch; charset=US-ASCII; name=v3-0001-meson-Fix-libpq-header-include-order.patchDownload
From aa390e77bedf25c7bf53f8dc5c05b6e826ccbc88 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 14 Jul 2025 11:54:00 +1200
Subject: [PATCH v3 1/2] meson: Fix libpq header include order.

On systems using -Dextra_include_dir that happen to have a copy of libpq
installed there, it's important to put the libpq header search path
ahead of -Dextra_include_dir.  Reorder the dependencies to achieve that.
This matches the order used by configure.

There is probably a better way to do this using declarations rather than
adjusting the order of many lists, but this works for now until someone
shows up with a patch for that.

Back-patch to 16, where meson arrived.

Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>
Discussion: https://postgr.es/m/CA+hUKGKispvxLyrBn3=3mp0BB1N+RBYR5eE2guCOksnwEoOcPQ@mail.gmail.com
---
 contrib/dblink/meson.build                           | 2 +-
 contrib/oid2name/meson.build                         | 2 +-
 contrib/postgres_fdw/meson.build                     | 2 +-
 contrib/vacuumlo/meson.build                         | 2 +-
 src/backend/replication/libpqwalreceiver/meson.build | 2 +-
 src/bin/initdb/meson.build                           | 2 +-
 src/bin/pg_amcheck/meson.build                       | 2 +-
 src/bin/pg_basebackup/meson.build                    | 4 ++--
 src/bin/pg_combinebackup/meson.build                 | 2 +-
 src/bin/pg_ctl/meson.build                           | 2 +-
 src/bin/pg_dump/meson.build                          | 8 ++++----
 src/bin/pg_rewind/meson.build                        | 2 +-
 src/bin/pg_upgrade/meson.build                       | 2 +-
 src/bin/pg_verifybackup/meson.build                  | 2 +-
 src/bin/pgbench/meson.build                          | 2 +-
 src/bin/psql/meson.build                             | 2 +-
 src/bin/scripts/meson.build                          | 8 ++++----
 src/fe_utils/meson.build                             | 2 +-
 src/interfaces/ecpg/ecpglib/meson.build              | 6 +++---
 src/interfaces/ecpg/include/meson.build              | 2 +-
 src/interfaces/ecpg/test/meson.build                 | 4 ++--
 src/interfaces/ecpg/test/thread/meson.build          | 2 +-
 src/interfaces/libpq/test/meson.build                | 4 ++--
 src/test/isolation/meson.build                       | 4 ++--
 src/test/modules/libpq_pipeline/meson.build          | 2 +-
 src/test/modules/oauth_validator/meson.build         | 2 +-
 src/test/modules/test_escape/meson.build             | 2 +-
 src/test/modules/test_int128/meson.build             | 2 +-
 src/test/modules/test_json_parser/meson.build        | 2 +-
 src/test/modules/test_oat_hooks/meson.build          | 4 +++-
 src/test/regress/meson.build                         | 4 ++--
 31 files changed, 46 insertions(+), 44 deletions(-)

diff --git a/contrib/dblink/meson.build b/contrib/dblink/meson.build
index a19ce6cf4b9..28da26d3d45 100644
--- a/contrib/dblink/meson.build
+++ b/contrib/dblink/meson.build
@@ -13,7 +13,7 @@ endif
 dblink = shared_module('dblink',
   dblink_sources,
   kwargs: contrib_mod_args + {
-    'dependencies': contrib_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + contrib_mod_args['dependencies'],
   },
 )
 contrib_targets += dblink
diff --git a/contrib/oid2name/meson.build b/contrib/oid2name/meson.build
index 074f16acd72..dd958f00bdf 100644
--- a/contrib/oid2name/meson.build
+++ b/contrib/oid2name/meson.build
@@ -12,7 +12,7 @@ endif
 
 oid2name = executable('oid2name',
   oid2name_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 contrib_targets += oid2name
diff --git a/contrib/postgres_fdw/meson.build b/contrib/postgres_fdw/meson.build
index aac89ffdde8..a2890251d69 100644
--- a/contrib/postgres_fdw/meson.build
+++ b/contrib/postgres_fdw/meson.build
@@ -17,7 +17,7 @@ endif
 postgres_fdw = shared_module('postgres_fdw',
   postgres_fdw_sources,
   kwargs: contrib_mod_args + {
-    'dependencies': contrib_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + contrib_mod_args['dependencies'],
   },
 )
 contrib_targets += postgres_fdw
diff --git a/contrib/vacuumlo/meson.build b/contrib/vacuumlo/meson.build
index deee1d2832d..51e8fe9a325 100644
--- a/contrib/vacuumlo/meson.build
+++ b/contrib/vacuumlo/meson.build
@@ -12,7 +12,7 @@ endif
 
 vacuumlo = executable('vacuumlo',
   vacuumlo_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 contrib_targets += vacuumlo
diff --git a/src/backend/replication/libpqwalreceiver/meson.build b/src/backend/replication/libpqwalreceiver/meson.build
index 2150f31cfa3..5318a76e29d 100644
--- a/src/backend/replication/libpqwalreceiver/meson.build
+++ b/src/backend/replication/libpqwalreceiver/meson.build
@@ -14,7 +14,7 @@ libpqwalreceiver = shared_module('pqwalreceiver',
   libpqwalreceiver_sources,
   kwargs: pg_mod_args + {
     'name_prefix': 'lib',
-    'dependencies': pg_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + pg_mod_args['dependencies'],
   }
 )
 
diff --git a/src/bin/initdb/meson.build b/src/bin/initdb/meson.build
index 06958e370f3..b8584fe56de 100644
--- a/src/bin/initdb/meson.build
+++ b/src/bin/initdb/meson.build
@@ -21,7 +21,7 @@ initdb = executable('initdb',
   # shared library from a different PG version.  Define
   # USE_PRIVATE_ENCODING_FUNCS to ensure that that happens.
   c_args: ['-DUSE_PRIVATE_ENCODING_FUNCS'],
-  dependencies: [frontend_code, libpq, icu, icu_i18n],
+  dependencies: [libpq, frontend_code, icu, icu_i18n],
   kwargs: default_bin_args,
 )
 bin_targets += initdb
diff --git a/src/bin/pg_amcheck/meson.build b/src/bin/pg_amcheck/meson.build
index 316ea0d40b8..c8f792ff971 100644
--- a/src/bin/pg_amcheck/meson.build
+++ b/src/bin/pg_amcheck/meson.build
@@ -12,7 +12,7 @@ endif
 
 pg_amcheck = executable('pg_amcheck',
   pg_amcheck_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_amcheck
diff --git a/src/bin/pg_basebackup/meson.build b/src/bin/pg_basebackup/meson.build
index 3a7fc10eab0..b85ba465d47 100644
--- a/src/bin/pg_basebackup/meson.build
+++ b/src/bin/pg_basebackup/meson.build
@@ -7,7 +7,7 @@ common_sources = files(
   'walmethods.c',
 )
 
-pg_basebackup_deps = [frontend_code, libpq, lz4, zlib, zstd]
+pg_basebackup_deps = [libpq, frontend_code, lz4, zlib, zstd]
 pg_basebackup_common = static_library('libpg_basebackup_common',
   common_sources,
   dependencies: pg_basebackup_deps,
@@ -45,7 +45,7 @@ endif
 
 pg_createsubscriber = executable('pg_createsubscriber',
   pg_createsubscriber_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_createsubscriber
diff --git a/src/bin/pg_combinebackup/meson.build b/src/bin/pg_combinebackup/meson.build
index e80a4756a7f..04e09162d9b 100644
--- a/src/bin/pg_combinebackup/meson.build
+++ b/src/bin/pg_combinebackup/meson.build
@@ -17,7 +17,7 @@ endif
 
 pg_combinebackup = executable('pg_combinebackup',
   pg_combinebackup_sources,
-  dependencies: [frontend_code],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_combinebackup
diff --git a/src/bin/pg_ctl/meson.build b/src/bin/pg_ctl/meson.build
index e92ba50f8a3..4f4a977dc0f 100644
--- a/src/bin/pg_ctl/meson.build
+++ b/src/bin/pg_ctl/meson.build
@@ -12,7 +12,7 @@ endif
 
 pg_ctl = executable('pg_ctl',
   pg_ctl_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_ctl
diff --git a/src/bin/pg_dump/meson.build b/src/bin/pg_dump/meson.build
index f3c669f484e..2264ed9faa9 100644
--- a/src/bin/pg_dump/meson.build
+++ b/src/bin/pg_dump/meson.build
@@ -22,7 +22,7 @@ pg_dump_common_sources = files(
 pg_dump_common = static_library('libpgdump_common',
   pg_dump_common_sources,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_code, libpq, lz4, zlib, zstd],
+  dependencies: [libpq, frontend_code, lz4, zlib, zstd],
   kwargs: internal_lib_args,
 )
 
@@ -42,7 +42,7 @@ endif
 pg_dump = executable('pg_dump',
   pg_dump_sources,
   link_with: [pg_dump_common],
-  dependencies: [frontend_code, libpq, zlib],
+  dependencies: [libpq, frontend_code, zlib],
   kwargs: default_bin_args,
 )
 bin_targets += pg_dump
@@ -61,7 +61,7 @@ endif
 pg_dumpall = executable('pg_dumpall',
   pg_dumpall_sources,
   link_with: [pg_dump_common],
-  dependencies: [frontend_code, libpq, zlib],
+  dependencies: [libpq, frontend_code, zlib],
   kwargs: default_bin_args,
 )
 bin_targets += pg_dumpall
@@ -80,7 +80,7 @@ endif
 pg_restore = executable('pg_restore',
   pg_restore_sources,
   link_with: [pg_dump_common],
-  dependencies: [frontend_code, libpq, zlib],
+  dependencies: [libpq, frontend_code, zlib],
   kwargs: default_bin_args,
 )
 bin_targets += pg_restore
diff --git a/src/bin/pg_rewind/meson.build b/src/bin/pg_rewind/meson.build
index 97f001d94a5..f241f37673c 100644
--- a/src/bin/pg_rewind/meson.build
+++ b/src/bin/pg_rewind/meson.build
@@ -21,7 +21,7 @@ endif
 
 pg_rewind = executable('pg_rewind',
   pg_rewind_sources,
-  dependencies: [frontend_code, libpq, lz4, zstd],
+  dependencies: [libpq, frontend_code, lz4, zstd],
   c_args: ['-DFRONTEND'], # needed for xlogreader et al
   kwargs: default_bin_args,
 )
diff --git a/src/bin/pg_upgrade/meson.build b/src/bin/pg_upgrade/meson.build
index ac992f0d14b..8aefaeea91f 100644
--- a/src/bin/pg_upgrade/meson.build
+++ b/src/bin/pg_upgrade/meson.build
@@ -28,7 +28,7 @@ endif
 pg_upgrade = executable('pg_upgrade',
   pg_upgrade_sources,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_upgrade
diff --git a/src/bin/pg_verifybackup/meson.build b/src/bin/pg_verifybackup/meson.build
index f45ea790d8e..7aaee24bd79 100644
--- a/src/bin/pg_verifybackup/meson.build
+++ b/src/bin/pg_verifybackup/meson.build
@@ -13,7 +13,7 @@ endif
 
 pg_verifybackup = executable('pg_verifybackup',
   pg_verifybackup_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_verifybackup
diff --git a/src/bin/pgbench/meson.build b/src/bin/pgbench/meson.build
index a2a909a8442..de013cc9827 100644
--- a/src/bin/pgbench/meson.build
+++ b/src/bin/pgbench/meson.build
@@ -27,7 +27,7 @@ endif
 
 pgbench = executable('pgbench',
   pgbench_sources,
-  dependencies: [frontend_code, libpq, thread_dep],
+  dependencies: [libpq, frontend_code, thread_dep],
   include_directories: include_directories('.'),
   c_pch: pch_postgres_fe_h,
   c_args: host_system == 'windows' ? ['-DFD_SETSIZE=1024'] : [],
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index d344053c23b..4749d751aa8 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -58,7 +58,7 @@ psql = executable('psql',
   psql_sources,
   c_pch: pch_postgres_fe_h,
   include_directories: include_directories('.'),
-  dependencies: [frontend_code, libpq, readline],
+  dependencies: [libpq, frontend_code, readline],
   kwargs: default_bin_args,
 )
 bin_targets += psql
diff --git a/src/bin/scripts/meson.build b/src/bin/scripts/meson.build
index a4fed59d1c9..d422ca542f9 100644
--- a/src/bin/scripts/meson.build
+++ b/src/bin/scripts/meson.build
@@ -2,7 +2,7 @@
 
 scripts_common = static_library('libscripts_common',
   files('common.c'),
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: internal_lib_args,
 )
 
@@ -28,7 +28,7 @@ foreach binary : binaries
   binary = executable(binary,
     binary_sources,
     link_with: [scripts_common],
-    dependencies: [frontend_code, libpq],
+    dependencies: [libpq, frontend_code],
     kwargs: default_bin_args,
   )
   bin_targets += binary
@@ -36,7 +36,7 @@ endforeach
 
 vacuuming_common = static_library('libvacuuming_common',
   files('common.c', 'vacuuming.c'),
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: internal_lib_args,
 )
 
@@ -55,7 +55,7 @@ foreach binary : binaries
   binary = executable(binary,
     binary_sources,
     link_with: [vacuuming_common],
-    dependencies: [frontend_code, libpq],
+    dependencies: [libpq, frontend_code],
     kwargs: default_bin_args,
   )
   bin_targets += binary
diff --git a/src/fe_utils/meson.build b/src/fe_utils/meson.build
index ddac3c3a658..0a65b973779 100644
--- a/src/fe_utils/meson.build
+++ b/src/fe_utils/meson.build
@@ -32,7 +32,7 @@ fe_utils_sources += psqlscan
 fe_utils = static_library('libpgfeutils',
   fe_utils_sources,
   c_pch: pch_postgres_fe_h,
-  include_directories: [postgres_inc, libpq_inc],
+  include_directories: [libpq_inc, postgres_inc],
   c_args: host_system == 'windows' ? ['-DFD_SETSIZE=1024'] : [],
   dependencies: frontend_common_code,
   kwargs: default_lib_args,
diff --git a/src/interfaces/ecpg/ecpglib/meson.build b/src/interfaces/ecpg/ecpglib/meson.build
index 8f478c6a73e..fb7d33d2a0e 100644
--- a/src/interfaces/ecpg/ecpglib/meson.build
+++ b/src/interfaces/ecpg/ecpglib/meson.build
@@ -30,7 +30,7 @@ ecpglib_st = static_library('libecpg',
   include_directories: ecpglib_inc,
   c_args: ecpglib_c_args,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_stlib_code, thread_dep, libpq],
+  dependencies: [libpq, frontend_stlib_code, thread_dep],
   link_with: [ecpg_pgtypes_st],
   kwargs: default_lib_args,
 )
@@ -41,7 +41,7 @@ ecpglib_so = shared_library('libecpg',
   include_directories: ecpglib_inc,
   c_args: ecpglib_c_args,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_shlib_code, libpq, thread_dep],
+  dependencies: [libpq, frontend_shlib_code, thread_dep],
   link_with: ecpg_pgtypes_so,
   soversion: host_system != 'windows' ? '6' : '',
   darwin_versions: ['6', '6.' + pg_version_major.to_string()],
@@ -58,7 +58,7 @@ pkgconfig.generate(
   url: pg_url,
   libraries: ecpglib_so,
   libraries_private: [frontend_stlib_code, thread_dep],
-  requires_private: ['libpgtypes', 'libpq'],
+  requires_private: ['libpq', 'libpgtypes'],
 )
 
 subdir('po', if_found: libintl)
diff --git a/src/interfaces/ecpg/include/meson.build b/src/interfaces/ecpg/include/meson.build
index a6541e1a686..133ebe2f11f 100644
--- a/src/interfaces/ecpg/include/meson.build
+++ b/src/interfaces/ecpg/include/meson.build
@@ -1,6 +1,6 @@
 # Copyright (c) 2022-2025, PostgreSQL Global Development Group
 
-ecpg_inc = include_directories('.')
+ecpg_inc = [libpq_inc, include_directories('.')]
 
 ecpg_conf_keys = [
   'SIZEOF_LONG',
diff --git a/src/interfaces/ecpg/test/meson.build b/src/interfaces/ecpg/test/meson.build
index 4ccdf9d295a..0d48e035e5a 100644
--- a/src/interfaces/ecpg/test/meson.build
+++ b/src/interfaces/ecpg/test/meson.build
@@ -20,7 +20,7 @@ pg_regress_ecpg = executable('pg_regress_ecpg',
   pg_regress_ecpg_sources,
   c_args: pg_regress_cflags,
   include_directories: [pg_regress_inc, include_directories('.')],
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false
   },
@@ -30,7 +30,7 @@ ecpg_test_dependencies += pg_regress_ecpg
 # create .c files and executables from .pgc files
 ecpg_test_exec_kw = {
   'c_args': cflags_no_missing_var_decls,
-  'dependencies': [frontend_code, libpq],
+  'dependencies': [libpq, frontend_code],
   'include_directories': [ecpg_inc],
   'link_with': [ecpglib_so, ecpg_compat_so, ecpg_pgtypes_so],
   'build_by_default': false,
diff --git a/src/interfaces/ecpg/test/thread/meson.build b/src/interfaces/ecpg/test/thread/meson.build
index 6a6745ce0fc..2c34a611898 100644
--- a/src/interfaces/ecpg/test/thread/meson.build
+++ b/src/interfaces/ecpg/test/thread/meson.build
@@ -18,6 +18,6 @@ foreach pgc_file : pgc_files
 
   ecpg_test_dependencies += executable(pgc_file,
     exe_input,
-    kwargs: ecpg_test_exec_kw + {'dependencies': [frontend_code, libpq, thread_dep,]},
+    kwargs: ecpg_test_exec_kw + {'dependencies': [libpq, frontend_code, thread_dep,]},
   )
 endforeach
diff --git a/src/interfaces/libpq/test/meson.build b/src/interfaces/libpq/test/meson.build
index 07a5facc321..75d2012ba17 100644
--- a/src/interfaces/libpq/test/meson.build
+++ b/src/interfaces/libpq/test/meson.build
@@ -14,7 +14,7 @@ endif
 
 libpq_test_deps += executable('libpq_uri_regress',
   libpq_uri_regress_sources,
-  dependencies: [frontend_no_fe_utils_code, libpq],
+  dependencies: [libpq, frontend_no_fe_utils_code],
   kwargs: default_bin_args + {
     'install': false,
   }
@@ -33,7 +33,7 @@ endif
 
 libpq_test_deps += executable('libpq_testclient',
   libpq_testclient_sources,
-  dependencies: [frontend_no_fe_utils_code, libpq],
+  dependencies: [libpq, frontend_no_fe_utils_code],
   kwargs: default_bin_args + {
     'install': false,
   }
diff --git a/src/test/isolation/meson.build b/src/test/isolation/meson.build
index a180e4e2741..660b11eff8b 100644
--- a/src/test/isolation/meson.build
+++ b/src/test/isolation/meson.build
@@ -35,7 +35,7 @@ pg_isolation_regress = executable('pg_isolation_regress',
   isolation_sources,
   c_args: pg_regress_cflags,
   include_directories: pg_regress_inc,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install_dir': dir_pgxs / 'src/test/isolation',
   },
@@ -52,7 +52,7 @@ endif
 isolationtester = executable('isolationtester',
   isolationtester_sources,
   include_directories: include_directories('.'),
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install_dir': dir_pgxs / 'src/test/isolation',
   },
diff --git a/src/test/modules/libpq_pipeline/meson.build b/src/test/modules/libpq_pipeline/meson.build
index 3fd70a04a38..83201111ca3 100644
--- a/src/test/modules/libpq_pipeline/meson.build
+++ b/src/test/modules/libpq_pipeline/meson.build
@@ -12,7 +12,7 @@ endif
 
 libpq_pipeline = executable('libpq_pipeline',
   libpq_pipeline_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   },
diff --git a/src/test/modules/oauth_validator/meson.build b/src/test/modules/oauth_validator/meson.build
index a6f937fd7d7..2cd5c4cd537 100644
--- a/src/test/modules/oauth_validator/meson.build
+++ b/src/test/modules/oauth_validator/meson.build
@@ -60,7 +60,7 @@ endif
 
 oauth_hook_client = executable('oauth_hook_client',
   oauth_hook_client_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   },
diff --git a/src/test/modules/test_escape/meson.build b/src/test/modules/test_escape/meson.build
index a21341d5067..04fd0be0889 100644
--- a/src/test/modules/test_escape/meson.build
+++ b/src/test/modules/test_escape/meson.build
@@ -10,7 +10,7 @@ endif
 
 test_escape = executable('test_escape',
   test_escape_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   }
diff --git a/src/test/modules/test_int128/meson.build b/src/test/modules/test_int128/meson.build
index 4c2be7a0326..94bca2edc5c 100644
--- a/src/test/modules/test_int128/meson.build
+++ b/src/test/modules/test_int128/meson.build
@@ -12,7 +12,7 @@ endif
 
 test_int128 = executable('test_int128',
   test_int128_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   },
diff --git a/src/test/modules/test_json_parser/meson.build b/src/test/modules/test_json_parser/meson.build
index 5672045f496..2c6031a14df 100644
--- a/src/test/modules/test_json_parser/meson.build
+++ b/src/test/modules/test_json_parser/meson.build
@@ -23,7 +23,7 @@ test_json_parser_incremental = executable('test_json_parser_incremental',
 # the shared-library flavor of jsonapi.
 test_json_parser_incremental_shlib = executable('test_json_parser_incremental_shlib',
   test_json_parser_incremental_sources,
-  dependencies: [frontend_shlib_code, libpq],
+  dependencies: [libpq, frontend_shlib_code],
   c_args: ['-DJSONAPI_SHLIB_ALLOC'],
   link_with: [common_excluded_shlib],
   kwargs: default_bin_args + {
diff --git a/src/test/modules/test_oat_hooks/meson.build b/src/test/modules/test_oat_hooks/meson.build
index 1e600b0f4c7..6f0c4afd433 100644
--- a/src/test/modules/test_oat_hooks/meson.build
+++ b/src/test/modules/test_oat_hooks/meson.build
@@ -12,7 +12,9 @@ endif
 
 test_oat_hooks = shared_module('test_oat_hooks',
   test_oat_hooks_sources,
-  kwargs: pg_test_mod_args,
+  kwargs: pg_test_mod_args + {
+	'dependencies': [libpq] + pg_test_mod_args['dependencies'],
+  }
 )
 test_install_libs += test_oat_hooks
 
diff --git a/src/test/regress/meson.build b/src/test/regress/meson.build
index 1da9e9462a9..99f402f49a8 100644
--- a/src/test/regress/meson.build
+++ b/src/test/regress/meson.build
@@ -2,7 +2,7 @@
 
 # also used by isolationtester and ecpg tests
 pg_regress_c = files('pg_regress.c')
-pg_regress_inc = include_directories('.')
+pg_regress_inc = [libpq_inc, include_directories('.')]
 
 regress_sources = pg_regress_c + files(
   'pg_regress_main.c'
@@ -30,7 +30,7 @@ endif
 pg_regress = executable('pg_regress',
   regress_sources,
   c_args: pg_regress_cflags,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install_dir': dir_pgxs / 'src/test/regress',
   },
-- 
2.51.1

v3-0002-ci-Test-include-path-order-with-decoy-libpq-fe.h.patchtext/x-patch; charset=US-ASCII; name=v3-0002-ci-Test-include-path-order-with-decoy-libpq-fe.h.patchDownload
From 23df76b10985b3624a71e9357b98a6cccf377542 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 3 Nov 2025 11:32:57 +1300
Subject: [PATCH v3 2/2] ci: Test include path order with decoy libpq-fe.h.

If the configure or meson scripts would allow headers from a copy of
libpq installed under --with-includes or -Dextra_include_dirs to be
found before the in-tree copy we need, then this canary header will
raise an error.

This problem has bitten us in both build systems, but often goes
unnoticed until libpq-fe.h changes incompatibly and someone with an
affected system notices.  This test would cause CI to fail fast.

Also add missing set -e to the shell scripts run under su, since
otherwise the build step would confusingly succeed.

Discussion: https://postgr.es/m/CA%2BhUKG%2Bx-cys30%3D7L2B8%3DcZ%2B-z6QDOj-oQy9O3CnkeXnrnm3OQ%40mail.gmail.com
---
 .cirrus.tasks.yml | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/.cirrus.tasks.yml b/.cirrus.tasks.yml
index f2581cfb2e5..3df6de6acd9 100644
--- a/.cirrus.tasks.yml
+++ b/.cirrus.tasks.yml
@@ -424,6 +424,8 @@ task:
     CCACHE_DIR: /tmp/ccache_dir
     DEBUGINFOD_URLS: "https://debuginfod.debian.net"
 
+    POISONED_HEADERS: /tmp/poisoned_headers
+
     # Enable a reasonable set of sanitizers. Use the linux task for that, as
     # it's one of the fastest tasks (without sanitizers). Also several of the
     # sanitizers work best on linux.
@@ -492,11 +494,24 @@ task:
     #apt-get update
     #DEBIAN_FRONTEND=noninteractive apt-get -y install ...
 
+  # Detect mistakes that would allow unwanted headers from outside our tree be
+  # found with --with-includes or -Dextra_include_dirs.  That is primarily a
+  # risk on non-Linux systems that install packages under eg /usr/local that
+  # must be explicitly added to the header search path, but this is a
+  # convenient place to test both build systems.
+  setup_poisoned_headers_script: |
+    for header in "libpq-fe.h" "libpq/libpq-fe.h" ; do
+      mkdir -p "${POISONED_HEADERS}/$(dirname $header)"
+      echo '#error "external header hides in-tree header"' \
+      > "${POISONED_HEADERS}/$header"
+    done
+
   matrix:
     # SPECIAL:
     # - Uses address sanitizer, sanitizer failures are typically printed in
     #   the server log
     # - Configures postgres with a small segment size
+    # - Poisoned headers in search path
     - name: Linux - Debian Bookworm - Autoconf
 
       env:
@@ -517,6 +532,7 @@ task:
             --with-segsize-blocks=6 \
             --with-libnuma \
             --with-liburing \
+            --with-includes="${POISONED_HEADERS}" \
             \
             ${LINUX_CONFIGURE_FEATURES} \
             \
@@ -540,6 +556,7 @@ task:
     # - Test both 64bit and 32 bit builds
     # - uses io_method=io_uring
     # - Uses meson feature autodetection
+    # - Poisoned headers in search path
     - name: Linux - Debian Bookworm - Meson
 
       env:
@@ -552,6 +569,7 @@ task:
         su postgres <<-EOF
           meson setup \
             ${MESON_COMMON_PG_CONFIG_ARGS} \
+            -Dextra_include_dirs="${POISONED_HEADERS}" \
             --buildtype=debug \
             ${LINUX_MESON_FEATURES} -Dllvm=enabled \
             build
@@ -564,6 +582,7 @@ task:
           export CC='ccache gcc -m32'
           meson setup \
             ${MESON_COMMON_PG_CONFIG_ARGS} \
+            -Dextra_include_dirs="${POISONED_HEADERS}" \
             --buildtype=debug \
             --pkg-config-path /usr/lib/i386-linux-gnu/pkgconfig/ \
             -DPERL=perl5.36-i386-linux-gnu \
@@ -573,12 +592,14 @@ task:
 
       build_script: |
         su postgres <<-EOF
+          set -e
           ninja -C build -j${BUILD_JOBS} ${MBUILD_TARGET}
           ninja -C build -t missingdeps
         EOF
 
       build_32_script: |
         su postgres <<-EOF
+          set -e
           ninja -C build-32 -j${BUILD_JOBS} ${MBUILD_TARGET}
           ninja -C build -t missingdeps
         EOF
-- 
2.51.1

#10Tristan Partin
tristan@partin.io
In reply to: Thomas Munro (#1)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On Sun Feb 2, 2025 at 6:29 PM CST, Thomas Munro wrote:

When I use configure/make and --with-includes=/usr/local/include, I
see compile lines like this:

... -I../../src/interfaces/libpq -I../../src/include -I/usr/local/include ...

That's important, because if I happen to have libpq headers installed
on the system I don't want to be confused by them. Yet meson's
-Dextra_include_dirs=/usr/local/include compiles like this:

... -I../src/include -I/usr/local/include -Isrc/interfaces/libpq ...

... which gives me errors due to the v16 headers I happen to have installed:

../src/fe_utils/connect_utils.c:164:3: error: unknown type name
'PGcancelConn'; did you mean 'PGcancel'?
164 | PGcancelConn *cancelConn = PQcancelCreate(conn);

I guess this affects Macs, BSDs and a few lesser used Linux distros
that put optional packages outside the base system and default search
paths. Perhaps you can see this on everything-goes-into-base-system
distros if you redundantly say -Dextra_include_dirs=/usr/include; I
didn't check if that is true, it wouldn't be if meson is opinionated
enough to remove it.

What is the benefit of -Dextra_include_dirs when you could use -Dc_flags
or CFLAGS to specify extra include directories? If you tried either of
those as an alternative, would they fix the issue? The existence of the
option goes all the way back to the initial commit of the meson port, so
I presume it exists to mirror --with-includes, but why does that exist?

--
Tristan Partin
Databricks (https://databricks.com)

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Tristan Partin (#10)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

"Tristan Partin" <tristan@partin.io> writes:

What is the benefit of -Dextra_include_dirs when you could use -Dc_flags
or CFLAGS to specify extra include directories? If you tried either of
those as an alternative, would they fix the issue? The existence of the
option goes all the way back to the initial commit of the meson port, so
I presume it exists to mirror --with-includes, but why does that exist?

--with-includes, and likewise --with-libs, exist because there are
platforms that require it. For example, on pretty much any
BSD-derived system, the core /usr/include and /usr/lib files are very
limited and you'll need to point at places like /usr/pkg/include or
/opt/local/include or whatever to pull in non-core packages.

I see your point about putting -I flags into CFLAGS, but what you
are missing is that the order of these flags is unbelievably critical,
so "just shove it into CFLAGS" is a recipe for failure, especially
if you haven't even stopped to think about which end to add it at.
We've learned over decades of trial-and-error with the makefile system
that treating -I and -L flags specially is more reliable. (See for
example all the logic in our autoconf scripts to forcibly separate
-I and -L out of sources like pkg-config. Tracing that stuff back to
the originating commits and mail discussions would be a constructive
learning exercise.)

I'm not sure that the meson crew have learned all that yet.
But this does not suggest to me that they know more than we do.

regards, tom lane

#12Thomas Munro
thomas.munro@gmail.com
In reply to: Tom Lane (#11)
4 attachment(s)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On Mon, Nov 3, 2025 at 7:14 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

"Tristan Partin" <tristan@partin.io> writes:

What is the benefit of -Dextra_include_dirs when you could use -Dc_flags
or CFLAGS to specify extra include directories? If you tried either of
those as an alternative, would they fix the issue? The existence of the
option goes all the way back to the initial commit of the meson port, so
I presume it exists to mirror --with-includes, but why does that exist?

Good questions. Sounds plausible and probably more conventional. It
doesn't work right now but probably only for superficial reasons. But
I also worry about hiding current or future problems and generally
being too blunt. But that's also true of -Dextra_XXX.

--with-includes, and likewise --with-libs, exist because there are
platforms that require it. For example, on pretty much any
BSD-derived system, the core /usr/include and /usr/lib files are very
limited and you'll need to point at places like /usr/pkg/include or
/opt/local/include or whatever to pull in non-core packages.

Yeah. I for one cargo-culted that configure habit across to the new
world, but you can actually build without -Dextra_XXX on FreeBSD and
maybe macOS + MacPorts too with -Dnls=disabled, as long as your $PATH
contains pkg-config (FreeBSD: yes by default, macOS: probably yes if
you can type "meson"). NetBSD handles even -Dnls=enabled thanks to
its built-in libintl, as CI already demonstrates. So I wonder if we
have this only because out-of-libc libintl and pkg-config have some
unresolved beef[1]https://lists.gnu.org/archive/html/bug-gettext/2012-03/msg00022.html[2]https://lists.nongnu.org/archive/html/bug-gettext/2019-10/msg00003.html...? Know of any other complications in our
universe or libraries that I might be overlooking?

If that really is the last reason we need this on typical
package-managed non-Linux environments (not sure!)... here's an
experimental patch that probes the conventional paths relative to
msgfmt to hunt for them. They were all compiled from the same source
package by the same packaging pipeline so I think it has pretty good
odds. It's not quite $PATH -> pkg-config -> .pc -> exact results, but
it's ... well, something vaguely similar, instead of nothing. Too
crazy an overreaching assumption, or friendly out-of-the-box support
for lots of common systems including Macs? It passes on all CI OSes.
No loss if it guesses wrong on your DeathStation 9000: you can still
supply -Dextra_XXX as before.

On the flip side, if you stop using a "catch-all" include path, you
have to declare dependencies accurately or some combinations might
break, for example that gssapi I had to add, because the CI macOS task
found Apple's base system OpenSSL and then didn't think it needed any
extra includes, because we forgot to say that libpq_oauth_st also
depends on gssapi. That's an example of a "broad"
-Dextra_include_dirs hiding something that is technically incorrect,
or being forgiving depending on your perspective...

First two patches as before, except for a couple of unnecessary hunks
I deleted based on an off-list review from Bilal.

[1]: https://lists.gnu.org/archive/html/bug-gettext/2012-03/msg00022.html
[2]: https://lists.nongnu.org/archive/html/bug-gettext/2019-10/msg00003.html

Attachments:

v4-0001-meson-Fix-libpq-header-include-order.patchapplication/x-patch; name=v4-0001-meson-Fix-libpq-header-include-order.patchDownload
From 172ebde3ea93a9c9bd3db8a134cd873ca3daedda Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 14 Jul 2025 11:54:00 +1200
Subject: [PATCH v4 1/4] meson: Fix libpq header include order.

Don't allow external copies of libpq-fe.h to hide the in-tree libpq's
headers.  libpq is now always declared as the first dependency, so that
it can arrange for libpq_inc to be searched before extra_include_dirs,
c_flags or system/ports include directory discovered by Meson.

This is mostly a mechanical change to multiple dependency lists.  A
separate commit will add a CI check to prevent future mistakes.

This clearly didn't affect many people, maybe just me (Thomas), but it
could have broken on basically any non-Linux system that happened to
install a libpq-with-headers package.

Back-patch to 16, where meson arrived.

Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>
Reviewed-by: Bilal Yavuz <byavuz81@gmail.com>
Reviewed-by: Tristan Partin <tristan@partin.io>
Discussion: https://postgr.es/m/CA+hUKGKispvxLyrBn3=3mp0BB1N+RBYR5eE2guCOksnwEoOcPQ@mail.gmail.com
---
 contrib/dblink/meson.build                           | 2 +-
 contrib/oid2name/meson.build                         | 2 +-
 contrib/postgres_fdw/meson.build                     | 2 +-
 contrib/vacuumlo/meson.build                         | 2 +-
 src/backend/replication/libpqwalreceiver/meson.build | 2 +-
 src/bin/initdb/meson.build                           | 2 +-
 src/bin/pg_amcheck/meson.build                       | 2 +-
 src/bin/pg_basebackup/meson.build                    | 4 ++--
 src/bin/pg_ctl/meson.build                           | 2 +-
 src/bin/pg_dump/meson.build                          | 8 ++++----
 src/bin/pg_rewind/meson.build                        | 2 +-
 src/bin/pg_upgrade/meson.build                       | 2 +-
 src/bin/pg_verifybackup/meson.build                  | 2 +-
 src/bin/pgbench/meson.build                          | 2 +-
 src/bin/psql/meson.build                             | 2 +-
 src/bin/scripts/meson.build                          | 8 ++++----
 src/fe_utils/meson.build                             | 2 +-
 src/interfaces/ecpg/ecpglib/meson.build              | 6 +++---
 src/interfaces/ecpg/include/meson.build              | 2 +-
 src/interfaces/ecpg/test/meson.build                 | 4 ++--
 src/interfaces/ecpg/test/thread/meson.build          | 2 +-
 src/interfaces/libpq/test/meson.build                | 4 ++--
 src/test/isolation/meson.build                       | 4 ++--
 src/test/modules/libpq_pipeline/meson.build          | 2 +-
 src/test/modules/oauth_validator/meson.build         | 2 +-
 src/test/modules/test_escape/meson.build             | 2 +-
 src/test/modules/test_int128/meson.build             | 2 +-
 src/test/modules/test_json_parser/meson.build        | 2 +-
 src/test/regress/meson.build                         | 2 +-
 29 files changed, 41 insertions(+), 41 deletions(-)

diff --git a/contrib/dblink/meson.build b/contrib/dblink/meson.build
index a19ce6cf4b9..28da26d3d45 100644
--- a/contrib/dblink/meson.build
+++ b/contrib/dblink/meson.build
@@ -13,7 +13,7 @@ endif
 dblink = shared_module('dblink',
   dblink_sources,
   kwargs: contrib_mod_args + {
-    'dependencies': contrib_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + contrib_mod_args['dependencies'],
   },
 )
 contrib_targets += dblink
diff --git a/contrib/oid2name/meson.build b/contrib/oid2name/meson.build
index 074f16acd72..dd958f00bdf 100644
--- a/contrib/oid2name/meson.build
+++ b/contrib/oid2name/meson.build
@@ -12,7 +12,7 @@ endif
 
 oid2name = executable('oid2name',
   oid2name_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 contrib_targets += oid2name
diff --git a/contrib/postgres_fdw/meson.build b/contrib/postgres_fdw/meson.build
index aac89ffdde8..a2890251d69 100644
--- a/contrib/postgres_fdw/meson.build
+++ b/contrib/postgres_fdw/meson.build
@@ -17,7 +17,7 @@ endif
 postgres_fdw = shared_module('postgres_fdw',
   postgres_fdw_sources,
   kwargs: contrib_mod_args + {
-    'dependencies': contrib_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + contrib_mod_args['dependencies'],
   },
 )
 contrib_targets += postgres_fdw
diff --git a/contrib/vacuumlo/meson.build b/contrib/vacuumlo/meson.build
index deee1d2832d..51e8fe9a325 100644
--- a/contrib/vacuumlo/meson.build
+++ b/contrib/vacuumlo/meson.build
@@ -12,7 +12,7 @@ endif
 
 vacuumlo = executable('vacuumlo',
   vacuumlo_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 contrib_targets += vacuumlo
diff --git a/src/backend/replication/libpqwalreceiver/meson.build b/src/backend/replication/libpqwalreceiver/meson.build
index 2150f31cfa3..5318a76e29d 100644
--- a/src/backend/replication/libpqwalreceiver/meson.build
+++ b/src/backend/replication/libpqwalreceiver/meson.build
@@ -14,7 +14,7 @@ libpqwalreceiver = shared_module('pqwalreceiver',
   libpqwalreceiver_sources,
   kwargs: pg_mod_args + {
     'name_prefix': 'lib',
-    'dependencies': pg_mod_args['dependencies'] + [libpq],
+    'dependencies': [libpq] + pg_mod_args['dependencies'],
   }
 )
 
diff --git a/src/bin/initdb/meson.build b/src/bin/initdb/meson.build
index 06958e370f3..b8584fe56de 100644
--- a/src/bin/initdb/meson.build
+++ b/src/bin/initdb/meson.build
@@ -21,7 +21,7 @@ initdb = executable('initdb',
   # shared library from a different PG version.  Define
   # USE_PRIVATE_ENCODING_FUNCS to ensure that that happens.
   c_args: ['-DUSE_PRIVATE_ENCODING_FUNCS'],
-  dependencies: [frontend_code, libpq, icu, icu_i18n],
+  dependencies: [libpq, frontend_code, icu, icu_i18n],
   kwargs: default_bin_args,
 )
 bin_targets += initdb
diff --git a/src/bin/pg_amcheck/meson.build b/src/bin/pg_amcheck/meson.build
index 316ea0d40b8..c8f792ff971 100644
--- a/src/bin/pg_amcheck/meson.build
+++ b/src/bin/pg_amcheck/meson.build
@@ -12,7 +12,7 @@ endif
 
 pg_amcheck = executable('pg_amcheck',
   pg_amcheck_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_amcheck
diff --git a/src/bin/pg_basebackup/meson.build b/src/bin/pg_basebackup/meson.build
index 3a7fc10eab0..b85ba465d47 100644
--- a/src/bin/pg_basebackup/meson.build
+++ b/src/bin/pg_basebackup/meson.build
@@ -7,7 +7,7 @@ common_sources = files(
   'walmethods.c',
 )
 
-pg_basebackup_deps = [frontend_code, libpq, lz4, zlib, zstd]
+pg_basebackup_deps = [libpq, frontend_code, lz4, zlib, zstd]
 pg_basebackup_common = static_library('libpg_basebackup_common',
   common_sources,
   dependencies: pg_basebackup_deps,
@@ -45,7 +45,7 @@ endif
 
 pg_createsubscriber = executable('pg_createsubscriber',
   pg_createsubscriber_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_createsubscriber
diff --git a/src/bin/pg_ctl/meson.build b/src/bin/pg_ctl/meson.build
index e92ba50f8a3..4f4a977dc0f 100644
--- a/src/bin/pg_ctl/meson.build
+++ b/src/bin/pg_ctl/meson.build
@@ -12,7 +12,7 @@ endif
 
 pg_ctl = executable('pg_ctl',
   pg_ctl_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_ctl
diff --git a/src/bin/pg_dump/meson.build b/src/bin/pg_dump/meson.build
index f3c669f484e..2264ed9faa9 100644
--- a/src/bin/pg_dump/meson.build
+++ b/src/bin/pg_dump/meson.build
@@ -22,7 +22,7 @@ pg_dump_common_sources = files(
 pg_dump_common = static_library('libpgdump_common',
   pg_dump_common_sources,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_code, libpq, lz4, zlib, zstd],
+  dependencies: [libpq, frontend_code, lz4, zlib, zstd],
   kwargs: internal_lib_args,
 )
 
@@ -42,7 +42,7 @@ endif
 pg_dump = executable('pg_dump',
   pg_dump_sources,
   link_with: [pg_dump_common],
-  dependencies: [frontend_code, libpq, zlib],
+  dependencies: [libpq, frontend_code, zlib],
   kwargs: default_bin_args,
 )
 bin_targets += pg_dump
@@ -61,7 +61,7 @@ endif
 pg_dumpall = executable('pg_dumpall',
   pg_dumpall_sources,
   link_with: [pg_dump_common],
-  dependencies: [frontend_code, libpq, zlib],
+  dependencies: [libpq, frontend_code, zlib],
   kwargs: default_bin_args,
 )
 bin_targets += pg_dumpall
@@ -80,7 +80,7 @@ endif
 pg_restore = executable('pg_restore',
   pg_restore_sources,
   link_with: [pg_dump_common],
-  dependencies: [frontend_code, libpq, zlib],
+  dependencies: [libpq, frontend_code, zlib],
   kwargs: default_bin_args,
 )
 bin_targets += pg_restore
diff --git a/src/bin/pg_rewind/meson.build b/src/bin/pg_rewind/meson.build
index 97f001d94a5..f241f37673c 100644
--- a/src/bin/pg_rewind/meson.build
+++ b/src/bin/pg_rewind/meson.build
@@ -21,7 +21,7 @@ endif
 
 pg_rewind = executable('pg_rewind',
   pg_rewind_sources,
-  dependencies: [frontend_code, libpq, lz4, zstd],
+  dependencies: [libpq, frontend_code, lz4, zstd],
   c_args: ['-DFRONTEND'], # needed for xlogreader et al
   kwargs: default_bin_args,
 )
diff --git a/src/bin/pg_upgrade/meson.build b/src/bin/pg_upgrade/meson.build
index ac992f0d14b..8aefaeea91f 100644
--- a/src/bin/pg_upgrade/meson.build
+++ b/src/bin/pg_upgrade/meson.build
@@ -28,7 +28,7 @@ endif
 pg_upgrade = executable('pg_upgrade',
   pg_upgrade_sources,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_upgrade
diff --git a/src/bin/pg_verifybackup/meson.build b/src/bin/pg_verifybackup/meson.build
index f45ea790d8e..7aaee24bd79 100644
--- a/src/bin/pg_verifybackup/meson.build
+++ b/src/bin/pg_verifybackup/meson.build
@@ -13,7 +13,7 @@ endif
 
 pg_verifybackup = executable('pg_verifybackup',
   pg_verifybackup_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args,
 )
 bin_targets += pg_verifybackup
diff --git a/src/bin/pgbench/meson.build b/src/bin/pgbench/meson.build
index a2a909a8442..de013cc9827 100644
--- a/src/bin/pgbench/meson.build
+++ b/src/bin/pgbench/meson.build
@@ -27,7 +27,7 @@ endif
 
 pgbench = executable('pgbench',
   pgbench_sources,
-  dependencies: [frontend_code, libpq, thread_dep],
+  dependencies: [libpq, frontend_code, thread_dep],
   include_directories: include_directories('.'),
   c_pch: pch_postgres_fe_h,
   c_args: host_system == 'windows' ? ['-DFD_SETSIZE=1024'] : [],
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index d344053c23b..4749d751aa8 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -58,7 +58,7 @@ psql = executable('psql',
   psql_sources,
   c_pch: pch_postgres_fe_h,
   include_directories: include_directories('.'),
-  dependencies: [frontend_code, libpq, readline],
+  dependencies: [libpq, frontend_code, readline],
   kwargs: default_bin_args,
 )
 bin_targets += psql
diff --git a/src/bin/scripts/meson.build b/src/bin/scripts/meson.build
index a4fed59d1c9..d422ca542f9 100644
--- a/src/bin/scripts/meson.build
+++ b/src/bin/scripts/meson.build
@@ -2,7 +2,7 @@
 
 scripts_common = static_library('libscripts_common',
   files('common.c'),
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: internal_lib_args,
 )
 
@@ -28,7 +28,7 @@ foreach binary : binaries
   binary = executable(binary,
     binary_sources,
     link_with: [scripts_common],
-    dependencies: [frontend_code, libpq],
+    dependencies: [libpq, frontend_code],
     kwargs: default_bin_args,
   )
   bin_targets += binary
@@ -36,7 +36,7 @@ endforeach
 
 vacuuming_common = static_library('libvacuuming_common',
   files('common.c', 'vacuuming.c'),
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: internal_lib_args,
 )
 
@@ -55,7 +55,7 @@ foreach binary : binaries
   binary = executable(binary,
     binary_sources,
     link_with: [vacuuming_common],
-    dependencies: [frontend_code, libpq],
+    dependencies: [libpq, frontend_code],
     kwargs: default_bin_args,
   )
   bin_targets += binary
diff --git a/src/fe_utils/meson.build b/src/fe_utils/meson.build
index ddac3c3a658..0a65b973779 100644
--- a/src/fe_utils/meson.build
+++ b/src/fe_utils/meson.build
@@ -32,7 +32,7 @@ fe_utils_sources += psqlscan
 fe_utils = static_library('libpgfeutils',
   fe_utils_sources,
   c_pch: pch_postgres_fe_h,
-  include_directories: [postgres_inc, libpq_inc],
+  include_directories: [libpq_inc, postgres_inc],
   c_args: host_system == 'windows' ? ['-DFD_SETSIZE=1024'] : [],
   dependencies: frontend_common_code,
   kwargs: default_lib_args,
diff --git a/src/interfaces/ecpg/ecpglib/meson.build b/src/interfaces/ecpg/ecpglib/meson.build
index 8f478c6a73e..fb7d33d2a0e 100644
--- a/src/interfaces/ecpg/ecpglib/meson.build
+++ b/src/interfaces/ecpg/ecpglib/meson.build
@@ -30,7 +30,7 @@ ecpglib_st = static_library('libecpg',
   include_directories: ecpglib_inc,
   c_args: ecpglib_c_args,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_stlib_code, thread_dep, libpq],
+  dependencies: [libpq, frontend_stlib_code, thread_dep],
   link_with: [ecpg_pgtypes_st],
   kwargs: default_lib_args,
 )
@@ -41,7 +41,7 @@ ecpglib_so = shared_library('libecpg',
   include_directories: ecpglib_inc,
   c_args: ecpglib_c_args,
   c_pch: pch_postgres_fe_h,
-  dependencies: [frontend_shlib_code, libpq, thread_dep],
+  dependencies: [libpq, frontend_shlib_code, thread_dep],
   link_with: ecpg_pgtypes_so,
   soversion: host_system != 'windows' ? '6' : '',
   darwin_versions: ['6', '6.' + pg_version_major.to_string()],
@@ -58,7 +58,7 @@ pkgconfig.generate(
   url: pg_url,
   libraries: ecpglib_so,
   libraries_private: [frontend_stlib_code, thread_dep],
-  requires_private: ['libpgtypes', 'libpq'],
+  requires_private: ['libpq', 'libpgtypes'],
 )
 
 subdir('po', if_found: libintl)
diff --git a/src/interfaces/ecpg/include/meson.build b/src/interfaces/ecpg/include/meson.build
index a6541e1a686..133ebe2f11f 100644
--- a/src/interfaces/ecpg/include/meson.build
+++ b/src/interfaces/ecpg/include/meson.build
@@ -1,6 +1,6 @@
 # Copyright (c) 2022-2025, PostgreSQL Global Development Group
 
-ecpg_inc = include_directories('.')
+ecpg_inc = [libpq_inc, include_directories('.')]
 
 ecpg_conf_keys = [
   'SIZEOF_LONG',
diff --git a/src/interfaces/ecpg/test/meson.build b/src/interfaces/ecpg/test/meson.build
index 4ccdf9d295a..0d48e035e5a 100644
--- a/src/interfaces/ecpg/test/meson.build
+++ b/src/interfaces/ecpg/test/meson.build
@@ -20,7 +20,7 @@ pg_regress_ecpg = executable('pg_regress_ecpg',
   pg_regress_ecpg_sources,
   c_args: pg_regress_cflags,
   include_directories: [pg_regress_inc, include_directories('.')],
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false
   },
@@ -30,7 +30,7 @@ ecpg_test_dependencies += pg_regress_ecpg
 # create .c files and executables from .pgc files
 ecpg_test_exec_kw = {
   'c_args': cflags_no_missing_var_decls,
-  'dependencies': [frontend_code, libpq],
+  'dependencies': [libpq, frontend_code],
   'include_directories': [ecpg_inc],
   'link_with': [ecpglib_so, ecpg_compat_so, ecpg_pgtypes_so],
   'build_by_default': false,
diff --git a/src/interfaces/ecpg/test/thread/meson.build b/src/interfaces/ecpg/test/thread/meson.build
index 6a6745ce0fc..2c34a611898 100644
--- a/src/interfaces/ecpg/test/thread/meson.build
+++ b/src/interfaces/ecpg/test/thread/meson.build
@@ -18,6 +18,6 @@ foreach pgc_file : pgc_files
 
   ecpg_test_dependencies += executable(pgc_file,
     exe_input,
-    kwargs: ecpg_test_exec_kw + {'dependencies': [frontend_code, libpq, thread_dep,]},
+    kwargs: ecpg_test_exec_kw + {'dependencies': [libpq, frontend_code, thread_dep,]},
   )
 endforeach
diff --git a/src/interfaces/libpq/test/meson.build b/src/interfaces/libpq/test/meson.build
index 07a5facc321..75d2012ba17 100644
--- a/src/interfaces/libpq/test/meson.build
+++ b/src/interfaces/libpq/test/meson.build
@@ -14,7 +14,7 @@ endif
 
 libpq_test_deps += executable('libpq_uri_regress',
   libpq_uri_regress_sources,
-  dependencies: [frontend_no_fe_utils_code, libpq],
+  dependencies: [libpq, frontend_no_fe_utils_code],
   kwargs: default_bin_args + {
     'install': false,
   }
@@ -33,7 +33,7 @@ endif
 
 libpq_test_deps += executable('libpq_testclient',
   libpq_testclient_sources,
-  dependencies: [frontend_no_fe_utils_code, libpq],
+  dependencies: [libpq, frontend_no_fe_utils_code],
   kwargs: default_bin_args + {
     'install': false,
   }
diff --git a/src/test/isolation/meson.build b/src/test/isolation/meson.build
index a180e4e2741..660b11eff8b 100644
--- a/src/test/isolation/meson.build
+++ b/src/test/isolation/meson.build
@@ -35,7 +35,7 @@ pg_isolation_regress = executable('pg_isolation_regress',
   isolation_sources,
   c_args: pg_regress_cflags,
   include_directories: pg_regress_inc,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install_dir': dir_pgxs / 'src/test/isolation',
   },
@@ -52,7 +52,7 @@ endif
 isolationtester = executable('isolationtester',
   isolationtester_sources,
   include_directories: include_directories('.'),
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install_dir': dir_pgxs / 'src/test/isolation',
   },
diff --git a/src/test/modules/libpq_pipeline/meson.build b/src/test/modules/libpq_pipeline/meson.build
index 3fd70a04a38..83201111ca3 100644
--- a/src/test/modules/libpq_pipeline/meson.build
+++ b/src/test/modules/libpq_pipeline/meson.build
@@ -12,7 +12,7 @@ endif
 
 libpq_pipeline = executable('libpq_pipeline',
   libpq_pipeline_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   },
diff --git a/src/test/modules/oauth_validator/meson.build b/src/test/modules/oauth_validator/meson.build
index a6f937fd7d7..2cd5c4cd537 100644
--- a/src/test/modules/oauth_validator/meson.build
+++ b/src/test/modules/oauth_validator/meson.build
@@ -60,7 +60,7 @@ endif
 
 oauth_hook_client = executable('oauth_hook_client',
   oauth_hook_client_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   },
diff --git a/src/test/modules/test_escape/meson.build b/src/test/modules/test_escape/meson.build
index a21341d5067..04fd0be0889 100644
--- a/src/test/modules/test_escape/meson.build
+++ b/src/test/modules/test_escape/meson.build
@@ -10,7 +10,7 @@ endif
 
 test_escape = executable('test_escape',
   test_escape_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   }
diff --git a/src/test/modules/test_int128/meson.build b/src/test/modules/test_int128/meson.build
index 4c2be7a0326..94bca2edc5c 100644
--- a/src/test/modules/test_int128/meson.build
+++ b/src/test/modules/test_int128/meson.build
@@ -12,7 +12,7 @@ endif
 
 test_int128 = executable('test_int128',
   test_int128_sources,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install': false,
   },
diff --git a/src/test/modules/test_json_parser/meson.build b/src/test/modules/test_json_parser/meson.build
index 5672045f496..2c6031a14df 100644
--- a/src/test/modules/test_json_parser/meson.build
+++ b/src/test/modules/test_json_parser/meson.build
@@ -23,7 +23,7 @@ test_json_parser_incremental = executable('test_json_parser_incremental',
 # the shared-library flavor of jsonapi.
 test_json_parser_incremental_shlib = executable('test_json_parser_incremental_shlib',
   test_json_parser_incremental_sources,
-  dependencies: [frontend_shlib_code, libpq],
+  dependencies: [libpq, frontend_shlib_code],
   c_args: ['-DJSONAPI_SHLIB_ALLOC'],
   link_with: [common_excluded_shlib],
   kwargs: default_bin_args + {
diff --git a/src/test/regress/meson.build b/src/test/regress/meson.build
index 1da9e9462a9..3fe900d5847 100644
--- a/src/test/regress/meson.build
+++ b/src/test/regress/meson.build
@@ -30,7 +30,7 @@ endif
 pg_regress = executable('pg_regress',
   regress_sources,
   c_args: pg_regress_cflags,
-  dependencies: [frontend_code, libpq],
+  dependencies: [libpq, frontend_code],
   kwargs: default_bin_args + {
     'install_dir': dir_pgxs / 'src/test/regress',
   },
-- 
2.51.1

v4-0002-ci-Test-include-path-order-with-canary-libpq-fe.h.patchapplication/x-patch; name=v4-0002-ci-Test-include-path-order-with-canary-libpq-fe.h.patchDownload
From 3ed517c7904666e1f9dcf45171ffa2684126eb2f Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 3 Nov 2025 11:32:57 +1300
Subject: [PATCH v4 2/4] ci: Test include path order with canary libpq-fe.h.

If the build script would allow libpq-fe.h installed under
--with-includes (configure) or -Dextra_include_dirs (meson) to hide the
in-tree header, it will now reach an #error in contrived error on CI.
This tests that libpq is ordered correctly for all code that includes
the header.  The list of canary headers could be extended in future.

Also add missing set -e to the shell scripts run under su, since
otherwise the build step would confusingly succeed despite not
completing.

Reviewed-by: Bilal Yavuz <byavuz81@gmail.com>
Discussion: https://postgr.es/m/CA%2BhUKG%2Bx-cys30%3D7L2B8%3DcZ%2B-z6QDOj-oQy9O3CnkeXnrnm3OQ%40mail.gmail.com
---
 .cirrus.tasks.yml | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/.cirrus.tasks.yml b/.cirrus.tasks.yml
index f2581cfb2e5..3df6de6acd9 100644
--- a/.cirrus.tasks.yml
+++ b/.cirrus.tasks.yml
@@ -424,6 +424,8 @@ task:
     CCACHE_DIR: /tmp/ccache_dir
     DEBUGINFOD_URLS: "https://debuginfod.debian.net"
 
+    POISONED_HEADERS: /tmp/poisoned_headers
+
     # Enable a reasonable set of sanitizers. Use the linux task for that, as
     # it's one of the fastest tasks (without sanitizers). Also several of the
     # sanitizers work best on linux.
@@ -492,11 +494,24 @@ task:
     #apt-get update
     #DEBIAN_FRONTEND=noninteractive apt-get -y install ...
 
+  # Detect mistakes that would allow unwanted headers from outside our tree be
+  # found with --with-includes or -Dextra_include_dirs.  That is primarily a
+  # risk on non-Linux systems that install packages under eg /usr/local that
+  # must be explicitly added to the header search path, but this is a
+  # convenient place to test both build systems.
+  setup_poisoned_headers_script: |
+    for header in "libpq-fe.h" "libpq/libpq-fe.h" ; do
+      mkdir -p "${POISONED_HEADERS}/$(dirname $header)"
+      echo '#error "external header hides in-tree header"' \
+      > "${POISONED_HEADERS}/$header"
+    done
+
   matrix:
     # SPECIAL:
     # - Uses address sanitizer, sanitizer failures are typically printed in
     #   the server log
     # - Configures postgres with a small segment size
+    # - Poisoned headers in search path
     - name: Linux - Debian Bookworm - Autoconf
 
       env:
@@ -517,6 +532,7 @@ task:
             --with-segsize-blocks=6 \
             --with-libnuma \
             --with-liburing \
+            --with-includes="${POISONED_HEADERS}" \
             \
             ${LINUX_CONFIGURE_FEATURES} \
             \
@@ -540,6 +556,7 @@ task:
     # - Test both 64bit and 32 bit builds
     # - uses io_method=io_uring
     # - Uses meson feature autodetection
+    # - Poisoned headers in search path
     - name: Linux - Debian Bookworm - Meson
 
       env:
@@ -552,6 +569,7 @@ task:
         su postgres <<-EOF
           meson setup \
             ${MESON_COMMON_PG_CONFIG_ARGS} \
+            -Dextra_include_dirs="${POISONED_HEADERS}" \
             --buildtype=debug \
             ${LINUX_MESON_FEATURES} -Dllvm=enabled \
             build
@@ -564,6 +582,7 @@ task:
           export CC='ccache gcc -m32'
           meson setup \
             ${MESON_COMMON_PG_CONFIG_ARGS} \
+            -Dextra_include_dirs="${POISONED_HEADERS}" \
             --buildtype=debug \
             --pkg-config-path /usr/lib/i386-linux-gnu/pkgconfig/ \
             -DPERL=perl5.36-i386-linux-gnu \
@@ -573,12 +592,14 @@ task:
 
       build_script: |
         su postgres <<-EOF
+          set -e
           ninja -C build -j${BUILD_JOBS} ${MBUILD_TARGET}
           ninja -C build -t missingdeps
         EOF
 
       build_32_script: |
         su postgres <<-EOF
+          set -e
           ninja -C build-32 -j${BUILD_JOBS} ${MBUILD_TARGET}
           ninja -C build -t missingdeps
         EOF
-- 
2.51.1

v4-0003-meson-Try-to-find-libintl-without-Dextra_XXX.patchapplication/x-patch; name=v4-0003-meson-Try-to-find-libintl-without-Dextra_XXX.patchDownload
From 290bf24a89a075f3380c7e9af03d357475718887 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Tue, 4 Nov 2025 03:09:22 +1300
Subject: [PATCH v4 3/4] meson: Try to find libintl without -Dextra_XXX.

Since libintl is the only dependency that Meson can't usually find via
pkg-config on typical non-Linux Unix systems, let's try a bit harder to
find it with existing clues.

We typically find msgfmt in $PATH, so we can plausibly guess that the
library and headers are installed in the same prefix, and then try that
before giving up and requiring a manual path configuration.

This revealed some missing dependency declarations previously covered by
the catch-all extra_includes_dir, added here.

XXX Is this a terrible idea for some reason?
---
 meson.build                            | 34 +++++++++++++++++++++-----
 src/backend/jit/llvm/meson.build       |  5 +++-
 src/interfaces/libpq-oauth/meson.build |  1 +
 src/port/meson.build                   |  6 ++---
 4 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/meson.build b/meson.build
index 0f61ff6a700..2690d7a7b2b 100644
--- a/meson.build
+++ b/meson.build
@@ -2973,6 +2973,7 @@ cdata.set_quoted('PG_VERSION_STR',
 
 nlsopt = get_option('nls')
 libintl = not_found_dep
+libintl_include_dirs = []
 
 if not nlsopt.disabled()
   # otherwise there'd be lots of
@@ -2982,7 +2983,7 @@ if not nlsopt.disabled()
 
   # meson 0.59 has this wrapped in dependency('intl')
   if (msgfmt.found() and
-      cc.check_header('libintl.h', required: nlsopt,
+      cc.check_header('libintl.h',
         args: test_c_args, include_directories: postgres_inc))
 
     # in libc
@@ -2990,13 +2991,34 @@ if not nlsopt.disabled()
       libintl = declare_dependency()
     else
       libintl = cc.find_library('intl',
-        has_headers: ['libintl.h'], required: nlsopt,
+        has_headers: ['libintl.h'],
         header_include_directories: postgres_inc,
         dirs: test_lib_d)
     endif
   endif
 
+  if (msgfmt.found() and not libintl.found())
+    # libintl doesn't provide libintl.pc, but since we found its companion
+    # binary msgfmt we have a solid clue where to look, assuming standard
+    # layout within the install prefix.
+    guess_libintl_prefix = fs.parent(fs.parent(msgfmt.full_path()))
+    guess_libintl_lib_d = guess_libintl_prefix / 'lib'
+    guess_libintl_include = guess_libintl_prefix / 'include'
+    guess_libintl_include_dirs = include_directories(guess_libintl_include)
+    libintl_lib = cc.find_library('intl',
+      has_headers: ['libintl.h'],
+      header_include_directories: guess_libintl_include_dirs,
+      dirs: guess_libintl_lib_d)
+    if libintl_lib.found()
+      libintl = declare_dependency(dependencies: libintl_lib,
+                                   include_directories: guess_libintl_include_dirs)
+      libintl_include_dirs += guess_libintl_include # needed for llvmjit_types.bc
+    endif
+  endif
+
   if libintl.found()
+    cc.check_header('libintl.h', required: nlsopt,
+      args: test_c_args, include_directories: postgres_inc, dependencies: libintl)
     i18n = import('i18n')
     cdata.set('ENABLE_NLS', 1)
   endif
@@ -3220,14 +3242,14 @@ subdir('config')
 frontend_port_code = declare_dependency(
   compile_args: ['-DFRONTEND'],
   include_directories: [postgres_inc],
-  dependencies: os_deps,
+  dependencies: [os_deps, libintl]
 )
 
 backend_port_code = declare_dependency(
   compile_args: ['-DBUILDING_DLL'],
   include_directories: [postgres_inc],
   sources: [errcodes], # errcodes.h is needed due to use of ereport
-  dependencies: os_deps,
+  dependencies: [os_deps, libintl]
 )
 
 subdir('src/port')
@@ -3236,14 +3258,14 @@ frontend_common_code = declare_dependency(
   compile_args: ['-DFRONTEND'],
   include_directories: [postgres_inc],
   sources: generated_headers_stamp,
-  dependencies: [os_deps, zlib, zstd, lz4],
+  dependencies: [os_deps, libintl, zlib, zstd, lz4],
 )
 
 backend_common_code = declare_dependency(
   compile_args: ['-DBUILDING_DLL'],
   include_directories: [postgres_inc],
   sources: generated_headers_stamp,
-  dependencies: [os_deps, zlib, zstd],
+  dependencies: [os_deps, libintl, zlib, zstd],
 )
 
 subdir('src/common')
diff --git a/src/backend/jit/llvm/meson.build b/src/backend/jit/llvm/meson.build
index 805fbd69006..bcc5efed8a2 100644
--- a/src/backend/jit/llvm/meson.build
+++ b/src/backend/jit/llvm/meson.build
@@ -32,7 +32,7 @@ endif
 llvmjit = shared_module('llvmjit',
   llvmjit_sources,
   kwargs: pg_mod_args + {
-    'dependencies': pg_mod_args['dependencies'] + [llvm],
+    'dependencies': pg_mod_args['dependencies'] + [llvm, libintl],
     'cpp_args': pg_mod_args['cpp_args'] + llvm.get_variable(configtool: 'cxxflags').split(),
   }
 )
@@ -69,6 +69,9 @@ bitcode_cflags += '-I@BUILD_ROOT@/src/include'
 bitcode_cflags += '-I@BUILD_ROOT@/src/backend/utils/misc'
 bitcode_cflags += '-I@SOURCE_ROOT@/src/include'
 
+foreach d : libintl_include_dirs
+  bitcode_cflags += ['-I' + d]
+endforeach
 
 # Note this is intentionally not installed to bitcodedir, as it's not for
 # inlining
diff --git a/src/interfaces/libpq-oauth/meson.build b/src/interfaces/libpq-oauth/meson.build
index 505e1671b86..9d7fa1bc67c 100644
--- a/src/interfaces/libpq-oauth/meson.build
+++ b/src/interfaces/libpq-oauth/meson.build
@@ -29,6 +29,7 @@ libpq_oauth_st = static_library('libpq-oauth',
     frontend_stlib_code,
     libpq_oauth_deps,
     ssl, # libpq-int.h includes OpenSSL headers
+    gssapi, # and gssapi
   ],
   kwargs: default_lib_args,
 )
diff --git a/src/port/meson.build b/src/port/meson.build
index fc7b059fee5..9c4f5459e0a 100644
--- a/src/port/meson.build
+++ b/src/port/meson.build
@@ -154,14 +154,14 @@ endif
 pgport = {}
 pgport_variants = {
   '_srv': internal_lib_args + {
-    'dependencies': [backend_port_code],
+    'dependencies': [backend_port_code, libintl],
   },
   '': default_lib_args + {
-    'dependencies': [frontend_port_code],
+    'dependencies': [frontend_port_code, libintl],
   },
   '_shlib': default_lib_args + {
     'pic': true,
-    'dependencies': [frontend_port_code],
+    'dependencies': [frontend_port_code, libintl],
   },
 }
 
-- 
2.51.1

v4-0004-ci-Remove-Dextra_XXX-on-FreeBSD-and-macOS.patchapplication/x-patch; name=v4-0004-ci-Remove-Dextra_XXX-on-FreeBSD-and-macOS.patchDownload
From e553e694574113cd0ddf9b07af7ebc8bbaa8e246 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Tue, 4 Nov 2025 12:47:41 +1300
Subject: [PATCH v4 4/4] ci: Remove -Dextra_XXX on FreeBSD and macOS.

Since FreeBSD and MacPorts install .pc files for the libraries want,
Meson can find them.  The only exception is libintl, which the previous
commit handled specially.

NetBSD has its own libintl in libc, and OpenBSD doesn't have nls
enabled, explaining why they didn't need -Dextra_include_dirs and
-Dextra_lib_dirs in the first place.  The Windows task still needs them
to find OpenSSL.
---
 .cirrus.tasks.yml | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/.cirrus.tasks.yml b/.cirrus.tasks.yml
index 3df6de6acd9..31e498dc9b9 100644
--- a/.cirrus.tasks.yml
+++ b/.cirrus.tasks.yml
@@ -232,7 +232,6 @@ task:
       meson setup \
         ${MESON_COMMON_PG_CONFIG_ARGS} \
         --buildtype=debug \
-        -Dextra_lib_dirs=/usr/local/lib -Dextra_include_dirs=/usr/local/include/ \
         ${MESON_COMMON_FEATURES} ${MESON_FEATURES} \
         build
     EOF
@@ -731,8 +730,6 @@ task:
     meson setup \
       ${MESON_COMMON_PG_CONFIG_ARGS} \
       --buildtype=debug \
-      -Dextra_include_dirs=/opt/local/include \
-      -Dextra_lib_dirs=/opt/local/lib \
       ${MESON_COMMON_FEATURES} ${MESON_FEATURES} \
       build
 
-- 
2.51.1

#13Tristan Partin
tristan@partin.io
In reply to: Tom Lane (#11)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On Mon Nov 3, 2025 at 12:14 AM CST, Tom Lane wrote:

"Tristan Partin" <tristan@partin.io> writes:

What is the benefit of -Dextra_include_dirs when you could use -Dc_flags
or CFLAGS to specify extra include directories? If you tried either of
those as an alternative, would they fix the issue? The existence of the
option goes all the way back to the initial commit of the meson port, so
I presume it exists to mirror --with-includes, but why does that exist?

--with-includes, and likewise --with-libs, exist because there are
platforms that require it. For example, on pretty much any
BSD-derived system, the core /usr/include and /usr/lib files are very
limited and you'll need to point at places like /usr/pkg/include or
/opt/local/include or whatever to pull in non-core packages.

I see your point about putting -I flags into CFLAGS, but what you
are missing is that the order of these flags is unbelievably critical,
so "just shove it into CFLAGS" is a recipe for failure, especially
if you haven't even stopped to think about which end to add it at.
We've learned over decades of trial-and-error with the makefile system
that treating -I and -L flags specially is more reliable. (See for
example all the logic in our autoconf scripts to forcibly separate
-I and -L out of sources like pkg-config. Tracing that stuff back to
the originating commits and mail discussions would be a constructive
learning exercise.)

I'm well aware of ordering dependencies, and how annoying they can be.
The perils of C will outlive us all! Given your message and Thomas's
patch, I decided to take a look at the autotools build on PG 16 (since
that is what I had checked out, but maybe also maybe useful as the
original Meson build) and how it compared. Thomas has this hunk in his
patch:

	diff --git a/src/test/isolation/meson.build b/src/test/isolation/meson.build
	index a180e4e2741..660b11eff8b 100644
	--- a/src/test/isolation/meson.build
	+++ b/src/test/isolation/meson.build
	@@ -35,7 +35,7 @@ pg_isolation_regress = executable('pg_isolation_regress',
	   isolation_sources,
	   c_args: pg_regress_cflags,
	   include_directories: pg_regress_inc,
	-  dependencies: [frontend_code, libpq],
	+  dependencies: [libpq, frontend_code],
	   kwargs: default_bin_args + {
	     'install_dir': dir_pgxs / 'src/test/isolation',
	   },
	@@ -52,7 +52,7 @@ endif
	 isolationtester = executable('isolationtester',
	   isolationtester_sources,
	   include_directories: include_directories('.'),
	-  dependencies: [frontend_code, libpq],
	+  dependencies: [libpq, frontend_code],
	   kwargs: default_bin_args + {
	     'install_dir': dir_pgxs / 'src/test/isolation',
	   },

Taking a look, specifically, at isolationtester, I get the following
final compilation line for isolationtester.o (please forgive the macOS garbage):

gcc -Wall -Wmissing-prototypes -Wpointer-arith \
-Wdeclaration-after-statement -Werror=vla \
-Werror=unguarded-availability-new -Wendif-labels \
-Wmissing-format-attribute -Wcast-function-type \
-Wformat-security -fno-strict-aliasing -fwrapv \
-fexcess-precision=standard -Wno-unused-command-line-argument \
-Wno-compound-token-split-by-macro -Wno-format-truncation \
-Wno-cast-function-type-strict -O2 -I. \
-I/Users/tristan.partin/Projects/work/postgres/build-autotools/../src/test/isolation \
-I/Users/tristan.partin/Projects/work/postgres/build-autotools/../src/interfaces/libpq \
-I/Users/tristan.partin/Projects/work/postgres/build-autotools/../src/test/isolation/../regress \
-I../../../src/include \
-I/Users/tristan.partin/Projects/work/postgres/build-autotools/../src/include \
-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX26.0.sdk \
-c -o isolationtester.o \
/Users/tristan.partin/Projects/work/postgres/build-autotools/../src/test/isolation/isolationtester.c

The first include directory that isn't the source or build directory is
the in-tree libpq headers. I assume that is controlled by:

override CPPFLAGS := -I. -I$(srcdir) -I$(libpq_srcdir) \
-I$(srcdir)/../regress $(CPPFLAGS)

Thomas's patch seemingly makes the meson build equivalent to the
autotools build. Just for good measure, following --with-includes, the
include directories in that configure argument end up in CPPFLAGS:

CPPFLAGS="$CPPFLAGS $INCLUDES"

where INCLUDES is:

#
# Include directories
#
ac_save_IFS=$IFS
IFS="${IFS}${PATH_SEPARATOR}"
# SRCH_INC comes from the template file
for dir in $with_includes $SRCH_INC; do
if test -d "$dir"; then
INCLUDES="$INCLUDES -I$dir"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** Include directory $dir does not exist." >&5
$as_echo "$as_me: WARNING: *** Include directory $dir does not exist." >&2;}
fi
done
IFS=$ac_save_IFS

From my perspective, it looks like the autotools build has just been
_lucky_ to avoid this problem. I don't see anything that inherently
prevents the problem that Thomas ran into. In my opinion Thomas's patch
is just a parity patch. It's incredible that it took this long to find.

--
Tristan Partin
Databricks (https://databricks.com)

#14Tristan Partin
tristan@partin.io
In reply to: Thomas Munro (#12)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

After my analysis in the other subthread, patch 1 looks ready to be
committed. Patch 2 looks good to go, but maybe you should split the "set
-e" changes into a separate commit. Not sure how much I like patch
3 since it hardcodes $libdir as "lib" for the purposes of finding the
header, but it's also kind of a last resort thing, so ¯\_(ツ)_/¯. Maybe
it's best if the meson build just copies whatever the autotools build
does?

--
Tristan Partin
Databricks (https://databricks.com)

#15Andres Freund
andres@anarazel.de
In reply to: Thomas Munro (#12)
1 attachment(s)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

Hi,

On 2025-11-04 18:30:51 +1300, Thomas Munro wrote:

First two patches as before, except for a couple of unnecessary hunks
I deleted based on an off-list review from Bilal.

I think there may be a less verbose way to do this:

The problem is caused by us adding extra_include_dirs to postgres_inc_d, which
does not include the private include dir for e.g. libpq. As it is added to
frontend_code etc as a flat list, there's no way for meson to know that
src/interfaces/libpq should be added earlier in the commandline.

The position we add extra_include_dirs right now also seems wrong on windows,
leaving libpq aside, as it's added *before* src/include/port/win32 etc.

The easiest way to fix that seems to be to simply not extra_include_dirs to
postgres_inc_d, but instead either add it to cppflags (the meson variable, not
the environment) or add it to the project C flags.

It seems that with autoconf we add the --with-includes to the pg_config
--cppflags, but we don't today with meson. Adding it to the cppflags variable
would take care of that too.

A quick prototype of that is attached.

Thoughts?

Greetings,

Andres Freund

Attachments:

fix_extra_include_prototype.diff.txttext/plain; charset=us-asciiDownload
diff --git i/meson.build w/meson.build
index 0f61ff6a700..31ef82a52a4 100644
--- i/meson.build
+++ w/meson.build
@@ -84,7 +84,6 @@ endif
 ###############################################################
 
 postgres_inc_d = ['src/include']
-postgres_inc_d += get_option('extra_include_dirs')
 
 postgres_lib_d = get_option('extra_lib_dirs')
 
@@ -327,6 +326,23 @@ else
   error('unknown host system: @0@'.format(host_system))
 endif
 
+# While the most obvious way to implement extra_include_dirs would be to add
+# extra_include_dirs to postgres_inc_d, that turns out to not work well: For
+# some parts of the buildtree (e.g. src/interfaces/libpq or tools linking
+# against libpq) we need to add additional directories to be included, and
+# those directories need to take precedence over extra_include_dirs.  The
+# easiest way to achieve that is to gin up the compiler arguments ourselves
+# (which luckily is the same across all supported compilers) and add it to
+# cppflags. That works as meson always adds explicit include_directories
+# before generic compiler arguments.
+#
+# An additional advantage is that this way the extra include directories are
+# included in pg_config --cppflags output, keeping the behavior consistent
+# with autoconf builds.
+foreach extra_dir : get_option('extra_include_dirs')
+  cppflags += '-I@0@'.format(extra_dir)
+endforeach
+
 
 
 ###############################################################
#16Thomas Munro
thomas.munro@gmail.com
In reply to: Andres Freund (#15)
Re: meson's in-tree libpq header search order vs -Dextra_include_dirs

On Wed, Nov 5, 2025 at 8:55 AM Andres Freund <andres@anarazel.de> wrote:

The problem is caused by us adding extra_include_dirs to postgres_inc_d, which
does not include the private include dir for e.g. libpq. As it is added to
frontend_code etc as a flat list, there's no way for meson to know that
src/interfaces/libpq should be added earlier in the commandline.

Check.

The position we add extra_include_dirs right now also seems wrong on windows,
leaving libpq aside, as it's added *before* src/include/port/win32 etc.

Yeah.

The easiest way to fix that seems to be to simply not extra_include_dirs to
postgres_inc_d, but instead either add it to cppflags (the meson variable, not
the environment) or add it to the project C flags.

I see. Similar to what Tristan said except higher level than having
the human write command line arguments -I/foo/bar as C/CPP flags. I
actually tried to do something like that myself early on, but I
couldn't figure out how to expand include directories to command line
arguments using meson machinery, as it does for dependencies. I had a
notion that raw -I strings weren't OK, but I see that we already do
that while probing perl.

It seems that with autoconf we add the --with-includes to the pg_config
--cppflags, but we don't today with meson. Adding it to the cppflags variable
would take care of that too.

Right!

To experience a problem I suppose you'd need meson-built PostgreSQL, an
extension running pg_config, and a PostgreSQL header that wants to
include something from an external package on a platform that puts
them outside the base system, and that hasn't come up in the wild yet.
Meson adoption in the relevant packaging projects may not be happening
yet.

A quick prototype of that is attached.

I think where cc.find_library() probe headers, we need
s/header_include_directories: postgres_inc/header_args: test_c_args/.
Otherwise for example the libintl probe fails on my system, and in
that particular case it doesn't seem necessary to probe the header
anyway so that can just be removed.

Similarly, cc.has_function() and cc.has_header() could probably drop
include_directories as they already have test_c_args, as long as they
are probing external libraries.

Some probes do still need postgres_inc, though: I think it's the
category that is testing OS headers, but we've interposed a bunch of
wrappers on Windows eg sockets. Maybe that's confusing enough that we
should just leave postgres_inc everywhere but supplement it with
header_args: test_c_args where it's missing, or should we add
postgres_inc to test_c_args?

With extra_include_dirs in cpp_flags, bitcode_cflags also needs to be
reordered, or else:

... clang ... -I/usr/local/include -I./src/include
-I./src/backend/utils/misc -I../src/include
../src/include/c.h:113:9: warning: 'pg_restrict' macro redefined
[-Wmacro-redefined]
113 | #define pg_restrict restrict
| ^
/usr/local/include/pg_config.h:806:9: note: previous definition is here
806 | #define pg_restrict __restrict
| ^

I have some rough patches for those changes...

I think there might be a fundamental problem with this though.
Although it fixes my original complaint today, all it would take to
make it break again would be something to appear with "dependencies:
[<something-found-by-pkg-config> libpq]", or libintl with the v4-0003
patch I posted yesterday (which finds the headers and declares the
dependency, an extended fuzzy version of the sort of thing meson does
inside dependency(...) IIUC, the point of my patch being to make it
so that you don't need -Dextra_include_dirs at all on Macs and *BSD).
In other words, this problem isn't really just about
extra_include_dirs, it's also about paths produced by dependencies,
which can bring external libpq-fe.h back into visibility. Hence my
attempt to engage with the dependency system.