Doc: use CURRENT_USER not USER in plpgsql trigger examples.
[pgsql.git] / meson.build
blob862c955453f4f0e16bb12854efc495ca4ab31f50
1 # Copyright (c) 2022-2023, PostgreSQL Global Development Group
3 # Entry point for building PostgreSQL with meson
5 # Good starting points for writing meson.build files are:
6 #  - https://mesonbuild.com/Syntax.html
7 #  - https://mesonbuild.com/Reference-manual.html
9 project('postgresql',
10   ['c'],
11   version: '17devel',
12   license: 'PostgreSQL',
14   # We want < 0.56 for python 3.5 compatibility on old platforms. EPEL for
15   # RHEL 7 has 0.55. < 0.54 would require replacing some uses of the fs
16   # module, < 0.53 all uses of fs. So far there's no need to go to >=0.56.
17   meson_version: '>=0.54',
18   default_options: [
19     'warning_level=1', #-Wall equivalent
20     'b_pch=false',
21     'buildtype=debugoptimized', # -O2 + debug
22     # For compatibility with the autoconf build, set a default prefix. This
23     # works even on windows, where it's a drive-relative path (i.e. when on
24     # d:/somepath it'll install to d:/usr/local/pgsql)
25     'prefix=/usr/local/pgsql',
26   ]
31 ###############################################################
32 # Basic prep
33 ###############################################################
35 fs = import('fs')
36 pkgconfig = import('pkgconfig')
38 host_system = host_machine.system()
39 build_system = build_machine.system()
40 host_cpu = host_machine.cpu_family()
42 cc = meson.get_compiler('c')
44 not_found_dep = dependency('', required: false)
45 thread_dep = dependency('threads')
46 auto_features = get_option('auto_features')
50 ###############################################################
51 # Safety first
52 ###############################################################
54 # It's very easy to get into confusing states when the source directory
55 # contains an in-place build. E.g. the wrong pg_config.h will be used. So just
56 # refuse to build in that case.
58 # There's a more elaborate check later, that checks for conflicts around all
59 # generated files. But we can only do that much further down the line, so this
60 # quick check seems worth it. Adhering to this advice should clean up the
61 # conflict, but won't protect against somebody doing make distclean or just
62 # removing pg_config.h
63 errmsg_nonclean_base = '''
64 ****
65 Non-clean source code directory detected.
67 To build with meson the source tree may not have an in-place, ./configure
68 style, build configured. You can have both meson and ./configure style builds
69 for the same source tree by building out-of-source / VPATH with
70 configure. Alternatively use a separate check out for meson based builds.
72 @0@
73 ****'''
74 if fs.exists(meson.current_source_dir() / 'src' / 'include' / 'pg_config.h')
75   errmsg_cleanup = 'To clean up, run make maintainer-clean in the source tree.'
76   error(errmsg_nonclean_base.format(errmsg_cleanup))
77 endif
81 ###############################################################
82 # Variables to be determined
83 ###############################################################
85 postgres_inc_d = ['src/include']
86 postgres_inc_d += get_option('extra_include_dirs')
88 postgres_lib_d = get_option('extra_lib_dirs')
90 cppflags = []
92 cflags = []
93 cxxflags = []
94 cflags_warn = []
95 cxxflags_warn = []
96 cflags_mod = []
97 cxxflags_mod = []
99 ldflags = []
100 ldflags_be = []
101 ldflags_sl = []
102 ldflags_mod = []
104 test_c_args = []
106 os_deps = []
107 backend_both_deps = []
108 backend_deps = []
109 libpq_deps = []
111 pg_sysroot = ''
113 # source of data for pg_config.h etc
114 cdata = configuration_data()
118 ###############################################################
119 # Version and other metadata
120 ###############################################################
122 pg_version = meson.project_version()
124 if pg_version.endswith('devel')
125   pg_version_arr = [pg_version.split('devel')[0], '0']
126 elif pg_version.contains('beta')
127   pg_version_arr = [pg_version.split('beta')[0], '0']
128 elif pg_version.contains('rc')
129   pg_version_arr = [pg_version.split('rc')[0], '0']
130 else
131   pg_version_arr = pg_version.split('.')
132 endif
134 pg_version_major = pg_version_arr[0].to_int()
135 pg_version_minor = pg_version_arr[1].to_int()
136 pg_version_num = (pg_version_major * 10000) + pg_version_minor
138 pg_url = 'https://www.postgresql.org/'
140 cdata.set_quoted('PACKAGE_NAME', 'PostgreSQL')
141 cdata.set_quoted('PACKAGE_BUGREPORT', 'pgsql-bugs@lists.postgresql.org')
142 cdata.set_quoted('PACKAGE_URL', pg_url)
143 cdata.set_quoted('PACKAGE_VERSION', pg_version)
144 cdata.set_quoted('PACKAGE_STRING', 'PostgreSQL @0@'.format(pg_version))
145 cdata.set_quoted('PACKAGE_TARNAME', 'postgresql')
147 pg_version += get_option('extra_version')
148 cdata.set_quoted('PG_VERSION', pg_version)
149 cdata.set_quoted('PG_MAJORVERSION', pg_version_major.to_string())
150 cdata.set('PG_MAJORVERSION_NUM', pg_version_major)
151 cdata.set('PG_MINORVERSION_NUM', pg_version_minor)
152 cdata.set('PG_VERSION_NUM', pg_version_num)
153 # PG_VERSION_STR is built later, it depends on compiler test results
154 cdata.set_quoted('CONFIGURE_ARGS', '')
158 ###############################################################
159 # Basic platform specific configuration
160 ###############################################################
162 # meson's system names don't quite map to our "traditional" names. In some
163 # places we need the "traditional" name, e.g., for mapping
164 # src/include/port/$os.h to src/include/pg_config_os.h. Define portname for
165 # that purpose.
166 portname = host_system
168 exesuffix = '' # overridden below where necessary
169 dlsuffix = '.so' # overridden below where necessary
170 library_path_var = 'LD_LIBRARY_PATH'
172 # Format of file to control exports from libraries, and how to pass them to
173 # the compiler. For export_fmt @0@ is the path to the file export file.
174 export_file_format = 'gnu'
175 export_file_suffix = 'list'
176 export_fmt = '-Wl,--version-script=@0@'
178 # Flags to add when linking a postgres extension, @0@ is path to
179 # the relevant object on the platform.
180 mod_link_args_fmt = []
182 memset_loop_limit = 1024
184 # Choice of shared memory and semaphore implementation
185 shmem_kind = 'sysv'
186 sema_kind = 'sysv'
188 # We implement support for some operating systems by pretending they're
189 # another. Map here, before determining system properties below
190 if host_system == 'dragonfly'
191   # apparently the most similar
192   host_system = 'netbsd'
193 endif
195 if host_system == 'aix'
196   library_path_var = 'LIBPATH'
198   export_file_format = 'aix'
199   export_fmt = '-Wl,-bE:@0@'
200   mod_link_args_fmt = ['-Wl,-bI:@0@']
201   mod_link_with_dir = 'libdir'
202   mod_link_with_name = '@0@.imp'
204   # M:SRE sets a flag indicating that an object is a shared library. Seems to
205   # work in some circumstances without, but required in others.
206   ldflags_sl += '-Wl,-bM:SRE'
207   ldflags_be += '-Wl,-brtllib'
209   # Native memset() is faster, tested on:
210   # - AIX 5.1 and 5.2, XLC 6.0 (IBM's cc)
211   # - AIX 5.3 ML3, gcc 4.0.1
212   memset_loop_limit = 0
214 elif host_system == 'cygwin'
215   sema_kind = 'unnamed_posix'
216   cppflags += '-D_GNU_SOURCE'
217   dlsuffix = '.dll'
218   mod_link_args_fmt = ['@0@']
219   mod_link_with_name = 'lib@0@.exe.a'
220   mod_link_with_dir = 'libdir'
222 elif host_system == 'darwin'
223   dlsuffix = '.dylib'
224   library_path_var = 'DYLD_LIBRARY_PATH'
226   export_file_format = 'darwin'
227   export_fmt = '-Wl,-exported_symbols_list,@0@'
229   mod_link_args_fmt = ['-bundle_loader', '@0@']
230   mod_link_with_dir = 'bindir'
231   mod_link_with_name = '@0@'
233   sysroot_args = [files('src/tools/darwin_sysroot'), get_option('darwin_sysroot')]
234   pg_sysroot = run_command(sysroot_args, check:true).stdout().strip()
235   message('darwin sysroot: @0@'.format(pg_sysroot))
236   if pg_sysroot != ''
237     cflags += ['-isysroot', pg_sysroot]
238     ldflags += ['-isysroot', pg_sysroot]
239   endif
240   # meson defaults to -Wl,-undefined,dynamic_lookup for modules, which we
241   # don't want because a) it's different from what we do for autoconf, b) it
242   # causes warnings starting in macOS Ventura
243   ldflags_mod += ['-Wl,-undefined,error']
245 elif host_system == 'freebsd'
246   sema_kind = 'unnamed_posix'
248 elif host_system == 'linux'
249   sema_kind = 'unnamed_posix'
250   cppflags += '-D_GNU_SOURCE'
252 elif host_system == 'netbsd'
253   # We must resolve all dynamic linking in the core server at program start.
254   # Otherwise the postmaster can self-deadlock due to signals interrupting
255   # resolution of calls, since NetBSD's linker takes a lock while doing that
256   # and some postmaster signal handlers do things that will also acquire that
257   # lock.  As long as we need "-z now", might as well specify "-z relro" too.
258   # While there's not a hard reason to adopt these settings for our other
259   # executables, there's also little reason not to, so just add them to
260   # LDFLAGS.
261   ldflags += ['-Wl,-z,now', '-Wl,-z,relro']
263 elif host_system == 'openbsd'
264   # you're ok
266 elif host_system == 'sunos'
267   portname = 'solaris'
268   export_fmt = '-Wl,-M@0@'
269   cppflags += '-D_POSIX_PTHREAD_SEMANTICS'
271 elif host_system == 'windows'
272   portname = 'win32'
273   exesuffix = '.exe'
274   dlsuffix = '.dll'
275   library_path_var = ''
277   export_file_format = 'win'
278   export_file_suffix = 'def'
279   if cc.get_id() == 'msvc'
280     export_fmt = '/DEF:@0@'
281     mod_link_with_name = '@0@.exe.lib'
282   else
283     export_fmt = '@0@'
284     mod_link_with_name = 'lib@0@.exe.a'
285   endif
286   mod_link_args_fmt = ['@0@']
287   mod_link_with_dir = 'libdir'
289   shmem_kind = 'win32'
290   sema_kind = 'win32'
292   cdata.set('WIN32_STACK_RLIMIT', 4194304)
293   if cc.get_id() == 'msvc'
294     ldflags += '/INCREMENTAL:NO'
295     ldflags += '/STACK:@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
296     # ldflags += '/nxcompat' # generated by msbuild, should have it for ninja?
297   else
298     ldflags += '-Wl,--stack,@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
299     # Need to allow multiple definitions, we e.g. want to override getopt.
300     ldflags += '-Wl,--allow-multiple-definition'
301     # Ensure we get MSVC-like linking behavior.
302     ldflags += '-Wl,--disable-auto-import'
303   endif
305   os_deps += cc.find_library('ws2_32', required: true)
306   secur32_dep = cc.find_library('secur32', required: true)
307   backend_deps += secur32_dep
308   libpq_deps += secur32_dep
310   postgres_inc_d += 'src/include/port/win32'
311   if cc.get_id() == 'msvc'
312     postgres_inc_d += 'src/include/port/win32_msvc'
313   endif
315   windows = import('windows')
317 else
318   # XXX: Should we add an option to override the host_system as an escape
319   # hatch?
320   error('unknown host system: @0@'.format(host_system))
321 endif
325 ###############################################################
326 # Program paths
327 ###############################################################
329 # External programs
330 perl = find_program(get_option('PERL'), required: true, native: true)
331 python = find_program(get_option('PYTHON'), required: true, native: true)
332 flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.35')
333 bison = find_program(get_option('BISON'), native: true, version: '>= 2.3')
334 sed = find_program(get_option('SED'), 'sed', native: true)
335 prove = find_program(get_option('PROVE'), native: true, required: false)
336 tar = find_program(get_option('TAR'), native: true)
337 gzip = find_program(get_option('GZIP'), native: true)
338 program_lz4 = find_program(get_option('LZ4'), native: true, required: false)
339 openssl = find_program(get_option('OPENSSL'), native: true, required: false)
340 program_zstd = find_program(get_option('ZSTD'), native: true, required: false)
341 dtrace = find_program(get_option('DTRACE'), native: true, required: get_option('dtrace'))
342 missing = find_program('config/missing', native: true)
343 cp = find_program('cp', required: false, native: true)
344 xmllint_bin = find_program(get_option('XMLLINT'), native: true, required: false)
345 xsltproc_bin = find_program(get_option('XSLTPROC'), native: true, required: false)
347 bison_flags = []
348 if bison.found()
349   bison_version_c = run_command(bison, '--version', check: true)
350   # bison version string helpfully is something like
351   # >>bison (GNU bison) 3.8.1<<
352   bison_version = bison_version_c.stdout().split(' ')[3].split('\n')[0]
353   if bison_version.version_compare('>=3.0')
354     bison_flags += ['-Wno-deprecated']
355   endif
356 endif
357 bison_cmd = [bison, bison_flags, '-o', '@OUTPUT0@', '-d', '@INPUT@']
358 bison_kw = {
359   'output': ['@BASENAME@.c', '@BASENAME@.h'],
360   'command': bison_cmd,
363 flex_flags = []
364 if flex.found()
365   flex_version_c = run_command(flex, '--version', check: true)
366   flex_version = flex_version_c.stdout().split(' ')[1].split('\n')[0]
367 endif
368 flex_wrapper = files('src/tools/pgflex')
369 flex_cmd = [python, flex_wrapper,
370   '--builddir', '@BUILD_ROOT@',
371   '--srcdir', '@SOURCE_ROOT@',
372   '--privatedir', '@PRIVATE_DIR@',
373   '--flex', flex, '--perl', perl,
374   '-i', '@INPUT@', '-o', '@OUTPUT0@',
377 wget = find_program('wget', required: false, native: true)
378 wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
380 install_files = files('src/tools/install_files')
384 ###############################################################
385 # Path to meson (for tests etc)
386 ###############################################################
388 # NB: this should really be part of meson, see
389 # https://github.com/mesonbuild/meson/issues/8511
390 meson_binpath_r = run_command(python, 'src/tools/find_meson', check: true)
392 if meson_binpath_r.stdout() == ''
393   error('huh, could not run find_meson.\nerrcode: @0@\nstdout: @1@\nstderr: @2@'.format(
394     meson_binpath_r.returncode(),
395     meson_binpath_r.stdout(),
396     meson_binpath_r.stderr()))
397 endif
399 meson_binpath_s = meson_binpath_r.stdout().split('\n')
400 meson_binpath_len = meson_binpath_s.length()
402 if meson_binpath_len < 1
403   error('unexpected introspect line @0@'.format(meson_binpath_r.stdout()))
404 endif
406 i = 0
407 meson_impl = ''
408 meson_binpath = ''
409 meson_args = []
410 foreach e : meson_binpath_s
411   if i == 0
412     meson_impl = e
413   elif i == 1
414     meson_binpath = e
415   else
416     meson_args += e
417   endif
418   i += 1
419 endforeach
421 if meson_impl not in ['muon', 'meson']
422   error('unknown meson implementation "@0@"'.format(meson_impl))
423 endif
425 meson_bin = find_program(meson_binpath, native: true)
429 ###############################################################
430 # Option Handling
431 ###############################################################
433 cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false)
435 blocksize = get_option('blocksize').to_int() * 1024
437 if get_option('segsize_blocks') != 0
438   if get_option('segsize') != 1
439     warning('both segsize and segsize_blocks specified, segsize_blocks wins')
440   endif
442   segsize = get_option('segsize_blocks')
443 else
444   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
445 endif
447 cdata.set('BLCKSZ', blocksize, description:
448 '''Size of a disk block --- this also limits the size of a tuple. You can set
449    it bigger if you need bigger tuples (although TOAST should reduce the need
450    to have large tuples, since fields can be spread across multiple tuples).
451    BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
452    currently 2^15 (32768). This is determined by the 15-bit widths of the
453    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
454    Changing BLCKSZ requires an initdb.''')
456 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
457 cdata.set('RELSEG_SIZE', segsize)
458 cdata.set('DEF_PGPORT', get_option('pgport'))
459 cdata.set_quoted('DEF_PGPORT_STR', get_option('pgport').to_string())
460 cdata.set_quoted('PG_KRB_SRVNAM', get_option('krb_srvnam'))
461 if get_option('system_tzdata') != ''
462   cdata.set_quoted('SYSTEMTZDIR', get_option('system_tzdata'))
463 endif
467 ###############################################################
468 # Directories
469 ###############################################################
471 # These are set by the equivalent --xxxdir configure options.  We
472 # append "postgresql" to some of them, if the string does not already
473 # contain "pgsql" or "postgres", in order to avoid directory clutter.
475 pkg = 'postgresql'
477 dir_prefix = get_option('prefix')
479 dir_prefix_contains_pg = (dir_prefix.contains('pgsql') or dir_prefix.contains('postgres'))
481 dir_bin = get_option('bindir')
483 dir_data = get_option('datadir')
484 if not (dir_prefix_contains_pg or dir_data.contains('pgsql') or dir_data.contains('postgres'))
485   dir_data = dir_data / pkg
486 endif
488 dir_sysconf = get_option('sysconfdir')
489 if not (dir_prefix_contains_pg or dir_sysconf.contains('pgsql') or dir_sysconf.contains('postgres'))
490   dir_sysconf = dir_sysconf / pkg
491 endif
493 dir_lib = get_option('libdir')
495 dir_lib_pkg = dir_lib
496 if not (dir_prefix_contains_pg or dir_lib_pkg.contains('pgsql') or dir_lib_pkg.contains('postgres'))
497   dir_lib_pkg = dir_lib_pkg / pkg
498 endif
500 dir_pgxs = dir_lib_pkg / 'pgxs'
502 dir_include = get_option('includedir')
504 dir_include_pkg = dir_include
505 dir_include_pkg_rel = ''
506 if not (dir_prefix_contains_pg or dir_include_pkg.contains('pgsql') or dir_include_pkg.contains('postgres'))
507   dir_include_pkg = dir_include_pkg / pkg
508   dir_include_pkg_rel = pkg
509 endif
511 dir_man = get_option('mandir')
513 # FIXME: These used to be separately configurable - worth adding?
514 dir_doc = get_option('datadir') / 'doc' / 'postgresql'
515 dir_doc_html = dir_doc / 'html'
517 dir_locale = get_option('localedir')
520 # Derived values
521 dir_bitcode = dir_lib_pkg / 'bitcode'
522 dir_include_internal = dir_include_pkg / 'internal'
523 dir_include_server = dir_include_pkg / 'server'
524 dir_include_extension = dir_include_server / 'extension'
525 dir_data_extension = dir_data / 'extension'
529 ###############################################################
530 # Search paths, preparation for compiler tests
532 # NB: Arguments added later are not automatically used for subsequent
533 # configuration-time checks (so they are more isolated). If they should be
534 # used, they need to be added to test_c_args as well.
535 ###############################################################
537 postgres_inc = [include_directories(postgres_inc_d)]
538 test_lib_d = postgres_lib_d
539 test_c_args = cppflags + cflags
543 ###############################################################
544 # Library: bsd-auth
545 ###############################################################
547 bsd_authopt = get_option('bsd_auth')
548 bsd_auth = not_found_dep
549 if cc.check_header('bsd_auth.h', required: bsd_authopt,
550     args: test_c_args, include_directories: postgres_inc)
551   cdata.set('USE_BSD_AUTH', 1)
552   bsd_auth = declare_dependency()
553 endif
557 ###############################################################
558 # Library: bonjour
560 # For now don't search for DNSServiceRegister in a library - only Apple's
561 # Bonjour implementation, which is always linked, works.
562 ###############################################################
564 bonjouropt = get_option('bonjour')
565 bonjour = not_found_dep
566 if cc.check_header('dns_sd.h', required: bonjouropt,
567     args: test_c_args, include_directories: postgres_inc) and \
568    cc.has_function('DNSServiceRegister',
569     args: test_c_args, include_directories: postgres_inc)
570   cdata.set('USE_BONJOUR', 1)
571   bonjour = declare_dependency()
572 endif
576 ###############################################################
577 # Option: docs in HTML and man page format
578 ###############################################################
580 docs_opt = get_option('docs')
581 docs_dep = not_found_dep
582 if not docs_opt.disabled()
583   if xmllint_bin.found() and xsltproc_bin.found()
584     docs_dep = declare_dependency()
585   elif docs_opt.enabled()
586     error('missing required tools for docs in HTML / man page format')
587   endif
588 endif
592 ###############################################################
593 # Option: docs in PDF format
594 ###############################################################
596 docs_pdf_opt = get_option('docs_pdf')
597 docs_pdf_dep = not_found_dep
598 if not docs_pdf_opt.disabled()
599   fop = find_program(get_option('FOP'), native: true, required: docs_pdf_opt)
600   if xmllint_bin.found() and xsltproc_bin.found() and fop.found()
601     docs_pdf_dep = declare_dependency()
602   elif docs_pdf_opt.enabled()
603     error('missing required tools for docs in PDF format')
604   endif
605 endif
609 ###############################################################
610 # Library: GSSAPI
611 ###############################################################
613 gssapiopt = get_option('gssapi')
614 krb_srvtab = ''
615 have_gssapi = false
616 if not gssapiopt.disabled()
617   gssapi = dependency('krb5-gssapi', required: gssapiopt)
618   have_gssapi = gssapi.found()
620   if not have_gssapi
621   elif cc.check_header('gssapi/gssapi.h', dependencies: gssapi, required: false,
622       args: test_c_args, include_directories: postgres_inc)
623     cdata.set('HAVE_GSSAPI_GSSAPI_H', 1)
624   elif cc.check_header('gssapi.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
625     cdata.set('HAVE_GSSAPI_H', 1)
626   else
627     have_gssapi = false
628   endif
630   if not have_gssapi
631   elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
632       args: test_c_args, include_directories: postgres_inc)
633     cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
634   elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
635     cdata.set('HAVE_GSSAPI_EXT_H', 1)
636   else
637     have_gssapi = false
638   endif
640   if not have_gssapi
641   elif cc.has_function('gss_store_cred_into', dependencies: gssapi,
642       args: test_c_args, include_directories: postgres_inc)
643     cdata.set('ENABLE_GSS', 1)
645     krb_srvtab = 'FILE:/@0@/krb5.keytab)'.format(get_option('sysconfdir'))
646     cdata.set_quoted('PG_KRB_SRVTAB', krb_srvtab)
647   elif gssapiopt.enabled()
648     error('''could not find function 'gss_store_cred_into' required for GSSAPI''')
649   else
650     have_gssapi = false
651   endif
652 endif
653 if not have_gssapi
654   gssapi = not_found_dep
655 endif
659 ###############################################################
660 # Library: ldap
661 ###############################################################
663 ldapopt = get_option('ldap')
664 if ldapopt.disabled()
665   ldap = not_found_dep
666   ldap_r = not_found_dep
667 elif host_system == 'windows'
668   ldap = cc.find_library('wldap32', required: ldapopt)
669   ldap_r = ldap
670 else
671   # macos framework dependency is buggy for ldap (one can argue whether it's
672   # Apple's or meson's fault), leading to an endless recursion with ldap.h
673   # including itself. See https://github.com/mesonbuild/meson/issues/10002
674   # Luckily we only need pkg-config support, so the workaround isn't
675   # complicated.
676   ldap = dependency('ldap', method: 'pkg-config', required: false)
677   ldap_r = ldap
679   # Before 2.5 openldap didn't have a pkg-config file, and it might not be
680   # installed
681   if not ldap.found()
682     ldap = cc.find_library('ldap', required: ldapopt, dirs: test_lib_d,
683       has_headers: 'ldap.h', header_include_directories: postgres_inc)
685     # The separate ldap_r library only exists in OpenLDAP < 2.5, and if we
686     # have 2.5 or later, we shouldn't even probe for ldap_r (we might find a
687     # library from a separate OpenLDAP installation).  The most reliable
688     # way to check that is to check for a function introduced in 2.5.
689     if not ldap.found()
690       # don't have ldap, we shouldn't check for ldap_r
691     elif cc.has_function('ldap_verify_credentials',
692         dependencies: ldap, args: test_c_args)
693       ldap_r = ldap # ldap >= 2.5, no need for ldap_r
694     else
696       # Use ldap_r for FE if available, else assume ldap is thread-safe.
697       ldap_r = cc.find_library('ldap_r', required: false, dirs: test_lib_d,
698         has_headers: 'ldap.h', header_include_directories: postgres_inc)
699       if not ldap_r.found()
700         ldap_r = ldap
701       else
702         # On some platforms ldap_r fails to link without PTHREAD_LIBS.
703         ldap_r = declare_dependency(dependencies: [ldap_r, thread_dep])
704       endif
706       # PostgreSQL sometimes loads libldap_r and plain libldap into the same
707       # process.  Check for OpenLDAP versions known not to tolerate doing so;
708       # assume non-OpenLDAP implementations are safe.  The dblink test suite
709       # exercises the hazardous interaction directly.
710       compat_test_code = '''
711 #include <ldap.h>
712 #if !defined(LDAP_VENDOR_VERSION) || \
713      (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
714       LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
715 choke me
716 #endif
718       if not cc.compiles(compat_test_code,
719           name: 'LDAP implementation compatible',
720           dependencies: ldap, args: test_c_args)
721         warning('''
722 *** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
723 *** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
724 *** also uses LDAP will crash on exit.''')
725       endif
726     endif
727   endif
729   if ldap.found() and cc.has_function('ldap_initialize',
730       dependencies: ldap, args: test_c_args)
731     cdata.set('HAVE_LDAP_INITIALIZE', 1)
732   endif
733 endif
735 if ldap.found()
736   assert(ldap_r.found())
737   cdata.set('USE_LDAP', 1)
738 else
739   assert(not ldap_r.found())
740 endif
744 ###############################################################
745 # Library: LLVM
746 ###############################################################
748 llvmopt = get_option('llvm')
749 llvm = not_found_dep
750 if add_languages('cpp', required: llvmopt, native: false)
751   llvm = dependency('llvm', version: '>=3.9', method: 'config-tool', required: llvmopt)
753   if llvm.found()
755     cdata.set('USE_LLVM', 1)
757     cpp = meson.get_compiler('cpp')
759     llvm_binpath = llvm.get_variable(configtool: 'bindir')
761     ccache = find_program('ccache', native: true, required: false)
762     clang = find_program(llvm_binpath / 'clang', required: true)
763   endif
764 elif llvmopt.auto()
765   message('llvm requires a C++ compiler')
766 endif
770 ###############################################################
771 # Library: icu
772 ###############################################################
774 icuopt = get_option('icu')
775 if not icuopt.disabled()
776   icu = dependency('icu-uc', required: icuopt)
777   icu_i18n = dependency('icu-i18n', required: icuopt)
779   if icu.found()
780     cdata.set('USE_ICU', 1)
781   endif
783 else
784   icu = not_found_dep
785   icu_i18n = not_found_dep
786 endif
790 ###############################################################
791 # Library: libxml
792 ###############################################################
794 libxmlopt = get_option('libxml')
795 if not libxmlopt.disabled()
796   libxml = dependency('libxml-2.0', required: libxmlopt, version: '>= 2.6.23')
798   if libxml.found()
799     cdata.set('USE_LIBXML', 1)
800   endif
801 else
802   libxml = not_found_dep
803 endif
807 ###############################################################
808 # Library: libxslt
809 ###############################################################
811 libxsltopt = get_option('libxslt')
812 if not libxsltopt.disabled()
813   libxslt = dependency('libxslt', required: libxsltopt)
815   if libxslt.found()
816     cdata.set('USE_LIBXSLT', 1)
817   endif
818 else
819   libxslt = not_found_dep
820 endif
824 ###############################################################
825 # Library: lz4
826 ###############################################################
828 lz4opt = get_option('lz4')
829 if not lz4opt.disabled()
830   lz4 = dependency('liblz4', required: lz4opt)
832   if lz4.found()
833     cdata.set('USE_LZ4', 1)
834     cdata.set('HAVE_LIBLZ4', 1)
835   endif
837 else
838   lz4 = not_found_dep
839 endif
843 ###############################################################
844 # Library: Tcl (for pltcl)
846 # NB: tclConfig.sh is used in autoconf build for getting
847 # TCL_SHARED_BUILD, TCL_INCLUDE_SPEC, TCL_LIBS and TCL_LIB_SPEC
848 # variables. For now we have not seen a need to copy
849 # that behaviour to the meson build.
850 ###############################################################
852 tclopt = get_option('pltcl')
853 tcl_version = get_option('tcl_version')
854 tcl_dep = not_found_dep
855 if not tclopt.disabled()
857   # via pkg-config
858   tcl_dep = dependency(tcl_version, required: false)
860   if not tcl_dep.found()
861     tcl_dep = cc.find_library(tcl_version,
862       required: tclopt,
863       dirs: test_lib_d)
864   endif
866   if not cc.has_header('tcl.h', dependencies: tcl_dep, required: tclopt)
867     tcl_dep = not_found_dep
868   endif
869 endif
873 ###############################################################
874 # Library: pam
875 ###############################################################
877 pamopt = get_option('pam')
878 if not pamopt.disabled()
879   pam = dependency('pam', required: false)
881   if not pam.found()
882     pam = cc.find_library('pam', required: pamopt, dirs: test_lib_d)
883   endif
885   if pam.found()
886     pam_header_found = false
888     # header file <security/pam_appl.h> or <pam/pam_appl.h> is required for PAM.
889     if cc.check_header('security/pam_appl.h', dependencies: pam, required: false,
890         args: test_c_args, include_directories: postgres_inc)
891       cdata.set('HAVE_SECURITY_PAM_APPL_H', 1)
892       pam_header_found = true
893     elif cc.check_header('pam/pam_appl.h', dependencies: pam, required: pamopt,
894         args: test_c_args, include_directories: postgres_inc)
895       cdata.set('HAVE_PAM_PAM_APPL_H', 1)
896       pam_header_found = true
897     endif
899     if pam_header_found
900       cdata.set('USE_PAM', 1)
901     else
902       pam = not_found_dep
903     endif
904   endif
905 else
906   pam = not_found_dep
907 endif
911 ###############################################################
912 # Library: Perl (for plperl)
913 ###############################################################
915 perlopt = get_option('plperl')
916 perl_dep = not_found_dep
917 if not perlopt.disabled()
918   perl_may_work = true
920   # First verify that perl has the necessary dependencies installed
921   perl_mods = run_command(
922     [perl,
923      '-MConfig', '-MOpcode', '-MExtUtils::Embed', '-MExtUtils::ParseXS',
924      '-e', ''],
925     check: false)
926   if perl_mods.returncode() != 0
927     perl_may_work = false
928     perl_msg = 'perl installation does not have the required modules'
929   endif
931   # Then inquire perl about its configuration
932   if perl_may_work
933     perl_conf_cmd = [perl, '-MConfig', '-e', 'print $Config{$ARGV[0]}']
934     perlversion = run_command(perl_conf_cmd, 'api_versionstring', check: true).stdout()
935     archlibexp = run_command(perl_conf_cmd, 'archlibexp', check: true).stdout()
936     privlibexp = run_command(perl_conf_cmd, 'privlibexp', check: true).stdout()
937     useshrplib = run_command(perl_conf_cmd, 'useshrplib', check: true).stdout()
939     perl_inc_dir = '@0@/CORE'.format(archlibexp)
941     if perlversion.version_compare('< 5.14')
942       perl_may_work = false
943       perl_msg = 'Perl version 5.14 or later is required, but this is @0@'.format(perlversion)
944     elif useshrplib != 'true'
945       perl_may_work = false
946       perl_msg = 'need a shared perl'
947     endif
948   endif
950   if perl_may_work
951     # On most platforms, archlibexp is also where the Perl include files live ...
952     perl_ccflags = ['-I@0@'.format(perl_inc_dir)]
953     # ... but on newer macOS versions, we must use -iwithsysroot to look
954     # under sysroot
955     if not fs.is_file('@0@/perl.h'.format(perl_inc_dir)) and \
956        fs.is_file('@0@@1@/perl.h'.format(pg_sysroot, perl_inc_dir))
957       perl_ccflags = ['-iwithsysroot', perl_inc_dir]
958     endif
960     # check compiler finds header
961     if not cc.has_header('perl.h', required: false,
962         args: test_c_args + perl_ccflags, include_directories: postgres_inc)
963       perl_may_work = false
964       perl_msg = 'missing perl.h'
965     endif
966   endif
968   if perl_may_work
969     perl_ccflags_r = run_command(perl_conf_cmd, 'ccflags', check: true).stdout()
971     # See comments for PGAC_CHECK_PERL_EMBED_CCFLAGS in perl.m4
972     foreach flag : perl_ccflags_r.split(' ')
973       if flag.startswith('-D') and \
974           (not flag.startswith('-D_') or flag == '_USE_32BIT_TIME_T')
975         perl_ccflags += flag
976       endif
977     endforeach
979     if host_system == 'windows'
980       perl_ccflags += ['-DPLPERL_HAVE_UID_GID']
982       if cc.get_id() == 'msvc'
983         # prevent binary mismatch between MSVC built plperl and Strawberry or
984         # msys ucrt perl libraries
985         perl_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
986       endif
987     endif
989     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
990     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
992     # We are after Embed's ldopts, but without the subset mentioned in
993     # Config's ccdlflags and ldflags.  (Those are the choices of those who
994     # built the Perl installation, which are not necessarily appropriate
995     # for building PostgreSQL.)
996     ldopts = run_command(perl, '-MExtUtils::Embed', '-e', 'ldopts', check: true).stdout().strip()
997     undesired = run_command(perl_conf_cmd, 'ccdlflags', check: true).stdout().split()
998     undesired += run_command(perl_conf_cmd, 'ldflags', check: true).stdout().split()
1000     perl_ldopts = []
1001     foreach ldopt : ldopts.split(' ')
1002       if ldopt == '' or ldopt in undesired
1003         continue
1004       endif
1006       perl_ldopts += ldopt.strip('"')
1007     endforeach
1009     message('LDFLAGS recommended by perl: "@0@"'.format(ldopts))
1010     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1012     perl_dep_int = declare_dependency(
1013       compile_args: perl_ccflags,
1014       link_args: perl_ldopts,
1015       version: perlversion,
1016     )
1018     # While we're at it, check that we can link to libperl.
1019     # On most platforms, if perl.h is there then libperl.so will be too, but
1020     # at this writing Debian packages them separately.
1021     perl_link_test = '''
1022 /* see plperl.h */
1023 #ifdef _MSC_VER
1024 #define __inline__ inline
1025 #endif
1026 #include <EXTERN.h>
1027 #include <perl.h>
1028 int main(void)
1030 perl_alloc();
1031 }'''
1032     if not cc.links(perl_link_test, name: 'libperl',
1033           args: test_c_args + perl_ccflags + perl_ldopts,
1034           include_directories: postgres_inc)
1035       perl_may_work = false
1036       perl_msg = 'missing libperl'
1037     endif
1039   endif # perl_may_work
1041   if perl_may_work
1042     perl_dep = perl_dep_int
1043   else
1044     if perlopt.enabled()
1045       error('dependency plperl failed: @0@'.format(perl_msg))
1046     else
1047       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1048     endif
1049   endif
1050 endif
1054 ###############################################################
1055 # Library: Python (for plpython)
1056 ###############################################################
1058 pyopt = get_option('plpython')
1059 if not pyopt.disabled()
1060   pm = import('python')
1061   python3_inst = pm.find_installation(required: pyopt)
1062   python3_dep = python3_inst.dependency(embed: true, required: pyopt)
1063   if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt)
1064     python3_dep = not_found_dep
1065   endif
1066 else
1067   python3_dep = not_found_dep
1068 endif
1072 ###############################################################
1073 # Library: Readline
1074 ###############################################################
1076 if not get_option('readline').disabled()
1077   libedit_preferred = get_option('libedit_preferred')
1078   # Set the order of readline dependencies
1079   check_readline_deps = libedit_preferred ? \
1080     ['libedit', 'readline'] : ['readline', 'libedit']
1082   foreach readline_dep : check_readline_deps
1083     readline = dependency(readline_dep, required: false)
1084     if not readline.found()
1085       readline = cc.find_library(readline_dep,
1086         required: get_option('readline'),
1087         dirs: test_lib_d)
1088     endif
1089     if readline.found()
1090       break
1091     endif
1092   endforeach
1094   if readline.found()
1095     cdata.set('HAVE_LIBREADLINE', 1)
1097     editline_prefix = {
1098       'header_prefix': 'editline/',
1099       'flag_prefix': 'EDITLINE_',
1100     }
1101     readline_prefix = {
1102       'header_prefix': 'readline/',
1103       'flag_prefix': 'READLINE_',
1104     }
1105     default_prefix = {
1106       'header_prefix': '',
1107       'flag_prefix': '',
1108     }
1110     # Set the order of prefixes
1111     prefixes = libedit_preferred ? \
1112       [editline_prefix, default_prefix, readline_prefix] : \
1113       [readline_prefix, default_prefix, editline_prefix]
1115     at_least_one_header_found = false
1116     foreach header : ['history', 'readline']
1117       is_found = false
1118       foreach prefix : prefixes
1119         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1120         # Check history.h and readline.h
1121         if not is_found and cc.has_header(header_file,
1122             args: test_c_args, include_directories: postgres_inc,
1123             dependencies: [readline], required: false)
1124           if header == 'readline'
1125             readline_h = header_file
1126           endif
1127           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1128           is_found = true
1129           at_least_one_header_found = true
1130         endif
1131       endforeach
1132     endforeach
1134     if not at_least_one_header_found
1135       error('''readline header not found
1136 If you have @0@ already installed, see meson-log/meson-log.txt for details on the
1137 failure. It is possible the compiler isn't looking in the proper directory.
1138 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1139     endif
1141     check_funcs = [
1142       'append_history',
1143       'history_truncate_file',
1144       'rl_completion_matches',
1145       'rl_filename_completion_function',
1146       'rl_reset_screen_size',
1147       'rl_variable_bind',
1148     ]
1150     foreach func : check_funcs
1151       found = cc.has_function(func, dependencies: [readline],
1152         args: test_c_args, include_directories: postgres_inc)
1153       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1154     endforeach
1156     check_vars = [
1157       'rl_completion_suppress_quote',
1158       'rl_filename_quote_characters',
1159       'rl_filename_quoting_function',
1160     ]
1162     foreach var : check_vars
1163       cdata.set('HAVE_' + var.to_upper(),
1164         cc.has_header_symbol(readline_h, var,
1165           args: test_c_args, include_directories: postgres_inc,
1166           prefix: '#include <stdio.h>',
1167           dependencies: [readline]) ? 1 : false)
1168     endforeach
1170     # If found via cc.find_library() ensure headers are found when using the
1171     # dependency. On meson < 0.57 one cannot do compiler checks using the
1172     # dependency returned by declare_dependency(), so we can't do this above.
1173     if readline.type_name() == 'library'
1174       readline = declare_dependency(dependencies: readline,
1175         include_directories: postgres_inc)
1176     endif
1178     # On windows with mingw readline requires auto-import to successfully
1179     # link, as the headers don't use declspec(dllimport)
1180     if host_system == 'windows' and cc.get_id() != 'msvc'
1181       readline = declare_dependency(dependencies: readline,
1182         link_args: '-Wl,--enable-auto-import')
1183     endif
1184   endif
1186   # XXX: Figure out whether to implement mingw warning equivalent
1187 else
1188   readline = not_found_dep
1189 endif
1193 ###############################################################
1194 # Library: selinux
1195 ###############################################################
1197 selinux = not_found_dep
1198 selinuxopt = get_option('selinux')
1199 if meson.version().version_compare('>=0.59')
1200   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1201 endif
1202 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1203 cdata.set('HAVE_LIBSELINUX',
1204   selinux.found() ? 1 : false)
1208 ###############################################################
1209 # Library: systemd
1210 ###############################################################
1212 systemd = not_found_dep
1213 systemdopt = get_option('systemd')
1214 if meson.version().version_compare('>=0.59')
1215   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1216 endif
1217 systemd = dependency('libsystemd', required: systemdopt)
1218 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1222 ###############################################################
1223 # Library: SSL
1224 ###############################################################
1226 ssl = not_found_dep
1227 ssl_library = 'none'
1228 sslopt = get_option('ssl')
1230 if sslopt == 'auto' and auto_features.disabled()
1231   sslopt = 'none'
1232 endif
1234 if sslopt in ['auto', 'openssl']
1235   openssl_required = (sslopt == 'openssl')
1237   # Try to find openssl via pkg-config et al, if that doesn't work
1238   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1239   # the library names that we know about.
1241   # via pkg-config et al
1242   ssl = dependency('openssl', required: false)
1243   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1244   # we pass cc.find_library() results if necessary
1245   ssl_int = []
1247   # via library + headers
1248   if not ssl.found()
1249     ssl_lib = cc.find_library('ssl',
1250       dirs: test_lib_d,
1251       header_include_directories: postgres_inc,
1252       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1253       required: openssl_required)
1254     crypto_lib = cc.find_library('crypto',
1255       dirs: test_lib_d,
1256       required: openssl_required)
1257     if ssl_lib.found() and crypto_lib.found()
1258       ssl_int = [ssl_lib, crypto_lib]
1259       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1260     endif
1261   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1262        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1263     ssl_int = [ssl]
1264   else
1265     ssl = not_found_dep
1266   endif
1268   if ssl.found()
1269     check_funcs = [
1270       ['CRYPTO_new_ex_data', {'required': true}],
1271       ['SSL_new', {'required': true}],
1273       # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
1274       ['SSL_CTX_set_cert_cb'],
1276       # Functions introduced in OpenSSL 1.1.0. We used to check for
1277       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1278       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1279       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1280       # functions.
1281       ['OPENSSL_init_ssl'],
1282       ['BIO_get_data'],
1283       ['BIO_meth_new'],
1284       ['ASN1_STRING_get0_data'],
1285       ['HMAC_CTX_new'],
1286       ['HMAC_CTX_free'],
1288       # OpenSSL versions before 1.1.0 required setting callback functions, for
1289       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1290       # function was removed.
1291       ['CRYPTO_lock'],
1293       # Function introduced in OpenSSL 1.1.1
1294       ['X509_get_signature_info'],
1295     ]
1297     are_openssl_funcs_complete = true
1298     foreach c : check_funcs
1299       func = c.get(0)
1300       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1301       required = c.get(1, {}).get('required', false)
1302       if required and not val
1303         are_openssl_funcs_complete = false
1304         if openssl_required
1305           error('openssl function @0@ is required'.format(func))
1306         endif
1307         break
1308       elif not required
1309         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1310       endif
1311     endforeach
1313     if are_openssl_funcs_complete
1314       cdata.set('USE_OPENSSL', 1,
1315                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1316       cdata.set('OPENSSL_API_COMPAT', '0x10002000L',
1317                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1318       ssl_library = 'openssl'
1319     else
1320       ssl = not_found_dep
1321     endif
1322   endif
1323 endif
1325 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1326   error('no SSL library found')
1327 endif
1331 ###############################################################
1332 # Library: uuid
1333 ###############################################################
1335 uuidopt = get_option('uuid')
1336 if uuidopt != 'none'
1337   uuidname = uuidopt.to_upper()
1338   if uuidopt == 'e2fs'
1339     uuid = dependency('uuid', required: true)
1340     uuidfunc = 'uuid_generate'
1341     uuidheader = 'uuid/uuid.h'
1342   elif uuidopt == 'bsd'
1343     # libc should have uuid function
1344     uuid = declare_dependency()
1345     uuidfunc = 'uuid_to_string'
1346     uuidheader = 'uuid.h'
1347   elif uuidopt == 'ossp'
1348     uuid = dependency('ossp-uuid', required: true)
1349     uuidfunc = 'uuid_export'
1350     uuidheader = 'uuid.h'
1351   else
1352     error('unknown uuid build option value: @0@'.format(uuidopt))
1353   endif
1355   if not cc.has_header_symbol(uuidheader, uuidfunc, args: test_c_args, dependencies: uuid)
1356     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1357   endif
1358   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1360   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1361            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1362 else
1363   uuid = not_found_dep
1364 endif
1368 ###############################################################
1369 # Library: zlib
1370 ###############################################################
1372 zlibopt = get_option('zlib')
1373 zlib = not_found_dep
1374 if not zlibopt.disabled()
1375   zlib_t = dependency('zlib', required: zlibopt)
1377   if zlib_t.type_name() == 'internal'
1378     # if fallback was used, we don't need to test if headers are present (they
1379     # aren't built yet, so we can't test)
1380     zlib = zlib_t
1381   elif not zlib_t.found()
1382     warning('did not find zlib')
1383   elif not cc.has_header('zlib.h',
1384       args: test_c_args, include_directories: postgres_inc,
1385       dependencies: [zlib_t], required: zlibopt)
1386     warning('zlib header not found')
1387   else
1388     zlib = zlib_t
1389   endif
1391   if zlib.found()
1392     cdata.set('HAVE_LIBZ', 1)
1393   endif
1394 endif
1398 ###############################################################
1399 # Library: tap test dependencies
1400 ###############################################################
1402 # Check whether tap tests are enabled or not
1403 tap_tests_enabled = false
1404 tapopt = get_option('tap_tests')
1405 if not tapopt.disabled()
1406   # Checking for perl modules for tap tests
1407   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1408   if perl_ipc_run_check.returncode() != 0
1409     message(perl_ipc_run_check.stderr().strip())
1410     if tapopt.enabled()
1411       error('Additional Perl modules are required to run TAP tests.')
1412     else
1413       warning('Additional Perl modules are required to run TAP tests.')
1414     endif
1415   else
1416     tap_tests_enabled = true
1417   endif
1418 endif
1422 ###############################################################
1423 # Library: zstd
1424 ###############################################################
1426 zstdopt = get_option('zstd')
1427 if not zstdopt.disabled()
1428   zstd = dependency('libzstd', required: zstdopt, version: '>=1.4.0')
1430   if zstd.found()
1431     cdata.set('USE_ZSTD', 1)
1432     cdata.set('HAVE_LIBZSTD', 1)
1433   endif
1435 else
1436   zstd = not_found_dep
1437 endif
1441 ###############################################################
1442 # Compiler tests
1443 ###############################################################
1445 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1446 # unnecessarily, because we optionally rely on newer features.
1447 c99_test = '''
1448 #include <stdbool.h>
1449 #include <complex.h>
1450 #include <tgmath.h>
1451 #include <inttypes.h>
1453 struct named_init_test {
1454   int a;
1455   int b;
1458 extern void structfunc(struct named_init_test);
1460 int main(int argc, char **argv)
1462   struct named_init_test nit = {
1463     .a = 3,
1464     .b = 5,
1465   };
1467   for (int loop_var = 0; loop_var < 3; loop_var++)
1468   {
1469     nit.a += nit.b;
1470   }
1472   structfunc((struct named_init_test){1, 0});
1474   return nit.a != 0;
1478 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1479   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1480         args: test_c_args + ['-std=c99'])
1481     test_c_args += '-std=c99'
1482     cflags += '-std=c99'
1483   else
1484     error('C compiler does not support C99')
1485   endif
1486 endif
1488 sizeof_long = cc.sizeof('long', args: test_c_args)
1489 cdata.set('SIZEOF_LONG', sizeof_long)
1490 if sizeof_long == 8
1491   cdata.set('HAVE_LONG_INT_64', 1)
1492   cdata.set('PG_INT64_TYPE', 'long int')
1493   cdata.set_quoted('INT64_MODIFIER', 'l')
1494 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1495   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1496   cdata.set('PG_INT64_TYPE', 'long long int')
1497   cdata.set_quoted('INT64_MODIFIER', 'll')
1498 else
1499   error('do not know how to get a 64bit int')
1500 endif
1502 if host_machine.endian() == 'big'
1503   cdata.set('WORDS_BIGENDIAN', 1)
1504 endif
1506 alignof_types = ['short', 'int', 'long', 'double']
1507 maxalign = 0
1508 foreach t : alignof_types
1509   align = cc.alignment(t, args: test_c_args)
1510   if maxalign < align
1511     maxalign = align
1512   endif
1513   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1514 endforeach
1515 cdata.set('MAXIMUM_ALIGNOF', maxalign)
1517 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1518 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1521 # Check if __int128 is a working 128 bit integer type, and if so
1522 # define PG_INT128_TYPE to that typename.
1524 # This currently only detects a GCC/clang extension, but support for other
1525 # environments may be added in the future.
1527 # For the moment we only test for support for 128bit math; support for
1528 # 128bit literals and snprintf is not required.
1529 if cc.links('''
1530   /*
1531    * We don't actually run this test, just link it to verify that any support
1532    * functions needed for __int128 are present.
1533    *
1534    * These are globals to discourage the compiler from folding all the
1535    * arithmetic tests down to compile-time constants.  We do not have
1536    * convenient support for 128bit literals at this point...
1537    */
1538   __int128 a = 48828125;
1539   __int128 b = 97656250;
1541   int main(void)
1542   {
1543       __int128 c,d;
1544       a = (a << 12) + 1; /* 200000000001 */
1545       b = (b << 12) + 5; /* 400000000005 */
1546       /* try the most relevant arithmetic ops */
1547       c = a * b;
1548       d = (c + b) / b;
1549       /* must use the results, else compiler may optimize arithmetic away */
1550       return d != a+1;
1551   }''',
1552   name: '__int128',
1553   args: test_c_args)
1555   buggy_int128 = false
1557   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1558   # If not cross-compiling, we can test for bugs and disable use of __int128
1559   # with buggy compilers.  If cross-compiling, hope for the best.
1560   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1561   if not meson.is_cross_build()
1562     r = cc.run('''
1563     /* This must match the corresponding code in c.h: */
1564     #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
1565     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1566     #elif defined(_MSC_VER)
1567     #define pg_attribute_aligned(a) __declspec(align(a))
1568     #endif
1569     typedef __int128 int128a
1570     #if defined(pg_attribute_aligned)
1571     pg_attribute_aligned(8)
1572     #endif
1573     ;
1575     int128a holder;
1576     void pass_by_val(void *buffer, int128a par) { holder = par; }
1578     int main(void)
1579     {
1580         long int i64 = 97656225L << 12;
1581         int128a q;
1582         pass_by_val(main, (int128a) i64);
1583         q = (int128a) i64;
1584         return q != holder;
1585     }''',
1586     name: '__int128 alignment bug',
1587     args: test_c_args)
1588     assert(r.compiled())
1589     if r.returncode() != 0
1590       buggy_int128 = true
1591       message('__int128 support present but buggy and thus disabled')
1592     endif
1593   endif
1595   if not buggy_int128
1596     cdata.set('PG_INT128_TYPE', '__int128')
1597     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1598   endif
1599 endif
1602 # Check if the C compiler knows computed gotos (gcc extension, also
1603 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1605 # Checking whether computed gotos are supported syntax-wise ought to
1606 # be enough, as the syntax is otherwise illegal.
1607 if cc.compiles('''
1608     static inline int foo(void)
1609     {
1610       void *labeladdrs[] = {&&my_label};
1611       goto *labeladdrs[0];
1612       my_label:
1613       return 1;
1614     }''',
1615     args: test_c_args)
1616   cdata.set('HAVE_COMPUTED_GOTO', 1)
1617 endif
1620 # Check if the C compiler understands _Static_assert(),
1621 # and define HAVE__STATIC_ASSERT if so.
1623 # We actually check the syntax ({ _Static_assert(...) }), because we need
1624 # gcc-style compound expressions to be able to wrap the thing into macros.
1625 if cc.compiles('''
1626     int main(int arg, char **argv)
1627     {
1628         ({ _Static_assert(1, "foo"); });
1629     }
1630     ''',
1631     args: test_c_args)
1632   cdata.set('HAVE__STATIC_ASSERT', 1)
1633 endif
1636 # We use <stdbool.h> if we have it and it declares type bool as having
1637 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1638 if cc.has_type('_Bool', args: test_c_args) \
1639     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1640     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1641   cdata.set('HAVE__BOOL', 1)
1642   cdata.set('PG_USE_STDBOOL', 1)
1643 endif
1646 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1647 # warning for each use of %m.
1648 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1649 testsrc = '''
1650 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1651 static void call_log(void)
1653     emit_log(0, "error: %s: %m", "foo");
1656 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1657 foreach a : printf_attributes
1658   if cc.compiles(testsrc.format(a),
1659       args: test_c_args + attrib_error_args, name: 'format ' + a)
1660     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1661     break
1662   endif
1663 endforeach
1666 if cc.has_function_attribute('visibility:default') and \
1667     cc.has_function_attribute('visibility:hidden')
1668   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1670   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1671   # inlineshidden to C code as well... And either way, we want to put these
1672   # flags into exported files (pgxs, .pc files).
1673   cflags_mod += '-fvisibility=hidden'
1674   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1675   ldflags_mod += '-fvisibility=hidden'
1676 endif
1679 # Check if various builtins exist. Some builtins are tested separately,
1680 # because we want to test something more complicated than the generic case.
1681 builtins = [
1682   'bswap16',
1683   'bswap32',
1684   'bswap64',
1685   'clz',
1686   'ctz',
1687   'constant_p',
1688   'frame_address',
1689   'popcount',
1690   'unreachable',
1693 foreach builtin : builtins
1694   fname = '__builtin_@0@'.format(builtin)
1695   if cc.has_function(fname, args: test_c_args)
1696     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1697   endif
1698 endforeach
1701 # Check if the C compiler understands __builtin_types_compatible_p,
1702 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1704 # We check usage with __typeof__, though it's unlikely any compiler would
1705 # have the former and not the latter.
1706 if cc.compiles('''
1707     static int x;
1708     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1709     ''',
1710     name: '__builtin_types_compatible_p',
1711     args: test_c_args)
1712   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1713 endif
1716 # Check if the C compiler understands __builtin_$op_overflow(),
1717 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1719 # Check for the most complicated case, 64 bit multiplication, as a
1720 # proxy for all of the operations.  To detect the case where the compiler
1721 # knows the function but library support is missing, we must link not just
1722 # compile, and store the results in global variables so the compiler doesn't
1723 # optimize away the call.
1724 if cc.links('''
1725     INT64 a = 1;
1726     INT64 b = 1;
1727     INT64 result;
1729     int main(void)
1730     {
1731         return __builtin_mul_overflow(a, b, &result);
1732     }''',
1733     name: '__builtin_mul_overflow',
1734     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1735     )
1736   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1737 endif
1740 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1741 # here. To prevent problems due to two detection methods working, stop
1742 # checking after one.
1743 if cc.links('''
1744     #include <cpuid.h>
1745     int main(int arg, char **argv)
1746     {
1747         unsigned int exx[4] = {0, 0, 0, 0};
1748         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1749     }
1750     ''', name: '__get_cpuid',
1751     args: test_c_args)
1752   cdata.set('HAVE__GET_CPUID', 1)
1753 elif cc.links('''
1754     #include <intrin.h>
1755     int main(int arg, char **argv)
1756     {
1757         unsigned int exx[4] = {0, 0, 0, 0};
1758         __cpuid(exx, 1);
1759     }
1760     ''', name: '__cpuid',
1761     args: test_c_args)
1762   cdata.set('HAVE__CPUID', 1)
1763 endif
1766 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1767 # versions of clang do not understand -fexcess-precision=standard, the use of
1768 # x87 floating point operations leads to problems like isinf possibly returning
1769 # false for a value that is infinite when converted from the 80bit register to
1770 # the 8byte memory representation.
1772 # Only perform the test if the compiler doesn't understand
1773 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1774 # automatically.
1775 if '-fexcess-precision=standard' not in cflags
1776   if not cc.compiles('''
1777 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1778 choke me
1779 #endif''',
1780       name: '', args: test_c_args)
1781     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1782   endif
1783 endif
1787 ###############################################################
1788 # Compiler flags
1789 ###############################################################
1791 common_functional_flags = [
1792   # Disable strict-aliasing rules; needed for gcc 3.3+
1793   '-fno-strict-aliasing',
1794   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1795   '-fwrapv',
1796   '-fexcess-precision=standard',
1799 cflags += cc.get_supported_arguments(common_functional_flags)
1800 if llvm.found()
1801   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1802 endif
1804 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1805 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1807 common_warning_flags = [
1808   '-Wmissing-prototypes',
1809   '-Wpointer-arith',
1810   # Really don't want VLAs to be used in our dialect of C
1811   '-Werror=vla',
1812   # On macOS, complain about usage of symbols newer than the deployment target
1813   '-Werror=unguarded-availability-new',
1814   '-Wendif-labels',
1815   '-Wmissing-format-attribute',
1816   '-Wimplicit-fallthrough=3',
1817   '-Wcast-function-type',
1818   '-Wshadow=compatible-local',
1819   # This was included in -Wall/-Wformat in older GCC versions
1820   '-Wformat-security',
1823 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1824 if llvm.found()
1825   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1826 endif
1828 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1829 # the result for them
1830 cflags_no_decl_after_statement = []
1831 if cc.has_argument('-Wdeclaration-after-statement')
1832   cflags_warn += '-Wdeclaration-after-statement'
1833   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1834 endif
1837 # The following tests want to suppress various unhelpful warnings by adding
1838 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1839 # switches, so we have to test for the positive form and if that works,
1840 # add the negative form.
1842 negative_warning_flags = [
1843   # Suppress clang's unhelpful unused-command-line-argument warnings.
1844   'unused-command-line-argument',
1846   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
1847   # of warnings when building plperl because of usages in the Perl headers.
1848   'compound-token-split-by-macro',
1850   # Similarly disable useless truncation warnings from gcc 8+
1851   'format-truncation',
1852   'stringop-truncation',
1854   # Suppress clang 16's strict warnings about function casts
1855   'cast-function-type-strict',
1857   # To make warning_level=2 / -Wextra work, we'd need at least the following
1858   # 'clobbered',
1859   # 'missing-field-initializers',
1860   # 'sign-compare',
1861   # 'unused-parameter',
1864 foreach w : negative_warning_flags
1865   if cc.has_argument('-W' + w)
1866     cflags_warn += '-Wno-' + w
1867   endif
1868   if llvm.found() and cpp.has_argument('-W' + w)
1869     cxxflags_warn += '-Wno-' + w
1870   endif
1871 endforeach
1874 # From Project.pm
1875 if cc.get_id() == 'msvc'
1876   cflags_warn += [
1877     '/wd4018', # signed/unsigned mismatch
1878     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
1879     '/wd4273', # inconsistent DLL linkage
1880     '/wd4101', # unreferenced local variable
1881     '/wd4102', # unreferenced label
1882     '/wd4090', # different 'modifier' qualifiers
1883     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
1884   ]
1886   cppflags += [
1887     '/DWIN32',
1888     '/DWINDOWS',
1889     '/D__WINDOWS__',
1890     '/D__WIN32__',
1891     '/D_CRT_SECURE_NO_DEPRECATE',
1892     '/D_CRT_NONSTDC_NO_DEPRECATE',
1893   ]
1895   # We never need export libraries. As link.exe reports their creation, they
1896   # are unnecessarily noisy. Similarly, we don't need import library for
1897   # modules, we only import them dynamically, and they're also noisy.
1898   ldflags += '/NOEXP'
1899   ldflags_mod += '/NOIMPLIB'
1900 endif
1904 ###############################################################
1905 # Atomics
1906 ###############################################################
1908 if not get_option('spinlocks')
1909   warning('Not using spinlocks will cause poor performance')
1910 else
1911   cdata.set('HAVE_SPINLOCKS', 1)
1912 endif
1914 if not get_option('atomics')
1915   warning('Not using atomics will cause poor performance')
1916 else
1917   # XXX: perhaps we should require some atomics support in this case these
1918   # days?
1919   cdata.set('HAVE_ATOMICS', 1)
1921   atomic_checks = [
1922     {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
1923      'desc': '__sync_lock_test_and_set(char)',
1924      'test': '''
1925 char lock = 0;
1926 __sync_lock_test_and_set(&lock, 1);
1927 __sync_lock_release(&lock);'''},
1929     {'name': 'HAVE_GCC__SYNC_INT32_TAS',
1930      'desc': '__sync_lock_test_and_set(int32)',
1931      'test': '''
1932 int lock = 0;
1933 __sync_lock_test_and_set(&lock, 1);
1934 __sync_lock_release(&lock);'''},
1936     {'name': 'HAVE_GCC__SYNC_INT32_CAS',
1937      'desc': '__sync_val_compare_and_swap(int32)',
1938      'test': '''
1939 int val = 0;
1940 __sync_val_compare_and_swap(&val, 0, 37);'''},
1942     {'name': 'HAVE_GCC__SYNC_INT64_CAS',
1943      'desc': '__sync_val_compare_and_swap(int64)',
1944      'test': '''
1945 INT64 val = 0;
1946 __sync_val_compare_and_swap(&val, 0, 37);'''},
1948     {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
1949      'desc': ' __atomic_compare_exchange_n(int32)',
1950      'test': '''
1951 int val = 0;
1952 int expect = 0;
1953 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1955     {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
1956      'desc': ' __atomic_compare_exchange_n(int64)',
1957      'test': '''
1958 INT64 val = 0;
1959 INT64 expect = 0;
1960 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1961   ]
1963   foreach check : atomic_checks
1964     test = '''
1965 int main(void)
1968 }'''.format(check['test'])
1970     cdata.set(check['name'],
1971       cc.links(test,
1972         name: check['desc'],
1973         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
1974     )
1975   endforeach
1977 endif
1981 ###############################################################
1982 # Select CRC-32C implementation.
1984 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
1985 # use the special CRC instructions for calculating CRC-32C. If we're not
1986 # targeting such a processor, but we can nevertheless produce code that uses
1987 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
1988 # implementations and select which one to use at runtime, depending on whether
1989 # SSE 4.2 is supported by the processor we're running on.
1991 # Similarly, if we are targeting an ARM processor that has the CRC
1992 # instructions that are part of the ARMv8 CRC Extension, use them. And if
1993 # we're not targeting such a processor, but can nevertheless produce code that
1994 # uses the CRC instructions, compile both, and select at runtime.
1995 ###############################################################
1997 have_optimized_crc = false
1998 cflags_crc = []
1999 if host_cpu == 'x86' or host_cpu == 'x86_64'
2001   if cc.get_id() == 'msvc'
2002     cdata.set('USE_SSE42_CRC32C', false)
2003     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2004     have_optimized_crc = true
2005   else
2007     prog = '''
2008 #include <nmmintrin.h>
2010 int main(void)
2012     unsigned int crc = 0;
2013     crc = _mm_crc32_u8(crc, 0);
2014     crc = _mm_crc32_u32(crc, 0);
2015     /* return computed value, to prevent the above being optimized away */
2016     return crc == 0;
2020     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2021           args: test_c_args)
2022       # Use Intel SSE 4.2 unconditionally.
2023       cdata.set('USE_SSE42_CRC32C', 1)
2024       have_optimized_crc = true
2025     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2026           args: test_c_args + ['-msse4.2'])
2027       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2028       # the runtime check.
2029       cflags_crc += '-msse4.2'
2030       cdata.set('USE_SSE42_CRC32C', false)
2031       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2032       have_optimized_crc = true
2033     endif
2035   endif
2037 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2039   prog = '''
2040 #include <arm_acle.h>
2042 int main(void)
2044     unsigned int crc = 0;
2045     crc = __crc32cb(crc, 0);
2046     crc = __crc32ch(crc, 0);
2047     crc = __crc32cw(crc, 0);
2048     crc = __crc32cd(crc, 0);
2050     /* return computed value, to prevent the above being optimized away */
2051     return crc == 0;
2055   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2056       args: test_c_args)
2057     # Use ARM CRC Extension unconditionally
2058     cdata.set('USE_ARMV8_CRC32C', 1)
2059     have_optimized_crc = true
2060   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2061       args: test_c_args + ['-march=armv8-a+crc'])
2062     # Use ARM CRC Extension, with runtime check
2063     cflags_crc += '-march=armv8-a+crc'
2064     cdata.set('USE_ARMV8_CRC32C', false)
2065     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2066     have_optimized_crc = true
2067   endif
2069 elif host_cpu == 'loongarch64'
2071   prog = '''
2072 int main(void)
2074     unsigned int crc = 0;
2075     crc = __builtin_loongarch_crcc_w_b_w(0, crc);
2076     crc = __builtin_loongarch_crcc_w_h_w(0, crc);
2077     crc = __builtin_loongarch_crcc_w_w_w(0, crc);
2078     crc = __builtin_loongarch_crcc_w_d_w(0, crc);
2080     /* return computed value, to prevent the above being optimized away */
2081     return crc == 0;
2085   if cc.links(prog, name: '__builtin_loongarch_crcc_w_b_w, __builtin_loongarch_crcc_w_h_w, __builtin_loongarch_crcc_w_w_w, and __builtin_loongarch_crcc_w_d_w',
2086       args: test_c_args)
2087     # Use LoongArch CRC instruction unconditionally
2088     cdata.set('USE_LOONGARCH_CRC32C', 1)
2089     have_optimized_crc = true
2090   endif
2092 endif
2094 if not have_optimized_crc
2095   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2096   # support.
2097   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2098 endif
2102 ###############################################################
2103 # Other CPU specific stuff
2104 ###############################################################
2106 if host_cpu == 'x86_64'
2108   if cc.compiles('''
2109       void main(void)
2110       {
2111           long long x = 1; long long r;
2112           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2113       }''',
2114       name: '@0@: popcntq instruction'.format(host_cpu),
2115       args: test_c_args)
2116     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2117   endif
2119 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2120   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2121   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2122     if cc.compiles('''
2123       static inline int
2124       addi(int ra, int si)
2125       {
2126           int res = 0;
2127           if (__builtin_constant_p(si))
2128               __asm__ __volatile__(
2129                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2130           return res;
2131       }
2132       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2133       ''',
2134       args: test_c_args)
2135       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2136     endif
2137   endif
2138 endif
2142 ###############################################################
2143 # Library / OS tests
2144 ###############################################################
2146 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2147 # unnecessary checks over and over, particularly on windows.
2148 header_checks = [
2149   'atomic.h',
2150   'copyfile.h',
2151   'crtdefs.h',
2152   'execinfo.h',
2153   'getopt.h',
2154   'ifaddrs.h',
2155   'langinfo.h',
2156   'mbarrier.h',
2157   'stdbool.h',
2158   'strings.h',
2159   'sys/epoll.h',
2160   'sys/event.h',
2161   'sys/personality.h',
2162   'sys/prctl.h',
2163   'sys/procctl.h',
2164   'sys/signalfd.h',
2165   'sys/ucred.h',
2166   'termios.h',
2167   'ucred.h',
2170 foreach header : header_checks
2171   varname = 'HAVE_' + header.underscorify().to_upper()
2173   # Emulate autoconf behaviour of not-found->undef, found->1
2174   found = cc.has_header(header,
2175     include_directories: postgres_inc, args: test_c_args)
2176   cdata.set(varname, found ? 1 : false,
2177             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2178 endforeach
2181 decl_checks = [
2182   ['F_FULLFSYNC', 'fcntl.h'],
2183   ['fdatasync', 'unistd.h'],
2184   ['posix_fadvise', 'fcntl.h'],
2185   ['strlcat', 'string.h'],
2186   ['strlcpy', 'string.h'],
2187   ['strnlen', 'string.h'],
2190 # Need to check for function declarations for these functions, because
2191 # checking for library symbols wouldn't handle deployment target
2192 # restrictions on macOS
2193 decl_checks += [
2194   ['preadv', 'sys/uio.h'],
2195   ['pwritev', 'sys/uio.h'],
2198 foreach c : decl_checks
2199   func = c.get(0)
2200   header = c.get(1)
2201   args = c.get(2, {})
2202   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2204   found = cc.has_header_symbol(header, func,
2205     args: test_c_args, include_directories: postgres_inc,
2206     kwargs: args)
2207   cdata.set10(varname, found, description:
2208 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2209    don't.'''.format(func))
2210 endforeach
2213 if cc.has_type('struct option',
2214     args: test_c_args, include_directories: postgres_inc,
2215     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2216   cdata.set('HAVE_STRUCT_OPTION', 1)
2217 endif
2220 foreach c : ['opterr', 'optreset']
2221   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2223   if cc.links('''
2224 #include <unistd.h>
2225 int main(void)
2227     extern int @0@;
2228     @0@ = 1;
2230 '''.format(c), name: c, args: test_c_args)
2231     cdata.set(varname, 1)
2232   else
2233     cdata.set(varname, false)
2234   endif
2235 endforeach
2237 if cc.has_type('socklen_t',
2238     args: test_c_args, include_directories: postgres_inc,
2239     prefix: '''
2240 #include <sys/socket.h>''')
2241   cdata.set('HAVE_SOCKLEN_T', 1)
2242 endif
2244 if cc.has_member('struct sockaddr', 'sa_len',
2245     args: test_c_args, include_directories: postgres_inc,
2246     prefix: '''
2247 #include <sys/types.h>
2248 #include <sys/socket.h>''')
2249   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2250 endif
2252 if cc.has_member('struct tm', 'tm_zone',
2253     args: test_c_args, include_directories: postgres_inc,
2254     prefix: '''
2255 #include <sys/types.h>
2256 #include <time.h>
2257 ''')
2258   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2259 endif
2261 if cc.compiles('''
2262 #include <time.h>
2263 extern int foo(void);
2264 int foo(void)
2266     return timezone / 60;
2268 ''',
2269     name: 'global variable `timezone\' exists',
2270     args: test_c_args, include_directories: postgres_inc)
2271   cdata.set('HAVE_INT_TIMEZONE', 1)
2272 else
2273   cdata.set('HAVE_INT_TIMEZONE', false)
2274 endif
2276 if cc.has_type('union semun',
2277     args: test_c_args,
2278     include_directories: postgres_inc,
2279     prefix: '''
2280 #include <sys/types.h>
2281 #include <sys/ipc.h>
2282 #include <sys/sem.h>
2283 ''')
2284   cdata.set('HAVE_UNION_SEMUN', 1)
2285 endif
2287 if cc.compiles('''
2288 #include <string.h>
2289 int main(void)
2291   char buf[100];
2292   switch (strerror_r(1, buf, sizeof(buf)))
2293   { case 0: break; default: break; }
2294 }''',
2295     name: 'strerror_r',
2296     args: test_c_args, include_directories: postgres_inc)
2297   cdata.set('STRERROR_R_INT', 1)
2298 else
2299   cdata.set('STRERROR_R_INT', false)
2300 endif
2302 # Find the right header file for the locale_t type.  macOS needs xlocale.h;
2303 # standard is locale.h, but glibc <= 2.25 also had an xlocale.h file that
2304 # we should not use so we check the standard header first.  MSVC has a
2305 # replacement defined in src/include/port/win32_port.h.
2306 if not cc.has_type('locale_t', prefix: '#include <locale.h>') and \
2307    cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2308   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2309 endif
2311 # Check if the C compiler understands typeof or a variant.  Define
2312 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2313 foreach kw : ['typeof', '__typeof__', 'decltype']
2314   if cc.compiles('''
2315 int main(void)
2317     int x = 0;
2318     @0@(x) y;
2319     y = x;
2320     return y;
2322 '''.format(kw),
2323     name: 'typeof()',
2324     args: test_c_args, include_directories: postgres_inc)
2326     cdata.set('HAVE_TYPEOF', 1)
2327     if kw != 'typeof'
2328       cdata.set('typeof', kw)
2329     endif
2331     break
2332   endif
2333 endforeach
2336 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2337 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2338 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2339 wcstombs_l_test = '''
2340 #include <stdlib.h>
2341 #include <locale.h>
2344 void main(void)
2346 #ifndef wcstombs_l
2347     (void) wcstombs_l;
2348 #endif
2351 if (not cc.compiles(wcstombs_l_test.format(''),
2352       name: 'wcstombs_l') and
2353     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2354       name: 'wcstombs_l in xlocale.h'))
2355     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2356 endif
2359 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2360 # understands, because it conflicts with __declspec(restrict). Therefore we
2361 # define pg_restrict to the appropriate definition, which presumably won't
2362 # conflict.
2364 # We assume C99 support, so we don't need to make this conditional.
2366 # XXX: Historically we allowed platforms to disable restrict in template
2367 # files, but that was only added for AIX when building with XLC, which we
2368 # don't support yet.
2369 cdata.set('pg_restrict', '__restrict')
2372 # Most libraries are included only if they demonstrably provide a function we
2373 # need, but libm is an exception: always include it, because there are too
2374 # many compilers that play cute optimization games that will break probes for
2375 # standard functions such as pow().
2376 os_deps += cc.find_library('m', required: false)
2378 rt_dep = cc.find_library('rt', required: false)
2380 dl_dep = cc.find_library('dl', required: false)
2382 util_dep = cc.find_library('util', required: false)
2384 getopt_dep = cc.find_library('getopt', required: false)
2385 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2386 # Check if we want to replace getopt/getopt_long even if provided by the system
2387 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2388 #   so always use our version on Windows
2389 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2390 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2391 # - We want to use system's getopt_long() only if the system provides struct
2392 #   option
2393 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2394 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2396 # Required on BSDs
2397 execinfo_dep = cc.find_library('execinfo', required: false)
2399 if host_system == 'cygwin'
2400   cygipc_dep = cc.find_library('cygipc', required: false)
2401 else
2402   cygipc_dep = not_found_dep
2403 endif
2405 if host_system == 'sunos'
2406   socket_dep = cc.find_library('socket', required: false)
2407 else
2408   socket_dep = not_found_dep
2409 endif
2411 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2412 # unnecessary checks over and over, particularly on windows.
2413 func_checks = [
2414   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2415   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2416   ['clock_gettime', {'dependencies': [rt_dep], 'define': false}],
2417   ['copyfile'],
2418   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2419   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2420   # required. Just checking for dlsym() ought to suffice.
2421   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2422   ['explicit_bzero'],
2423   ['getifaddrs'],
2424   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2425   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2426   ['getpeereid'],
2427   ['getpeerucred'],
2428   ['inet_aton'],
2429   ['inet_pton'],
2430   ['kqueue'],
2431   ['mbstowcs_l'],
2432   ['memset_s'],
2433   ['mkdtemp'],
2434   ['posix_fadvise'],
2435   ['posix_fallocate'],
2436   ['ppoll'],
2437   ['pstat'],
2438   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2439   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2440   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2441   ['setproctitle', {'dependencies': [util_dep]}],
2442   ['setproctitle_fast'],
2443   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2444   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2445   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2446   ['socket', {'dependencies': [socket_dep], 'define': false}],
2447   ['strchrnul'],
2448   ['strerror_r', {'dependencies': [thread_dep]}],
2449   ['strlcat'],
2450   ['strlcpy'],
2451   ['strnlen'],
2452   ['strsignal'],
2453   ['sync_file_range'],
2454   ['syncfs'],
2455   ['uselocale'],
2456   ['wcstombs_l'],
2459 func_check_results = {}
2460 foreach c : func_checks
2461   func = c.get(0)
2462   kwargs = c.get(1, {})
2463   deps = kwargs.get('dependencies', [])
2465   if kwargs.get('skip', false)
2466     continue
2467   endif
2469   found = cc.has_function(func, args: test_c_args)
2471   if not found
2472     foreach dep : deps
2473       if not dep.found()
2474         continue
2475       endif
2476       found = cc.has_function(func, args: test_c_args,
2477                               dependencies: [dep])
2478       if found
2479         os_deps += dep
2480         break
2481       endif
2482     endforeach
2483   endif
2485   func_check_results += {func: found}
2487   if kwargs.get('define', true)
2488     # Emulate autoconf behaviour of not-found->undef, found->1
2489     cdata.set('HAVE_' + func.underscorify().to_upper(),
2490               found  ? 1 : false,
2491               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2492   endif
2493 endforeach
2496 if cc.has_function('syslog', args: test_c_args) and \
2497     cc.check_header('syslog.h', args: test_c_args)
2498   cdata.set('HAVE_SYSLOG', 1)
2499 endif
2502 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2503 # semaphores
2504 if sema_kind == 'unnamed_posix' and \
2505    not func_check_results.get('sem_init', false)
2506   sema_kind = 'sysv'
2507 endif
2509 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2510 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2512 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2513 cdata.set_quoted('DLSUFFIX', dlsuffix)
2516 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2517 cdata.set_quoted('PG_VERSION_STR',
2518   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2519     pg_version, host_machine.cpu_family(), host_system,
2520     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2521   )
2525 ###############################################################
2526 # NLS / Gettext
2527 ###############################################################
2529 nlsopt = get_option('nls')
2530 libintl = not_found_dep
2532 if not nlsopt.disabled()
2533   # otherwise there'd be lots of
2534   # "Gettext not found, all translation (po) targets will be ignored."
2535   # warnings if not found.
2536   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2538   # meson 0.59 has this wrapped in dependency('intl')
2539   if (msgfmt.found() and
2540       cc.check_header('libintl.h', required: nlsopt,
2541         args: test_c_args, include_directories: postgres_inc))
2543     # in libc
2544     if cc.has_function('ngettext')
2545       libintl = declare_dependency()
2546     else
2547       libintl = cc.find_library('intl',
2548         has_headers: ['libintl.h'], required: nlsopt,
2549         header_include_directories: postgres_inc,
2550         dirs: test_lib_d)
2551     endif
2552   endif
2554   if libintl.found()
2555     i18n = import('i18n')
2556     cdata.set('ENABLE_NLS', 1)
2557   endif
2558 endif
2562 ###############################################################
2563 # Build
2564 ###############################################################
2566 # Set up compiler / linker arguments to be used everywhere, individual targets
2567 # can add further args directly, or indirectly via dependencies
2568 add_project_arguments(cflags, language: ['c'])
2569 add_project_arguments(cppflags, language: ['c'])
2570 add_project_arguments(cflags_warn, language: ['c'])
2571 add_project_arguments(cxxflags, language: ['cpp'])
2572 add_project_arguments(cppflags, language: ['cpp'])
2573 add_project_arguments(cxxflags_warn, language: ['cpp'])
2574 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2577 # Collect a number of lists of things while recursing through the source
2578 # tree. Later steps then can use those.
2580 # list of targets for various alias targets
2581 backend_targets = []
2582 bin_targets = []
2583 pl_targets = []
2584 contrib_targets = []
2585 testprep_targets = []
2586 nls_targets = []
2589 # Define the tests to distribute them to the correct test styles later
2590 test_deps = []
2591 tests = []
2594 # Default options for targets
2596 # First identify rpaths
2597 bin_install_rpaths = []
2598 lib_install_rpaths = []
2599 mod_install_rpaths = []
2602 # Don't add rpaths on darwin for now - as long as only absolute references to
2603 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2604 # their final destination.
2605 if host_system != 'darwin'
2606   # Add absolute path to libdir to rpath. This ensures installed binaries /
2607   # libraries find our libraries (mainly libpq).
2608   bin_install_rpaths += dir_prefix / dir_lib
2609   lib_install_rpaths += dir_prefix / dir_lib
2610   mod_install_rpaths += dir_prefix / dir_lib
2612   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2613   #
2614   # Not needed on darwin even if we use relative rpaths for our own libraries,
2615   # as the install_name of libraries in extra_lib_dirs will point to their
2616   # location anyway.
2617   bin_install_rpaths += postgres_lib_d
2618   lib_install_rpaths += postgres_lib_d
2619   mod_install_rpaths += postgres_lib_d
2620 endif
2623 # Define arguments for default targets
2625 default_target_args = {
2626   'implicit_include_directories': false,
2627   'install': true,
2630 default_lib_args = default_target_args + {
2631   'name_prefix': '',
2634 internal_lib_args = default_lib_args + {
2635   'build_by_default': false,
2636   'install': false,
2639 default_mod_args = default_lib_args + {
2640   'name_prefix': '',
2641   'install_dir': dir_lib_pkg,
2644 default_bin_args = default_target_args + {
2645   'install_dir': dir_bin,
2648 if get_option('rpath')
2649   default_lib_args += {
2650     'install_rpath': ':'.join(lib_install_rpaths),
2651   }
2653   default_mod_args += {
2654     'install_rpath': ':'.join(mod_install_rpaths),
2655   }
2657   default_bin_args += {
2658     'install_rpath': ':'.join(bin_install_rpaths),
2659   }
2660 endif
2663 # Helper for exporting a limited number of symbols
2664 gen_export_kwargs = {
2665   'input': 'exports.txt',
2666   'output': '@BASENAME@.'+export_file_suffix,
2667   'command': [perl, files('src/tools/gen_export.pl'),
2668    '--format', export_file_format,
2669    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2670   'build_by_default': false,
2671   'install': false,
2677 ### Helpers for custom targets used across the tree
2680 catalog_pm = files('src/backend/catalog/Catalog.pm')
2681 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2682 gen_kwlist_deps = [perfect_hash_pm]
2683 gen_kwlist_cmd = [
2684   perl, '-I', '@SOURCE_ROOT@/src/tools',
2685   files('src/tools/gen_keywordlist.pl'),
2686   '--output', '@OUTDIR@', '@INPUT@']
2691 ### windows resources related stuff
2694 if host_system == 'windows'
2695   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2696   win32ver_rc = files('src/port/win32ver.rc')
2697   rcgen = find_program('src/tools/rcgen', native: true)
2699   rcgen_base_args = [
2700     '--srcdir', '@SOURCE_DIR@',
2701     '--builddir', meson.build_root(),
2702     '--rcout', '@OUTPUT0@',
2703     '--out', '@OUTPUT1@',
2704     '--input', '@INPUT@',
2705     '@EXTRA_ARGS@',
2706   ]
2708   if cc.get_argument_syntax() == 'msvc'
2709     rc = find_program('rc', required: true)
2710     rcgen_base_args += ['--rc', rc.path()]
2711     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2712   else
2713     windres = find_program('windres', required: true)
2714     rcgen_base_args += ['--windres', windres.path()]
2715     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2716   endif
2718   # msbuild backend doesn't support this atm
2719   if meson.backend() == 'ninja'
2720     rcgen_base_args += ['--depfile', '@DEPFILE@']
2721   endif
2723   rcgen_bin_args = rcgen_base_args + [
2724     '--VFT_TYPE', 'VFT_APP',
2725     '--FILEENDING', 'exe',
2726     '--ICO', pg_ico
2727   ]
2729   rcgen_lib_args = rcgen_base_args + [
2730     '--VFT_TYPE', 'VFT_DLL',
2731     '--FILEENDING', 'dll',
2732   ]
2734   rc_bin_gen = generator(rcgen,
2735     depfile: '@BASENAME@.d',
2736     arguments: rcgen_bin_args,
2737     output: rcgen_outputs,
2738   )
2740   rc_lib_gen = generator(rcgen,
2741     depfile: '@BASENAME@.d',
2742     arguments: rcgen_lib_args,
2743     output: rcgen_outputs,
2744   )
2745 endif
2749 # headers that the whole build tree depends on
2750 generated_headers = []
2751 # headers that the backend build depends on
2752 generated_backend_headers = []
2753 # configure_files() output, needs a way of converting to file names
2754 configure_files = []
2756 # generated files that might conflict with a partial in-tree autoconf build
2757 generated_sources = []
2758 # same, for paths that differ between autoconf / meson builds
2759 # elements are [dir, [files]]
2760 generated_sources_ac = {}
2763 # First visit src/include - all targets creating headers are defined
2764 # within. That makes it easy to add the necessary dependencies for the
2765 # subsequent build steps.
2767 subdir('src/include')
2769 subdir('config')
2771 # Then through src/port and src/common, as most other things depend on them
2773 frontend_port_code = declare_dependency(
2774   compile_args: ['-DFRONTEND'],
2775   include_directories: [postgres_inc],
2776   dependencies: os_deps,
2779 backend_port_code = declare_dependency(
2780   compile_args: ['-DBUILDING_DLL'],
2781   include_directories: [postgres_inc],
2782   sources: [errcodes], # errcodes.h is needed due to use of ereport
2783   dependencies: os_deps,
2786 subdir('src/port')
2788 frontend_common_code = declare_dependency(
2789   compile_args: ['-DFRONTEND'],
2790   include_directories: [postgres_inc],
2791   sources: generated_headers,
2792   dependencies: [os_deps, zlib, zstd],
2795 backend_common_code = declare_dependency(
2796   compile_args: ['-DBUILDING_DLL'],
2797   include_directories: [postgres_inc],
2798   sources: generated_headers,
2799   dependencies: [os_deps, zlib, zstd],
2802 subdir('src/common')
2804 # all shared libraries should depend on shlib_code
2805 shlib_code = declare_dependency(
2806   link_args: ldflags_sl,
2809 # all static libraries not part of the backend should depend on this
2810 frontend_stlib_code = declare_dependency(
2811   include_directories: [postgres_inc],
2812   link_with: [common_static, pgport_static],
2813   sources: generated_headers,
2814   dependencies: [os_deps, libintl],
2817 # all shared libraries not part of the backend should depend on this
2818 frontend_shlib_code = declare_dependency(
2819   include_directories: [postgres_inc],
2820   link_with: [common_shlib, pgport_shlib],
2821   sources: generated_headers,
2822   dependencies: [shlib_code, os_deps, libintl],
2825 # Dependencies both for static and shared libpq
2826 libpq_deps += [
2827   thread_dep,
2829   gssapi,
2830   ldap_r,
2831   libintl,
2832   ssl,
2835 subdir('src/interfaces/libpq')
2836 # fe_utils depends on libpq
2837 subdir('src/fe_utils')
2839 # for frontend binaries
2840 frontend_code = declare_dependency(
2841   include_directories: [postgres_inc],
2842   link_with: [fe_utils, common_static, pgport_static],
2843   sources: generated_headers,
2844   dependencies: [os_deps, libintl],
2847 backend_both_deps += [
2848   thread_dep,
2849   bsd_auth,
2850   gssapi,
2851   icu,
2852   icu_i18n,
2853   ldap,
2854   libintl,
2855   libxml,
2856   lz4,
2857   pam,
2858   ssl,
2859   systemd,
2860   zlib,
2861   zstd,
2864 backend_mod_deps = backend_both_deps + os_deps
2866 backend_code = declare_dependency(
2867   compile_args: ['-DBUILDING_DLL'],
2868   include_directories: [postgres_inc],
2869   link_args: ldflags_be,
2870   link_with: [],
2871   sources: generated_headers + generated_backend_headers,
2872   dependencies: os_deps + backend_both_deps + backend_deps,
2875 # install these files only during test, not main install
2876 test_install_data = []
2877 test_install_libs = []
2879 # src/backend/meson.build defines backend_mod_code used for extension
2880 # libraries.
2883 # Then through the main sources. That way contrib can have dependencies on
2884 # main sources. Note that this explicitly doesn't enter src/test, right now a
2885 # few regression tests depend on contrib files.
2887 subdir('src')
2889 subdir('contrib')
2891 subdir('src/test')
2892 subdir('src/interfaces/libpq/test')
2893 subdir('src/interfaces/ecpg/test')
2895 subdir('doc/src/sgml')
2897 generated_sources_ac += {'': ['GNUmakefile']}
2899 # After processing src/test, add test_install_libs to the testprep_targets
2900 # to build them
2901 testprep_targets += test_install_libs
2904 # If there are any files in the source directory that we also generate in the
2905 # build directory, they might get preferred over the newly generated files,
2906 # e.g. because of a #include "file", which always will search in the current
2907 # directory first.
2908 message('checking for file conflicts between source and build directory')
2909 conflicting_files = []
2910 potentially_conflicting_files_t = []
2911 potentially_conflicting_files_t += generated_headers
2912 potentially_conflicting_files_t += generated_backend_headers
2913 potentially_conflicting_files_t += generated_backend_sources
2914 potentially_conflicting_files_t += generated_sources
2916 potentially_conflicting_files = []
2918 # convert all sources of potentially conflicting files into uniform shape
2919 foreach t : potentially_conflicting_files_t
2920   potentially_conflicting_files += t.full_path()
2921 endforeach
2922 foreach t : configure_files
2923   t = '@0@'.format(t)
2924   potentially_conflicting_files += meson.current_build_dir() / t
2925 endforeach
2926 foreach sub, fnames : generated_sources_ac
2927   sub = meson.build_root() / sub
2928   foreach fname : fnames
2929     potentially_conflicting_files += sub / fname
2930   endforeach
2931 endforeach
2933 # find and report conflicting files
2934 foreach build_path : potentially_conflicting_files
2935   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
2936   # str.replace is in 0.56
2937   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
2938   if fs.exists(src_path) or fs.is_symlink(src_path)
2939     conflicting_files += src_path
2940   endif
2941 endforeach
2942 # XXX: Perhaps we should generate a file that would clean these up? The list
2943 # can be long.
2944 if conflicting_files.length() > 0
2945   errmsg_cleanup = '''
2946 Conflicting files in source directory:
2947   @0@
2949 The conflicting files need to be removed, either by removing the files listed
2950 above, or by running configure and then make maintainer-clean.
2952   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
2953   error(errmsg_nonclean_base.format(errmsg_cleanup))
2954 endif
2958 ###############################################################
2959 # Install targets
2960 ###############################################################
2963 # We want to define additional install targets beyond what meson provides. For
2964 # that we need to define targets depending on nearly everything. We collected
2965 # the results of i18n.gettext() invocations into nls_targets, that also
2966 # includes maintainer targets though. Collect the ones we want as a dependency.
2968 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
2969 # generation happens during install, so that's not a real issue.
2970 nls_mo_targets = []
2971 if libintl.found() and meson.version().version_compare('>=0.60')
2972   # use range() to avoid the flattening of the list that foreach() would do
2973   foreach off : range(0, nls_targets.length())
2974     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
2975     # -pot target 3) maintainer -pot target
2976     nls_mo_targets += nls_targets[off][0]
2977   endforeach
2978   alias_target('nls', nls_mo_targets)
2979 endif
2982 all_built = [
2983   backend_targets,
2984   bin_targets,
2985   libpq_st,
2986   pl_targets,
2987   contrib_targets,
2988   nls_mo_targets,
2989   testprep_targets,
2990   ecpg_targets,
2993 # Meson's default install target is quite verbose. Provide one that is quiet.
2994 install_quiet = custom_target('install-quiet',
2995   output: 'install-quiet',
2996   build_always_stale: true,
2997   build_by_default: false,
2998   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
2999   depends: all_built,
3002 # Target to install files used for tests, which aren't installed by default
3003 install_test_files_args = [
3004   install_files,
3005   '--prefix', dir_prefix,
3006   '--install', contrib_data_dir, test_install_data,
3007   '--install', dir_lib_pkg, test_install_libs,
3009 run_target('install-test-files',
3010   command: [python] + install_test_files_args,
3011   depends: testprep_targets,
3016 ###############################################################
3017 # Test prep
3018 ###############################################################
3020 # DESTDIR for the installation we'll run tests in
3021 test_install_destdir = meson.build_root() / 'tmp_install/'
3023 # DESTDIR + prefix appropriately munged
3024 if build_system != 'windows'
3025   # On unixoid systems this is trivial, we just prepend the destdir
3026   assert(dir_prefix.startswith('/')) # enforced by meson
3027   test_install_location = '@0@@1@'.format(test_install_destdir, dir_prefix)
3028 else
3029   # drives, drive-relative paths, etc make this complicated on windows, call
3030   # into a copy of meson's logic for it
3031   command = [
3032     python, '-c',
3033     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3034     test_install_destdir, dir_prefix]
3035   test_install_location = run_command(command, check: true).stdout().strip()
3036 endif
3038 meson_install_args = meson_args + ['install'] + {
3039     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3040     'muon': []
3041 }[meson_impl]
3043 # setup tests should be run first,
3044 # so define priority for these
3045 setup_tests_priority = 100
3046 test('tmp_install',
3047     meson_bin, args: meson_install_args ,
3048     env: {'DESTDIR':test_install_destdir},
3049     priority: setup_tests_priority,
3050     timeout: 300,
3051     is_parallel: false,
3052     suite: ['setup'])
3054 test('install_test_files',
3055     python,
3056     args: install_test_files_args + ['--destdir', test_install_destdir],
3057     priority: setup_tests_priority,
3058     is_parallel: false,
3059     suite: ['setup'])
3061 test_result_dir = meson.build_root() / 'testrun'
3064 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3065 # inevitable conflicts from running tests in parallel, hackishly assign
3066 # different ports for different tests.
3068 testport = 40000
3070 test_env = environment()
3072 temp_install_bindir = test_install_location / get_option('bindir')
3073 test_initdb_template = meson.build_root() / 'tmp_install' / 'initdb-template'
3074 test_env.set('PG_REGRESS', pg_regress.full_path())
3075 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3076 test_env.set('INITDB_TEMPLATE', test_initdb_template)
3078 # Test suites that are not safe by default but can be run if selected
3079 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3080 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3081 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3083 # Add the temporary installation to the library search path on platforms where
3084 # that works (everything but windows, basically). On windows everything
3085 # library-like gets installed into bindir, solving that issue.
3086 if library_path_var != ''
3087   test_env.prepend(library_path_var, test_install_location / get_option('libdir'))
3088 endif
3091 # Create (and remove old) initdb template directory. Tests use that, where
3092 # possible, to make it cheaper to run tests.
3094 # Use python to remove the old cached initdb, as we cannot rely on a working
3095 # 'rm' binary on windows.
3096 test('initdb_cache',
3097      python,
3098      args: [
3099        '-c', '''
3100 import shutil
3101 import sys
3102 import subprocess
3104 shutil.rmtree(sys.argv[1], ignore_errors=True)
3105 sp = subprocess.run(sys.argv[2:] + [sys.argv[1]])
3106 sys.exit(sp.returncode)
3107 ''',
3108        test_initdb_template,
3109        temp_install_bindir / 'initdb',
3110        '-A', 'trust', '-N', '--no-instructions', '--no-locale'
3111      ],
3112      priority: setup_tests_priority - 1,
3113      timeout: 300,
3114      is_parallel: false,
3115      env: test_env,
3116      suite: ['setup'])
3120 ###############################################################
3121 # Test Generation
3122 ###############################################################
3124 # When using a meson version understanding exclude_suites, define a
3125 # 'tmp_install' test setup (the default) that excludes tests running against a
3126 # pre-existing install and a 'running' setup that conflicts with creation of
3127 # the temporary installation and tap tests (which don't support running
3128 # against a running server).
3130 running_suites = []
3131 install_suites = []
3132 if meson.version().version_compare('>=0.57')
3133   runningcheck = true
3134 else
3135   runningcheck = false
3136 endif
3138 testwrap = files('src/tools/testwrap')
3140 foreach test_dir : tests
3141   testwrap_base = [
3142     testwrap,
3143     '--basedir', meson.build_root(),
3144     '--srcdir', test_dir['sd'],
3145   ]
3147   foreach kind, v : test_dir
3148     if kind in ['sd', 'bd', 'name']
3149       continue
3150     endif
3152     t = test_dir[kind]
3154     if kind in ['regress', 'isolation', 'ecpg']
3155       if kind == 'regress'
3156         runner = pg_regress
3157         fallback_dbname = 'regression_@0@'
3158       elif kind == 'isolation'
3159         runner = pg_isolation_regress
3160         fallback_dbname = 'isolation_regression_@0@'
3161       elif kind == 'ecpg'
3162         runner = pg_regress_ecpg
3163         fallback_dbname = 'ecpg_regression_@0@'
3164       endif
3166       test_group = test_dir['name']
3167       test_group_running = test_dir['name'] + '-running'
3169       test_output = test_result_dir / test_group / kind
3170       test_output_running = test_result_dir / test_group_running/ kind
3172       # Unless specified by the test, choose a non-conflicting database name,
3173       # to avoid conflicts when running against existing server.
3174       dbname = t.get('dbname',
3175         fallback_dbname.format(test_dir['name']))
3177       test_command_base = [
3178         runner.full_path(),
3179         '--inputdir', t.get('inputdir', test_dir['sd']),
3180         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3181         '--bindir', '',
3182         '--dlpath', test_dir['bd'],
3183         '--max-concurrent-tests=20',
3184         '--dbname', dbname,
3185       ] + t.get('regress_args', [])
3187       test_selection = []
3188       if t.has_key('schedule')
3189         test_selection += ['--schedule', t['schedule'],]
3190       endif
3192       if kind == 'isolation'
3193         test_selection += t.get('specs', [])
3194       else
3195         test_selection += t.get('sql', [])
3196       endif
3198       env = test_env
3199       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3201       test_kwargs = {
3202         'protocol': 'tap',
3203         'priority': 10,
3204         'timeout': 1000,
3205         'depends': test_deps + t.get('deps', []),
3206         'env': env,
3207       } + t.get('test_kwargs', {})
3209       test(test_group / kind,
3210         python,
3211         args: [
3212           testwrap_base,
3213           '--testgroup', test_group,
3214           '--testname', kind,
3215           '--',
3216           test_command_base,
3217           '--outputdir', test_output,
3218           '--temp-instance', test_output / 'tmp_check',
3219           '--port', testport.to_string(),
3220           test_selection,
3221         ],
3222         suite: test_group,
3223         kwargs: test_kwargs,
3224       )
3225       install_suites += test_group
3227       # some tests can't support running against running DB
3228       if runningcheck and t.get('runningcheck', true)
3229         test(test_group_running / kind,
3230           python,
3231           args: [
3232             testwrap_base,
3233             '--testgroup', test_group_running,
3234             '--testname', kind,
3235             '--',
3236             test_command_base,
3237             '--outputdir', test_output_running,
3238             test_selection,
3239           ],
3240           is_parallel: t.get('runningcheck-parallel', true),
3241           suite: test_group_running,
3242           kwargs: test_kwargs,
3243         )
3244         running_suites += test_group_running
3245       endif
3247       testport += 1
3248     elif kind == 'tap'
3249       if not tap_tests_enabled
3250         continue
3251       endif
3253       test_command = [
3254         perl.path(),
3255         '-I', meson.source_root() / 'src/test/perl',
3256         '-I', test_dir['sd'],
3257       ]
3259       # Add temporary install, the build directory for non-installed binaries and
3260       # also test/ for non-installed test binaries built separately.
3261       env = test_env
3262       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3264       foreach name, value : t.get('env', {})
3265         env.set(name, value)
3266       endforeach
3268       test_group = test_dir['name']
3269       test_kwargs = {
3270         'protocol': 'tap',
3271         'suite': test_group,
3272         'timeout': 1000,
3273         'depends': test_deps + t.get('deps', []),
3274         'env': env,
3275       } + t.get('test_kwargs', {})
3277       foreach onetap : t['tests']
3278         # Make tap test names prettier, remove t/ and .pl
3279         onetap_p = onetap
3280         if onetap_p.startswith('t/')
3281           onetap_p = onetap.split('t/')[1]
3282         endif
3283         if onetap_p.endswith('.pl')
3284           onetap_p = fs.stem(onetap_p)
3285         endif
3287         test(test_dir['name'] / onetap_p,
3288           python,
3289           kwargs: test_kwargs,
3290           args: testwrap_base + [
3291             '--testgroup', test_dir['name'],
3292             '--testname', onetap_p,
3293             '--', test_command,
3294             test_dir['sd'] / onetap,
3295           ],
3296         )
3297       endforeach
3298       install_suites += test_group
3299     else
3300       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3301     endif
3303   endforeach # kinds of tests
3305 endforeach # directories with tests
3307 # repeat condition so meson realizes version dependency
3308 if meson.version().version_compare('>=0.57')
3309   add_test_setup('tmp_install',
3310     is_default: true,
3311     exclude_suites: running_suites)
3312   add_test_setup('running',
3313     exclude_suites: ['setup'] + install_suites)
3314 endif
3318 ###############################################################
3319 # Pseudo targets
3320 ###############################################################
3322 alias_target('backend', backend_targets)
3323 alias_target('bin', bin_targets + [libpq_st])
3324 alias_target('pl', pl_targets)
3325 alias_target('contrib', contrib_targets)
3326 alias_target('testprep', testprep_targets)
3327 alias_target('install-world', install_quiet, installdocs)
3331 ###############################################################
3332 # The End, The End, My Friend
3333 ###############################################################
3335 if meson.version().version_compare('>=0.57')
3337   summary(
3338     {
3339       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3340       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3341       'segment size': get_option('segsize_blocks') != 0 ?
3342         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3343         '@0@ GB'.format(get_option('segsize')),
3344     },
3345     section: 'Data layout',
3346   )
3348   summary(
3349     {
3350       'host system': '@0@ @1@'.format(host_system, host_cpu),
3351       'build system': '@0@ @1@'.format(build_machine.system(),
3352                                        build_machine.cpu_family()),
3353     },
3354     section: 'System',
3355   )
3357   summary(
3358     {
3359       'linker': '@0@'.format(cc.get_linker_id()),
3360       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3361     },
3362     section: 'Compiler',
3363   )
3365   summary(
3366     {
3367       'CPP FLAGS': ' '.join(cppflags),
3368       'C FLAGS, functional': ' '.join(cflags),
3369       'C FLAGS, warnings': ' '.join(cflags_warn),
3370       'C FLAGS, modules': ' '.join(cflags_mod),
3371       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3372       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3373     },
3374     section: 'Compiler Flags',
3375   )
3377   if llvm.found()
3378     summary(
3379       {
3380         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3381       },
3382       section: 'Compiler',
3383     )
3385     summary(
3386       {
3387         'C++ FLAGS, functional': ' '.join(cxxflags),
3388         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3389         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3390       },
3391       section: 'Compiler Flags',
3392     )
3393   endif
3395   summary(
3396     {
3397       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3398       'dtrace': dtrace,
3399       'flex': '@0@ @1@'.format(flex.full_path(), flex_version),
3400     },
3401     section: 'Programs',
3402   )
3404   summary(
3405     {
3406       'bonjour': bonjour,
3407       'bsd_auth': bsd_auth,
3408       'docs': docs_dep,
3409       'docs_pdf': docs_pdf_dep,
3410       'gss': gssapi,
3411       'icu': icu,
3412       'ldap': ldap,
3413       'libxml': libxml,
3414       'libxslt': libxslt,
3415       'llvm': llvm,
3416       'lz4': lz4,
3417       'nls': libintl,
3418       'openssl': ssl,
3419       'pam': pam,
3420       'plperl': perl_dep,
3421       'plpython': python3_dep,
3422       'pltcl': tcl_dep,
3423       'readline': readline,
3424       'selinux': selinux,
3425       'systemd': systemd,
3426       'uuid': uuid,
3427       'zlib': zlib,
3428       'zstd': zstd,
3429     },
3430     section: 'External libraries',
3431   )
3433 endif