From 48c665854e1b1aa8cf8bd2d34ff7a00e21892e8c Mon Sep 17 00:00:00 2001
From: Tristan Partin <tristan@partin.io>
Date: Fri, 2 Jan 2026 22:16:26 -0600
Subject: [PATCH v1] Add subproject support to the meson build

Meson subprojects[0] are a way for projects to build a dependency that
may not exist on the system.

libpq is a popular library to depend on for other projects, whether
they are Postgres applications or client libraries. Exposing subproject
support can help projects that depend on libpq to build more easily on
systems which would not have libpq installed. It can also be useful if
the project requires a newer version of libpq, but the system has an
older version.

Using @BUILD_ROOT@ and @SOURCE_ROOT@ is problematic because they point
to the global build and source roots.

Link: https://mesonbuild.com/Subprojects.html [0]
Signed-off-by: Tristan Partin <tristan@partin.io>
---
 meson.build                          | 8 +++-----
 src/backend/jit/llvm/meson.build     | 6 +++---
 src/bin/psql/meson.build             | 2 +-
 src/common/unicode/meson.build       | 2 +-
 src/include/catalog/meson.build      | 2 +-
 src/include/utils/meson.build        | 5 ++++-
 src/interfaces/ecpg/test/meson.build | 2 +-
 src/interfaces/libpq/meson.build     | 2 ++
 src/tools/pgflex                     | 2 --
 9 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/meson.build b/meson.build
