Improve performance of dumpSequenceData().
[pgsql.git] / meson.build
blob7de0371226d25f2a87f3e4fab4564420f45c472c
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: '18devel',
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_ccflags += ['-DNO_THREAD_SAFE_LOCALE']
1058       endif
1059     endif
1061     message('CCFLAGS recommended by perl: @0@'.format(perl_ccflags_r))
1062     message('CCFLAGS for embedding perl: @0@'.format(' '.join(perl_ccflags)))
1064     # We are after Embed's ldopts, but without the subset mentioned in
1065     # Config's ccdlflags and ldflags.  (Those are the choices of those who
1066     # built the Perl installation, which are not necessarily appropriate
1067     # for building PostgreSQL.)
1068     ldopts = run_command(perl, '-MExtUtils::Embed', '-e', 'ldopts', check: true).stdout().strip()
1069     undesired = run_command(perl_conf_cmd, 'ccdlflags', check: true).stdout().split()
1070     undesired += run_command(perl_conf_cmd, 'ldflags', check: true).stdout().split()
1072     perl_ldopts = []
1073     foreach ldopt : ldopts.split(' ')
1074       if ldopt == '' or ldopt in undesired
1075         continue
1076       endif
1078       perl_ldopts += ldopt.strip('"')
1079     endforeach
1081     message('LDFLAGS recommended by perl: "@0@"'.format(ldopts))
1082     message('LDFLAGS for embedding perl: "@0@"'.format(' '.join(perl_ldopts)))
1084     perl_dep_int = declare_dependency(
1085       compile_args: perl_ccflags,
1086       link_args: perl_ldopts,
1087       version: perlversion,
1088     )
1090     # While we're at it, check that we can link to libperl.
1091     # On most platforms, if perl.h is there then libperl.so will be too, but
1092     # at this writing Debian packages them separately.
1093     perl_link_test = '''
1094 /* see plperl.h */
1095 #ifdef _MSC_VER
1096 #define __inline__ inline
1097 #endif
1098 #include <EXTERN.h>
1099 #include <perl.h>
1100 int main(void)
1102 perl_alloc();
1103 }'''
1104     if not cc.links(perl_link_test, name: 'libperl',
1105           args: test_c_args + perl_ccflags + perl_ldopts,
1106           include_directories: postgres_inc)
1107       perl_may_work = false
1108       perl_msg = 'missing libperl'
1109     endif
1111   endif # perl_may_work
1113   if perl_may_work
1114     perl_dep = perl_dep_int
1115   else
1116     if perlopt.enabled()
1117       error('dependency plperl failed: @0@'.format(perl_msg))
1118     else
1119       message('disabling optional dependency plperl: @0@'.format(perl_msg))
1120     endif
1121   endif
1122 endif
1126 ###############################################################
1127 # Library: Python (for plpython)
1128 ###############################################################
1130 pyopt = get_option('plpython')
1131 python3_dep = not_found_dep
1132 if not pyopt.disabled()
1133   pm = import('python')
1134   python3_inst = pm.find_installation(python.path(), required: pyopt)
1135   if python3_inst.found()
1136     python3_dep = python3_inst.dependency(embed: true, required: pyopt)
1137     # Remove this check after we depend on Meson >= 1.1.0
1138     if not cc.check_header('Python.h', dependencies: python3_dep, required: pyopt, include_directories: postgres_inc)
1139       python3_dep = not_found_dep
1140     endif
1141   endif
1142 endif
1146 ###############################################################
1147 # Library: Readline
1148 ###############################################################
1150 if not get_option('readline').disabled()
1151   libedit_preferred = get_option('libedit_preferred')
1152   # Set the order of readline dependencies.
1153   # cc.find_library breaks and throws on the first dependency which
1154   # is marked as required=true and can't be found. Thus, we only mark
1155   # the last dependency to look up as required, to not throw too early.
1156   check_readline_deps = [
1157     {
1158       'name': libedit_preferred ? 'libedit' : 'readline',
1159       'required': false
1160     },
1161     {
1162       'name': libedit_preferred ? 'readline' : 'libedit',
1163       'required': get_option('readline')
1164     }
1165   ]
1167   foreach readline_dep : check_readline_deps
1168     readline = dependency(readline_dep['name'], required: false)
1169     if not readline.found()
1170       readline = cc.find_library(readline_dep['name'],
1171         required: readline_dep['required'],
1172         dirs: test_lib_d)
1173     endif
1174     if readline.found()
1175       break
1176     endif
1177   endforeach
1179   if readline.found()
1180     cdata.set('HAVE_LIBREADLINE', 1)
1182     editline_prefix = {
1183       'header_prefix': 'editline/',
1184       'flag_prefix': 'EDITLINE_',
1185     }
1186     readline_prefix = {
1187       'header_prefix': 'readline/',
1188       'flag_prefix': 'READLINE_',
1189     }
1190     default_prefix = {
1191       'header_prefix': '',
1192       'flag_prefix': '',
1193     }
1195     # Set the order of prefixes
1196     prefixes = libedit_preferred ? \
1197       [editline_prefix, default_prefix, readline_prefix] : \
1198       [readline_prefix, default_prefix, editline_prefix]
1200     at_least_one_header_found = false
1201     foreach header : ['history', 'readline']
1202       is_found = false
1203       foreach prefix : prefixes
1204         header_file = '@0@@1@.h'.format(prefix['header_prefix'], header)
1205         # Check history.h and readline.h
1206         if not is_found and cc.has_header(header_file,
1207             args: test_c_args, include_directories: postgres_inc,
1208             dependencies: [readline], required: false)
1209           if header == 'readline'
1210             readline_h = header_file
1211           endif
1212           cdata.set('HAVE_@0@@1@_H'.format(prefix['flag_prefix'], header).to_upper(), 1)
1213           is_found = true
1214           at_least_one_header_found = true
1215         endif
1216       endforeach
1217     endforeach
1219     if not at_least_one_header_found
1220       error('''readline header not found
1221 If you have @0@ already installed, see meson-logs/meson-log.txt for details on the
1222 failure. It is possible the compiler isn't looking in the proper directory.
1223 Use -Dreadline=disabled to disable readline support.'''.format(readline_dep))
1224     endif
1226     check_funcs = [
1227       'append_history',
1228       'history_truncate_file',
1229       'rl_completion_matches',
1230       'rl_filename_completion_function',
1231       'rl_reset_screen_size',
1232       'rl_variable_bind',
1233     ]
1235     foreach func : check_funcs
1236       found = cc.has_function(func, dependencies: [readline],
1237         args: test_c_args, include_directories: postgres_inc)
1238       cdata.set('HAVE_' + func.to_upper(), found ? 1 : false)
1239     endforeach
1241     check_vars = [
1242       'rl_completion_suppress_quote',
1243       'rl_filename_quote_characters',
1244       'rl_filename_quoting_function',
1245     ]
1247     foreach var : check_vars
1248       cdata.set('HAVE_' + var.to_upper(),
1249         cc.has_header_symbol(readline_h, var,
1250           args: test_c_args, include_directories: postgres_inc,
1251           prefix: '#include <stdio.h>',
1252           dependencies: [readline]) ? 1 : false)
1253     endforeach
1255     # If found via cc.find_library() ensure headers are found when using the
1256     # dependency. On meson < 0.57 one cannot do compiler checks using the
1257     # dependency returned by declare_dependency(), so we can't do this above.
1258     if readline.type_name() == 'library'
1259       readline = declare_dependency(dependencies: readline,
1260         include_directories: postgres_inc)
1261     endif
1263     # On windows with mingw readline requires auto-import to successfully
1264     # link, as the headers don't use declspec(dllimport)
1265     if host_system == 'windows' and cc.get_id() != 'msvc'
1266       readline = declare_dependency(dependencies: readline,
1267         link_args: '-Wl,--enable-auto-import')
1268     endif
1269   endif
1271   # XXX: Figure out whether to implement mingw warning equivalent
1272 else
1273   readline = not_found_dep
1274 endif
1278 ###############################################################
1279 # Library: selinux
1280 ###############################################################
1282 selinux = not_found_dep
1283 selinuxopt = get_option('selinux')
1284 if meson.version().version_compare('>=0.59')
1285   selinuxopt = selinuxopt.disable_auto_if(host_system != 'linux')
1286 endif
1287 selinux = dependency('libselinux', required: selinuxopt, version: '>= 2.1.10')
1288 cdata.set('HAVE_LIBSELINUX',
1289   selinux.found() ? 1 : false)
1293 ###############################################################
1294 # Library: systemd
1295 ###############################################################
1297 systemd = not_found_dep
1298 systemdopt = get_option('systemd')
1299 if meson.version().version_compare('>=0.59')
1300   systemdopt = systemdopt.disable_auto_if(host_system != 'linux')
1301 endif
1302 systemd = dependency('libsystemd', required: systemdopt)
1303 cdata.set('USE_SYSTEMD', systemd.found() ? 1 : false)
1307 ###############################################################
1308 # Library: SSL
1309 ###############################################################
1311 ssl = not_found_dep
1312 ssl_library = 'none'
1313 sslopt = get_option('ssl')
1315 if sslopt == 'auto' and auto_features.disabled()
1316   sslopt = 'none'
1317 endif
1319 if sslopt in ['auto', 'openssl']
1320   openssl_required = (sslopt == 'openssl')
1322   # Try to find openssl via pkg-config et al, if that doesn't work
1323   # (e.g. because it's provided as part of the OS, like on FreeBSD), look for
1324   # the library names that we know about.
1326   # via pkg-config et al
1327   ssl = dependency('openssl', required: false)
1328   # only meson >= 0.57 supports declare_dependency() in cc.has_function(), so
1329   # we pass cc.find_library() results if necessary
1330   ssl_int = []
1332   # via library + headers
1333   if not ssl.found()
1334     ssl_lib = cc.find_library('ssl',
1335       dirs: test_lib_d,
1336       header_include_directories: postgres_inc,
1337       has_headers: ['openssl/ssl.h', 'openssl/err.h'],
1338       required: openssl_required)
1339     crypto_lib = cc.find_library('crypto',
1340       dirs: test_lib_d,
1341       required: openssl_required)
1342     if ssl_lib.found() and crypto_lib.found()
1343       ssl_int = [ssl_lib, crypto_lib]
1344       ssl = declare_dependency(dependencies: ssl_int, include_directories: postgres_inc)
1345     endif
1346   elif cc.has_header('openssl/ssl.h', args: test_c_args, dependencies: ssl, required: openssl_required) and \
1347        cc.has_header('openssl/err.h', args: test_c_args, dependencies: ssl, required: openssl_required)
1348     ssl_int = [ssl]
1349   else
1350     ssl = not_found_dep
1351   endif
1353   if ssl.found()
1354     check_funcs = [
1355       ['CRYPTO_new_ex_data', {'required': true}],
1356       ['SSL_new', {'required': true}],
1358       # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
1359       ['SSL_CTX_set_cert_cb'],
1361       # Functions introduced in OpenSSL 1.1.0. We used to check for
1362       # OPENSSL_VERSION_NUMBER, but that didn't work with 1.1.0, because LibreSSL
1363       # defines OPENSSL_VERSION_NUMBER to claim version 2.0.0, even though it
1364       # doesn't have these OpenSSL 1.1.0 functions. So check for individual
1365       # functions.
1366       ['OPENSSL_init_ssl'],
1367       ['BIO_meth_new'],
1368       ['ASN1_STRING_get0_data'],
1369       ['HMAC_CTX_new'],
1370       ['HMAC_CTX_free'],
1372       # OpenSSL versions before 1.1.0 required setting callback functions, for
1373       # thread-safety. In 1.1.0, it's no longer required, and CRYPTO_lock()
1374       # function was removed.
1375       ['CRYPTO_lock'],
1377       # Function introduced in OpenSSL 1.1.1
1378       ['X509_get_signature_info'],
1379       ['SSL_CTX_set_num_tickets'],
1380     ]
1382     are_openssl_funcs_complete = true
1383     foreach c : check_funcs
1384       func = c.get(0)
1385       val = cc.has_function(func, args: test_c_args, dependencies: ssl_int)
1386       required = c.get(1, {}).get('required', false)
1387       if required and not val
1388         are_openssl_funcs_complete = false
1389         if openssl_required
1390           error('openssl function @0@ is required'.format(func))
1391         endif
1392         break
1393       elif not required
1394         cdata.set('HAVE_' + func.to_upper(), val ? 1 : false)
1395       endif
1396     endforeach
1398     if are_openssl_funcs_complete
1399       cdata.set('USE_OPENSSL', 1,
1400                 description: 'Define to 1 to build with OpenSSL support. (-Dssl=openssl)')
1401       cdata.set('OPENSSL_API_COMPAT', '0x10002000L',
1402                 description: 'Define to the OpenSSL API version in use. This avoids deprecation warnings from newer OpenSSL versions.')
1403       ssl_library = 'openssl'
1404     else
1405       ssl = not_found_dep
1406     endif
1407   endif
1408 endif
1410 if sslopt == 'auto' and auto_features.enabled() and not ssl.found()
1411   error('no SSL library found')
1412 endif
1416 ###############################################################
1417 # Library: uuid
1418 ###############################################################
1420 uuidopt = get_option('uuid')
1421 if uuidopt != 'none'
1422   uuidname = uuidopt.to_upper()
1423   if uuidopt == 'e2fs'
1424     uuid = dependency('uuid', required: true)
1425     uuidfunc = 'uuid_generate'
1426     uuidheader = 'uuid/uuid.h'
1427   elif uuidopt == 'bsd'
1428     # libc should have uuid function
1429     uuid = declare_dependency()
1430     uuidfunc = 'uuid_to_string'
1431     uuidheader = 'uuid.h'
1432   elif uuidopt == 'ossp'
1433     # In upstream, the package and library is called just 'uuid', but many
1434     # distros change it to 'ossp-uuid'.
1435     uuid = dependency('ossp-uuid', 'uuid', required: false)
1436     uuidfunc = 'uuid_export'
1437     uuidheader = 'uuid.h'
1439     # Hardcoded lookup for ossp-uuid. This is necessary as ossp-uuid on
1440     # windows installs neither a pkg-config nor a cmake dependency
1441     # information. Nor is there another supported uuid implementation
1442     # available on windows.
1443     if not uuid.found()
1444       uuid = cc.find_library('ossp-uuid',
1445         required: false, dirs: test_lib_d,
1446         has_headers: uuidheader, header_include_directories: postgres_inc)
1447     endif
1448     if not uuid.found()
1449       uuid = cc.find_library('uuid',
1450         required: true, dirs: test_lib_d,
1451         has_headers: uuidheader, header_include_directories: postgres_inc)
1452     endif
1453   else
1454     error('unknown uuid build option value: @0@'.format(uuidopt))
1455   endif
1457   if not cc.has_header_symbol(uuidheader, uuidfunc,
1458                               args: test_c_args,
1459                               include_directories: postgres_inc,
1460                               dependencies: uuid)
1461     error('uuid library @0@ missing required function @1@'.format(uuidopt, uuidfunc))
1462   endif
1463   cdata.set('HAVE_@0@'.format(uuidheader.underscorify().to_upper()), 1)
1465   cdata.set('HAVE_UUID_@0@'.format(uuidname), 1,
1466            description: 'Define to 1 if you have @0@ UUID support.'.format(uuidname))
1467 else
1468   uuid = not_found_dep
1469 endif
1473 ###############################################################
1474 # Library: zlib
1475 ###############################################################
1477 zlibopt = get_option('zlib')
1478 zlib = not_found_dep
1479 if not zlibopt.disabled()
1480   zlib_t = dependency('zlib', required: zlibopt)
1482   if zlib_t.type_name() == 'internal'
1483     # if fallback was used, we don't need to test if headers are present (they
1484     # aren't built yet, so we can't test)
1485     zlib = zlib_t
1486   elif not zlib_t.found()
1487     warning('did not find zlib')
1488   elif not cc.has_header('zlib.h',
1489       args: test_c_args, include_directories: postgres_inc,
1490       dependencies: [zlib_t], required: zlibopt)
1491     warning('zlib header not found')
1492   else
1493     zlib = zlib_t
1494   endif
1496   if zlib.found()
1497     cdata.set('HAVE_LIBZ', 1)
1498   endif
1499 endif
1503 ###############################################################
1504 # Library: tap test dependencies
1505 ###############################################################
1507 # Check whether tap tests are enabled or not
1508 tap_tests_enabled = false
1509 tapopt = get_option('tap_tests')
1510 if not tapopt.disabled()
1511   # Checking for perl modules for tap tests
1512   perl_ipc_run_check = run_command(perl, 'config/check_modules.pl', check: false)
1513   if perl_ipc_run_check.returncode() != 0
1514     message(perl_ipc_run_check.stderr().strip())
1515     if tapopt.enabled()
1516       error('Additional Perl modules are required to run TAP tests.')
1517     else
1518       warning('Additional Perl modules are required to run TAP tests.')
1519     endif
1520   else
1521     tap_tests_enabled = true
1522   endif
1523 endif
1527 ###############################################################
1528 # Library: zstd
1529 ###############################################################
1531 zstdopt = get_option('zstd')
1532 if not zstdopt.disabled()
1533   zstd = dependency('libzstd', required: false, version: '>=1.4.0')
1534   # Unfortunately the dependency is named differently with cmake
1535   if not zstd.found() # combine with above once meson 0.60.0 is required
1536     zstd = dependency('zstd', required: zstdopt, version: '>=1.4.0',
1537                       method: 'cmake', modules: ['zstd::libzstd_shared'])
1538   endif
1540   if zstd.found()
1541     cdata.set('USE_ZSTD', 1)
1542     cdata.set('HAVE_LIBZSTD', 1)
1543   endif
1545 else
1546   zstd = not_found_dep
1547 endif
1551 ###############################################################
1552 # Compiler tests
1553 ###############################################################
1555 # Do we need -std=c99 to compile C99 code? We don't want to add -std=c99
1556 # unnecessarily, because we optionally rely on newer features.
1557 c99_test = '''
1558 #include <stdbool.h>
1559 #include <complex.h>
1560 #include <tgmath.h>
1561 #include <inttypes.h>
1563 struct named_init_test {
1564   int a;
1565   int b;
1568 extern void structfunc(struct named_init_test);
1570 int main(int argc, char **argv)
1572   struct named_init_test nit = {
1573     .a = 3,
1574     .b = 5,
1575   };
1577   for (int loop_var = 0; loop_var < 3; loop_var++)
1578   {
1579     nit.a += nit.b;
1580   }
1582   structfunc((struct named_init_test){1, 0});
1584   return nit.a != 0;
1588 if not cc.compiles(c99_test, name: 'c99', args: test_c_args)
1589   if cc.compiles(c99_test, name: 'c99 with -std=c99',
1590         args: test_c_args + ['-std=c99'])
1591     test_c_args += '-std=c99'
1592     cflags += '-std=c99'
1593   else
1594     error('C compiler does not support C99')
1595   endif
1596 endif
1598 sizeof_long = cc.sizeof('long', args: test_c_args)
1599 cdata.set('SIZEOF_LONG', sizeof_long)
1600 if sizeof_long == 8
1601   cdata.set('HAVE_LONG_INT_64', 1)
1602   pg_int64_type = 'long int'
1603   cdata.set_quoted('INT64_MODIFIER', 'l')
1604 elif sizeof_long == 4 and cc.sizeof('long long', args: test_c_args) == 8
1605   cdata.set('HAVE_LONG_LONG_INT_64', 1)
1606   pg_int64_type = 'long long int'
1607   cdata.set_quoted('INT64_MODIFIER', 'll')
1608 else
1609   error('do not know how to get a 64bit int')
1610 endif
1611 cdata.set('PG_INT64_TYPE', pg_int64_type)
1613 if host_machine.endian() == 'big'
1614   cdata.set('WORDS_BIGENDIAN', 1)
1615 endif
1617 # Determine memory alignment requirements for the basic C data types.
1619 alignof_types = ['short', 'int', 'long', 'double']
1620 foreach t : alignof_types
1621   align = cc.alignment(t, args: test_c_args)
1622   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
1623 endforeach
1625 # Compute maximum alignment of any basic type.
1627 # We require 'double' to have the strictest alignment among the basic types,
1628 # because otherwise the C ABI might impose 8-byte alignment on some of the
1629 # other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
1630 # cause a mismatch between the tuple layout and the C struct layout of a
1631 # catalog tuple.  We used to carefully order catalog columns such that any
1632 # fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
1633 # of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
1634 # where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
1636 # We assume without checking that int64's alignment is at least as strong
1637 # as long, char, short, or int.  Note that we intentionally do not consider
1638 # any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8
1639 # would be too much of a penalty for disk and memory space.
1640 alignof_double = cdata.get('ALIGNOF_DOUBLE')
1641 if cc.alignment(pg_int64_type, args: test_c_args) > alignof_double
1642   error('alignment of int64 is greater than the alignment of double')
1643 endif
1644 cdata.set('MAXIMUM_ALIGNOF', alignof_double)
1646 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
1647 cdata.set('SIZEOF_SIZE_T', cc.sizeof('size_t', args: test_c_args))
1650 # Check if __int128 is a working 128 bit integer type, and if so
1651 # define PG_INT128_TYPE to that typename.
1653 # This currently only detects a GCC/clang extension, but support for other
1654 # environments may be added in the future.
1656 # For the moment we only test for support for 128bit math; support for
1657 # 128bit literals and snprintf is not required.
1658 if cc.links('''
1659   /*
1660    * We don't actually run this test, just link it to verify that any support
1661    * functions needed for __int128 are present.
1662    *
1663    * These are globals to discourage the compiler from folding all the
1664    * arithmetic tests down to compile-time constants.  We do not have
1665    * convenient support for 128bit literals at this point...
1666    */
1667   __int128 a = 48828125;
1668   __int128 b = 97656250;
1670   int main(void)
1671   {
1672       __int128 c,d;
1673       a = (a << 12) + 1; /* 200000000001 */
1674       b = (b << 12) + 5; /* 400000000005 */
1675       /* try the most relevant arithmetic ops */
1676       c = a * b;
1677       d = (c + b) / b;
1678       /* must use the results, else compiler may optimize arithmetic away */
1679       return d != a+1;
1680   }''',
1681   name: '__int128',
1682   args: test_c_args)
1684   buggy_int128 = false
1686   # Use of non-default alignment with __int128 tickles bugs in some compilers.
1687   # If not cross-compiling, we can test for bugs and disable use of __int128
1688   # with buggy compilers.  If cross-compiling, hope for the best.
1689   # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
1690   if not meson.is_cross_build()
1691     r = cc.run('''
1692     /* This must match the corresponding code in c.h: */
1693     #if defined(__GNUC__) || defined(__SUNPRO_C)
1694     #define pg_attribute_aligned(a) __attribute__((aligned(a)))
1695     #elif defined(_MSC_VER)
1696     #define pg_attribute_aligned(a) __declspec(align(a))
1697     #endif
1698     typedef __int128 int128a
1699     #if defined(pg_attribute_aligned)
1700     pg_attribute_aligned(8)
1701     #endif
1702     ;
1704     int128a holder;
1705     void pass_by_val(void *buffer, int128a par) { holder = par; }
1707     int main(void)
1708     {
1709         long int i64 = 97656225L << 12;
1710         int128a q;
1711         pass_by_val(main, (int128a) i64);
1712         q = (int128a) i64;
1713         return q != holder;
1714     }''',
1715     name: '__int128 alignment bug',
1716     args: test_c_args)
1717     assert(r.compiled())
1718     if r.returncode() != 0
1719       buggy_int128 = true
1720       message('__int128 support present but buggy and thus disabled')
1721     endif
1722   endif
1724   if not buggy_int128
1725     cdata.set('PG_INT128_TYPE', '__int128')
1726     cdata.set('ALIGNOF_PG_INT128_TYPE', cc.alignment('__int128', args: test_c_args))
1727   endif
1728 endif
1731 # Check if the C compiler knows computed gotos (gcc extension, also
1732 # available in at least clang).  If so, define HAVE_COMPUTED_GOTO.
1734 # Checking whether computed gotos are supported syntax-wise ought to
1735 # be enough, as the syntax is otherwise illegal.
1736 if cc.compiles('''
1737     static inline int foo(void)
1738     {
1739       void *labeladdrs[] = {&&my_label};
1740       goto *labeladdrs[0];
1741       my_label:
1742       return 1;
1743     }''',
1744     args: test_c_args)
1745   cdata.set('HAVE_COMPUTED_GOTO', 1)
1746 endif
1749 # Check if the C compiler understands _Static_assert(),
1750 # and define HAVE__STATIC_ASSERT if so.
1752 # We actually check the syntax ({ _Static_assert(...) }), because we need
1753 # gcc-style compound expressions to be able to wrap the thing into macros.
1754 if cc.compiles('''
1755     int main(int arg, char **argv)
1756     {
1757         ({ _Static_assert(1, "foo"); });
1758     }
1759     ''',
1760     args: test_c_args)
1761   cdata.set('HAVE__STATIC_ASSERT', 1)
1762 endif
1765 # We use <stdbool.h> if we have it and it declares type bool as having
1766 # size 1.  Otherwise, c.h will fall back to declaring bool as unsigned char.
1767 if cc.has_type('_Bool', args: test_c_args) \
1768     and cc.has_type('bool', prefix: '#include <stdbool.h>', args: test_c_args) \
1769     and cc.sizeof('bool', prefix: '#include <stdbool.h>', args: test_c_args) == 1
1770   cdata.set('HAVE__BOOL', 1)
1771   cdata.set('PG_USE_STDBOOL', 1)
1772 endif
1775 # Need to check a call with %m because netbsd supports gnu_printf but emits a
1776 # warning for each use of %m.
1777 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
1778 testsrc = '''
1779 extern void emit_log(int ignore, const char *fmt,...) __attribute__((format(@0@, 2,3)));
1780 static void call_log(void)
1782     emit_log(0, "error: %s: %m", "foo");
1785 attrib_error_args = cc.get_supported_arguments('-Werror=format', '-Werror=ignored-attributes')
1786 foreach a : printf_attributes
1787   if cc.compiles(testsrc.format(a),
1788       args: test_c_args + attrib_error_args, name: 'format ' + a)
1789     cdata.set('PG_PRINTF_ATTRIBUTE', a)
1790     break
1791   endif
1792 endforeach
1795 if cc.has_function_attribute('visibility:default') and \
1796     cc.has_function_attribute('visibility:hidden')
1797   cdata.set('HAVE_VISIBILITY_ATTRIBUTE', 1)
1799   # Only newer versions of meson know not to apply gnu_symbol_visibility =
1800   # inlineshidden to C code as well... And either way, we want to put these
1801   # flags into exported files (pgxs, .pc files).
1802   cflags_mod += '-fvisibility=hidden'
1803   cxxflags_mod += ['-fvisibility=hidden', '-fvisibility-inlines-hidden']
1804   ldflags_mod += '-fvisibility=hidden'
1805 endif
1808 # Check if various builtins exist. Some builtins are tested separately,
1809 # because we want to test something more complicated than the generic case.
1810 builtins = [
1811   'bswap16',
1812   'bswap32',
1813   'bswap64',
1814   'clz',
1815   'ctz',
1816   'constant_p',
1817   'frame_address',
1818   'popcount',
1819   'unreachable',
1822 foreach builtin : builtins
1823   fname = '__builtin_@0@'.format(builtin)
1824   if cc.has_function(fname, args: test_c_args)
1825     cdata.set('HAVE@0@'.format(fname.to_upper()), 1)
1826   endif
1827 endforeach
1830 # Check if the C compiler understands __builtin_types_compatible_p,
1831 # and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
1833 # We check usage with __typeof__, though it's unlikely any compiler would
1834 # have the former and not the latter.
1835 if cc.compiles('''
1836     static int x;
1837     static int y[__builtin_types_compatible_p(__typeof__(x), int)];
1838     ''',
1839     name: '__builtin_types_compatible_p',
1840     args: test_c_args)
1841   cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
1842 endif
1845 # Check if the C compiler understands __builtin_$op_overflow(),
1846 # and define HAVE__BUILTIN_OP_OVERFLOW if so.
1848 # Check for the most complicated case, 64 bit multiplication, as a
1849 # proxy for all of the operations.  To detect the case where the compiler
1850 # knows the function but library support is missing, we must link not just
1851 # compile, and store the results in global variables so the compiler doesn't
1852 # optimize away the call.
1853 if cc.links('''
1854     INT64 a = 1;
1855     INT64 b = 1;
1856     INT64 result;
1858     int main(void)
1859     {
1860         return __builtin_mul_overflow(a, b, &result);
1861     }''',
1862     name: '__builtin_mul_overflow',
1863     args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))],
1864     )
1865   cdata.set('HAVE__BUILTIN_OP_OVERFLOW', 1)
1866 endif
1869 # XXX: The configure.ac check for __cpuid() is broken, we don't copy that
1870 # here. To prevent problems due to two detection methods working, stop
1871 # checking after one.
1872 if cc.links('''
1873     #include <cpuid.h>
1874     int main(int arg, char **argv)
1875     {
1876         unsigned int exx[4] = {0, 0, 0, 0};
1877         __get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
1878     }
1879     ''', name: '__get_cpuid',
1880     args: test_c_args)
1881   cdata.set('HAVE__GET_CPUID', 1)
1882 elif cc.links('''
1883     #include <intrin.h>
1884     int main(int arg, char **argv)
1885     {
1886         unsigned int exx[4] = {0, 0, 0, 0};
1887         __cpuid(exx, 1);
1888     }
1889     ''', name: '__cpuid',
1890     args: test_c_args)
1891   cdata.set('HAVE__CPUID', 1)
1892 endif
1895 # Check for __get_cpuid_count() and __cpuidex() in a similar fashion.
1896 if cc.links('''
1897     #include <cpuid.h>
1898     int main(int arg, char **argv)
1899     {
1900         unsigned int exx[4] = {0, 0, 0, 0};
1901         __get_cpuid_count(7, 0, &exx[0], &exx[1], &exx[2], &exx[3]);
1902     }
1903     ''', name: '__get_cpuid_count',
1904     args: test_c_args)
1905   cdata.set('HAVE__GET_CPUID_COUNT', 1)
1906 elif cc.links('''
1907     #include <intrin.h>
1908     int main(int arg, char **argv)
1909     {
1910         unsigned int exx[4] = {0, 0, 0, 0};
1911         __cpuidex(exx, 7, 0);
1912     }
1913     ''', name: '__cpuidex',
1914     args: test_c_args)
1915   cdata.set('HAVE__CPUIDEX', 1)
1916 endif
1919 # Defend against clang being used on x86-32 without SSE2 enabled.  As current
1920 # versions of clang do not understand -fexcess-precision=standard, the use of
1921 # x87 floating point operations leads to problems like isinf possibly returning
1922 # false for a value that is infinite when converted from the 80bit register to
1923 # the 8byte memory representation.
1925 # Only perform the test if the compiler doesn't understand
1926 # -fexcess-precision=standard, that way a potentially fixed compiler will work
1927 # automatically.
1928 if '-fexcess-precision=standard' not in cflags
1929   if not cc.compiles('''
1930 #if defined(__clang__) && defined(__i386__) && !defined(__SSE2_MATH__)
1931 choke me
1932 #endif''',
1933       name: '', args: test_c_args)
1934     error('Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.')
1935   endif
1936 endif
1940 ###############################################################
1941 # Compiler flags
1942 ###############################################################
1944 common_functional_flags = [
1945   # Disable strict-aliasing rules; needed for gcc 3.3+
1946   '-fno-strict-aliasing',
1947   # Disable optimizations that assume no overflow; needed for gcc 4.3+
1948   '-fwrapv',
1949   '-fexcess-precision=standard',
1952 cflags += cc.get_supported_arguments(common_functional_flags)
1953 if llvm.found()
1954   cxxflags += cpp.get_supported_arguments(common_functional_flags)
1955 endif
1957 vectorize_cflags = cc.get_supported_arguments(['-ftree-vectorize'])
1958 unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
1960 common_warning_flags = [
1961   '-Wmissing-prototypes',
1962   '-Wpointer-arith',
1963   # Really don't want VLAs to be used in our dialect of C
1964   '-Werror=vla',
1965   # On macOS, complain about usage of symbols newer than the deployment target
1966   '-Werror=unguarded-availability-new',
1967   '-Wendif-labels',
1968   '-Wmissing-format-attribute',
1969   '-Wimplicit-fallthrough=3',
1970   '-Wcast-function-type',
1971   '-Wshadow=compatible-local',
1972   # This was included in -Wall/-Wformat in older GCC versions
1973   '-Wformat-security',
1976 cflags_warn += cc.get_supported_arguments(common_warning_flags)
1977 if llvm.found()
1978   cxxflags_warn += cpp.get_supported_arguments(common_warning_flags)
1979 endif
1981 # A few places with imported code get a pass on -Wdeclaration-after-statement, remember
1982 # the result for them
1983 cflags_no_decl_after_statement = []
1984 if cc.has_argument('-Wdeclaration-after-statement')
1985   cflags_warn += '-Wdeclaration-after-statement'
1986   cflags_no_decl_after_statement += '-Wno-declaration-after-statement'
1987 endif
1990 # The following tests want to suppress various unhelpful warnings by adding
1991 # -Wno-foo switches.  But gcc won't complain about unrecognized -Wno-foo
1992 # switches, so we have to test for the positive form and if that works,
1993 # add the negative form.
1995 negative_warning_flags = [
1996   # Suppress clang's unhelpful unused-command-line-argument warnings.
1997   'unused-command-line-argument',
1999   # Remove clang 12+'s compound-token-split-by-macro, as this causes a lot
2000   # of warnings when building plperl because of usages in the Perl headers.
2001   'compound-token-split-by-macro',
2003   # Similarly disable useless truncation warnings from gcc 8+
2004   'format-truncation',
2005   'stringop-truncation',
2007   # Suppress clang 16's strict warnings about function casts
2008   'cast-function-type-strict',
2010   # To make warning_level=2 / -Wextra work, we'd need at least the following
2011   # 'clobbered',
2012   # 'missing-field-initializers',
2013   # 'sign-compare',
2014   # 'unused-parameter',
2017 foreach w : negative_warning_flags
2018   if cc.has_argument('-W' + w)
2019     cflags_warn += '-Wno-' + w
2020   endif
2021   if llvm.found() and cpp.has_argument('-W' + w)
2022     cxxflags_warn += '-Wno-' + w
2023   endif
2024 endforeach
2027 if cc.get_id() == 'msvc'
2028   cflags_warn += [
2029     '/wd4018', # signed/unsigned mismatch
2030     '/wd4244', # conversion from 'type1' to 'type2', possible loss of data
2031     '/wd4273', # inconsistent DLL linkage
2032     '/wd4101', # unreferenced local variable
2033     '/wd4102', # unreferenced label
2034     '/wd4090', # different 'modifier' qualifiers
2035     '/wd4267', # conversion from 'size_t' to 'type', possible loss of data
2036   ]
2038   cppflags += [
2039     '/DWIN32',
2040     '/DWINDOWS',
2041     '/D__WINDOWS__',
2042     '/D__WIN32__',
2043     '/D_CRT_SECURE_NO_DEPRECATE',
2044     '/D_CRT_NONSTDC_NO_DEPRECATE',
2045   ]
2047   # We never need export libraries. As link.exe reports their creation, they
2048   # are unnecessarily noisy. Similarly, we don't need import library for
2049   # modules, we only import them dynamically, and they're also noisy.
2050   ldflags += '/NOEXP'
2051   ldflags_mod += '/NOIMPLIB'
2052 endif
2055 # Compute flags that are built into Meson.  We need these to
2056 # substitute into Makefile.global and for pg_config.  We only compute
2057 # the flags for Unix-style compilers, since that's the only style that
2058 # would use Makefile.global or pg_config.
2060 # We don't use get_option('warning_level') here, because the other
2061 # warning levels are not useful with PostgreSQL source code.
2062 common_builtin_flags = ['-Wall']
2064 if get_option('debug')
2065   common_builtin_flags += ['-g']
2066 endif
2068 optimization = get_option('optimization')
2069 if optimization == '0'
2070   common_builtin_flags += ['-O0']
2071 elif optimization == '1'
2072   common_builtin_flags += ['-O1']
2073 elif optimization == '2'
2074   common_builtin_flags += ['-O2']
2075 elif optimization == '3'
2076   common_builtin_flags += ['-O3']
2077 elif optimization == 's'
2078   common_builtin_flags += ['-Os']
2079 endif
2081 cflags_builtin = cc.get_supported_arguments(common_builtin_flags)
2082 if llvm.found()
2083   cxxflags_builtin = cpp.get_supported_arguments(common_builtin_flags)
2084 endif
2088 ###############################################################
2089 # Atomics
2090 ###############################################################
2092 atomic_checks = [
2093   {'name': 'HAVE_GCC__SYNC_CHAR_TAS',
2094    'desc': '__sync_lock_test_and_set(char)',
2095    'test': '''
2096 char lock = 0;
2097 __sync_lock_test_and_set(&lock, 1);
2098 __sync_lock_release(&lock);'''},
2100   {'name': 'HAVE_GCC__SYNC_INT32_TAS',
2101    'desc': '__sync_lock_test_and_set(int32)',
2102    'test': '''
2103 int lock = 0;
2104 __sync_lock_test_and_set(&lock, 1);
2105 __sync_lock_release(&lock);'''},
2107   {'name': 'HAVE_GCC__SYNC_INT32_CAS',
2108    'desc': '__sync_val_compare_and_swap(int32)',
2109    'test': '''
2110 int val = 0;
2111 __sync_val_compare_and_swap(&val, 0, 37);'''},
2113   {'name': 'HAVE_GCC__SYNC_INT64_CAS',
2114    'desc': '__sync_val_compare_and_swap(int64)',
2115    'test': '''
2116 INT64 val = 0;
2117 __sync_val_compare_and_swap(&val, 0, 37);'''},
2119   {'name': 'HAVE_GCC__ATOMIC_INT32_CAS',
2120    'desc': ' __atomic_compare_exchange_n(int32)',
2121    'test': '''
2122 int val = 0;
2123 int expect = 0;
2124 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
2126   {'name': 'HAVE_GCC__ATOMIC_INT64_CAS',
2127    'desc': ' __atomic_compare_exchange_n(int64)',
2128    'test': '''
2129 INT64 val = 0;
2130 INT64 expect = 0;
2131 __atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);'''},
2134 foreach check : atomic_checks
2135   test = '''
2136 int main(void)
2139 }'''.format(check['test'])
2141   cdata.set(check['name'],
2142     cc.links(test,
2143       name: check['desc'],
2144       args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))]) ? 1 : false
2145   )
2146 endforeach
2149 ###############################################################
2150 # Check for the availability of XSAVE intrinsics.
2151 ###############################################################
2153 cflags_xsave = []
2154 if host_cpu == 'x86' or host_cpu == 'x86_64'
2156   prog = '''
2157 #include <immintrin.h>
2159 int main(void)
2161     return _xgetbv(0) & 0xe0;
2165   if cc.links(prog, name: 'XSAVE intrinsics without -mxsave',
2166         args: test_c_args)
2167     cdata.set('HAVE_XSAVE_INTRINSICS', 1)
2168   elif cc.links(prog, name: 'XSAVE intrinsics with -mxsave',
2169         args: test_c_args + ['-mxsave'])
2170     cdata.set('HAVE_XSAVE_INTRINSICS', 1)
2171     cflags_xsave += '-mxsave'
2172   endif
2174 endif
2177 ###############################################################
2178 # Check for the availability of AVX-512 popcount intrinsics.
2179 ###############################################################
2181 cflags_popcnt = []
2182 if host_cpu == 'x86_64'
2184   prog = '''
2185 #include <immintrin.h>
2187 int main(void)
2189     const char buf[sizeof(__m512i)];
2190     INT64 popcnt = 0;
2191     __m512i accum = _mm512_setzero_si512();
2192     const __m512i val = _mm512_maskz_loadu_epi8((__mmask64) 0xf0f0f0f0f0f0f0f0, (const __m512i *) buf);
2193     const __m512i cnt = _mm512_popcnt_epi64(val);
2194     accum = _mm512_add_epi64(accum, cnt);
2195     popcnt = _mm512_reduce_add_epi64(accum);
2196     /* return computed value, to prevent the above being optimized away */
2197     return popcnt == 0;
2201   if cc.links(prog, name: 'AVX-512 popcount without -mavx512vpopcntdq -mavx512bw',
2202         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))])
2203     cdata.set('USE_AVX512_POPCNT_WITH_RUNTIME_CHECK', 1)
2204   elif cc.links(prog, name: 'AVX-512 popcount with -mavx512vpopcntdq -mavx512bw',
2205         args: test_c_args + ['-DINT64=@0@'.format(cdata.get('PG_INT64_TYPE'))] + ['-mavx512vpopcntdq'] + ['-mavx512bw'])
2206     cdata.set('USE_AVX512_POPCNT_WITH_RUNTIME_CHECK', 1)
2207     cflags_popcnt += ['-mavx512vpopcntdq'] + ['-mavx512bw']
2208   endif
2210 endif
2213 ###############################################################
2214 # Select CRC-32C implementation.
2216 # If we are targeting a processor that has Intel SSE 4.2 instructions, we can
2217 # use the special CRC instructions for calculating CRC-32C. If we're not
2218 # targeting such a processor, but we can nevertheless produce code that uses
2219 # the SSE intrinsics, perhaps with some extra CFLAGS, compile both
2220 # implementations and select which one to use at runtime, depending on whether
2221 # SSE 4.2 is supported by the processor we're running on.
2223 # Similarly, if we are targeting an ARM processor that has the CRC
2224 # instructions that are part of the ARMv8 CRC Extension, use them. And if
2225 # we're not targeting such a processor, but can nevertheless produce code that
2226 # uses the CRC instructions, compile both, and select at runtime.
2227 ###############################################################
2229 have_optimized_crc = false
2230 cflags_crc = []
2231 if host_cpu == 'x86' or host_cpu == 'x86_64'
2233   if cc.get_id() == 'msvc'
2234     cdata.set('USE_SSE42_CRC32C', false)
2235     cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2236     have_optimized_crc = true
2237   else
2239     prog = '''
2240 #include <nmmintrin.h>
2242 int main(void)
2244     unsigned int crc = 0;
2245     crc = _mm_crc32_u8(crc, 0);
2246     crc = _mm_crc32_u32(crc, 0);
2247     /* return computed value, to prevent the above being optimized away */
2248     return crc == 0;
2252     if cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 without -msse4.2',
2253           args: test_c_args)
2254       # Use Intel SSE 4.2 unconditionally.
2255       cdata.set('USE_SSE42_CRC32C', 1)
2256       have_optimized_crc = true
2257     elif cc.links(prog, name: '_mm_crc32_u8 and _mm_crc32_u32 with -msse4.2',
2258           args: test_c_args + ['-msse4.2'])
2259       # Use Intel SSE 4.2, with runtime check. The CPUID instruction is needed for
2260       # the runtime check.
2261       cflags_crc += '-msse4.2'
2262       cdata.set('USE_SSE42_CRC32C', false)
2263       cdata.set('USE_SSE42_CRC32C_WITH_RUNTIME_CHECK', 1)
2264       have_optimized_crc = true
2265     endif
2267   endif
2269 elif host_cpu == 'arm' or host_cpu == 'aarch64'
2271   prog = '''
2272 #include <arm_acle.h>
2274 int main(void)
2276     unsigned int crc = 0;
2277     crc = __crc32cb(crc, 0);
2278     crc = __crc32ch(crc, 0);
2279     crc = __crc32cw(crc, 0);
2280     crc = __crc32cd(crc, 0);
2282     /* return computed value, to prevent the above being optimized away */
2283     return crc == 0;
2287   if cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd without -march=armv8-a+crc',
2288       args: test_c_args)
2289     # Use ARM CRC Extension unconditionally
2290     cdata.set('USE_ARMV8_CRC32C', 1)
2291     have_optimized_crc = true
2292   elif cc.links(prog, name: '__crc32cb, __crc32ch, __crc32cw, and __crc32cd with -march=armv8-a+crc',
2293       args: test_c_args + ['-march=armv8-a+crc'])
2294     # Use ARM CRC Extension, with runtime check
2295     cflags_crc += '-march=armv8-a+crc'
2296     cdata.set('USE_ARMV8_CRC32C', false)
2297     cdata.set('USE_ARMV8_CRC32C_WITH_RUNTIME_CHECK', 1)
2298     have_optimized_crc = true
2299   endif
2301 elif host_cpu == 'loongarch64'
2303   prog = '''
2304 int main(void)
2306     unsigned int crc = 0;
2307     crc = __builtin_loongarch_crcc_w_b_w(0, crc);
2308     crc = __builtin_loongarch_crcc_w_h_w(0, crc);
2309     crc = __builtin_loongarch_crcc_w_w_w(0, crc);
2310     crc = __builtin_loongarch_crcc_w_d_w(0, crc);
2312     /* return computed value, to prevent the above being optimized away */
2313     return crc == 0;
2317   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',
2318       args: test_c_args)
2319     # Use LoongArch CRC instruction unconditionally
2320     cdata.set('USE_LOONGARCH_CRC32C', 1)
2321     have_optimized_crc = true
2322   endif
2324 endif
2326 if not have_optimized_crc
2327   # fall back to slicing-by-8 algorithm, which doesn't require any special CPU
2328   # support.
2329   cdata.set('USE_SLICING_BY_8_CRC32C', 1)
2330 endif
2334 ###############################################################
2335 # Other CPU specific stuff
2336 ###############################################################
2338 if host_cpu == 'x86_64'
2340   if cc.compiles('''
2341       void main(void)
2342       {
2343           long long x = 1; long long r;
2344           __asm__ __volatile__ (" popcntq %1,%0\n" : "=q"(r) : "rm"(x));
2345       }''',
2346       name: '@0@: popcntq instruction'.format(host_cpu),
2347       args: test_c_args)
2348     cdata.set('HAVE_X86_64_POPCNTQ', 1)
2349   endif
2351 elif host_cpu == 'ppc' or host_cpu == 'ppc64'
2352   # Check if compiler accepts "i"(x) when __builtin_constant_p(x).
2353   if cdata.has('HAVE__BUILTIN_CONSTANT_P')
2354     if cc.compiles('''
2355       static inline int
2356       addi(int ra, int si)
2357       {
2358           int res = 0;
2359           if (__builtin_constant_p(si))
2360               __asm__ __volatile__(
2361                   " addi %0,%1,%2\n" : "=r"(res) : "b"(ra), "i"(si));
2362           return res;
2363       }
2364       int test_adds(int x) { return addi(3, x) + addi(x, 5); }
2365       ''',
2366       args: test_c_args)
2367       cdata.set('HAVE_I_CONSTRAINT__BUILTIN_CONSTANT_P', 1)
2368     endif
2369   endif
2370 endif
2374 ###############################################################
2375 # Library / OS tests
2376 ###############################################################
2378 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2379 # unnecessary checks over and over, particularly on windows.
2380 header_checks = [
2381   'atomic.h',
2382   'copyfile.h',
2383   'crtdefs.h',
2384   'execinfo.h',
2385   'getopt.h',
2386   'ifaddrs.h',
2387   'langinfo.h',
2388   'mbarrier.h',
2389   'stdbool.h',
2390   'strings.h',
2391   'sys/epoll.h',
2392   'sys/event.h',
2393   'sys/personality.h',
2394   'sys/prctl.h',
2395   'sys/procctl.h',
2396   'sys/signalfd.h',
2397   'sys/ucred.h',
2398   'termios.h',
2399   'ucred.h',
2402 foreach header : header_checks
2403   varname = 'HAVE_' + header.underscorify().to_upper()
2405   # Emulate autoconf behaviour of not-found->undef, found->1
2406   found = cc.has_header(header,
2407     include_directories: postgres_inc, args: test_c_args)
2408   cdata.set(varname, found ? 1 : false,
2409             description: 'Define to 1 if you have the <@0@> header file.'.format(header))
2410 endforeach
2413 decl_checks = [
2414   ['F_FULLFSYNC', 'fcntl.h'],
2415   ['fdatasync', 'unistd.h'],
2416   ['posix_fadvise', 'fcntl.h'],
2417   ['strlcat', 'string.h'],
2418   ['strlcpy', 'string.h'],
2419   ['strnlen', 'string.h'],
2420   ['strsep',  'string.h'],
2423 # Need to check for function declarations for these functions, because
2424 # checking for library symbols wouldn't handle deployment target
2425 # restrictions on macOS
2426 decl_checks += [
2427   ['preadv', 'sys/uio.h'],
2428   ['pwritev', 'sys/uio.h'],
2431 # Check presence of some optional LLVM functions.
2432 if llvm.found()
2433   decl_checks += [
2434     ['LLVMCreateGDBRegistrationListener', 'llvm-c/ExecutionEngine.h'],
2435     ['LLVMCreatePerfJITEventListener', 'llvm-c/ExecutionEngine.h'],
2436   ]
2437 endif
2439 foreach c : decl_checks
2440   func = c.get(0)
2441   header = c.get(1)
2442   args = c.get(2, {})
2443   varname = 'HAVE_DECL_' + func.underscorify().to_upper()
2445   found = cc.has_header_symbol(header, func,
2446     args: test_c_args, include_directories: postgres_inc,
2447     kwargs: args)
2448   cdata.set10(varname, found, description:
2449 '''Define to 1 if you have the declaration of `@0@', and to 0 if you
2450    don't.'''.format(func))
2451 endforeach
2454 if cc.has_type('struct option',
2455     args: test_c_args, include_directories: postgres_inc,
2456     prefix: '@0@'.format(cdata.get('HAVE_GETOPT_H')) == '1' ? '#include <getopt.h>' : '')
2457   cdata.set('HAVE_STRUCT_OPTION', 1)
2458 endif
2461 foreach c : ['opterr', 'optreset']
2462   varname = 'HAVE_INT_' + c.underscorify().to_upper()
2464   if cc.links('''
2465 #include <unistd.h>
2466 int main(void)
2468     extern int @0@;
2469     @0@ = 1;
2471 '''.format(c), name: c, args: test_c_args)
2472     cdata.set(varname, 1)
2473   else
2474     cdata.set(varname, false)
2475   endif
2476 endforeach
2478 if cc.has_type('socklen_t',
2479     args: test_c_args, include_directories: postgres_inc,
2480     prefix: '''
2481 #include <sys/socket.h>''')
2482   cdata.set('HAVE_SOCKLEN_T', 1)
2483 endif
2485 if cc.has_member('struct sockaddr', 'sa_len',
2486     args: test_c_args, include_directories: postgres_inc,
2487     prefix: '''
2488 #include <sys/types.h>
2489 #include <sys/socket.h>''')
2490   cdata.set('HAVE_STRUCT_SOCKADDR_SA_LEN', 1)
2491 endif
2493 if cc.has_member('struct tm', 'tm_zone',
2494     args: test_c_args, include_directories: postgres_inc,
2495     prefix: '''
2496 #include <sys/types.h>
2497 #include <time.h>
2498 ''')
2499   cdata.set('HAVE_STRUCT_TM_TM_ZONE', 1)
2500 endif
2502 if cc.compiles('''
2503 #include <time.h>
2504 extern int foo(void);
2505 int foo(void)
2507     return timezone / 60;
2509 ''',
2510     name: 'global variable `timezone\' exists',
2511     args: test_c_args, include_directories: postgres_inc)
2512   cdata.set('HAVE_INT_TIMEZONE', 1)
2513 else
2514   cdata.set('HAVE_INT_TIMEZONE', false)
2515 endif
2517 if cc.has_type('union semun',
2518     args: test_c_args,
2519     include_directories: postgres_inc,
2520     prefix: '''
2521 #include <sys/types.h>
2522 #include <sys/ipc.h>
2523 #include <sys/sem.h>
2524 ''')
2525   cdata.set('HAVE_UNION_SEMUN', 1)
2526 endif
2528 if cc.compiles('''
2529 #include <string.h>
2530 int main(void)
2532   char buf[100];
2533   switch (strerror_r(1, buf, sizeof(buf)))
2534   { case 0: break; default: break; }
2535 }''',
2536     name: 'strerror_r',
2537     args: test_c_args, include_directories: postgres_inc)
2538   cdata.set('STRERROR_R_INT', 1)
2539 else
2540   cdata.set('STRERROR_R_INT', false)
2541 endif
2543 # Find the right header file for the locale_t type.  macOS needs xlocale.h;
2544 # standard is locale.h, but glibc <= 2.25 also had an xlocale.h file that
2545 # we should not use so we check the standard header first.  MSVC has a
2546 # replacement defined in src/include/port/win32_port.h.
2547 if not cc.has_type('locale_t', prefix: '#include <locale.h>') and \
2548    cc.has_type('locale_t', prefix: '#include <xlocale.h>')
2549   cdata.set('LOCALE_T_IN_XLOCALE', 1)
2550 endif
2552 # Check if the C compiler understands typeof or a variant.  Define
2553 # HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
2554 foreach kw : ['typeof', '__typeof__', 'decltype']
2555   if cc.compiles('''
2556 int main(void)
2558     int x = 0;
2559     @0@(x) y;
2560     y = x;
2561     return y;
2563 '''.format(kw),
2564     name: 'typeof()',
2565     args: test_c_args, include_directories: postgres_inc)
2567     cdata.set('HAVE_TYPEOF', 1)
2568     if kw != 'typeof'
2569       cdata.set('typeof', kw)
2570     endif
2572     break
2573   endif
2574 endforeach
2577 # Try to find a declaration for wcstombs_l().  It might be in stdlib.h
2578 # (following the POSIX requirement for wcstombs()), or in locale.h, or in
2579 # xlocale.h.  If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
2580 wcstombs_l_test = '''
2581 #include <stdlib.h>
2582 #include <locale.h>
2585 void main(void)
2587 #ifndef wcstombs_l
2588     (void) wcstombs_l;
2589 #endif
2592 if (not cc.compiles(wcstombs_l_test.format(''),
2593       name: 'wcstombs_l') and
2594     cc.compiles(wcstombs_l_test.format('#include <xlocale.h>'),
2595       name: 'wcstombs_l in xlocale.h'))
2596     cdata.set('WCSTOMBS_L_IN_XLOCALE', 1)
2597 endif
2600 # MSVC doesn't cope well with defining restrict to __restrict, the spelling it
2601 # understands, because it conflicts with __declspec(restrict). Therefore we
2602 # define pg_restrict to the appropriate definition, which presumably won't
2603 # conflict.
2605 # We assume C99 support, so we don't need to make this conditional.
2606 cdata.set('pg_restrict', '__restrict')
2609 # Most libraries are included only if they demonstrably provide a function we
2610 # need, but libm is an exception: always include it, because there are too
2611 # many compilers that play cute optimization games that will break probes for
2612 # standard functions such as pow().
2613 os_deps += cc.find_library('m', required: false)
2615 rt_dep = cc.find_library('rt', required: false)
2617 dl_dep = cc.find_library('dl', required: false)
2619 util_dep = cc.find_library('util', required: false)
2621 getopt_dep = cc.find_library('getopt', required: false)
2622 gnugetopt_dep = cc.find_library('gnugetopt', required: false)
2623 # Check if we want to replace getopt/getopt_long even if provided by the system
2624 # - Mingw has adopted a GNU-centric interpretation of optind/optreset,
2625 #   so always use our version on Windows
2626 # - On OpenBSD and Solaris, getopt() doesn't do what we want for long options
2627 #   (i.e., allow '-' as a flag character), so use our version on those platforms
2628 # - We want to use system's getopt_long() only if the system provides struct
2629 #   option
2630 always_replace_getopt = host_system in ['windows', 'cygwin', 'openbsd', 'solaris']
2631 always_replace_getopt_long = host_system in ['windows', 'cygwin'] or not cdata.has('HAVE_STRUCT_OPTION')
2633 # Required on BSDs
2634 execinfo_dep = cc.find_library('execinfo', required: false)
2636 if host_system == 'cygwin'
2637   cygipc_dep = cc.find_library('cygipc', required: false)
2638 else
2639   cygipc_dep = not_found_dep
2640 endif
2642 if host_system == 'sunos'
2643   socket_dep = cc.find_library('socket', required: false)
2644 else
2645   socket_dep = not_found_dep
2646 endif
2648 # XXX: Might be worth conditioning some checks on the OS, to avoid doing
2649 # unnecessary checks over and over, particularly on windows.
2650 func_checks = [
2651   ['_configthreadlocale', {'skip': host_system != 'windows'}],
2652   ['backtrace_symbols', {'dependencies': [execinfo_dep]}],
2653   ['clock_gettime', {'dependencies': [rt_dep], 'define': false}],
2654   ['copyfile'],
2655   ['copy_file_range'],
2656   # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus
2657   # when enabling asan the dlopen check doesn't notice that -ldl is actually
2658   # required. Just checking for dlsym() ought to suffice.
2659   ['dlsym', {'dependencies': [dl_dep], 'define': false}],
2660   ['explicit_bzero'],
2661   ['getifaddrs'],
2662   ['getopt', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt}],
2663   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
2664   ['getpeereid'],
2665   ['getpeerucred'],
2666   ['inet_aton'],
2667   ['inet_pton'],
2668   ['kqueue'],
2669   ['mbstowcs_l'],
2670   ['memset_s'],
2671   ['mkdtemp'],
2672   ['posix_fadvise'],
2673   ['posix_fallocate'],
2674   ['ppoll'],
2675   ['pthread_barrier_wait', {'dependencies': [thread_dep]}],
2676   ['pthread_is_threaded_np', {'dependencies': [thread_dep]}],
2677   ['sem_init', {'dependencies': [rt_dep, thread_dep], 'skip': sema_kind != 'unnamed_posix', 'define': false}],
2678   ['setproctitle', {'dependencies': [util_dep]}],
2679   ['setproctitle_fast'],
2680   ['shm_open', {'dependencies': [rt_dep], 'define': false}],
2681   ['shm_unlink', {'dependencies': [rt_dep], 'define': false}],
2682   ['shmget', {'dependencies': [cygipc_dep], 'define': false}],
2683   ['socket', {'dependencies': [socket_dep], 'define': false}],
2684   ['strchrnul'],
2685   ['strerror_r', {'dependencies': [thread_dep]}],
2686   ['strlcat'],
2687   ['strlcpy'],
2688   ['strnlen'],
2689   ['strsep'],
2690   ['strsignal'],
2691   ['sync_file_range'],
2692   ['syncfs'],
2693   ['uselocale'],
2694   ['wcstombs_l'],
2697 func_check_results = {}
2698 foreach c : func_checks
2699   func = c.get(0)
2700   kwargs = c.get(1, {})
2701   deps = kwargs.get('dependencies', [])
2703   if kwargs.get('skip', false)
2704     continue
2705   endif
2707   found = cc.has_function(func, args: test_c_args)
2709   if not found
2710     foreach dep : deps
2711       if not dep.found()
2712         continue
2713       endif
2714       found = cc.has_function(func, args: test_c_args,
2715                               dependencies: [dep])
2716       if found
2717         os_deps += dep
2718         break
2719       endif
2720     endforeach
2721   endif
2723   func_check_results += {func: found}
2725   if kwargs.get('define', true)
2726     # Emulate autoconf behaviour of not-found->undef, found->1
2727     cdata.set('HAVE_' + func.underscorify().to_upper(),
2728               found  ? 1 : false,
2729               description: 'Define to 1 if you have the `@0@\' function.'.format(func))
2730   endif
2731 endforeach
2734 if cc.has_function('syslog', args: test_c_args) and \
2735     cc.check_header('syslog.h', args: test_c_args)
2736   cdata.set('HAVE_SYSLOG', 1)
2737 endif
2740 # if prerequisites for unnamed posix semas aren't fulfilled, fall back to sysv
2741 # semaphores
2742 if sema_kind == 'unnamed_posix' and \
2743    not func_check_results.get('sem_init', false)
2744   sema_kind = 'sysv'
2745 endif
2747 cdata.set('USE_@0@_SHARED_MEMORY'.format(shmem_kind.to_upper()), 1)
2748 cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
2750 cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
2751 cdata.set_quoted('DLSUFFIX', dlsuffix)
2754 # built later than the rest of the version metadata, we need SIZEOF_VOID_P
2755 cdata.set_quoted('PG_VERSION_STR',
2756   'PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit'.format(
2757     pg_version, host_machine.cpu_family(), host_system,
2758     cc.get_id(), cc.version(), cdata.get('SIZEOF_VOID_P') * 8,
2759   )
2763 ###############################################################
2764 # NLS / Gettext
2765 ###############################################################
2767 nlsopt = get_option('nls')
2768 libintl = not_found_dep
2770 if not nlsopt.disabled()
2771   # otherwise there'd be lots of
2772   # "Gettext not found, all translation (po) targets will be ignored."
2773   # warnings if not found.
2774   msgfmt = find_program('msgfmt', required: nlsopt, native: true)
2776   # meson 0.59 has this wrapped in dependency('intl')
2777   if (msgfmt.found() and
2778       cc.check_header('libintl.h', required: nlsopt,
2779         args: test_c_args, include_directories: postgres_inc))
2781     # in libc
2782     if cc.has_function('ngettext')
2783       libintl = declare_dependency()
2784     else
2785       libintl = cc.find_library('intl',
2786         has_headers: ['libintl.h'], required: nlsopt,
2787         header_include_directories: postgres_inc,
2788         dirs: test_lib_d)
2789     endif
2790   endif
2792   if libintl.found()
2793     i18n = import('i18n')
2794     cdata.set('ENABLE_NLS', 1)
2795   endif
2796 endif
2800 ###############################################################
2801 # Build
2802 ###############################################################
2804 # Set up compiler / linker arguments to be used everywhere, individual targets
2805 # can add further args directly, or indirectly via dependencies
2806 add_project_arguments(cflags, language: ['c'])
2807 add_project_arguments(cppflags, language: ['c'])
2808 add_project_arguments(cflags_warn, language: ['c'])
2809 add_project_arguments(cxxflags, language: ['cpp'])
2810 add_project_arguments(cppflags, language: ['cpp'])
2811 add_project_arguments(cxxflags_warn, language: ['cpp'])
2812 add_project_link_arguments(ldflags, language: ['c', 'cpp'])
2815 # Collect a number of lists of things while recursing through the source
2816 # tree. Later steps then can use those.
2818 # list of targets for various alias targets
2819 backend_targets = []
2820 bin_targets = []
2821 pl_targets = []
2822 contrib_targets = []
2823 testprep_targets = []
2824 nls_targets = []
2827 # Define the tests to distribute them to the correct test styles later
2828 test_deps = []
2829 tests = []
2832 # Default options for targets
2834 # First identify rpaths
2835 bin_install_rpaths = []
2836 lib_install_rpaths = []
2837 mod_install_rpaths = []
2840 # Don't add rpaths on darwin for now - as long as only absolute references to
2841 # libraries are needed, absolute LC_ID_DYLIB ensures libraries can be found in
2842 # their final destination.
2843 if host_system != 'darwin'
2844   # Add absolute path to libdir to rpath. This ensures installed binaries /
2845   # libraries find our libraries (mainly libpq).
2846   bin_install_rpaths += dir_prefix / dir_lib
2847   lib_install_rpaths += dir_prefix / dir_lib
2848   mod_install_rpaths += dir_prefix / dir_lib
2850   # Add extra_lib_dirs to rpath. This ensures we find libraries we depend on.
2851   #
2852   # Not needed on darwin even if we use relative rpaths for our own libraries,
2853   # as the install_name of libraries in extra_lib_dirs will point to their
2854   # location anyway.
2855   bin_install_rpaths += postgres_lib_d
2856   lib_install_rpaths += postgres_lib_d
2857   mod_install_rpaths += postgres_lib_d
2858 endif
2861 # Define arguments for default targets
2863 default_target_args = {
2864   'implicit_include_directories': false,
2865   'install': true,
2868 default_lib_args = default_target_args + {
2869   'name_prefix': '',
2872 internal_lib_args = default_lib_args + {
2873   'build_by_default': false,
2874   'install': false,
2877 default_mod_args = default_lib_args + {
2878   'name_prefix': '',
2879   'install_dir': dir_lib_pkg,
2882 default_bin_args = default_target_args + {
2883   'install_dir': dir_bin,
2886 if get_option('rpath')
2887   default_lib_args += {
2888     'install_rpath': ':'.join(lib_install_rpaths),
2889   }
2891   default_mod_args += {
2892     'install_rpath': ':'.join(mod_install_rpaths),
2893   }
2895   default_bin_args += {
2896     'install_rpath': ':'.join(bin_install_rpaths),
2897   }
2898 endif
2901 # Helper for exporting a limited number of symbols
2902 gen_export_kwargs = {
2903   'input': 'exports.txt',
2904   'output': '@BASENAME@.'+export_file_suffix,
2905   'command': [perl, files('src/tools/gen_export.pl'),
2906    '--format', export_file_format,
2907    '--input', '@INPUT0@', '--output', '@OUTPUT0@'],
2908   'build_by_default': false,
2909   'install': false,
2915 ### Helpers for custom targets used across the tree
2918 catalog_pm = files('src/backend/catalog/Catalog.pm')
2919 perfect_hash_pm = files('src/tools/PerfectHash.pm')
2920 gen_kwlist_deps = [perfect_hash_pm]
2921 gen_kwlist_cmd = [
2922   perl, '-I', '@SOURCE_ROOT@/src/tools',
2923   files('src/tools/gen_keywordlist.pl'),
2924   '--output', '@OUTDIR@', '@INPUT@']
2929 ### windows resources related stuff
2932 if host_system == 'windows'
2933   pg_ico = meson.source_root() / 'src' / 'port' / 'win32.ico'
2934   win32ver_rc = files('src/port/win32ver.rc')
2935   rcgen = find_program('src/tools/rcgen', native: true)
2937   rcgen_base_args = [
2938     '--srcdir', '@SOURCE_DIR@',
2939     '--builddir', meson.build_root(),
2940     '--rcout', '@OUTPUT0@',
2941     '--out', '@OUTPUT1@',
2942     '--input', '@INPUT@',
2943     '@EXTRA_ARGS@',
2944   ]
2946   if cc.get_argument_syntax() == 'msvc'
2947     rc = find_program('rc', required: true)
2948     rcgen_base_args += ['--rc', rc.path()]
2949     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.res']
2950   else
2951     windres = find_program('windres', required: true)
2952     rcgen_base_args += ['--windres', windres.path()]
2953     rcgen_outputs = ['@BASENAME@.rc', '@BASENAME@.obj']
2954   endif
2956   # msbuild backend doesn't support this atm
2957   if meson.backend() == 'ninja'
2958     rcgen_base_args += ['--depfile', '@DEPFILE@']
2959   endif
2961   rcgen_bin_args = rcgen_base_args + [
2962     '--VFT_TYPE', 'VFT_APP',
2963     '--FILEENDING', 'exe',
2964     '--ICO', pg_ico
2965   ]
2967   rcgen_lib_args = rcgen_base_args + [
2968     '--VFT_TYPE', 'VFT_DLL',
2969     '--FILEENDING', 'dll',
2970   ]
2972   rc_bin_gen = generator(rcgen,
2973     depfile: '@BASENAME@.d',
2974     arguments: rcgen_bin_args,
2975     output: rcgen_outputs,
2976   )
2978   rc_lib_gen = generator(rcgen,
2979     depfile: '@BASENAME@.d',
2980     arguments: rcgen_lib_args,
2981     output: rcgen_outputs,
2982   )
2983 endif
2987 # headers that the whole build tree depends on
2988 generated_headers = []
2989 # headers that the backend build depends on
2990 generated_backend_headers = []
2991 # configure_files() output, needs a way of converting to file names
2992 configure_files = []
2994 # generated files that might conflict with a partial in-tree autoconf build
2995 generated_sources = []
2996 # same, for paths that differ between autoconf / meson builds
2997 # elements are [dir, [files]]
2998 generated_sources_ac = {}
3001 # First visit src/include - all targets creating headers are defined
3002 # within. That makes it easy to add the necessary dependencies for the
3003 # subsequent build steps.
3005 subdir('src/include')
3007 subdir('config')
3009 # Then through src/port and src/common, as most other things depend on them
3011 frontend_port_code = declare_dependency(
3012   compile_args: ['-DFRONTEND'],
3013   include_directories: [postgres_inc],
3014   dependencies: os_deps,
3017 backend_port_code = declare_dependency(
3018   compile_args: ['-DBUILDING_DLL'],
3019   include_directories: [postgres_inc],
3020   sources: [errcodes], # errcodes.h is needed due to use of ereport
3021   dependencies: os_deps,
3024 subdir('src/port')
3026 frontend_common_code = declare_dependency(
3027   compile_args: ['-DFRONTEND'],
3028   include_directories: [postgres_inc],
3029   sources: generated_headers,
3030   dependencies: [os_deps, zlib, zstd],
3033 backend_common_code = declare_dependency(
3034   compile_args: ['-DBUILDING_DLL'],
3035   include_directories: [postgres_inc],
3036   sources: generated_headers,
3037   dependencies: [os_deps, zlib, zstd],
3040 subdir('src/common')
3042 # all shared libraries should depend on shlib_code
3043 shlib_code = declare_dependency(
3044   link_args: ldflags_sl,
3047 # all static libraries not part of the backend should depend on this
3048 frontend_stlib_code = declare_dependency(
3049   include_directories: [postgres_inc],
3050   link_with: [common_static, pgport_static],
3051   sources: generated_headers,
3052   dependencies: [os_deps, libintl],
3055 # all shared libraries not part of the backend should depend on this
3056 frontend_shlib_code = declare_dependency(
3057   include_directories: [postgres_inc],
3058   link_with: [common_shlib, pgport_shlib],
3059   sources: generated_headers,
3060   dependencies: [shlib_code, os_deps, libintl],
3063 # Dependencies both for static and shared libpq
3064 libpq_deps += [
3065   thread_dep,
3067   gssapi,
3068   ldap_r,
3069   libintl,
3070   ssl,
3073 subdir('src/interfaces/libpq')
3074 # fe_utils depends on libpq
3075 subdir('src/fe_utils')
3077 # for frontend binaries
3078 frontend_code = declare_dependency(
3079   include_directories: [postgres_inc],
3080   link_with: [fe_utils, common_static, pgport_static],
3081   sources: generated_headers,
3082   dependencies: [os_deps, libintl],
3085 backend_both_deps += [
3086   thread_dep,
3087   bsd_auth,
3088   gssapi,
3089   icu,
3090   icu_i18n,
3091   ldap,
3092   libintl,
3093   libxml,
3094   lz4,
3095   pam,
3096   ssl,
3097   systemd,
3098   zlib,
3099   zstd,
3102 backend_mod_deps = backend_both_deps + os_deps
3104 backend_code = declare_dependency(
3105   compile_args: ['-DBUILDING_DLL'],
3106   include_directories: [postgres_inc],
3107   link_args: ldflags_be,
3108   link_with: [],
3109   sources: generated_headers + generated_backend_headers,
3110   dependencies: os_deps + backend_both_deps + backend_deps,
3113 # install these files only during test, not main install
3114 test_install_data = []
3115 test_install_libs = []
3117 # src/backend/meson.build defines backend_mod_code used for extension
3118 # libraries.
3121 # Then through the main sources. That way contrib can have dependencies on
3122 # main sources. Note that this explicitly doesn't enter src/test, right now a
3123 # few regression tests depend on contrib files.
3125 subdir('src')
3127 subdir('contrib')
3129 subdir('src/test')
3130 subdir('src/interfaces/libpq/test')
3131 subdir('src/interfaces/ecpg/test')
3133 subdir('doc/src/sgml')
3135 generated_sources_ac += {'': ['GNUmakefile']}
3137 # After processing src/test, add test_install_libs to the testprep_targets
3138 # to build them
3139 testprep_targets += test_install_libs
3142 # If there are any files in the source directory that we also generate in the
3143 # build directory, they might get preferred over the newly generated files,
3144 # e.g. because of a #include "file", which always will search in the current
3145 # directory first.
3146 message('checking for file conflicts between source and build directory')
3147 conflicting_files = []
3148 potentially_conflicting_files_t = []
3149 potentially_conflicting_files_t += generated_headers
3150 potentially_conflicting_files_t += generated_backend_headers
3151 potentially_conflicting_files_t += generated_backend_sources
3152 potentially_conflicting_files_t += generated_sources
3154 potentially_conflicting_files = []
3156 # convert all sources of potentially conflicting files into uniform shape
3157 foreach t : potentially_conflicting_files_t
3158   potentially_conflicting_files += t.full_path()
3159 endforeach
3160 foreach t1 : configure_files
3161   if meson.version().version_compare('>=0.59')
3162     t = fs.parent(t1) / fs.name(t1)
3163   else
3164     t = '@0@'.format(t1)
3165   endif
3166   potentially_conflicting_files += meson.current_build_dir() / t
3167 endforeach
3168 foreach sub, fnames : generated_sources_ac
3169   sub = meson.build_root() / sub
3170   foreach fname : fnames
3171     potentially_conflicting_files += sub / fname
3172   endforeach
3173 endforeach
3175 # find and report conflicting files
3176 foreach build_path : potentially_conflicting_files
3177   build_path = host_system == 'windows' ? fs.as_posix(build_path) : build_path
3178   # str.replace is in 0.56
3179   src_path = meson.current_source_dir() / build_path.split(meson.current_build_dir() / '')[1]
3180   if fs.exists(src_path) or fs.is_symlink(src_path)
3181     conflicting_files += src_path
3182   endif
3183 endforeach
3184 # XXX: Perhaps we should generate a file that would clean these up? The list
3185 # can be long.
3186 if conflicting_files.length() > 0
3187   errmsg_cleanup = '''
3188 Conflicting files in source directory:
3189   @0@
3191 The conflicting files need to be removed, either by removing the files listed
3192 above, or by running configure and then make maintainer-clean.
3194   errmsg_cleanup = errmsg_cleanup.format(' '.join(conflicting_files))
3195   error(errmsg_nonclean_base.format(errmsg_cleanup))
3196 endif
3200 ###############################################################
3201 # Install targets
3202 ###############################################################
3205 # We want to define additional install targets beyond what meson provides. For
3206 # that we need to define targets depending on nearly everything. We collected
3207 # the results of i18n.gettext() invocations into nls_targets, that also
3208 # includes maintainer targets though. Collect the ones we want as a dependency.
3210 # i18n.gettext() doesn't return the dependencies before 0.60 - but the gettext
3211 # generation happens during install, so that's not a real issue.
3212 nls_mo_targets = []
3213 if libintl.found() and meson.version().version_compare('>=0.60')
3214   # use range() to avoid the flattening of the list that foreach() would do
3215   foreach off : range(0, nls_targets.length())
3216     # i18n.gettext() list containing 1) list of built .mo files 2) maintainer
3217     # -pot target 3) maintainer -pot target
3218     nls_mo_targets += nls_targets[off][0]
3219   endforeach
3220   alias_target('nls', nls_mo_targets)
3221 endif
3224 all_built = [
3225   backend_targets,
3226   bin_targets,
3227   libpq_st,
3228   pl_targets,
3229   contrib_targets,
3230   nls_mo_targets,
3231   testprep_targets,
3232   ecpg_targets,
3235 # Meson's default install target is quite verbose. Provide one that is quiet.
3236 install_quiet = custom_target('install-quiet',
3237   output: 'install-quiet',
3238   build_always_stale: true,
3239   build_by_default: false,
3240   command: [meson_bin, meson_args, 'install', '--quiet', '--no-rebuild'],
3241   depends: all_built,
3244 # Target to install files used for tests, which aren't installed by default
3245 install_test_files_args = [
3246   install_files,
3247   '--prefix', dir_prefix,
3248   '--install', contrib_data_dir, test_install_data,
3249   '--install', dir_lib_pkg, test_install_libs,
3251 run_target('install-test-files',
3252   command: [python] + install_test_files_args,
3253   depends: testprep_targets,
3258 ###############################################################
3259 # Test prep
3260 ###############################################################
3262 # DESTDIR for the installation we'll run tests in
3263 test_install_destdir = meson.build_root() / 'tmp_install/'
3265 # DESTDIR + prefix appropriately munged
3266 if build_system != 'windows'
3267   # On unixoid systems this is trivial, we just prepend the destdir
3268   assert(dir_prefix.startswith('/')) # enforced by meson
3269   temp_install_bindir = '@0@@1@'.format(test_install_destdir, dir_prefix / dir_bin)
3270   temp_install_libdir = '@0@@1@'.format(test_install_destdir, dir_prefix / dir_lib)
3271 else
3272   # drives, drive-relative paths, etc make this complicated on windows, call
3273   # into a copy of meson's logic for it
3274   command = [
3275     python, '-c',
3276     'import sys; from pathlib import PurePath; d1=sys.argv[1]; d2=sys.argv[2]; print(str(PurePath(d1, *PurePath(d2).parts[1:])))',
3277     test_install_destdir]
3278   temp_install_bindir = run_command(command, dir_prefix / dir_bin, check: true).stdout().strip()
3279   temp_install_libdir = run_command(command, dir_prefix / dir_lib, check: true).stdout().strip()
3280 endif
3282 meson_install_args = meson_args + ['install'] + {
3283     'meson': ['--quiet', '--only-changed', '--no-rebuild'],
3284     'muon': []
3285 }[meson_impl]
3287 # setup tests should be run first,
3288 # so define priority for these
3289 setup_tests_priority = 100
3290 test('tmp_install',
3291     meson_bin, args: meson_install_args ,
3292     env: {'DESTDIR':test_install_destdir},
3293     priority: setup_tests_priority,
3294     timeout: 300,
3295     is_parallel: false,
3296     suite: ['setup'])
3298 test('install_test_files',
3299     python,
3300     args: install_test_files_args + ['--destdir', test_install_destdir],
3301     priority: setup_tests_priority,
3302     is_parallel: false,
3303     suite: ['setup'])
3305 test_result_dir = meson.build_root() / 'testrun'
3308 # XXX: pg_regress doesn't assign unique ports on windows. To avoid the
3309 # inevitable conflicts from running tests in parallel, hackishly assign
3310 # different ports for different tests.
3312 testport = 40000
3314 test_env = environment()
3316 test_initdb_template = meson.build_root() / 'tmp_install' / 'initdb-template'
3317 test_env.set('PG_REGRESS', pg_regress.full_path())
3318 test_env.set('REGRESS_SHLIB', regress_module.full_path())
3319 test_env.set('INITDB_TEMPLATE', test_initdb_template)
3321 # Test suites that are not safe by default but can be run if selected
3322 # by the user via the whitespace-separated list in variable PG_TEST_EXTRA.
3323 # Export PG_TEST_EXTRA so it can be checked in individual tap tests.
3324 test_env.set('PG_TEST_EXTRA', get_option('PG_TEST_EXTRA'))
3326 # Add the temporary installation to the library search path on platforms where
3327 # that works (everything but windows, basically). On windows everything
3328 # library-like gets installed into bindir, solving that issue.
3329 if library_path_var != ''
3330   test_env.prepend(library_path_var, temp_install_libdir)
3331 endif
3334 # Create (and remove old) initdb template directory. Tests use that, where
3335 # possible, to make it cheaper to run tests.
3337 # Use python to remove the old cached initdb, as we cannot rely on a working
3338 # 'rm' binary on windows.
3339 test('initdb_cache',
3340      python,
3341      args: [
3342        '-c', '''
3343 import shutil
3344 import sys
3345 import subprocess
3347 shutil.rmtree(sys.argv[1], ignore_errors=True)
3348 sp = subprocess.run(sys.argv[2:] + [sys.argv[1]])
3349 sys.exit(sp.returncode)
3350 ''',
3351        test_initdb_template,
3352        temp_install_bindir / 'initdb',
3353        '--auth', 'trust', '--no-sync', '--no-instructions', '--lc-messages=C',
3354        '--no-clean'
3355      ],
3356      priority: setup_tests_priority - 1,
3357      timeout: 300,
3358      is_parallel: false,
3359      env: test_env,
3360      suite: ['setup'])
3364 ###############################################################
3365 # Test Generation
3366 ###############################################################
3368 # When using a meson version understanding exclude_suites, define a
3369 # 'tmp_install' test setup (the default) that excludes tests running against a
3370 # pre-existing install and a 'running' setup that conflicts with creation of
3371 # the temporary installation and tap tests (which don't support running
3372 # against a running server).
3374 running_suites = []
3375 install_suites = []
3376 if meson.version().version_compare('>=0.57')
3377   runningcheck = true
3378 else
3379   runningcheck = false
3380 endif
3382 testwrap = files('src/tools/testwrap')
3384 foreach test_dir : tests
3385   testwrap_base = [
3386     testwrap,
3387     '--basedir', meson.build_root(),
3388     '--srcdir', test_dir['sd'],
3389   ]
3391   foreach kind, v : test_dir
3392     if kind in ['sd', 'bd', 'name']
3393       continue
3394     endif
3396     t = test_dir[kind]
3398     if kind in ['regress', 'isolation', 'ecpg']
3399       if kind == 'regress'
3400         runner = pg_regress
3401         fallback_dbname = 'regression_@0@'
3402       elif kind == 'isolation'
3403         runner = pg_isolation_regress
3404         fallback_dbname = 'isolation_regression_@0@'
3405       elif kind == 'ecpg'
3406         runner = pg_regress_ecpg
3407         fallback_dbname = 'ecpg_regression_@0@'
3408       endif
3410       test_group = test_dir['name']
3411       test_group_running = test_dir['name'] + '-running'
3413       test_output = test_result_dir / test_group / kind
3414       test_output_running = test_result_dir / test_group_running/ kind
3416       # Unless specified by the test, choose a non-conflicting database name,
3417       # to avoid conflicts when running against existing server.
3418       dbname = t.get('dbname',
3419         fallback_dbname.format(test_dir['name']))
3421       test_command_base = [
3422         runner.full_path(),
3423         '--inputdir', t.get('inputdir', test_dir['sd']),
3424         '--expecteddir', t.get('expecteddir', test_dir['sd']),
3425         '--bindir', '',
3426         '--dlpath', test_dir['bd'],
3427         '--max-concurrent-tests=20',
3428         '--dbname', dbname,
3429       ] + t.get('regress_args', [])
3431       test_selection = []
3432       if t.has_key('schedule')
3433         test_selection += ['--schedule', t['schedule'],]
3434       endif
3436       if kind == 'isolation'
3437         test_selection += t.get('specs', [])
3438       else
3439         test_selection += t.get('sql', [])
3440       endif
3442       env = test_env
3443       env.prepend('PATH', temp_install_bindir, test_dir['bd'])
3445       test_kwargs = {
3446         'protocol': 'tap',
3447         'priority': 10,
3448         'timeout': 1000,
3449         'depends': test_deps + t.get('deps', []),
3450         'env': env,
3451       } + t.get('test_kwargs', {})
3453       test(test_group / kind,
3454         python,
3455         args: [
3456           testwrap_base,
3457           '--testgroup', test_group,
3458           '--testname', kind,
3459           '--',
3460           test_command_base,
3461           '--outputdir', test_output,
3462           '--temp-instance', test_output / 'tmp_check',
3463           '--port', testport.to_string(),
3464           test_selection,
3465         ],
3466         suite: test_group,
3467         kwargs: test_kwargs,
3468       )
3469       install_suites += test_group
3471       # some tests can't support running against running DB
3472       if runningcheck and t.get('runningcheck', true)
3473         test(test_group_running / kind,
3474           python,
3475           args: [
3476             testwrap_base,
3477             '--testgroup', test_group_running,
3478             '--testname', kind,
3479             '--',
3480             test_command_base,
3481             '--outputdir', test_output_running,
3482             test_selection,
3483           ],
3484           is_parallel: t.get('runningcheck-parallel', true),
3485           suite: test_group_running,
3486           kwargs: test_kwargs,
3487         )
3488         running_suites += test_group_running
3489       endif
3491       testport += 1
3492     elif kind == 'tap'
3493       testwrap_tap = testwrap_base
3494       if not tap_tests_enabled
3495         testwrap_tap += ['--skip', 'TAP tests not enabled']
3496       endif
3498       test_command = [
3499         perl.path(),
3500         '-I', meson.source_root() / 'src/test/perl',
3501         '-I', test_dir['sd'],
3502       ]
3504       # Add temporary install, the build directory for non-installed binaries and
3505       # also test/ for non-installed test binaries built separately.
3506       env = test_env
3507       env.prepend('PATH', temp_install_bindir, test_dir['bd'], test_dir['bd'] / 'test')
3509       foreach name, value : t.get('env', {})
3510         env.set(name, value)
3511       endforeach
3513       test_group = test_dir['name']
3514       test_kwargs = {
3515         'protocol': 'tap',
3516         'suite': test_group,
3517         'timeout': 1000,
3518         'depends': test_deps + t.get('deps', []),
3519         'env': env,
3520       } + t.get('test_kwargs', {})
3522       foreach onetap : t['tests']
3523         # Make tap test names prettier, remove t/ and .pl
3524         onetap_p = onetap
3525         if onetap_p.startswith('t/')
3526           onetap_p = onetap.split('t/')[1]
3527         endif
3528         if onetap_p.endswith('.pl')
3529           onetap_p = fs.stem(onetap_p)
3530         endif
3532         test(test_dir['name'] / onetap_p,
3533           python,
3534           kwargs: test_kwargs,
3535           args: testwrap_tap + [
3536             '--testgroup', test_dir['name'],
3537             '--testname', onetap_p,
3538             '--', test_command,
3539             test_dir['sd'] / onetap,
3540           ],
3541         )
3542       endforeach
3543       install_suites += test_group
3544     else
3545       error('unknown kind @0@ of test in @1@'.format(kind, test_dir['sd']))
3546     endif
3548   endforeach # kinds of tests
3550 endforeach # directories with tests
3552 # repeat condition so meson realizes version dependency
3553 if meson.version().version_compare('>=0.57')
3554   add_test_setup('tmp_install',
3555     is_default: true,
3556     exclude_suites: running_suites)
3557   add_test_setup('running',
3558     exclude_suites: ['setup'] + install_suites)
3559 endif
3563 ###############################################################
3564 # Pseudo targets
3565 ###############################################################
3567 alias_target('backend', backend_targets)
3568 alias_target('bin', bin_targets + [libpq_st])
3569 alias_target('pl', pl_targets)
3570 alias_target('contrib', contrib_targets)
3571 alias_target('testprep', testprep_targets)
3573 alias_target('world', all_built, docs)
3574 alias_target('install-world', install_quiet, installdocs)
3576 run_target('help',
3577   command: [
3578     perl, '-ne', 'next if /^#/; print',
3579     files('doc/src/sgml/targets-meson.txt'),
3580   ]
3585 ###############################################################
3586 # Distribution archive
3587 ###############################################################
3589 # Meson has its own distribution building command (meson dist), but we
3590 # are not using that at this point.  The main problem is that, the way
3591 # they have implemented it, it is not deterministic.  Also, we want it
3592 # to be equivalent to the "make" version for the time being.  But the
3593 # target name "dist" in meson is reserved for that reason, so we call
3594 # the custom target "pgdist".
3596 git = find_program('git', required: false, native: true, disabler: true)
3597 bzip2 = find_program('bzip2', required: false, native: true)
3599 distdir = meson.project_name() + '-' + meson.project_version()
3601 pg_git_revision = get_option('PG_GIT_REVISION')
3603 # Note: core.autocrlf=false is needed to avoid line-ending conversion
3604 # in case the environment has a different setting.  Without this, a
3605 # tarball created on Windows might be different than on, and unusable
3606 # on, Unix machines.
3608 tar_gz = custom_target('tar.gz',
3609   build_always_stale: true,
3610   command: [git, '-C', '@SOURCE_ROOT@',
3611             '-c', 'core.autocrlf=false',
3612             'archive',
3613             '--format', 'tar.gz',
3614             '-9',
3615             '--prefix', distdir + '/',
3616             '-o', join_paths(meson.build_root(), '@OUTPUT@'),
3617             pg_git_revision],
3618   output: distdir + '.tar.gz',
3621 if bzip2.found()
3622   tar_bz2 = custom_target('tar.bz2',
3623     build_always_stale: true,
3624     command: [git, '-C', '@SOURCE_ROOT@',
3625               '-c', 'core.autocrlf=false',
3626               '-c', 'tar.tar.bz2.command="@0@" -c'.format(bzip2.path()),
3627               'archive',
3628               '--format', 'tar.bz2',
3629               '--prefix', distdir + '/',
3630               '-o', join_paths(meson.build_root(), '@OUTPUT@'),
3631               pg_git_revision],
3632     output: distdir + '.tar.bz2',
3633   )
3634 else
3635   tar_bz2 = custom_target('tar.bz2',
3636     command: [perl, '-e', 'exit 1'],
3637     output: distdir + '.tar.bz2',
3638   )
3639 endif
3641 alias_target('pgdist', [tar_gz, tar_bz2])
3643 # Make the standard "dist" command fail, to prevent accidental use.
3644 # But not if we are in a subproject, in case the parent project wants to
3645 # create a dist using the standard Meson command.
3646 if not meson.is_subproject()
3647   # We can only pass the identifier perl here when we depend on >= 0.55
3648   if meson.version().version_compare('>=0.55')
3649     meson.add_dist_script(perl, '-e', 'exit 1')
3650   endif
3651 endif
3655 ###############################################################
3656 # The End, The End, My Friend
3657 ###############################################################
3659 if meson.version().version_compare('>=0.57')
3661   summary(
3662     {
3663       'data block size': '@0@ kB'.format(cdata.get('BLCKSZ') / 1024),
3664       'WAL block size': '@0@ kB'.format(cdata.get('XLOG_BLCKSZ') / 1024),
3665       'segment size': get_option('segsize_blocks') != 0 ?
3666         '@0@ blocks'.format(cdata.get('RELSEG_SIZE')) :
3667         '@0@ GB'.format(get_option('segsize')),
3668     },
3669     section: 'Data layout',
3670   )
3672   summary(
3673     {
3674       'host system': '@0@ @1@'.format(host_system, host_cpu),
3675       'build system': '@0@ @1@'.format(build_machine.system(),
3676                                        build_machine.cpu_family()),
3677     },
3678     section: 'System',
3679   )
3681   summary(
3682     {
3683       'linker': '@0@'.format(cc.get_linker_id()),
3684       'C compiler': '@0@ @1@'.format(cc.get_id(), cc.version()),
3685     },
3686     section: 'Compiler',
3687   )
3689   summary(
3690     {
3691       'CPP FLAGS': ' '.join(cppflags),
3692       'C FLAGS, functional': ' '.join(cflags),
3693       'C FLAGS, warnings': ' '.join(cflags_warn),
3694       'C FLAGS, modules': ' '.join(cflags_mod),
3695       'C FLAGS, user specified': ' '.join(get_option('c_args')),
3696       'LD FLAGS': ' '.join(ldflags + get_option('c_link_args')),
3697     },
3698     section: 'Compiler Flags',
3699   )
3701   if llvm.found()
3702     summary(
3703       {
3704         'C++ compiler': '@0@ @1@'.format(cpp.get_id(), cpp.version()),
3705       },
3706       section: 'Compiler',
3707     )
3709     summary(
3710       {
3711         'C++ FLAGS, functional': ' '.join(cxxflags),
3712         'C++ FLAGS, warnings': ' '.join(cxxflags_warn),
3713         'C++ FLAGS, user specified': ' '.join(get_option('cpp_args')),
3714       },
3715       section: 'Compiler Flags',
3716     )
3717   endif
3719   summary(
3720     {
3721       'bison': '@0@ @1@'.format(bison.full_path(), bison_version),
3722       'dtrace': dtrace,
3723       'flex': '@0@ @1@'.format(flex.full_path(), flex_version),
3724     },
3725     section: 'Programs',
3726   )
3728   summary(
3729     {
3730       'bonjour': bonjour,
3731       'bsd_auth': bsd_auth,
3732       'docs': docs_dep,
3733       'docs_pdf': docs_pdf_dep,
3734       'gss': gssapi,
3735       'icu': icu,
3736       'ldap': ldap,
3737       'libxml': libxml,
3738       'libxslt': libxslt,
3739       'llvm': llvm,
3740       'lz4': lz4,
3741       'nls': libintl,
3742       'openssl': ssl,
3743       'pam': pam,
3744       'plperl': perl_dep,
3745       'plpython': python3_dep,
3746       'pltcl': tcl_dep,
3747       'readline': readline,
3748       'selinux': selinux,
3749       'systemd': systemd,
3750       'uuid': uuid,
3751       'zlib': zlib,
3752       'zstd': zstd,
3753     },
3754     section: 'External libraries',
3755   )
3757 endif