Fix ecpg's mechanism for detecting unsupported cases in the grammar.
[pgsql.git] / meson.build
blob077edc8644a86e299ae35dbc9280762dad5cf6ba
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: '16.2',
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 exesuffix = '' # overridden below where necessary
163 dlsuffix = '.so' # overridden below where necessary
164 library_path_var = 'LD_LIBRARY_PATH'
166 # Format of file to control exports from libraries, and how to pass them to
167 # the compiler. For export_fmt @0@ is the path to the file export file.
168 export_file_format = 'gnu'
169 export_file_suffix = 'list'
170 export_fmt = '-Wl,--version-script=@0@'
172 # Flags to add when linking a postgres extension, @0@ is path to
173 # the relevant object on the platform.
174 mod_link_args_fmt = []
176 memset_loop_limit = 1024
178 # Choice of shared memory and semaphore implementation
179 shmem_kind = 'sysv'
180 sema_kind = 'sysv'
182 # We implement support for some operating systems by pretending they're
183 # another. Map here, before determining system properties below
184 if host_system == 'dragonfly'
185   # apparently the most similar
186   host_system = 'netbsd'
187 endif
189 # meson's system names don't quite map to our "traditional" names. In some
190 # places we need the "traditional" name, e.g., for mapping
191 # src/include/port/$os.h to src/include/pg_config_os.h. Define portname for
192 # that purpose.
193 portname = host_system
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
241   # meson defaults to -Wl,-undefined,dynamic_lookup for modules, which we
242   # don't want because a) it's different from what we do for autoconf, b) it
243   # causes warnings in macOS Ventura. But using -Wl,-undefined,error causes a
244   # warning starting in Sonoma. So only add -Wl,-undefined,error if it does
245   # not cause a warning.
246   if cc.has_multi_link_arguments('-Wl,-undefined,error', '-Werror')
247     ldflags_mod += '-Wl,-undefined,error'
248   endif
250   # Starting in Sonoma, the linker warns about the same library being
251   # linked twice.  Which can easily happen when multiple dependencies
252   # depend on the same library. Quiesce the ill considered warning.
253   ldflags += cc.get_supported_link_arguments('-Wl,-no_warn_duplicate_libraries')
255 elif host_system == 'freebsd'
256   sema_kind = 'unnamed_posix'
258 elif host_system == 'linux'
259   sema_kind = 'unnamed_posix'
260   cppflags += '-D_GNU_SOURCE'
262 elif host_system == 'netbsd'
263   # We must resolve all dynamic linking in the core server at program start.
264   # Otherwise the postmaster can self-deadlock due to signals interrupting
265   # resolution of calls, since NetBSD's linker takes a lock while doing that
266   # and some postmaster signal handlers do things that will also acquire that
267   # lock.  As long as we need "-z now", might as well specify "-z relro" too.
268   # While there's not a hard reason to adopt these settings for our other
269   # executables, there's also little reason not to, so just add them to
270   # LDFLAGS.
271   ldflags += ['-Wl,-z,now', '-Wl,-z,relro']
273 elif host_system == 'openbsd'
274   # you're ok
276 elif host_system == 'sunos'
277   portname = 'solaris'
278   export_fmt = '-Wl,-M@0@'
279   cppflags += '-D_POSIX_PTHREAD_SEMANTICS'
281 elif host_system == 'windows'
282   portname = 'win32'
283   exesuffix = '.exe'
284   dlsuffix = '.dll'
285   library_path_var = ''
287   export_file_format = 'win'
288   export_file_suffix = 'def'
289   if cc.get_id() == 'msvc'
290     export_fmt = '/DEF:@0@'
291     mod_link_with_name = '@0@.exe.lib'
292   else
293     export_fmt = '@0@'
294     mod_link_with_name = 'lib@0@.exe.a'
295   endif
296   mod_link_args_fmt = ['@0@']
297   mod_link_with_dir = 'libdir'
299   shmem_kind = 'win32'
300   sema_kind = 'win32'
302   cdata.set('WIN32_STACK_RLIMIT', 4194304)
303   if cc.get_id() == 'msvc'
304     ldflags += '/INCREMENTAL:NO'
305     ldflags += '/STACK:@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
306     # ldflags += '/nxcompat' # generated by msbuild, should have it for ninja?
307   else
308     ldflags += '-Wl,--stack,@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
309     # Need to allow multiple definitions, we e.g. want to override getopt.
310     ldflags += '-Wl,--allow-multiple-definition'
311     # Ensure we get MSVC-like linking behavior.
312     ldflags += '-Wl,--disable-auto-import'
313   endif
315   os_deps += cc.find_library('ws2_32', required: true)
316   secur32_dep = cc.find_library('secur32', required: true)
317   backend_deps += secur32_dep
318   libpq_deps += secur32_dep
320   postgres_inc_d += 'src/include/port/win32'
321   if cc.get_id() == 'msvc'
322     postgres_inc_d += 'src/include/port/win32_msvc'
323   endif
325   windows = import('windows')
327 else
328   # XXX: Should we add an option to override the host_system as an escape
329   # hatch?
330   error('unknown host system: @0@'.format(host_system))
331 endif
335 ###############################################################
336 # Program paths
337 ###############################################################
339 # External programs
340 perl = find_program(get_option('PERL'), required: true, native: true)
341 python = find_program(get_option('PYTHON'), required: true, native: true)
342 flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.35')
343 bison = find_program(get_option('BISON'), native: true, version: '>= 2.3')
344 sed = find_program(get_option('SED'), 'sed', native: true)
345 prove = find_program(get_option('PROVE'), native: true, required: false)
346 tar = find_program(get_option('TAR'), native: true)
347 gzip = find_program(get_option('GZIP'), native: true)
348 program_lz4 = find_program(get_option('LZ4'), native: true, required: false)
349 openssl = find_program(get_option('OPENSSL'), native: true, required: false)
350 program_zstd = find_program(get_option('ZSTD'), native: true, required: false)
351 dtrace = find_program(get_option('DTRACE'), native: true, required: get_option('dtrace'))
352 missing = find_program('config/missing', native: true)
353 cp = find_program('cp', required: false, native: true)
354 xmllint_bin = find_program(get_option('XMLLINT'), native: true, required: false)
355 xsltproc_bin = find_program(get_option('XSLTPROC'), native: true, required: false)
357 bison_flags = []
358 if bison.found()
359   bison_version_c = run_command(bison, '--version', check: true)
360   # bison version string helpfully is something like
361   # >>bison (GNU bison) 3.8.1<<
362   bison_version = bison_version_c.stdout().split(' ')[3].split('\n')[0]
363   if bison_version.version_compare('>=3.0')
364     bison_flags += ['-Wno-deprecated']
365   endif
366 endif
367 bison_cmd = [bison, bison_flags, '-o', '@OUTPUT0@', '-d', '@INPUT@']
368 bison_kw = {
369   'output': ['@BASENAME@.c', '@BASENAME@.h'],
370   'command': bison_cmd,
373 flex_flags = []
374 flex_wrapper = files('src/tools/pgflex')
375 flex_cmd = [python, flex_wrapper,
376   '--builddir', '@BUILD_ROOT@',
377   '--srcdir', '@SOURCE_ROOT@',
378   '--privatedir', '@PRIVATE_DIR@',
379   '--flex', flex, '--perl', perl,
380   '-i', '@INPUT@', '-o', '@OUTPUT0@',
383 wget = find_program('wget', required: false, native: true)
384 wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
386 install_files = files('src/tools/install_files')
390 ###############################################################
391 # Path to meson (for tests etc)
392 ###############################################################
394 # NB: this should really be part of meson, see
395 # https://github.com/mesonbuild/meson/issues/8511
396 meson_binpath_r = run_command(python, 'src/tools/find_meson', check: true)
398 if meson_binpath_r.stdout() == ''
399   error('huh, could not run find_meson.\nerrcode: @0@\nstdout: @1@\nstderr: @2@'.format(
400     meson_binpath_r.returncode(),
401     meson_binpath_r.stdout(),
402     meson_binpath_r.stderr()))
403 endif
405 meson_binpath_s = meson_binpath_r.stdout().split('\n')
406 meson_binpath_len = meson_binpath_s.length()
408 if meson_binpath_len < 1
409   error('unexpected introspect line @0@'.format(meson_binpath_r.stdout()))
410 endif
412 i = 0
413 meson_impl = ''
414 meson_binpath = ''
415 meson_args = []
416 foreach e : meson_binpath_s
417   if i == 0
418     meson_impl = e
419   elif i == 1
420     meson_binpath = e
421   else
422     meson_args += e
423   endif
424   i += 1
425 endforeach
427 if meson_impl not in ['muon', 'meson']
428   error('unknown meson implementation "@0@"'.format(meson_impl))
429 endif
431 meson_bin = find_program(meson_binpath, native: true)
435 ###############################################################
436 # Option Handling
437 ###############################################################
439 cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false)
441 blocksize = get_option('blocksize').to_int() * 1024
443 if get_option('segsize_blocks') != 0
444   if get_option('segsize') != 1
445     warning('both segsize and segsize_blocks specified, segsize_blocks wins')
446   endif
448   segsize = get_option('segsize_blocks')
449 else
450   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
451 endif
453 cdata.set('BLCKSZ', blocksize, description:
454 '''Size of a disk block --- this also limits the size of a tuple. You can set
455    it bigger if you need bigger tuples (although TOAST should reduce the need
456    to have large tuples, since fields can be spread across multiple tuples).
457    BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
458    currently 2^15 (32768). This is determined by the 15-bit widths of the
459    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
460    Changing BLCKSZ requires an initdb.''')
462 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
463 cdata.set('RELSEG_SIZE', segsize)
464 cdata.set('DEF_PGPORT', get_option('pgport'))
465 cdata.set_quoted('DEF_PGPORT_STR', get_option('pgport').to_string())
466 cdata.set_quoted('PG_KRB_SRVNAM', get_option('krb_srvnam'))
467 if get_option('system_tzdata') != ''
468   cdata.set_quoted('SYSTEMTZDIR', get_option('system_tzdata'))
469 endif
473 ###############################################################
474 # Directories
475 ###############################################################
477 # These are set by the equivalent --xxxdir configure options.  We
478 # append "postgresql" to some of them, if the string does not already
479 # contain "pgsql" or "postgres", in order to avoid directory clutter.
481 pkg = 'postgresql'
483 dir_prefix = get_option('prefix')
485 dir_prefix_contains_pg = (dir_prefix.contains('pgsql') or dir_prefix.contains('postgres'))
487 dir_bin = get_option('bindir')
489 dir_data = get_option('datadir')
490 if not (dir_prefix_contains_pg or dir_data.contains('pgsql') or dir_data.contains('postgres'))
491   dir_data = dir_data / pkg
492 endif
494 dir_sysconf = get_option('sysconfdir')
495 if not (dir_prefix_contains_pg or dir_sysconf.contains('pgsql') or dir_sysconf.contains('postgres'))
496   dir_sysconf = dir_sysconf / pkg
497 endif
499 dir_lib = get_option('libdir')
501 dir_lib_pkg = dir_lib
502 if not (dir_prefix_contains_pg or dir_lib_pkg.contains('pgsql') or dir_lib_pkg.contains('postgres'))
503   dir_lib_pkg = dir_lib_pkg / pkg
504 endif
506 dir_pgxs = dir_lib_pkg / 'pgxs'
508 dir_include = get_option('includedir')
510 dir_include_pkg = dir_include
511 dir_include_pkg_rel = ''
512 if not (dir_prefix_contains_pg or dir_include_pkg.contains('pgsql') or dir_include_pkg.contains('postgres'))
513   dir_include_pkg = dir_include_pkg / pkg
514   dir_include_pkg_rel = pkg
515 endif
517 dir_man = get_option('mandir')
519 # FIXME: These used to be separately configurable - worth adding?
520 dir_doc = get_option('datadir') / 'doc' / 'postgresql'
521 dir_doc_html = dir_doc / 'html'
523 dir_locale = get_option('localedir')
526 # Derived values
527 dir_bitcode = dir_lib_pkg / 'bitcode'
528 dir_include_internal = dir_include_pkg / 'internal'
529 dir_include_server = dir_include_pkg / 'server'
530 dir_include_extension = dir_include_server / 'extension'
531 dir_data_extension = dir_data / 'extension'
535 ###############################################################
536 # Search paths, preparation for compiler tests
538 # NB: Arguments added later are not automatically used for subsequent
539 # configuration-time checks (so they are more isolated). If they should be
540 # used, they need to be added to test_c_args as well.
541 ###############################################################
543 postgres_inc = [include_directories(postgres_inc_d)]
544 test_lib_d = postgres_lib_d
545 test_c_args = cppflags + cflags
549 ###############################################################
550 # Library: bsd-auth
551 ###############################################################
553 bsd_authopt = get_option('bsd_auth')
554 bsd_auth = not_found_dep
555 if cc.check_header('bsd_auth.h', required: bsd_authopt,
556     args: test_c_args, include_directories: postgres_inc)
557   cdata.set('USE_BSD_AUTH', 1)
558   bsd_auth = declare_dependency()
559 endif
563 ###############################################################
564 # Library: bonjour
566 # For now don't search for DNSServiceRegister in a library - only Apple's
567 # Bonjour implementation, which is always linked, works.
568 ###############################################################
570 bonjouropt = get_option('bonjour')
571 bonjour = not_found_dep
572 if cc.check_header('dns_sd.h', required: bonjouropt,
573     args: test_c_args, include_directories: postgres_inc) and \
574    cc.has_function('DNSServiceRegister',
575     args: test_c_args, include_directories: postgres_inc)
576   cdata.set('USE_BONJOUR', 1)
577   bonjour = declare_dependency()
578 endif
582 ###############################################################
583 # Option: docs in HTML and man page format
584 ###############################################################
586 docs_opt = get_option('docs')
587 docs_dep = not_found_dep
588 if not docs_opt.disabled()
589   if xmllint_bin.found() and xsltproc_bin.found()
590     docs_dep = declare_dependency()
591   elif docs_opt.enabled()
592     error('missing required tools for docs in HTML / man page format')
593   endif
594 endif
598 ###############################################################
599 # Option: docs in PDF format
600 ###############################################################
602 docs_pdf_opt = get_option('docs_pdf')
603 docs_pdf_dep = not_found_dep
604 if not docs_pdf_opt.disabled()
605   fop = find_program(get_option('FOP'), native: true, required: docs_pdf_opt)
606   if xmllint_bin.found() and xsltproc_bin.found() and fop.found()
607     docs_pdf_dep = declare_dependency()
608   elif docs_pdf_opt.enabled()
609     error('missing required tools for docs in PDF format')
610   endif
611 endif
615 ###############################################################
616 # Library: GSSAPI
617 ###############################################################
619 gssapiopt = get_option('gssapi')
620 krb_srvtab = ''
621 have_gssapi = false
622 if not gssapiopt.disabled()
623   gssapi = dependency('krb5-gssapi', required: gssapiopt)
624   have_gssapi = gssapi.found()
626   if not have_gssapi
627   elif cc.check_header('gssapi/gssapi.h', dependencies: gssapi, required: false,
628       args: test_c_args, include_directories: postgres_inc)
629     cdata.set('HAVE_GSSAPI_GSSAPI_H', 1)
630   elif cc.check_header('gssapi.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
631     cdata.set('HAVE_GSSAPI_H', 1)
632   else
633     have_gssapi = false
634   endif
636   if not have_gssapi
637   elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi, required: false,
638       args: test_c_args, include_directories: postgres_inc)
639     cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
640   elif cc.check_header('gssapi_ext.h', args: test_c_args, dependencies: gssapi, required: gssapiopt)
641     cdata.set('HAVE_GSSAPI_EXT_H', 1)
642   else
643     have_gssapi = false
644   endif
646   if not have_gssapi
647   elif cc.has_function('gss_store_cred_into', dependencies: gssapi,
648       args: test_c_args, include_directories: postgres_inc)
649     cdata.set('ENABLE_GSS', 1)
651     krb_srvtab = 'FILE:/@0@/krb5.keytab)'.format(get_option('sysconfdir'))
652     cdata.set_quoted('PG_KRB_SRVTAB', krb_srvtab)
653   elif gssapiopt.enabled()
654     error('''could not find function 'gss_store_cred_into' required for GSSAPI''')
655   else
656     have_gssapi = false
657   endif
658 endif
659 if not have_gssapi
660   gssapi = not_found_dep
661 endif
665 ###############################################################
666 # Library: ldap
667 ###############################################################
669 ldapopt = get_option('ldap')
670 if ldapopt.disabled()
671   ldap = not_found_dep
672   ldap_r = not_found_dep
673 elif host_system == 'windows'
674   ldap = cc.find_library('wldap32', required: ldapopt)
675   ldap_r = ldap
676 else
677   # macos framework dependency is buggy for ldap (one can argue whether it's
678   # Apple's or meson's fault), leading to an endless recursion with ldap.h
679   # including itself. See https://github.com/mesonbuild/meson/issues/10002
680   # Luckily we only need pkg-config support, so the workaround isn't
681   # complicated.
682   ldap = dependency('ldap', method: 'pkg-config', required: false)
683   ldap_r = ldap
685   # Before 2.5 openldap didn't have a pkg-config file, and it might not be
686   # installed
687   if not ldap.found()
688     ldap = cc.find_library('ldap', required: ldapopt, dirs: test_lib_d,
689       has_headers: 'ldap.h', header_include_directories: postgres_inc)
691     # The separate ldap_r library only exists in OpenLDAP < 2.5, and if we
692     # have 2.5 or later, we shouldn't even probe for ldap_r (we might find a
693     # library from a separate OpenLDAP installation).  The most reliable
694     # way to check that is to check for a function introduced in 2.5.
695     if not ldap.found()
696       # don't have ldap, we shouldn't check for ldap_r
697     elif cc.has_function('ldap_verify_credentials',
698         dependencies: ldap, args: test_c_args)
699       ldap_r = ldap # ldap >= 2.5, no need for ldap_r
700     else
702       # Use ldap_r for FE if available, else assume ldap is thread-safe.
703       ldap_r = cc.find_library('ldap_r', required: false, dirs: test_lib_d,
704         has_headers: 'ldap.h', header_include_directories: postgres_inc)
705       if not ldap_r.found()
706         ldap_r = ldap
707       else
708         # On some platforms ldap_r fails to link without PTHREAD_LIBS.
709         ldap_r = declare_dependency(dependencies: [ldap_r, thread_dep])
710       endif
712       # PostgreSQL sometimes loads libldap_r and plain libldap into the same
713       # process.  Check for OpenLDAP versions known not to tolerate doing so;
714       # assume non-OpenLDAP implementations are safe.  The dblink test suite
715       # exercises the hazardous interaction directly.
716       compat_test_code = '''
717 #include <ldap.h>
718 #if !defined(LDAP_VENDOR_VERSION) || \
719      (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
720       LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
721 choke me
722 #endif
724       if not cc.compiles(compat_test_code,
725           name: 'LDAP implementation compatible',
726           dependencies: ldap, args: test_c_args)
727         warning('''
728 *** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
729 *** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
730 *** also uses LDAP will crash on exit.''')
731       endif
732     endif
733   endif
735   if ldap.found() and cc.has_function('ldap_initialize',
736       dependencies: ldap, args: test_c_args)
737     cdata.set('HAVE_LDAP_INITIALIZE', 1)
738   endif
739 endif
741 if ldap.found()
742   assert(ldap_r.found())
743   cdata.set('USE_LDAP', 1)
744 else
745   assert(not ldap_r.found())
746 endif
750 ###############################################################
751 # Library: LLVM
752 ###############################################################
754 llvmopt = get_option('llvm')
755 llvm = not_found_dep
756 if add_languages('cpp', required: llvmopt, native: false)
757   llvm = dependency('llvm', version: '>=3.9', method: 'config-tool', required: llvmopt)
759   if llvm.found()
761     cdata.set('USE_LLVM', 1)
763     cpp = meson.get_compiler('cpp')
765     llvm_binpath = llvm.get_variable(configtool: 'bindir')
767     ccache = find_program('ccache', native: true, required: false)
768     clang = find_program(llvm_binpath / 'clang', required: true)
769   endif
770 elif llvmopt.auto()
771   message('llvm requires a C++ compiler')
772 endif
776 ###############################################################
777 # Library: icu
778 ###############################################################
780 icuopt = get_option('icu')
781 if not icuopt.disabled()
782   icu = dependency('icu-uc', required: icuopt)
783   icu_i18n = dependency('icu-i18n', required: icuopt)
785   if icu.found()
786     cdata.set('USE_ICU', 1)
787   endif
789 else
790   icu = not_found_dep
791   icu_i18n = not_found_dep
792 endif
796 ###############################################################
797 # Library: libxml
798 ###############################################################
800 libxmlopt = get_option('libxml')
801 if not libxmlopt.disabled()
802   libxml = dependency('libxml-2.0', required: libxmlopt, version: '>= 2.6.23')
804   if libxml.found()
805     cdata.set('USE_LIBXML', 1)
806   endif
807 else
808   libxml = not_found_dep
809 endif
813 ###############################################################
814 # Library: libxslt
815 ###############################################################
817 libxsltopt = get_option('libxslt')
818 if not libxsltopt.disabled()
819   libxslt = dependency('libxslt', required: libxsltopt)
821   if libxslt.found()
822     cdata.set('USE_LIBXSLT', 1)
823   endif
824 else
825   libxslt = not_found_dep
826 endif
830 ###############################################################
831 # Library: lz4
832 ###############################################################
834 lz4opt = get_option('lz4')
835 if not lz4opt.disabled()
836   lz4 = dependency('liblz4', required: lz4opt)
838   if lz4.found()
839     cdata.set('USE_LZ4', 1)
840     cdata.set('HAVE_LIBLZ4', 1)
841   endif
843 else
844   lz4 = not_found_dep
845 endif
849 ###############################################################
850 # Library: Tcl (for pltcl)
852 # NB: tclConfig.sh is used in autoconf build for getting
853 # TCL_SHARED_BUILD, TCL_INCLUDE_SPEC, TCL_LIBS and TCL_LIB_SPEC
854 # variables. For now we have not seen a need to copy
855 # that behaviour to the meson build.
856 ###############################################################
858 tclopt = get_option('pltcl')
859 tcl_version = get_option('tcl_version')
860 tcl_dep = not_found_dep
861 if not tclopt.disabled()
863   # via pkg-config
864   tcl_dep = dependency(tcl_version, required: false)
866   if not tcl_dep.found()
867     tcl_dep = cc.find_library(tcl_version,
868       required: tclopt,
869       dirs: test_lib_d)
870   endif
872   if not cc.has_header('tcl.h', dependencies: tcl_dep, required: tclopt)
873     tcl_dep = not_found_dep
874   endif
875 endif
879 ###############################################################
880 # Library: pam
881 ###############################################################
883 pamopt = get_option('pam')
884 if not pamopt.disabled()
885   pam = dependency('pam', required: false)
887   if not pam.found()
888     pam = cc.find_library('pam', required: pamopt, dirs: test_lib_d)
889   endif
891   if pam.found()
892     pam_header_found = false
894     # header file <security/pam_appl.h> or <pam/pam_appl.h> is required for PAM.
895     if cc.check_header('security/pam_appl.h', dependencies: pam, required: false,
896         args: test_c_args, include_directories: postgres_inc)
897       cdata.set('HAVE_SECURITY_PAM_APPL_H', 1)
898       pam_header_found = true
899     elif cc.check_header('pam/pam_appl.h', dependencies: pam, required: pamopt,
900         args: test_c_args, include_directories: postgres_inc)
901       cdata.set('HAVE_PAM_PAM_APPL_H', 1)
902       pam_header_found = true
903     endif
905     if pam_header_found
906       cdata.set('USE_PAM', 1)
907     else
908       pam = not_found_dep
909     endif
910   endif
911 else
912   pam = not_found_dep
913 endif
917 ###############################################################
918 # Library: Perl (for plperl)
919 ###############################################################
921 perlopt = get_option('plperl')
922 perl_dep = not_found_dep
923 if not perlopt.disabled()
924   perl_may_work = true
926   # First verify that perl has the necessary dependencies installed
927   perl_mods = run_command(
928     [perl,
929      '-MConfig', '-MOpcode', '-MExtUtils::Embed', '-MExtUtils::ParseXS',
930      '-e', ''],
931     check: false)
932   if perl_mods.returncode() != 0
933     perl_may_work = false
934     perl_msg = 'perl installation does not have the required modules'
935   endif
937   # Then inquire perl about its configuration
938   if perl_may_work
939     perl_conf_cmd = [perl, '-MConfig', '-e', 'print $Config{$ARGV[0]}']
940     perlversion = run_command(perl_conf_cmd, 'api_versionstring', check: true).stdout()
941     archlibexp = run_command(perl_conf_cmd, 'archlibexp', check: true).stdout()
942     privlibexp = run_command(perl_conf_cmd, 'privlibexp', check: true).stdout()
943     useshrplib = run_command(perl_conf_cmd, 'useshrplib', check: true).stdout()
945     perl_inc_dir = '@0@/CORE'.format(archlibexp)
947     if perlversion.version_compare('< 5.14')
948       perl_may_work = false
949       perl_msg = 'Perl version 5.14 or later is required, but this is @0@'.format(perlversion)
950     elif useshrplib != 'true'
951       perl_may_work = false
952       perl_msg = 'need a shared perl'
953     endif
954   endif
956   if perl_may_work
957     # On most platforms, archlibexp is also where the Perl include files live ...
958     perl_ccflags = ['-I@0@'.format(perl_inc_dir)]
959     # ... but on newer macOS versions, we must use -iwithsysroot to look
960     # under sysroot
961     if not fs.is_file('@0@/perl.h'.format(perl_inc_dir)) and \
962        fs.is_file('@0@@1@/perl.h'.format(pg_sysroot, perl_inc_dir))
963       perl_ccflags = ['-iwithsysroot', perl_inc_dir]
964     endif
966     # check compiler finds header
967     if not cc.has_header('perl.h', required: false,
968         args: test_c_args + perl_ccflags, include_directories: postgres_inc)
969       perl_may_work = false
970       perl_msg = 'missing perl.h'
971     endif
972   endif
974   if perl_may_work
975     perl_ccflags_r = run_command(perl_conf_cmd, 'ccflags', check: true).stdout()
977     # See comments for PGAC_CHECK_PERL_EMBED_CCFLAGS in perl.m4
978     foreach flag : perl_ccflags_r.split(' ')
979       if flag.startswith('-D') and \
980           (not flag.startswith('-D_') or flag == '_USE_32BIT_TIME_T')
981         perl_ccflags += flag
982       endif
983     endforeach
985     if host_system == 'windows'
986       perl_ccflags += ['-DPLPERL_HAVE_UID_GID']
988       if cc.get_id() == 'msvc'
989         # prevent binary mismatch between MSVC built plperl and Strawberry or
990         # msys ucrt perl libraries
991         perl_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
992       endif
993     endif
995     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
996     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
998     # We are after Embed's ldopts, but without the subset mentioned in
999     # Config's ccdlflags and ldflags.  (Those are the choices of those who
1000     # built the Perl installation, which are not necessarily appropriate
1001     # for building PostgreSQL.)
1002     ldopts = run_command(perl, '-MExtUtils::Embed', '-e', 'ldopts', check: true).stdout().strip()
1003     undesired = run_command(perl_conf_cmd, 'ccdlflags', check: true).stdout().split()
1004     undesired += run_command(perl_conf_cmd, 'ldflags', check: true).stdout().split()
1006     perl_ldopts = []
1007     foreach ldopt : ldopts.split(' ')
1008       if ldopt == '' or ldopt in undesired
1009         continue
1010       endif
1012       perl_ldopts += ldopt.strip('"')
1013     endforeach
1015     message('LDFLAGS recommended by perl: "@0@"'.format(ldopts))
1016     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1018     perl_dep_int = declare_dependency(
1019       compile_args: perl_ccflags,
1020       link_args: perl_ldopts,
1021       version: perlversion,
1022     )
1024     # While we're at it, check that we can link to libperl.
1025     # On most platforms, if perl.h is there then libperl.so will be too, but
1026     # at this writing Debian packages them separately.
1027     perl_link_test = '''
1028 /* see plperl.h */
1029 #ifdef _MSC_VER
1030 #define __inline__ inline
1031 #endif
1032 #include <EXTERN.h>
1033 #include <perl.h>
1034 int main(void)
1036 perl_alloc();
1037 }'''
1038     if not cc.links(perl_link_test, name: 'libperl',
1039           args: test_c_args + perl_ccflags + perl_ldopts,
1040           include_directories: postgres_inc)
1041       perl_may_work = false
1042       perl_msg = 'missing libperl'
1043     endif
1045   endif # perl_may_work
1047   if perl_may_work
1048     perl_dep = perl_dep_int
1049   else
1050     if perlopt.enabled()
1051       error('dependency plperl failed: @0@'.format(perl_msg))
1052     else
1053       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1054     endif
1055   endif
1056 endif
1060 ###############################################################
1061 # Library: Python (for plpython)
1062 ###############################################################
1064 pyopt = get_option('plpython')
1065 python3_dep = not_found_dep
1066 if not pyopt.disabled()
1067   pm = import('python')
1068   python3_inst = pm.find_installation(python.path(), required: pyopt)
1069   if python3_inst.found()
1070     python3_dep = python3_inst.dependency(embed: true, required: pyopt)
1071     # Remove this check after we depend on Meson >= 1.1.0
1072     if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt)
1073       python3_dep = not_found_dep
1074     endif
1075   endif
1076 endif
1080 ###############################################################
1081 # Library: Readline
1082 ###############################################################
1084 if not get_option('readline').disabled()
1085   libedit_preferred = get_option('libedit_preferred')
1086   # Set the order of readline dependencies
1087   check_readline_deps = libedit_preferred ? \
1088     ['libedit', 'readline'] : ['readline', 'libedit']
1090   foreach readline_dep : check_readline_deps
1091     readline = dependency(readline_dep, required: false)
1092     if not readline.found()
1093       readline = cc.find_library(readline_dep,
1094         required: get_option('readline'),
1095         dirs: test_lib_d)
1096     endif
1097     if readline.found()
1098       break
1099     endif
1100   endforeach
1102   if readline.found()
1103     cdata.set('HAVE_LIBREADLINE', 1)
1105     editline_prefix = {
1106       'header_prefix': 'editline/',
1107       'flag_prefix': 'EDITLINE_',
1108     }
1109     readline_prefix = {
1110       'header_prefix': 'readline/',
1111       'flag_prefix': 'READLINE_',
1112     }
1113     default_prefix = {
1114       'header_prefix': '',
1115       'flag_prefix': '',
1116     }
1118     # Set the order of prefixes
1119     prefixes = libedit_preferred ? \
1120       [editline_prefix, default_prefix, readline_prefix] : \
1121       [readline_prefix, default_prefix, editline_prefix]
1123     at_least_one_header_found = false
1124     foreach header : ['history', 'readline']
1125       is_found = false
1126       foreach prefix : prefixes
1127         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1128         # Check history.h and readline.h
1129         if not is_found and cc.has_header(header_file,
1130             args: test_c_args, include_directories: postgres_inc,
1131             dependencies: [readline], required: false)
1132           if header == 'readline'
1133             readline_h = header_file
1134           endif
1135           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1136           is_found = true
1137           at_least_one_header_found = true
1138         endif
1139       endforeach
1140     endforeach
1142     if not at_least_one_header_found
1143       error('''readline header not found
1144 If you have @0@ already installed, see meson-logs/meson-log.txt for details on the
1145 failure. It is possible the compiler isn't looking in the proper directory.
1146 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1147     endif
1149     check_funcs = [
1150       'append_history',
1151       'history_truncate_file',
1152       'rl_completion_matches',
1153       'rl_filename_completion_function',
1154       'rl_reset_screen_size',
1155       'rl_variable_bind',
1156     ]
1158     foreach func : check_funcs
1159       found = cc.has_function(func, dependencies: [readline],
1160         args: test_c_args, include_directories: postgres_inc)
1161       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1162     endforeach
1164     check_vars = [
1165       'rl_completion_suppress_quote',
1166       'rl_filename_quote_characters',
1167       'rl_filename_quoting_function',
1168     ]
1170     foreach var : check_vars
1171       cdata.set('HAVE_' + var.to_upper(),
1172         cc.has_header_symbol(readline_h, var,
1173           args: test_c_args, include_directories: postgres_inc,
1174           prefix: '#include <stdio.h>',
1175           dependencies: [readline]) ? 1 : false)
1176     endforeach
1178     # If found via cc.find_library() ensure headers are found when using the
1179     # dependency. On meson < 0.57 one cannot do compiler checks using the
1180     # dependency returned by declare_dependency(), so we can't do this above.
1181     if readline.type_name() == 'library'
1182       readline = declare_dependency(dependencies: readline,
1183         include_directories: postgres_inc)
1184     endif
1186     # On windows with mingw readline requires auto-import to successfully
1187     # link, as the headers don't use declspec(dllimport)
1188     if host_system == 'windows' and cc.get_id() != 'msvc'
1189       readline = declare_dependency(dependencies: readline,
1190         link_args: '-Wl,--enable-auto-import')
1191     endif
1192   endif
1194   # XXX: Figure out whether to implement mingw warning equivalent
1195 else
1196   readline = not_found_dep
1197 endif
1201 ###############################################################
1202 # Library: selinux
1203 ###############################################################
1205 selinux = not_found_dep
1206 selinuxopt = get_option('selinux')
1207 if meson.version().version_compare('>=0.59')
1208   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1209 endif
1210 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1211 cdata.set('HAVE_LIBSELINUX',
1212   selinux.found() ? 1 : false)
1216 ###############################################################
1217 # Library: systemd
1218 ###############################################################
1220 systemd = not_found_dep
1221 systemdopt = get_option('systemd')
1222 if meson.version().version_compare('>=0.59')
1223   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1224 endif
1225 systemd = dependency('libsystemd', required: systemdopt)
1226 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1230 ###############################################################
1231 # Library: SSL
1232 ###############################################################
1234 ssl = not_found_dep
1235 ssl_library = 'none'
1236 sslopt = get_option('ssl')
1238 if sslopt == 'auto' and auto_features.disabled()
1239   sslopt = 'none'
1240 endif
1242 if sslopt in ['auto', 'openssl']
1243   openssl_required = (sslopt == 'openssl')
1245   # Try to find openssl via pkg-config et al, if that doesn't work
1246   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1247   # the library names that we know about.
1249   # via pkg-config et al
1250   ssl = dependency('openssl', required: false)
1251   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1252   # we pass cc.find_library() results if necessary
1253   ssl_int = []
1255   # via library + headers
1256   if not ssl.found()
1257     ssl_lib = cc.find_library('ssl',
1258       dirs: test_lib_d,
1259       header_include_directories: postgres_inc,
1260       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1261       required: openssl_required)
1262     crypto_lib = cc.find_library('crypto',
1263       dirs: test_lib_d,
1264       required: openssl_required)
1265     if ssl_lib.found() and crypto_lib.found()
1266       ssl_int = [ssl_lib, crypto_lib]
1267       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1268     endif
1269   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1270        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1271     ssl_int = [ssl]
1272   else
1273     ssl = not_found_dep
1274   endif
1276   if ssl.found()
1277     check_funcs = [
1278       ['CRYPTO_new_ex_data', {'required': true}],
1279       ['SSL_new', {'required': true}],
1281       # Functions introduced in OpenSSL 1.0.2.
1282       ['X509_get_signature_nid'],
1283       ['SSL_CTX_set_cert_cb'], # not in LibreSSL
1285       # Functions introduced in OpenSSL 1.1.0. We used to check for
1286       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1287       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1288       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1289       # functions.
1290       ['OPENSSL_init_ssl'],
1291       ['BIO_meth_new'],
1292       ['ASN1_STRING_get0_data'],
1293       ['HMAC_CTX_new'],
1294       ['HMAC_CTX_free'],
1296       # OpenSSL versions before 1.1.0 required setting callback functions, for
1297       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1298       # function was removed.
1299       ['CRYPTO_lock'],
1301       # Function introduced in OpenSSL 1.1.1
1302       ['X509_get_signature_info'],
1303     ]
1305     are_openssl_funcs_complete = true
1306     foreach c : check_funcs
1307       func = c.get(0)
1308       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1309       required = c.get(1, {}).get('required', false)
1310       if required and not val
1311         are_openssl_funcs_complete = false
1312         if openssl_required
1313           error('openssl function @0@ is required'.format(func))
1314         endif
1315         break
1316       elif not required
1317         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1318       endif
1319     endforeach
1321     if are_openssl_funcs_complete
1322       cdata.set('USE_OPENSSL', 1,
1323                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1324       cdata.set('OPENSSL_API_COMPAT', '0x10001000L',
1325                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1326       ssl_library = 'openssl'
1327     else
1328       ssl = not_found_dep
1329     endif
1330   endif
1331 endif
1333 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1334   error('no SSL library found')
1335 endif
1339 ###############################################################
1340 # Library: uuid
1341 ###############################################################
1343 uuidopt = get_option('uuid')
1344 if uuidopt != 'none'
1345   uuidname = uuidopt.to_upper()
1346   if uuidopt == 'e2fs'
1347     uuid = dependency('uuid', required: true)
1348     uuidfunc = 'uuid_generate'
1349     uuidheader = 'uuid/uuid.h'
1350   elif uuidopt == 'bsd'
1351     # libc should have uuid function
1352     uuid = declare_dependency()
1353     uuidfunc = 'uuid_to_string'
1354     uuidheader = 'uuid.h'
1355   elif uuidopt == 'ossp'
1356     uuid = dependency('ossp-uuid', required: true)
1357     uuidfunc = 'uuid_export'
1358     uuidheader = 'uuid.h'
1359   else
1360     error('unknown uuid build option value: @0@'.format(uuidopt))
1361   endif
1363   if not cc.has_header_symbol(uuidheader, uuidfunc, args: test_c_args, dependencies: uuid)
1364     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1365   endif
1366   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1368   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1369            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1370 else
1371   uuid = not_found_dep
1372 endif
1376 ###############################################################
1377 # Library: zlib
1378 ###############################################################
1380 zlibopt = get_option('zlib')
1381 zlib = not_found_dep
1382 if not zlibopt.disabled()
1383   zlib_t = dependency('zlib', required: zlibopt)
1385   if zlib_t.type_name() == 'internal'
1386     # if fallback was used, we don't need to test if headers are present (they
1387     # aren't built yet, so we can't test)
1388     zlib = zlib_t
1389   elif not zlib_t.found()
1390     warning('did not find zlib')
1391   elif not cc.has_header('zlib.h',
1392       args: test_c_args, include_directories: postgres_inc,
1393       dependencies: [zlib_t], required: zlibopt)
1394     warning('zlib header not found')
1395   elif not cc.has_type('z_streamp',
1396       dependencies: [zlib_t], prefix: '#include <zlib.h>',
1397       args: test_c_args, include_directories: postgres_inc)
1398     if zlibopt.enabled()
1399       error('zlib version is too old')
1400     else
1401       warning('zlib version is too old')
1402     endif
1403   else
1404     zlib = zlib_t
1405   endif
1407   if zlib.found()
1408     cdata.set('HAVE_LIBZ', 1)
1409   endif
1410 endif
1414 ###############################################################
1415 # Library: tap test dependencies
1416 ###############################################################
1418 # Check whether tap tests are enabled or not
1419 tap_tests_enabled = false
1420 tapopt = get_option('tap_tests')
1421 if not tapopt.disabled()
1422   # Checking for perl modules for tap tests
1423   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1424   if perl_ipc_run_check.returncode() != 0
1425     message(perl_ipc_run_check.stderr().strip())
1426     if tapopt.enabled()
1427       error('Additional Perl modules are required to run TAP tests.')
1428     else
1429       warning('Additional Perl modules are required to run TAP tests.')
1430     endif
1431   else
1432     tap_tests_enabled = true
1433   endif
1434 endif
1438 ###############################################################
1439 # Library: zstd
1440 ###############################################################
1442 zstdopt = get_option('zstd')
1443 if not zstdopt.disabled()
1444   zstd = dependency('libzstd', required: zstdopt, version: '>=1.4.0')
1446   if zstd.found()
1447     cdata.set('USE_ZSTD', 1)
1448     cdata.set('HAVE_LIBZSTD', 1)
1449   endif
1451 else
1452   zstd = not_found_dep
1453 endif
1457 ###############################################################
1458 # Compiler tests
1459 ###############################################################
1461 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1462 # unnecessarily, because we optionally rely on newer features.
1463 c99_test = '''
1464 #include <stdbool.h>
1465 #include <complex.h>
1466 #include <tgmath.h>
1467 #include <inttypes.h>
1469 struct named_init_test {
1470   int a;
1471   int b;
1474 extern void structfunc(struct named_init_test);
1476 int main(int argc, char **argv)
1478   struct named_init_test nit = {
1479     .a = 3,
1480     .b = 5,
1481   };
1483   for (int loop_var = 0; loop_var < 3; loop_var++)
1484   {
1485     nit.a += nit.b;
1486   }
1488   structfunc((struct named_init_test){1, 0});
1490   return nit.a != 0;
1494 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1495   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1496         args: test_c_args + ['-std=c99'])
1497     test_c_args += '-std=c99'
1498     cflags += '-std=c99'
1499   else
1500     error('C compiler does not support C99')
1501   endif
1502 endif
1504 sizeof_long = cc.sizeof('long', args: test_c_args)
1505 cdata.set('SIZEOF_LONG', sizeof_long)
1506 if sizeof_long == 8
1507   cdata.set('HAVE_LONG_INT_64', 1)
1508   cdata.set('PG_INT64_TYPE', 'long int')
1509   cdata.set_quoted('INT64_MODIFIER', 'l')
1510 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1511   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1512   cdata.set('PG_INT64_TYPE', 'long long int')
1513   cdata.set_quoted('INT64_MODIFIER', 'll')
1514 else
1515   error('do not know how to get a 64bit int')
1516 endif
1518 if host_machine.endian() == 'big'
1519   cdata.set('WORDS_BIGENDIAN', 1)
1520 endif
1522 alignof_types = ['short', 'int', 'long', 'double']
1523 maxalign = 0
1524 foreach t : alignof_types
1525   align = cc.alignment(t, args: test_c_args)
1526   if maxalign < align
1527     maxalign = align
1528   endif
1529   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1530 endforeach
1531 cdata.set('MAXIMUM_ALIGNOF', maxalign)
1533 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1534 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1537 # Check if __int128 is a working 128 bit integer type, and if so
1538 # define PG_INT128_TYPE to that typename.
1540 # This currently only detects a GCC/clang extension, but support for other
1541 # environments may be added in the future.
1543 # For the moment we only test for support for 128bit math; support for
1544 # 128bit literals and snprintf is not required.
1545 if cc.links('''
1546   /*
1547    * We don't actually run this test, just link it to verify that any support
1548    * functions needed for __int128 are present.
1549    *
1550    * These are globals to discourage the compiler from folding all the
1551    * arithmetic tests down to compile-time constants.  We do not have
1552    * convenient support for 128bit literals at this point...
1553    */
1554   __int128 a = 48828125;
1555   __int128 b = 97656250;
1557   int main(void)
1558   {
1559       __int128 c,d;
1560       a = (a << 12) + 1; /* 200000000001 */
1561       b = (b << 12) + 5; /* 400000000005 */
1562       /* try the most relevant arithmetic ops */
1563       c = a * b;
1564       d = (c + b) / b;
1565       /* must use the results, else compiler may optimize arithmetic away */
1566       return d != a+1;
1567   }''',
1568   name: '__int128',
1569   args: test_c_args)
1571   buggy_int128 = false
1573   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1574   # If not cross-compiling, we can test for bugs and disable use of __int128
1575   # with buggy compilers.  If cross-compiling, hope for the best.
1576   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1577   if not meson.is_cross_build()
1578     r = cc.run('''
1579     /* This must match the corresponding code in c.h: */
1580     #if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
1581     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1582     #elif defined(_MSC_VER)
1583     #define pg_attribute_aligned(a) __declspec(align(a))
1584     #endif
1585     typedef __int128 int128a
1586     #if defined(pg_attribute_aligned)
1587     pg_attribute_aligned(8)
1588     #endif
1589     ;
1591     int128a holder;
1592     void pass_by_val(void *buffer, int128a par) { holder = par; }
1594     int main(void)
1595     {
1596         long int i64 = 97656225L << 12;
1597         int128a q;
1598         pass_by_val(main, (int128a) i64);
1599         q = (int128a) i64;
1600         return q != holder;
1601     }''',
1602     name: '__int128 alignment bug',
1603     args: test_c_args)
1604     assert(r.compiled())
1605     if r.returncode() != 0
1606       buggy_int128 = true
1607       message('__int128 support present but buggy and thus disabled')
1608     endif
1609   endif
1611   if not buggy_int128
1612     cdata.set('PG_INT128_TYPE', '__int128')
1613     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1614   endif
1615 endif
1618 # Check if the C compiler knows computed gotos (gcc extension, also
1619 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1621 # Checking whether computed gotos are supported syntax-wise ought to
1622 # be enough, as the syntax is otherwise illegal.
1623 if cc.compiles('''
1624     static inline int foo(void)
1625     {
1626       void *labeladdrs[] = {&&my_label};
1627       goto *labeladdrs[0];
1628       my_label:
1629       return 1;
1630     }''',
1631     args: test_c_args)
1632   cdata.set('HAVE_COMPUTED_GOTO', 1)
1633 endif
1636 # Check if the C compiler understands _Static_assert(),
1637 # and define HAVE__STATIC_ASSERT if so.
1639 # We actually check the syntax ({ _Static_assert(...) }), because we need
1640 # gcc-style compound expressions to be able to wrap the thing into macros.
1641 if cc.compiles('''
1642     int main(int arg, char **argv)
1643     {
1644         ({ _Static_assert(1, "foo"); });
1645     }
1646     ''',
1647     args: test_c_args)
1648   cdata.set('HAVE__STATIC_ASSERT', 1)
1649 endif
1652 # We use <stdbool.h> if we have it and it declares type bool as having
1653 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1654 if cc.has_type('_Bool', args: test_c_args) \
1655     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1656     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1657   cdata.set('HAVE__BOOL', 1)
1658   cdata.set('PG_USE_STDBOOL', 1)
1659 endif
1662 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1663 # warning for each use of %m.
1664 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1665 testsrc = '''
1666 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1667 static void call_log(void)
1669     emit_log(0, "error: %s: %m", "foo");
1672 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1673 foreach a : printf_attributes
1674   if cc.compiles(testsrc.format(a),
1675       args: test_c_args + attrib_error_args, name: 'format ' + a)
1676     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1677     break
1678   endif
1679 endforeach
1682 if cc.has_function_attribute('visibility:default') and \
1683     cc.has_function_attribute('visibility:hidden')
1684   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1686   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1687   # inlineshidden to C code as well... And either way, we want to put these
1688   # flags into exported files (pgxs, .pc files).
1689   cflags_mod += '-fvisibility=hidden'
1690   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1691   ldflags_mod += '-fvisibility=hidden'
1692 endif
1695 # Check if various builtins exist. Some builtins are tested separately,
1696 # because we want to test something more complicated than the generic case.
1697 builtins = [
1698   'bswap16',
1699   'bswap32',
1700   'bswap64',
1701   'clz',
1702   'ctz',
1703   'constant_p',
1704   'frame_address',
1705   'popcount',
1706   'unreachable',
1709 foreach builtin : builtins
1710   fname = '__builtin_@0@'.format(builtin)
1711   if cc.has_function(fname, args: test_c_args)
1712     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1713   endif
1714 endforeach
1717 # Check if the C compiler understands __builtin_types_compatible_p,
1718 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1720 # We check usage with __typeof__, though it's unlikely any compiler would
1721 # have the former and not the latter.
1722 if cc.compiles('''
1723     static int x;
1724     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1725     ''',
1726     name: '__builtin_types_compatible_p',
1727     args: test_c_args)
1728   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1729 endif
1732 # Check if the C compiler understands __builtin_$op_overflow(),
1733 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1735 # Check for the most complicated case, 64 bit multiplication, as a
1736 # proxy for all of the operations.  To detect the case where the compiler
1737 # knows the function but library support is missing, we must link not just
1738 # compile, and store the results in global variables so the compiler doesn't
1739 # optimize away the call.
1740 if cc.links('''
1741     INT64 a = 1;
1742     INT64 b = 1;
1743     INT64 result;
1745     int main(void)
1746     {
1747         return __builtin_mul_overflow(a, b, &result);
1748     }''',
1749     name: '__builtin_mul_overflow',
1750     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1751     )
1752   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1753 endif
1756 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1757 # here. To prevent problems due to two detection methods working, stop
1758 # checking after one.
1759 if cc.links('''
1760     #include <cpuid.h>
1761     int main(int arg, char **argv)
1762     {
1763         unsigned int exx[4] = {0, 0, 0, 0};
1764         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1765     }
1766     ''', name: '__get_cpuid',
1767     args: test_c_args)
1768   cdata.set('HAVE__GET_CPUID', 1)
1769 elif cc.links('''
1770     #include <intrin.h>
1771     int main(int arg, char **argv)
1772     {
1773         unsigned int exx[4] = {0, 0, 0, 0};
1774         __cpuid(exx, 1);
1775     }
1776     ''', name: '__cpuid',
1777     args: test_c_args)
1778   cdata.set('HAVE__CPUID', 1)
1779 endif
1782 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1783 # versions of clang do not understand -fexcess-precision=standard, the use of
1784 # x87 floating point operations leads to problems like isinf possibly returning
1785 # false for a value that is infinite when converted from the 80bit register to
1786 # the 8byte memory representation.
1788 # Only perform the test if the compiler doesn't understand
1789 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1790 # automatically.
1791 if '-fexcess-precision=standard' not in cflags
1792   if not cc.compiles('''
1793 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1794 choke me
1795 #endif''',
1796       name: '', args: test_c_args)
1797     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1798   endif
1799 endif
1803 ###############################################################
1804 # Compiler flags
1805 ###############################################################
1807 common_functional_flags = [
1808   # Disable strict-aliasing rules; needed for gcc 3.3+
1809   '-fno-strict-aliasing',
1810   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1811   '-fwrapv',
1812   '-fexcess-precision=standard',
1815 cflags += cc.get_supported_arguments(common_functional_flags)
1816 if llvm.found()
1817   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1818 endif
1820 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1821 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1823 common_warning_flags = [
1824   '-Wmissing-prototypes',
1825   '-Wpointer-arith',
1826   # Really don't want VLAs to be used in our dialect of C
1827   '-Werror=vla',
1828   # On macOS, complain about usage of symbols newer than the deployment target
1829   '-Werror=unguarded-availability-new',
1830   '-Wendif-labels',
1831   '-Wmissing-format-attribute',
1832   '-Wimplicit-fallthrough=3',
1833   '-Wcast-function-type',
1834   '-Wshadow=compatible-local',
1835   # This was included in -Wall/-Wformat in older GCC versions
1836   '-Wformat-security',
1839 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1840 if llvm.found()
1841   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1842 endif
1844 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1845 # the result for them
1846 cflags_no_decl_after_statement = []
1847 if cc.has_argument('-Wdeclaration-after-statement')
1848   cflags_warn += '-Wdeclaration-after-statement'
1849   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1850 endif
1853 # The following tests want to suppress various unhelpful warnings by adding
1854 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1855 # switches, so we have to test for the positive form and if that works,
1856 # add the negative form.
1858 negative_warning_flags = [
1859   # Suppress clang's unhelpful unused-command-line-argument warnings.
1860   'unused-command-line-argument',
1862   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
1863   # of warnings when building plperl because of usages in the Perl headers.
1864   'compound-token-split-by-macro',
1866   # Similarly disable useless truncation warnings from gcc 8+
1867   'format-truncation',
1868   'stringop-truncation',
1870   # Suppress clang 16's strict warnings about function casts
1871   'cast-function-type-strict',
1873   # To make warning_level=2 / -Wextra work, we'd need at least the following
1874   # 'clobbered',
1875   # 'missing-field-initializers',
1876   # 'sign-compare',
1877   # 'unused-parameter',
1880 foreach w : negative_warning_flags
1881   if cc.has_argument('-W' + w)
1882     cflags_warn += '-Wno-' + w
1883   endif
1884   if llvm.found() and cpp.has_argument('-W' + w)
1885     cxxflags_warn += '-Wno-' + w
1886   endif
1887 endforeach
1890 # From Project.pm
1891 if cc.get_id() == 'msvc'
1892   cflags_warn += [
1893     '/wd4018', # signed/unsigned mismatch
1894     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
1895     '/wd4273', # inconsistent DLL linkage
1896     '/wd4101', # unreferenced local variable
1897     '/wd4102', # unreferenced label
1898     '/wd4090', # different 'modifier' qualifiers
1899     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
1900   ]
1902   cppflags += [
1903     '/DWIN32',
1904     '/DWINDOWS',
1905     '/D__WINDOWS__',
1906     '/D__WIN32__',
1907     '/D_CRT_SECURE_NO_DEPRECATE',
1908     '/D_CRT_NONSTDC_NO_DEPRECATE',
1909   ]
1911   # We never need export libraries. As link.exe reports their creation, they
1912   # are unnecessarily noisy. Similarly, we don't need import library for
1913   # modules, we only import them dynamically, and they're also noisy.
1914   ldflags += '/NOEXP'
1915   ldflags_mod += '/NOIMPLIB'
1916 endif
1920 ###############################################################
1921 # Atomics
1922 ###############################################################
1924 if not get_option('spinlocks')
1925   warning('Not using spinlocks will cause poor performance')
1926 else
1927   cdata.set('HAVE_SPINLOCKS', 1)
1928 endif
1930 if not get_option('atomics')
1931   warning('Not using atomics will cause poor performance')
1932 else
1933   # XXX: perhaps we should require some atomics support in this case these
1934   # days?
1935   cdata.set('HAVE_ATOMICS', 1)
1937   atomic_checks = [
1938     {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
1939      'desc': '__sync_lock_test_and_set(char)',
1940      'test': '''
1941 char lock = 0;
1942 __sync_lock_test_and_set(&lock, 1);
1943 __sync_lock_release(&lock);'''},
1945     {'name': 'HAVE_GCC__SYNC_INT32_TAS',
1946      'desc': '__sync_lock_test_and_set(int32)',
1947      'test': '''
1948 int lock = 0;
1949 __sync_lock_test_and_set(&lock, 1);
1950 __sync_lock_release(&lock);'''},
1952     {'name': 'HAVE_GCC__SYNC_INT32_CAS',
1953      'desc': '__sync_val_compare_and_swap(int32)',
1954      'test': '''
1955 int val = 0;
1956 __sync_val_compare_and_swap(&val, 0, 37);'''},
1958     {'name': 'HAVE_GCC__SYNC_INT64_CAS',
1959      'desc': '__sync_val_compare_and_swap(int64)',
1960      'test': '''
1961 INT64 val = 0;
1962 __sync_val_compare_and_swap(&val, 0, 37);'''},
1964     {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
1965      'desc': ' __atomic_compare_exchange_n(int32)',
1966      'test': '''
1967 int val = 0;
1968 int expect = 0;
1969 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1971     {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
1972      'desc': ' __atomic_compare_exchange_n(int64)',
1973      'test': '''
1974 INT64 val = 0;
1975 INT64 expect = 0;
1976 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
1977   ]
1979   foreach check : atomic_checks
1980     test = '''
1981 int main(void)
1984 }'''.format(check['test'])
1986     cdata.set(check['name'],
1987       cc.links(test,
1988         name: check['desc'],
1989         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
1990     )
1991   endforeach
1993 endif
1997 ###############################################################
1998 # Select CRC-32C implementation.
2000 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
2001 # use the special CRC instructions for calculating CRC-32C. If we're not
2002 # targeting such a processor, but we can nevertheless produce code that uses
2003 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
2004 # implementations and select which one to use at runtime, depending on whether
2005 # SSE 4.2 is supported by the processor we're running on.
2007 # Similarly, if we are targeting an ARM processor that has the CRC
2008 # instructions that are part of the ARMv8 CRC Extension, use them. And if
2009 # we're not targeting such a processor, but can nevertheless produce code that
2010 # uses the CRC instructions, compile both, and select at runtime.
2011 ###############################################################
2013 have_optimized_crc = false
2014 cflags_crc = []
2015 if host_cpu == 'x86' or host_cpu == 'x86_64'
2017   if cc.get_id() == 'msvc'
2018     cdata.set('USE_SSE42_CRC32C', false)
2019     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2020     have_optimized_crc = true
2021   else
2023     prog = '''
2024 #include <nmmintrin.h>
2026 int main(void)
2028     unsigned int crc = 0;
2029     crc = _mm_crc32_u8(crc, 0);
2030     crc = _mm_crc32_u32(crc, 0);
2031     /* return computed value, to prevent the above being optimized away */
2032     return crc == 0;
2036     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2037           args: test_c_args)
2038       # Use Intel SSE 4.2 unconditionally.
2039       cdata.set('USE_SSE42_CRC32C', 1)
2040       have_optimized_crc = true
2041     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2042           args: test_c_args + ['-msse4.2'])
2043       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2044       # the runtime check.
2045       cflags_crc += '-msse4.2'
2046       cdata.set('USE_SSE42_CRC32C', false)
2047       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2048       have_optimized_crc = true
2049     endif
2051   endif
2053 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2055   prog = '''
2056 #include <arm_acle.h>
2058 int main(void)
2060     unsigned int crc = 0;
2061     crc = __crc32cb(crc, 0);
2062     crc = __crc32ch(crc, 0);
2063     crc = __crc32cw(crc, 0);
2064     crc = __crc32cd(crc, 0);
2066     /* return computed value, to prevent the above being optimized away */
2067     return crc == 0;
2071   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2072       args: test_c_args)
2073     # Use ARM CRC Extension unconditionally
2074     cdata.set('USE_ARMV8_CRC32C', 1)
2075     have_optimized_crc = true
2076   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2077       args: test_c_args + ['-march=armv8-a+crc'])
2078     # Use ARM CRC Extension, with runtime check
2079     cflags_crc += '-march=armv8-a+crc'
2080     cdata.set('USE_ARMV8_CRC32C', false)
2081     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2082     have_optimized_crc = true
2083   endif
2084 endif
2086 if not have_optimized_crc
2087   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2088   # support.
2089   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2090 endif
2094 ###############################################################
2095 # Other CPU specific stuff
2096 ###############################################################
2098 if host_cpu == 'x86_64'
2100   if cc.compiles('''
2101       void main(void)
2102       {
2103           long long x = 1; long long r;
2104           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2105       }''',
2106       name: '@0@: popcntq instruction'.format(host_cpu),
2107       args: test_c_args)
2108     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2109   endif
2111 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2112   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2113   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2114     if cc.compiles('''
2115       static inline int
2116       addi(int ra, int si)
2117       {
2118           int res = 0;
2119           if (__builtin_constant_p(si))
2120               __asm__ __volatile__(
2121                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2122           return res;
2123       }
2124       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2125       ''',
2126       args: test_c_args)
2127       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2128     endif
2129   endif
2130 endif
2134 ###############################################################
2135 # Library / OS tests
2136 ###############################################################
2138 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2139 # unnecessary checks over and over, particularly on windows.
2140 header_checks = [
2141   'atomic.h',
2142   'copyfile.h',
2143   'crtdefs.h',
2144   'execinfo.h',
2145   'getopt.h',
2146   'ifaddrs.h',
2147   'langinfo.h',
2148   'mbarrier.h',
2149   'stdbool.h',
2150   'strings.h',
2151   'sys/epoll.h',
2152   'sys/event.h',
2153   'sys/personality.h',
2154   'sys/prctl.h',
2155   'sys/procctl.h',
2156   'sys/signalfd.h',
2157   'sys/ucred.h',
2158   'termios.h',
2159   'ucred.h',
2162 foreach header : header_checks
2163   varname = 'HAVE_' + header.underscorify().to_upper()
2165   # Emulate autoconf behaviour of not-found->undef, found->1
2166   found = cc.has_header(header,
2167     include_directories: postgres_inc, args: test_c_args)
2168   cdata.set(varname, found ? 1 : false,
2169             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2170 endforeach
2173 decl_checks = [
2174   ['F_FULLFSYNC', 'fcntl.h'],
2175   ['fdatasync', 'unistd.h'],
2176   ['posix_fadvise', 'fcntl.h'],
2177   ['strlcat', 'string.h'],
2178   ['strlcpy', 'string.h'],
2179   ['strnlen', 'string.h'],
2182 # Need to check for function declarations for these functions, because
2183 # checking for library symbols wouldn't handle deployment target
2184 # restrictions on macOS
2185 decl_checks += [
2186   ['preadv', 'sys/uio.h'],
2187   ['pwritev', 'sys/uio.h'],
2190 foreach c : decl_checks
2191   func = c.get(0)
2192   header = c.get(1)
2193   args = c.get(2, {})
2194   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2196   found = cc.has_header_symbol(header, func,
2197     args: test_c_args, include_directories: postgres_inc,
2198     kwargs: args)
2199   cdata.set10(varname, found, description:
2200 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2201    don't.'''.format(func))
2202 endforeach
2205 if cc.has_type('struct option',
2206     args: test_c_args, include_directories: postgres_inc,
2207     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2208   cdata.set('HAVE_STRUCT_OPTION', 1)
2209 endif
2212 foreach c : ['opterr', 'optreset']
2213   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2215   if cc.links('''
2216 #include <unistd.h>
2217 int main(void)
2219     extern int @0@;
2220     @0@ = 1;
2222 '''.format(c), name: c, args: test_c_args)
2223     cdata.set(varname, 1)
2224   else
2225     cdata.set(varname, false)
2226   endif
2227 endforeach
2229 if cc.has_type('socklen_t',
2230     args: test_c_args, include_directories: postgres_inc,
2231     prefix: '''
2232 #include <sys/socket.h>''')
2233   cdata.set('HAVE_SOCKLEN_T', 1)
2234 endif
2236 if cc.has_member('struct sockaddr', 'sa_len',
2237     args: test_c_args, include_directories: postgres_inc,
2238     prefix: '''
2239 #include <sys/types.h>
2240 #include <sys/socket.h>''')
2241   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2242 endif
2244 if cc.has_member('struct tm', 'tm_zone',
2245     args: test_c_args, include_directories: postgres_inc,
2246     prefix: '''
2247 #include <sys/types.h>
2248 #include <time.h>
2249 ''')
2250   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2251 endif
2253 if cc.compiles('''
2254 #include <time.h>
2255 extern int foo(void);
2256 int foo(void)
2258     return timezone / 60;
2260 ''',
2261     name: 'global variable `timezone\' exists',
2262     args: test_c_args, include_directories: postgres_inc)
2263   cdata.set('HAVE_INT_TIMEZONE', 1)
2264 else
2265   cdata.set('HAVE_INT_TIMEZONE', false)
2266 endif
2268 if cc.has_type('union semun',
2269     args: test_c_args,
2270     include_directories: postgres_inc,
2271     prefix: '''
2272 #include <sys/types.h>
2273 #include <sys/ipc.h>
2274 #include <sys/sem.h>
2275 ''')
2276   cdata.set('HAVE_UNION_SEMUN', 1)
2277 endif
2279 if cc.compiles('''
2280 #include <string.h>
2281 int main(void)
2283   char buf[100];
2284   switch (strerror_r(1, buf, sizeof(buf)))
2285   { case 0: break; default: break; }
2286 }''',
2287     name: 'strerror_r',
2288     args: test_c_args, include_directories: postgres_inc)
2289   cdata.set('STRERROR_R_INT', 1)
2290 else
2291   cdata.set('STRERROR_R_INT', false)
2292 endif
2294 # Check for the locale_t type and find the right header file.  macOS
2295 # needs xlocale.h; standard is locale.h, but glibc also has an
2296 # xlocale.h file that we should not use.  MSVC has a replacement
2297 # defined in src/include/port/win32_port.h.
2298 if cc.has_type('locale_t', prefix: '#include <locale.h>')
2299   cdata.set('HAVE_LOCALE_T', 1)
2300 elif cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2301   cdata.set('HAVE_LOCALE_T', 1)
2302   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2303 elif cc.get_id() == 'msvc'
2304   cdata.set('HAVE_LOCALE_T', 1)
2305 endif
2307 # Check if the C compiler understands typeof or a variant.  Define
2308 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2309 foreach kw : ['typeof', '__typeof__', 'decltype']
2310   if cc.compiles('''
2311 int main(void)
2313     int x = 0;
2314     @0@(x) y;
2315     y = x;
2316     return y;
2318 '''.format(kw),
2319     name: 'typeof()',
2320     args: test_c_args, include_directories: postgres_inc)
2322     cdata.set('HAVE_TYPEOF', 1)
2323     if kw != 'typeof'
2324       cdata.set('typeof', kw)
2325     endif
2327     break
2328   endif
2329 endforeach
2332 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2333 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2334 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2335 wcstombs_l_test = '''
2336 #include <stdlib.h>
2337 #include <locale.h>
2340 void main(void)
2342 #ifndef wcstombs_l
2343     (void) wcstombs_l;
2344 #endif
2347 if (not cc.compiles(wcstombs_l_test.format(''),
2348       name: 'wcstombs_l') and
2349     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2350       name: 'wcstombs_l in xlocale.h'))
2351     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2352 endif
2355 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2356 # understands, because it conflicts with __declspec(restrict). Therefore we
2357 # define pg_restrict to the appropriate definition, which presumably won't
2358 # conflict.
2360 # We assume C99 support, so we don't need to make this conditional.
2362 # XXX: Historically we allowed platforms to disable restrict in template
2363 # files, but that was only added for AIX when building with XLC, which we
2364 # don't support yet.
2365 cdata.set('pg_restrict', '__restrict')
2368 # Most libraries are included only if they demonstrably provide a function we
2369 # need, but libm is an exception: always include it, because there are too
2370 # many compilers that play cute optimization games that will break probes for
2371 # standard functions such as pow().
2372 os_deps += cc.find_library('m', required: false)
2374 rt_dep = cc.find_library('rt', required: false)
2376 dl_dep = cc.find_library('dl', required: false)
2378 util_dep = cc.find_library('util', required: false)
2379 posix4_dep = cc.find_library('posix4', required: false)
2381 getopt_dep = cc.find_library('getopt', required: false)
2382 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2383 # Check if we want to replace getopt/getopt_long even if provided by the system
2384 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2385 #   so always use our version on Windows
2386 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2387 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2388 # - We want to use system's getopt_long() only if the system provides struct
2389 #   option
2390 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2391 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2393 # Required on BSDs
2394 execinfo_dep = cc.find_library('execinfo', required: false)
2396 if host_system == 'cygwin'
2397   cygipc_dep = cc.find_library('cygipc', required: false)
2398 else
2399   cygipc_dep = not_found_dep
2400 endif
2402 if host_system == 'sunos'
2403   socket_dep = cc.find_library('socket', required: false)
2404 else
2405   socket_dep = not_found_dep
2406 endif
2408 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2409 # unnecessary checks over and over, particularly on windows.
2410 func_checks = [
2411   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2412   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2413   ['clock_gettime', {'dependencies': [rt_dep, posix4_dep], 'define': false}],
2414   ['copyfile'],
2415   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2416   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2417   # required. Just checking for dlsym() ought to suffice.
2418   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2419   ['explicit_bzero'],
2420   ['fdatasync', {'dependencies': [rt_dep, posix4_dep], 'define': false}], # Solaris
2421   ['getifaddrs'],
2422   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2423   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2424   ['getpeereid'],
2425   ['getpeerucred'],
2426   ['inet_aton'],
2427   ['inet_pton'],
2428   ['kqueue'],
2429   ['mbstowcs_l'],
2430   ['memset_s'],
2431   ['mkdtemp'],
2432   ['posix_fadvise'],
2433   ['posix_fallocate'],
2434   ['ppoll'],
2435   ['pstat'],
2436   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2437   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2438   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2439   ['setproctitle', {'dependencies': [util_dep]}],
2440   ['setproctitle_fast'],
2441   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2442   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2443   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2444   ['socket', {'dependencies': [socket_dep], 'define': false}],
2445   ['strchrnul'],
2446   ['strerror_r', {'dependencies': [thread_dep]}],
2447   ['strlcat'],
2448   ['strlcpy'],
2449   ['strnlen'],
2450   ['strsignal'],
2451   ['sync_file_range'],
2452   ['syncfs'],
2453   ['uselocale'],
2454   ['wcstombs_l'],
2457 func_check_results = {}
2458 foreach c : func_checks
2459   func = c.get(0)
2460   kwargs = c.get(1, {})
2461   deps = kwargs.get('dependencies', [])
2463   if kwargs.get('skip', false)
2464     continue
2465   endif
2467   found = cc.has_function(func, args: test_c_args)
2469   if not found
2470     foreach dep : deps
2471       if not dep.found()
2472         continue
2473       endif
2474       found = cc.has_function(func, args: test_c_args,
2475                               dependencies: [dep])
2476       if found
2477         os_deps += dep
2478         break
2479       endif
2480     endforeach
2481   endif
2483   func_check_results += {func: found}
2485   if kwargs.get('define', true)
2486     # Emulate autoconf behaviour of not-found->undef, found->1
2487     cdata.set('HAVE_' + func.underscorify().to_upper(),
2488               found  ? 1 : false,
2489               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2490   endif
2491 endforeach
2494 if cc.has_function('syslog', args: test_c_args) and \
2495     cc.check_header('syslog.h', args: test_c_args)
2496   cdata.set('HAVE_SYSLOG', 1)
2497 endif
2500 # MSVC has replacements defined in src/include/port/win32_port.h.
2501 if cc.get_id() == 'msvc'
2502   cdata.set('HAVE_WCSTOMBS_L', 1)
2503   cdata.set('HAVE_MBSTOWCS_L', 1)
2504 endif
2507 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2508 # semaphores
2509 if sema_kind == 'unnamed_posix' and \
2510    not func_check_results.get('sem_init', false)
2511   sema_kind = 'sysv'
2512 endif
2514 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2515 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2517 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2518 cdata.set_quoted('DLSUFFIX', dlsuffix)
2521 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2522 cdata.set_quoted('PG_VERSION_STR',
2523   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2524     pg_version, host_machine.cpu_family(), host_system,
2525     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2526   )
2531 ###############################################################
2532 # Threading
2533 ###############################################################
2535 # XXX: About to rely on thread safety in the autoconf build, so not worth
2536 # implementing a fallback.
2537 cdata.set('ENABLE_THREAD_SAFETY', 1)
2541 ###############################################################
2542 # NLS / Gettext
2543 ###############################################################
2545 nlsopt = get_option('nls')
2546 libintl = not_found_dep
2548 if not nlsopt.disabled()
2549   # otherwise there'd be lots of
2550   # "Gettext not found, all translation (po) targets will be ignored."
2551   # warnings if not found.
2552   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2554   # meson 0.59 has this wrapped in dependency('intl')
2555   if (msgfmt.found() and
2556       cc.check_header('libintl.h', required: nlsopt,
2557         args: test_c_args, include_directories: postgres_inc))
2559     # in libc
2560     if cc.has_function('ngettext')
2561       libintl = declare_dependency()
2562     else
2563       libintl = cc.find_library('intl',
2564         has_headers: ['libintl.h'], required: nlsopt,
2565         header_include_directories: postgres_inc,
2566         dirs: test_lib_d)
2567     endif
2568   endif
2570   if libintl.found()
2571     i18n = import('i18n')
2572     cdata.set('ENABLE_NLS', 1)
2573   endif
2574 endif
2578 ###############################################################
2579 # Build
2580 ###############################################################
2582 # Set up compiler / linker arguments to be used everywhere, individual targets
2583 # can add further args directly, or indirectly via dependencies
2584 add_project_arguments(cflags, language: ['c'])
2585 add_project_arguments(cppflags, language: ['c'])
2586 add_project_arguments(cflags_warn, language: ['c'])
2587 add_project_arguments(cxxflags, language: ['cpp'])
2588 add_project_arguments(cppflags, language: ['cpp'])
2589 add_project_arguments(cxxflags_warn, language: ['cpp'])
2590 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2593 # Collect a number of lists of things while recursing through the source
2594 # tree. Later steps then can use those.
2596 # list of targets for various alias targets
2597 backend_targets = []
2598 bin_targets = []
2599 pl_targets = []
2600 contrib_targets = []
2601 testprep_targets = []
2602 nls_targets = []
2605 # Define the tests to distribute them to the correct test styles later
2606 test_deps = []
2607 tests = []
2610 # Default options for targets
2612 # First identify rpaths
2613 bin_install_rpaths = []
2614 lib_install_rpaths = []
2615 mod_install_rpaths = []
2618 # Don't add rpaths on darwin for now - as long as only absolute references to
2619 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2620 # their final destination.
2621 if host_system != 'darwin'
2622   # Add absolute path to libdir to rpath. This ensures installed binaries /
2623   # libraries find our libraries (mainly libpq).
2624   bin_install_rpaths += dir_prefix / dir_lib
2625   lib_install_rpaths += dir_prefix / dir_lib
2626   mod_install_rpaths += dir_prefix / dir_lib
2628   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2629   #
2630   # Not needed on darwin even if we use relative rpaths for our own libraries,
2631   # as the install_name of libraries in extra_lib_dirs will point to their
2632   # location anyway.
2633   bin_install_rpaths += postgres_lib_d
2634   lib_install_rpaths += postgres_lib_d
2635   mod_install_rpaths += postgres_lib_d
2636 endif
2639 # Define arguments for default targets
2641 default_target_args = {
2642   'implicit_include_directories': false,
2643   'install': true,
2646 default_lib_args = default_target_args + {
2647   'name_prefix': '',
2650 internal_lib_args = default_lib_args + {
2651   'build_by_default': false,
2652   'install': false,
2655 default_mod_args = default_lib_args + {
2656   'name_prefix': '',
2657   'install_dir': dir_lib_pkg,
2660 default_bin_args = default_target_args + {
2661   'install_dir': dir_bin,
2664 if get_option('rpath')
2665   default_lib_args += {
2666     'install_rpath': ':'.join(lib_install_rpaths),
2667   }
2669   default_mod_args += {
2670     'install_rpath': ':'.join(mod_install_rpaths),
2671   }
2673   default_bin_args += {
2674     'install_rpath': ':'.join(bin_install_rpaths),
2675   }
2676 endif
2679 # Helper for exporting a limited number of symbols
2680 gen_export_kwargs = {
2681   'input': 'exports.txt',
2682   'output': '@BASENAME@.'+export_file_suffix,
2683   'command': [perl, files('src/tools/gen_export.pl'),
2684    '--format', export_file_format,
2685    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2686   'build_by_default': false,
2687   'install': false,
2693 ### Helpers for custom targets used across the tree
2696 catalog_pm = files('src/backend/catalog/Catalog.pm')
2697 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2698 gen_kwlist_deps = [perfect_hash_pm]
2699 gen_kwlist_cmd = [
2700   perl, '-I', '@SOURCE_ROOT@/src/tools',
2701   files('src/tools/gen_keywordlist.pl'),
2702   '--output', '@OUTDIR@', '@INPUT@']
2707 ### windows resources related stuff
2710 if host_system == 'windows'
2711   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2712   win32ver_rc = files('src/port/win32ver.rc')
2713   rcgen = find_program('src/tools/rcgen', native: true)
2715   rcgen_base_args = [
2716     '--srcdir', '@SOURCE_DIR@',
2717     '--builddir', meson.build_root(),
2718     '--rcout', '@OUTPUT0@',
2719     '--out', '@OUTPUT1@',
2720     '--input', '@INPUT@',
2721     '@EXTRA_ARGS@',
2722   ]
2724   if cc.get_argument_syntax() == 'msvc'
2725     rc = find_program('rc', required: true)
2726     rcgen_base_args += ['--rc', rc.path()]
2727     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2728   else
2729     windres = find_program('windres', required: true)
2730     rcgen_base_args += ['--windres', windres.path()]
2731     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2732   endif
2734   # msbuild backend doesn't support this atm
2735   if meson.backend() == 'ninja'
2736     rcgen_base_args += ['--depfile', '@DEPFILE@']
2737   endif
2739   rcgen_bin_args = rcgen_base_args + [
2740     '--VFT_TYPE', 'VFT_APP',
2741     '--FILEENDING', 'exe',
2742     '--ICO', pg_ico
2743   ]
2745   rcgen_lib_args = rcgen_base_args + [
2746     '--VFT_TYPE', 'VFT_DLL',
2747     '--FILEENDING', 'dll',
2748   ]
2750   rc_bin_gen = generator(rcgen,
2751     depfile: '@BASENAME@.d',
2752     arguments: rcgen_bin_args,
2753     output: rcgen_outputs,
2754   )
2756   rc_lib_gen = generator(rcgen,
2757     depfile: '@BASENAME@.d',
2758     arguments: rcgen_lib_args,
2759     output: rcgen_outputs,
2760   )
2761 endif
2765 # headers that the whole build tree depends on
2766 generated_headers = []
2767 # headers that the backend build depends on
2768 generated_backend_headers = []
2769 # configure_files() output, needs a way of converting to file names
2770 configure_files = []
2772 # generated files that might conflict with a partial in-tree autoconf build
2773 generated_sources = []
2774 # same, for paths that differ between autoconf / meson builds
2775 # elements are [dir, [files]]
2776 generated_sources_ac = {}
2779 # First visit src/include - all targets creating headers are defined
2780 # within. That makes it easy to add the necessary dependencies for the
2781 # subsequent build steps.
2783 subdir('src/include')
2785 subdir('config')
2787 # Then through src/port and src/common, as most other things depend on them
2789 frontend_port_code = declare_dependency(
2790   compile_args: ['-DFRONTEND'],
2791   include_directories: [postgres_inc],
2792   dependencies: os_deps,
2795 backend_port_code = declare_dependency(
2796   compile_args: ['-DBUILDING_DLL'],
2797   include_directories: [postgres_inc],
2798   sources: [errcodes], # errcodes.h is needed due to use of ereport
2799   dependencies: os_deps,
2802 subdir('src/port')
2804 frontend_common_code = declare_dependency(
2805   compile_args: ['-DFRONTEND'],
2806   include_directories: [postgres_inc],
2807   sources: generated_headers,
2808   dependencies: [os_deps, zlib, zstd],
2811 backend_common_code = declare_dependency(
2812   compile_args: ['-DBUILDING_DLL'],
2813   include_directories: [postgres_inc],
2814   sources: generated_headers,
2815   dependencies: [os_deps, zlib, zstd],
2818 subdir('src/common')
2820 # all shared libraries should depend on shlib_code
2821 shlib_code = declare_dependency(
2822   link_args: ldflags_sl,
2825 # all static libraries not part of the backend should depend on this
2826 frontend_stlib_code = declare_dependency(
2827   include_directories: [postgres_inc],
2828   link_with: [common_static, pgport_static],
2829   sources: generated_headers,
2830   dependencies: [os_deps, libintl],
2833 # all shared libraries not part of the backend should depend on this
2834 frontend_shlib_code = declare_dependency(
2835   include_directories: [postgres_inc],
2836   link_with: [common_shlib, pgport_shlib],
2837   sources: generated_headers,
2838   dependencies: [shlib_code, os_deps, libintl],
2841 # Dependencies both for static and shared libpq
2842 libpq_deps += [
2843   thread_dep,
2845   gssapi,
2846   ldap_r,
2847   libintl,
2848   ssl,
2851 subdir('src/interfaces/libpq')
2852 # fe_utils depends on libpq
2853 subdir('src/fe_utils')
2855 # for frontend binaries
2856 frontend_code = declare_dependency(
2857   include_directories: [postgres_inc],
2858   link_with: [fe_utils, common_static, pgport_static],
2859   sources: generated_headers,
2860   dependencies: [os_deps, libintl],
2863 backend_both_deps += [
2864   thread_dep,
2865   bsd_auth,
2866   gssapi,
2867   icu,
2868   icu_i18n,
2869   ldap,
2870   libintl,
2871   libxml,
2872   lz4,
2873   pam,
2874   ssl,
2875   systemd,
2876   zlib,
2877   zstd,
2880 backend_mod_deps = backend_both_deps + os_deps
2882 backend_code = declare_dependency(
2883   compile_args: ['-DBUILDING_DLL'],
2884   include_directories: [postgres_inc],
2885   link_args: ldflags_be,
2886   link_with: [],
2887   sources: generated_headers + generated_backend_headers,
2888   dependencies: os_deps + backend_both_deps + backend_deps,
2891 # install these files only during test, not main install
2892 test_install_data = []
2893 test_install_libs = []
2895 # src/backend/meson.build defines backend_mod_code used for extension
2896 # libraries.
2899 # Then through the main sources. That way contrib can have dependencies on
2900 # main sources. Note that this explicitly doesn't enter src/test, right now a
2901 # few regression tests depend on contrib files.
2903 subdir('src')
2905 subdir('contrib')
2907 subdir('src/test')
2908 subdir('src/interfaces/libpq/test')
2909 subdir('src/interfaces/ecpg/test')
2911 subdir('doc/src/sgml')
2913 generated_sources_ac += {'': ['GNUmakefile']}
2915 # After processing src/test, add test_install_libs to the testprep_targets
2916 # to build them
2917 testprep_targets += test_install_libs
2920 # If there are any files in the source directory that we also generate in the
2921 # build directory, they might get preferred over the newly generated files,
2922 # e.g. because of a #include "file", which always will search in the current
2923 # directory first.
2924 message('checking for file conflicts between source and build directory')
2925 conflicting_files = []
2926 potentially_conflicting_files_t = []
2927 potentially_conflicting_files_t += generated_headers
2928 potentially_conflicting_files_t += generated_backend_headers
2929 potentially_conflicting_files_t += generated_backend_sources
2930 potentially_conflicting_files_t += generated_sources
2932 potentially_conflicting_files = []
2934 # convert all sources of potentially conflicting files into uniform shape
2935 foreach t : potentially_conflicting_files_t
2936   potentially_conflicting_files += t.full_path()
2937 endforeach
2938 foreach t1 : configure_files
2939   if meson.version().version_compare('>=0.59')
2940     t = fs.parent(t1) / fs.name(t1)
2941   else
2942     t = '@0@'.format(t1)
2943   endif
2944   potentially_conflicting_files += meson.current_build_dir() / t
2945 endforeach
2946 foreach sub, fnames : generated_sources_ac
2947   sub = meson.build_root() / sub
2948   foreach fname : fnames
2949     potentially_conflicting_files += sub / fname
2950   endforeach
2951 endforeach
2953 # find and report conflicting files
2954 foreach build_path : potentially_conflicting_files
2955   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
2956   # str.replace is in 0.56
2957   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
2958   if fs.exists(src_path) or fs.is_symlink(src_path)
2959     conflicting_files += src_path
2960   endif
2961 endforeach
2962 # XXX: Perhaps we should generate a file that would clean these up? The list
2963 # can be long.
2964 if conflicting_files.length() > 0
2965   errmsg_cleanup = '''
2966 Conflicting files in source directory:
2967   @0@
2969 The conflicting files need to be removed, either by removing the files listed
2970 above, or by running configure and then make maintainer-clean.
2972   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
2973   error(errmsg_nonclean_base.format(errmsg_cleanup))
2974 endif
2978 ###############################################################
2979 # Install targets
2980 ###############################################################
2983 # We want to define additional install targets beyond what meson provides. For
2984 # that we need to define targets depending on nearly everything. We collected
2985 # the results of i18n.gettext() invocations into nls_targets, that also
2986 # includes maintainer targets though. Collect the ones we want as a dependency.
2988 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
2989 # generation happens during install, so that's not a real issue.
2990 nls_mo_targets = []
2991 if libintl.found() and meson.version().version_compare('>=0.60')
2992   # use range() to avoid the flattening of the list that foreach() would do
2993   foreach off : range(0, nls_targets.length())
2994     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
2995     # -pot target 3) maintainer -pot target
2996     nls_mo_targets += nls_targets[off][0]
2997   endforeach
2998   alias_target('nls', nls_mo_targets)
2999 endif
3002 all_built = [
3003   backend_targets,
3004   bin_targets,
3005   libpq_st,
3006   pl_targets,
3007   contrib_targets,
3008   nls_mo_targets,
3009   testprep_targets,
3010   ecpg_targets,
3013 # Meson's default install target is quite verbose. Provide one that is quiet.
3014 install_quiet = custom_target('install-quiet',
3015   output: 'install-quiet',
3016   build_always_stale: true,
3017   build_by_default: false,
3018   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
3019   depends: all_built,
3022 # Target to install files used for tests, which aren't installed by default
3023 install_test_files_args = [
3024   install_files,
3025   '--prefix', dir_prefix,
3026   '--install', contrib_data_dir, test_install_data,
3027   '--install', dir_lib_pkg, test_install_libs,
3029 run_target('install-test-files',
3030   command: [python] + install_test_files_args,
3031   depends: testprep_targets,
3036 ###############################################################
3037 # Test prep
3038 ###############################################################
3040 # DESTDIR for the installation we'll run tests in
3041 test_install_destdir = meson.build_root() / 'tmp_install/'
3043 # DESTDIR + prefix appropriately munged
3044 if build_system != 'windows'
3045   # On unixoid systems this is trivial, we just prepend the destdir
3046   assert(dir_prefix.startswith('/')) # enforced by meson
3047   test_install_location = '@0@@1@'.format(test_install_destdir, dir_prefix)
3048 else
3049   # drives, drive-relative paths, etc make this complicated on windows, call
3050   # into a copy of meson's logic for it
3051   command = [
3052     python, '-c',
3053     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3054     test_install_destdir, dir_prefix]
3055   test_install_location = run_command(command, check: true).stdout().strip()
3056 endif
3058 meson_install_args = meson_args + ['install'] + {
3059     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3060     'muon': []
3061 }[meson_impl]
3063 # setup tests should be run first,
3064 # so define priority for these
3065 setup_tests_priority = 100
3066 test('tmp_install',
3067     meson_bin, args: meson_install_args ,
3068     env: {'DESTDIR':test_install_destdir},
3069     priority: setup_tests_priority,
3070     timeout: 300,
3071     is_parallel: false,
3072     suite: ['setup'])
3074 test('install_test_files',
3075     python,
3076     args: install_test_files_args + ['--destdir', test_install_destdir],
3077     priority: setup_tests_priority,
3078     is_parallel: false,
3079     suite: ['setup'])
3081 test_result_dir = meson.build_root() / 'testrun'
3084 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3085 # inevitable conflicts from running tests in parallel, hackishly assign
3086 # different ports for different tests.
3088 testport = 40000
3090 test_env = environment()
3092 temp_install_bindir = test_install_location / get_option('bindir')
3093 test_env.set('PG_REGRESS', pg_regress.full_path())
3094 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3096 # Test suites that are not safe by default but can be run if selected
3097 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3098 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3099 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3101 # Add the temporary installation to the library search path on platforms where
3102 # that works (everything but windows, basically). On windows everything
3103 # library-like gets installed into bindir, solving that issue.
3104 if library_path_var != ''
3105   test_env.prepend(library_path_var, test_install_location / get_option('libdir'))
3106 endif
3110 ###############################################################
3111 # Test Generation
3112 ###############################################################
3114 # When using a meson version understanding exclude_suites, define a
3115 # 'tmp_install' test setup (the default) that excludes tests running against a
3116 # pre-existing install and a 'running' setup that conflicts with creation of
3117 # the temporary installation and tap tests (which don't support running
3118 # against a running server).
3120 running_suites = []
3121 install_suites = []
3122 if meson.version().version_compare('>=0.57')
3123   runningcheck = true
3124 else
3125   runningcheck = false
3126 endif
3128 testwrap = files('src/tools/testwrap')
3130 foreach test_dir : tests
3131   testwrap_base = [
3132     testwrap,
3133     '--basedir', meson.build_root(),
3134     '--srcdir', test_dir['sd'],
3135   ]
3137   foreach kind, v : test_dir
3138     if kind in ['sd', 'bd', 'name']
3139       continue
3140     endif
3142     t = test_dir[kind]
3144     if kind in ['regress', 'isolation', 'ecpg']
3145       if kind == 'regress'
3146         runner = pg_regress
3147         fallback_dbname = 'regression_@0@'
3148       elif kind == 'isolation'
3149         runner = pg_isolation_regress
3150         fallback_dbname = 'isolation_regression_@0@'
3151       elif kind == 'ecpg'
3152         runner = pg_regress_ecpg
3153         fallback_dbname = 'ecpg_regression_@0@'
3154       endif
3156       test_group = test_dir['name']
3157       test_group_running = test_dir['name'] + '-running'
3159       test_output = test_result_dir / test_group / kind
3160       test_output_running = test_result_dir / test_group_running/ kind
3162       # Unless specified by the test, choose a non-conflicting database name,
3163       # to avoid conflicts when running against existing server.
3164       dbname = t.get('dbname',
3165         fallback_dbname.format(test_dir['name']))
3167       test_command_base = [
3168         runner.full_path(),
3169         '--inputdir', t.get('inputdir', test_dir['sd']),
3170         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3171         '--bindir', '',
3172         '--dlpath', test_dir['bd'],
3173         '--max-concurrent-tests=20',
3174         '--dbname', dbname,
3175       ] + t.get('regress_args', [])
3177       test_selection = []
3178       if t.has_key('schedule')
3179         test_selection += ['--schedule', t['schedule'],]
3180       endif
3182       if kind == 'isolation'
3183         test_selection += t.get('specs', [])
3184       else
3185         test_selection += t.get('sql', [])
3186       endif
3188       env = test_env
3189       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3191       test_kwargs = {
3192         'protocol': 'tap',
3193         'priority': 10,
3194         'timeout': 1000,
3195         'depends': test_deps + t.get('deps', []),
3196         'env': env,
3197       } + t.get('test_kwargs', {})
3199       test(test_group / kind,
3200         python,
3201         args: [
3202           testwrap_base,
3203           '--testgroup', test_group,
3204           '--testname', kind,
3205           '--',
3206           test_command_base,
3207           '--outputdir', test_output,
3208           '--temp-instance', test_output / 'tmp_check',
3209           '--port', testport.to_string(),
3210           test_selection,
3211         ],
3212         suite: test_group,
3213         kwargs: test_kwargs,
3214       )
3215       install_suites += test_group
3217       # some tests can't support running against running DB
3218       if runningcheck and t.get('runningcheck', true)
3219         test(test_group_running / kind,
3220           python,
3221           args: [
3222             testwrap_base,
3223             '--testgroup', test_group_running,
3224             '--testname', kind,
3225             '--',
3226             test_command_base,
3227             '--outputdir', test_output_running,
3228             test_selection,
3229           ],
3230           is_parallel: t.get('runningcheck-parallel', true),
3231           suite: test_group_running,
3232           kwargs: test_kwargs,
3233         )
3234         running_suites += test_group_running
3235       endif
3237       testport += 1
3238     elif kind == 'tap'
3239       if not tap_tests_enabled
3240         continue
3241       endif
3243       test_command = [
3244         perl.path(),
3245         '-I', meson.source_root() / 'src/test/perl',
3246         '-I', test_dir['sd'],
3247       ]
3249       # Add temporary install, the build directory for non-installed binaries and
3250       # also test/ for non-installed test binaries built separately.
3251       env = test_env
3252       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3254       foreach name, value : t.get('env', {})
3255         env.set(name, value)
3256       endforeach
3258       test_group = test_dir['name']
3259       test_kwargs = {
3260         'protocol': 'tap',
3261         'suite': test_group,
3262         'timeout': 1000,
3263         'depends': test_deps + t.get('deps', []),
3264         'env': env,
3265       } + t.get('test_kwargs', {})
3267       foreach onetap : t['tests']
3268         # Make tap test names prettier, remove t/ and .pl
3269         onetap_p = onetap
3270         if onetap_p.startswith('t/')
3271           onetap_p = onetap.split('t/')[1]
3272         endif
3273         if onetap_p.endswith('.pl')
3274           onetap_p = fs.stem(onetap_p)
3275         endif
3277         test(test_dir['name'] / onetap_p,
3278           python,
3279           kwargs: test_kwargs,
3280           args: testwrap_base + [
3281             '--testgroup', test_dir['name'],
3282             '--testname', onetap_p,
3283             '--', test_command,
3284             test_dir['sd'] / onetap,
3285           ],
3286         )
3287       endforeach
3288       install_suites += test_group
3289     else
3290       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3291     endif
3293   endforeach # kinds of tests
3295 endforeach # directories with tests
3297 # repeat condition so meson realizes version dependency
3298 if meson.version().version_compare('>=0.57')
3299   add_test_setup('tmp_install',
3300     is_default: true,
3301     exclude_suites: running_suites)
3302   add_test_setup('running',
3303     exclude_suites: ['setup'] + install_suites)
3304 endif
3308 ###############################################################
3309 # Pseudo targets
3310 ###############################################################
3312 alias_target('backend', backend_targets)
3313 alias_target('bin', bin_targets + [libpq_st])
3314 alias_target('pl', pl_targets)
3315 alias_target('contrib', contrib_targets)
3316 alias_target('testprep', testprep_targets)
3317 alias_target('install-world', install_quiet, installdocs)
3321 ###############################################################
3322 # The End, The End, My Friend
3323 ###############################################################
3325 if meson.version().version_compare('>=0.57')
3327   summary(
3328     {
3329       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3330       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3331       'segment size': get_option('segsize_blocks') != 0 ?
3332         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3333         '@0@ GB'.format(get_option('segsize')),
3334     },
3335     section: 'Data layout',
3336   )
3338   summary(
3339     {
3340       'host system': '@0@ @1@'.format(host_system, host_cpu),
3341       'build system': '@0@ @1@'.format(build_machine.system(),
3342                                        build_machine.cpu_family()),
3343     },
3344     section: 'System',
3345   )
3347   summary(
3348     {
3349       'linker': '@0@'.format(cc.get_linker_id()),
3350       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3351     },
3352     section: 'Compiler',
3353   )
3355   summary(
3356     {
3357       'CPP FLAGS': ' '.join(cppflags),
3358       'C FLAGS, functional': ' '.join(cflags),
3359       'C FLAGS, warnings': ' '.join(cflags_warn),
3360       'C FLAGS, modules': ' '.join(cflags_mod),
3361       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3362       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3363     },
3364     section: 'Compiler Flags',
3365   )
3367   if llvm.found()
3368     summary(
3369       {
3370         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3371       },
3372       section: 'Compiler',
3373     )
3375     summary(
3376       {
3377         'C++ FLAGS, functional': ' '.join(cxxflags),
3378         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3379         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3380       },
3381       section: 'Compiler Flags',
3382     )
3383   endif
3385   summary(
3386     {
3387       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3388       'dtrace': dtrace,
3389     },
3390     section: 'Programs',
3391   )
3393   summary(
3394     {
3395       'bonjour': bonjour,
3396       'bsd_auth': bsd_auth,
3397       'docs': docs_dep,
3398       'docs_pdf': docs_pdf_dep,
3399       'gss': gssapi,
3400       'icu': icu,
3401       'ldap': ldap,
3402       'libxml': libxml,
3403       'libxslt': libxslt,
3404       'llvm': llvm,
3405       'lz4': lz4,
3406       'nls': libintl,
3407       'openssl': ssl,
3408       'pam': pam,
3409       'plperl': perl_dep,
3410       'plpython': python3_dep,
3411       'pltcl': tcl_dep,
3412       'readline': readline,
3413       'selinux': selinux,
3414       'systemd': systemd,
3415       'uuid': uuid,
3416       'zlib': zlib,
3417       'zstd': zstd,
3418     },
3419     section: 'External libraries',
3420   )
3422 endif