index 467f7f005a6..dbbead2bd88 100644
--- a/meson.build
+++ b/meson.build
@@ -375,8 +375,6 @@ if flex.found()
 endif
 flex_wrapper = files('src/tools/pgflex')
 flex_cmd = [python, flex_wrapper,
-  '--builddir', '@BUILD_ROOT@',
-  '--srcdir', '@SOURCE_ROOT@',
   '--privatedir', '@PRIVATE_DIR@',
   '--flex', flex, '--perl', perl,
   '-i', '@INPUT@', '-o', '@OUTPUT0@',
@@ -3149,7 +3147,7 @@ catalog_pm = files('src/backend/catalog/Catalog.pm')
 perfect_hash_pm = files('src/tools/PerfectHash.pm')
 gen_kwlist_deps = [perfect_hash_pm]
 gen_kwlist_cmd = [
-  perl, '-I', '@SOURCE_ROOT@/src/tools',
+  perl, '-I', '@0@/src/tools'.format(meson.project_source_root()),
   files('src/tools/gen_keywordlist.pl'),
   '--output', '@OUTDIR@', '@INPUT@']
 
@@ -3864,7 +3862,7 @@ pg_git_revision = get_option('PG_GIT_REVISION')
 
 tar_gz = custom_target('tar.gz',
   build_always_stale: true,
-  command: [git, '-C', '@SOURCE_ROOT@',
+  command: [git, '-C', meson.project_source_root(),
             '-c', 'core.autocrlf=false',
             'archive',
             '--format', 'tar.gz',
@@ -3878,7 +3876,7 @@ tar_gz = custom_target('tar.gz',
 if bzip2.found()
   tar_bz2 = custom_target('tar.bz2',
     build_always_stale: true,
-    command: [git, '-C', '@SOURCE_ROOT@',
+    command: [git, '-C', meson.project_source_root(),
               '-c', 'core.autocrlf=false',
               '-c', 'tar.tar.bz2.command="@0@" -c'.format(bzip2.full_path()),
               'archive',
diff --git a/src/backend/jit/llvm/meson.build b/src/backend/jit/llvm/meson.build
index 7df8453ad6f..27e47000b11 100644
--- a/src/backend/jit/llvm/meson.build
+++ b/src/backend/jit/llvm/meson.build
@@ -65,9 +65,9 @@ bitcode_cflags += get_option('c_args')
 bitcode_cflags += cppflags
 
 # XXX: Worth improving on the logic to find directories here
-bitcode_cflags += '-I@BUILD_ROOT@/src/include'
-bitcode_cflags += '-I@BUILD_ROOT@/src/backend/utils/misc'
-bitcode_cflags += '-I@SOURCE_ROOT@/src/include'
+bitcode_cflags += '-I@0@src/include'.format(meson.project_build_root())
+bitcode_cflags += '-I@0@src/backend/utils/misc'.format(meson.project_build_root())
+bitcode_cflags += '-I@0@src/include'.format(meson.project_source_root())
 
 
 # Note this is intentionally not installed to bitcodedir, as it's not for
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index 922b2845267..ef660e8c3d4 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -39,7 +39,7 @@ sql_help = custom_target('psql_help',
   depfile: 'sql_help.dep',
   command: [
     perl, files('create_help.pl'),
-    '--docdir', '@SOURCE_ROOT@/doc/src/sgml/ref',
+    '--docdir', join_paths(meson.project_source_root(), 'doc/src/sgml/ref'),
     '--depfile', '@DEPFILE@',
     '--outdir', '@OUTDIR@',
     '--basename', 'sql_help',
diff --git a/src/common/unicode/meson.build b/src/common/unicode/meson.build
index f650dd95b5c..a34912a95bb 100644
--- a/src/common/unicode/meson.build
+++ b/src/common/unicode/meson.build
@@ -174,7 +174,7 @@ update_unicode = custom_target('update-unicode',
   depends: update_unicode_dep,
   output: ['dont-exist'],
   input: update_unicode_targets,
-  command: [cp, '@INPUT@', '@SOURCE_ROOT@/src/include/common/'],
+  command: [cp, '@INPUT@', join_paths(meson.project_source_root(), 'src/include/common/')],
   build_by_default: false,
   build_always_stale: true,
 )
diff --git a/src/include/catalog/meson.build b/src/include/catalog/meson.build
index b63cd584068..bea387b98bc 100644
--- a/src/include/catalog/meson.build
+++ b/src/include/catalog/meson.build
@@ -136,7 +136,7 @@ generated_catalog_headers = custom_target('generated_catalog_headers',
   command: [
     perl,
     files('../../backend/catalog/genbki.pl'),
-    '--include-path=@SOURCE_ROOT@/src/include',
+    '--include-path=@0@/src/include'.format(meson.project_source_root()),
     '--set-version=' + pg_version_major.to_string(),
     '--output=@OUTDIR@', '@INPUT@'
   ],
diff --git a/src/include/utils/meson.build b/src/include/utils/meson.build
index 318a6aec0d0..8db68322b86 100644
--- a/src/include/utils/meson.build
+++ b/src/include/utils/meson.build
@@ -69,7 +69,10 @@ fmgrtab_target = custom_target('fmgrtab',
   input: '../catalog/pg_proc.dat',
   output : fmgrtab_output,
   depend_files: catalog_pm,
-  command: [perl, '-I', '@SOURCE_ROOT@/src/backend/catalog/', files('../../backend/utils/Gen_fmgrtab.pl'), '--include-path=@SOURCE_ROOT@/src/include', '--output=@OUTDIR@', '@INPUT@'],
+  command: [perl, '-I', '@0@/src/backend/catalog/'.format(meson.project_source_root()),
+    files('../../backend/utils/Gen_fmgrtab.pl'),
+    '--include-path=@0@/src/include'.format(meson.project_source_root()),
+    '--output=@OUTDIR@', '@INPUT@'],
   install: true,
   install_dir: [dir_include_server / 'utils', dir_include_server / 'utils', false],
 )
diff --git a/src/interfaces/ecpg/test/meson.build b/src/interfaces/ecpg/test/meson.build
index 7f5b950c186..4b2d49a4b78 100644
--- a/src/interfaces/ecpg/test/meson.build
+++ b/src/interfaces/ecpg/test/meson.build
@@ -47,7 +47,7 @@ ecpg_preproc_test_command_start = [
   ecpg_exe,
   '--regression',
   '-I@CURRENT_SOURCE_DIR@',
-  '-I@SOURCE_ROOT@' + '/src/interfaces/ecpg/include/',
+  '-I@0@/src/interfaces/ecpg/include/'.format(meson.project_build_root()),
 ]
 ecpg_preproc_test_command_end = [
   '-o', '@OUTPUT@', '@INPUT@'
diff --git a/src/interfaces/libpq/meson.build b/src/interfaces/libpq/meson.build
index c5ecd9c3a87..4aea220cd38 100644
--- a/src/interfaces/libpq/meson.build
+++ b/src/interfaces/libpq/meson.build
@@ -85,6 +85,8 @@ libpq = declare_dependency(
   include_directories: [include_directories('.')]
 )
 
+meson.override_dependency('libpq', libpq)
+
 # Check for functions that libpq must not call.  See libpq_check.pl for the
 # full set of platform rules.  Skip the test when profiling, as gcc may
 # insert exit() calls for that.
diff --git a/src/tools/pgflex b/src/tools/pgflex
index b8d9aa0086f..a1b471ee487 100755
--- a/src/tools/pgflex
+++ b/src/tools/pgflex
@@ -17,8 +17,6 @@ parser = argparse.ArgumentParser()
 
 parser.add_argument('--flex', type=abspath, required=True)
 parser.add_argument('--perl', type=abspath, required=True)
-parser.add_argument('--builddir', type=abspath, required=True)
-parser.add_argument('--srcdir', type=abspath, required=True)
 parser.add_argument('--privatedir', type=abspath, required=True,
                     help='private directory for target')
 
-- 
Tristan Partin
https://tristan.partin.io

