Improve fix for not entering parallel mode when holding interrupts.
[pgsql.git] / meson.build
blob4c2769dee0aa2dce2b0766a35d890b85cfb46a79
1 # Copyright (c) 2022-2024, 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: '17.0',
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 distclean 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 elif host_system == 'android'
188   # while android isn't quite a normal linux, it seems close enough
189   # for our purposes so far
190   host_system = 'linux'
191 endif
193 # meson's system names don't quite map to our "traditional" names. In some
194 # places we need the "traditional" name, e.g., for mapping
195 # src/include/port/$os.h to src/include/pg_config_os.h. Define portname for
196 # that purpose.
197 portname = host_system
199 if host_system == 'cygwin'
200   sema_kind = 'unnamed_posix'
201   cppflags += '-D_GNU_SOURCE'
202   dlsuffix = '.dll'
203   mod_link_args_fmt = ['@0@']
204   mod_link_with_name = 'lib@0@.a'
205   mod_link_with_dir = 'libdir'
207 elif host_system == 'darwin'
208   dlsuffix = '.dylib'
209   library_path_var = 'DYLD_LIBRARY_PATH'
211   export_file_format = 'darwin'
212   export_fmt = '-Wl,-exported_symbols_list,@0@'
214   mod_link_args_fmt = ['-bundle_loader', '@0@']
215   mod_link_with_dir = 'bindir'
216   mod_link_with_name = '@0@'
218   sysroot_args = [files('src/tools/darwin_sysroot'), get_option('darwin_sysroot')]
219   pg_sysroot = run_command(sysroot_args, check:true).stdout().strip()
220   message('darwin sysroot: @0@'.format(pg_sysroot))
221   if pg_sysroot != ''
222     cflags += ['-isysroot', pg_sysroot]
223     ldflags += ['-isysroot', pg_sysroot]
224   endif
226   # meson defaults to -Wl,-undefined,dynamic_lookup for modules, which we
227   # don't want because a) it's different from what we do for autoconf, b) it
228   # causes warnings in macOS Ventura. But using -Wl,-undefined,error causes a
229   # warning starting in Sonoma. So only add -Wl,-undefined,error if it does
230   # not cause a warning.
231   if cc.has_multi_link_arguments('-Wl,-undefined,error', '-Werror')
232     ldflags_mod += '-Wl,-undefined,error'
233   endif
235   # Starting in Sonoma, the linker warns about the same library being
236   # linked twice.  Which can easily happen when multiple dependencies
237   # depend on the same library. Quiesce the ill considered warning.
238   ldflags += cc.get_supported_link_arguments('-Wl,-no_warn_duplicate_libraries')
240 elif host_system == 'freebsd'
241   sema_kind = 'unnamed_posix'
243 elif host_system == 'linux'
244   sema_kind = 'unnamed_posix'
245   cppflags += '-D_GNU_SOURCE'
247 elif host_system == 'netbsd'
248   # We must resolve all dynamic linking in the core server at program start.
249   # Otherwise the postmaster can self-deadlock due to signals interrupting
250   # resolution of calls, since NetBSD's linker takes a lock while doing that
251   # and some postmaster signal handlers do things that will also acquire that
252   # lock.  As long as we need "-z now", might as well specify "-z relro" too.
253   # While there's not a hard reason to adopt these settings for our other
254   # executables, there's also little reason not to, so just add them to
255   # LDFLAGS.
256   ldflags += ['-Wl,-z,now', '-Wl,-z,relro']
258 elif host_system == 'openbsd'
259   # you're ok
261 elif host_system == 'sunos'
262   portname = 'solaris'
263   export_fmt = '-Wl,-M@0@'
264   cppflags += '-D_POSIX_PTHREAD_SEMANTICS'
266 elif host_system == 'windows'
267   portname = 'win32'
268   exesuffix = '.exe'
269   dlsuffix = '.dll'
270   library_path_var = ''
272   export_file_format = 'win'
273   export_file_suffix = 'def'
274   if cc.get_id() == 'msvc'
275     export_fmt = '/DEF:@0@'
276     mod_link_with_name = '@0@.lib'
277   else
278     export_fmt = '@0@'
279     mod_link_with_name = 'lib@0@.a'
280   endif
281   mod_link_args_fmt = ['@0@']
282   mod_link_with_dir = 'libdir'
284   shmem_kind = 'win32'
285   sema_kind = 'win32'
287   cdata.set('WIN32_STACK_RLIMIT', 4194304)
288   if cc.get_id() == 'msvc'
289     ldflags += '/INCREMENTAL:NO'
290     ldflags += '/STACK:@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
291     # ldflags += '/nxcompat' # generated by msbuild, should have it for ninja?
292   else
293     ldflags += '-Wl,--stack,@0@'.format(cdata.get('WIN32_STACK_RLIMIT'))
294     # Need to allow multiple definitions, we e.g. want to override getopt.
295     ldflags += '-Wl,--allow-multiple-definition'
296     # Ensure we get MSVC-like linking behavior.
297     ldflags += '-Wl,--disable-auto-import'
298   endif
300   os_deps += cc.find_library('ws2_32', required: true)
301   secur32_dep = cc.find_library('secur32', required: true)
302   backend_deps += secur32_dep
303   libpq_deps += secur32_dep
305   postgres_inc_d += 'src/include/port/win32'
306   if cc.get_id() == 'msvc'
307     postgres_inc_d += 'src/include/port/win32_msvc'
308   endif
310   windows = import('windows')
312 else
313   # XXX: Should we add an option to override the host_system as an escape
314   # hatch?
315   error('unknown host system: @0@'.format(host_system))
316 endif
320 ###############################################################
321 # Program paths
322 ###############################################################
324 # External programs
325 perl = find_program(get_option('PERL'), required: true, native: true)
326 python = find_program(get_option('PYTHON'), required: true, native: true)
327 flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.35')
328 bison = find_program(get_option('BISON'), native: true, version: '>= 2.3')
329 sed = find_program(get_option('SED'), 'sed', native: true, required: false)
330 prove = find_program(get_option('PROVE'), native: true, required: false)
331 tar = find_program(get_option('TAR'), native: true, required: false)
332 gzip = find_program(get_option('GZIP'), native: true, required: false)
333 program_lz4 = find_program(get_option('LZ4'), native: true, required: false)
334 openssl = find_program(get_option('OPENSSL'), native: true, required: false)
335 program_zstd = find_program(get_option('ZSTD'), native: true, required: false)
336 dtrace = find_program(get_option('DTRACE'), native: true, required: get_option('dtrace'))
337 missing = find_program('config/missing', native: true)
338 cp = find_program('cp', required: false, native: true)
339 xmllint_bin = find_program(get_option('XMLLINT'), native: true, required: false)
340 xsltproc_bin = find_program(get_option('XSLTPROC'), native: true, required: false)
342 bison_flags = []
343 if bison.found()
344   bison_version_c = run_command(bison, '--version', check: true)
345   # bison version string helpfully is something like
346   # >>bison (GNU bison) 3.8.1<<
347   bison_version = bison_version_c.stdout().split(' ')[3].split('\n')[0]
348   if bison_version.version_compare('>=3.0')
349     bison_flags += ['-Wno-deprecated']
350   endif
351 endif
352 bison_cmd = [bison, bison_flags, '-o', '@OUTPUT0@', '-d', '@INPUT@']
353 bison_kw = {
354   'output': ['@BASENAME@.c', '@BASENAME@.h'],
355   'command': bison_cmd,
358 flex_flags = []
359 if flex.found()
360   flex_version_c = run_command(flex, '--version', check: true)
361   flex_version = flex_version_c.stdout().split(' ')[1].split('\n')[0]
362 endif
363 flex_wrapper = files('src/tools/pgflex')
364 flex_cmd = [python, flex_wrapper,
365   '--builddir', '@BUILD_ROOT@',
366   '--srcdir', '@SOURCE_ROOT@',
367   '--privatedir', '@PRIVATE_DIR@',
368   '--flex', flex, '--perl', perl,
369   '-i', '@INPUT@', '-o', '@OUTPUT0@',
372 wget = find_program('wget', required: false, native: true)
373 wget_flags = ['-O', '@OUTPUT0@', '--no-use-server-timestamps']
375 install_files = files('src/tools/install_files')
379 ###############################################################
380 # Path to meson (for tests etc)
381 ###############################################################
383 # NB: this should really be part of meson, see
384 # https://github.com/mesonbuild/meson/issues/8511
385 meson_binpath_r = run_command(python, 'src/tools/find_meson', check: true)
387 if meson_binpath_r.stdout() == ''
388   error('huh, could not run find_meson.\nerrcode: @0@\nstdout: @1@\nstderr: @2@'.format(
389     meson_binpath_r.returncode(),
390     meson_binpath_r.stdout(),
391     meson_binpath_r.stderr()))
392 endif
394 meson_binpath_s = meson_binpath_r.stdout().split('\n')
395 meson_binpath_len = meson_binpath_s.length()
397 if meson_binpath_len < 1
398   error('unexpected introspect line @0@'.format(meson_binpath_r.stdout()))
399 endif
401 i = 0
402 meson_impl = ''
403 meson_binpath = ''
404 meson_args = []
405 foreach e : meson_binpath_s
406   if i == 0
407     meson_impl = e
408   elif i == 1
409     meson_binpath = e
410   else
411     meson_args += e
412   endif
413   i += 1
414 endforeach
416 if meson_impl not in ['muon', 'meson']
417   error('unknown meson implementation "@0@"'.format(meson_impl))
418 endif
420 meson_bin = find_program(meson_binpath, native: true)
424 ###############################################################
425 # Option Handling
426 ###############################################################
428 cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false)
429 cdata.set('USE_INJECTION_POINTS', get_option('injection_points') ? 1 : false)
431 blocksize = get_option('blocksize').to_int() * 1024
433 if get_option('segsize_blocks') != 0
434   if get_option('segsize') != 1
435     warning('both segsize and segsize_blocks specified, segsize_blocks wins')
436   endif
438   segsize = get_option('segsize_blocks')
439 else
440   segsize = (get_option('segsize') * 1024 * 1024 * 1024) / blocksize
441 endif
443 cdata.set('BLCKSZ', blocksize, description:
444 '''Size of a disk block --- this also limits the size of a tuple. You can set
445    it bigger if you need bigger tuples (although TOAST should reduce the need
446    to have large tuples, since fields can be spread across multiple tuples).
447    BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is
448    currently 2^15 (32768). This is determined by the 15-bit widths of the
449    lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h).
450    Changing BLCKSZ requires an initdb.''')
452 cdata.set('XLOG_BLCKSZ', get_option('wal_blocksize').to_int() * 1024)
453 cdata.set('RELSEG_SIZE', segsize)
454 cdata.set('DEF_PGPORT', get_option('pgport'))
455 cdata.set_quoted('DEF_PGPORT_STR', get_option('pgport').to_string())
456 cdata.set_quoted('PG_KRB_SRVNAM', get_option('krb_srvnam'))
457 if get_option('system_tzdata') != ''
458   cdata.set_quoted('SYSTEMTZDIR', get_option('system_tzdata'))
459 endif
463 ###############################################################
464 # Directories
465 ###############################################################
467 # These are set by the equivalent --xxxdir configure options.  We
468 # append "postgresql" to some of them, if the string does not already
469 # contain "pgsql" or "postgres", in order to avoid directory clutter.
471 pkg = 'postgresql'
473 dir_prefix = get_option('prefix')
475 dir_prefix_contains_pg = (dir_prefix.contains('pgsql') or dir_prefix.contains('postgres'))
477 dir_bin = get_option('bindir')
479 dir_data = get_option('datadir')
480 if not (dir_prefix_contains_pg or dir_data.contains('pgsql') or dir_data.contains('postgres'))
481   dir_data = dir_data / pkg
482 endif
484 dir_sysconf = get_option('sysconfdir')
485 if not (dir_prefix_contains_pg or dir_sysconf.contains('pgsql') or dir_sysconf.contains('postgres'))
486   dir_sysconf = dir_sysconf / pkg
487 endif
489 dir_lib = get_option('libdir')
491 dir_lib_pkg = dir_lib
492 if not (dir_prefix_contains_pg or dir_lib_pkg.contains('pgsql') or dir_lib_pkg.contains('postgres'))
493   dir_lib_pkg = dir_lib_pkg / pkg
494 endif
496 dir_pgxs = dir_lib_pkg / 'pgxs'
498 dir_include = get_option('includedir')
500 dir_include_pkg = dir_include
501 dir_include_pkg_rel = ''
502 if not (dir_prefix_contains_pg or dir_include_pkg.contains('pgsql') or dir_include_pkg.contains('postgres'))
503   dir_include_pkg = dir_include_pkg / pkg
504   dir_include_pkg_rel = pkg
505 endif
507 dir_man = get_option('mandir')
509 # FIXME: These used to be separately configurable - worth adding?
510 dir_doc = get_option('datadir') / 'doc'
511 if not (dir_prefix_contains_pg or dir_doc.contains('pgsql') or dir_doc.contains('postgres'))
512   dir_doc = dir_doc / pkg
513 endif
514 dir_doc_html = dir_doc / 'html'
516 dir_locale = get_option('localedir')
519 # Derived values
520 dir_bitcode = dir_lib_pkg / 'bitcode'
521 dir_include_internal = dir_include_pkg / 'internal'
522 dir_include_server = dir_include_pkg / 'server'
523 dir_include_extension = dir_include_server / 'extension'
524 dir_data_extension = dir_data / 'extension'
525 dir_doc_extension = dir_doc / 'extension'
529 ###############################################################
530 # Search paths, preparation for compiler tests
532 # NB: Arguments added later are not automatically used for subsequent
533 # configuration-time checks (so they are more isolated). If they should be
534 # used, they need to be added to test_c_args as well.
535 ###############################################################
537 postgres_inc = [include_directories(postgres_inc_d)]
538 test_lib_d = postgres_lib_d
539 test_c_args = cppflags + cflags
543 ###############################################################
544 # Library: bsd-auth
545 ###############################################################
547 bsd_authopt = get_option('bsd_auth')
548 bsd_auth = not_found_dep
549 if cc.check_header('bsd_auth.h', required: bsd_authopt,
550     args: test_c_args, include_directories: postgres_inc)
551   cdata.set('USE_BSD_AUTH', 1)
552   bsd_auth = declare_dependency()
553 endif
557 ###############################################################
558 # Library: bonjour
560 # For now don't search for DNSServiceRegister in a library - only Apple's
561 # Bonjour implementation, which is always linked, works.
562 ###############################################################
564 bonjouropt = get_option('bonjour')
565 bonjour = not_found_dep
566 if cc.check_header('dns_sd.h', required: bonjouropt,
567     args: test_c_args, include_directories: postgres_inc) and \
568    cc.has_function('DNSServiceRegister',
569     args: test_c_args, include_directories: postgres_inc)
570   cdata.set('USE_BONJOUR', 1)
571   bonjour = declare_dependency()
572 endif
576 ###############################################################
577 # Option: docs in HTML and man page format
578 ###############################################################
580 docs_opt = get_option('docs')
581 docs_dep = not_found_dep
582 if not docs_opt.disabled()
583   if xmllint_bin.found() and xsltproc_bin.found()
584     docs_dep = declare_dependency()
585   elif docs_opt.enabled()
586     error('missing required tools (xmllint and xsltproc needed) for docs in HTML / man page format')
587   endif
588 endif
592 ###############################################################
593 # Option: docs in PDF format
594 ###############################################################
596 docs_pdf_opt = get_option('docs_pdf')
597 docs_pdf_dep = not_found_dep
598 if not docs_pdf_opt.disabled()
599   fop = find_program(get_option('FOP'), native: true, required: docs_pdf_opt)
600   if xmllint_bin.found() and xsltproc_bin.found() and fop.found()
601     docs_pdf_dep = declare_dependency()
602   elif docs_pdf_opt.enabled()
603     error('missing required tools for docs in PDF format')
604   endif
605 endif
609 ###############################################################
610 # Library: GSSAPI
611 ###############################################################
613 gssapiopt = get_option('gssapi')
614 krb_srvtab = ''
615 have_gssapi = false
616 if not gssapiopt.disabled()
617   gssapi = dependency('krb5-gssapi', required: false)
618   have_gssapi = gssapi.found()
620   if have_gssapi
621       gssapi_deps = [gssapi]
622   elif not have_gssapi
623     # Hardcoded lookup for gssapi. This is necessary as gssapi on windows does
624     # not install neither pkg-config nor cmake dependency information.
625     if host_system == 'windows'
626       is_64  = cc.sizeof('void *', args: test_c_args) == 8
627       if is_64
628         gssapi_search_libs = ['gssapi64', 'krb5_64', 'comerr64']
629       else
630         gssapi_search_libs = ['gssapi32', 'krb5_32', 'comerr32']
631       endif
632     else
633       gssapi_search_libs = ['gssapi_krb5']
634     endif
636     gssapi_deps = []
637     foreach libname : gssapi_search_libs
638       lib = cc.find_library(libname, dirs: test_lib_d, required: false)
639       if lib.found()
640         have_gssapi = true
641         gssapi_deps += lib
642       endif
643     endforeach
645     if have_gssapi
646       # Meson before 0.57.0 did not support using check_header() etc with
647       # declare_dependency(). Thus the tests below use the library looked up
648       # above.  Once we require a newer meson version, we can simplify.
649       gssapi = declare_dependency(dependencies: gssapi_deps)
650     endif
651   endif
653   if not have_gssapi
654   elif cc.check_header('gssapi/gssapi.h', dependencies: gssapi_deps, required: false,
655       args: test_c_args, include_directories: postgres_inc)
656     cdata.set('HAVE_GSSAPI_GSSAPI_H', 1)
657   elif cc.check_header('gssapi.h', dependencies: gssapi_deps, required: gssapiopt,
658       args: test_c_args, include_directories: postgres_inc)
659     cdata.set('HAVE_GSSAPI_H', 1)
660   else
661     have_gssapi = false
662   endif
664   if not have_gssapi
665   elif cc.check_header('gssapi/gssapi_ext.h', dependencies: gssapi_deps, required: false,
666       args: test_c_args, include_directories: postgres_inc)
667     cdata.set('HAVE_GSSAPI_GSSAPI_EXT_H', 1)
668   elif cc.check_header('gssapi_ext.h', dependencies: gssapi_deps, required: gssapiopt,
669       args: test_c_args, include_directories: postgres_inc)
670     cdata.set('HAVE_GSSAPI_EXT_H', 1)
671   else
672     have_gssapi = false
673   endif
675   if not have_gssapi
676   elif cc.has_function('gss_store_cred_into', dependencies: gssapi_deps,
677       args: test_c_args, include_directories: postgres_inc)
678     cdata.set('ENABLE_GSS', 1)
680     krb_srvtab = 'FILE:/@0@/krb5.keytab)'.format(get_option('sysconfdir'))
681     cdata.set_quoted('PG_KRB_SRVTAB', krb_srvtab)
682   elif gssapiopt.enabled()
683     error('''could not find function 'gss_store_cred_into' required for GSSAPI''')
684   else
685     have_gssapi = false
686   endif
688   if not have_gssapi and gssapiopt.enabled()
689     error('dependency lookup for gssapi failed')
690   endif
692 endif
693 if not have_gssapi
694   gssapi = not_found_dep
695 endif
699 ###############################################################
700 # Library: ldap
701 ###############################################################
703 ldapopt = get_option('ldap')
704 if ldapopt.disabled()
705   ldap = not_found_dep
706   ldap_r = not_found_dep
707 elif host_system == 'windows'
708   ldap = cc.find_library('wldap32', required: ldapopt)
709   ldap_r = ldap
710 else
711   # macos framework dependency is buggy for ldap (one can argue whether it's
712   # Apple's or meson's fault), leading to an endless recursion with ldap.h
713   # including itself. See https://github.com/mesonbuild/meson/issues/10002
714   # Luckily we only need pkg-config support, so the workaround isn't
715   # complicated.
716   ldap = dependency('ldap', method: 'pkg-config', required: false)
717   ldap_r = ldap
719   # Before 2.5 openldap didn't have a pkg-config file, and it might not be
720   # installed
721   if not ldap.found()
722     ldap = cc.find_library('ldap', required: ldapopt, dirs: test_lib_d,
723       has_headers: 'ldap.h', header_include_directories: postgres_inc)
725     # The separate ldap_r library only exists in OpenLDAP < 2.5, and if we
726     # have 2.5 or later, we shouldn't even probe for ldap_r (we might find a
727     # library from a separate OpenLDAP installation).  The most reliable
728     # way to check that is to check for a function introduced in 2.5.
729     if not ldap.found()
730       # don't have ldap, we shouldn't check for ldap_r
731     elif cc.has_function('ldap_verify_credentials',
732         dependencies: ldap, args: test_c_args)
733       ldap_r = ldap # ldap >= 2.5, no need for ldap_r
734     else
736       # Use ldap_r for FE if available, else assume ldap is thread-safe.
737       ldap_r = cc.find_library('ldap_r', required: false, dirs: test_lib_d,
738         has_headers: 'ldap.h', header_include_directories: postgres_inc)
739       if not ldap_r.found()
740         ldap_r = ldap
741       else
742         # On some platforms ldap_r fails to link without PTHREAD_LIBS.
743         ldap_r = declare_dependency(dependencies: [ldap_r, thread_dep])
744       endif
746       # PostgreSQL sometimes loads libldap_r and plain libldap into the same
747       # process.  Check for OpenLDAP versions known not to tolerate doing so;
748       # assume non-OpenLDAP implementations are safe.  The dblink test suite
749       # exercises the hazardous interaction directly.
750       compat_test_code = '''
751 #include <ldap.h>
752 #if !defined(LDAP_VENDOR_VERSION) || \
753      (defined(LDAP_API_FEATURE_X_OPENLDAP) && \
754       LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
755 choke me
756 #endif
758       if not cc.compiles(compat_test_code,
759           name: 'LDAP implementation compatible',
760           dependencies: ldap, args: test_c_args)
761         warning('''
762 *** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
763 *** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
764 *** also uses LDAP will crash on exit.''')
765       endif
766     endif
767   endif
769   if ldap.found() and cc.has_function('ldap_initialize',
770       dependencies: ldap, args: test_c_args)
771     cdata.set('HAVE_LDAP_INITIALIZE', 1)
772   endif
773 endif
775 if ldap.found()
776   assert(ldap_r.found())
777   cdata.set('USE_LDAP', 1)
778 else
779   assert(not ldap_r.found())
780 endif
784 ###############################################################
785 # Library: LLVM
786 ###############################################################
788 llvmopt = get_option('llvm')
789 llvm = not_found_dep
790 if add_languages('cpp', required: llvmopt, native: false)
791   llvm = dependency('llvm', version: '>=10', method: 'config-tool', required: llvmopt)
793   if llvm.found()
795     cdata.set('USE_LLVM', 1)
797     cpp = meson.get_compiler('cpp')
799     llvm_binpath = llvm.get_variable(configtool: 'bindir')
801     ccache = find_program('ccache', native: true, required: false)
803     # Some distros put LLVM and clang in different paths, so fallback to
804     # find via PATH, too.
805     clang = find_program(llvm_binpath / 'clang', 'clang', required: true)
806   endif
807 elif llvmopt.auto()
808   message('llvm requires a C++ compiler')
809 endif
813 ###############################################################
814 # Library: icu
815 ###############################################################
817 icuopt = get_option('icu')
818 if not icuopt.disabled()
819   icu = dependency('icu-uc', required: false)
820   if icu.found()
821     icu_i18n = dependency('icu-i18n', required: true)
822   endif
824   # Unfortunately the dependency is named differently with cmake
825   if not icu.found() # combine with above once meson 0.60.0 is required
826     icu = dependency('ICU', required: icuopt,
827                      components: ['uc'], modules: ['ICU::uc'], method: 'cmake')
828     if icu.found()
829       icu_i18n = dependency('ICU', required: true,
830                             components: ['i18n'], modules: ['ICU::i18n'])
831     endif
832   endif
834   if icu.found()
835     cdata.set('USE_ICU', 1)
836   else
837     icu_i18n = not_found_dep
838   endif
840 else
841   icu = not_found_dep
842   icu_i18n = not_found_dep
843 endif
847 ###############################################################
848 # Library: libxml
849 ###############################################################
851 libxmlopt = get_option('libxml')
852 if not libxmlopt.disabled()
853   libxml = dependency('libxml-2.0', required: false, version: '>= 2.6.23')
854   # Unfortunately the dependency is named differently with cmake
855   if not libxml.found() # combine with above once meson 0.60.0 is required
856     libxml = dependency('LibXml2', required: libxmlopt, version: '>= 2.6.23',
857       method: 'cmake')
858   endif
860   if libxml.found()
861     cdata.set('USE_LIBXML', 1)
862   endif
863 else
864   libxml = not_found_dep
865 endif
869 ###############################################################
870 # Library: libxslt
871 ###############################################################
873 libxsltopt = get_option('libxslt')
874 if not libxsltopt.disabled()
875   libxslt = dependency('libxslt', required: false)
876   # Unfortunately the dependency is named differently with cmake
877   if not libxslt.found() # combine with above once meson 0.60.0 is required
878     libxslt = dependency('LibXslt', required: libxsltopt, method: 'cmake')
879   endif
881   if libxslt.found()
882     cdata.set('USE_LIBXSLT', 1)
883   endif
884 else
885   libxslt = not_found_dep
886 endif
890 ###############################################################
891 # Library: lz4
892 ###############################################################
894 lz4opt = get_option('lz4')
895 if not lz4opt.disabled()
896   lz4 = dependency('liblz4', required: false)
897   # Unfortunately the dependency is named differently with cmake
898   if not lz4.found() # combine with above once meson 0.60.0 is required
899     lz4 = dependency('lz4', required: lz4opt,
900                      method: 'cmake', modules: ['LZ4::lz4_shared'],
901                     )
902   endif
904   if lz4.found()
905     cdata.set('USE_LZ4', 1)
906     cdata.set('HAVE_LIBLZ4', 1)
907   endif
909 else
910   lz4 = not_found_dep
911 endif
915 ###############################################################
916 # Library: Tcl (for pltcl)
918 # NB: tclConfig.sh is used in autoconf build for getting
919 # TCL_SHARED_BUILD, TCL_INCLUDE_SPEC, TCL_LIBS and TCL_LIB_SPEC
920 # variables. For now we have not seen a need to copy
921 # that behaviour to the meson build.
922 ###############################################################
924 tclopt = get_option('pltcl')
925 tcl_version = get_option('tcl_version')
926 tcl_dep = not_found_dep
927 if not tclopt.disabled()
929   # via pkg-config
930   tcl_dep = dependency(tcl_version, required: false)
932   if not tcl_dep.found()
933     tcl_dep = cc.find_library(tcl_version,
934       required: tclopt,
935       dirs: test_lib_d)
936   endif
938   if not cc.has_header('tcl.h', dependencies: tcl_dep, required: tclopt)
939     tcl_dep = not_found_dep
940   endif
941 endif
945 ###############################################################
946 # Library: pam
947 ###############################################################
949 pamopt = get_option('pam')
950 if not pamopt.disabled()
951   pam = dependency('pam', required: false)
953   if not pam.found()
954     pam = cc.find_library('pam', required: pamopt, dirs: test_lib_d)
955   endif
957   if pam.found()
958     pam_header_found = false
960     # header file <security/pam_appl.h> or <pam/pam_appl.h> is required for PAM.
961     if cc.check_header('security/pam_appl.h', dependencies: pam, required: false,
962         args: test_c_args, include_directories: postgres_inc)
963       cdata.set('HAVE_SECURITY_PAM_APPL_H', 1)
964       pam_header_found = true
965     elif cc.check_header('pam/pam_appl.h', dependencies: pam, required: pamopt,
966         args: test_c_args, include_directories: postgres_inc)
967       cdata.set('HAVE_PAM_PAM_APPL_H', 1)
968       pam_header_found = true
969     endif
971     if pam_header_found
972       cdata.set('USE_PAM', 1)
973     else
974       pam = not_found_dep
975     endif
976   endif
977 else
978   pam = not_found_dep
979 endif
983 ###############################################################
984 # Library: Perl (for plperl)
985 ###############################################################
987 perlopt = get_option('plperl')
988 perl_dep = not_found_dep
989 if not perlopt.disabled()
990   perl_may_work = true
992   # First verify that perl has the necessary dependencies installed
993   perl_mods = run_command(
994     [perl,
995      '-MConfig', '-MOpcode', '-MExtUtils::Embed', '-MExtUtils::ParseXS',
996      '-e', ''],
997     check: false)
998   if perl_mods.returncode() != 0
999     perl_may_work = false
1000     perl_msg = 'perl installation does not have the required modules'
1001   endif
1003   # Then inquire perl about its configuration
1004   if perl_may_work
1005     perl_conf_cmd = [perl, '-MConfig', '-e', 'print $Config{$ARGV[0]}']
1006     perlversion = run_command(perl_conf_cmd, 'api_versionstring', check: true).stdout()
1007     archlibexp = run_command(perl_conf_cmd, 'archlibexp', check: true).stdout()
1008     privlibexp = run_command(perl_conf_cmd, 'privlibexp', check: true).stdout()
1009     useshrplib = run_command(perl_conf_cmd, 'useshrplib', check: true).stdout()
1011     perl_inc_dir = '@0@/CORE'.format(archlibexp)
1013     if perlversion.version_compare('< 5.14')
1014       perl_may_work = false
1015       perl_msg = 'Perl version 5.14 or later is required, but this is @0@'.format(perlversion)
1016     elif useshrplib != 'true'
1017       perl_may_work = false
1018       perl_msg = 'need a shared perl'
1019     endif
1020   endif
1022   if perl_may_work
1023     # On most platforms, archlibexp is also where the Perl include files live ...
1024     perl_ccflags = ['-I@0@'.format(perl_inc_dir)]
1025     # ... but on newer macOS versions, we must use -iwithsysroot to look
1026     # under sysroot
1027     if not fs.is_file('@0@/perl.h'.format(perl_inc_dir)) and \
1028        fs.is_file('@0@@1@/perl.h'.format(pg_sysroot, perl_inc_dir))
1029       perl_ccflags = ['-iwithsysroot', perl_inc_dir]
1030     endif
1032     # check compiler finds header
1033     if not cc.has_header('perl.h', required: false,
1034         args: test_c_args + perl_ccflags, include_directories: postgres_inc)
1035       perl_may_work = false
1036       perl_msg = 'missing perl.h'
1037     endif
1038   endif
1040   if perl_may_work
1041     perl_ccflags_r = run_command(perl_conf_cmd, 'ccflags', check: true).stdout()
1043     # See comments for PGAC_CHECK_PERL_EMBED_CCFLAGS in perl.m4
1044     foreach flag : perl_ccflags_r.split(' ')
1045       if flag.startswith('-D') and \
1046           (not flag.startswith('-D_') or flag == '_USE_32BIT_TIME_T')
1047         perl_ccflags += flag
1048       endif
1049     endforeach
1051     if host_system == 'windows'
1052       perl_ccflags += ['-DPLPERL_HAVE_UID_GID']
1054       if cc.get_id() == 'msvc'
1055         # prevent binary mismatch between MSVC built plperl and Strawberry or
1056         # msys ucrt perl libraries
1057         perl_v = run_command(perl, '-V').stdout()
1058         if not perl_v.contains('USE_THREAD_SAFE_LOCALE')
1059           perl_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
1060         endif
1061       endif
1062     endif
1064     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
1065     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
1067     # We are after Embed's ldopts, but without the subset mentioned in
1068     # Config's ccdlflags and ldflags.  (Those are the choices of those who
1069     # built the Perl installation, which are not necessarily appropriate
1070     # for building PostgreSQL.)
1071     perl_ldopts = run_command(perl, '-e', '''
1072 use ExtUtils::Embed;
1073 use Text::ParseWords;
1074 # tell perl to suppress including these in ldopts
1075 *ExtUtils::Embed::_ldflags =*ExtUtils::Embed::_ccdlflags = sub { return ""; };
1076 # adding an argument to ldopts makes it return a value instead of printing
1077 # print one of these per line so splitting will preserve spaces in file names.
1078 # shellwords eats backslashes, so we need to escape them.
1079 (my $opts = ldopts(undef)) =~ s!\\!\\\\!g;
1080 print "$_\n" foreach shellwords($opts);
1081 ''',
1082      check: true).stdout().strip().split('\n')
1084     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1086     perl_dep_int = declare_dependency(
1087       compile_args: perl_ccflags,
1088       link_args: perl_ldopts,
1089       version: perlversion,
1090     )
1092     # While we're at it, check that we can link to libperl.
1093     # On most platforms, if perl.h is there then libperl.so will be too, but
1094     # at this writing Debian packages them separately.
1095     perl_link_test = '''
1096 /* see plperl.h */
1097 #ifdef _MSC_VER
1098 #define __inline__ inline
1099 #endif
1100 #include <EXTERN.h>
1101 #include <perl.h>
1102 int main(void)
1104 perl_alloc();
1105 }'''
1106     if not cc.links(perl_link_test, name: 'libperl',
1107           args: test_c_args + perl_ccflags + perl_ldopts,
1108           include_directories: postgres_inc)
1109       perl_may_work = false
1110       perl_msg = 'missing libperl'
1111     endif
1113   endif # perl_may_work
1115   if perl_may_work
1116     perl_dep = perl_dep_int
1117   else
1118     if perlopt.enabled()
1119       error('dependency plperl failed: @0@'.format(perl_msg))
1120     else
1121       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1122     endif
1123   endif
1124 endif
1128 ###############################################################
1129 # Library: Python (for plpython)
1130 ###############################################################
1132 pyopt = get_option('plpython')
1133 python3_dep = not_found_dep
1134 if not pyopt.disabled()
1135   pm = import('python')
1136   python3_inst = pm.find_installation(python.path(), required: pyopt)
1137   if python3_inst.found()
1138     python3_dep = python3_inst.dependency(embed: true, required: pyopt)
1139     # Remove this check after we depend on Meson >= 1.1.0
1140     if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt, include_directories: postgres_inc)
1141       python3_dep = not_found_dep
1142     endif
1143   endif
1144 endif
1148 ###############################################################
1149 # Library: Readline
1150 ###############################################################
1152 if not get_option('readline').disabled()
1153   libedit_preferred = get_option('libedit_preferred')
1154   # Set the order of readline dependencies.
1155   # cc.find_library breaks and throws on the first dependency which
1156   # is marked as required=true and can't be found. Thus, we only mark
1157   # the last dependency to look up as required, to not throw too early.
1158   check_readline_deps = [
1159     {
1160       'name': libedit_preferred ? 'libedit' : 'readline',
1161       'required': false
1162     },
1163     {
1164       'name': libedit_preferred ? 'readline' : 'libedit',
1165       'required': get_option('readline')
1166     }
1167   ]
1169   foreach readline_dep : check_readline_deps
1170     readline = dependency(readline_dep['name'], required: false)
1171     if not readline.found()
1172       readline = cc.find_library(readline_dep['name'],
1173         required: readline_dep['required'],
1174         dirs: test_lib_d)
1175     endif
1176     if readline.found()
1177       break
1178     endif
1179   endforeach
1181   if readline.found()
1182     cdata.set('HAVE_LIBREADLINE', 1)
1184     editline_prefix = {
1185       'header_prefix': 'editline/',
1186       'flag_prefix': 'EDITLINE_',
1187     }
1188     readline_prefix = {
1189       'header_prefix': 'readline/',
1190       'flag_prefix': 'READLINE_',
1191     }
1192     default_prefix = {
1193       'header_prefix': '',
1194       'flag_prefix': '',
1195     }
1197     # Set the order of prefixes
1198     prefixes = libedit_preferred ? \
1199       [editline_prefix, default_prefix, readline_prefix] : \
1200       [readline_prefix, default_prefix, editline_prefix]
1202     at_least_one_header_found = false
1203     foreach header : ['history', 'readline']
1204       is_found = false
1205       foreach prefix : prefixes
1206         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1207         # Check history.h and readline.h
1208         if not is_found and cc.has_header(header_file,
1209             args: test_c_args, include_directories: postgres_inc,
1210             dependencies: [readline], required: false)
1211           if header == 'readline'
1212             readline_h = header_file
1213           endif
1214           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1215           is_found = true
1216           at_least_one_header_found = true
1217         endif
1218       endforeach
1219     endforeach
1221     if not at_least_one_header_found
1222       error('''readline header not found
1223 If you have @0@ already installed, see meson-logs/meson-log.txt for details on the
1224 failure. It is possible the compiler isn't looking in the proper directory.
1225 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1226     endif
1228     check_funcs = [
1229       'append_history',
1230       'history_truncate_file',
1231       'rl_completion_matches',
1232       'rl_filename_completion_function',
1233       'rl_reset_screen_size',
1234       'rl_variable_bind',
1235     ]
1237     foreach func : check_funcs
1238       found = cc.has_function(func, dependencies: [readline],
1239         args: test_c_args, include_directories: postgres_inc)
1240       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1241     endforeach
1243     check_vars = [
1244       'rl_completion_suppress_quote',
1245       'rl_filename_quote_characters',
1246       'rl_filename_quoting_function',
1247     ]
1249     foreach var : check_vars
1250       cdata.set('HAVE_' + var.to_upper(),
1251         cc.has_header_symbol(readline_h, var,
1252           args: test_c_args, include_directories: postgres_inc,
1253           prefix: '#include <stdio.h>',
1254           dependencies: [readline]) ? 1 : false)
1255     endforeach
1257     # If found via cc.find_library() ensure headers are found when using the
1258     # dependency. On meson < 0.57 one cannot do compiler checks using the
1259     # dependency returned by declare_dependency(), so we can't do this above.
1260     if readline.type_name() == 'library'
1261       readline = declare_dependency(dependencies: readline,
1262         include_directories: postgres_inc)
1263     endif
1265     # On windows with mingw readline requires auto-import to successfully
1266     # link, as the headers don't use declspec(dllimport)
1267     if host_system == 'windows' and cc.get_id() != 'msvc'
1268       readline = declare_dependency(dependencies: readline,
1269         link_args: '-Wl,--enable-auto-import')
1270     endif
1271   endif
1273   # XXX: Figure out whether to implement mingw warning equivalent
1274 else
1275   readline = not_found_dep
1276 endif
1280 ###############################################################
1281 # Library: selinux
1282 ###############################################################
1284 selinux = not_found_dep
1285 selinuxopt = get_option('selinux')
1286 if meson.version().version_compare('>=0.59')
1287   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1288 endif
1289 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1290 cdata.set('HAVE_LIBSELINUX',
1291   selinux.found() ? 1 : false)
1295 ###############################################################
1296 # Library: systemd
1297 ###############################################################
1299 systemd = not_found_dep
1300 systemdopt = get_option('systemd')
1301 if meson.version().version_compare('>=0.59')
1302   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1303 endif
1304 systemd = dependency('libsystemd', required: systemdopt)
1305 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1309 ###############################################################
1310 # Library: SSL
1311 ###############################################################
1313 ssl = not_found_dep
1314 ssl_library = 'none'
1315 sslopt = get_option('ssl')
1317 if sslopt == 'auto' and auto_features.disabled()
1318   sslopt = 'none'
1319 endif
1321 if sslopt in ['auto', 'openssl']
1322   openssl_required = (sslopt == 'openssl')
1324   # Try to find openssl via pkg-config et al, if that doesn't work
1325   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1326   # the library names that we know about.
1328   # via pkg-config et al
1329   ssl = dependency('openssl', required: false)
1330   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1331   # we pass cc.find_library() results if necessary
1332   ssl_int = []
1334   # via library + headers
1335   if not ssl.found()
1336     ssl_lib = cc.find_library('ssl',
1337       dirs: test_lib_d,
1338       header_include_directories: postgres_inc,
1339       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1340       required: openssl_required)
1341     crypto_lib = cc.find_library('crypto',
1342       dirs: test_lib_d,
1343       required: openssl_required)
1344     if ssl_lib.found() and crypto_lib.found()
1345       ssl_int = [ssl_lib, crypto_lib]
1346       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1347     endif
1348   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1349        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1350     ssl_int = [ssl]
1351   else
1352     ssl = not_found_dep
1353   endif
1355   if ssl.found()
1356     check_funcs = [
1357       ['CRYPTO_new_ex_data', {'required': true}],
1358       ['SSL_new', {'required': true}],
1360       # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
1361       ['SSL_CTX_set_cert_cb'],
1363       # Functions introduced in OpenSSL 1.1.0. We used to check for
1364       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1365       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1366       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1367       # functions.
1368       ['OPENSSL_init_ssl'],
1369       ['BIO_meth_new'],
1370       ['ASN1_STRING_get0_data'],
1371       ['HMAC_CTX_new'],
1372       ['HMAC_CTX_free'],
1374       # OpenSSL versions before 1.1.0 required setting callback functions, for
1375       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1376       # function was removed.
1377       ['CRYPTO_lock'],
1379       # Function introduced in OpenSSL 1.1.1
1380       ['X509_get_signature_info'],
1381       ['SSL_CTX_set_num_tickets'],
1382     ]
1384     are_openssl_funcs_complete = true
1385     foreach c : check_funcs
1386       func = c.get(0)
1387       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1388       required = c.get(1, {}).get('required', false)
1389       if required and not val
1390         are_openssl_funcs_complete = false
1391         if openssl_required
1392           error('openssl function @0@ is required'.format(func))
1393         endif
1394         break
1395       elif not required
1396         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1397       endif
1398     endforeach
1400     if are_openssl_funcs_complete
1401       cdata.set('USE_OPENSSL', 1,
1402                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1403       cdata.set('OPENSSL_API_COMPAT', '0x10002000L',
1404                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1405       ssl_library = 'openssl'
1406     else
1407       ssl = not_found_dep
1408     endif
1409   endif
1410 endif
1412 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1413   error('no SSL library found')
1414 endif
1418 ###############################################################
1419 # Library: uuid
1420 ###############################################################
1422 uuidopt = get_option('uuid')
1423 if uuidopt != 'none'
1424   uuidname = uuidopt.to_upper()
1425   if uuidopt == 'e2fs'
1426     uuid = dependency('uuid', required: true)
1427     uuidfunc = 'uuid_generate'
1428     uuidheader = 'uuid/uuid.h'
1429   elif uuidopt == 'bsd'
1430     # libc should have uuid function
1431     uuid = declare_dependency()
1432     uuidfunc = 'uuid_to_string'
1433     uuidheader = 'uuid.h'
1434   elif uuidopt == 'ossp'
1435     # In upstream, the package and library is called just 'uuid', but many
1436     # distros change it to 'ossp-uuid'.
1437     uuid = dependency('ossp-uuid', 'uuid', required: false)
1438     uuidfunc = 'uuid_export'
1439     uuidheader = 'uuid.h'
1441     # Hardcoded lookup for ossp-uuid. This is necessary as ossp-uuid on
1442     # windows installs neither a pkg-config nor a cmake dependency
1443     # information. Nor is there another supported uuid implementation
1444     # available on windows.
1445     if not uuid.found()
1446       uuid = cc.find_library('ossp-uuid',
1447         required: false, dirs: test_lib_d,
1448         has_headers: uuidheader, header_include_directories: postgres_inc)
1449     endif
1450     if not uuid.found()
1451       uuid = cc.find_library('uuid',
1452         required: true, dirs: test_lib_d,
1453         has_headers: uuidheader, header_include_directories: postgres_inc)
1454     endif
1455   else
1456     error('unknown uuid build option value: @0@'.format(uuidopt))
1457   endif
1459   if not cc.has_header_symbol(uuidheader, uuidfunc,
1460                               args: test_c_args,
1461                               include_directories: postgres_inc,
1462                               dependencies: uuid)
1463     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1464   endif
1465   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1467   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1468            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1469 else
1470   uuid = not_found_dep
1471 endif
1475 ###############################################################
1476 # Library: zlib
1477 ###############################################################
1479 zlibopt = get_option('zlib')
1480 zlib = not_found_dep
1481 if not zlibopt.disabled()
1482   zlib_t = dependency('zlib', required: zlibopt)
1484   if zlib_t.type_name() == 'internal'
1485     # if fallback was used, we don't need to test if headers are present (they
1486     # aren't built yet, so we can't test)
1487     zlib = zlib_t
1488   elif not zlib_t.found()
1489     warning('did not find zlib')
1490   elif not cc.has_header('zlib.h',
1491       args: test_c_args, include_directories: postgres_inc,
1492       dependencies: [zlib_t], required: zlibopt)
1493     warning('zlib header not found')
1494   else
1495     zlib = zlib_t
1496   endif
1498   if zlib.found()
1499     cdata.set('HAVE_LIBZ', 1)
1500   endif
1501 endif
1505 ###############################################################
1506 # Library: tap test dependencies
1507 ###############################################################
1509 # Check whether tap tests are enabled or not
1510 tap_tests_enabled = false
1511 tapopt = get_option('tap_tests')
1512 if not tapopt.disabled()
1513   # Checking for perl modules for tap tests
1514   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1515   if perl_ipc_run_check.returncode() != 0
1516     message(perl_ipc_run_check.stderr().strip())
1517     if tapopt.enabled()
1518       error('Additional Perl modules are required to run TAP tests.')
1519     else
1520       warning('Additional Perl modules are required to run TAP tests.')
1521     endif
1522   else
1523     tap_tests_enabled = true
1524   endif
1525 endif
1529 ###############################################################
1530 # Library: zstd
1531 ###############################################################
1533 zstdopt = get_option('zstd')
1534 if not zstdopt.disabled()
1535   zstd = dependency('libzstd', required: false, version: '>=1.4.0')
1536   # Unfortunately the dependency is named differently with cmake
1537   if not zstd.found() # combine with above once meson 0.60.0 is required
1538     zstd = dependency('zstd', required: zstdopt, version: '>=1.4.0',
1539                       method: 'cmake', modules: ['zstd::libzstd_shared'])
1540   endif
1542   if zstd.found()
1543     cdata.set('USE_ZSTD', 1)
1544     cdata.set('HAVE_LIBZSTD', 1)
1545   endif
1547 else
1548   zstd = not_found_dep
1549 endif
1553 ###############################################################
1554 # Compiler tests
1555 ###############################################################
1557 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1558 # unnecessarily, because we optionally rely on newer features.
1559 c99_test = '''
1560 #include <stdbool.h>
1561 #include <complex.h>
1562 #include <tgmath.h>
1563 #include <inttypes.h>
1565 struct named_init_test {
1566   int a;
1567   int b;
1570 extern void structfunc(struct named_init_test);
1572 int main(int argc, char **argv)
1574   struct named_init_test nit = {
1575     .a = 3,
1576     .b = 5,
1577   };
1579   for (int loop_var = 0; loop_var < 3; loop_var++)
1580   {
1581     nit.a += nit.b;
1582   }
1584   structfunc((struct named_init_test){1, 0});
1586   return nit.a != 0;
1590 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1591   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1592         args: test_c_args + ['-std=c99'])
1593     test_c_args += '-std=c99'
1594     cflags += '-std=c99'
1595   else
1596     error('C compiler does not support C99')
1597   endif
1598 endif
1600 sizeof_long = cc.sizeof('long', args: test_c_args)
1601 cdata.set('SIZEOF_LONG', sizeof_long)
1602 if sizeof_long == 8
1603   cdata.set('HAVE_LONG_INT_64', 1)
1604   pg_int64_type = 'long int'
1605   cdata.set_quoted('INT64_MODIFIER', 'l')
1606 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1607   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1608   pg_int64_type = 'long long int'
1609   cdata.set_quoted('INT64_MODIFIER', 'll')
1610 else
1611   error('do not know how to get a 64bit int')
1612 endif
1613 cdata.set('PG_INT64_TYPE', pg_int64_type)
1615 if host_machine.endian() == 'big'
1616   cdata.set('WORDS_BIGENDIAN', 1)
1617 endif
1619 # Determine memory alignment requirements for the basic C data types.
1621 alignof_types = ['short', 'int', 'long', 'double']
1622 foreach t : alignof_types
1623   align = cc.alignment(t, args: test_c_args)
1624   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1625 endforeach
1627 # Compute maximum alignment of any basic type.
1629 # We require 'double' to have the strictest alignment among the basic types,
1630 # because otherwise the C ABI might impose 8-byte alignment on some of the
1631 # other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
1632 # cause a mismatch between the tuple layout and the C struct layout of a
1633 # catalog tuple.  We used to carefully order catalog columns such that any
1634 # fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
1635 # of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
1636 # where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
1638 # We assume without checking that int64's alignment is at least as strong
1639 # as long, char, short, or int.  Note that we intentionally do not consider
1640 # any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8
1641 # would be too much of a penalty for disk and memory space.
1642 alignof_double = cdata.get('ALIGNOF_DOUBLE')
1643 if cc.alignment(pg_int64_type, args: test_c_args) > alignof_double
1644   error('alignment of int64 is greater than the alignment of double')
1645 endif
1646 cdata.set('MAXIMUM_ALIGNOF', alignof_double)
1648 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1649 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1652 # Check if __int128 is a working 128 bit integer type, and if so
1653 # define PG_INT128_TYPE to that typename.
1655 # This currently only detects a GCC/clang extension, but support for other
1656 # environments may be added in the future.
1658 # For the moment we only test for support for 128bit math; support for
1659 # 128bit literals and snprintf is not required.
1660 if cc.links('''
1661   /*
1662    * We don't actually run this test, just link it to verify that any support
1663    * functions needed for __int128 are present.
1664    *
1665    * These are globals to discourage the compiler from folding all the
1666    * arithmetic tests down to compile-time constants.  We do not have
1667    * convenient support for 128bit literals at this point...
1668    */
1669   __int128 a = 48828125;
1670   __int128 b = 97656250;
1672   int main(void)
1673   {
1674       __int128 c,d;
1675       a = (a << 12) + 1; /* 200000000001 */
1676       b = (b << 12) + 5; /* 400000000005 */
1677       /* try the most relevant arithmetic ops */
1678       c = a * b;
1679       d = (c + b) / b;
1680       /* must use the results, else compiler may optimize arithmetic away */
1681       return d != a+1;
1682   }''',
1683   name: '__int128',
1684   args: test_c_args)
1686   buggy_int128 = false
1688   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1689   # If not cross-compiling, we can test for bugs and disable use of __int128
1690   # with buggy compilers.  If cross-compiling, hope for the best.
1691   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1692   if not meson.is_cross_build()
1693     r = cc.run('''
1694     /* This must match the corresponding code in c.h: */
1695     #if defined(__GNUC__) || defined(__SUNPRO_C)
1696     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1697     #elif defined(_MSC_VER)
1698     #define pg_attribute_aligned(a) __declspec(align(a))
1699     #endif
1700     typedef __int128 int128a
1701     #if defined(pg_attribute_aligned)
1702     pg_attribute_aligned(8)
1703     #endif
1704     ;
1706     int128a holder;
1707     void pass_by_val(void *buffer, int128a par) { holder = par; }
1709     int main(void)
1710     {
1711         long int i64 = 97656225L << 12;
1712         int128a q;
1713         pass_by_val(main, (int128a) i64);
1714         q = (int128a) i64;
1715         return q != holder;
1716     }''',
1717     name: '__int128 alignment bug',
1718     args: test_c_args)
1719     assert(r.compiled())
1720     if r.returncode() != 0
1721       buggy_int128 = true
1722       message('__int128 support present but buggy and thus disabled')
1723     endif
1724   endif
1726   if not buggy_int128
1727     cdata.set('PG_INT128_TYPE', '__int128')
1728     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1729   endif
1730 endif
1733 # Check if the C compiler knows computed gotos (gcc extension, also
1734 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1736 # Checking whether computed gotos are supported syntax-wise ought to
1737 # be enough, as the syntax is otherwise illegal.
1738 if cc.compiles('''
1739     static inline int foo(void)
1740     {
1741       void *labeladdrs[] = {&&my_label};
1742       goto *labeladdrs[0];
1743       my_label:
1744       return 1;
1745     }''',
1746     args: test_c_args)
1747   cdata.set('HAVE_COMPUTED_GOTO', 1)
1748 endif
1751 # Check if the C compiler understands _Static_assert(),
1752 # and define HAVE__STATIC_ASSERT if so.
1754 # We actually check the syntax ({ _Static_assert(...) }), because we need
1755 # gcc-style compound expressions to be able to wrap the thing into macros.
1756 if cc.compiles('''
1757     int main(int arg, char **argv)
1758     {
1759         ({ _Static_assert(1, "foo"); });
1760     }
1761     ''',
1762     args: test_c_args)
1763   cdata.set('HAVE__STATIC_ASSERT', 1)
1764 endif
1767 # We use <stdbool.h> if we have it and it declares type bool as having
1768 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1769 if cc.has_type('_Bool', args: test_c_args) \
1770     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1771     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1772   cdata.set('HAVE__BOOL', 1)
1773   cdata.set('PG_USE_STDBOOL', 1)
1774 endif
1777 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1778 # warning for each use of %m.
1779 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1780 testsrc = '''
1781 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1782 static void call_log(void)
1784     emit_log(0, "error: %s: %m", "foo");
1787 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1788 foreach a : printf_attributes
1789   if cc.compiles(testsrc.format(a),
1790       args: test_c_args + attrib_error_args, name: 'format ' + a)
1791     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1792     break
1793   endif
1794 endforeach
1797 if cc.has_function_attribute('visibility:default') and \
1798     cc.has_function_attribute('visibility:hidden')
1799   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1801   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1802   # inlineshidden to C code as well... And either way, we want to put these
1803   # flags into exported files (pgxs, .pc files).
1804   cflags_mod += '-fvisibility=hidden'
1805   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1806   ldflags_mod += '-fvisibility=hidden'
1807 endif
1810 # Check if various builtins exist. Some builtins are tested separately,
1811 # because we want to test something more complicated than the generic case.
1812 builtins = [
1813   'bswap16',
1814   'bswap32',
1815   'bswap64',
1816   'clz',
1817   'ctz',
1818   'constant_p',
1819   'frame_address',
1820   'popcount',
1821   'unreachable',
1824 foreach builtin : builtins
1825   fname = '__builtin_@0@'.format(builtin)
1826   if cc.has_function(fname, args: test_c_args)
1827     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1828   endif
1829 endforeach
1832 # Check if the C compiler understands __builtin_types_compatible_p,
1833 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1835 # We check usage with __typeof__, though it's unlikely any compiler would
1836 # have the former and not the latter.
1837 if cc.compiles('''
1838     static int x;
1839     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1840     ''',
1841     name: '__builtin_types_compatible_p',
1842     args: test_c_args)
1843   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1844 endif
1847 # Check if the C compiler understands __builtin_$op_overflow(),
1848 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1850 # Check for the most complicated case, 64 bit multiplication, as a
1851 # proxy for all of the operations.  To detect the case where the compiler
1852 # knows the function but library support is missing, we must link not just
1853 # compile, and store the results in global variables so the compiler doesn't
1854 # optimize away the call.
1855 if cc.links('''
1856     INT64 a = 1;
1857     INT64 b = 1;
1858     INT64 result;
1860     int main(void)
1861     {
1862         return __builtin_mul_overflow(a, b, &result);
1863     }''',
1864     name: '__builtin_mul_overflow',
1865     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1866     )
1867   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1868 endif
1871 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1872 # here. To prevent problems due to two detection methods working, stop
1873 # checking after one.
1874 if cc.links('''
1875     #include <cpuid.h>
1876     int main(int arg, char **argv)
1877     {
1878         unsigned int exx[4] = {0, 0, 0, 0};
1879         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1880     }
1881     ''', name: '__get_cpuid',
1882     args: test_c_args)
1883   cdata.set('HAVE__GET_CPUID', 1)
1884 elif cc.links('''
1885     #include <intrin.h>
1886     int main(int arg, char **argv)
1887     {
1888         unsigned int exx[4] = {0, 0, 0, 0};
1889         __cpuid(exx, 1);
1890     }
1891     ''', name: '__cpuid',
1892     args: test_c_args)
1893   cdata.set('HAVE__CPUID', 1)
1894 endif
1897 # Check for __get_cpuid_count() and __cpuidex() in a similar fashion.
1898 if cc.links('''
1899     #include <cpuid.h>
1900     int main(int arg, char **argv)
1901     {
1902         unsigned int exx[4] = {0, 0, 0, 0};
1903         __get_cpuid_count(7, 0, &exx[0], &exx[1], &exx[2], &exx[3]);
1904     }
1905     ''', name: '__get_cpuid_count',
1906     args: test_c_args)
1907   cdata.set('HAVE__GET_CPUID_COUNT', 1)
1908 elif cc.links('''
1909     #include <intrin.h>
1910     int main(int arg, char **argv)
1911     {
1912         unsigned int exx[4] = {0, 0, 0, 0};
1913         __cpuidex(exx, 7, 0);
1914     }
1915     ''', name: '__cpuidex',
1916     args: test_c_args)
1917   cdata.set('HAVE__CPUIDEX', 1)
1918 endif
1921 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1922 # versions of clang do not understand -fexcess-precision=standard, the use of
1923 # x87 floating point operations leads to problems like isinf possibly returning
1924 # false for a value that is infinite when converted from the 80bit register to
1925 # the 8byte memory representation.
1927 # Only perform the test if the compiler doesn't understand
1928 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1929 # automatically.
1930 if '-fexcess-precision=standard' not in cflags
1931   if not cc.compiles('''
1932 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1933 choke me
1934 #endif''',
1935       name: '', args: test_c_args)
1936     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1937   endif
1938 endif
1942 ###############################################################
1943 # Compiler flags
1944 ###############################################################
1946 common_functional_flags = [
1947   # Disable strict-aliasing rules; needed for gcc 3.3+
1948   '-fno-strict-aliasing',
1949   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1950   '-fwrapv',
1951   '-fexcess-precision=standard',
1954 cflags += cc.get_supported_arguments(common_functional_flags)
1955 if llvm.found()
1956   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1957 endif
1959 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1960 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1962 common_warning_flags = [
1963   '-Wmissing-prototypes',
1964   '-Wpointer-arith',
1965   # Really don't want VLAs to be used in our dialect of C
1966   '-Werror=vla',
1967   # On macOS, complain about usage of symbols newer than the deployment target
1968   '-Werror=unguarded-availability-new',
1969   '-Wendif-labels',
1970   '-Wmissing-format-attribute',
1971   '-Wimplicit-fallthrough=3',
1972   '-Wcast-function-type',
1973   '-Wshadow=compatible-local',
1974   # This was included in -Wall/-Wformat in older GCC versions
1975   '-Wformat-security',
1978 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1979 if llvm.found()
1980   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1981 endif
1983 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1984 # the result for them
1985 cflags_no_decl_after_statement = []
1986 if cc.has_argument('-Wdeclaration-after-statement')
1987   cflags_warn += '-Wdeclaration-after-statement'
1988   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1989 endif
1992 # The following tests want to suppress various unhelpful warnings by adding
1993 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1994 # switches, so we have to test for the positive form and if that works,
1995 # add the negative form.
1997 negative_warning_flags = [
1998   # Suppress clang's unhelpful unused-command-line-argument warnings.
1999   'unused-command-line-argument',
2001   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
2002   # of warnings when building plperl because of usages in the Perl headers.
2003   'compound-token-split-by-macro',
2005   # Similarly disable useless truncation warnings from gcc 8+
2006   'format-truncation',
2007   'stringop-truncation',
2009   # Suppress clang 16's strict warnings about function casts
2010   'cast-function-type-strict',
2012   # To make warning_level=2 / -Wextra work, we'd need at least the following
2013   # 'clobbered',
2014   # 'missing-field-initializers',
2015   # 'sign-compare',
2016   # 'unused-parameter',
2019 foreach w : negative_warning_flags
2020   if cc.has_argument('-W' + w)
2021     cflags_warn += '-Wno-' + w
2022   endif
2023   if llvm.found() and cpp.has_argument('-W' + w)
2024     cxxflags_warn += '-Wno-' + w
2025   endif
2026 endforeach
2029 if cc.get_id() == 'msvc'
2030   cflags_warn += [
2031     '/wd4018', # signed/unsigned mismatch
2032     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
2033     '/wd4273', # inconsistent DLL linkage
2034     '/wd4101', # unreferenced local variable
2035     '/wd4102', # unreferenced label
2036     '/wd4090', # different 'modifier' qualifiers
2037     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
2038   ]
2040   cppflags += [
2041     '/DWIN32',
2042     '/DWINDOWS',
2043     '/D__WINDOWS__',
2044     '/D__WIN32__',
2045     '/D_CRT_SECURE_NO_DEPRECATE',
2046     '/D_CRT_NONSTDC_NO_DEPRECATE',
2047   ]
2049   # We never need export libraries. As link.exe reports their creation, they
2050   # are unnecessarily noisy. Similarly, we don't need import library for
2051   # modules, we only import them dynamically, and they're also noisy.
2052   ldflags += '/NOEXP'
2053   ldflags_mod += '/NOIMPLIB'
2054 endif
2057 # Compute flags that are built into Meson.  We need these to
2058 # substitute into Makefile.global and for pg_config.  We only compute
2059 # the flags for Unix-style compilers, since that's the only style that
2060 # would use Makefile.global or pg_config.
2062 # We don't use get_option('warning_level') here, because the other
2063 # warning levels are not useful with PostgreSQL source code.
2064 common_builtin_flags = ['-Wall']
2066 if get_option('debug')
2067   common_builtin_flags += ['-g']
2068 endif
2070 optimization = get_option('optimization')
2071 if optimization == '0'
2072   common_builtin_flags += ['-O0']
2073 elif optimization == '1'
2074   common_builtin_flags += ['-O1']
2075 elif optimization == '2'
2076   common_builtin_flags += ['-O2']
2077 elif optimization == '3'
2078   common_builtin_flags += ['-O3']
2079 elif optimization == 's'
2080   common_builtin_flags += ['-Os']
2081 endif
2083 cflags_builtin = cc.get_supported_arguments(common_builtin_flags)
2084 if llvm.found()
2085   cxxflags_builtin = cpp.get_supported_arguments(common_builtin_flags)
2086 endif
2090 ###############################################################
2091 # Atomics
2092 ###############################################################
2094 if not get_option('spinlocks')
2095   warning('Not using spinlocks will cause poor performance')
2096 else
2097   cdata.set('HAVE_SPINLOCKS', 1)
2098 endif
2100 if not get_option('atomics')
2101   warning('Not using atomics will cause poor performance')
2102 else
2103   # XXX: perhaps we should require some atomics support in this case these
2104   # days?
2105   cdata.set('HAVE_ATOMICS', 1)
2107   atomic_checks = [
2108     {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
2109      'desc': '__sync_lock_test_and_set(char)',
2110      'test': '''
2111 char lock = 0;
2112 __sync_lock_test_and_set(&lock, 1);
2113 __sync_lock_release(&lock);'''},
2115     {'name': 'HAVE_GCC__SYNC_INT32_TAS',
2116      'desc': '__sync_lock_test_and_set(int32)',
2117      'test': '''
2118 int lock = 0;
2119 __sync_lock_test_and_set(&lock, 1);
2120 __sync_lock_release(&lock);'''},
2122     {'name': 'HAVE_GCC__SYNC_INT32_CAS',
2123      'desc': '__sync_val_compare_and_swap(int32)',
2124      'test': '''
2125 int val = 0;
2126 __sync_val_compare_and_swap(&val, 0, 37);'''},
2128     {'name': 'HAVE_GCC__SYNC_INT64_CAS',
2129      'desc': '__sync_val_compare_and_swap(int64)',
2130      'test': '''
2131 INT64 val = 0;
2132 __sync_val_compare_and_swap(&val, 0, 37);'''},
2134     {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
2135      'desc': ' __atomic_compare_exchange_n(int32)',
2136      'test': '''
2137 int val = 0;
2138 int expect = 0;
2139 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
2141     {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
2142      'desc': ' __atomic_compare_exchange_n(int64)',
2143      'test': '''
2144 INT64 val = 0;
2145 INT64 expect = 0;
2146 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
2147   ]
2149   foreach check : atomic_checks
2150     test = '''
2151 int main(void)
2154 }'''.format(check['test'])
2156     cdata.set(check['name'],
2157       cc.links(test,
2158         name: check['desc'],
2159         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
2160     )
2161   endforeach
2163 endif
2166 ###############################################################
2167 # Check for the availability of XSAVE intrinsics.
2168 ###############################################################
2170 cflags_xsave = []
2171 if host_cpu == 'x86' or host_cpu == 'x86_64'
2173   prog = '''
2174 #include <immintrin.h>
2176 int main(void)
2178     return _xgetbv(0) & 0xe0;
2182   if cc.links(prog, name: 'XSAVE intrinsics without -mxsave',
2183         args: test_c_args)
2184     cdata.set('HAVE_XSAVE_INTRINSICS', 1)
2185   elif cc.links(prog, name: 'XSAVE intrinsics with -mxsave',
2186         args: test_c_args + ['-mxsave'])
2187     cdata.set('HAVE_XSAVE_INTRINSICS', 1)
2188     cflags_xsave += '-mxsave'
2189   endif
2191 endif
2194 ###############################################################
2195 # Check for the availability of AVX-512 popcount intrinsics.
2196 ###############################################################
2198 cflags_popcnt = []
2199 if host_cpu == 'x86_64'
2201   prog = '''
2202 #include <immintrin.h>
2204 int main(void)
2206     const char buf[sizeof(__m512i)];
2207     INT64 popcnt = 0;
2208     __m512i accum = _mm512_setzero_si512();
2209     const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
2210     const __m512i cnt = _mm512_popcnt_epi64(val);
2211     accum = _mm512_add_epi64(accum, cnt);
2212     popcnt = _mm512_reduce_add_epi64(accum);
2213     /* return computed value, to prevent the above being optimized away */
2214     return popcnt == 0;
2218   if cc.links(prog, name: 'AVX-512 popcount without -mavx512vpopcntdq -mavx512bw',
2219         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))])
2220     cdata.set('USE_AVX512_POPCNT_WITH_RUNTIME_CHECK', 1)
2221   elif cc.links(prog, name: 'AVX-512 popcount with -mavx512vpopcntdq -mavx512bw',
2222         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))] + ['-mavx512vpopcntdq'] + ['-mavx512bw'])
2223     cdata.set('USE_AVX512_POPCNT_WITH_RUNTIME_CHECK', 1)
2224     cflags_popcnt += ['-mavx512vpopcntdq'] + ['-mavx512bw']
2225   endif
2227 endif
2230 ###############################################################
2231 # Select CRC-32C implementation.
2233 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
2234 # use the special CRC instructions for calculating CRC-32C. If we're not
2235 # targeting such a processor, but we can nevertheless produce code that uses
2236 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
2237 # implementations and select which one to use at runtime, depending on whether
2238 # SSE 4.2 is supported by the processor we're running on.
2240 # Similarly, if we are targeting an ARM processor that has the CRC
2241 # instructions that are part of the ARMv8 CRC Extension, use them. And if
2242 # we're not targeting such a processor, but can nevertheless produce code that
2243 # uses the CRC instructions, compile both, and select at runtime.
2244 ###############################################################
2246 have_optimized_crc = false
2247 cflags_crc = []
2248 if host_cpu == 'x86' or host_cpu == 'x86_64'
2250   if cc.get_id() == 'msvc'
2251     cdata.set('USE_SSE42_CRC32C', false)
2252     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2253     have_optimized_crc = true
2254   else
2256     prog = '''
2257 #include <nmmintrin.h>
2259 int main(void)
2261     unsigned int crc = 0;
2262     crc = _mm_crc32_u8(crc, 0);
2263     crc = _mm_crc32_u32(crc, 0);
2264     /* return computed value, to prevent the above being optimized away */
2265     return crc == 0;
2269     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2270           args: test_c_args)
2271       # Use Intel SSE 4.2 unconditionally.
2272       cdata.set('USE_SSE42_CRC32C', 1)
2273       have_optimized_crc = true
2274     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2275           args: test_c_args + ['-msse4.2'])
2276       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2277       # the runtime check.
2278       cflags_crc += '-msse4.2'
2279       cdata.set('USE_SSE42_CRC32C', false)
2280       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2281       have_optimized_crc = true
2282     endif
2284   endif
2286 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2288   prog = '''
2289 #include <arm_acle.h>
2291 int main(void)
2293     unsigned int crc = 0;
2294     crc = __crc32cb(crc, 0);
2295     crc = __crc32ch(crc, 0);
2296     crc = __crc32cw(crc, 0);
2297     crc = __crc32cd(crc, 0);
2299     /* return computed value, to prevent the above being optimized away */
2300     return crc == 0;
2304   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2305       args: test_c_args)
2306     # Use ARM CRC Extension unconditionally
2307     cdata.set('USE_ARMV8_CRC32C', 1)
2308     have_optimized_crc = true
2309   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2310       args: test_c_args + ['-march=armv8-a+crc'])
2311     # Use ARM CRC Extension, with runtime check
2312     cflags_crc += '-march=armv8-a+crc'
2313     cdata.set('USE_ARMV8_CRC32C', false)
2314     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2315     have_optimized_crc = true
2316   endif
2318 elif host_cpu == 'loongarch64'
2320   prog = '''
2321 int main(void)
2323     unsigned int crc = 0;
2324     crc = __builtin_loongarch_crcc_w_b_w(0, crc);
2325     crc = __builtin_loongarch_crcc_w_h_w(0, crc);
2326     crc = __builtin_loongarch_crcc_w_w_w(0, crc);
2327     crc = __builtin_loongarch_crcc_w_d_w(0, crc);
2329     /* return computed value, to prevent the above being optimized away */
2330     return crc == 0;
2334   if cc.links(prog, name: '__builtin_loongarch_crcc_w_b_w, __builtin_loongarch_crcc_w_h_w, __builtin_loongarch_crcc_w_w_w, and __builtin_loongarch_crcc_w_d_w',
2335       args: test_c_args)
2336     # Use LoongArch CRC instruction unconditionally
2337     cdata.set('USE_LOONGARCH_CRC32C', 1)
2338     have_optimized_crc = true
2339   endif
2341 endif
2343 if not have_optimized_crc
2344   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2345   # support.
2346   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2347 endif
2351 ###############################################################
2352 # Other CPU specific stuff
2353 ###############################################################
2355 if host_cpu == 'x86_64'
2357   if cc.compiles('''
2358       void main(void)
2359       {
2360           long long x = 1; long long r;
2361           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2362       }''',
2363       name: '@0@: popcntq instruction'.format(host_cpu),
2364       args: test_c_args)
2365     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2366   endif
2368 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2369   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2370   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2371     if cc.compiles('''
2372       static inline int
2373       addi(int ra, int si)
2374       {
2375           int res = 0;
2376           if (__builtin_constant_p(si))
2377               __asm__ __volatile__(
2378                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2379           return res;
2380       }
2381       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2382       ''',
2383       args: test_c_args)
2384       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2385     endif
2386   endif
2387 endif
2391 ###############################################################
2392 # Library / OS tests
2393 ###############################################################
2395 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2396 # unnecessary checks over and over, particularly on windows.
2397 header_checks = [
2398   'atomic.h',
2399   'copyfile.h',
2400   'crtdefs.h',
2401   'execinfo.h',
2402   'getopt.h',
2403   'ifaddrs.h',
2404   'langinfo.h',
2405   'mbarrier.h',
2406   'stdbool.h',
2407   'strings.h',
2408   'sys/epoll.h',
2409   'sys/event.h',
2410   'sys/personality.h',
2411   'sys/prctl.h',
2412   'sys/procctl.h',
2413   'sys/signalfd.h',
2414   'sys/ucred.h',
2415   'termios.h',
2416   'ucred.h',
2419 foreach header : header_checks
2420   varname = 'HAVE_' + header.underscorify().to_upper()
2422   # Emulate autoconf behaviour of not-found->undef, found->1
2423   found = cc.has_header(header,
2424     include_directories: postgres_inc, args: test_c_args)
2425   cdata.set(varname, found ? 1 : false,
2426             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2427 endforeach
2430 decl_checks = [
2431   ['F_FULLFSYNC', 'fcntl.h'],
2432   ['fdatasync', 'unistd.h'],
2433   ['posix_fadvise', 'fcntl.h'],
2434   ['strlcat', 'string.h'],
2435   ['strlcpy', 'string.h'],
2436   ['strnlen', 'string.h'],
2439 # Need to check for function declarations for these functions, because
2440 # checking for library symbols wouldn't handle deployment target
2441 # restrictions on macOS
2442 decl_checks += [
2443   ['preadv', 'sys/uio.h'],
2444   ['pwritev', 'sys/uio.h'],
2447 # Check presence of some optional LLVM functions.
2448 if llvm.found()
2449   decl_checks += [
2450     ['LLVMCreateGDBRegistrationListener', 'llvm-c/ExecutionEngine.h'],
2451     ['LLVMCreatePerfJITEventListener', 'llvm-c/ExecutionEngine.h'],
2452   ]
2453 endif
2455 foreach c : decl_checks
2456   func = c.get(0)
2457   header = c.get(1)
2458   args = c.get(2, {})
2459   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2461   found = cc.has_header_symbol(header, func,
2462     args: test_c_args, include_directories: postgres_inc,
2463     kwargs: args)
2464   cdata.set10(varname, found, description:
2465 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2466    don't.'''.format(func))
2467 endforeach
2470 if cc.has_type('struct option',
2471     args: test_c_args, include_directories: postgres_inc,
2472     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2473   cdata.set('HAVE_STRUCT_OPTION', 1)
2474 endif
2477 foreach c : ['opterr', 'optreset']
2478   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2480   if cc.links('''
2481 #include <unistd.h>
2482 int main(void)
2484     extern int @0@;
2485     @0@ = 1;
2487 '''.format(c), name: c, args: test_c_args)
2488     cdata.set(varname, 1)
2489   else
2490     cdata.set(varname, false)
2491   endif
2492 endforeach
2494 if cc.has_type('socklen_t',
2495     args: test_c_args, include_directories: postgres_inc,
2496     prefix: '''
2497 #include <sys/socket.h>''')
2498   cdata.set('HAVE_SOCKLEN_T', 1)
2499 endif
2501 if cc.has_member('struct sockaddr', 'sa_len',
2502     args: test_c_args, include_directories: postgres_inc,
2503     prefix: '''
2504 #include <sys/types.h>
2505 #include <sys/socket.h>''')
2506   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2507 endif
2509 if cc.has_member('struct tm', 'tm_zone',
2510     args: test_c_args, include_directories: postgres_inc,
2511     prefix: '''
2512 #include <sys/types.h>
2513 #include <time.h>
2514 ''')
2515   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2516 endif
2518 if cc.compiles('''
2519 #include <time.h>
2520 extern int foo(void);
2521 int foo(void)
2523     return timezone / 60;
2525 ''',
2526     name: 'global variable `timezone\' exists',
2527     args: test_c_args, include_directories: postgres_inc)
2528   cdata.set('HAVE_INT_TIMEZONE', 1)
2529 else
2530   cdata.set('HAVE_INT_TIMEZONE', false)
2531 endif
2533 if cc.has_type('union semun',
2534     args: test_c_args,
2535     include_directories: postgres_inc,
2536     prefix: '''
2537 #include <sys/types.h>
2538 #include <sys/ipc.h>
2539 #include <sys/sem.h>
2540 ''')
2541   cdata.set('HAVE_UNION_SEMUN', 1)
2542 endif
2544 if cc.compiles('''
2545 #include <string.h>
2546 int main(void)
2548   char buf[100];
2549   switch (strerror_r(1, buf, sizeof(buf)))
2550   { case 0: break; default: break; }
2551 }''',
2552     name: 'strerror_r',
2553     args: test_c_args, include_directories: postgres_inc)
2554   cdata.set('STRERROR_R_INT', 1)
2555 else
2556   cdata.set('STRERROR_R_INT', false)
2557 endif
2559 # Find the right header file for the locale_t type.  macOS needs xlocale.h;
2560 # standard is locale.h, but glibc <= 2.25 also had an xlocale.h file that
2561 # we should not use so we check the standard header first.  MSVC has a
2562 # replacement defined in src/include/port/win32_port.h.
2563 if not cc.has_type('locale_t', prefix: '#include <locale.h>') and \
2564    cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2565   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2566 endif
2568 # Check if the C compiler understands typeof or a variant.  Define
2569 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2570 foreach kw : ['typeof', '__typeof__', 'decltype']
2571   if cc.compiles('''
2572 int main(void)
2574     int x = 0;
2575     @0@(x) y;
2576     y = x;
2577     return y;
2579 '''.format(kw),
2580     name: 'typeof()',
2581     args: test_c_args, include_directories: postgres_inc)
2583     cdata.set('HAVE_TYPEOF', 1)
2584     if kw != 'typeof'
2585       cdata.set('typeof', kw)
2586     endif
2588     break
2589   endif
2590 endforeach
2593 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2594 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2595 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2596 wcstombs_l_test = '''
2597 #include <stdlib.h>
2598 #include <locale.h>
2601 void main(void)
2603 #ifndef wcstombs_l
2604     (void) wcstombs_l;
2605 #endif
2608 if (not cc.compiles(wcstombs_l_test.format(''),
2609       name: 'wcstombs_l') and
2610     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2611       name: 'wcstombs_l in xlocale.h'))
2612     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2613 endif
2616 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2617 # understands, because it conflicts with __declspec(restrict). Therefore we
2618 # define pg_restrict to the appropriate definition, which presumably won't
2619 # conflict.
2621 # We assume C99 support, so we don't need to make this conditional.
2622 cdata.set('pg_restrict', '__restrict')
2625 # Most libraries are included only if they demonstrably provide a function we
2626 # need, but libm is an exception: always include it, because there are too
2627 # many compilers that play cute optimization games that will break probes for
2628 # standard functions such as pow().
2629 os_deps += cc.find_library('m', required: false)
2631 rt_dep = cc.find_library('rt', required: false)
2633 dl_dep = cc.find_library('dl', required: false)
2635 util_dep = cc.find_library('util', required: false)
2637 getopt_dep = cc.find_library('getopt', required: false)
2638 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2639 # Check if we want to replace getopt/getopt_long even if provided by the system
2640 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2641 #   so always use our version on Windows
2642 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2643 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2644 # - We want to use system's getopt_long() only if the system provides struct
2645 #   option
2646 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2647 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2649 # Required on BSDs
2650 execinfo_dep = cc.find_library('execinfo', required: false)
2652 if host_system == 'cygwin'
2653   cygipc_dep = cc.find_library('cygipc', required: false)
2654 else
2655   cygipc_dep = not_found_dep
2656 endif
2658 if host_system == 'sunos'
2659   socket_dep = cc.find_library('socket', required: false)
2660 else
2661   socket_dep = not_found_dep
2662 endif
2664 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2665 # unnecessary checks over and over, particularly on windows.
2666 func_checks = [
2667   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2668   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2669   ['clock_gettime', {'dependencies': [rt_dep], 'define': false}],
2670   ['copyfile'],
2671   ['copy_file_range'],
2672   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2673   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2674   # required. Just checking for dlsym() ought to suffice.
2675   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2676   ['explicit_bzero'],
2677   ['getifaddrs'],
2678   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2679   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2680   ['getpeereid'],
2681   ['getpeerucred'],
2682   ['inet_aton'],
2683   ['inet_pton'],
2684   ['kqueue'],
2685   ['mbstowcs_l'],
2686   ['memset_s'],
2687   ['mkdtemp'],
2688   ['posix_fadvise'],
2689   ['posix_fallocate'],
2690   ['ppoll'],
2691   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2692   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2693   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2694   ['setproctitle', {'dependencies': [util_dep]}],
2695   ['setproctitle_fast'],
2696   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2697   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2698   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2699   ['socket', {'dependencies': [socket_dep], 'define': false}],
2700   ['strchrnul'],
2701   ['strerror_r', {'dependencies': [thread_dep]}],
2702   ['strlcat'],
2703   ['strlcpy'],
2704   ['strnlen'],
2705   ['strsignal'],
2706   ['sync_file_range'],
2707   ['syncfs'],
2708   ['uselocale'],
2709   ['wcstombs_l'],
2712 func_check_results = {}
2713 foreach c : func_checks
2714   func = c.get(0)
2715   kwargs = c.get(1, {})
2716   deps = kwargs.get('dependencies', [])
2718   if kwargs.get('skip', false)
2719     continue
2720   endif
2722   found = cc.has_function(func, args: test_c_args)
2724   if not found
2725     foreach dep : deps
2726       if not dep.found()
2727         continue
2728       endif
2729       found = cc.has_function(func, args: test_c_args,
2730                               dependencies: [dep])
2731       if found
2732         os_deps += dep
2733         break
2734       endif
2735     endforeach
2736   endif
2738   func_check_results += {func: found}
2740   if kwargs.get('define', true)
2741     # Emulate autoconf behaviour of not-found->undef, found->1
2742     cdata.set('HAVE_' + func.underscorify().to_upper(),
2743               found  ? 1 : false,
2744               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2745   endif
2746 endforeach
2749 if cc.has_function('syslog', args: test_c_args) and \
2750     cc.check_header('syslog.h', args: test_c_args)
2751   cdata.set('HAVE_SYSLOG', 1)
2752 endif
2755 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2756 # semaphores
2757 if sema_kind == 'unnamed_posix' and \
2758    not func_check_results.get('sem_init', false)
2759   sema_kind = 'sysv'
2760 endif
2762 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2763 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2765 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2766 cdata.set_quoted('DLSUFFIX', dlsuffix)
2769 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2770 cdata.set_quoted('PG_VERSION_STR',
2771   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2772     pg_version, host_machine.cpu_family(), host_system,
2773     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2774   )
2778 ###############################################################
2779 # NLS / Gettext
2780 ###############################################################
2782 nlsopt = get_option('nls')
2783 libintl = not_found_dep
2785 if not nlsopt.disabled()
2786   # otherwise there'd be lots of
2787   # "Gettext not found, all translation (po) targets will be ignored."
2788   # warnings if not found.
2789   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2791   # meson 0.59 has this wrapped in dependency('intl')
2792   if (msgfmt.found() and
2793       cc.check_header('libintl.h', required: nlsopt,
2794         args: test_c_args, include_directories: postgres_inc))
2796     # in libc
2797     if cc.has_function('ngettext')
2798       libintl = declare_dependency()
2799     else
2800       libintl = cc.find_library('intl',
2801         has_headers: ['libintl.h'], required: nlsopt,
2802         header_include_directories: postgres_inc,
2803         dirs: test_lib_d)
2804     endif
2805   endif
2807   if libintl.found()
2808     i18n = import('i18n')
2809     cdata.set('ENABLE_NLS', 1)
2810   endif
2811 endif
2815 ###############################################################
2816 # Build
2817 ###############################################################
2819 # Set up compiler / linker arguments to be used everywhere, individual targets
2820 # can add further args directly, or indirectly via dependencies
2821 add_project_arguments(cflags, language: ['c'])
2822 add_project_arguments(cppflags, language: ['c'])
2823 add_project_arguments(cflags_warn, language: ['c'])
2824 add_project_arguments(cxxflags, language: ['cpp'])
2825 add_project_arguments(cppflags, language: ['cpp'])
2826 add_project_arguments(cxxflags_warn, language: ['cpp'])
2827 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2830 # Collect a number of lists of things while recursing through the source
2831 # tree. Later steps then can use those.
2833 # list of targets for various alias targets
2834 backend_targets = []
2835 bin_targets = []
2836 pl_targets = []
2837 contrib_targets = []
2838 testprep_targets = []
2839 nls_targets = []
2842 # Define the tests to distribute them to the correct test styles later
2843 test_deps = []
2844 tests = []
2847 # Default options for targets
2849 # First identify rpaths
2850 bin_install_rpaths = []
2851 lib_install_rpaths = []
2852 mod_install_rpaths = []
2855 # Don't add rpaths on darwin for now - as long as only absolute references to
2856 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2857 # their final destination.
2858 if host_system != 'darwin'
2859   # Add absolute path to libdir to rpath. This ensures installed binaries /
2860   # libraries find our libraries (mainly libpq).
2861   bin_install_rpaths += dir_prefix / dir_lib
2862   lib_install_rpaths += dir_prefix / dir_lib
2863   mod_install_rpaths += dir_prefix / dir_lib
2865   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2866   #
2867   # Not needed on darwin even if we use relative rpaths for our own libraries,
2868   # as the install_name of libraries in extra_lib_dirs will point to their
2869   # location anyway.
2870   bin_install_rpaths += postgres_lib_d
2871   lib_install_rpaths += postgres_lib_d
2872   mod_install_rpaths += postgres_lib_d
2873 endif
2876 # Define arguments for default targets
2878 default_target_args = {
2879   'implicit_include_directories': false,
2880   'install': true,
2883 default_lib_args = default_target_args + {
2884   'name_prefix': '',
2887 internal_lib_args = default_lib_args + {
2888   'build_by_default': false,
2889   'install': false,
2892 default_mod_args = default_lib_args + {
2893   'name_prefix': '',
2894   'install_dir': dir_lib_pkg,
2897 default_bin_args = default_target_args + {
2898   'install_dir': dir_bin,
2901 if get_option('rpath')
2902   default_lib_args += {
2903     'install_rpath': ':'.join(lib_install_rpaths),
2904   }
2906   default_mod_args += {
2907     'install_rpath': ':'.join(mod_install_rpaths),
2908   }
2910   default_bin_args += {
2911     'install_rpath': ':'.join(bin_install_rpaths),
2912   }
2913 endif
2916 # Helper for exporting a limited number of symbols
2917 gen_export_kwargs = {
2918   'input': 'exports.txt',
2919   'output': '@BASENAME@.'+export_file_suffix,
2920   'command': [perl, files('src/tools/gen_export.pl'),
2921    '--format', export_file_format,
2922    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2923   'build_by_default': false,
2924   'install': false,
2930 ### Helpers for custom targets used across the tree
2933 catalog_pm = files('src/backend/catalog/Catalog.pm')
2934 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2935 gen_kwlist_deps = [perfect_hash_pm]
2936 gen_kwlist_cmd = [
2937   perl, '-I', '@SOURCE_ROOT@/src/tools',
2938   files('src/tools/gen_keywordlist.pl'),
2939   '--output', '@OUTDIR@', '@INPUT@']
2944 ### windows resources related stuff
2947 if host_system == 'windows'
2948   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2949   win32ver_rc = files('src/port/win32ver.rc')
2950   rcgen = find_program('src/tools/rcgen', native: true)
2952   rcgen_base_args = [
2953     '--srcdir', '@SOURCE_DIR@',
2954     '--builddir', meson.build_root(),
2955     '--rcout', '@OUTPUT0@',
2956     '--out', '@OUTPUT1@',
2957     '--input', '@INPUT@',
2958     '@EXTRA_ARGS@',
2959   ]
2961   if cc.get_argument_syntax() == 'msvc'
2962     rc = find_program('rc', required: true)
2963     rcgen_base_args += ['--rc', rc.path()]
2964     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2965   else
2966     windres = find_program('windres', required: true)
2967     rcgen_base_args += ['--windres', windres.path()]
2968     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2969   endif
2971   # msbuild backend doesn't support this atm
2972   if meson.backend() == 'ninja'
2973     rcgen_base_args += ['--depfile', '@DEPFILE@']
2974   endif
2976   rcgen_bin_args = rcgen_base_args + [
2977     '--VFT_TYPE', 'VFT_APP',
2978     '--FILEENDING', 'exe',
2979     '--ICO', pg_ico
2980   ]
2982   rcgen_lib_args = rcgen_base_args + [
2983     '--VFT_TYPE', 'VFT_DLL',
2984     '--FILEENDING', 'dll',
2985   ]
2987   rc_bin_gen = generator(rcgen,
2988     depfile: '@BASENAME@.d',
2989     arguments: rcgen_bin_args,
2990     output: rcgen_outputs,
2991   )
2993   rc_lib_gen = generator(rcgen,
2994     depfile: '@BASENAME@.d',
2995     arguments: rcgen_lib_args,
2996     output: rcgen_outputs,
2997   )
2998 endif
3002 # headers that the whole build tree depends on
3003 generated_headers = []
3004 # headers that the backend build depends on
3005 generated_backend_headers = []
3006 # configure_files() output, needs a way of converting to file names
3007 configure_files = []
3009 # generated files that might conflict with a partial in-tree autoconf build
3010 generated_sources = []
3011 # same, for paths that differ between autoconf / meson builds
3012 # elements are [dir, [files]]
3013 generated_sources_ac = {}
3016 # First visit src/include - all targets creating headers are defined
3017 # within. That makes it easy to add the necessary dependencies for the
3018 # subsequent build steps.
3020 subdir('src/include')
3022 subdir('config')
3024 # Then through src/port and src/common, as most other things depend on them
3026 frontend_port_code = declare_dependency(
3027   compile_args: ['-DFRONTEND'],
3028   include_directories: [postgres_inc],
3029   dependencies: os_deps,
3032 backend_port_code = declare_dependency(
3033   compile_args: ['-DBUILDING_DLL'],
3034   include_directories: [postgres_inc],
3035   sources: [errcodes], # errcodes.h is needed due to use of ereport
3036   dependencies: os_deps,
3039 subdir('src/port')
3041 frontend_common_code = declare_dependency(
3042   compile_args: ['-DFRONTEND'],
3043   include_directories: [postgres_inc],
3044   sources: generated_headers,
3045   dependencies: [os_deps, zlib, zstd],
3048 backend_common_code = declare_dependency(
3049   compile_args: ['-DBUILDING_DLL'],
3050   include_directories: [postgres_inc],
3051   sources: generated_headers,
3052   dependencies: [os_deps, zlib, zstd],
3055 subdir('src/common')
3057 # all shared libraries should depend on shlib_code
3058 shlib_code = declare_dependency(
3059   link_args: ldflags_sl,
3062 # all static libraries not part of the backend should depend on this
3063 frontend_stlib_code = declare_dependency(
3064   include_directories: [postgres_inc],
3065   link_with: [common_static, pgport_static],
3066   sources: generated_headers,
3067   dependencies: [os_deps, libintl],
3070 # all shared libraries not part of the backend should depend on this
3071 frontend_shlib_code = declare_dependency(
3072   include_directories: [postgres_inc],
3073   link_with: [common_shlib, pgport_shlib],
3074   sources: generated_headers,
3075   dependencies: [shlib_code, os_deps, libintl],
3078 # Dependencies both for static and shared libpq
3079 libpq_deps += [
3080   thread_dep,
3082   gssapi,
3083   ldap_r,
3084   libintl,
3085   ssl,
3088 subdir('src/interfaces/libpq')
3089 # fe_utils depends on libpq
3090 subdir('src/fe_utils')
3092 # for frontend binaries
3093 frontend_code = declare_dependency(
3094   include_directories: [postgres_inc],
3095   link_with: [fe_utils, common_static, pgport_static],
3096   sources: generated_headers,
3097   dependencies: [os_deps, libintl],
3100 backend_both_deps += [
3101   thread_dep,
3102   bsd_auth,
3103   gssapi,
3104   icu,
3105   icu_i18n,
3106   ldap,
3107   libintl,
3108   libxml,
3109   lz4,
3110   pam,
3111   ssl,
3112   systemd,
3113   zlib,
3114   zstd,
3117 backend_mod_deps = backend_both_deps + os_deps
3119 backend_code = declare_dependency(
3120   compile_args: ['-DBUILDING_DLL'],
3121   include_directories: [postgres_inc],
3122   link_args: ldflags_be,
3123   link_with: [],
3124   sources: generated_headers + generated_backend_headers,
3125   dependencies: os_deps + backend_both_deps + backend_deps,
3128 # install these files only during test, not main install
3129 test_install_data = []
3130 test_install_libs = []
3132 # src/backend/meson.build defines backend_mod_code used for extension
3133 # libraries.
3136 # Then through the main sources. That way contrib can have dependencies on
3137 # main sources. Note that this explicitly doesn't enter src/test, right now a
3138 # few regression tests depend on contrib files.
3140 subdir('src')
3142 subdir('contrib')
3144 subdir('src/test')
3145 subdir('src/interfaces/libpq/test')
3146 subdir('src/interfaces/ecpg/test')
3148 subdir('doc/src/sgml')
3150 generated_sources_ac += {'': ['GNUmakefile']}
3152 # After processing src/test, add test_install_libs to the testprep_targets
3153 # to build them
3154 testprep_targets += test_install_libs
3157 # If there are any files in the source directory that we also generate in the
3158 # build directory, they might get preferred over the newly generated files,
3159 # e.g. because of a #include "file", which always will search in the current
3160 # directory first.
3161 message('checking for file conflicts between source and build directory')
3162 conflicting_files = []
3163 potentially_conflicting_files_t = []
3164 potentially_conflicting_files_t += generated_headers
3165 potentially_conflicting_files_t += generated_backend_headers
3166 potentially_conflicting_files_t += generated_backend_sources
3167 potentially_conflicting_files_t += generated_sources
3169 potentially_conflicting_files = []
3171 # convert all sources of potentially conflicting files into uniform shape
3172 foreach t : potentially_conflicting_files_t
3173   potentially_conflicting_files += t.full_path()
3174 endforeach
3175 foreach t1 : configure_files
3176   if meson.version().version_compare('>=0.59')
3177     t = fs.parent(t1) / fs.name(t1)
3178   else
3179     t = '@0@'.format(t1)
3180   endif
3181   potentially_conflicting_files += meson.current_build_dir() / t
3182 endforeach
3183 foreach sub, fnames : generated_sources_ac
3184   sub = meson.build_root() / sub
3185   foreach fname : fnames
3186     potentially_conflicting_files += sub / fname
3187   endforeach
3188 endforeach
3190 # find and report conflicting files
3191 foreach build_path : potentially_conflicting_files
3192   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
3193   # str.replace is in 0.56
3194   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
3195   if fs.exists(src_path) or fs.is_symlink(src_path)
3196     conflicting_files += src_path
3197   endif
3198 endforeach
3199 # XXX: Perhaps we should generate a file that would clean these up? The list
3200 # can be long.
3201 if conflicting_files.length() > 0
3202   errmsg_cleanup = '''
3203 Conflicting files in source directory:
3204   @0@
3206 The conflicting files need to be removed, either by removing the files listed
3207 above, or by running configure and then make maintainer-clean.
3209   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
3210   error(errmsg_nonclean_base.format(errmsg_cleanup))
3211 endif
3215 ###############################################################
3216 # Install targets
3217 ###############################################################
3220 # We want to define additional install targets beyond what meson provides. For
3221 # that we need to define targets depending on nearly everything. We collected
3222 # the results of i18n.gettext() invocations into nls_targets, that also
3223 # includes maintainer targets though. Collect the ones we want as a dependency.
3225 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
3226 # generation happens during install, so that's not a real issue.
3227 nls_mo_targets = []
3228 if libintl.found() and meson.version().version_compare('>=0.60')
3229   # use range() to avoid the flattening of the list that foreach() would do
3230   foreach off : range(0, nls_targets.length())
3231     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
3232     # -pot target 3) maintainer -pot target
3233     nls_mo_targets += nls_targets[off][0]
3234   endforeach
3235   alias_target('nls', nls_mo_targets)
3236 endif
3239 all_built = [
3240   backend_targets,
3241   bin_targets,
3242   libpq_st,
3243   pl_targets,
3244   contrib_targets,
3245   nls_mo_targets,
3246   testprep_targets,
3247   ecpg_targets,
3250 # Meson's default install target is quite verbose. Provide one that is quiet.
3251 install_quiet = custom_target('install-quiet',
3252   output: 'install-quiet',
3253   build_always_stale: true,
3254   build_by_default: false,
3255   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
3256   depends: all_built,
3259 # Target to install files used for tests, which aren't installed by default
3260 install_test_files_args = [
3261   install_files,
3262   '--prefix', dir_prefix,
3263   '--install', contrib_data_dir, test_install_data,
3264   '--install', dir_lib_pkg, test_install_libs,
3266 run_target('install-test-files',
3267   command: [python] + install_test_files_args,
3268   depends: testprep_targets,
3273 ###############################################################
3274 # Test prep
3275 ###############################################################
3277 # DESTDIR for the installation we'll run tests in
3278 test_install_destdir = meson.build_root() / 'tmp_install/'
3280 # DESTDIR + prefix appropriately munged
3281 if build_system != 'windows'
3282   # On unixoid systems this is trivial, we just prepend the destdir
3283   assert(dir_prefix.startswith('/')) # enforced by meson
3284   temp_install_bindir = '@0@@1@'.format(test_install_destdir, dir_prefix / dir_bin)
3285   temp_install_libdir = '@0@@1@'.format(test_install_destdir, dir_prefix / dir_lib)
3286 else
3287   # drives, drive-relative paths, etc make this complicated on windows, call
3288   # into a copy of meson's logic for it
3289   command = [
3290     python, '-c',
3291     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3292     test_install_destdir]
3293   temp_install_bindir = run_command(command, dir_prefix / dir_bin, check: true).stdout().strip()
3294   temp_install_libdir = run_command(command, dir_prefix / dir_lib, check: true).stdout().strip()
3295 endif
3297 meson_install_args = meson_args + ['install'] + {
3298     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3299     'muon': []
3300 }[meson_impl]
3302 # setup tests should be run first,
3303 # so define priority for these
3304 setup_tests_priority = 100
3305 test('tmp_install',
3306     meson_bin, args: meson_install_args ,
3307     env: {'DESTDIR':test_install_destdir},
3308     priority: setup_tests_priority,
3309     timeout: 300,
3310     is_parallel: false,
3311     suite: ['setup'])
3313 test('install_test_files',
3314     python,
3315     args: install_test_files_args + ['--destdir', test_install_destdir],
3316     priority: setup_tests_priority,
3317     is_parallel: false,
3318     suite: ['setup'])
3320 test_result_dir = meson.build_root() / 'testrun'
3323 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3324 # inevitable conflicts from running tests in parallel, hackishly assign
3325 # different ports for different tests.
3327 testport = 40000
3329 test_env = environment()
3331 test_initdb_template = meson.build_root() / 'tmp_install' / 'initdb-template'
3332 test_env.set('PG_REGRESS', pg_regress.full_path())
3333 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3334 test_env.set('INITDB_TEMPLATE', test_initdb_template)
3336 # Test suites that are not safe by default but can be run if selected
3337 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3338 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3339 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3341 # Add the temporary installation to the library search path on platforms where
3342 # that works (everything but windows, basically). On windows everything
3343 # library-like gets installed into bindir, solving that issue.
3344 if library_path_var != ''
3345   test_env.prepend(library_path_var, temp_install_libdir)
3346 endif
3349 # Create (and remove old) initdb template directory. Tests use that, where
3350 # possible, to make it cheaper to run tests.
3352 # Use python to remove the old cached initdb, as we cannot rely on a working
3353 # 'rm' binary on windows.
3354 test('initdb_cache',
3355      python,
3356      args: [
3357        '-c', '''
3358 import shutil
3359 import sys
3360 import subprocess
3362 shutil.rmtree(sys.argv[1], ignore_errors=True)
3363 sp = subprocess.run(sys.argv[2:] + [sys.argv[1]])
3364 sys.exit(sp.returncode)
3365 ''',
3366        test_initdb_template,
3367        temp_install_bindir / 'initdb',
3368        '--auth', 'trust', '--no-sync', '--no-instructions', '--lc-messages=C',
3369        '--no-clean'
3370      ],
3371      priority: setup_tests_priority - 1,
3372      timeout: 300,
3373      is_parallel: false,
3374      env: test_env,
3375      suite: ['setup'])
3379 ###############################################################
3380 # Test Generation
3381 ###############################################################
3383 # When using a meson version understanding exclude_suites, define a
3384 # 'tmp_install' test setup (the default) that excludes tests running against a
3385 # pre-existing install and a 'running' setup that conflicts with creation of
3386 # the temporary installation and tap tests (which don't support running
3387 # against a running server).
3389 running_suites = []
3390 install_suites = []
3391 if meson.version().version_compare('>=0.57')
3392   runningcheck = true
3393 else
3394   runningcheck = false
3395 endif
3397 testwrap = files('src/tools/testwrap')
3399 foreach test_dir : tests
3400   testwrap_base = [
3401     testwrap,
3402     '--basedir', meson.build_root(),
3403     '--srcdir', test_dir['sd'],
3404   ]
3406   foreach kind, v : test_dir
3407     if kind in ['sd', 'bd', 'name']
3408       continue
3409     endif
3411     t = test_dir[kind]
3413     if kind in ['regress', 'isolation', 'ecpg']
3414       if kind == 'regress'
3415         runner = pg_regress
3416         fallback_dbname = 'regression_@0@'
3417       elif kind == 'isolation'
3418         runner = pg_isolation_regress
3419         fallback_dbname = 'isolation_regression_@0@'
3420       elif kind == 'ecpg'
3421         runner = pg_regress_ecpg
3422         fallback_dbname = 'ecpg_regression_@0@'
3423       endif
3425       test_group = test_dir['name']
3426       test_group_running = test_dir['name'] + '-running'
3428       test_output = test_result_dir / test_group / kind
3429       test_output_running = test_result_dir / test_group_running/ kind
3431       # Unless specified by the test, choose a non-conflicting database name,
3432       # to avoid conflicts when running against existing server.
3433       dbname = t.get('dbname',
3434         fallback_dbname.format(test_dir['name']))
3436       test_command_base = [
3437         runner.full_path(),
3438         '--inputdir', t.get('inputdir', test_dir['sd']),
3439         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3440         '--bindir', '',
3441         '--dlpath', test_dir['bd'],
3442         '--max-concurrent-tests=20',
3443         '--dbname', dbname,
3444       ] + t.get('regress_args', [])
3446       test_selection = []
3447       if t.has_key('schedule')
3448         test_selection += ['--schedule', t['schedule'],]
3449       endif
3451       if kind == 'isolation'
3452         test_selection += t.get('specs', [])
3453       else
3454         test_selection += t.get('sql', [])
3455       endif
3457       env = test_env
3458       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3460       test_kwargs = {
3461         'protocol': 'tap',
3462         'priority': 10,
3463         'timeout': 1000,
3464         'depends': test_deps + t.get('deps', []),
3465         'env': env,
3466       } + t.get('test_kwargs', {})
3468       test(test_group / kind,
3469         python,
3470         args: [
3471           testwrap_base,
3472           '--testgroup', test_group,
3473           '--testname', kind,
3474           '--',
3475           test_command_base,
3476           '--outputdir', test_output,
3477           '--temp-instance', test_output / 'tmp_check',
3478           '--port', testport.to_string(),
3479           test_selection,
3480         ],
3481         suite: test_group,
3482         kwargs: test_kwargs,
3483       )
3484       install_suites += test_group
3486       # some tests can't support running against running DB
3487       if runningcheck and t.get('runningcheck', true)
3488         test(test_group_running / kind,
3489           python,
3490           args: [
3491             testwrap_base,
3492             '--testgroup', test_group_running,
3493             '--testname', kind,
3494             '--',
3495             test_command_base,
3496             '--outputdir', test_output_running,
3497             test_selection,
3498           ],
3499           is_parallel: t.get('runningcheck-parallel', true),
3500           suite: test_group_running,
3501           kwargs: test_kwargs,
3502         )
3503         running_suites += test_group_running
3504       endif
3506       testport += 1
3507     elif kind == 'tap'
3508       testwrap_tap = testwrap_base
3509       if not tap_tests_enabled
3510         testwrap_tap += ['--skip', 'TAP tests not enabled']
3511       endif
3513       test_command = [
3514         perl.path(),
3515         '-I', meson.source_root() / 'src/test/perl',
3516         '-I', test_dir['sd'],
3517       ]
3519       # Add temporary install, the build directory for non-installed binaries and
3520       # also test/ for non-installed test binaries built separately.
3521       env = test_env
3522       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3524       foreach name, value : t.get('env', {})
3525         env.set(name, value)
3526       endforeach
3528       test_group = test_dir['name']
3529       test_kwargs = {
3530         'protocol': 'tap',
3531         'suite': test_group,
3532         'timeout': 1000,
3533         'depends': test_deps + t.get('deps', []),
3534         'env': env,
3535       } + t.get('test_kwargs', {})
3537       foreach onetap : t['tests']
3538         # Make tap test names prettier, remove t/ and .pl
3539         onetap_p = onetap
3540         if onetap_p.startswith('t/')
3541           onetap_p = onetap.split('t/')[1]
3542         endif
3543         if onetap_p.endswith('.pl')
3544           onetap_p = fs.stem(onetap_p)
3545         endif
3547         test(test_dir['name'] / onetap_p,
3548           python,
3549           kwargs: test_kwargs,
3550           args: testwrap_tap + [
3551             '--testgroup', test_dir['name'],
3552             '--testname', onetap_p,
3553             '--', test_command,
3554             test_dir['sd'] / onetap,
3555           ],
3556         )
3557       endforeach
3558       install_suites += test_group
3559     else
3560       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3561     endif
3563   endforeach # kinds of tests
3565 endforeach # directories with tests
3567 # repeat condition so meson realizes version dependency
3568 if meson.version().version_compare('>=0.57')
3569   add_test_setup('tmp_install',
3570     is_default: true,
3571     exclude_suites: running_suites)
3572   add_test_setup('running',
3573     exclude_suites: ['setup'] + install_suites)
3574 endif
3578 ###############################################################
3579 # Pseudo targets
3580 ###############################################################
3582 alias_target('backend', backend_targets)
3583 alias_target('bin', bin_targets + [libpq_st])
3584 alias_target('pl', pl_targets)
3585 alias_target('contrib', contrib_targets)
3586 alias_target('testprep', testprep_targets)
3588 alias_target('world', all_built, docs)
3589 alias_target('install-world', install_quiet, installdocs)
3591 run_target('help',
3592   command: [
3593     perl, '-ne', 'next if /^#/; print',
3594     files('doc/src/sgml/targets-meson.txt'),
3595   ]
3600 ###############################################################
3601 # Distribution archive
3602 ###############################################################
3604 # Meson has its own distribution building command (meson dist), but we
3605 # are not using that at this point.  The main problem is that, the way
3606 # they have implemented it, it is not deterministic.  Also, we want it
3607 # to be equivalent to the "make" version for the time being.  But the
3608 # target name "dist" in meson is reserved for that reason, so we call
3609 # the custom target "pgdist".
3611 git = find_program('git', required: false, native: true, disabler: true)
3612 bzip2 = find_program('bzip2', required: false, native: true)
3614 distdir = meson.project_name() + '-' + meson.project_version()
3616 pg_git_revision = get_option('PG_GIT_REVISION')
3618 # Note: core.autocrlf=false is needed to avoid line-ending conversion
3619 # in case the environment has a different setting.  Without this, a
3620 # tarball created on Windows might be different than on, and unusable
3621 # on, Unix machines.
3623 tar_gz = custom_target('tar.gz',
3624   build_always_stale: true,
3625   command: [git, '-C', '@SOURCE_ROOT@',
3626             '-c', 'core.autocrlf=false',
3627             'archive',
3628             '--format', 'tar.gz',
3629             '-9',
3630             '--prefix', distdir + '/',
3631             '-o', join_paths(meson.build_root(), '@OUTPUT@'),
3632             pg_git_revision],
3633   output: distdir + '.tar.gz',
3636 if bzip2.found()
3637   tar_bz2 = custom_target('tar.bz2',
3638     build_always_stale: true,
3639     command: [git, '-C', '@SOURCE_ROOT@',
3640               '-c', 'core.autocrlf=false',
3641               '-c', 'tar.tar.bz2.command="@0@" -c'.format(bzip2.path()),
3642               'archive',
3643               '--format', 'tar.bz2',
3644               '--prefix', distdir + '/',
3645               '-o', join_paths(meson.build_root(), '@OUTPUT@'),
3646               pg_git_revision],
3647     output: distdir + '.tar.bz2',
3648   )
3649 else
3650   tar_bz2 = custom_target('tar.bz2',
3651     command: [perl, '-e', 'exit 1'],
3652     output: distdir + '.tar.bz2',
3653   )
3654 endif
3656 alias_target('pgdist', [tar_gz, tar_bz2])
3658 # Make the standard "dist" command fail, to prevent accidental use.
3659 # But not if we are in a subproject, in case the parent project wants to
3660 # create a dist using the standard Meson command.
3661 if not meson.is_subproject()
3662   # We can only pass the identifier perl here when we depend on >= 0.55
3663   if meson.version().version_compare('>=0.55')
3664     meson.add_dist_script(perl, '-e', 'exit 1')
3665   endif
3666 endif
3670 ###############################################################
3671 # The End, The End, My Friend
3672 ###############################################################
3674 if meson.version().version_compare('>=0.57')
3676   summary(
3677     {
3678       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3679       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3680       'segment size': get_option('segsize_blocks') != 0 ?
3681         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3682         '@0@ GB'.format(get_option('segsize')),
3683     },
3684     section: 'Data layout',
3685   )
3687   summary(
3688     {
3689       'host system': '@0@ @1@'.format(host_system, host_cpu),
3690       'build system': '@0@ @1@'.format(build_machine.system(),
3691                                        build_machine.cpu_family()),
3692     },
3693     section: 'System',
3694   )
3696   summary(
3697     {
3698       'linker': '@0@'.format(cc.get_linker_id()),
3699       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3700     },
3701     section: 'Compiler',
3702   )
3704   summary(
3705     {
3706       'CPP FLAGS': ' '.join(cppflags),
3707       'C FLAGS, functional': ' '.join(cflags),
3708       'C FLAGS, warnings': ' '.join(cflags_warn),
3709       'C FLAGS, modules': ' '.join(cflags_mod),
3710       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3711       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3712     },
3713     section: 'Compiler Flags',
3714   )
3716   if llvm.found()
3717     summary(
3718       {
3719         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3720       },
3721       section: 'Compiler',
3722     )
3724     summary(
3725       {
3726         'C++ FLAGS, functional': ' '.join(cxxflags),
3727         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3728         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3729       },
3730       section: 'Compiler Flags',
3731     )
3732   endif
3734   summary(
3735     {
3736       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3737       'dtrace': dtrace,
3738       'flex': '@0@ @1@'.format(flex.full_path(), flex_version),
3739     },
3740     section: 'Programs',
3741   )
3743   summary(
3744     {
3745       'bonjour': bonjour,
3746       'bsd_auth': bsd_auth,
3747       'docs': docs_dep,
3748       'docs_pdf': docs_pdf_dep,
3749       'gss': gssapi,
3750       'icu': icu,
3751       'ldap': ldap,
3752       'libxml': libxml,
3753       'libxslt': libxslt,
3754       'llvm': llvm,
3755       'lz4': lz4,
3756       'nls': libintl,
3757       'openssl': ssl,
3758       'pam': pam,
3759       'plperl': perl_dep,
3760       'plpython': python3_dep,
3761       'pltcl': tcl_dep,
3762       'readline': readline,
3763       'selinux': selinux,
3764       'systemd': systemd,
3765       'uuid': uuid,
3766       'zlib': zlib,
3767       'zstd': zstd,
3768     },
3769     section: 'External libraries',
3770   )
3772 endif