doc: add reference to wire protocol details
[pgsql.git] / meson.build
blob2d516c8f372a7d9550ba8dd2bfd15581fff647ac
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 python3_dep = not_found_dep
1060 if not pyopt.disabled()
1061   pm = import('python')
1062   python3_inst = pm.find_installation(required: pyopt)
1063   if python3_inst.found()
1064     python3_dep = python3_inst.dependency(embed: true, required: pyopt)
1065     # Remove this check after we depend on Meson >= 1.1.0
1066     if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt)
1067       python3_dep = not_found_dep
1068     endif
1069   endif
1070 endif
1074 ###############################################################
1075 # Library: Readline
1076 ###############################################################
1078 if not get_option('readline').disabled()
1079   libedit_preferred = get_option('libedit_preferred')
1080   # Set the order of readline dependencies
1081   check_readline_deps = libedit_preferred ? \
1082     ['libedit', 'readline'] : ['readline', 'libedit']
1084   foreach readline_dep : check_readline_deps
1085     readline = dependency(readline_dep, required: false)
1086     if not readline.found()
1087       readline = cc.find_library(readline_dep,
1088         required: get_option('readline'),
1089         dirs: test_lib_d)
1090     endif
1091     if readline.found()
1092       break
1093     endif
1094   endforeach
1096   if readline.found()
1097     cdata.set('HAVE_LIBREADLINE', 1)
1099     editline_prefix = {
1100       'header_prefix': 'editline/',
1101       'flag_prefix': 'EDITLINE_',
1102     }
1103     readline_prefix = {
1104       'header_prefix': 'readline/',
1105       'flag_prefix': 'READLINE_',
1106     }
1107     default_prefix = {
1108       'header_prefix': '',
1109       'flag_prefix': '',
1110     }
1112     # Set the order of prefixes
1113     prefixes = libedit_preferred ? \
1114       [editline_prefix, default_prefix, readline_prefix] : \
1115       [readline_prefix, default_prefix, editline_prefix]
1117     at_least_one_header_found = false
1118     foreach header : ['history', 'readline']
1119       is_found = false
1120       foreach prefix : prefixes
1121         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1122         # Check history.h and readline.h
1123         if not is_found and cc.has_header(header_file,
1124             args: test_c_args, include_directories: postgres_inc,
1125             dependencies: [readline], required: false)
1126           if header == 'readline'
1127             readline_h = header_file
1128           endif
1129           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1130           is_found = true
1131           at_least_one_header_found = true
1132         endif
1133       endforeach
1134     endforeach
1136     if not at_least_one_header_found
1137       error('''readline header not found
1138 If you have @0@ already installed, see meson-log/meson-log.txt for details on the
1139 failure. It is possible the compiler isn't looking in the proper directory.
1140 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1141     endif
1143     check_funcs = [
1144       'append_history',
1145       'history_truncate_file',
1146       'rl_completion_matches',
1147       'rl_filename_completion_function',
1148       'rl_reset_screen_size',
1149       'rl_variable_bind',
1150     ]
1152     foreach func : check_funcs
1153       found = cc.has_function(func, dependencies: [readline],
1154         args: test_c_args, include_directories: postgres_inc)
1155       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1156     endforeach
1158     check_vars = [
1159       'rl_completion_suppress_quote',
1160       'rl_filename_quote_characters',
1161       'rl_filename_quoting_function',
1162     ]
1164     foreach var : check_vars
1165       cdata.set('HAVE_' + var.to_upper(),
1166         cc.has_header_symbol(readline_h, var,
1167           args: test_c_args, include_directories: postgres_inc,
1168           prefix: '#include <stdio.h>',
1169           dependencies: [readline]) ? 1 : false)
1170     endforeach
1172     # If found via cc.find_library() ensure headers are found when using the
1173     # dependency. On meson < 0.57 one cannot do compiler checks using the
1174     # dependency returned by declare_dependency(), so we can't do this above.
1175     if readline.type_name() == 'library'
1176       readline = declare_dependency(dependencies: readline,
1177         include_directories: postgres_inc)
1178     endif
1180     # On windows with mingw readline requires auto-import to successfully
1181     # link, as the headers don't use declspec(dllimport)
1182     if host_system == 'windows' and cc.get_id() != 'msvc'
1183       readline = declare_dependency(dependencies: readline,
1184         link_args: '-Wl,--enable-auto-import')
1185     endif
1186   endif
1188   # XXX: Figure out whether to implement mingw warning equivalent
1189 else
1190   readline = not_found_dep
1191 endif
1195 ###############################################################
1196 # Library: selinux
1197 ###############################################################
1199 selinux = not_found_dep
1200 selinuxopt = get_option('selinux')
1201 if meson.version().version_compare('>=0.59')
1202   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1203 endif
1204 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1205 cdata.set('HAVE_LIBSELINUX',
1206   selinux.found() ? 1 : false)
1210 ###############################################################
1211 # Library: systemd
1212 ###############################################################
1214 systemd = not_found_dep
1215 systemdopt = get_option('systemd')
1216 if meson.version().version_compare('>=0.59')
1217   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1218 endif
1219 systemd = dependency('libsystemd', required: systemdopt)
1220 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1224 ###############################################################
1225 # Library: SSL
1226 ###############################################################
1228 ssl = not_found_dep
1229 ssl_library = 'none'
1230 sslopt = get_option('ssl')
1232 if sslopt == 'auto' and auto_features.disabled()
1233   sslopt = 'none'
1234 endif
1236 if sslopt in ['auto', 'openssl']
1237   openssl_required = (sslopt == 'openssl')
1239   # Try to find openssl via pkg-config et al, if that doesn't work
1240   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1241   # the library names that we know about.
1243   # via pkg-config et al
1244   ssl = dependency('openssl', required: false)
1245   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1246   # we pass cc.find_library() results if necessary
1247   ssl_int = []
1249   # via library + headers
1250   if not ssl.found()
1251     ssl_lib = cc.find_library('ssl',
1252       dirs: test_lib_d,
1253       header_include_directories: postgres_inc,
1254       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1255       required: openssl_required)
1256     crypto_lib = cc.find_library('crypto',
1257       dirs: test_lib_d,
1258       required: openssl_required)
1259     if ssl_lib.found() and crypto_lib.found()
1260       ssl_int = [ssl_lib, crypto_lib]
1261       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1262     endif
1263   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1264        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1265     ssl_int = [ssl]
1266   else
1267     ssl = not_found_dep
1268   endif
1270   if ssl.found()
1271     check_funcs = [
1272       ['CRYPTO_new_ex_data', {'required': true}],
1273       ['SSL_new', {'required': true}],
1275       # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
1276       ['SSL_CTX_set_cert_cb'],
1278       # Functions introduced in OpenSSL 1.1.0. We used to check for
1279       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1280       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1281       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1282       # functions.
1283       ['OPENSSL_init_ssl'],
1284       ['BIO_get_data'],
1285       ['BIO_meth_new'],
1286       ['ASN1_STRING_get0_data'],
1287       ['HMAC_CTX_new'],
1288       ['HMAC_CTX_free'],
1290       # OpenSSL versions before 1.1.0 required setting callback functions, for
1291       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1292       # function was removed.
1293       ['CRYPTO_lock'],
1295       # Function introduced in OpenSSL 1.1.1
1296       ['X509_get_signature_info'],
1297     ]
1299     are_openssl_funcs_complete = true
1300     foreach c : check_funcs
1301       func = c.get(0)
1302       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1303       required = c.get(1, {}).get('required', false)
1304       if required and not val
1305         are_openssl_funcs_complete = false
1306         if openssl_required
1307           error('openssl function @0@ is required'.format(func))
1308         endif
1309         break
1310       elif not required
1311         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1312       endif
1313     endforeach
1315     if are_openssl_funcs_complete
1316       cdata.set('USE_OPENSSL', 1,
1317                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1318       cdata.set('OPENSSL_API_COMPAT', '0x10002000L',
1319                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1320       ssl_library = 'openssl'
1321     else
1322       ssl = not_found_dep
1323     endif
1324   endif
1325 endif
1327 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1328   error('no SSL library found')
1329 endif
1333 ###############################################################
1334 # Library: uuid
1335 ###############################################################
1337 uuidopt = get_option('uuid')
1338 if uuidopt != 'none'
1339   uuidname = uuidopt.to_upper()
1340   if uuidopt == 'e2fs'
1341     uuid = dependency('uuid', required: true)
1342     uuidfunc = 'uuid_generate'
1343     uuidheader = 'uuid/uuid.h'
1344   elif uuidopt == 'bsd'
1345     # libc should have uuid function
1346     uuid = declare_dependency()
1347     uuidfunc = 'uuid_to_string'
1348     uuidheader = 'uuid.h'
1349   elif uuidopt == 'ossp'
1350     uuid = dependency('ossp-uuid', required: true)
1351     uuidfunc = 'uuid_export'
1352     uuidheader = 'uuid.h'
1353   else
1354     error('unknown uuid build option value: @0@'.format(uuidopt))
1355   endif
1357   if not cc.has_header_symbol(uuidheader, uuidfunc, args: test_c_args, dependencies: uuid)
1358     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1359   endif
1360   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1362   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1363            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1364 else
1365   uuid = not_found_dep
1366 endif
1370 ###############################################################
1371 # Library: zlib
1372 ###############################################################
1374 zlibopt = get_option('zlib')
1375 zlib = not_found_dep
1376 if not zlibopt.disabled()
1377   zlib_t = dependency('zlib', required: zlibopt)
1379   if zlib_t.type_name() == 'internal'
1380     # if fallback was used, we don't need to test if headers are present (they
1381     # aren't built yet, so we can't test)
1382     zlib = zlib_t
1383   elif not zlib_t.found()
1384     warning('did not find zlib')
1385   elif not cc.has_header('zlib.h',
1386       args: test_c_args, include_directories: postgres_inc,
1387       dependencies: [zlib_t], required: zlibopt)
1388     warning('zlib header not found')
1389   else
1390     zlib = zlib_t
1391   endif
1393   if zlib.found()
1394     cdata.set('HAVE_LIBZ', 1)
1395   endif
1396 endif
1400 ###############################################################
1401 # Library: tap test dependencies
1402 ###############################################################
1404 # Check whether tap tests are enabled or not
1405 tap_tests_enabled = false
1406 tapopt = get_option('tap_tests')
1407 if not tapopt.disabled()
1408   # Checking for perl modules for tap tests
1409   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1410   if perl_ipc_run_check.returncode() != 0
1411     message(perl_ipc_run_check.stderr().strip())
1412     if tapopt.enabled()
1413       error('Additional Perl modules are required to run TAP tests.')
1414     else
1415       warning('Additional Perl modules are required to run TAP tests.')
1416     endif
1417   else
1418     tap_tests_enabled = true
1419   endif
1420 endif
1424 ###############################################################
1425 # Library: zstd
1426 ###############################################################
1428 zstdopt = get_option('zstd')
1429 if not zstdopt.disabled()
1430   zstd = dependency('libzstd', required: zstdopt, version: '>=1.4.0')
1432   if zstd.found()
1433     cdata.set('USE_ZSTD', 1)
1434     cdata.set('HAVE_LIBZSTD', 1)
1435   endif
1437 else
1438   zstd = not_found_dep
1439 endif
1443 ###############################################################
1444 # Compiler tests
1445 ###############################################################
1447 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1448 # unnecessarily, because we optionally rely on newer features.
1449 c99_test = '''
1450 #include <stdbool.h>
1451 #include <complex.h>
1452 #include <tgmath.h>
1453 #include <inttypes.h>
1455 struct named_init_test {
1456   int a;
1457   int b;
1460 extern void structfunc(struct named_init_test);
1462 int main(int argc, char **argv)
1464   struct named_init_test nit = {
1465     .a = 3,
1466     .b = 5,
1467   };
1469   for (int loop_var = 0; loop_var < 3; loop_var++)
1470   {
1471     nit.a += nit.b;
1472   }
1474   structfunc((struct named_init_test){1, 0});
1476   return nit.a != 0;
1480 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1481   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1482         args: test_c_args + ['-std=c99'])
1483     test_c_args += '-std=c99'
1484     cflags += '-std=c99'
1485   else
1486     error('C compiler does not support C99')
1487   endif
1488 endif
1490 sizeof_long = cc.sizeof('long', args: test_c_args)
1491 cdata.set('SIZEOF_LONG', sizeof_long)
1492 if sizeof_long == 8
1493   cdata.set('HAVE_LONG_INT_64', 1)
1494   cdata.set('PG_INT64_TYPE', 'long int')
1495   cdata.set_quoted('INT64_MODIFIER', 'l')
1496 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1497   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1498   cdata.set('PG_INT64_TYPE', 'long long int')
1499   cdata.set_quoted('INT64_MODIFIER', 'll')
1500 else
1501   error('do not know how to get a 64bit int')
1502 endif
1504 if host_machine.endian() == 'big'
1505   cdata.set('WORDS_BIGENDIAN', 1)
1506 endif
1508 alignof_types = ['short', 'int', 'long', 'double']
1509 maxalign = 0
1510 foreach t : alignof_types
1511   align = cc.alignment(t, args: test_c_args)
1512   if maxalign < align
1513     maxalign = align
1514   endif
1515   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1516 endforeach
1517 cdata.set('MAXIMUM_ALIGNOF', maxalign)
1519 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1520 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1523 # Check if __int128 is a working 128 bit integer type, and if so
1524 # define PG_INT128_TYPE to that typename.
1526 # This currently only detects a GCC/clang extension, but support for other
1527 # environments may be added in the future.
1529 # For the moment we only test for support for 128bit math; support for
1530 # 128bit literals and snprintf is not required.
1531 if cc.links('''
1532   /*
1533    * We don't actually run this test, just link it to verify that any support
1534    * functions needed for __int128 are present.
1535    *
1536    * These are globals to discourage the compiler from folding all the
1537    * arithmetic tests down to compile-time constants.  We do not have
1538    * convenient support for 128bit literals at this point...
1539    */
1540   __int128 a = 48828125;
1541   __int128 b = 97656250;
1543   int main(void)
1544   {
1545       __int128 c,d;
1546       a = (a << 12) + 1; /* 200000000001 */
1547       b = (b << 12) + 5; /* 400000000005 */
1548       /* try the most relevant arithmetic ops */
1549       c = a * b;
1550       d = (c + b) / b;
1551       /* must use the results, else compiler may optimize arithmetic away */
1552       return d != a+1;
1553   }''',
1554   name: '__int128',
1555   args: test_c_args)
1557   buggy_int128 = false
1559   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1560   # If not cross-compiling, we can test for bugs and disable use of __int128
1561   # with buggy compilers.  If cross-compiling, hope for the best.
1562   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1563   if not meson.is_cross_build()
1564     r = cc.run('''
1565     /* This must match the corresponding code in c.h: */
1566     #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
1567     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1568     #elif defined(_MSC_VER)
1569     #define pg_attribute_aligned(a) __declspec(align(a))
1570     #endif
1571     typedef __int128 int128a
1572     #if defined(pg_attribute_aligned)
1573     pg_attribute_aligned(8)
1574     #endif
1575     ;
1577     int128a holder;
1578     void pass_by_val(void *buffer, int128a par) { holder = par; }
1580     int main(void)
1581     {
1582         long int i64 = 97656225L << 12;
1583         int128a q;
1584         pass_by_val(main, (int128a) i64);
1585         q = (int128a) i64;
1586         return q != holder;
1587     }''',
1588     name: '__int128 alignment bug',
1589     args: test_c_args)
1590     assert(r.compiled())
1591     if r.returncode() != 0
1592       buggy_int128 = true
1593       message('__int128 support present but buggy and thus disabled')
1594     endif
1595   endif
1597   if not buggy_int128
1598     cdata.set('PG_INT128_TYPE', '__int128')
1599     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1600   endif
1601 endif
1604 # Check if the C compiler knows computed gotos (gcc extension, also
1605 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1607 # Checking whether computed gotos are supported syntax-wise ought to
1608 # be enough, as the syntax is otherwise illegal.
1609 if cc.compiles('''
1610     static inline int foo(void)
1611     {
1612       void *labeladdrs[] = {&&my_label};
1613       goto *labeladdrs[0];
1614       my_label:
1615       return 1;
1616     }''',
1617     args: test_c_args)
1618   cdata.set('HAVE_COMPUTED_GOTO', 1)
1619 endif
1622 # Check if the C compiler understands _Static_assert(),
1623 # and define HAVE__STATIC_ASSERT if so.
1625 # We actually check the syntax ({ _Static_assert(...) }), because we need
1626 # gcc-style compound expressions to be able to wrap the thing into macros.
1627 if cc.compiles('''
1628     int main(int arg, char **argv)
1629     {
1630         ({ _Static_assert(1, "foo"); });
1631     }
1632     ''',
1633     args: test_c_args)
1634   cdata.set('HAVE__STATIC_ASSERT', 1)
1635 endif
1638 # We use <stdbool.h> if we have it and it declares type bool as having
1639 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1640 if cc.has_type('_Bool', args: test_c_args) \
1641     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1642     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1643   cdata.set('HAVE__BOOL', 1)
1644   cdata.set('PG_USE_STDBOOL', 1)
1645 endif
1648 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1649 # warning for each use of %m.
1650 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1651 testsrc = '''
1652 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1653 static void call_log(void)
1655     emit_log(0, "error: %s: %m", "foo");
1658 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1659 foreach a : printf_attributes
1660   if cc.compiles(testsrc.format(a),
1661       args: test_c_args + attrib_error_args, name: 'format ' + a)
1662     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1663     break
1664   endif
1665 endforeach
1668 if cc.has_function_attribute('visibility:default') and \
1669     cc.has_function_attribute('visibility:hidden')
1670   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1672   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1673   # inlineshidden to C code as well... And either way, we want to put these
1674   # flags into exported files (pgxs, .pc files).
1675   cflags_mod += '-fvisibility=hidden'
1676   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1677   ldflags_mod += '-fvisibility=hidden'
1678 endif
1681 # Check if various builtins exist. Some builtins are tested separately,
1682 # because we want to test something more complicated than the generic case.
1683 builtins = [
1684   'bswap16',
1685   'bswap32',
1686   'bswap64',
1687   'clz',
1688   'ctz',
1689   'constant_p',
1690   'frame_address',
1691   'popcount',
1692   'unreachable',
1695 foreach builtin : builtins
1696   fname = '__builtin_@0@'.format(builtin)
1697   if cc.has_function(fname, args: test_c_args)
1698     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1699   endif
1700 endforeach
1703 # Check if the C compiler understands __builtin_types_compatible_p,
1704 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1706 # We check usage with __typeof__, though it's unlikely any compiler would
1707 # have the former and not the latter.
1708 if cc.compiles('''
1709     static int x;
1710     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1711     ''',
1712     name: '__builtin_types_compatible_p',
1713     args: test_c_args)
1714   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1715 endif
1718 # Check if the C compiler understands __builtin_$op_overflow(),
1719 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1721 # Check for the most complicated case, 64 bit multiplication, as a
1722 # proxy for all of the operations.  To detect the case where the compiler
1723 # knows the function but library support is missing, we must link not just
1724 # compile, and store the results in global variables so the compiler doesn't
1725 # optimize away the call.
1726 if cc.links('''
1727     INT64 a = 1;
1728     INT64 b = 1;
1729     INT64 result;
1731     int main(void)
1732     {
1733         return __builtin_mul_overflow(a, b, &result);
1734     }''',
1735     name: '__builtin_mul_overflow',
1736     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1737     )
1738   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1739 endif
1742 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1743 # here. To prevent problems due to two detection methods working, stop
1744 # checking after one.
1745 if cc.links('''
1746     #include <cpuid.h>
1747     int main(int arg, char **argv)
1748     {
1749         unsigned int exx[4] = {0, 0, 0, 0};
1750         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1751     }
1752     ''', name: '__get_cpuid',
1753     args: test_c_args)
1754   cdata.set('HAVE__GET_CPUID', 1)
1755 elif cc.links('''
1756     #include <intrin.h>
1757     int main(int arg, char **argv)
1758     {
1759         unsigned int exx[4] = {0, 0, 0, 0};
1760         __cpuid(exx, 1);
1761     }
1762     ''', name: '__cpuid',
1763     args: test_c_args)
1764   cdata.set('HAVE__CPUID', 1)
1765 endif
1768 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1769 # versions of clang do not understand -fexcess-precision=standard, the use of
1770 # x87 floating point operations leads to problems like isinf possibly returning
1771 # false for a value that is infinite when converted from the 80bit register to
1772 # the 8byte memory representation.
1774 # Only perform the test if the compiler doesn't understand
1775 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1776 # automatically.
1777 if '-fexcess-precision=standard' not in cflags
1778   if not cc.compiles('''
1779 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1780 choke me
1781 #endif''',
1782       name: '', args: test_c_args)
1783     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1784   endif
1785 endif
1789 ###############################################################
1790 # Compiler flags
1791 ###############################################################
1793 common_functional_flags = [
1794   # Disable strict-aliasing rules; needed for gcc 3.3+
1795   '-fno-strict-aliasing',
1796   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1797   '-fwrapv',
1798   '-fexcess-precision=standard',
1801 cflags += cc.get_supported_arguments(common_functional_flags)
1802 if llvm.found()
1803   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1804 endif
1806 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1807 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1809 common_warning_flags = [
1810   '-Wmissing-prototypes',
1811   '-Wpointer-arith',
1812   # Really don't want VLAs to be used in our dialect of C
1813   '-Werror=vla',
1814   # On macOS, complain about usage of symbols newer than the deployment target
1815   '-Werror=unguarded-availability-new',
1816   '-Wendif-labels',
1817   '-Wmissing-format-attribute',
1818   '-Wimplicit-fallthrough=3',
1819   '-Wcast-function-type',
1820   '-Wshadow=compatible-local',
1821   # This was included in -Wall/-Wformat in older GCC versions
1822   '-Wformat-security',
1825 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1826 if llvm.found()
1827   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1828 endif
1830 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1831 # the result for them
1832 cflags_no_decl_after_statement = []
1833 if cc.has_argument('-Wdeclaration-after-statement')
1834   cflags_warn += '-Wdeclaration-after-statement'
1835   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1836 endif
1839 # The following tests want to suppress various unhelpful warnings by adding
1840 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1841 # switches, so we have to test for the positive form and if that works,
1842 # add the negative form.
1844 negative_warning_flags = [
1845   # Suppress clang's unhelpful unused-command-line-argument warnings.
1846   'unused-command-line-argument',
1848   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
1849   # of warnings when building plperl because of usages in the Perl headers.
1850   'compound-token-split-by-macro',
1852   # Similarly disable useless truncation warnings from gcc 8+
1853   'format-truncation',
1854   'stringop-truncation',
1856   # Suppress clang 16's strict warnings about function casts
1857   'cast-function-type-strict',
1859   # To make warning_level=2 / -Wextra work, we'd need at least the following
1860   # 'clobbered',
1861   # 'missing-field-initializers',
1862   # 'sign-compare',
1863   # 'unused-parameter',
1866 foreach w : negative_warning_flags
1867   if cc.has_argument('-W' + w)
1868     cflags_warn += '-Wno-' + w
1869   endif
1870   if llvm.found() and cpp.has_argument('-W' + w)
1871     cxxflags_warn += '-Wno-' + w
1872   endif
1873 endforeach
1876 # From Project.pm
1877 if cc.get_id() == 'msvc'
1878   cflags_warn += [
1879     '/wd4018', # signed/unsigned mismatch
1880     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
1881     '/wd4273', # inconsistent DLL linkage
1882     '/wd4101', # unreferenced local variable
1883     '/wd4102', # unreferenced label
1884     '/wd4090', # different 'modifier' qualifiers
1885     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
1886   ]
1888   cppflags += [
1889     '/DWIN32',
1890     '/DWINDOWS',
1891     '/D__WINDOWS__',
1892     '/D__WIN32__',
1893     '/D_CRT_SECURE_NO_DEPRECATE',
1894     '/D_CRT_NONSTDC_NO_DEPRECATE',
1895   ]
1897   # We never need export libraries. As link.exe reports their creation, they
1898   # are unnecessarily noisy. Similarly, we don't need import library for
1899   # modules, we only import them dynamically, and they're also noisy.
1900   ldflags += '/NOEXP'
1901   ldflags_mod += '/NOIMPLIB'
1902 endif
1906 ###############################################################
1907 # Atomics
1908 ###############################################################
1910 if not get_option('spinlocks')
1911   warning('Not using spinlocks will cause poor performance')
1912 else
1913   cdata.set('HAVE_SPINLOCKS', 1)
1914 endif
1916 if not get_option('atomics')
1917   warning('Not using atomics will cause poor performance')
1918 else
1919   # XXX: perhaps we should require some atomics support in this case these
1920   # days?
1921   cdata.set('HAVE_ATOMICS', 1)
1923   atomic_checks = [
1924     {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
1925      'desc': '__sync_lock_test_and_set(char)',
1926      'test': '''
1927 char lock = 0;
1928 __sync_lock_test_and_set(&lock, 1);
1929 __sync_lock_release(&lock);'''},
1931     {'name': 'HAVE_GCC__SYNC_INT32_TAS',
1932      'desc': '__sync_lock_test_and_set(int32)',
1933      'test': '''
1934 int lock = 0;
1935 __sync_lock_test_and_set(&lock, 1);
1936 __sync_lock_release(&lock);'''},
1938     {'name': 'HAVE_GCC__SYNC_INT32_CAS',
1939      'desc': '__sync_val_compare_and_swap(int32)',
1940      'test': '''
1941 int val = 0;
1942 __sync_val_compare_and_swap(&val, 0, 37);'''},
1944     {'name': 'HAVE_GCC__SYNC_INT64_CAS',
1945      'desc': '__sync_val_compare_and_swap(int64)',
1946      'test': '''
1947 INT64 val = 0;
1948 __sync_val_compare_and_swap(&val, 0, 37);'''},
1950     {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
1951      'desc': ' __atomic_compare_exchange_n(int32)',
1952      'test': '''
1953 int val = 0;
1954 int expect = 0;
1955 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1957     {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
1958      'desc': ' __atomic_compare_exchange_n(int64)',
1959      'test': '''
1960 INT64 val = 0;
1961 INT64 expect = 0;
1962 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1963   ]
1965   foreach check : atomic_checks
1966     test = '''
1967 int main(void)
1970 }'''.format(check['test'])
1972     cdata.set(check['name'],
1973       cc.links(test,
1974         name: check['desc'],
1975         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
1976     )
1977   endforeach
1979 endif
1983 ###############################################################
1984 # Select CRC-32C implementation.
1986 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
1987 # use the special CRC instructions for calculating CRC-32C. If we're not
1988 # targeting such a processor, but we can nevertheless produce code that uses
1989 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
1990 # implementations and select which one to use at runtime, depending on whether
1991 # SSE 4.2 is supported by the processor we're running on.
1993 # Similarly, if we are targeting an ARM processor that has the CRC
1994 # instructions that are part of the ARMv8 CRC Extension, use them. And if
1995 # we're not targeting such a processor, but can nevertheless produce code that
1996 # uses the CRC instructions, compile both, and select at runtime.
1997 ###############################################################
1999 have_optimized_crc = false
2000 cflags_crc = []
2001 if host_cpu == 'x86' or host_cpu == 'x86_64'
2003   if cc.get_id() == 'msvc'
2004     cdata.set('USE_SSE42_CRC32C', false)
2005     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2006     have_optimized_crc = true
2007   else
2009     prog = '''
2010 #include <nmmintrin.h>
2012 int main(void)
2014     unsigned int crc = 0;
2015     crc = _mm_crc32_u8(crc, 0);
2016     crc = _mm_crc32_u32(crc, 0);
2017     /* return computed value, to prevent the above being optimized away */
2018     return crc == 0;
2022     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2023           args: test_c_args)
2024       # Use Intel SSE 4.2 unconditionally.
2025       cdata.set('USE_SSE42_CRC32C', 1)
2026       have_optimized_crc = true
2027     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2028           args: test_c_args + ['-msse4.2'])
2029       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2030       # the runtime check.
2031       cflags_crc += '-msse4.2'
2032       cdata.set('USE_SSE42_CRC32C', false)
2033       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2034       have_optimized_crc = true
2035     endif
2037   endif
2039 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2041   prog = '''
2042 #include <arm_acle.h>
2044 int main(void)
2046     unsigned int crc = 0;
2047     crc = __crc32cb(crc, 0);
2048     crc = __crc32ch(crc, 0);
2049     crc = __crc32cw(crc, 0);
2050     crc = __crc32cd(crc, 0);
2052     /* return computed value, to prevent the above being optimized away */
2053     return crc == 0;
2057   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2058       args: test_c_args)
2059     # Use ARM CRC Extension unconditionally
2060     cdata.set('USE_ARMV8_CRC32C', 1)
2061     have_optimized_crc = true
2062   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2063       args: test_c_args + ['-march=armv8-a+crc'])
2064     # Use ARM CRC Extension, with runtime check
2065     cflags_crc += '-march=armv8-a+crc'
2066     cdata.set('USE_ARMV8_CRC32C', false)
2067     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2068     have_optimized_crc = true
2069   endif
2071 elif host_cpu == 'loongarch64'
2073   prog = '''
2074 int main(void)
2076     unsigned int crc = 0;
2077     crc = __builtin_loongarch_crcc_w_b_w(0, crc);
2078     crc = __builtin_loongarch_crcc_w_h_w(0, crc);
2079     crc = __builtin_loongarch_crcc_w_w_w(0, crc);
2080     crc = __builtin_loongarch_crcc_w_d_w(0, crc);
2082     /* return computed value, to prevent the above being optimized away */
2083     return crc == 0;
2087   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',
2088       args: test_c_args)
2089     # Use LoongArch CRC instruction unconditionally
2090     cdata.set('USE_LOONGARCH_CRC32C', 1)
2091     have_optimized_crc = true
2092   endif
2094 endif
2096 if not have_optimized_crc
2097   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2098   # support.
2099   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2100 endif
2104 ###############################################################
2105 # Other CPU specific stuff
2106 ###############################################################
2108 if host_cpu == 'x86_64'
2110   if cc.compiles('''
2111       void main(void)
2112       {
2113           long long x = 1; long long r;
2114           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2115       }''',
2116       name: '@0@: popcntq instruction'.format(host_cpu),
2117       args: test_c_args)
2118     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2119   endif
2121 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2122   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2123   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2124     if cc.compiles('''
2125       static inline int
2126       addi(int ra, int si)
2127       {
2128           int res = 0;
2129           if (__builtin_constant_p(si))
2130               __asm__ __volatile__(
2131                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2132           return res;
2133       }
2134       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2135       ''',
2136       args: test_c_args)
2137       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2138     endif
2139   endif
2140 endif
2144 ###############################################################
2145 # Library / OS tests
2146 ###############################################################
2148 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2149 # unnecessary checks over and over, particularly on windows.
2150 header_checks = [
2151   'atomic.h',
2152   'copyfile.h',
2153   'crtdefs.h',
2154   'execinfo.h',
2155   'getopt.h',
2156   'ifaddrs.h',
2157   'langinfo.h',
2158   'mbarrier.h',
2159   'stdbool.h',
2160   'strings.h',
2161   'sys/epoll.h',
2162   'sys/event.h',
2163   'sys/personality.h',
2164   'sys/prctl.h',
2165   'sys/procctl.h',
2166   'sys/signalfd.h',
2167   'sys/ucred.h',
2168   'termios.h',
2169   'ucred.h',
2172 foreach header : header_checks
2173   varname = 'HAVE_' + header.underscorify().to_upper()
2175   # Emulate autoconf behaviour of not-found->undef, found->1
2176   found = cc.has_header(header,
2177     include_directories: postgres_inc, args: test_c_args)
2178   cdata.set(varname, found ? 1 : false,
2179             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2180 endforeach
2183 decl_checks = [
2184   ['F_FULLFSYNC', 'fcntl.h'],
2185   ['fdatasync', 'unistd.h'],
2186   ['posix_fadvise', 'fcntl.h'],
2187   ['strlcat', 'string.h'],
2188   ['strlcpy', 'string.h'],
2189   ['strnlen', 'string.h'],
2192 # Need to check for function declarations for these functions, because
2193 # checking for library symbols wouldn't handle deployment target
2194 # restrictions on macOS
2195 decl_checks += [
2196   ['preadv', 'sys/uio.h'],
2197   ['pwritev', 'sys/uio.h'],
2200 foreach c : decl_checks
2201   func = c.get(0)
2202   header = c.get(1)
2203   args = c.get(2, {})
2204   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2206   found = cc.has_header_symbol(header, func,
2207     args: test_c_args, include_directories: postgres_inc,
2208     kwargs: args)
2209   cdata.set10(varname, found, description:
2210 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2211    don't.'''.format(func))
2212 endforeach
2215 if cc.has_type('struct option',
2216     args: test_c_args, include_directories: postgres_inc,
2217     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2218   cdata.set('HAVE_STRUCT_OPTION', 1)
2219 endif
2222 foreach c : ['opterr', 'optreset']
2223   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2225   if cc.links('''
2226 #include <unistd.h>
2227 int main(void)
2229     extern int @0@;
2230     @0@ = 1;
2232 '''.format(c), name: c, args: test_c_args)
2233     cdata.set(varname, 1)
2234   else
2235     cdata.set(varname, false)
2236   endif
2237 endforeach
2239 if cc.has_type('socklen_t',
2240     args: test_c_args, include_directories: postgres_inc,
2241     prefix: '''
2242 #include <sys/socket.h>''')
2243   cdata.set('HAVE_SOCKLEN_T', 1)
2244 endif
2246 if cc.has_member('struct sockaddr', 'sa_len',
2247     args: test_c_args, include_directories: postgres_inc,
2248     prefix: '''
2249 #include <sys/types.h>
2250 #include <sys/socket.h>''')
2251   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2252 endif
2254 if cc.has_member('struct tm', 'tm_zone',
2255     args: test_c_args, include_directories: postgres_inc,
2256     prefix: '''
2257 #include <sys/types.h>
2258 #include <time.h>
2259 ''')
2260   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2261 endif
2263 if cc.compiles('''
2264 #include <time.h>
2265 extern int foo(void);
2266 int foo(void)
2268     return timezone / 60;
2270 ''',
2271     name: 'global variable `timezone\' exists',
2272     args: test_c_args, include_directories: postgres_inc)
2273   cdata.set('HAVE_INT_TIMEZONE', 1)
2274 else
2275   cdata.set('HAVE_INT_TIMEZONE', false)
2276 endif
2278 if cc.has_type('union semun',
2279     args: test_c_args,
2280     include_directories: postgres_inc,
2281     prefix: '''
2282 #include <sys/types.h>
2283 #include <sys/ipc.h>
2284 #include <sys/sem.h>
2285 ''')
2286   cdata.set('HAVE_UNION_SEMUN', 1)
2287 endif
2289 if cc.compiles('''
2290 #include <string.h>
2291 int main(void)
2293   char buf[100];
2294   switch (strerror_r(1, buf, sizeof(buf)))
2295   { case 0: break; default: break; }
2296 }''',
2297     name: 'strerror_r',
2298     args: test_c_args, include_directories: postgres_inc)
2299   cdata.set('STRERROR_R_INT', 1)
2300 else
2301   cdata.set('STRERROR_R_INT', false)
2302 endif
2304 # Find the right header file for the locale_t type.  macOS needs xlocale.h;
2305 # standard is locale.h, but glibc <= 2.25 also had an xlocale.h file that
2306 # we should not use so we check the standard header first.  MSVC has a
2307 # replacement defined in src/include/port/win32_port.h.
2308 if not cc.has_type('locale_t', prefix: '#include <locale.h>') and \
2309    cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2310   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2311 endif
2313 # Check if the C compiler understands typeof or a variant.  Define
2314 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2315 foreach kw : ['typeof', '__typeof__', 'decltype']
2316   if cc.compiles('''
2317 int main(void)
2319     int x = 0;
2320     @0@(x) y;
2321     y = x;
2322     return y;
2324 '''.format(kw),
2325     name: 'typeof()',
2326     args: test_c_args, include_directories: postgres_inc)
2328     cdata.set('HAVE_TYPEOF', 1)
2329     if kw != 'typeof'
2330       cdata.set('typeof', kw)
2331     endif
2333     break
2334   endif
2335 endforeach
2338 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2339 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2340 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2341 wcstombs_l_test = '''
2342 #include <stdlib.h>
2343 #include <locale.h>
2346 void main(void)
2348 #ifndef wcstombs_l
2349     (void) wcstombs_l;
2350 #endif
2353 if (not cc.compiles(wcstombs_l_test.format(''),
2354       name: 'wcstombs_l') and
2355     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2356       name: 'wcstombs_l in xlocale.h'))
2357     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2358 endif
2361 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2362 # understands, because it conflicts with __declspec(restrict). Therefore we
2363 # define pg_restrict to the appropriate definition, which presumably won't
2364 # conflict.
2366 # We assume C99 support, so we don't need to make this conditional.
2368 # XXX: Historically we allowed platforms to disable restrict in template
2369 # files, but that was only added for AIX when building with XLC, which we
2370 # don't support yet.
2371 cdata.set('pg_restrict', '__restrict')
2374 # Most libraries are included only if they demonstrably provide a function we
2375 # need, but libm is an exception: always include it, because there are too
2376 # many compilers that play cute optimization games that will break probes for
2377 # standard functions such as pow().
2378 os_deps += cc.find_library('m', required: false)
2380 rt_dep = cc.find_library('rt', required: false)
2382 dl_dep = cc.find_library('dl', required: false)
2384 util_dep = cc.find_library('util', required: false)
2386 getopt_dep = cc.find_library('getopt', required: false)
2387 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2388 # Check if we want to replace getopt/getopt_long even if provided by the system
2389 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2390 #   so always use our version on Windows
2391 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2392 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2393 # - We want to use system's getopt_long() only if the system provides struct
2394 #   option
2395 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2396 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2398 # Required on BSDs
2399 execinfo_dep = cc.find_library('execinfo', required: false)
2401 if host_system == 'cygwin'
2402   cygipc_dep = cc.find_library('cygipc', required: false)
2403 else
2404   cygipc_dep = not_found_dep
2405 endif
2407 if host_system == 'sunos'
2408   socket_dep = cc.find_library('socket', required: false)
2409 else
2410   socket_dep = not_found_dep
2411 endif
2413 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2414 # unnecessary checks over and over, particularly on windows.
2415 func_checks = [
2416   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2417   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2418   ['clock_gettime', {'dependencies': [rt_dep], 'define': false}],
2419   ['copyfile'],
2420   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2421   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2422   # required. Just checking for dlsym() ought to suffice.
2423   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2424   ['explicit_bzero'],
2425   ['getifaddrs'],
2426   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2427   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2428   ['getpeereid'],
2429   ['getpeerucred'],
2430   ['inet_aton'],
2431   ['inet_pton'],
2432   ['kqueue'],
2433   ['mbstowcs_l'],
2434   ['memset_s'],
2435   ['mkdtemp'],
2436   ['posix_fadvise'],
2437   ['posix_fallocate'],
2438   ['ppoll'],
2439   ['pstat'],
2440   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2441   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2442   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2443   ['setproctitle', {'dependencies': [util_dep]}],
2444   ['setproctitle_fast'],
2445   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2446   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2447   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2448   ['socket', {'dependencies': [socket_dep], 'define': false}],
2449   ['strchrnul'],
2450   ['strerror_r', {'dependencies': [thread_dep]}],
2451   ['strlcat'],
2452   ['strlcpy'],
2453   ['strnlen'],
2454   ['strsignal'],
2455   ['sync_file_range'],
2456   ['syncfs'],
2457   ['uselocale'],
2458   ['wcstombs_l'],
2461 func_check_results = {}
2462 foreach c : func_checks
2463   func = c.get(0)
2464   kwargs = c.get(1, {})
2465   deps = kwargs.get('dependencies', [])
2467   if kwargs.get('skip', false)
2468     continue
2469   endif
2471   found = cc.has_function(func, args: test_c_args)
2473   if not found
2474     foreach dep : deps
2475       if not dep.found()
2476         continue
2477       endif
2478       found = cc.has_function(func, args: test_c_args,
2479                               dependencies: [dep])
2480       if found
2481         os_deps += dep
2482         break
2483       endif
2484     endforeach
2485   endif
2487   func_check_results += {func: found}
2489   if kwargs.get('define', true)
2490     # Emulate autoconf behaviour of not-found->undef, found->1
2491     cdata.set('HAVE_' + func.underscorify().to_upper(),
2492               found  ? 1 : false,
2493               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2494   endif
2495 endforeach
2498 if cc.has_function('syslog', args: test_c_args) and \
2499     cc.check_header('syslog.h', args: test_c_args)
2500   cdata.set('HAVE_SYSLOG', 1)
2501 endif
2504 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2505 # semaphores
2506 if sema_kind == 'unnamed_posix' and \
2507    not func_check_results.get('sem_init', false)
2508   sema_kind = 'sysv'
2509 endif
2511 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2512 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2514 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2515 cdata.set_quoted('DLSUFFIX', dlsuffix)
2518 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2519 cdata.set_quoted('PG_VERSION_STR',
2520   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2521     pg_version, host_machine.cpu_family(), host_system,
2522     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2523   )
2527 ###############################################################
2528 # NLS / Gettext
2529 ###############################################################
2531 nlsopt = get_option('nls')
2532 libintl = not_found_dep
2534 if not nlsopt.disabled()
2535   # otherwise there'd be lots of
2536   # "Gettext not found, all translation (po) targets will be ignored."
2537   # warnings if not found.
2538   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2540   # meson 0.59 has this wrapped in dependency('intl')
2541   if (msgfmt.found() and
2542       cc.check_header('libintl.h', required: nlsopt,
2543         args: test_c_args, include_directories: postgres_inc))
2545     # in libc
2546     if cc.has_function('ngettext')
2547       libintl = declare_dependency()
2548     else
2549       libintl = cc.find_library('intl',
2550         has_headers: ['libintl.h'], required: nlsopt,
2551         header_include_directories: postgres_inc,
2552         dirs: test_lib_d)
2553     endif
2554   endif
2556   if libintl.found()
2557     i18n = import('i18n')
2558     cdata.set('ENABLE_NLS', 1)
2559   endif
2560 endif
2564 ###############################################################
2565 # Build
2566 ###############################################################
2568 # Set up compiler / linker arguments to be used everywhere, individual targets
2569 # can add further args directly, or indirectly via dependencies
2570 add_project_arguments(cflags, language: ['c'])
2571 add_project_arguments(cppflags, language: ['c'])
2572 add_project_arguments(cflags_warn, language: ['c'])
2573 add_project_arguments(cxxflags, language: ['cpp'])
2574 add_project_arguments(cppflags, language: ['cpp'])
2575 add_project_arguments(cxxflags_warn, language: ['cpp'])
2576 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2579 # Collect a number of lists of things while recursing through the source
2580 # tree. Later steps then can use those.
2582 # list of targets for various alias targets
2583 backend_targets = []
2584 bin_targets = []
2585 pl_targets = []
2586 contrib_targets = []
2587 testprep_targets = []
2588 nls_targets = []
2591 # Define the tests to distribute them to the correct test styles later
2592 test_deps = []
2593 tests = []
2596 # Default options for targets
2598 # First identify rpaths
2599 bin_install_rpaths = []
2600 lib_install_rpaths = []
2601 mod_install_rpaths = []
2604 # Don't add rpaths on darwin for now - as long as only absolute references to
2605 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2606 # their final destination.
2607 if host_system != 'darwin'
2608   # Add absolute path to libdir to rpath. This ensures installed binaries /
2609   # libraries find our libraries (mainly libpq).
2610   bin_install_rpaths += dir_prefix / dir_lib
2611   lib_install_rpaths += dir_prefix / dir_lib
2612   mod_install_rpaths += dir_prefix / dir_lib
2614   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2615   #
2616   # Not needed on darwin even if we use relative rpaths for our own libraries,
2617   # as the install_name of libraries in extra_lib_dirs will point to their
2618   # location anyway.
2619   bin_install_rpaths += postgres_lib_d
2620   lib_install_rpaths += postgres_lib_d
2621   mod_install_rpaths += postgres_lib_d
2622 endif
2625 # Define arguments for default targets
2627 default_target_args = {
2628   'implicit_include_directories': false,
2629   'install': true,
2632 default_lib_args = default_target_args + {
2633   'name_prefix': '',
2636 internal_lib_args = default_lib_args + {
2637   'build_by_default': false,
2638   'install': false,
2641 default_mod_args = default_lib_args + {
2642   'name_prefix': '',
2643   'install_dir': dir_lib_pkg,
2646 default_bin_args = default_target_args + {
2647   'install_dir': dir_bin,
2650 if get_option('rpath')
2651   default_lib_args += {
2652     'install_rpath': ':'.join(lib_install_rpaths),
2653   }
2655   default_mod_args += {
2656     'install_rpath': ':'.join(mod_install_rpaths),
2657   }
2659   default_bin_args += {
2660     'install_rpath': ':'.join(bin_install_rpaths),
2661   }
2662 endif
2665 # Helper for exporting a limited number of symbols
2666 gen_export_kwargs = {
2667   'input': 'exports.txt',
2668   'output': '@BASENAME@.'+export_file_suffix,
2669   'command': [perl, files('src/tools/gen_export.pl'),
2670    '--format', export_file_format,
2671    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2672   'build_by_default': false,
2673   'install': false,
2679 ### Helpers for custom targets used across the tree
2682 catalog_pm = files('src/backend/catalog/Catalog.pm')
2683 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2684 gen_kwlist_deps = [perfect_hash_pm]
2685 gen_kwlist_cmd = [
2686   perl, '-I', '@SOURCE_ROOT@/src/tools',
2687   files('src/tools/gen_keywordlist.pl'),
2688   '--output', '@OUTDIR@', '@INPUT@']
2693 ### windows resources related stuff
2696 if host_system == 'windows'
2697   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2698   win32ver_rc = files('src/port/win32ver.rc')
2699   rcgen = find_program('src/tools/rcgen', native: true)
2701   rcgen_base_args = [
2702     '--srcdir', '@SOURCE_DIR@',
2703     '--builddir', meson.build_root(),
2704     '--rcout', '@OUTPUT0@',
2705     '--out', '@OUTPUT1@',
2706     '--input', '@INPUT@',
2707     '@EXTRA_ARGS@',
2708   ]
2710   if cc.get_argument_syntax() == 'msvc'
2711     rc = find_program('rc', required: true)
2712     rcgen_base_args += ['--rc', rc.path()]
2713     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2714   else
2715     windres = find_program('windres', required: true)
2716     rcgen_base_args += ['--windres', windres.path()]
2717     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2718   endif
2720   # msbuild backend doesn't support this atm
2721   if meson.backend() == 'ninja'
2722     rcgen_base_args += ['--depfile', '@DEPFILE@']
2723   endif
2725   rcgen_bin_args = rcgen_base_args + [
2726     '--VFT_TYPE', 'VFT_APP',
2727     '--FILEENDING', 'exe',
2728     '--ICO', pg_ico
2729   ]
2731   rcgen_lib_args = rcgen_base_args + [
2732     '--VFT_TYPE', 'VFT_DLL',
2733     '--FILEENDING', 'dll',
2734   ]
2736   rc_bin_gen = generator(rcgen,
2737     depfile: '@BASENAME@.d',
2738     arguments: rcgen_bin_args,
2739     output: rcgen_outputs,
2740   )
2742   rc_lib_gen = generator(rcgen,
2743     depfile: '@BASENAME@.d',
2744     arguments: rcgen_lib_args,
2745     output: rcgen_outputs,
2746   )
2747 endif
2751 # headers that the whole build tree depends on
2752 generated_headers = []
2753 # headers that the backend build depends on
2754 generated_backend_headers = []
2755 # configure_files() output, needs a way of converting to file names
2756 configure_files = []
2758 # generated files that might conflict with a partial in-tree autoconf build
2759 generated_sources = []
2760 # same, for paths that differ between autoconf / meson builds
2761 # elements are [dir, [files]]
2762 generated_sources_ac = {}
2765 # First visit src/include - all targets creating headers are defined
2766 # within. That makes it easy to add the necessary dependencies for the
2767 # subsequent build steps.
2769 subdir('src/include')
2771 subdir('config')
2773 # Then through src/port and src/common, as most other things depend on them
2775 frontend_port_code = declare_dependency(
2776   compile_args: ['-DFRONTEND'],
2777   include_directories: [postgres_inc],
2778   dependencies: os_deps,
2781 backend_port_code = declare_dependency(
2782   compile_args: ['-DBUILDING_DLL'],
2783   include_directories: [postgres_inc],
2784   sources: [errcodes], # errcodes.h is needed due to use of ereport
2785   dependencies: os_deps,
2788 subdir('src/port')
2790 frontend_common_code = declare_dependency(
2791   compile_args: ['-DFRONTEND'],
2792   include_directories: [postgres_inc],
2793   sources: generated_headers,
2794   dependencies: [os_deps, zlib, zstd],
2797 backend_common_code = declare_dependency(
2798   compile_args: ['-DBUILDING_DLL'],
2799   include_directories: [postgres_inc],
2800   sources: generated_headers,
2801   dependencies: [os_deps, zlib, zstd],
2804 subdir('src/common')
2806 # all shared libraries should depend on shlib_code
2807 shlib_code = declare_dependency(
2808   link_args: ldflags_sl,
2811 # all static libraries not part of the backend should depend on this
2812 frontend_stlib_code = declare_dependency(
2813   include_directories: [postgres_inc],
2814   link_with: [common_static, pgport_static],
2815   sources: generated_headers,
2816   dependencies: [os_deps, libintl],
2819 # all shared libraries not part of the backend should depend on this
2820 frontend_shlib_code = declare_dependency(
2821   include_directories: [postgres_inc],
2822   link_with: [common_shlib, pgport_shlib],
2823   sources: generated_headers,
2824   dependencies: [shlib_code, os_deps, libintl],
2827 # Dependencies both for static and shared libpq
2828 libpq_deps += [
2829   thread_dep,
2831   gssapi,
2832   ldap_r,
2833   libintl,
2834   ssl,
2837 subdir('src/interfaces/libpq')
2838 # fe_utils depends on libpq
2839 subdir('src/fe_utils')
2841 # for frontend binaries
2842 frontend_code = declare_dependency(
2843   include_directories: [postgres_inc],
2844   link_with: [fe_utils, common_static, pgport_static],
2845   sources: generated_headers,
2846   dependencies: [os_deps, libintl],
2849 backend_both_deps += [
2850   thread_dep,
2851   bsd_auth,
2852   gssapi,
2853   icu,
2854   icu_i18n,
2855   ldap,
2856   libintl,
2857   libxml,
2858   lz4,
2859   pam,
2860   ssl,
2861   systemd,
2862   zlib,
2863   zstd,
2866 backend_mod_deps = backend_both_deps + os_deps
2868 backend_code = declare_dependency(
2869   compile_args: ['-DBUILDING_DLL'],
2870   include_directories: [postgres_inc],
2871   link_args: ldflags_be,
2872   link_with: [],
2873   sources: generated_headers + generated_backend_headers,
2874   dependencies: os_deps + backend_both_deps + backend_deps,
2877 # install these files only during test, not main install
2878 test_install_data = []
2879 test_install_libs = []
2881 # src/backend/meson.build defines backend_mod_code used for extension
2882 # libraries.
2885 # Then through the main sources. That way contrib can have dependencies on
2886 # main sources. Note that this explicitly doesn't enter src/test, right now a
2887 # few regression tests depend on contrib files.
2889 subdir('src')
2891 subdir('contrib')
2893 subdir('src/test')
2894 subdir('src/interfaces/libpq/test')
2895 subdir('src/interfaces/ecpg/test')
2897 subdir('doc/src/sgml')
2899 generated_sources_ac += {'': ['GNUmakefile']}
2901 # After processing src/test, add test_install_libs to the testprep_targets
2902 # to build them
2903 testprep_targets += test_install_libs
2906 # If there are any files in the source directory that we also generate in the
2907 # build directory, they might get preferred over the newly generated files,
2908 # e.g. because of a #include "file", which always will search in the current
2909 # directory first.
2910 message('checking for file conflicts between source and build directory')
2911 conflicting_files = []
2912 potentially_conflicting_files_t = []
2913 potentially_conflicting_files_t += generated_headers
2914 potentially_conflicting_files_t += generated_backend_headers
2915 potentially_conflicting_files_t += generated_backend_sources
2916 potentially_conflicting_files_t += generated_sources
2918 potentially_conflicting_files = []
2920 # convert all sources of potentially conflicting files into uniform shape
2921 foreach t : potentially_conflicting_files_t
2922   potentially_conflicting_files += t.full_path()
2923 endforeach
2924 foreach t : configure_files
2925   t = '@0@'.format(t)
2926   potentially_conflicting_files += meson.current_build_dir() / t
2927 endforeach
2928 foreach sub, fnames : generated_sources_ac
2929   sub = meson.build_root() / sub
2930   foreach fname : fnames
2931     potentially_conflicting_files += sub / fname
2932   endforeach
2933 endforeach
2935 # find and report conflicting files
2936 foreach build_path : potentially_conflicting_files
2937   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
2938   # str.replace is in 0.56
2939   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
2940   if fs.exists(src_path) or fs.is_symlink(src_path)
2941     conflicting_files += src_path
2942   endif
2943 endforeach
2944 # XXX: Perhaps we should generate a file that would clean these up? The list
2945 # can be long.
2946 if conflicting_files.length() > 0
2947   errmsg_cleanup = '''
2948 Conflicting files in source directory:
2949   @0@
2951 The conflicting files need to be removed, either by removing the files listed
2952 above, or by running configure and then make maintainer-clean.
2954   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
2955   error(errmsg_nonclean_base.format(errmsg_cleanup))
2956 endif
2960 ###############################################################
2961 # Install targets
2962 ###############################################################
2965 # We want to define additional install targets beyond what meson provides. For
2966 # that we need to define targets depending on nearly everything. We collected
2967 # the results of i18n.gettext() invocations into nls_targets, that also
2968 # includes maintainer targets though. Collect the ones we want as a dependency.
2970 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
2971 # generation happens during install, so that's not a real issue.
2972 nls_mo_targets = []
2973 if libintl.found() and meson.version().version_compare('>=0.60')
2974   # use range() to avoid the flattening of the list that foreach() would do
2975   foreach off : range(0, nls_targets.length())
2976     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
2977     # -pot target 3) maintainer -pot target
2978     nls_mo_targets += nls_targets[off][0]
2979   endforeach
2980   alias_target('nls', nls_mo_targets)
2981 endif
2984 all_built = [
2985   backend_targets,
2986   bin_targets,
2987   libpq_st,
2988   pl_targets,
2989   contrib_targets,
2990   nls_mo_targets,
2991   testprep_targets,
2992   ecpg_targets,
2995 # Meson's default install target is quite verbose. Provide one that is quiet.
2996 install_quiet = custom_target('install-quiet',
2997   output: 'install-quiet',
2998   build_always_stale: true,
2999   build_by_default: false,
3000   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
3001   depends: all_built,
3004 # Target to install files used for tests, which aren't installed by default
3005 install_test_files_args = [
3006   install_files,
3007   '--prefix', dir_prefix,
3008   '--install', contrib_data_dir, test_install_data,
3009   '--install', dir_lib_pkg, test_install_libs,
3011 run_target('install-test-files',
3012   command: [python] + install_test_files_args,
3013   depends: testprep_targets,
3018 ###############################################################
3019 # Test prep
3020 ###############################################################
3022 # DESTDIR for the installation we'll run tests in
3023 test_install_destdir = meson.build_root() / 'tmp_install/'
3025 # DESTDIR + prefix appropriately munged
3026 if build_system != 'windows'
3027   # On unixoid systems this is trivial, we just prepend the destdir
3028   assert(dir_prefix.startswith('/')) # enforced by meson
3029   test_install_location = '@0@@1@'.format(test_install_destdir, dir_prefix)
3030 else
3031   # drives, drive-relative paths, etc make this complicated on windows, call
3032   # into a copy of meson's logic for it
3033   command = [
3034     python, '-c',
3035     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3036     test_install_destdir, dir_prefix]
3037   test_install_location = run_command(command, check: true).stdout().strip()
3038 endif
3040 meson_install_args = meson_args + ['install'] + {
3041     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3042     'muon': []
3043 }[meson_impl]
3045 # setup tests should be run first,
3046 # so define priority for these
3047 setup_tests_priority = 100
3048 test('tmp_install',
3049     meson_bin, args: meson_install_args ,
3050     env: {'DESTDIR':test_install_destdir},
3051     priority: setup_tests_priority,
3052     timeout: 300,
3053     is_parallel: false,
3054     suite: ['setup'])
3056 test('install_test_files',
3057     python,
3058     args: install_test_files_args + ['--destdir', test_install_destdir],
3059     priority: setup_tests_priority,
3060     is_parallel: false,
3061     suite: ['setup'])
3063 test_result_dir = meson.build_root() / 'testrun'
3066 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3067 # inevitable conflicts from running tests in parallel, hackishly assign
3068 # different ports for different tests.
3070 testport = 40000
3072 test_env = environment()
3074 temp_install_bindir = test_install_location / get_option('bindir')
3075 test_initdb_template = meson.build_root() / 'tmp_install' / 'initdb-template'
3076 test_env.set('PG_REGRESS', pg_regress.full_path())
3077 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3078 test_env.set('INITDB_TEMPLATE', test_initdb_template)
3080 # Test suites that are not safe by default but can be run if selected
3081 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3082 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3083 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3085 # Add the temporary installation to the library search path on platforms where
3086 # that works (everything but windows, basically). On windows everything
3087 # library-like gets installed into bindir, solving that issue.
3088 if library_path_var != ''
3089   test_env.prepend(library_path_var, test_install_location / get_option('libdir'))
3090 endif
3093 # Create (and remove old) initdb template directory. Tests use that, where
3094 # possible, to make it cheaper to run tests.
3096 # Use python to remove the old cached initdb, as we cannot rely on a working
3097 # 'rm' binary on windows.
3098 test('initdb_cache',
3099      python,
3100      args: [
3101        '-c', '''
3102 import shutil
3103 import sys
3104 import subprocess
3106 shutil.rmtree(sys.argv[1], ignore_errors=True)
3107 sp = subprocess.run(sys.argv[2:] + [sys.argv[1]])
3108 sys.exit(sp.returncode)
3109 ''',
3110        test_initdb_template,
3111        temp_install_bindir / 'initdb',
3112        '-A', 'trust', '-N', '--no-instructions', '--no-locale'
3113      ],
3114      priority: setup_tests_priority - 1,
3115      timeout: 300,
3116      is_parallel: false,
3117      env: test_env,
3118      suite: ['setup'])
3122 ###############################################################
3123 # Test Generation
3124 ###############################################################
3126 # When using a meson version understanding exclude_suites, define a
3127 # 'tmp_install' test setup (the default) that excludes tests running against a
3128 # pre-existing install and a 'running' setup that conflicts with creation of
3129 # the temporary installation and tap tests (which don't support running
3130 # against a running server).
3132 running_suites = []
3133 install_suites = []
3134 if meson.version().version_compare('>=0.57')
3135   runningcheck = true
3136 else
3137   runningcheck = false
3138 endif
3140 testwrap = files('src/tools/testwrap')
3142 foreach test_dir : tests
3143   testwrap_base = [
3144     testwrap,
3145     '--basedir', meson.build_root(),
3146     '--srcdir', test_dir['sd'],
3147   ]
3149   foreach kind, v : test_dir
3150     if kind in ['sd', 'bd', 'name']
3151       continue
3152     endif
3154     t = test_dir[kind]
3156     if kind in ['regress', 'isolation', 'ecpg']
3157       if kind == 'regress'
3158         runner = pg_regress
3159         fallback_dbname = 'regression_@0@'
3160       elif kind == 'isolation'
3161         runner = pg_isolation_regress
3162         fallback_dbname = 'isolation_regression_@0@'
3163       elif kind == 'ecpg'
3164         runner = pg_regress_ecpg
3165         fallback_dbname = 'ecpg_regression_@0@'
3166       endif
3168       test_group = test_dir['name']
3169       test_group_running = test_dir['name'] + '-running'
3171       test_output = test_result_dir / test_group / kind
3172       test_output_running = test_result_dir / test_group_running/ kind
3174       # Unless specified by the test, choose a non-conflicting database name,
3175       # to avoid conflicts when running against existing server.
3176       dbname = t.get('dbname',
3177         fallback_dbname.format(test_dir['name']))
3179       test_command_base = [
3180         runner.full_path(),
3181         '--inputdir', t.get('inputdir', test_dir['sd']),
3182         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3183         '--bindir', '',
3184         '--dlpath', test_dir['bd'],
3185         '--max-concurrent-tests=20',
3186         '--dbname', dbname,
3187       ] + t.get('regress_args', [])
3189       test_selection = []
3190       if t.has_key('schedule')
3191         test_selection += ['--schedule', t['schedule'],]
3192       endif
3194       if kind == 'isolation'
3195         test_selection += t.get('specs', [])
3196       else
3197         test_selection += t.get('sql', [])
3198       endif
3200       env = test_env
3201       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3203       test_kwargs = {
3204         'protocol': 'tap',
3205         'priority': 10,
3206         'timeout': 1000,
3207         'depends': test_deps + t.get('deps', []),
3208         'env': env,
3209       } + t.get('test_kwargs', {})
3211       test(test_group / kind,
3212         python,
3213         args: [
3214           testwrap_base,
3215           '--testgroup', test_group,
3216           '--testname', kind,
3217           '--',
3218           test_command_base,
3219           '--outputdir', test_output,
3220           '--temp-instance', test_output / 'tmp_check',
3221           '--port', testport.to_string(),
3222           test_selection,
3223         ],
3224         suite: test_group,
3225         kwargs: test_kwargs,
3226       )
3227       install_suites += test_group
3229       # some tests can't support running against running DB
3230       if runningcheck and t.get('runningcheck', true)
3231         test(test_group_running / kind,
3232           python,
3233           args: [
3234             testwrap_base,
3235             '--testgroup', test_group_running,
3236             '--testname', kind,
3237             '--',
3238             test_command_base,
3239             '--outputdir', test_output_running,
3240             test_selection,
3241           ],
3242           is_parallel: t.get('runningcheck-parallel', true),
3243           suite: test_group_running,
3244           kwargs: test_kwargs,
3245         )
3246         running_suites += test_group_running
3247       endif
3249       testport += 1
3250     elif kind == 'tap'
3251       if not tap_tests_enabled
3252         continue
3253       endif
3255       test_command = [
3256         perl.path(),
3257         '-I', meson.source_root() / 'src/test/perl',
3258         '-I', test_dir['sd'],
3259       ]
3261       # Add temporary install, the build directory for non-installed binaries and
3262       # also test/ for non-installed test binaries built separately.
3263       env = test_env
3264       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3266       foreach name, value : t.get('env', {})
3267         env.set(name, value)
3268       endforeach
3270       test_group = test_dir['name']
3271       test_kwargs = {
3272         'protocol': 'tap',
3273         'suite': test_group,
3274         'timeout': 1000,
3275         'depends': test_deps + t.get('deps', []),
3276         'env': env,
3277       } + t.get('test_kwargs', {})
3279       foreach onetap : t['tests']
3280         # Make tap test names prettier, remove t/ and .pl
3281         onetap_p = onetap
3282         if onetap_p.startswith('t/')
3283           onetap_p = onetap.split('t/')[1]
3284         endif
3285         if onetap_p.endswith('.pl')
3286           onetap_p = fs.stem(onetap_p)
3287         endif
3289         test(test_dir['name'] / onetap_p,
3290           python,
3291           kwargs: test_kwargs,
3292           args: testwrap_base + [
3293             '--testgroup', test_dir['name'],
3294             '--testname', onetap_p,
3295             '--', test_command,
3296             test_dir['sd'] / onetap,
3297           ],
3298         )
3299       endforeach
3300       install_suites += test_group
3301     else
3302       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3303     endif
3305   endforeach # kinds of tests
3307 endforeach # directories with tests
3309 # repeat condition so meson realizes version dependency
3310 if meson.version().version_compare('>=0.57')
3311   add_test_setup('tmp_install',
3312     is_default: true,
3313     exclude_suites: running_suites)
3314   add_test_setup('running',
3315     exclude_suites: ['setup'] + install_suites)
3316 endif
3320 ###############################################################
3321 # Pseudo targets
3322 ###############################################################
3324 alias_target('backend', backend_targets)
3325 alias_target('bin', bin_targets + [libpq_st])
3326 alias_target('pl', pl_targets)
3327 alias_target('contrib', contrib_targets)
3328 alias_target('testprep', testprep_targets)
3329 alias_target('install-world', install_quiet, installdocs)
3333 ###############################################################
3334 # The End, The End, My Friend
3335 ###############################################################
3337 if meson.version().version_compare('>=0.57')
3339   summary(
3340     {
3341       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3342       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3343       'segment size': get_option('segsize_blocks') != 0 ?
3344         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3345         '@0@ GB'.format(get_option('segsize')),
3346     },
3347     section: 'Data layout',
3348   )
3350   summary(
3351     {
3352       'host system': '@0@ @1@'.format(host_system, host_cpu),
3353       'build system': '@0@ @1@'.format(build_machine.system(),
3354                                        build_machine.cpu_family()),
3355     },
3356     section: 'System',
3357   )
3359   summary(
3360     {
3361       'linker': '@0@'.format(cc.get_linker_id()),
3362       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3363     },
3364     section: 'Compiler',
3365   )
3367   summary(
3368     {
3369       'CPP FLAGS': ' '.join(cppflags),
3370       'C FLAGS, functional': ' '.join(cflags),
3371       'C FLAGS, warnings': ' '.join(cflags_warn),
3372       'C FLAGS, modules': ' '.join(cflags_mod),
3373       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3374       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3375     },
3376     section: 'Compiler Flags',
3377   )
3379   if llvm.found()
3380     summary(
3381       {
3382         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3383       },
3384       section: 'Compiler',
3385     )
3387     summary(
3388       {
3389         'C++ FLAGS, functional': ' '.join(cxxflags),
3390         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3391         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3392       },
3393       section: 'Compiler Flags',
3394     )
3395   endif
3397   summary(
3398     {
3399       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3400       'dtrace': dtrace,
3401       'flex': '@0@ @1@'.format(flex.full_path(), flex_version),
3402     },
3403     section: 'Programs',
3404   )
3406   summary(
3407     {
3408       'bonjour': bonjour,
3409       'bsd_auth': bsd_auth,
3410       'docs': docs_dep,
3411       'docs_pdf': docs_pdf_dep,
3412       'gss': gssapi,
3413       'icu': icu,
3414       'ldap': ldap,
3415       'libxml': libxml,
3416       'libxslt': libxslt,
3417       'llvm': llvm,
3418       'lz4': lz4,
3419       'nls': libintl,
3420       'openssl': ssl,
3421       'pam': pam,
3422       'plperl': perl_dep,
3423       'plpython': python3_dep,
3424       'pltcl': tcl_dep,
3425       'readline': readline,
3426       'selinux': selinux,
3427       'systemd': systemd,
3428       'uuid': uuid,
3429       'zlib': zlib,
3430       'zstd': zstd,
3431     },
3432     section: 'External libraries',
3433   )
3435 endif